一、需求
最近遇到一个需求,要求:
- 时间默认选择一个月内
- 开始时间-结束时间在30天内
大概是这样 ↓
二、解决方案
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 中的onPick
和disabledDate
方法共同控制可选范围。
其中,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可能检测不到数据变化,导致视图不更新
优缺点
- 优点:实现简单,
pickerOptions
只是在初始化时设置了一次 - 缺点:选中了一个日期后,不能再往前选(不知道这么说能不能懂,因为会直接把它当成minDate),解决方案有二:
- 设置一个重置按钮(调用
getDate
重置日期的同时,重置pickerMinDate=''
),但这样用户操作也很麻烦 - 使用监听器,当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. 总结
呃呵呵,其实写着写着感觉这两种方案没啥区别。不过第二种方案是上周写的,所以和第一种方案的具体细节有些不同,比如时间的格式,不过这也不重要哈哈哈。有问题的欢迎提问!