ElementUI DateTimePicker 日期时间选择器,限制日期、时间的范围,datetimerange

5,918 阅读5分钟

使用ElementUI DateTimePicker,限制日期、时间范围

环境:
vue + element-ui

vue: 2.7.7
element-ui: 2.15.7
dayjs: 1.11.4

项目需要限制时间范围,日期、时间都需要限制。

原因:ElementUI文档中,date-pickerdisabledDate属性限制日期,time-pickerselectableRange属性限制时间;但是DateTimePicker日期时间选择器(也就是datetimerange类型),只接收参数disabledDate属性;难受-_-

这里写了个demo实现,看看大佬们有没更好的方案。(懒得看过程的,可以直接跳到结尾看全部代码)

效果1: 限制日期

pic04.png

效果2: 开始时间

pic02.png

效果3: 结束时间

pic03.png

涉及到ElementUI的源码: 首先是,date-picker目录下的date-picker.js(下面只是部分代码)。

import DateRangePanel from '../panel/date-range';

const getPanel = function(type) {
  // datetimerange 类型
  if (type === 'daterange' || type === 'datetimerange') {
    return DateRangePanel;
  } else if (type === 'monthrange') {
    return MonthRangePanel;
  }
  return DatePanel;
};

然后是,panel/date-range.vue。这里通过$refs获取到时间选择器组件minTimePickermaxTimePicker

<time-picker
  ref="minTimePicker"
  @pick="handleMinTimePick"
  :time-arrow-control="arrowControl"
  :visible="minTimePickerVisible"
  @mounted="$refs.minTimePicker.format=timeFormat">
</time-picker>

接着是,time-picker组件有watch,所以我们通过$refs获取到minTimePicker,给变量赋值即可; 这里要注意selectableRange的数据格式,见整体实现的第3点

watch: {
  // 这里省略部分代码
  selectableRange(val) {
    this.$refs.spinner.selectableRange = val;
  }
},

ElementUI组件也是这么玩的,

this.$refs.minSpinner.selectableRange = [[minTimeOfDay(this.minDate), this.maxDate]];

最后是,整体实现:

  1. disabledDate,禁用日期;本文使用dayjs来格式化时间,然后进行边界判断,不然开始日期会被禁用掉
  2. onPick事件,用于监听是否点击了最小日期和最大日期。文档中没有提供时间限制的方法,这里通过$refs获取 子组件(开始时间minTimePicker、结束时间maxTimePicker),更改子组件的变量selectableRange;进而触发watch更改时间范围。
  3. selectableRange并不是官方文档中的那个属性,是数组格式,数组元素必须是日期格式;这里重点的是时分秒,年月日这里用不到,只是拼凑日期格式用而已; 这里可以是多个可用的时间段,比如总体的时间范围是2022-07-18 23:59:50 ~ 2022-07-21 01:00:00,那么要设置的时间范围是3:59:50 ~ 01:00:00,代码如下
selectableRange = [
  [new Date(`2022-07-18 23:59:50`), new Date(`2022-07-21 01:00:00`)]
  // 这里2022-07-18也可以换成其他日期1997-01-1,只要是23:59:50时间点的Date即可;
  // 写成2022-01-01  23:59:50也可以
]
  1. 如果对应的日期不需要限制时间,不设置selectableRange即可;需要限制时间得,设置好范围,本文的范围就是
// 开始时间
23:59:50 ~ 23:59:59

// 结束时间
00:00:00 ~ 01:00:00

因为需要日期格式
 minTimePicker.selectableRange = [
    [new Date(`2022-01-01 23:59:50`), new Date(`2022-01-01 23:59:59`)]
 ]
 
 maxTimePicker.selectableRange = [
    [new Date(`2022-01-01 00:00:00`), new Date(`2022-01-01 01:00:00`)]
 ]

一、首先是组件的属性

<!-- 使用的是datePicker,datetimerange类型 -->
<el-date-picker
    v-model="time"
    :picker-options="pickerOptions"
    ref="datetime"
    type="datetimerange"
    range-separator="至"
    start-placeholder="开始日期"
    end-placeholder="结束日期"
    format="yyyy-MM-dd HH:mm:ss"
    value-format="timestamp"
></el-date-picker>

二、用到的时间范围

// 时间范围参数range
props: {
  range: {
    type: Array,
    default: () => ['2022-07-18 23:59:50', '2022-07-22 01:00:00']
  }
},

三、用于边界判断的常量

// 定义常量,这里设置成全局了,后面好几处地方用到;主要用于判断边界问题
const DateFormat = 'YYYY-MM-DD'
const TimeFormat = 'HH:mm:ss'
const StartTimeFormat = '00:00:00'
const ENDTimeFormat = '23:59:59'

四、定义的pickerOptions,也是实现的逻辑

data() {
  return {
    time: [], // 时间范围是数组
    // 在elementUI文档中提供的pickerOptions,这里设置disabledDate限制日期,onPick根据选择的日期进而显示时间
    pickerOptions: {
      disabledDate: time => {
        const [start, end] = this.range
        const d = new Date(time).getTime()
        const pickDate = dayjs(time).format(DateFormat)
        const startDate = dayjs(start).format(DateFormat)
        const startTime = dayjs(start).format(TimeFormat)
        const startStamp = new Date(start).getTime()
        const endStamp = new Date(end).getTime()
        // 组件(界面)上的日期 和 时间范围中最小的日期  相同时
        if (pickDate === startDate && startTime !== ENDTimeFormat) {
          return false
        }
        // 这里不用判断时间范围中最大的日期
        return d < startStamp || d >= endStamp // 小于最小日期 或者 大于最大日期
      },
      onPick: ({ minDate, maxDate }) => {
        if (!(minDate && maxDate)) {// 选择完整的日期后,再进入后面的逻辑
          return
        }
        const picker = this.$refs.datetime?.picker
        const { minTimePicker, maxTimePicker } = picker.$refs
        const [start, end] = this.range
        const pickStartDate = dayjs(minDate).format(DateFormat) // 手动选择的最小日期
        const pickEndDate = dayjs(maxDate).format(DateFormat) // 手动选择的最大日期
        const startDate = dayjs(start).format(DateFormat) // 时间范围this.range,最小日期 年月日
        const startTime = dayjs(start).format(TimeFormat) // 时间范围this.range,最小时间 时分秒
        const endDate = dayjs(end).format(DateFormat) // 时间范围this.range,最大日期 年月日 
        const endTime = dayjs(end).format(TimeFormat) // 时间范围this.range,最大日期 年月日
        // 手动选择的最小日期 等于 时间范围this.range的最小日期,那么需要限制时间范围
        if (pickStartDate === startDate) {
          minTimePicker.selectableRange = [
            [new Date(`${startDate} ${startTime}`), new Date(`${startDate} ${ENDTimeFormat}`)]
          ]
        } else {
          // 不相等,则可以选择全部的时间范围,不需要做限制
          minTimePicker.selectableRange = []
        }
        // 手动选择的最大日期 等于 时间范围this.range的最大日期,那么需要限制时间范围
        if (pickEndDate === endDate) {
          maxTimePicker.selectableRange = [
            [new Date(`${endDate} ${StartTimeFormat}`), new Date(`${endDate} ${endTime}`)]
          ]
        } else {
          // 不相等,则可以选择全部的时间范围,不需要做限制
          maxTimePicker.selectableRange = []
        }
      }
    }
  }
}

到这里结束了。大佬肯定会想:东西很少,逻辑居然写了那么多,还发那么长的内容,凑字数混经验吗?[狗头]

第一次写文章,写得不好的地方,多多见谅;实现的逻辑不会表达,排版也不好,大佬们可能看不下吧。如果有好的方案建议,麻烦大佬们评论一下!看在码字辛苦的份上,可以给个赞吗?[问号]

下面是完整代码,可以直接复制粘贴运行的![吃瓜]

<template>
  <div>
    <h3>ElementUI DateTimePicker</h3>
    <h5>时间范围range: ['2022-07-18 23:59:50', '2022-07-21 01:00:00']</h5>
    <el-date-picker
        v-model="time"
        :picker-options="pickerOptions"
        ref="datetime"
        type="datetimerange"
        range-separator="至"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
        format="yyyy-MM-dd HH:mm:ss"
        value-format="timestamp"
    ></el-date-picker>
  </div>
</template>

<script>
import dayjs from 'dayjs'

const DateFormat = 'YYYY-MM-DD'
const TimeFormat = 'HH:mm:ss'
const StartTimeFormat = '00:00:00'
const ENDTimeFormat = '23:59:59'

export default {
  name: "DateTimePicker",
  props: {
    range: {
      type: Array,
      default: () => ['2022-07-18 23:59:50', '2022-07-22 01:00:00']
    }
  },
  data() {
    return {
      time: [], // 时间范围是数组
      // 在elementUI文档中提供的pickerOptions,这里设置disabledDate限制日期,onPick根据选择的日期进而显示时间
      pickerOptions: {
        disabledDate: time => {
          const [start, end] = this.range
          const d = new Date(time).getTime()
          const pickDate = dayjs(time).format(DateFormat)
          const startDate = dayjs(start).format(DateFormat)
          const startTime = dayjs(start).format(TimeFormat)
          const startStamp = new Date(start).getTime()
          const endStamp = new Date(end).getTime()
          // 组件(界面)上的日期 和 时间范围中最小的日期  相同时
          if (pickDate === startDate && startTime !== ENDTimeFormat) {
            return false
          }
          // 这里不用判断时间范围中最大的日期
          return d < startStamp || d >= endStamp // 小于最小日期 或者 大于最大日期
        },
        onPick: ({ minDate, maxDate }) => {
          if (!(minDate && maxDate)) {// 选择完整的日期后,再进入后面的逻辑
            return
          }
          const picker = this.$refs.datetime?.picker
          const { minTimePicker, maxTimePicker } = picker.$refs
          const [start, end] = this.range
          const pickStartDate = dayjs(minDate).format(DateFormat) // 手动选择的最小日期
          const pickEndDate = dayjs(maxDate).format(DateFormat) // 手动选择的最大日期
          const startDate = dayjs(start).format(DateFormat) // 时间范围this.range,最小日期 年月日
          const startTime = dayjs(start).format(TimeFormat) // 时间范围this.range,最小时间 时分秒
          const endDate = dayjs(end).format(DateFormat) // 时间范围this.range,最大日期 年月日
          const endTime = dayjs(end).format(TimeFormat) // 时间范围this.range,最大日期 年月日
          // 手动选择的最小日期 等于 时间范围this.range的最小日期,那么需要限制时间范围
          if (pickStartDate === startDate) {
            minTimePicker.selectableRange = [
              [new Date(`${startDate} ${startTime}`), new Date(`${startDate} ${ENDTimeFormat}`)]
            ]
          } else {
            // 不相等,则可以选择全部的时间范围,不需要做限制
            minTimePicker.selectableRange = []
          }
          // 手动选择的最大日期 等于 时间范围this.range的最大日期,那么需要限制时间范围
          if (pickEndDate === endDate) {
            maxTimePicker.selectableRange = [
              [new Date(`${endDate} ${StartTimeFormat}`), new Date(`${endDate} ${endTime}`)]
            ]
          } else {
            // 不相等,则可以选择全部的时间范围,不需要做限制
            maxTimePicker.selectableRange = []
          }
        }
      }
    }
  }
}
</script>