距离上次更新13天了。。。
时间过的可真快
好的,让我们切入正题:
今天我们来做一个DatePicker(时间选择器)
如图:
由于时间紧迫,只实现了传入开始时间和结束时间的功能
使用插件:,
我们先上HTML和CSS代码
HTML+CSS
复制代码// 中间的指示器取消 { {getSelectTime}} 确定
- { {i.name}}
//年份列表
- { {i.name}}
//月份列表
- { {i.name}}
//天列表
- { {i.name}}
//时列表
- { {i.name}}
//分列表
复制代码
data/props里的变量:
props: { startTime: { // 开始时间 required: false }, endTime: { // 结束时间 required: false }},data() { return { scrollParam: { //scroll通用参数 scrollY: true, bounce: true, wheel: { selectedIndex: 0, rotate: 25, wheelWrapperClass: 'wheel-scroll', wheelItemClass: 'wheel-item' } }, dateItem: { yearsItem: [], //年份列表 monthsItem: [], //月份列表 daysItem: [], //天数列表 hoursItem: [], //时列表 minsItem: [] //分列表 }, selectItem: { year: { //当前选择的年份 value: 0, index: 0 }, month: { //当前选择的月份 value: 0, index: 0 }, day: { //当前选择的天 value: 0, index: 0 }, hour: { //当前选择的时 value: 0, index: 0 }, min: { //当前选中的分 value: 0, index: 0 }, } }},复制代码
还有两个import
import BScroll from 'better-scroll'import dateUtils from '@/utils/tools/DateUtils'复制代码复制代码
实现思路
首先我们先列出开发中的预知问题:
- 天数多月份向天数少月份转换问题
- 开始时间和结束时间日期圈定
- 更改年份后同步更改子时间单位
看起来很容易解决,但串联起来不容易
思考了半天,想了这么一种解决思路:
仔细阅读的小伙伴可能就了解了,我这种解决思路是层层递减的。也就是选中我所选择的单位时,只刷新他的子单位列表,不刷新父单位列表。
上代码:
对此我们二次封装了一堆获取日期列表的方法,供大家参考:
getYearsItems(startDate, endDate) { //获取年列表 return dateUtils.getYearItems({ format: '{value}年', startDate: startDate ? startDate : '', endDate: endDate ? endDate : '' })},getMonthItems(currentYear, startDate, endDate) { //获取月列表 return dateUtils.getMonthItems({ format: '{value}月', startDate: startDate ? startDate : '', endDate: endDate ? endDate : '', currentYear: currentYear ? currentYear : '' })},getDaysItems(currentYear, currentMonth, startDate, endDate) { //获取天列表 return dateUtils.getDayItems({ format: '{value}日', startDate: startDate ? startDate : '', endDate: endDate ? endDate : '', currentYear: currentYear ? currentYear : '', currentMonth: currentMonth ? currentMonth : '', })},getHoursItems(currentYear, currentMonth, currentDay, startHour, endHour, startDate, endDate) { return dateUtils.getHourItems({ //获取时列表 format: '{value}时', startDate: startDate ? startDate : '', endDate: endDate ? endDate : '', currentYear: currentYear ? currentYear : '', currentMonth: currentMonth ? currentMonth : '', currentDay: currentDay ? currentDay : '', startHour: startHour ? startHour : 0, endHour: endHour ? endHour : 23 })},getMinsItems(currentYear, currentMonth, currentDay, currentHour, startDate, endDate) { return dateUtils.getMinuteItems({ //获取分列表 format: '{value}分', startDate: startDate ? startDate : '', endDate: endDate ? endDate : '', currentYear: currentYear ? currentYear : '', currentMonth: currentMonth ? currentMonth : '', currentDay: currentDay ? currentDay : '', currentHour: currentHour ? currentHour : '', })},复制代码复制代码
获取时列表那里还需要传入开始时和结束时。空的话默认0时 - 23时
初始化:
mounted() { if (this.startTime && this.endTime) { //如果含有开始和结束时间 this.dateItem.yearsItem = this.getYearsItems(this.startTime, this.endTime); //按照开始结束时间获取年份列表 this.dateItem.monthsItem = this.getMonthItems(new Date(this.startTime).getFullYear(), this.startTime, this.endTime); //同上 this.dateItem.daysItem = this.getDaysItems(new Date(this.startTime).getFullYear(), new Date(this.startTime).getMonth() + 1, this.startTime, this.endTime); //同上 this.dateItem.hoursItem = this.getHoursItems(new Date(this.startTime).getFullYear(), new Date(this.startTime).getMonth() + 1, new Date(this.startTime).getDate(), new Date(this.startTime).getHours(), 23, this.startTime, this.endTime); //同上 this.dateItem.minsItem = this.getMinsItems(new Date(this.startTime).getFullYear(), new Date(this.startTime).getMonth() + 1, new Date(this.startTime).getDate(), new Date(this.startTime).getHours(), this.startTime, this.endTime) //同上 } else { //如果不存在开始结束时间 this.dateItem.yearsItem = this.getYearsItems(); //默认为空 获取当前日期前后100年的时间 this.dateItem.monthsItem = this.getMonthItems(); //默认为空 获取当前日期前后100年的时间 this.dateItem.daysItem = this.getDaysItems(); //默认为空 获取当前日期前后100年的时间 this.dateItem.hoursItem = this.getHoursItems(); //默认为空 获取当前日期前后100年的时间 this.dateItem.minsItem = this.getMinsItems(); //默认为空 获取当前日期前后100年的时间 } this.$nextTick(() => { //data更新后 //初始化当前选择项 this.selectItem.year.value = this.dateItem.yearsItem[0].value; //获取时间列表第一个的value给默认值 this.selectItem.year.index = 0; //初始化日期选择列表的index为0 this.selectItem.month.value = this.dateItem.monthsItem[0].value; this.selectItem.month.index = 0; this.selectItem.day.value = this.dateItem.daysItem[0].value; this.selectItem.day.index = 0; this.selectItem.hour.value = this.dateItem.hoursItem[0].value; this.selectItem.hour.index = 0; this.selectItem.min.value = this.dateItem.minsItem[0].value; this.selectItem.min.index = 0; this.initScroll(); //初始化scroll });},复制代码
注释已经写的很清楚了~~~~
接下来我们以年列表滚动监听事件为注释例子,方便大家理解(initScroll方法):
let self = this; //新建变量重新指向this//年列表滚动let YearsScroll = new BScroll(document.getElementsByClassName("datetime--years")[0], this.scrollParam); //初始化scrollYearsScroll.on('scrollEnd', () => { //scrollEnd监听事件 let selectYear = self.dateItem.yearsItem[YearsScroll.getSelectedIndex()].value; //获取当前选中的年份 let selectMonth = self.dateItem.monthsItem[MonthsScroll.getSelectedIndex()].value; //获取当前选中的月份 let selectDay = self.selectItem.day.value; //获取当前选中的天 self.selectItem.year.value = selectYear; //更新选中的年份 self.selectItem.year.index = YearsScroll.getSelectedIndex(); //更新选中的年份 if (self.startTime && self.endTime) { //如果有开始结束日期 if (new Date(self.startTime).getFullYear() === selectYear) { //如果开始年份===当前选择年份 self.$nextTick(() => { // 重新刷新月份列表 传入开始日期的年份,开始的时间和结束时间 self.dateItem.monthsItem = self.getMonthItems(new Date(self.startTime).getFullYear(), self.startTime, self.endTime); // 更新当前选中月份 self.fixMonthDateBug(); }); self.$nextTick(() => { // 重新刷新天份列表 传入开始日期的年份,开始时间的月份,开始的时间和结束时间 self.dateItem.daysItem = self.getDaysItems(new Date(self.startTime).getFullYear(), new Date(self.startTime).getMonth() + 1, self.startTime, self.endTime); // 更新当前选中天 self.fixDaysDateBug(); }); self.$nextTick(() => { // 重新刷新时列表 传入开始日期的年份,开始时间的月份,开始时间的天份,开始时刻-23时。 self.dateItem.hoursItem = self.getHoursItems(new Date(self.startTime).getFullYear(), new Date(self.startTime).getMonth() + 1, new Date(self.startTime).getDate(), new Date(self.startTime).getHours(), 23); // 更新当前选中时 self.fixHourDateBug(); }); self.$nextTick(() => { // 重新刷新分列表 传入开始日期的年份,开始时间的月份,开始时间的天份,开始时刻,开始的时间和结束时间 self.dateItem.minsItem = self.getMinsItems(new Date(self.startTime).getFullYear(), new Date(self.startTime).getMonth() + 1, new Date(self.startTime).getDate(), new Date(self.startTime).getHours(), self.startTime, self.endTime); // 更新当前选中分 self.fixMinDateBug(); }); return false; } else if (new Date(self.endTime).getFullYear() === selectYear) { //如果是结束时间 与开始时间同理 // 您选中了最后一个年份 self.dateItem.monthsItem = self.getMonthItems( new Date(self.endTime).getFullYear(), self.startTime, self.endTime); self.$nextTick(() => { self.fixMonthDateBug(); }); self.$nextTick(() => { self.dateItem.daysItem = self.getDaysItems( new Date(self.endTime).getFullYear(), self.startTime, self.endTime); self.fixDaysDateBug(); }); self.$nextTick(() => { self.dateItem.hoursItem = self.getHoursItems(new Date(self.endTime).getFullYear(), new Date(self.endTime).getMonth() + 1, new Date(self.endTime).getDate(), 0, new Date(self.endTime).getHours()); //因为是结束时间,所以时刻要从0-结束时间 self.fixHourDateBug(); }); self.$nextTick(() => { self.dateItem.minsItem = self.getMinsItems(new Date(self.endTime).getFullYear(), new Date(self.endTime).getMonth() + 1, new Date(self.endTime).getDate(), new Date(self.endTime).getHours(), self.startTime, self.endTime); self.fixMinDateBug(); }); return false; } } //如果没有传入开始和结束时间 self.dateItem.monthsItem = self.getMonthItems(selectYear, null, null); //根据当前选中的年份,更新月份列表 self.$nextTick(() => { // 更新当前选中月份 self.fixMonthDateBug(); }); self.$nextTick(() => { //根据当前选中的年份和月份,更新天份列表 self.dateItem.daysItem = self.getDaysItems(selectYear, this.selectItem.month.value, null, null); self.fixDaysDateBug(); }); self.$nextTick(() => { // 简单粗暴 0-23时 self.dateItem.hoursItem = self.getHoursItems(null, null, null, 0, 23); self.fixHourDateBug(); }); self.$nextTick(() => { //根据当前选中的年份,月份,天,更新分份列表 self.dateItem.minsItem = self.getMinsItems(selectYear, selectMonth, selectDay, null, null); self.fixMinDateBug(); }); return false;});复制代码
眼尖的小伙伴看出来了,对于重新选中选项的操作,单独分了一个名为fix****DateBug的方法,那我们就继续理解下列表更新,选中切换的思路
以自动切换月份当前选中为例:
fixMonthDateBug() { if (!this.dateItem.monthsItem[this.selectItem.month.index]) { // 如果当前选择的月份index没在当前的月份列表里 /* * 这里要解释一下:这种情况说明当前选中的月份value和index都不在当前的月份列表内。 * 例如:当前选择的是7.31,选中index就是32。如果切换到2月份,月份有28天,index最长29。 * 就找不到32这个index。所以直接赋值2月份最后一天。 * 这是月份的逻辑。其他同理还有“天”,“时”,“分”的同理逻辑。 * 因为年是最大的单位,所以年不设置此逻辑 * */ this.selectItem.month.index = this.dateItem.monthsItem.length - 1; this.selectItem.month.value = this.dateItem.monthsItem[this.dateItem.monthsItem.length - 1].value } else { //如果在的话,只更新value值。 this.selectItem.month.value = this.dateItem.monthsItem[this.selectItem.month.index].value }},复制代码复制代码
这个操作是为了解决:上次选择的index超过本次列表的长度,导致没有对应值问题。
解决思路:也就是当我上次选择7.31日时,本次切换到2月份。然而2月份没有31日,那我们就重新指向选择为2月最后一天。
那么我们放出剩下的“月”,"天",“时”,“分”监听事件(前方大量代码来袭):
//月列表滚动let MonthsScroll = new BScroll(document.getElementsByClassName("datetime--months")[0], this.scrollParam);MonthsScroll.on('scrollEnd', () => { let selectYear = self.dateItem.yearsItem[YearsScroll.getSelectedIndex()].value; //选中的月份Value let selectMonth = self.dateItem.monthsItem[MonthsScroll.getSelectedIndex()].value; let selectDay = self.selectItem.day.value; self.selectItem.month.value = selectMonth; self.selectItem.month.index = MonthsScroll.getSelectedIndex(); if (self.startTime && self.endTime) { if (MonthsScroll.getSelectedIndex() === 0 && new Date(self.startTime).getFullYear() === parseInt(selectYear)) { self.$nextTick(() => { self.dateItem.daysItem = self.getDaysItems(new Date(self.startTime).getFullYear(), new Date(self.startTime).getMonth() + 1, self.startTime, self.endTime); self.fixDaysDateBug(); }); self.$nextTick(() => { self.dateItem.hoursItem = self.getHoursItems(new Date(self.startTime).getFullYear(), new Date(self.startTime).getMonth() + 1, new Date(self.startTime).getDate(), new Date(self.startTime).getHours(), 23); self.fixHourDateBug(); }); self.$nextTick(() => { self.dateItem.minsItem = self.getMinsItems(new Date(self.startTime).getFullYear(), new Date(self.startTime).getMonth() + 1, new Date(self.startTime).getDate(), new Date(self.startTime).getHours(), self.startTime, self.endTime); self.fixMinDateBug(); }); return false; } else if (new Date(self.endTime).getMonth() + 1 === parseInt(selectMonth) && new Date(self.endTime).getFullYear() === parseInt(selectYear)) { self.$nextTick(() => { self.dateItem.daysItem = self.getDaysItems(new Date(self.endTime).getFullYear(), new Date(self.endTime).getMonth() + 1, self.startTime, self.endTime); self.fixDaysDateBug(); }); self.$nextTick(() => { self.dateItem.hoursItem = self.getHoursItems(new Date(self.endTime).getFullYear(), new Date(self.endTime).getMonth() + 1, new Date(self.endTime).getDate(), 0, new Date(self.endTime).getHours()); self.fixHourDateBug(); }); self.$nextTick(() => { self.dateItem.minsItem = self.getMinsItems(new Date(self.endTime).getFullYear(), new Date(self.endTime).getMonth() + 1, new Date(self.endTime).getDate(), new Date(self.endTime).getHours(), self.startTime, self.endTime); self.fixMinDateBug(); }); return false; } } self.$nextTick(() => { self.dateItem.daysItem = self.getDaysItems(selectYear, selectMonth, null, null); self.fixDaysDateBug(); }); self.$nextTick(() => { self.dateItem.hoursItem = self.getHoursItems(null, null, null, 0, 24); self.fixHourDateBug(); }); self.$nextTick(() => { self.dateItem.minsItem = self.getMinsItems(selectYear, selectMonth, selectDay, null, null); self.fixMinDateBug(); }); return false;});//日列表滚动let DaysScroll = new BScroll(document.getElementsByClassName("datetime--days")[0], this.scrollParam);DaysScroll.on('scrollEnd', () => { self.selectItem.day.value = self.dateItem.daysItem[DaysScroll.getSelectedIndex()].value; self.selectItem.day.index = DaysScroll.getSelectedIndex(); let selectYear = self.selectItem.year.value; let selectMonth = self.selectItem.month.value; let selectDay = self.selectItem.day.value; if (self.startTime && self.endTime) { if (new Date(self.startTime).getFullYear() === parseInt(selectYear) && new Date(self.startTime).getMonth() + 1 === parseInt(selectMonth) && new Date(self.startTime).getDate() === parseInt(selectDay)) { self.$nextTick(() => { self.dateItem.hoursItem = self.getHoursItems(new Date(self.startTime).getFullYear(), new Date(self.startTime).getMonth() + 1, new Date(self.startTime).getDate(), new Date(self.startTime).getHours(), 23); self.fixHourDateBug(); }); self.$nextTick(() => { self.dateItem.minsItem = self.getMinsItems(new Date(self.startTime).getFullYear(), new Date(self.startTime).getMonth() + 1, new Date(self.startTime).getDate(), new Date(self.startTime).getHours(), self.startTime, self.endTime); self.fixMinDateBug(); }); return false; } else if (new Date(self.endTime).getFullYear() === parseInt(selectYear) && new Date(self.endTime).getMonth() + 1 === parseInt(selectMonth) && new Date(self.endTime).getDate() === parseInt(selectDay)) { self.$nextTick(() => { self.dateItem.hoursItem = self.getHoursItems(new Date(self.endTime).getFullYear(), new Date(self.endTime).getMonth() + 1, new Date(self.endTime).getDate(), 0, new Date(self.endTime).getHours()); self.fixHourDateBug(); }); self.$nextTick(() => { self.dateItem.minsItem = self.getMinsItems(new Date(self.endTime).getFullYear(), new Date(self.endTime).getMonth() + 1, new Date(self.endTime).getDate(), new Date(self.endTime).getHours(), self.startTime, self.endTime); self.fixMinDateBug(); }); return false; } } self.dateItem.hoursItem = self.getHoursItems(null, null, null, 0, 24); self.$nextTick(() => { self.fixHourDateBug(); }); self.$nextTick(() => { self.dateItem.minsItem = self.getMinsItems(selectYear, selectMonth, selectDay, null, null); self.fixMinDateBug(); }); return false;})//时列表滚动let HoursScroll = new BScroll(document.getElementsByClassName("datetime--hours")[0], this.scrollParam);HoursScroll.on('scrollEnd', () => { self.selectItem.hour.value = self.dateItem.hoursItem[HoursScroll.getSelectedIndex()].value; self.selectItem.hour.index = HoursScroll.getSelectedIndex(); let selectYear = self.selectItem.year.value; let selectMonth = self.selectItem.month.value; let selectDay = self.selectItem.day.value; let selectHour = self.selectItem.hour.value; if (self.startTime && self.endTime) { if (new Date(self.startTime).getFullYear() === parseInt(selectYear) && new Date(self.startTime).getMonth() + 1 === parseInt(selectMonth) && new Date(self.startTime).getDate() === parseInt(selectDay) && new Date(self.startTime).getHours() === parseInt(selectHour)) { self.$nextTick(() => { self.dateItem.minsItem = self.getMinsItems(new Date(self.startTime).getFullYear(), new Date(self.startTime).getMonth() + 1, new Date(self.startTime).getDate(), new Date(self.startTime).getHours(), self.startTime, self.endTime); self.fixMinDateBug(); }); return false; } else if (new Date(self.endTime).getFullYear() === parseInt(selectYear) && new Date(self.endTime).getMonth() + 1 === parseInt(selectMonth) && new Date(self.endTime).getDate() === parseInt(selectDay) && new Date(self.endTime).getHours() === parseInt(selectHour)) { self.$nextTick(() => { self.dateItem.minsItem = self.getMinsItems(new Date(self.endTime).getFullYear(), new Date(self.endTime).getMonth() + 1, new Date(self.endTime).getDate(), new Date(self.endTime).getHours(), self.startTime, self.endTime); self.fixMinDateBug(); }); return false; } } self.$nextTick(() => { self.dateItem.minsItem = self.getMinsItems(selectYear, selectMonth, selectDay, null, null); self.fixMinDateBug(); }); return false;})//分列表滚动let MinsScroll = new BScroll(document.getElementsByClassName("datetime--mins")[0], this.scrollParam);MinsScroll.on('scrollEnd', () => { self.selectItem.min.value = self.dateItem.minsItem[MinsScroll.getSelectedIndex()].value; self.selectItem.min.index = MinsScroll.getSelectedIndex();})复制代码
注意:每一个监听事件,都是重新刷新子时间单位的列表并重新更改选择项。所以代码量从 ”年“ 到 ”分“ 是逐级递减的
剩余的日期选择项更改逻辑:
fixDaysDateBug() { if (!this.dateItem.daysItem[this.selectItem.day.index]) { this.selectItem.day.index = this.dateItem.daysItem.length - 1; this.selectItem.day.value = this.dateItem.daysItem[this.dateItem.daysItem.length - 1].value } else { this.selectItem.day.value = this.dateItem.daysItem[this.selectItem.day.index].value }},fixHourDateBug() { if (!this.dateItem.hoursItem[this.selectItem.hour.index]) { this.selectItem.hour.index = this.dateItem.hoursItem.length - 1; this.selectItem.hour.value = this.dateItem.hoursItem[this.dateItem.hoursItem.length - 1].value } else { this.selectItem.hour.value = this.dateItem.hoursItem[this.selectItem.hour.index].value }},fixMinDateBug() { if (!this.dateItem.minsItem[this.selectItem.min.index]) { this.selectItem.min.index = this.dateItem.minsItem.length - 1; this.selectItem.min.value = this.dateItem.minsItem[this.dateItem.minsItem.length - 1].value } else { this.selectItem.min.value = this.dateItem.minsItem[this.selectItem.min.index].value }},复制代码
好了,到这里一个功能基础功能的时间选择器(DatePicker)就做完了。组件代码在码云上有托管。链接:
emmm.....求各位路过点个小心心呗~~