vue2elelment年份区间选择组件(year-picker)

353 阅读5分钟

使用element组件的时候,这个组件提供了日期区间选择,但没有提供年份区间选择的能力,现在网络上也有一些成熟的其他人进行的改造。 比如图二那种类似官方组件的,选择一个开始年,选择一个结束年。 而官方组件提供的是在一个组件里面可以多选年,但不可以区间选择。 今天我们要实现的就是不采用这种两个组件集成到一起的方式,我们在一个组件里直接实现需求。

需求分析

需要实现的功能点主要如下:
1、在一个弹窗组件中,实现选择一个开始年 和 一个结束年, 选择的同时, 开始年与结束年之间的年份直接被动选中。
2、默认第一次选择的为开始年,避免在 picker-options 方法中进行排序产生栈溢出,第一次选择了开始年以后,这一年份之前的所有年份禁用。
3、选择了第二个年份以后,第二个年份默认为结束年,结束年之后的年份也默认禁用掉。
4、需要清空重新选择,则直接返回弹窗点 小x清空即可。

  • 图一 官方提供的组件类型,没有年份区间类型 image.png

  • 图二 其他比较流行的实现
    image.png

  • 图三 我们需要实现的效果
    image.png
    -图四 当我们选择第二个年份以后,中间这些年份自动选中,并且回填到表单中。 这种需求比较满足于后端入参非区间格式。 而是每一年都需要的情况。
    image.png

实现

使用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,防止栈溢出。 和避免进入死循环。 同时这里选择第一个年份以后,就禁用了之前的年份,是为了避免出现用户选择的第一个年份 在第二个年份后面的情况。 让我们处理逻辑尽量是按照顺序进行,如果逆序的话就要进行排序,而排序会引起栈溢出。 所以直接使用这种方式避免用户的此类操作。
  • 我这边暂时能够想到的这些弊端的解决办法只有这些,如果大家有更好的方式,可以评论区留言,大家一起互相交流学习哦~

后续更新

image.png pickerOptions 这里之前的写法是这样的, 这种会造成一个bug : image.png 就是因为 这里跨了页面进行了选择, 把 2029 年 给漏掉了, 所以后期更改成了上面已经改完了的样子。 不通过这种年份区间范围判断了 , 通过两种情景了, 一是初次选择,肯定是两个年份, 而且两个年份不连续, 可以走填充逻辑; 二是当前选择的年份在我已经划定了的年份范围之内, 则填充。 这个二可以重点说一下, 如下图: 我现在选择了一个区间以后, 这时候我选择 2031 这种在区间之内的, 其实代码上做的操作就是把这个2031重新选中了,页面上的效果就是 选区间内的无效, 目的是防止年份出现断续,程序出现bug,因为我们要做的功能就是满足连续选择的功能,断续的话直接用组件库的组件就完事了。 这里当我们选择 2035 这种端点年份的时候, 会生效, 这个2035 会取消选中,且会被禁用。
image.png