vue 日历选择组件
日历作为手机端一个常用的控件,想必大家多多少少都会用到,现在github上有很多优秀的日历控件,但可能有些时候并不能满足我们日常的业务开发需求,话不多说,下面简单实现一个日历的选择功能!
链接和图片展示
(●'◡'●) (●'◡'●)(●'◡'●)(●'◡'●)(●'◡'●)(●'◡'●)演示示例链接
核心代码分析
首先,我们明确一点,日历,就是时间的选择,有了时间对应的数据,就解决了绝大部分问题,简单分析下,无非就是对应12个月份,然后每个月份对应有几个星期,下面代码实现下,首先创造月份数据(下面代码部分日期方法封装在utils中,完整代码里面有):
buildData() {
let startDate = new Date();
let endDate = new Date().addMonths(12); // 月份加12
let dateData = [];
while (startDate <= endDate) {
let month = [];
dateData.push(month);
startDate.addMonths(1);
}
return dateData;
},
现在对应的月份数据已经有了,当然都是空数组,没什么用,下面我们再来创造对应月份的数据,分析下,每月的具体日期是从1号开始,然后让他循环加1天,只要判断的他的月份等于这个月就证明他是这个月的日期,同时周一到周日怎么跟我们的日期一一对应呢?在这里,我们明确一个问题,本文日历对应月份只展示该月中的数据(也就是1号到最后一天),不是该月的数据为“”。明确了之后就容易了,首先我们知道日期getDay()这个方法是可以取得对应周几的(注意:周日该方法返回的是0),这样我们先判断该月的1号是周几?这样第一周的数据(跟周几一一对应)就可以出来了,
new Array(time.getDay() === 0 ? 6 : time.getDay() - 1).fill('')
(不是该月份的值为空),该月的1号对应周几确定了后,下面对应日期加1循环判断就可以了, 还有一个问题,就是该月的最后一天结束后加入对应该周的数据不满足7天,我们要补充完整。
下面是完整代码:
// 创建日期月份数据
buildMonthDate(time) {
let monthDate = [];
let month = time.getMonth();
let date = new Date(time);
var t = time.getDay() === 0 ? 6 : time.getDay() - 1;
let dateList = new Array(t).fill('');
while (date.getMonth() === month) {
let dateInfo = {};
if (dateList.length === 7) {
monthDate.push(dateList);
dateList = [];
}
dateInfo = {
date: date.format('yyyy/MM/dd'),
}
dateList.push(dateInfo);
date.addDays(1);
}
// 不满7 补充完整
if (dateList.length) {
for (let i = dateList.length; i < 7; i++) {
dateList.push('');
}
monthDate.push(dateList);
dateList = null;
}
return {
month: monthDate,
title: `${time.getFullYear()}年${time.getMonth() + 1}月`,
monthId: `${time.getFullYear()}${time.getMonth() + 1}`
};
},
现在对应的数据已经有了,剩下就是画下对应的页面,这个本文就不做详细介绍了,感兴趣的话可以看详细代码(末尾有地址)。
其他功能完善
下面我们来完善下其他功能,增加节假日、加班日、日期范围限制不可选等,我们完善下代码如下:
// 创建日期月份数据
buildMonthDate(time) {
let monthDate = [];
let month = time.getMonth();
let date = new Date(time);
var t = time.getDay() === 0 ? 6 : time.getDay() - 1;
let dateList = new Array(t).fill('');
while (date.getMonth() === month) {
let dateInfo = {};
if (dateList.length === 7) {
monthDate.push(dateList);
dateList = [];
}
dateInfo = {
text: date.getDate(),
dateTime: date.getTime(),
date: date.format('yyyy/MM/dd'),
tips: ''
}
// 开始日期之前和结束日期之后的日期置灰
if (date < this.startDate || date > this.endDate) {
dateInfo.disable = true;
}
if (this.mode === 'end' && new Date(date) < new Date(this.checkStart)) {
// 返程时间必须大于去程时间
dateInfo.disable = true;
}
// 节日处理
let festival = getFestival(date);
if (festival) {
dateInfo.festival = festival;
dateInfo.text = festival;
}
if (getHolidays(dateInfo.date)) {
// 休息日
dateInfo.relax = true;
dateInfo.tips = '休';
} else if (getWorks(dateInfo.date)) {
// 工作日
dateInfo.fillWork = true;
dateInfo.tips = '班';
}
// 最近日期处理 今天明天 后天
let recentDay = getRecentDay(date);
if (recentDay) {
dateInfo.alias = recentDay;
dateInfo.festival = '';
}
dateList.push(dateInfo);
date.addDays(1);
}
// 不满7 补充完整
if (dateList.length) {
for (let i = dateList.length; i < 7; i++) {
dateList.push('');
}
monthDate.push(dateList);
dateList = null;
}
return {
month: monthDate,
title: `${time.getFullYear()}年${time.getMonth() + 1}月`,
monthId: `${time.getFullYear()}${time.getMonth() + 1}`
};
},
点击选择功能
下面我们来处理下选择日期后的事件,明确下我们支持的几种模式
- 选择开始模式
- 选择结束模式
- 选择范围
分析下对应功能
- 选择开始时间的话没有特殊处理
- 选择结束模式的话要确保只能选择开始时间之后的时间
- 选择范围模式的话,支持点击两次,第一次选择开始时间,第二次选择结束时间(第二次点击需要判断,如果第二次点击小于第一次点击的时间,认为是新的第一次点击)
下面代码实现:
// 点击事件处理
clickHandler(e) {
if (e.currentTarget.dataset.disabled) return;
let date = e.currentTarget.dataset.date;
let t = {
'start': 'clickHandlerStart',
'end': 'clickHandlerEnd',
'range': 'clickHandlerRange'
}
this.selected = new Date(date);
let emitData = this[t[this.mode]](date);
if (!emitData) return;
this.selecteFuc && this.selecteFuc({
date,
BE: emitData
});
},
clickHandlerStart(date) {
this.checkStart = date;
if (compare(date) >= compare(this.checkEnd)) {
this.checkEnd = '';
}
alert(`选择开始时间为${date},结束时间为${this.checkEnd || '--'}`);
return [date, this.checkEnd];
},
clickHandlerEnd(date) {
this.checkEnd = date;
alert(`选择开始时间为${this.checkStart || '--'},结束时间为${date || '--'}`);
return [this.checkStart, date];
},
clickHandlerRange(date) {
if (!this.twoClick) {
this.checkStart = date;
this.checkEnd = '';
} else {
// 点击比较 第二次点击值需要大于第一次点击值
if (compare(date) < compare(this.checkStart)) {
this.twoClick = 0;
this.checkStart = date;
} else {
this.checkEnd = date;
}
}
++this.twoClick;
if (this.twoClick === 1) return false;
this.twoClick = 0;
alert(`选择开始时间为${this.checkStart || '--'},结束时间为${this.checkEnd || '--'}`);
return [this.checkStart, this.checkEnd];
}
以上就是对应功能实现过程,欢迎大家issue。