待办事项日历组件实现
今天积累一个简易的待办事项日历组件的实现方法。
需求:
- 修改样式,变成符合项目要求的日历样式
- 日历上展示待办事项提示(有未完成待办:展示黄点,有已完成待办:展示绿点)
- 点击有待办事项的日期,会展示待办事项清单
解决方案:
- 修改样式:打开浏览器,点击查看组件中要修改的元素的类名,然后进行修改
- 代办实现圆点:遍历数组,为有待办事项的日期添加指定的类名,在指定类名的类后用
::after
来画出“小圆点” - 动态创建待办事项列表:利用render函数(vue3)里需要导入h函数
这种依据某种逻辑来动态创建元素的场景,个人认为利用render动态创建元素很好用!
具体实施:
打开浏览器,点击查看组件中要修改的元素的类名,然后进行修改
el-calendar 为用户预留了插槽(可以自定义日历),使用方法如下:详情可见https://cn.element-plus.org/zh-CN/component/calendar.html
<el-calendar class='custom-calendar' v-model="value" > <template #date-cell="{ data }"> ... </template> </el-calendar>
这里的data里有着日历所展示的“每一天”的各种关键数据,是个大对象
定义一个dateTd.vue组件,用于自定义日期展示的格式,传入data.day("xxxx-xx-xx"类型的日期)和dataList(待办事项的列表)
dataTd.vue组件内容:
- 定义一个render函数,内部使用h函数来动态创建元素
- 判断如果当前日期在dataList中(说明当前日期有待办事项),就添加
hastate
、finish/unfinish
类- finish类元素就在后面用
::after
创建一个绿色圆点 - unfinish类元素就在后面用
::after
创建一个黄色圆点
- finish类元素就在后面用
- 根据当日是否有待办事项,创建不同的元素
没有待办事项:创建一个div展示日期(只展示年月日中的日)
有待办事项:创建一个div展示日期+一个弹窗div(添加
toolTip
这个类)(其中包含多个p元素,展示待办事项)if(!hasInFlag){ return h('div', { class: classObj }, currentDay) }else{ return h('div', { class: { ...classObj, } }, [ currentDay, h('div', { class:{ toolTip: true } },[ ...createList(list) ]) ]) }
- 判断如果当前日期在dataList中(说明当前日期有待办事项),就添加
- 定义一个render函数,内部使用h函数来动态创建元素
el-calendar 会自动为选中的日期添加
is-selected
类,刚好我们希望点击选中才展示待办事项弹窗// 设置弹窗样式 .custom-calendar:deep(.toolTip) { display: none; position: absolute; z-index: 999; width: 80px; background-color: white; border: 1px solid #409EFF; line-height: 30px; } // 只有被选中的才弹出弹窗 .custom-calendar:deep(.is-selected .toolTip) { display: block; }
完整代码:
index.vue
<template>
<el-calendar class='custom-calendar' v-model="value" >
<template #date-cell="{ data }">
<dateTd :day="data.day" :datelist="dataList"/>
</template>
</el-calendar>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import dateTd from './dateTd.vue'
const value = ref(new Date())
const dataList = ref(
[
{
time: '2025-04-26',
state: 'finish',
list: [
'浇水',
'施肥',
'喷药'
]
},
{
time: '2025-04-23',
state: 'unfinish',
list: [
'浇水',
'杀虫'
]
},
{
time: '2025-04-24',
state: 'finish',
list: [
'施肥',
]
}
]
);
console.log(dataList);
</script>
<style scoped lang="scss">
// 让日历中每个格子的文字水平垂直居中
.custom-calendar:deep(.el-calendar-day) {
height: 37px;
line-height: 37px;
padding: 0;
// 让内部的span水平居中
text-align: center;
}
// 日历中今日的样式为蓝色原型、白字
.custom-calendar:deep(.is-today) {
background-color: #409EFF;
color: white;
border-radius: 37px;
}
// 设置今日的各自hover字体变黑色
.custom-calendar:deep(.is-today:hover) {
color: black;
}
// 悬停样式为圆形
.custom-calendar:deep(.el-calendar-day) {
border-radius: 37px;
}
.el-calendar {
--el-calendar-border: none;
width: 300px;
}
.hastate {
position: relative;
}
// 在有待办事项的日期格子后添加小圆点
.hastate::after {
content: "";
position: absolute;
bottom: 2px;
// 绝对居中
left: 50%;
margin-left: -4px;
display: block;
width: 8px;
height: 8px;
border-radius: 8px;
}
.unfinish::after {
background-color: #d8d808;
}
.finish::after {
background-color: green;
}
// 选中后样式为圆形
.custom-calendar:deep(.is-selected) {
border-radius: 37px;
}
// 设置弹窗样式
.custom-calendar:deep(.toolTip) {
display: none;
position: absolute;
z-index: 999;
width: 80px;
background-color: white;
border: 1px solid #409EFF;
line-height: 30px;
}
// 只有被选中的才弹出弹窗
.custom-calendar:deep(.is-selected .toolTip) {
display: block;
}
</style>
dateTd.vue
<script setup>
import { defineProps } from 'vue'
import { h } from 'vue'
const props = defineProps({
datelist: {
type: Array,
default: () => []
},
day: {
type: String,
required: true
}
})
// 利用render函数动态创建元素
const render = () => {
// 类名对象初始化
const classObj = {}
// 只提取年月日中的“日”
const currentDay = props.day.split('-')[2]
// 记录当前日期是否有待办事项
let hasInFlag = false
// 记录待办事项
let list = []
// 根据待办事项列表 生成li列表项的方法
function createList(list){
let arr = []
console.log(list)
for (let item of list){
arr.push(h('p',{},item))
}
return arr
}
// 遍历,添加类名(如果待办列表中有,则给该日期加“hastate”类,然后如果完成就加“finish”类,否则加"unfinish")
props.datelist.forEach(item => {
if (item.time === props.day) {
hasInFlag = true
list = item.list
classObj.hastate = true
classObj[item.state === 'finish' ? 'finish' : 'unfinish'] = true
}
})
if(!hasInFlag){
return h('div', {
class: classObj
}, currentDay)
}else{
return h('div', {
class: {
...classObj,
}
}, [
currentDay,
h('div', {
class:{
toolTip: true
}
},[
...createList(list)
])
])
}
}
</script>
<template>
<render />
</template>
<style scoped>
</style>
效果: