日历控件是我见过做复杂的组件了,当然除了富文本之外。很多日历组件在实现可选日期的时候都是大同小异的,要么提供一个enablexxx要么提供一个disabledxxx的函数,让使用者明确告诉它哪些日期是可用的。element中的日历控件也提供了这样一个属性disabledDate,它接收一个日期参数,返回true表示不可选择,否则是可以选择。
我们一般的处理方法是调用一个接口,后端告诉我们哪些是可选的哪些是不可选的,这样我们在disabledDate中通过查询日期是否在可选/不可选的列表中来返回。粗略看起来这个逻辑没问题,但是当遇到日期范围时问题就没那么简单了。日历首次弹开,不能选中的日期都是disabled的,这个没问题。不过当我们选择了一个开始日期,那么结束日期是不是要控制一下呢?如果不控制,势必会产生出一个日期范围,其中包含了一些不可用的日期。这个是不允许的。这也是我这篇文章要说明的问题。
先说思路,el-date-picker有一个onPick的事件回调,对于日期范围组件,点击开始日期和结束日期都会触发这个事件。我会在这里来区分出点击开始日期,然后置一个标志位,供后面使用,比如就叫firstDateClicked吧。
onPick({minDate, maxDate}) {
if (minDate && !maxDate) {
this.firstDateClicked = true;
} else {
this.firstDateClicked = false;
}
}
这里置true是足够了,但是关于什么时候置为false还需要再进一步讨论。如果我正常操作即点击了开始和结束日期,选择了一个日期范围,那这个代码没问题,如果我点击完开始日期后,中途点击了别的地方,导致日期控件消失了,此时也要把firstDateClicked置位false的。所以我觉得把置位false的逻辑放到日期控件消失时最理想。找遍了日期控件的文档也没能找到,我最后用了日期控件的blur事件。所以上面的代码就变成了
...
<el-date-picker @blur="firstDateClicked = false" ....
...
onPick({minDate, maxDate}) {
if (minDate && !maxDate) {
this.firstDateClicked = true;
}
}
好了,下面来说一下关键的部分了——disabledDate函数。说一下思路,在firstDateClicked为false时,我们按照以往的逻辑就行。当firstDateClicked为true时,我们要把跟第一个日期相连的所有可用日期保持可用,别的都应是不可用的。我这里假设我们有一个不可用日期范围的数组,如下,这个数组是按时间从小到大排序的。
disabledDates: [{
beginDate: '',
endDate: ''
}]
那么我们的disabledDate回调应该如下:
disabledDate: (date) => {
if (!this.firstDateClicked) {
return this.findInDisabledDates(date); // findInDisabledDates这个函数就是在disabledDates中找有没有date,很简单,就不列出了
} else {
// 找到date左边不可用的范围
const letfDisabledDate = this.findLeftDisabledDate(date);
// 找到date右边不可用的范围
const rightDisabledDate = this.findRightDisabledDate(date);
// 如果只有左边界
if (leftDisabledDate && !rightDisabledDate) {
const time = new Date(leftDisabledDate.endDate).getTime();
if (date.getTime() <= time) {
return true;
}
} else if (!leftDisabled && rightDisabledDate) { // 如果只有右边界
const time = new Date(rightDisabledDate.startDate).getTime();
if (date.getTime() >= time) {
return true;
}
} else if (leftDisabled && rightDisabledDate) { // 两个边界都有
const leftTime = (new Date(leftDisabledDate.endDate)).getTime();
const rightTime = (new Date(rightDisabledDate.startdate)).getTime();
const time = date.getTime();
if (time <= leftTime || time >= rightTime) {
return true;
}
} else { // 两个边界都没有
return false;
}
}
},
...
...
,
findLeftDisabledDate: (date) => {
const reversedDisabledDates = (JSON.parse(JSON.stringify(this.disabledDates)).reverse();
const time = date.getTime();
return reversedDisabledDates.find(item => {
const t= (new Date(item.endDate)).getTime();
return t < time;
});
},
findRightDisabledDate: (date) => {
const time = date.getTime();
return this.disabledDates.find(item => {
const t= (new Date(item.startDate)).getTime();
return t > time;
});
}
以上是手敲的(公司代码拷贝不出来),可能运行不起来,但是思路是正确的,亲测可用
如果有帮助,请点赞:)