首页 教程 Web前端 详解怎么使用vue封装一个自定义日历组件

详解怎么使用vue封装一个自定义日历组件

怎么开发一个自定义日历的vue组件,下面本篇文章就手把手教你如何封装一个自定义日历组件,希望对大家有所帮助!

详解怎么使用vue封装一个自定义日历组件

众所周知啊,一般来说,如果项目中有需要用到日历组件,往往是找第三方UI库中的组件来使用,或者是找现成的其他第三方插件。对于很多小伙伴来说,第一眼看到日历组件,下意识的就会觉得很复杂,无从下手。但是当我阅读了这个日历插件的源码之后,发现并没有我想象中的复杂。我以前傻傻得认为,想要做一个日历组件,得需要把距离现在年份前后至少十年的日历数据都获取到,然后才能进行下一步的开发。

然而,在我尝试着阅读了dycalendar.js这个库的源码之后,一方面感觉自己太笨了,把问题想得太复杂了。另外也感慨作者思路之清晰。看完之后感觉受益匪浅。

在将作者的思路逻辑梳理完毕后,我依据这个思路开发了一个vue组件。如下图所示:

详解怎么使用vue封装一个自定义日历组件

接下来,就随着我一起看看如何开发一个自己的日历组件吧。

核心代码实现

1、梳理思路

获取到目标日期数据

获取到当前日期的各项重要属性,诸如当前年当前月当前日期当前星期几当前月一共有几天当前月的第一天对应的是星期几上个月总共有多少天等。

根据这些属性,来生成具体的日历日期数据列表,然后将其循环渲染到模板中。

当切换月份的时候,获取到新的目标日期对应的各项关键数据。vue检测到日历属性变化之后,通知页面进行更新。

2、初始化所需要的数据

一般来说,成熟的日历组件,日期都是一个双向绑定的变量。为了方便使用,我们也采用双向绑定的方式。

<script setup> import { reactive, ref, computed, watch } from "vue"; const props = defineProps({   modelValue: Date, }); const emits = defineEmits(["update:modelValue"]); /**  * 最小年份  */ const MIN_YEAR = 1900; /**  * 最大年份  */ const MAX_YEAR = 9999; /**  * 目标日期  */ const targetDate = ref(props.modelValue);

接下来,我们还需要初始化一些常量用来表示月份和日期:

/**  * 有关月度的名称列表  */ const monthNameList = {   chineseFullName: [     "一月",     "二月",     "三月",     "四月",     "五月",     "六月",     "七月",     "八月",     "九月",     "十月",     "十一月",     "十二月",   ],   fullName: [     "January",     "February",     "March",     "April",     "May",     "June",     "July",     "August",     "September",     "October",     "November",     "December",   ],   mmm: [     "Jan",     "Feb",     "Mar",     "Apr",     "May",     "Jun",     "Jul",     "Aug",     "Sep",     "Oct",     "Nov",     "Dec",   ], }; /**  * 有关周几的名称列表  */ const dayNameList = [   {     chineseFullName: "周日",     chineseShortName: "日",     fullName: "Sunday",     shortName: "Sun",     dayNumber: 0,   },   {     chineseFullName: "周一",     chineseShortName: "一",     fullName: "Monday",     shortName: "Mon",     dayNumber: 1,   },   {     chineseFullName: "周二",     chineseShortName: "二",     fullName: "Tuesday",     shortName: "Tue",     dayNumber: 2,   },   {     chineseFullName: "周三",     chineseShortName: "三",     fullName: "Wednesday",     shortName: "Wed",     dayNumber: 3,   },   {     chineseFullName: "周四",     chineseShortName: "四",     fullName: "Thursday",     shortName: "Thu",     dayNumber: 4,   },   {     chineseFullName: "周五",     chineseShortName: "五",     fullName: "Friday",     shortName: "Fri",     dayNumber: 5,   },   {     chineseFullName: "周六",     chineseShortName: "六",     fullName: "Saturday",     shortName: "Sat",     dayNumber: 6,   }, ];

接下来,准备几个vue的响应式数据:

/**  * 今日  */ const today = new Date(); /**  * 日历的各项属性  */ const calendarProps = reactive({   target: {     year: null,     month: null,     date: null,     day: null,     monthShortName: null,     monthFullName: null,     monthChineseFullName: null,     firstDay: null,     firstDayIndex: null,     totalDays: null,   },   previous: {     totalDays: null,   }, }); /**  * 用于展现的日历数据  */ const calendarData = ref([]);

