前言
业务需要,做一个日历效果,但是某些ui框架,在使用过程中满足不了需求,所以只能自己造轮子了,于是有了这个东西,所以就自己写了这么一个东西,记录一下。
一、效果图
二、逻辑梳理
仿照百度搜索出来的日历,可以发现不仅有指定当前月份的号数
而且可能还有上一个/下一个月的号数
,但是总的来说一共展示出来的数据有7 * 6 = 42(天),所以我们需要先得到指定月份要展示的这42天
是哪些日期号数,然后调整好页面布局依次循环展示这42天便可。
三、代码实现
定义一个方法获得42天是哪42天
visibleCalendar() {
// 获取今天的日期并将时间设置到 0分0秒0点
const today = new Date()
today.setHours(0)
today.setMinutes(0)
today.setSeconds(0)
today.setMilliseconds(0)
const calendarArr = []
// 获取当前月份第一天
const currentFirstDay = new Date(this.time.year, this.time.month, 1)
// 获取第一天是周几,注意周日的时候getDay()返回的是0,要做特殊处理
const weekDay =
currentFirstDay.getDay() === 0 ? 7 : currentFirstDay.getDay()
// 用当前月份第一天减去周几前面几天,就是看见的日历的第一天
const startDay = currentFirstDay - (weekDay - 1) * 24 * 3600 * 1000
// 我们统一用42天来显示当前显示日历
for (let i = 0; i < 42; i++) {
const date = new Date(startDay + i * 24 * 3600 * 1000)
calendarArr.push({
date: new Date(startDay + i * 24 * 3600 * 1000),
year: date.getFullYear(),
month: date.getMonth(),
day: date.getDate(),
// 是否在当月
thisMonth:
date.getFullYear() === today.getFullYear() &&
date.getMonth() === today.getMonth()
? 'thisMonth'
: '',
// 是否是今天
isToday:
date.getFullYear() === today.getFullYear() &&
date.getMonth() === today.getMonth() &&
date.getDate() === today.getDate()
? 'isToday'
: 'notToday',
// 是否在今天之后
afterToday: date.getTime() >= today.getTime() ? 'afterToday' : '',
// 得到日期字符串
timeStr: date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' 00:00:00',
// 得到date类型日期
timeDate: date
})
}
return calendarArr
}
上面代码中time
的定义
time
对象指的是要查询的年份月份。
time: {
year: new Date().getFullYear(),
month: new Date().getMonth()
}
下面的内容是重点
前端代码核心讲解(上面的看不明白没关系,下面手把手教你去实现一个日历功能)
核心在于 v-for=“(ite, idx) in visibleCalendar”, 这个visibleCalendar
是上面最开始定义的获取42天的方法,我们只需要循环遍历这个方法的返回结果就行了。因为visibleCalendar 这个function中定义了day属性,所以循环时只需要 ite.day
就能拿到这一天的号数
。至于效果图中的星期一到星期天与下面的日期号数对齐排列是怎么实现的呢?下面是代码开撸(把代码cv到自己页面合适的位置上能直接用):
日历星期几的展示,如下:
// vue页面中data中定义
top: ['一', '二', '三', '四', '五', '六', '日']
// vue页面使用
<div class="calendar-box">
<!-- 日历表头 -->
<div class="calendarTop">
<div class="calendar-tag" v-for="ite in top" :key="ite">
<span>{{ `星期` + ite }}</span>
</div>
</div>
<!-- 具体内容 -->
<div class="calendarBottom">...</div>
</div>
// vue style中的样式
.calendar-box {
width: 100%; // 这里日历总的大小 取决于你自己的页面布局
height: 100%;
display: flex; // flex流式布局
flex-direction: column; // flex流式布局 竖向排列
}
.calendarTop {
width: 100%;
height: 30px; // 根据自己的页面固定好高度
display: flex; // flex流式布局 默认是横向排列
justify-content: space-evenly; // flex流式布局 相等间距
}
.calendar-tag {
width: 14%;
height: 30px; // 高度根据自己页面调整
}
.calendar-tag span {
background-color: rgb(205, 237, 248); // 蓝底
width: 100%;
height: 30px; // 高度根据自己页面调整
font-weight: bold; // 字体加粗
}
.calendarBottom {
width: 100%;
height: calc(100% - 30px); // 高度是父级div 的高度减去.calendarTop 的高度
}
效果如下:
然后是具体的内容对应,如下:
// vue computed中需要定义的方法
computed: {
visibleCalendar() {
// 获取今天的日期并将时间设置到 0分0秒0点
const today = new Date()
today.setHours(0)
today.setMinutes(0)
today.setSeconds(0)
today.setMilliseconds(0)
const calendarArr = []
// 获取当前月份第一天
const currentFirstDay = new Date(this.time.year, this.time.month, 1)
// 获取第一天是周几,注意周日的时候getDay()返回的是0,要做特殊处理
const weekDay =
currentFirstDay.getDay() === 0 ? 7 : currentFirstDay.getDay()
// 用当前月份第一天减去周几前面几天,就是看见的日历的第一天
const startDay = currentFirstDay - (weekDay - 1) * 24 * 3600 * 1000
// 我们统一用42天来显示当前显示日历
for (let i = 0; i < 42; i++) {
const date = new Date(startDay + i * 24 * 3600 * 1000)
calendarArr.push({
date: new Date(startDay + i * 24 * 3600 * 1000),
year: date.getFullYear(),
month: date.getMonth(),
day: date.getDate(),
// 是否在当月
thisMonth:
date.getFullYear() === today.getFullYear() &&
date.getMonth() === today.getMonth()
? 'thisMonth'
: '',
// 是否是今天
isToday:
date.getFullYear() === today.getFullYear() &&
date.getMonth() === today.getMonth() &&
date.getDate() === today.getDate()
? 'isToday'
: 'notToday',
// 是否在今天之后
afterToday: date.getTime() >= today.getTime() ? 'afterToday' : '',
// 得到日期字符串,格式 yyyy-MM-dd 00:00:00
timeStr: date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' 00:00:00',
// 得到date类型日期
timeDate: date
})
}
return calendarArr
}
}
// vue页面使用
<div class="calendar-box">
<!-- 日历表头 -->
<div class="calendarTop">
<div class="calendar-tag" v-for="ite in top" :key="ite">
<span>{{ `星期` + ite }}</span>
</div>
</div>
<!-- 具体内容 -->
<div class="calendarBottom">
<div class="calendar-content">
<div
class="calendar-item"
:class="ite.isToday"
v-for="(ite, indx) in visibleCalendar"
:key="indx">
<!-- 号数 -->
<div class="calendar-item-number">{{ ite.day }}</div>
<!-- 号数对应的需要展示的内容 -->
<div class="calendar-item-content">....</div>
</div>
</div>
</div>
</div>
// vue style样式
.calendar-content {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap; // 超出宽度换行
justify-content: space-evenly;
}
.calendar-item {
width: 14%; // 这个宽度需要和 .calendar-tag 的宽度保持一致(和星期几对应)
height: 16%; // 高度根据实际需求调整
display: flex; // flex流式布局
}
.calendar-item-number {
width: 100%;
height: 20px;
}
.calendar-item-content {
width: 100%;
height: calc(100% - 20px);
}
.isToday {
border: 1px solid #50bfff;
}
效果如下
四、作者的废话
visibleCalendar
这个方法按理来说只要用户改变了月份
,日历对应的内容就该变化,所以建议直接把visibleCalendar
这个方法定义到 vue的computed
函数中。
效果图中的日期选择框是用的Antd Design Vue 中的组件,这个主要是我用来改变time对象的值,所以什么时候改变time对象的值完全取决于你用什么东西让用户去选择日期。至于关于自定义日历的相关颜色和大小需要根据自己的情况调整。