需求描述
最近遇到了一个关于日期选择器动态禁用多个时间段的组件需求:
需求描述:在可新增的动态表格中,新增一条数据,该数据中的开始日期和结束日期可选择范围由整个表格的每条数据的开始日期和结束日期决定,选择的时间段不能有重叠,开始日期和结束日期可为同一天,时间格式为yyyy-MM-dd。
解决关键:需要用到element日期选择器组件的属性picker-options来处理需要禁用的时间段(我使用到了XUI,换回element同样ok)。
精确到年月日
首先搭建简单的vue页面,此时开始日期与结束日期选择器日期可以任意选择,表格1为初始的日期表格数据,表2为过滤掉错误数据后的日期表格数据。代码如下:
<template>
<div>
<div class="my-start-date">
<x-date :value="startDate" :editable="false" format="yyyy-MM-dd HH:mm" value-format="yyyy-MM-dd HH:mm" type="datetime" :picker-options="startPickerOptions" placeholder="选择开始时间" @change="startValueChangeEvent" @focus="startFocus">
</x-date>
</div>
<div class="my-end-date">
<x-date :value="endDate" :editable="false" format="yyyy-MM-dd HH:mm" value-format="yyyy-MM-dd HH:mm" type="datetime" :picker-options="endPickerOptions" placeholder="选择结束时间" @change="endValueChangeEvent" @focus="endFocus">
</x-date>
</div>
<div>
<el-button style="width: 20%; margin: 10px;" type="primary" @click="addDateRange">
新增时间段
</el-button>
<div>
<x-vxe-table
class="origin-table"
:colConfigs="colConfigs"
:tableData="dateList"
seqType="checkbox"
:pagination="{
total:dateList.length
}"
></x-vxe-table>
</div>
<div>
<x-vxe-table
class="handled-table"
:colConfigs="colConfigs"
:tableData="filterDataList"
seqType="checkbox"
:pagination="{
total:filterDataList.length
}"
></x-vxe-table>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ApaasCustomTestPage1',
components: {},
data() {
return {
finalSelectableRange: '', // 存储时分秒时间段
startDate: '', // 绑定的开始时间
endDate: '', // 绑定的结束时间
startPickerOptions: {},
endPickerOptions: {},
colConfigs: [ // 表格列配置
{
prop: 'dayStart',
label: '开始日期'
},
{
prop: 'dayEnd',
label: '结束日期'
}
],
dateList: [ // 模拟初始数据
{
dayStart: '2022-03-11',
dayEnd: '2022-03-14'
},
{
dayStart: 'undefined',
dayEnd: ''
},
{
dayStart: 'NaN-NaN-NaN',
dayEnd: '2022-04-08'
},
{
dayStart: 'Mon Feb 29 2022 00:00:00 GMT+0800 (中国标准时间)',
dayEnd: '2022-03-05'
},
{
dayStart: '2022-03-18',
dayEnd: '2022-03-20'
},
{
dayStart: '2022-03-24',
dayEnd: '2022-03-25'
}
],
filterDataList: [] // 过滤后的数据
}
},
computed: {},
watch: {
},
mounted() {
// 处理初始数据
this.filterDate()
},
created() {
},
methods: {
/**
* -----------------------------------------------------组件方法-----------------------------------------------------------
*
* 控制时间段禁用的主要逻辑是在时间选择器的focus方法中处理
*/
// 开始时间组件的focus方法
startFocus() {},
// 开始时间组件的change方法,只要选中的值改变,不管是否点击了确定按钮(关闭弹窗),都会触发
startValueChangeEvent(value) {
console.log('开始时间的值', value)
this.startDate = value
},
// 结束时间组件的focus方法
endFocus() {},
// 结束时间组件的change方法
endValueChangeEvent(value) {
console.log('结束时间的值:', value)
this.endDate = value
},
/**
* ---------------------------------------------------按钮相关方法-----------------------------------------------------------
*/
// 新增时间段
addDateRange() {
if (this.startDate && this.endDate) {
this.dateList.push({
dayStart: this.startDate,
dayEnd: this.endDate
})
// 处理数据
this.filterDate()
this.$message({
type: 'success',
message: '时间段添加成功!'
})
} else {
this.$message({
type: 'warning',
message: '数据有误,请重新选择时间!'
})
}
},
// 数据过滤 返回数据正常的数据,此方法可用作过滤接口返回数据
filterDate() {
this.filterDataList = []
// 对初始数据进行深拷贝,避免数据污染
let tempArr = this.deepCopy(this.dateList)
tempArr.forEach(item => {
if (item.dayStart && item.dayStart !== 'NaN-NaN-NaN' && item.dayEnd && item.dayEnd !== 'NaN-NaN-NaN') {
item.dayStart = this.changeType(item.dayStart)
item.dayEnd = this.changeType(item.dayEnd)
this.filterDataList.push(item)
}
})
},
/**
* ------------------------------------------------工具函数--------------------------------------------------------
*/
// 转换时间格式,type可不传 此方法可继续拆分实现获取单独的年、月、日、时、分、秒 此处简略
changeType(val, type) {
let date = new Date(val)
let Y = date.getFullYear()
let M = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : (date.getMonth() + 1)
let D = date.getDate() < 10 ? ('0' + date.getDate()) : date.getDate()
val = Y + '-' + M + '-' + D
if (type === 'yyyy-MM-dd HH:mm') {
let h = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours())
let m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
val = val + ' ' + h + ':' + m
}
return val
},
// 获取单独的小时(24小时制)
getHourChange(val) {
let date = new Date(Date.parse(val))
return date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()
},
// 获取单独的分钟
getMinutesChange(val) {
let date = new Date(Date.parse(val))
return date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes()
},
// 深拷贝
deepCopy (obj) {
const result = Array.isArray(obj) ? [] : {}
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
if (obj[key] instanceof RegExp) {
result[key] = obj[key]
} else {
result[key] = this.deepCopy(obj[key]) // 递归复制
}
} else {
result[key] = obj[key]
}
}
}
return result
}
}
}
</script>
<style lang="scss" scoped>
.origin-table {
height: 400px;
overflow: auto;
}
.handled-table {
height: 400px;
overflow: auto;
}
.my-start-date {
width: 40%;
margin: 10px;
/* 修改被禁用的时间背景色,需注意会不会影响全局样式 */
/deep/ .el-date-table td.disabled div {
background-color: pink !important;
}
}
.my-end-date {
width: 40%;
margin: 10px;
}
</style>
页面说明如图:
首先先禁用当天日期之前的日期
// 开始时间组件的focus方法
startFocus() {
// 禁用当天日期之前的日期
this.startPickerOptions = Object.assign({}, this.startPickerOptions, {
disabledDate: (time) => {
let disable = time < new Date(new Date().setHours(0, 0, 0, 0)) // 获取当天0点
return disable
}
})
},
首先,我们实现根据是否有结束日期来限定开始时间时间选择器的禁用时间段,代码如下:
// 开始时间组件的focus方法
startFocus() {
// 1. 将多个时间段按时间正序排序
this.filterDataList.sort(function(a, b) {
// sort按时间正序排序
return Date.parse(a.dayEnd) - Date.parse(b.dayEnd)
})
// 2. 判断是否存在结束日期
let today = this.changeType(new Date()) // 记录当前日期,格式为yyyy-MM-dd
// 存在结束日期
if (this.endDate) {
let changeEndDate = this.changeType(this.endDate) // 转为yyyy-MM-dd格式,用做字符串大小对比
console.log('处理后的日期:', changeEndDate, today)
if (this.filterDataList.length === 0) {
// 时间段数组无数据,不需做处理
} else {
for (let i = 0; i < this.filterDataList.length; i++) {
if (this.filterDataList.length === 1) {
// 长度为1时
if (changeEndDate > this.filterDataList[0].dayEnd) {
// 结束时间大于这个时间段的结束时间
this.limitedStartDate = this.filterDataList[0].dayStart
} else {
this.limitedStartDate = today
}
} else {
// 长度大于1时
// 结束时间大于等于当前时间段的结束时间并且结束时间小于等于下一个时间段的结束时间
if (i < this.filterDataList.length - 1 && changeEndDate >= this.filterDataList[i].dayEnd && changeEndDate <= this.filterDataList[i + 1].dayStart) {
this.limitedStartDate = this.filterDataList[i].dayEnd
} else if (changeEndDate < this.filterDataList[0].dayStart) {
// 结束时间在第一个时间段之前
this.limitedStartDate = today
} else if (changeEndDate > this.filterDataList[this.filterDataList.length - 1].dayEnd) {
// 结束时间在最后一个时间段之后
this.limitedStartDate = this.filterDataList[this.filterDataList.length - 1].dayEnd
}
}
}
}
// 3. 遍历过滤后的数据并禁用相应的时间段
this.startPickerOptions = Object.assign({}, this.startPickerOptions, {
disabledDate: (time) => {
let disable // 定义禁用字段
let finalStart = '' // 定义临时开始日期
if (today < changeEndDate) {
if (today < this.limitedStartDate) {
disable = time < new Date(this.limitedStartDate)
finalStart = this.limitedStartDate
} else {
disable = time < new Date(today).getTime() - 8.64e7 // 减去一天8.64e7,使得可以选择和结束日期同一天
finalStart = today
}
} else {
disable = time < new Date(today).getTime() - 8.64e7 // 减去一天8.64e7
finalStart = today
}
// 此数组存储的为根据结束时间限定的最终的禁用时间段
let tempList = [{
dayStart: finalStart,
dayEnd: changeEndDate
}]
tempList.forEach(item => {
disable = disable || (time.getTime() > new Date(item.dayEnd).getTime())
})
return disable
}
})
} else {
// 结束时间为空,遍历过滤后的数据并禁用相应的时间段
this.startPickerOptions = Object.assign({}, this.startPickerOptions, {
disabledDate: (time) => {
let disable = time < new Date(new Date().setHours(0, 0, 0, 0)) // 获取当天0点
this.filterDataList.forEach(item => {
disable = disable || (time.getTime() > new Date(item.dayStart).getTime() - 8.64e7 && time.getTime() < new Date(item.dayEnd).getTime()) // 减去一天8.64e7
})
return disable
}
})
}
},
接下来,如法炮制,实现根据开始时间来限定结束时间时间选择器的禁用时间段,代码如下:
// 结束时间组件的focus方法
endFocus() {
// 1. 将多个时间段按时间逆序排序
this.filterDataList.sort(function(a, b) {
// sort按时间逆序排序
return Date.parse(a.dayStart) - Date.parse(b.dayStart)
})
// 2. 判断是否存在开始日期
let today = this.changeType(new Date()) // 记录当前日期,格式为yyyy-MM-dd
if (this.startDate) {
let changeStartDate = this.changeType(this.startDate) // 转为yyyy-MM-dd格式,用做字符串大小对比
console.log('处理后的日期:', changeStartDate, today)
if (this.filterDataList.length === 0) {
// 时间段数组无数据,不需做处理
} else {
for (let i = 0; i < this.filterDataList.length; i++) {
if (this.filterDataList.length === 1) {
// 长度为1时
if (changeStartDate > this.filterDataList[0].dayEnd) {
// 开始时间大于这个时间段的结束时间
this.limitedEndDate = ''
} else {
this.limitedEndDate = this.filterDataList[0].dayStart
}
} else {
// 长度大于1时
// 开始时间大于等于当前时间段的开始时间并且开始时间小于等于下一个时间段的开始时间
if (i < this.filterDataList.length - 1 && changeStartDate >= this.filterDataList[i].dayEnd && changeStartDate <= this.filterDataList[i + 1].dayStart) {
this.limitedEndDate = this.filterDataList[i + 1].dayStart
} else if (changeStartDate < this.filterDataList[0].dayStart) {
// 开始时间在第一个时间段之前
this.limitedEndDate = this.filterDataList[0].dayStart
} else if (changeStartDate > this.filterDataList[this.filterDataList.length - 1].dayEnd) {
// 开始时间在最后一个时间段之后
this.limitedEndDate = ''
}
}
}
}
// 3. 遍历过滤后的数据并禁用相应的时间段
this.endPickerOptions = Object.assign({}, this.endPickerOptions, {
disabledDate: (time) => {
let disable // 定义禁用字段
if (changeStartDate) {
// 结束日期可与开始日期为同一天
disable = time.getTime() < new Date(Date.parse(changeStartDate)).getTime() - 8.64e7
} else {
disable = time.getTime() < new Date(today).getTime() - 8.64e7
}
let tempList = [{
dayStart: changeStartDate,
dayEnd: this.limitedEndDate
}]
tempList.forEach(item => {
if (!item.dayEnd) {
} else {
disable = disable || (time.getTime() > new Date(Date.parse(item.dayEnd)).getTime() - 8.64e7) // 减去一天8.64e7
}
})
return disable
}
})
} else {
// 无开始时间
this.endPickerOptions = Object.assign({}, this.endPickerOptions, {
disabledDate: (time) => {
let disable = time < new Date(new Date().setHours(0, 0, 0, 0)) // 获取当天0点
this.filterDataList.forEach(item => {
disable = disable || (time.getTime() > new Date(item.dayStart).getTime() - 8.64e7 && time.getTime() < new Date(item.dayEnd).getTime())
})
return disable
}
})
}
},
到这里,就实现了动态禁用时间段的需求,完整代码如下:
<template>
<div>
<div class="my-start-date">
<x-date :value="startDate" :editable="false" format="yyyy-MM-dd HH:mm" value-format="yyyy-MM-dd HH:mm" type="datetime" :picker-options="startPickerOptions" placeholder="选择开始时间" @change="startValueChangeEvent" @focus="startFocus">
</x-date>
</div>
<div class="my-end-date">
<x-date :value="endDate" :editable="false" format="yyyy-MM-dd HH:mm" value-format="yyyy-MM-dd HH:mm" type="datetime" :picker-options="endPickerOptions" placeholder="选择结束时间" @change="endValueChangeEvent" @focus="endFocus">
</x-date>
</div>
<div>
<el-button style="width: 20%; margin: 10px;" type="primary" @click="addDateRange">
新增时间段
</el-button>
<div>
<x-vxe-table
class="origin-table"
:colConfigs="colConfigs"
:tableData="dateList"
seqType="checkbox"
:pagination="{
total:dateList.length
}"
></x-vxe-table>
</div>
<div>
<x-vxe-table
class="handled-table"
:colConfigs="colConfigs"
:tableData="filterDataList"
seqType="checkbox"
:pagination="{
total:filterDataList.length
}"
></x-vxe-table>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ApaasCustomTestPage1',
components: {},
data() {
return {
finalSelectableRange: '', // 存储时分秒时间段
startDate: '', // 绑定的开始时间
endDate: '', // 绑定的结束时间
limitedStartDate: '', // focus事件限定的开始时间
limitedEndDate: '', // 限定的结束时间
startPickerOptions: {},
endPickerOptions: {},
colConfigs: [ // 表格列配置
{
prop: 'dayStart',
label: '开始日期'
},
{
prop: 'dayEnd',
label: '结束日期'
}
],
dateList: [ // 模拟初始数据
{
dayStart: '2022-03-11',
dayEnd: '2022-03-14'
},
{
dayStart: 'undefined',
dayEnd: ''
},
{
dayStart: 'NaN-NaN-NaN',
dayEnd: '2022-04-08'
},
{
dayStart: 'Mon Feb 29 2022 00:00:00 GMT+0800 (中国标准时间)',
dayEnd: '2022-03-05'
},
{
dayStart: '2022-03-18',
dayEnd: '2022-03-20'
},
{
dayStart: '2022-03-24',
dayEnd: '2022-03-25'
}
],
filterDataList: [] // 过滤后的数据
}
},
computed: {},
watch: {
},
mounted() {
// 处理初始数据
this.filterDate()
},
created() {},
methods: {
/**
* -----------------------------------------------------组件方法-----------------------------------------------------------
*
* 控制时间段禁用的主要逻辑是在时间选择器的focus方法中处理
*/
// 开始时间组件的focus方法
startFocus() {
// 1. 将多个时间段按时间正序排序
this.filterDataList.sort(function(a, b) {
// sort按时间正序排序
return Date.parse(a.dayEnd) - Date.parse(b.dayEnd)
})
// 2. 判断是否存在结束日期
let today = this.changeType(new Date()) // 记录当前日期,格式为yyyy-MM-dd
// 存在结束日期
if (this.endDate) {
let changeEndDate = this.changeType(this.endDate) // 转为yyyy-MM-dd格式,用做字符串大小对比
console.log('处理后的日期:', changeEndDate, today)
if (this.filterDataList.length === 0) {
// 时间段数组无数据,不需做处理
} else {
for (let i = 0; i < this.filterDataList.length; i++) {
if (this.filterDataList.length === 1) {
// 长度为1时
if (changeEndDate > this.filterDataList[0].dayEnd) {
// 结束时间大于这个时间段的结束时间
this.limitedStartDate = this.filterDataList[0].dayStart
} else {
this.limitedStartDate = today
}
} else {
// 长度大于1时
// 结束时间大于等于当前时间段的结束时间并且结束时间小于等于下一个时间段的结束时间
if (i < this.filterDataList.length - 1 && changeEndDate >= this.filterDataList[i].dayEnd && changeEndDate <= this.filterDataList[i + 1].dayStart) {
this.limitedStartDate = this.filterDataList[i].dayEnd
} else if (changeEndDate < this.filterDataList[0].dayStart) {
// 结束时间在第一个时间段之前
this.limitedStartDate = today
} else if (changeEndDate > this.filterDataList[this.filterDataList.length - 1].dayEnd) {
// 结束时间在最后一个时间段之后
this.limitedStartDate = this.filterDataList[this.filterDataList.length - 1].dayEnd
}
}
}
}
// 3. 遍历过滤后的数据并禁用相应的时间段
this.startPickerOptions = Object.assign({}, this.startPickerOptions, {
disabledDate: (time) => {
let disable // 定义禁用字段
let finalStart = '' // 定义临时开始日期
if (today < changeEndDate) {
if (today < this.limitedStartDate) {
disable = time < new Date(this.limitedStartDate)
finalStart = this.limitedStartDate
} else {
disable = time < new Date(today).getTime() - 8.64e7 // 减去一天8.64e7,使得可以选择和结束日期同一天
finalStart = today
}
} else {
disable = time < new Date(today).getTime() - 8.64e7 // 减去一天8.64e7
finalStart = today
}
// 此数组存储的为根据结束时间限定的最终的禁用时间段
let tempList = [{
dayStart: finalStart,
dayEnd: changeEndDate
}]
tempList.forEach(item => {
disable = disable || (time.getTime() > new Date(item.dayEnd).getTime())
})
return disable
}
})
} else {
// 结束时间为空,遍历过滤后的数据并禁用相应的时间段
this.startPickerOptions = Object.assign({}, this.startPickerOptions, {
disabledDate: (time) => {
let disable = time < new Date(new Date().setHours(0, 0, 0, 0)) // 获取当天0点
this.filterDataList.forEach(item => {
disable = disable || (time.getTime() > new Date(item.dayStart).getTime() - 8.64e7 && time.getTime() < new Date(item.dayEnd).getTime()) // 减去一天8.64e7
})
return disable
}
})
}
},
// 开始时间组件的change方法,只要选中的值改变,不管是否点击了确定按钮(关闭弹窗),都会触发
startValueChangeEvent(value) {
console.log('开始时间的值', value)
this.startDate = value
},
// 结束时间组件的focus方法
endFocus() {
// 1. 将多个时间段按时间逆序排序
this.filterDataList.sort(function(a, b) {
// sort按时间逆序排序
return Date.parse(a.dayStart) - Date.parse(b.dayStart)
})
// 2. 判断是否存在开始日期
let today = this.changeType(new Date()) // 记录当前日期,格式为yyyy-MM-dd
if (this.startDate) {
let changeStartDate = this.changeType(this.startDate) // 转为yyyy-MM-dd格式,用做字符串大小对比
console.log('处理后的日期:', changeStartDate, today)
if (this.filterDataList.length === 0) {
// 时间段数组无数据,不需做处理
} else {
for (let i = 0; i < this.filterDataList.length; i++) {
if (this.filterDataList.length === 1) {
// 长度为1时
if (changeStartDate > this.filterDataList[0].dayEnd) {
// 开始时间大于这个时间段的结束时间
this.limitedEndDate = ''
} else {
this.limitedEndDate = this.filterDataList[0].dayStart
}
} else {
// 长度大于1时
// 开始时间大于等于当前时间段的开始时间并且开始时间小于等于下一个时间段的开始时间
if (i < this.filterDataList.length - 1 && changeStartDate >= this.filterDataList[i].dayEnd && changeStartDate <= this.filterDataList[i + 1].dayStart) {
this.limitedEndDate = this.filterDataList[i + 1].dayStart
} else if (changeStartDate < this.filterDataList[0].dayStart) {
// 开始时间在第一个时间段之前
this.limitedEndDate = this.filterDataList[0].dayStart
} else if (changeStartDate > this.filterDataList[this.filterDataList.length - 1].dayEnd) {
// 开始时间在最后一个时间段之后
this.limitedEndDate = ''
}
}
}
}
// 3. 遍历过滤后的数据并禁用相应的时间段
this.endPickerOptions = Object.assign({}, this.endPickerOptions, {
disabledDate: (time) => {
let disable // 定义禁用字段
if (changeStartDate) {
// 结束日期可与开始日期为同一天
disable = time.getTime() < new Date(Date.parse(changeStartDate)).getTime() - 8.64e7
} else {
disable = time.getTime() < new Date(today).getTime() - 8.64e7
}
let tempList = [{
dayStart: changeStartDate,
dayEnd: this.limitedEndDate
}]
tempList.forEach(item => {
if (!item.dayEnd) {
} else {
disable = disable || (time.getTime() > new Date(Date.parse(item.dayEnd)).getTime() - 8.64e7) // 减去一天8.64e7
}
})
return disable
}
})
} else {
// 无开始时间
this.endPickerOptions = Object.assign({}, this.endPickerOptions, {
disabledDate: (time) => {
let disable = time < new Date(new Date().setHours(0, 0, 0, 0)) // 获取当天0点
this.filterDataList.forEach(item => {
disable = disable || (time.getTime() > new Date(item.dayStart).getTime() - 8.64e7 && time.getTime() < new Date(item.dayEnd).getTime())
})
return disable
}
})
}
},
// 结束时间组件的change方法
endValueChangeEvent(value) {
console.log('结束时间的值:', value)
this.endDate = value
},
/**
* ---------------------------------------------------按钮相关方法-------------------------------------------------
*/
// 新增时间段
addDateRange() {
if (this.startDate && this.endDate) {
this.dateList.push({
dayStart: this.startDate,
dayEnd: this.endDate
})
// 处理数据
this.filterDate()
this.$message({
type: 'success',
message: '时间段添加成功!'
})
} else {
this.$message({
type: 'warning',
message: '数据有误,请重新选择时间!'
})
}
},
// 数据过滤 返回数据正常的数据,此方法可用作过滤接口返回数据
filterDate() {
this.filterDataList = []
// 对初始数据进行深拷贝,避免数据污染
let tempArr = this.deepCopy(this.dateList)
tempArr.forEach(item => {
if (item.dayStart && item.dayStart !== 'NaN-NaN-NaN' && item.dayEnd && item.dayEnd !== 'NaN-NaN-NaN') {
item.dayStart = this.changeType(item.dayStart)
item.dayEnd = this.changeType(item.dayEnd)
this.filterDataList.push(item)
}
})
},
/**
* ------------------------------------------------工具函数--------------------------------------------------------
*/
// 转换时间格式,type可不传 此方法可继续拆分实现获取单独的年、月、日、时、分、秒 此处简略
changeType(val, type) {
let date = new Date(val)
let Y = date.getFullYear()
let M = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : (date.getMonth() + 1)
let D = date.getDate() < 10 ? ('0' + date.getDate()) : date.getDate()
val = Y + '-' + M + '-' + D
if (type === 'yyyy-MM-dd HH:mm') {
let h = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours())
let m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
val = val + ' ' + h + ':' + m
}
return val
},
// 获取单独的小时(24小时制)
getHourChange(val) {
let date = new Date(Date.parse(val))
return date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()
},
// 获取单独的分钟
getMinutesChange(val) {
let date = new Date(Date.parse(val))
return date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes()
},
// 深拷贝
deepCopy (obj) {
const result = Array.isArray(obj) ? [] : {}
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
if (obj[key] instanceof RegExp) {
result[key] = obj[key]
} else {
result[key] = this.deepCopy(obj[key]) // 递归复制
}
} else {
result[key] = obj[key]
}
}
}
return result
}
}
}
</script>
<style lang="scss" scoped>
.origin-table {
height: 400px;
overflow: auto;
}
.handled-table {
height: 400px;
overflow: auto;
}
.my-start-date {
width: 40%;
margin: 10px;
/* 修改被禁用的时间背景色,需注意会不会影响全局样式 */
/deep/ .el-date-table td.disabled div {
background-color: pink !important;
}
}
.my-end-date {
width: 40%;
margin: 10px;
}
</style>
实现精确到时分
如果我们想继续精确到时分,我们可以使用时间选择器的selectableRange
属性,然后我们需要监听开始时间和结束时间来控制时分的选择范围,相关代码如下:
watch: {
startDate: {
handler(newVal) {
if (!newVal) {
return
}
let today = new Date()
if (this.changeType(newVal) === this.changeType(today)) {
if (this.endDate && this.changeType(newVal) === this.changeType(this.endDate)) {
this.finalStartSelectableRange = this.getHourChange(today) + ':' + this.getMinutesChange(today) + ':00 - ' + this.getHourChange(this.endDate) + ':' + this.getMinutesChange(this.endDate) + ':59'
} else {
this.finalStartSelectableRange = this.getHourChange(today) + ':' + this.getMinutesChange(today) + ':00 - 23:59:59'
}
} else {
if (this.changeType(newVal) === this.changeType(this.endDate)) {
this.finalStartSelectableRange = '00:00:00 - ' + this.getHourChange(this.endDate) + ':' + this.getMinutesChange(this.endDate) + ':59'
} else {
this.finalStartSelectableRange = '00:00:00 - 23:59:59'
}
}
this.startPickerOptions.selectableRange = this.finalStartSelectableRange
this.$forceUpdate()
}
},
endDate: {
handler(newVal) {
if (!newVal) {
return
}
let today = new Date()
if (this.changeType(newVal) === this.changeType(today)) {
if (this.startDate && this.changeType(newVal) === this.changeType(today)) {
this.finalEndSelectableRange = this.getHourChange(this.startDate) + ':' + this.getMinutesChange(this.startDate) + ':00 - 23:59:59'
} else {
this.finalEndSelectableRange = '00:00:00 - 23:59:59'
}
} else {
if (this.changeType(newVal) === this.changeType(this.startDate)) {
this.finalEndSelectableRange = this.getHourChange(this.startDate) + ':' + this.getMinutesChange(this.startDate) + ':00 - 23:59:59'
} else {
this.finalEndSelectableRange = '00:00:00 - 23:59:59'
}
}
this.endPickerOptions.selectableRange = this.finalEndSelectableRange
this.$forceUpdate()
}
}
},
最终的精确到时分控制时间段的完整代码如下:
<template>
<div>
<div class="my-start-date">
<x-date :value="startDate" :editable="false" format="yyyy-MM-dd HH:mm" value-format="yyyy-MM-dd HH:mm" type="datetime" :picker-options="startPickerOptions" placeholder="选择开始时间" @change="startValueChangeEvent" @focus="startFocus">
</x-date>
</div>
<div class="my-end-date">
<x-date :value="endDate" :editable="false" format="yyyy-MM-dd HH:mm" value-format="yyyy-MM-dd HH:mm" type="datetime" :picker-options="endPickerOptions" placeholder="选择结束时间" @change="endValueChangeEvent" @focus="endFocus">
</x-date>
</div>
<div>
<el-button style="width: 20%; margin: 10px;" type="primary" @click="addDateRange">
新增时间段
</el-button>
<div>
<x-vxe-table
class="origin-table"
:colConfigs="colConfigs"
:tableData="dateList"
seqType="checkbox"
:pagination="{
total:dateList.length
}"
></x-vxe-table>
</div>
<div>
<x-vxe-table
class="handled-table"
:colConfigs="colConfigs"
:tableData="filterDataList"
seqType="checkbox"
:pagination="{
total:filterDataList.length
}"
></x-vxe-table>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ApaasCustomTestPage1',
components: {},
data() {
return {
finalStartSelectableRange: '00:00:00 - 23:59:59', // 存储时分秒时间段
finalEndSelectableRange: '00:00:00 - 23:59:59', // 存储时分秒时间段
startDate: '', // 绑定的开始时间
endDate: '', // 绑定的结束时间
limitedStartDate: '', // focus事件限定的开始时间
limitedEndDate: '', // 限定的结束时间
startPickerOptions: {},
endPickerOptions: {},
colConfigs: [ // 表格列配置
{
prop: 'dayStart',
label: '开始日期'
},
{
prop: 'dayEnd',
label: '结束日期'
}
],
dateList: [ // 模拟初始数据
{
dayStart: '2022-03-11',
dayEnd: '2022-03-14'
},
{
dayStart: 'undefined',
dayEnd: ''
},
{
dayStart: 'NaN-NaN-NaN',
dayEnd: '2022-04-08'
},
{
dayStart: 'Mon Feb 29 2022 00:00:00 GMT+0800 (中国标准时间)',
dayEnd: '2022-03-05'
},
{
dayStart: '2022-03-18',
dayEnd: '2022-03-20'
},
{
dayStart: '2022-03-24',
dayEnd: '2022-03-25'
}
],
filterDataList: [] // 过滤后的数据
}
},
computed: {},
watch: {
startDate: {
handler(newVal) {
if (!newVal) {
return
}
let today = new Date()
if (this.changeType(newVal) === this.changeType(today)) {
if (this.endDate && this.changeType(newVal) === this.changeType(this.endDate)) {
this.finalStartSelectableRange = this.getHourChange(today) + ':' + this.getMinutesChange(today) + ':00 - ' + this.getHourChange(this.endDate) + ':' + this.getMinutesChange(this.endDate) + ':59'
} else {
this.finalStartSelectableRange = this.getHourChange(today) + ':' + this.getMinutesChange(today) + ':00 - 23:59:59'
}
} else {
if (this.changeType(newVal) === this.changeType(this.endDate)) {
this.finalStartSelectableRange = '00:00:00 - ' + this.getHourChange(this.endDate) + ':' + this.getMinutesChange(this.endDate) + ':59'
} else {
this.finalStartSelectableRange = '00:00:00 - 23:59:59'
}
}
this.startPickerOptions.selectableRange = this.finalStartSelectableRange
this.$forceUpdate()
}
},
endDate: {
handler(newVal) {
if (!newVal) {
return
}
let today = new Date()
if (this.changeType(newVal) === this.changeType(today)) {
if (this.startDate && this.changeType(newVal) === this.changeType(today)) {
this.finalEndSelectableRange = this.getHourChange(this.startDate) + ':' + this.getMinutesChange(this.startDate) + ':00 - 23:59:59'
} else {
this.finalEndSelectableRange = '00:00:00 - 23:59:59'
}
} else {
if (this.changeType(newVal) === this.changeType(this.startDate)) {
this.finalEndSelectableRange = this.getHourChange(this.startDate) + ':' + this.getMinutesChange(this.startDate) + ':00 - 23:59:59'
} else {
this.finalEndSelectableRange = '00:00:00 - 23:59:59'
}
}
this.endPickerOptions.selectableRange = this.finalEndSelectableRange
this.$forceUpdate()
}
}
},
mounted() {
// 处理初始数据
this.filterDate()
},
created() {},
methods: {
/**
* -----------------------------------------------------组件方法-----------------------------------------------------------
*
* 控制时间段禁用的主要逻辑是在时间选择器的focus方法中处理
*/
// 开始时间组件的focus方法
startFocus() {
// 1. 将多个时间段按时间正序排序
this.filterDataList.sort(function(a, b) {
// sort按时间正序排序
return Date.parse(a.dayEnd) - Date.parse(b.dayEnd)
})
// 2. 判断是否存在结束日期
let today = this.changeType(new Date()) // 记录当前日期,格式为yyyy-MM-dd
// 存在结束日期
if (this.endDate) {
let changeEndDate = this.changeType(this.endDate) // 转为yyyy-MM-dd格式,用做字符串大小对比
console.log('处理后的日期:', changeEndDate, today)
if (this.filterDataList.length === 0) {
// 时间段数组无数据,不需做处理
} else {
for (let i = 0; i < this.filterDataList.length; i++) {
if (this.filterDataList.length === 1) {
// 长度为1时
if (changeEndDate > this.filterDataList[0].dayEnd) {
// 结束时间大于这个时间段的结束时间
this.limitedStartDate = this.filterDataList[0].dayStart
} else {
this.limitedStartDate = today
}
} else {
// 长度大于1时
// 结束时间大于等于当前时间段的结束时间并且结束时间小于等于下一个时间段的结束时间
if (i < this.filterDataList.length - 1 && changeEndDate >= this.filterDataList[i].dayEnd && changeEndDate <= this.filterDataList[i + 1].dayStart) {
this.limitedStartDate = this.filterDataList[i].dayEnd
} else if (changeEndDate < this.filterDataList[0].dayStart) {
// 结束时间在第一个时间段之前
this.limitedStartDate = today
} else if (changeEndDate > this.filterDataList[this.filterDataList.length - 1].dayEnd) {
// 结束时间在最后一个时间段之后
this.limitedStartDate = this.filterDataList[this.filterDataList.length - 1].dayEnd
}
}
}
}
// 3. 遍历过滤后的数据并禁用相应的时间段
this.startPickerOptions = Object.assign({}, this.startPickerOptions, {
disabledDate: (time) => {
let disable // 定义禁用字段
let finalStart = '' // 定义临时开始日期
if (today < changeEndDate) {
if (today < this.limitedStartDate) {
disable = time < new Date(this.limitedStartDate)
finalStart = this.limitedStartDate
} else {
disable = time < new Date(today).getTime() - 8.64e7 // 减去一天8.64e7,使得可以选择和结束日期同一天
finalStart = today
}
} else {
disable = time < new Date(today).getTime() - 8.64e7 // 减去一天8.64e7
finalStart = today
}
// 此数组存储的为根据结束时间限定的最终的禁用时间段
let tempList = [{
dayStart: finalStart,
dayEnd: changeEndDate
}]
tempList.forEach(item => {
disable = disable || (time.getTime() > new Date(item.dayEnd).getTime())
})
return disable
},
selectableRange: this.finalStartSelectableRange
})
} else {
// 结束时间为空,遍历过滤后的数据并禁用相应的时间段
this.startPickerOptions = Object.assign({}, this.startPickerOptions, {
disabledDate: (time) => {
let disable = time < new Date(new Date().setHours(0, 0, 0, 0)) // 获取当天0点
this.filterDataList.forEach(item => {
disable = disable || (time.getTime() > new Date(item.dayStart).getTime() - 8.64e7 && time.getTime() < new Date(item.dayEnd).getTime()) // 减去一天8.64e7
})
return disable
},
selectableRange: this.finalStartSelectableRange
})
}
},
// 开始时间组件的change方法,只要选中的值改变,不管是否点击了确定按钮(关闭弹窗),都会触发
startValueChangeEvent(value) {
console.log('开始时间的值', value)
this.startDate = value
},
// 结束时间组件的focus方法
endFocus() {
// 1. 将多个时间段按时间逆序排序
this.filterDataList.sort(function(a, b) {
// sort按时间逆序排序
return Date.parse(a.dayStart) - Date.parse(b.dayStart)
})
// 2. 判断是否存在开始日期
let today = this.changeType(new Date()) // 记录当前日期,格式为yyyy-MM-dd
if (this.startDate) {
let changeStartDate = this.changeType(this.startDate) // 转为yyyy-MM-dd格式,用做字符串大小对比
console.log('处理后的日期:', changeStartDate, today)
if (this.filterDataList.length === 0) {
// 时间段数组无数据,不需做处理
} else {
for (let i = 0; i < this.filterDataList.length; i++) {
if (this.filterDataList.length === 1) {
// 长度为1时
if (changeStartDate > this.filterDataList[0].dayEnd) {
// 开始时间大于这个时间段的结束时间
this.limitedEndDate = ''
} else {
this.limitedEndDate = this.filterDataList[0].dayStart
}
} else {
// 长度大于1时
// 开始时间大于等于当前时间段的开始时间并且开始时间小于等于下一个时间段的开始时间
if (i < this.filterDataList.length - 1 && changeStartDate >= this.filterDataList[i].dayEnd && changeStartDate <= this.filterDataList[i + 1].dayStart) {
this.limitedEndDate = this.filterDataList[i + 1].dayStart
} else if (changeStartDate < this.filterDataList[0].dayStart) {
// 开始时间在第一个时间段之前
this.limitedEndDate = this.filterDataList[0].dayStart
} else if (changeStartDate > this.filterDataList[this.filterDataList.length - 1].dayEnd) {
// 开始时间在最后一个时间段之后
this.limitedEndDate = ''
}
}
}
}
// 3. 遍历过滤后的数据并禁用相应的时间段
this.endPickerOptions = Object.assign({}, this.endPickerOptions, {
disabledDate: (time) => {
let disable // 定义禁用字段
if (changeStartDate) {
// 结束日期可与开始日期为同一天
disable = time.getTime() < new Date(Date.parse(changeStartDate)).getTime() - 8.64e7
} else {
disable = time.getTime() < new Date(today).getTime() - 8.64e7
}
let tempList = [{
dayStart: changeStartDate,
dayEnd: this.limitedEndDate
}]
tempList.forEach(item => {
if (!item.dayEnd) {
} else {
disable = disable || (time.getTime() > new Date(Date.parse(item.dayEnd)).getTime() - 8.64e7) // 减去一天8.64e7
}
})
return disable
},
selectableRange: this.finalEndSelectableRange
})
} else {
// 无开始时间
this.endPickerOptions = Object.assign({}, this.endPickerOptions, {
disabledDate: (time) => {
let disable = time < new Date(new Date().setHours(0, 0, 0, 0)) // 获取当天0点
this.filterDataList.forEach(item => {
disable = disable || (time.getTime() > new Date(item.dayStart).getTime() - 8.64e7 && time.getTime() < new Date(item.dayEnd).getTime())
})
return disable
},
selectableRange: this.finalEndSelectableRange
})
}
},
// 结束时间组件的change方法
endValueChangeEvent(value) {
console.log('结束时间的值:', value)
this.endDate = value
},
/**
* ---------------------------------------------------按钮相关方法-----------------------------------------------------------
*/
// 新增时间段
addDateRange() {
if (this.startDate && this.endDate) {
this.dateList.push({
dayStart: this.startDate,
dayEnd: this.endDate
})
// 处理数据
this.filterDate()
this.$message({
type: 'success',
message: '时间段添加成功!'
})
} else {
this.$message({
type: 'warning',
message: '数据有误,请重新选择时间!'
})
}
},
// 数据过滤 返回数据正常的数据,此方法可用作过滤接口返回数据
filterDate() {
this.filterDataList = []
// 对初始数据进行深拷贝,避免数据污染
let tempArr = this.deepCopy(this.dateList)
tempArr.forEach(item => {
if (item.dayStart && item.dayStart !== 'NaN-NaN-NaN' && item.dayEnd && item.dayEnd !== 'NaN-NaN-NaN') {
item.dayStart = this.changeType(item.dayStart)
item.dayEnd = this.changeType(item.dayEnd)
this.filterDataList.push(item)
}
})
},
/**
* ------------------------------------------------工具函数--------------------------------------------------------
*/
// 转换时间格式,type可不传 此方法可继续拆分实现获取单独的年、月、日、时、分、秒 此处简略
changeType(val, type) {
let date = new Date(Date.parse(val))
let Y = date.getFullYear()
let M = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : (date.getMonth() + 1)
let D = date.getDate() < 10 ? ('0' + date.getDate()) : date.getDate()
val = Y + '-' + M + '-' + D
if (type === 'yyyy-MM-dd HH:mm') {
let h = (date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours())
let m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes())
val = val + ' ' + h + ':' + m
}
return val
},
// 获取单独的小时(24小时制)
getHourChange(val) {
let date = new Date(Date.parse(val))
return date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours()
},
// 获取单独的分钟
getMinutesChange(val) {
let date = new Date(Date.parse(val))
return date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes()
},
// 深拷贝
deepCopy (obj) {
const result = Array.isArray(obj) ? [] : {}
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
if (obj[key] instanceof RegExp) {
result[key] = obj[key]
} else {
result[key] = this.deepCopy(obj[key]) // 递归复制
}
} else {
result[key] = obj[key]
}
}
}
return result
}
}
}
</script>
<style lang="scss" scoped>
.origin-table {
height: 400px;
overflow: auto;
}
.handled-table {
height: 400px;
overflow: auto;
}
.my-start-date {
width: 40%;
margin: 10px;
/* 修改被禁用的时间背景色,需注意会不会影响全局样式 */
/deep/ .el-date-table td.disabled div {
background-color: pink !important;
}
}
.my-end-date {
width: 40%;
margin: 10px;
}
</style>
最终的效果如下
完整代码文件
时间选择器实现禁用多个时间段(只精确到年月日)并能动态更新禁用时间段
时间选择器实现精确到时分禁用多个时间段并能动态更新禁用时间段