使用element组件的时候,这个组件提供了日期区间选择,但没有提供年份区间选择的能力,现在网络上也有一些成熟的其他人进行的改造。 比如图二那种类似官方组件的,选择一个开始年,选择一个结束年。 而官方组件提供的是在一个组件里面可以多选年,但不可以区间选择。 今天我们要实现的就是不采用这种两个组件集成到一起的方式,我们在一个组件里直接实现需求。
需求分析
需要实现的功能点主要如下:
1、在一个弹窗组件中,实现选择一个开始年 和 一个结束年, 选择的同时, 开始年与结束年之间的年份直接被动选中。
2、默认第一次选择的为开始年,避免在 picker-options
方法中进行排序产生栈溢出,第一次选择了开始年以后,这一年份之前的所有年份禁用。
3、选择了第二个年份以后,第二个年份默认为结束年,结束年之后的年份也默认禁用掉。
4、需要清空重新选择,则直接返回弹窗点 小x清空即可。
-
图一 官方提供的组件类型,没有年份区间类型
-
图二 其他比较流行的实现
-
图三 我们需要实现的效果
-图四 当我们选择第二个年份以后,中间这些年份自动选中,并且回填到表单中。 这种需求比较满足于后端入参非区间格式。 而是每一年都需要的情况。
实现
使用vue2 配合 element 。
template
<el-date-picker
v-model="formData.years"
type="years"
value-format="yyyy"
clearable
v-if="yearContinue"
:disabled="dialogType === 'edit'"
placeholder="选择连续年"
:picker-options="pickerOptions"
>
</el-date-picker>
script
vue 中的 data 里面
data(){
return {
pickerOptions: {
disabledDate: (time) => {
const otherYears = time.getFullYear()
let arr = this.formData.years || []
// 第一次选择一个年份, 第一年的前面所有年份禁用, 避免出现 开始年 再结尾年 后面的情况
if(arr?.length === 1){
return otherYears < arr[0]
}
if(arr?.length < 2) return
// 开始年 及结尾年 区间之外的禁用
if(otherYears < arr[0] || otherYears > arr.at(-1)) return true
// 如果选择的年份,在当前区间范围之内,直接return, 防止栈溢出
// 如果选择的年份,在当前区间范围之内,直接return, 防止栈溢出
// 对表单中的年份,进行区间值覆盖, 首先判定是初次选择, 两个年份 且这两个年份不连续, 则可以填充, 其次判定 如果当前选择的年份, 不在区间范围之内, 则也从新填充, 达到不能取消选择区间范围以内的年份的效果。 (因为已经针对范围之外的年份禁用了)
if((this.formData.years.length === 2 && !this.isYearsContinuous(this.formData.years)) || !this.formData.years.includes(String(otherYears))){
this.formData.years = generateYearRange(this.formData.years)
return
}
return otherYears < arr[0] || otherYears > arr.at(-1)
},
},
}
}
methods: {
isYearsContinuous(years) {
if (years.length < 2) {
return false;
}
const sortedYears = years.map(year => parseInt(year, 10)).sort((a, b) => a - b);
for (let i = 0; i < sortedYears.length - 1; i++) {
if (sortedYears[i + 1] - sortedYears[i] !== 1) {
return false;
}
}
return true;
},
}
generateYearRange
方法
function generateYearRange(years) {
const arr = years.sort((a,b)=>a-b)
const startYear = parseInt(arr[0], 10); // 将字符串转换为数字
const endYear = parseInt(arr.at(-1), 10);
const yearRange = [];
for (let year = startYear; year <= endYear; year++) {
yearRange.push(year.toString()); // 将数字转换为字符串
}
return yearRange;
}
- 这里需要注意 不能在
disabledDate
回调中进行sort
排序,因为disabledDate
这个方法会在每一次组件变动的时候,都会执行很多次, 相当于每一个年份的显示,就会执行一次,所以我们在满足条件的情况下,就尽快return
,防止栈溢出。 和避免进入死循环。 同时这里选择第一个年份以后,就禁用了之前的年份,是为了避免出现用户选择的第一个年份 在第二个年份后面的情况。 让我们处理逻辑尽量是按照顺序进行,如果逆序的话就要进行排序,而排序会引起栈溢出。 所以直接使用这种方式避免用户的此类操作。 - 我这边暂时能够想到的这些弊端的解决办法只有这些,如果大家有更好的方式,可以评论区留言,大家一起互相交流学习哦~
后续更新
pickerOptions 这里之前的写法是这样的, 这种会造成一个bug :
就是因为 这里跨了页面进行了选择, 把 2029 年 给漏掉了, 所以后期更改成了上面已经改完了的样子。 不通过这种年份区间范围判断了 , 通过两种情景了, 一是初次选择,肯定是两个年份, 而且两个年份不连续, 可以走填充逻辑; 二是当前选择的年份在我已经划定了的年份范围之内, 则填充。 这个二可以重点说一下, 如下图: 我现在选择了一个区间以后, 这时候我选择 2031 这种在区间之内的, 其实代码上做的操作就是把这个2031重新选中了,页面上的效果就是 选区间内的无效, 目的是防止年份出现断续,程序出现bug,因为我们要做的功能就是满足连续选择的功能,断续的话直接用组件库的组件就完事了。 这里当我们选择 2035 这种端点年份的时候, 会生效, 这个2035 会取消选中,且会被禁用。