待办事项日历组件实现

发布于:2025-05-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

待办事项日历组件实现

今天积累一个简易的待办事项日历组件的实现方法。

需求:

  1. 修改样式,变成符合项目要求的日历样式
  2. 日历上展示待办事项提示(有未完成待办:展示黄点,有已完成待办:展示绿点)
  3. 点击有待办事项的日期,会展示待办事项清单

解决方案:

  1. 修改样式:打开浏览器,点击查看组件中要修改的元素的类名,然后进行修改
  2. 代办实现圆点:遍历数组,为有待办事项的日期添加指定的类名,在指定类名的类后用::after 来画出“小圆点”
  3. 动态创建待办事项列表:利用render函数(vue3)里需要导入h函数

这种依据某种逻辑来动态创建元素的场景,个人认为利用render动态创建元素很好用!

具体实施:

  1. 打开浏览器,点击查看组件中要修改的元素的类名,然后进行修改

  2. 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里有着日历所展示的“每一天”的各种关键数据,是个大对象

  3. 定义一个dateTd.vue组件,用于自定义日期展示的格式,传入data.day("xxxx-xx-xx"类型的日期)和dataList(待办事项的列表)

  4. dataTd.vue组件内容:

    • 定义一个render函数,内部使用h函数来动态创建元素
      • 判断如果当前日期在dataList中(说明当前日期有待办事项),就添加hastatefinish/unfinish
        • finish类元素就在后面用::after创建一个绿色圆点
        • unfinish类元素就在后面用::after创建一个黄色圆点
      • 根据当日是否有待办事项,创建不同的元素
        • 没有待办事项:创建一个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)
              ])
            ])
          }
          
  5. 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>



效果:

在这里插入图片描述

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到