el-date-picker 限制日期选择范围

一、需求

最近遇到一个需求,要求:

  1. 时间默认选择一个月内
  2. 开始时间-结束时间在30天内

大概是这样 ↓
el-date-picker 限制日期选择范围_第1张图片
el-date-picker 限制日期选择范围_第2张图片

二、解决方案

elementUI 没有直接提供限制日期选择范围的属性,但我们可以通过picker-options属性实现。

琢磨了2种方案,各有优缺点。

1. 使用一个el-date-picker

实现步骤

(1) 设置type="daterange"让日期选择器变成选择日期范围

绑定v-model="date",其中date是Array类型
起始时间:date[0],
结束时间:date[1]

<label>日期label>
<el-date-picker
  type="daterange"
  value-format="yyyy-MM-dd HH:mm:ss"
  :default-time="['00:00:00', '23:59:59']"
  start-placeholder="开始日期"
  end-placeholder="结束日期"
  v-model="date"
  :picker-options="pickerOptions">
el-date-picker>

(2) 使用 pickerOptions 中的onPickdisabledDate方法共同控制可选范围。
其中,onPick是选中日期后会执行的回调(只有当 type 为 daterange 或 datetimerange 时才生效),参数是一个对象,里面有 maxDate 和 minDate。

会自动比较两个日期,判断max和min。
如果初始均为null,选择一个后:min=所选值,max=null

export default {
data () {
    return {
      date: ['', ''],
      pickerMinDate: '', // 保存选中的开始时间
      pickerOptions: {
        onPick: obj => {
          this.pickerMinDate = new Date(obj.minDate).getTime()
          const start = this.formatDate(obj.minDate, 'start')
          const end = this.formatDate(obj.maxDate, 'end')
          obj.maxDate && (this.date = [start, end])
        },
        disabledDate: time => {
          if (this.pickerMinDate) {
            const oneMonth = 1000 * 3600 * 24 * 31
            const maxTime = this.pickerMinDate + oneMonth
            const tomorrow = new Date(this.formatDate(new Date().getTime() + 1000 * 3600 * 24, 'start'))
            return time.getTime() >= tomorrow || time.getTime() >= maxTime || time.getTime() < this.pickerMinDate
          }
        }
      } // 日期选择策略
    }
  },
  created () {
    this.getDate()
  },
  methods: {
    formatDate (datetime, type) {
      const time = new Date(datetime)
      const year = time.getFullYear()
      const month = (time.getMonth() + 1).toString().padStart(2, '0')
      const date = (time.getDate()).toString().padStart(2, '0')
      return type === 'start' ? year + '-' + month + '-' + date + ' ' + '00' + ':' + '00' + ':' + '00' : year + '-' + month + '-' + date + ' ' + '23' + ':' + '59' + ':' + '59'
    },
    getDate () {
      const start = new Date()
      this.date[0] = this.formatDate(start.getTime() - 1000 * 3600 * 24 * 30, 'start')
      this.date[1] = this.formatDate(start.getTime(), 'end')
    }
  }
}

注意:
this.date = [start, end]:如果分别赋值(date[0]=start, date[1]=end),vue可能检测不到数据变化,导致视图不更新

优缺点

  1. 优点:实现简单,pickerOptions只是在初始化时设置了一次
  2. 缺点:选中了一个日期后,不能再往前选(不知道这么说能不能懂,因为会直接把它当成minDate),解决方案有二:
    1. 设置一个重置按钮(调用getDate重置日期的同时,重置pickerMinDate=''),但这样用户操作也很麻烦
    2. 使用监听器,当date为null时,pickerMinDate=''。但这样感觉和第二种方案也没啥区别了

2. 使用两个el-date-picker

实现步骤

(1) 放置2个el-date-picker,分别绑定数据

<label>日期label>
<el-date-picker
  v-model="date.startDate"
  type="date"
  placeholder="选择起始日期"
  value-format="yyyy-MM-dd"
  :picker-options="pickerStartOption">
el-date-picker>
-
<el-date-picker
  v-model="date.endDate"
  type="date"
  placeholder="选择结束日期"
  value-format="yyyy-MM-dd"
  :picker-options="pickerEndOption">
el-date-picker>

(2) 使用监听器watch替代elementUI内置的onPick方法

data () {
    return {
      date: {
        startDate: '',
        endDate: ''
      },
      oneMonth: 1000 * 3600 * 24 * 31,
      pickerStartOption: {},
      pickerEndOption: {}
    }
  },
  // 监听date变化
  watch: {
    'date.startDate' (newDate) {
      // 清空的时候重置选择策略
      if (!newDate) {
        this.date.endDate = null
        this.initOption()
        return
      }
      // 判断现有的endDate是否在新的startDate之前,如果是,同步到startDate
      if (new Date(this.date.endDate) < new Date(newDate)) {
        this.date.endDate = newDate
      }
      // 根据选中时间,刷新选择策略
      this.pickerEndOption = {
        disabledDate: time => {
          return time.getTime() > Date.now() || time.getTime() < new Date(new Date(newDate) - 1000 * 3600 * 24) || time.getTime() > new Date(newDate).getTime() + this.oneMonth
        }
      }
    },
    'date.endDate' (newDate) {
      if (!newDate) {
        this.date.startDate = null
        this.initOption()
        return
      }
      if (new Date(this.date.startDate) > new Date(newDate)) {
        this.date.startDate = newDate
      }
      this.pickerStartOption = {
        disabledDate: time => {
          return time.getTime() > new Date(newDate) || time.getTime() < new Date(newDate) - this.oneMonth
        }
      }
    }
  },
  methods: {
    // 初始化选项,仅禁止选择未来
    initOption () {
      this.pickerStartOption = {
        disabledDate: time => {
          return time.getTime() > Date.now()
        }
      }
      this.pickerEndOption = {
        disabledDate: time => {
          return time.getTime() > Date.now()
        }
      }
    },
    // 格式化日期
    formatDate (datetime) {
      const time = new Date(datetime)
      const year = time.getFullYear()
      const month = (time.getMonth() + 1).toString().padStart(2, '0')
      const date = (time.getDate()).toString().padStart(2, '0')
      return year + '-' + month + '-' + date
    },
    // 初始化日期
    getDate () {
      const time = new Date()
      this.date.startDate = this.formatDate(time - 1000 * 3600 * 24 * 30)
      this.date.endDate = this.formatDate(time)
      this.initOption()
    }
  },
  created () {
    this.getDate()
  }
}

3. 总结

呃呵呵,其实写着写着感觉这两种方案没啥区别。不过第二种方案是上周写的,所以和第一种方案的具体细节有些不同,比如时间的格式,不过这也不重要哈哈哈。有问题的欢迎提问!

你可能感兴趣的:(vue.js,前端,javascript,elementui)