3、初始化日历的各项属性

接下来,通过setCalendarProps方法获取日历的各个属性,逐个填充calendarProps中的数据:

function setCalendarProps() {   if (!targetDate.value) {     targetDate.value = today;   }   // 获取目标日期的年月日星期几数据   calendarProps.target.year = targetDate.value.getFullYear();   calendarProps.target.month = targetDate.value.getMonth();   calendarProps.target.date = targetDate.value.getDate();   calendarProps.target.day = targetDate.value.getDay();   if (     calendarProps.target.year < MIN_YEAR ||     calendarProps.target.year > MAX_YEAR   ) {     console.error("无效的年份,请检查传入的数据是否是正常");     return;   }   // 获取到目标日期的月份【中文】名称   let dateString;   dateString = targetDate.value.toString().split(" ");   calendarProps.target.monthShortName = dateString[1];   calendarProps.target.monthFullName =     monthNameList.fullName[calendarProps.target.month];   calendarProps.target.monthChineseFullName =     monthNameList.chineseFullName[calendarProps.target.month];   // 获取目标月份的第一天是星期几,和在星期几中的索引值   const targetMonthFirstDay = new Date(     calendarProps.target.year,     calendarProps.target.month,     1   );   calendarProps.target.firstDay = targetMonthFirstDay.getDay();   calendarProps.target.firstDayIndex = dayNameList.findIndex(     (day) => day.dayNumber === calendarProps.target.firstDay   );   // 获取目标月份总共多少天   const targetMonthLastDay = new Date(     calendarProps.target.year,     calendarProps.target.month + 1,     0   );   calendarProps.target.totalDays = targetMonthLastDay.getDate();   // 获取目标月份的上个月总共多少天   const previousMonth = new Date(     calendarProps.target.year,     calendarProps.target.month,     0   );   calendarProps.previous.totalDays = previousMonth.getDate(); }

需要注意的一个知识点是,在获取本月多少天和上个月多少天的时候,都将date值设置为了0。这是因为当date值为0的时候,返回的Date对象是上个月的最后一天。所以说,为了获取本月多少天,需要将本月的month值加1

执行这个方法之后,此时calendarProps的值为:

详解怎么使用vue封装一个自定义日历组件

4、根据日历属性生成日历日期的数据

当我们已经知道本月第一天对应的周几索引值本月一共有多少天上个月一共有多少天这三个核心数据之后,就可以开始生成对应的日历数据了。

思路如下

由于大部分情况下,本月的第一天不是从头开始的,之前的部分是上个月的日期。所以第一行要单独进行处理。

设置一个公用的date数值,初始值设置为1。然后从本月第一天对应的周几索引值开始进行递增。本月之前的日期和之后的日期设置一个算法进行计算。

为了方便之后进行日期切换、样式区分,将生成的数据加工成一个对象,其中包含日期类型——dateType,表示是本月还是上月还是下月;

/**  * 生成日历的数据  */ function setCalendarData() {   let i;   let date = 1;   const originData = [];   const firstRow = [];   // 设置第一行数据   for (i = 0; i <= 6; i++) {     // 设置目标月份之前月份的日期数据     if (i < calendarProps.target.firstDayIndex) {       const previousDate =         calendarProps.previous.totalDays -         calendarProps.target.firstDayIndex +         (i + 1);       firstRow.push({         dateObj: new Date(           calendarProps.target.year,           calendarProps.target.month - 1,           previousDate         ),         dateNumber: previousDate,         dateType: "previous"       });     } else {       // 设置目标月份当月的日期数据       firstRow.push({         dateObj: new Date(           calendarProps.target.year,           calendarProps.target.month,           date         ),         dateNumber: date,         dateType: "current"       });       date++;     }   }   originData.push(firstRow);   // 设置后面五行的数据   for (let j = 0; j <= 4; j++) {     const rowData = [];     for (let k = 0; k <= 6; k++) {       // 设置目标月份剩下的日期数据       if (date <= calendarProps.target.totalDays) {         rowData.push({           dateObj: new Date(             calendarProps.target.year,             calendarProps.target.month,             date           ),           dateNumber: date,           dateType: "current"         });       } else {         // 设置目标月份下个月的日期数据         const nextDate = date - calendarProps.target.totalDays;         rowData.push({           dateObj: new Date(             calendarProps.target.year,             calendarProps.target.month + 1,             nextDate           ),           dateNumber: nextDate,           dateType: "next"         });       }       date++;     }     originData.push(rowData);   }   calendarData.value = originData; }

至此,这个日历组件的核心部分的逻辑就已经实现了。你看,是不是很简单?

接下来,我们只需要根据calendarData中的数据渲染出相应的html模板和添加上样式就可以了。

5、添加模板和样式部分

一般来说,日历组件都是网格状的结构,所以我选择table的方式进行渲染。不过你要是问我还有没有别的方式,那还是有的,比如使用flex布局或者grid布局,但是如果采用这种方式的话,calendarData的数据结构就不是现在这个样子了。

dom结构如下图:

详解怎么使用vue封装一个自定义日历组件

至于按钮边框的流动效果,是我参照苏苏的文章做的,详情请见:

Clip-path实现按钮流动边框动画 juejin.cn/post/719877…

然后剩下的样式部分,即兴发挥或者根据UI设计图绘制即可。想必各位都领教过UI姐姐们精美的设计图吧(嘻嘻

具体的代码部分就不贴在文章中了,如有需要可以直接查看下方的完整源码

gitee.com/wushengyuan…

结语

有些感觉很麻烦的组件,可能核心逻辑往往不是那么复杂。有些时候,可能仅仅是需要一些耐心,将代码一行一行的拆解出来阅读,理清楚其中的思路。

评论(0)条

提示:请勿发布广告垃圾评论,否则封号处理!!

    猜你喜欢
    【MySQL】用户管理

    【MySQL】用户管理

     服务器/数据库  2个月前  2.04k

    我们推荐使用普通用户对数据的访问。而root作为管理员可以对普通用户对应的权限进行设置和管理。如给张三和李四这样的普通用户权限设定后。就只能操作给你权限的库了。

    Cursor Rules 让开发效率变成10倍速

    Cursor Rules 让开发效率变成10倍速

     服务器/数据库  2个月前  1.12k

    在AI与编程的交汇点上,awesome-cursorrules项目犹如一座灯塔,指引着开发者们驶向更高效、更智能的编程未来。无论你是经验丰富的老手,还是刚入行的新人,这个项目都能为你的编程之旅增添一抹亮色。这些规则文件就像是你私人定制的AI助手,能够根据你的项目需求和个人偏好,精确地调教AI的行为。突然间,你会发现AI不仅能理解Next.js的最佳实践,还能自动应用TypeScript的类型检查,甚至主动提供Tailwind CSS的类名建议。探索新的应用场景,推动AI辅助编程的边界。

    探索Django 5: 从零开始,打造你的第一个Web应用

    探索Django 5: 从零开始,打造你的第一个Web应用

     服务器/数据库  2个月前  1.05k

    Django 是一个开放源代码的 Web 应用程序框架,由 Python 写成。它遵循 MVT(Model-View-Template)的设计模式,旨在帮助开发者高效地构建复杂且功能丰富的 Web 应用程序。随着每个版本的升级,Django 不断演变,提供更多功能和改进,让开发变得更加便捷。《Django 5 Web应用开发实战》集Django架站基础、项目实践、开发经验于一体,是一本从零基础到精通Django Web企业级开发技术的实战指南《Django 5 Web应用开发实战》内容以。

    MySQL 的mysql_secure_installation安全脚本执行过程介绍

    MySQL 的mysql_secure_installation安全脚本执行过程介绍

     服务器/数据库  2个月前  1k

    mysql_secure_installation 是 MySQL 提供的一个安全脚本,用于提高数据库服务器的安全性

    【MySQL基础篇】概述及SQL指令:DDL及DML

    【MySQL基础篇】概述及SQL指令:DDL及DML

     服务器/数据库  2个月前  447

    数据库是长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。数据库不仅仅是数据的简单堆积,而是遵循一定的规则和模式进行组织和管理的。数据库中的数据可以包括文本、数字、图像、音频等各种类型的信息。

    Redis中的哨兵(Sentinel)

    Redis中的哨兵(Sentinel)

     服务器/数据库  2个月前  293

    ​ 上篇文章我们讲述了Redis中的主从复制(Redis分布式系统中的主从复制-CSDN博客),本篇文章针对主从复制中的问题引出Redis中的哨兵,希望本篇文章会对你有所帮助。