Vue实现万年日历的示例详解
作者:qb
又是一个老生常谈的功能,接下来会从零实现一个万年日历,从布局到逻辑,再到随处可见的打卡功能。文中的示例代码简洁易懂,需要的可以参考一下
前言
又是一个老生常谈的功能,接下来会从零实现一个万年日历,从布局到逻辑,再到随处可见的打卡功能。涉及的内容有:
- 日历的布局
- 日期数据的产生
- 年月的变化
- 连续最长打卡日期
- 补卡逻辑
1.日历的布局
<template> <div class="calendar"> <!-- 年份和日期变化 --> <div class="select"> <span class="select-icon select-sub" @click="changeYear(-1)">《</span> <span class="select-icon select-sub select-month" @click="changeMonth(-1)"><</span> <span>{{ currentMonth }}</span> <span class="select-icon select-add select-month" @click="changeMonth(+1)">></span> <span class="select-icon select-add" @click="changeYear(+1)">》</span> </div> <!-- 日历标题 --> <ul class="calendar-header"> <li v-for="(item, index) in headerData" :key="index" class="calendar-header-item"> <span>{{ item }}</span> </li> </ul> <!-- 日期内容 --> <div class="calendar-box"> <div class="calendar-box-item" :class="{ delMark: !hasRemarkDays.includes(item) }" v-for="(item, index) in baseDateList" :key="index"> <!-- 最高层级显示圆形日期 --> <div class="item-wrap" :class="{ hasMarked: hasRemarkDays.includes(item) }" @click="toRemark(item, currentDate)"> <span class="item-date">{{ item ? item : "" }}</span> <span class="item-patch" v-if="isShowPatch && !hasRemarkDays.includes(item) && item && item < currentDate">补卡</span> <span class="item-current" v-if="currentDate == item"><i></i></span> <span class="item-continue" v-if="continueMarkObj.maxDay == item">连续{{ continueMarkObj.maxLen }}天</span> </div> <!-- 全遮罩背景淡橙色背景 --> <span class="all-mark" v-if="hasRemarkDays.includes(item)"></span> <!-- 左右遮罩,如果左边无打卡,遮住;如果右边无打卡,遮住 --> <span class="left-mark" v-if="index % 7 === 0 || !hasRemarkDays.includes(item - 1)"></span> <span class="right-mark" v-if="index % 7 === 6 || !hasRemarkDays.includes(item + 1)"></span> </div> </div> </div> </template>
2.日期数据的产生
computed: { // 返回当前的实际天数,从周一算起 baseDateList() { let date = new Date(this.currentMonth); let monthFlag = date.getMonth() + 1; let yearFlag = date.getFullYear(); let currentData = date.getDay(); let dayLength = new Date(yearFlag, monthFlag, 0).getDate(); // 周一之前的补0 let dateBaseData = []; for (let i = 0; i < currentData; i++) { dateBaseData.push(0); } // 周一之后的实际填写 for (let i = 0; i < dayLength; i++) { dateBaseData.push(i + 1); } return dateBaseData; }, }
其中currentData表示今天是周几,在此之前的数组都补充为0,dayLength表示这月共多少天,然后,全部push进日期数据中。通过flex布局中的flex-wrap: wrap;让其自动换行即可。
3.年月的变化
// 修改年:当type = +1时,表示加一年,为-1时反之 changeYear(type) { let time = new Date(this.currentMonth); let year = time.getFullYear() + type; let month = time.getMonth() + 1; this.currentMonth = `${year}-${month}`; }, // 修改月:当type = -1时,表示加一月,为-1时次之 changeMonth(type) { let time = new Date(this.currentMonth); let year = time.getFullYear(); let month = time.getMonth() + 1; if (month === 12 && type > 0) { // 12月,并且是加的情况,年加1,月变为1 year++ month = 1 } else if(month === 1 && type < 0) { // 1月,并且是减的情况,年减1,月变为12 year-- month = 12 } else { // 其他情况,直接变化变量type的大小 month += type } this.currentMonth = `${year}-${month}`; },
引入type,既是加减的标志位,又是变量的大小。
4.连续最长打卡日期
getMaxDay() { let arr = this.hasRemarkDays; let buffChild = []; let max = []; for (let i = 0; i < arr.length; i++) { // 如果子集不连续了,重新进行赋值和更新 if (buffChild.length && arr[i] - buffChild[buffChild.length - 1] > 1) { max = max.length > buffChild.length ? [...max] : [...buffChild]; buffChild = []; buffChild.push(arr[i]); continue; } // 如果不是,直接进行累计 buffChild.push(arr[i]); } return max.length > buffChild.length ? [...max] : [...buffChild]; },
假设max是最大的数组,那么,每次找到连续的数组都max进行对比并更新。在节点中,通过:class="{ hasMarked: hasRemarkDays.includes(item) }"的方式,为其添加hasMarked的样式。
5.补卡日期
补卡逻辑很简单,当前日期之前的日期,均可进行补卡,然后,在触发的方法中,通过$emit的方式向父组件传递需要补卡的日期,并触发补卡逻辑。
总结:日历的实现主要是应用JavaScript中Date对象的api和经典的flex布局,而最长连续打卡是常规算法最长连续子序列的实现方式之一。
到此这篇关于Vue实现万年日历的示例详解的文章就介绍到这了,更多相关Vue万年日历内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!