使用ElementUI DateTimePicker,限制日期、时间范围
环境:
vue + element-ui
vue: 2.7.7
element-ui: 2.15.7
dayjs: 1.11.4
项目需要限制时间范围,日期、时间都需要限制。
原因:ElementUI文档中,date-picker有disabledDate属性限制日期,time-picker有selectableRange属性限制时间;但是DateTimePicker日期时间选择器(也就是datetimerange类型),只接收参数disabledDate属性;难受-_-
这里写了个demo实现,看看大佬们有没更好的方案。(懒得看过程的,可以直接跳到结尾看全部代码)
效果1: 限制日期
效果2: 开始时间
效果3: 结束时间
涉及到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获取到时间选择器组件minTimePicker和maxTimePicker,
<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]];
最后是,整体实现:
disabledDate,禁用日期;本文使用dayjs来格式化时间,然后进行边界判断,不然开始日期会被禁用掉onPick事件,用于监听是否点击了最小日期和最大日期。文档中没有提供时间限制的方法,这里通过$refs获取 子组件(开始时间minTimePicker、结束时间maxTimePicker),更改子组件的变量selectableRange;进而触发watch更改时间范围。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也可以
]
- 如果对应的日期不需要限制时间,不设置
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>