一个需求,DatePicker设置daterange,在选择了第一个日期后,设置可选日期为以选中日期前后一个月的区间,保证最多只能选一个月。
按照需求再根据iview官方文档,我们的思路就是,监听日期组件在用户点击第一个日期后触发的事件,我们再修改不可选日期的判断方法。习惯上我们就监听onchange事件,但实际上,日期组件的onchange事件是输入框的值发生变化才触发的。时间范围选择,是在用户选择了两个日期后才会去改变输入框的值。因此onchange事件不可用。然而官方文档提供的事件也没其他合适的。
解决方法有很多,比如可以直接修改源码,增加一个事件抛出。现在我这里要写的是在不修改源码的基础上实现功能。
vue有个机制,就是@事件名.native可监听组件根元素的原生事件。我们可以从这里下手,原生事件的参数就不像组件的事件的参数那么人性化直接提供我们想要的,因此我们需要寻找我们参数。群友发表了很多属性可以使用,如用直接拿iview官方提供的findComponentDownward 方法,查找子组件,再拿到RangeDatePickerPanel组件里面的$data属性rangeState。我这里就直接使用群友的方法,省下阅读iview源码的时间了。
由于我在在线测试上模拟,所以就直接控制台打印对象,找到children中的RangeDatePickerPanel。

按图中的层级,路径就是this.$refs["组件ref"].$refs["pickerPanel"]。

在这个对象中我也找到了rangeState的属性。那么我们就可以进行下一步操作,因为click是用户点击组件都会触发,我们需要判断一下from和to这两个属性是否有值来判断此次点击是否选择了第一个日期。

三次分别是点击输入框,选择第一个日期,再选择第二个日期。熟悉很清晰可以区别。后面没什么好说了,直接上完整示例代码。
<template>
<Row>
<Col span="12"> </Col>
<Col span="12">
<!-- 2022-01-15 感谢网友来信,click事件已经失效,目前改用mouseup事件 -->
<DatePicker ref="dd" :options="options2" type="daterange" @mouseup.native="c" placement="bottom-end" placeholder="Select date" style="width: 200px"></DatePicker>
</Col>
</Row>
</template>
<script>
export default {
data () {
return {
options2: {
disabledDate:function(){
return false;
}
}
}
},
methods:{
c(){
setTimeout(()=>{//从click改成使用mouseup,需要延时,等click事件触发,组件处理完click事件,相应属性有时间值
var tar = this.$refs["dd"].$refs["pickerPanel"];
//console.log("from:"+tar["rangeState"]["from"]);
//console.log("to:"+tar["rangeState"]["to"]);
if(tar["rangeState"]["from"]&&!tar["rangeState"]["to"]){//选择了第一个日期
var first = tar["rangeState"]["from"];
this.options2={
disabledDate:function(value){
var date_start = new Date(first);
var date_end = new Date(first);
date_start.setDate(first.getDate()-30);
date_end.setDate(first.getDate()+30);
return !(value >= date_start&&value<=date_end);
}
};
}
else if(tar["rangeState"]["from"]==null){//点击输入框还原所有日期可选
this.options2={
disabledDate:function(){
return false;
}
};
}
},0);
}
}
}
</script> 再来个我特别喜欢的gif效果图。

