这是一个没有套路的前端博主,热衷各种前端向的骚操作,经常想到哪就写到哪,如果有感兴趣的技术和前端效果可以留言~博主看到后会去代替大家踩坑的~
主页: oliver尹的主页
格言: 跌倒了爬起来就好~
目录
[4.1 本月日期计算](#4.1 本月日期计算)
[4.2 上月日期补齐](#4.2 上月日期补齐)
[4.3 下个月日期补齐](#4.3 下个月日期补齐)
[4.4 转化成二位数组](#4.4 转化成二位数组)
[4.5 使用v-for生成日历](#4.5 使用v-for生成日历)
[4.6 农历,假期等](#4.6 农历,假期等)
一、前言
近来项目中需要用到一个日历组件,由于找了找没有找到合适的,因此决定自己简单动手做一个,项目中肯定比这个复杂的多,这里只是给各个小伙伴一个思路或者说是开发的方向~
耐心看完,也许有所收获......(PS:如果要源码留下邮箱,博主看到后会发送的~)
二、效果图
大致效果图如下:
三、核心思路
日历的核心个人觉得就是 算出指定月份的第一天是星期几 ,为什么?因为,从形态上来说 日历展现出来就是这么一个7*6的矩形,如果知道1号是星期几,那么就可以把这个矩形在1号之前的日期补齐,最后一天后的日期也可以补齐;
因此,日历的计算一共可以分为三段:
- 第一段,本月计算,计算的是当前1号是周几,接着计算本月一共多少天;
- 第二段,上月计算,计算1号之前一共有几天;
- 第三段,下月计算,计算的是本月之后一共有多少天;
这样,三段连在一起就是一组完整的当前数组,如果还不清楚,那再具体举一个例子吧,以2023年7月为例,7月的1号是周六,那么它应该在矩形的这个位置
那么在周六前面应该还有6天,需要取6月的最后6天来补齐这个矩形的前半段;
接着计算7月最后一天,7月31号是周一,那么它应该在这个位置
这样一个完整的矩形(日历)在数据的角度上来说就齐全了;
四、代码实现
4.1 本月日期计算
关于本月日期的计算分为两部分,第一部分,获取当前日期 ,第二部分 获取本月1号的星期
先是获取当前日期
// 获取当前日期
function getCurrent() {
const date = new Date();
return [date.getFullYear(), date.getMonth() + 1, date.getDate()];
}
先是获取本月,很简单
/**
* 获取公历某一天是星期几
* @param {number} y 年
* @param {number} m 月
* @param {number} d 日
* @returns {number} 返回星期数字[0-6]
*/
function solarWeek(y: number, m: number, d: number) {
let date = new Date(y, m - 1, d);
let week = date.getDay();
return week;
}
const currentDate = getCurrent()
// 使用
this.solarWeek(currentDate[0], currentDate[1], 1)
这样我们就获取到了第一天是周几了;
4.2 上月日期补齐
关于上月数据获取有一点是需要注意的是,那就是如果当前是1月份,那么要获取的上上一年度的12月份,因此,在计算前首先要对当前月份进行确定
// 判断年度
const y = date[1] === 1 ? date[0] - 1 : date[0];
// 判断月份
const m = date[1] === 1 ? 12 : date[1] - 1;
接着就是判断需要补齐日期的数量了,很简单,我们只需要根据周几来判断,如果是周六,那么就是补齐6天(周一到周五,加上周日),如果是周五,那么就是补齐5天(周一到周四,加上周日);
/**
* 获取当月前面需补齐的数组
*/
beforDays(date: number[], last: number) {
const y = date[1] === 1 ? date[0] - 1 : date[0];
const m = date[1] === 1 ? 12 : date[1] - 1;
const arr: dateBase[] = [];
for (let i = 0; i < last; i++) {
arr.push({...});
}
return arr;
}
这个last就是就是1号的星期,也就是需要循环的次数;
4.3 下个月日期补齐
接着补齐下个月,和上个月计算一样,首先要判断当前是不是12月,如果是12月,那么下个月就是下一年的1月份
const y = date[1] === 12 ? date[0] + 1 : date[0];
const m = date[1] === 12 ? 1 : date[1] + 1;
接着,由于我们知道了当前月的天数和上个月补齐的天数,那么自然下个月需要补齐多少天也能算出来,42-当前月天数-上个月天数=下个月补齐天数;
/**
* 获取当月后面需补齐的数组
*/
afterDays(day: dateBase[], date: number[]) {
const arr: dateBase[] = [];
const y = date[1] === 12 ? date[0] + 1 : date[0];
const m = date[1] === 12 ? 1 : date[1] + 1;
for (let i = 1; i < 42 - day.length + 1; i++) {
arr.push({...});
}
return [...day, ...arr];
}
到这里,我们就可以获得一个长度为42的数组,这个数组代表从上月的倒数某一天到这个月再到下个月正数的某一天,一共42天;
4.4 转化成二位数组
为什么要转成二位数组,原因很简单,因为我们的这个日历从布局上来说它就是一个二位数组....一共有6行,每行都7格,对吧,因此需要转一下,转的代码也很简单
const dateArray = [];
for (let row = 0; row < 6; row++) {
dateArray.push(allDate.splice(0, 7));
}
4.5 使用v-for生成日历
基本数据整理完成后,只需要通过Vue的v-for指令去批量生成格子即可;
<template v-if="TBody.length">
<div
class="t-calendar-row"
v-for="(item, index) in TBody"
:key="index"
>
<div
class="t-calendar-col"
v-for="(col, colIdx) in item"
:key="colIdx"
>
<CalendarItem
:col="col"
:time="selectedTime"
@changeTargetDate="changeDate"
></CalendarItem>
</div>
</div>
</template>
<template v-else>
<div class="no-date">抱歉,暂无数据</div>
</template>
至于每一个格子的样式,可以根据需求进行定制化;
4.6 农历,假期等
真正的日历肯定远不止这些基础数据,肯定还有包括:农历,节日,假期等等不同标注,这些都可以在计算的时候将当前日期对应的值一并算好,在通过v-for渲染的时候将对应的CSS样式渲染上去即可,比如在进行数据push的时候可以:
arr.push({
// 日期
title: w - i,
// 是否本月
isCurrent: false,
// 是否节假日
isHolidays: DateClass.getHolidays([y, m, w - i]),
date: `${y}-${clockFactory(m)}-${clockFactory(w - i)}`,
// 阴历
lunars: lun,
isNow: false,
// 阳历节日,如国庆,元旦
solarDay: DateClass.getSolarDay(m, w - i),
// 农历节日,如中秋,春节,端午
lunarDay: DateClass.getlunarDay(Number(l[0]), Number(l[1]), Number(l[2])),
// 生肖
animal: DateClass.getAnimal(Number(l[0])),
// 星座
astro: DateClass.toAstro(y, m, w - i),
// 节气
term: getTerm(Number(l[0]), m, w - i)
});
等等都是可以在数据处理的时候一并处理好;
五、小结
其实知道了日历的生成原理后,居然觉得日历也没有那么难实现,就是稍微麻烦了点,最终要实现成什么样子还需要根据实际需求而定;