Vue实现万年日历的示例详解
qb 人气:0前言
又是一个老生常谈的功能,接下来会从零实现一个万年日历,从布局到逻辑,再到随处可见的打卡功能。涉及的内容有:
- 日历的布局
- 日期数据的产生
- 年月的变化
- 连续最长打卡日期
- 补卡逻辑
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布局,而最长连续打卡是常规算法最长连续子序列的实现方式之一。
加载全部内容