需求:开始日期不能小于结束日期,两个选择框之间的互相限制
月份:
固定十二个月,当月开始时间默认选择月第一天,结束时间默认选择月最后一天;
月份选择只允许选择当前月份
天:
将当月对应的每天按照时间段划分,段数不做限制。
时间段支持任意位置插入(新增)、删除。
每个时间段具有包含属性,同一时刻不允许在两个时间段中出现包含。
所有时间段之和必须满足24小时全覆盖且不允许交叉
复制:
点击同上,将上个月份的配置参数拷贝至当前月份
代码实现
开发环境:Vue
需引入插件:ElementUI,moment.js
template
<template>
<div class='picker-data-time'>
<el-form-item label="月份" required class="month-picker" v-for="item, index in month_picker_data" :key="index">
<el-collapse v-model="activeNames" @change="handleChange">
<slot name="title">
<div class="title-content">
<el-col :span="5">
<el-date-picker v-model="item.start_day" type="date" placeholder="选择日期" @change="sel_change"
:editable=false value-format="yyyy-MM-dd" :picker-options="picker_options_init(index, 0)"
:default-value="default_picker_day_start(index)">
</el-date-picker>
</el-col>
<el-col :span="2">
<center>——</center>
</el-col>
<el-col :span="5">
<el-date-picker v-model="item.end_day" type="date" :editable=false placeholder="选择日期"
:default-value="default_picker_day_end(index)" :picker-options="picker_options_init(index, 1, item)">
</el-date-picker>
</el-col>
<el-col :span="5">
<center>
<el-button type="text" @click="copy_data_from_up(item, index)" v-if="index != 0">同上</el-button>
</center>
</el-col>
<el-col :span="index != 0 ? 5 : 10" style="textAlign:right">
峰尖平谷
</el-col>
</div>
</slot>
<el-collapse-item :name="index" class="content-picker-time">
<table>
<tr>
<th v-for="itemy, indey in table_title" :key="indey">{{ itemy }}</th>
</tr>
<tr v-for="(itemz, indez) of item.time_picker" :key="indez">
<td class="table-name">
<el-select v-model="itemz.time_label" placeholder="请选择">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</td>
<td>
<div>
<el-col :span="18">
<el-time-select v-model="itemz.start_time" :editable="false"
:picker-options="picker_time_init(index, indez, 0, itemz)" placeholder="选择时间"
:disabled="indez != 0 && month_picker_data[index].time_picker[indez - 1].end_time == ''" />
</el-col>
</div>
<div style="padding-top: 5%">
( <el-checkbox v-model="itemz.include_start_time" :disabled="is_disable(index, indez, 0, itemz)" />含 )
</div>
</td>
<td>
<div>
<el-col :span="12">
<el-time-select v-model="itemz.end_time" :editable="false"
:picker-options="picker_time_init(index, indez, 1, itemz)" placeholder="选择时间"
:disabled="itemz.start_time == ''" />
</el-col>
</div>
<div style="padding-top: 7%">
( <el-checkbox v-model="itemz.include_end_time" :disabled="is_disable(index, indez, 1, itemz)" />含 )
</div>
</td>
<td>
<el-input v-model="itemz.single_price" class="inputTable" />
</td>
<td>
<el-button type="text" @click="del_time_picker(index, indez)">
<p style="color: red">删除</p>
</el-button>
<el-button type="text" @click="add_time_picker(index, indez)">
<p style="color: blue">新增</p>
</el-button>
</td>
</tr>
</table>
</el-collapse-item>
</el-collapse>
</el-form-item>
</div>
</template>
js
export default {
//import引入的组件需要注入到对象中才能使用
name: 'picker-data-time',
components: {},
data() {
//这里存放数据
return {
table_title: ['名称', '起', '止', '单元(元/kWh)', '操作'],
month_picker_data: [],
default_time: this.$moment().format(`YYYY-MM-01`),
activeNames: [],
start_picker_options: {},
end_picker_options: {},
options: [{
value: 1,
label: '尖段'
}, {
value: 2,
label: '峰段'
}, {
value: 3,
label: '平段'
}, {
value: 4,
label: '谷段'
}],
value: ''
};
},
//监听属性 类似于data概念
computed: {},
//监控data中的数据变化
watch: {},
//方法集合
methods: {
create_picker_data() {
this.month_picker_data = JSON.parse(JSON.stringify(new Array(12)
.fill({
start_day: '',
end_day: '',
time_picker: [{
time_label: 1,
start_time: '',
include_start_time: false,
end_time: '',
include_end_time: false,
single_price: ''
}]
})))
},
default_picker_day_start(index) {
if (this.month_picker_data[index].start_day) return
const _start_day = this.$moment(new Date(`${new Date().getFullYear()}-${('0' + (+index + 1)).substr(-2)}-01`)).format("YYYY-MM-DD");
this.month_picker_data[index].start_day = _start_day
return new Date(_start_day)
},
default_picker_day_end(index) {
if (this.month_picker_data[index].end_day) return
const _end_day = this.$moment(`
${new Date().getFullYear()}-${('0' + (+index + 1)).substr(-2)}-01`)
.endOf('month').format("YYYY-MM-DD");
this.month_picker_data[index].end_day = _end_day;
return new Date(_end_day)
},
/**
* 初始化月份选择区域
* @param {月份索引} index
* @param {0开始、1结束} type
* @param {月份对象值} item
*/
picker_options_init(index, type, item) {
if (0 == type) return {
disabledDate: time => time.getMonth() != index
}
if (1 == type) return {
disabledDate: time => time.getMonth() != index
|| time.getDate() < item.start_day?.substr(-2)
|| time.getFullYear() != item.start_day?.substr(0, 4)
}
},
/**
* 初始化时间选择
* @param {*月份索引} index
* @param {*日时间分段索引} indez
* @param {*类型 0-开始时间 ;1-结束时间} type
* @param {* 月份对象} itemz
*/
picker_time_init(index, indez, type, itemz) {
const step = '00:30';
const step_mins = time_to_mins(step)
const pick_length = this.month_picker_data[index].time_picker.length
const prev_itemz = this.month_picker_data[index].time_picker[indez - 1] || {}
const prev_end_time = prev_itemz.end_time || ''
const next_itemz = this.month_picker_data[index].time_picker[indez + 1] || {}
const next_start_time = next_itemz.start_time || ''
const next_end_time = next_itemz.end_time || ''
const next_next_start_time = this.month_picker_data[index].time_picker[indez + 2]?.start_time || ''
const now_end_time = itemz.end_time
const now_start_time = itemz.start_time
const result = {
start: '',
end: '',
step
}
function time_to_mins(time = '00:00') {
if (!time) return 0
const temp_time = time.split(':')
return temp_time.shift() * 60 + temp_time.shift() * 1
}
function mins_to_time(time) {
if (!time) return '00:00'
return `${('0' + parseInt(time / 60)).substr(-2)}:${('0' + parseInt(time % 60)).substr(-2)}`
}
let temp_arr
if (0 == type) {
if (0 == indez) {
result.start = '00:00';
temp_arr = [].concat(time_to_mins(now_end_time), time_to_mins(next_start_time), 1440 - step_mins).filter(_ => _ != 0);
result.end = mins_to_time(Math.min(...temp_arr))
} else {
temp_arr = [].concat(time_to_mins(prev_end_time), time_to_mins(next_start_time), time_to_mins(now_end_time)).filter(_ => _ != 0);
result.start = mins_to_time(Math.min(...temp_arr))
result.end = mins_to_time(Math.max(...temp_arr))
}
}
if (1 == type) {
if (indez == 0 && pick_length == 1) {
result.start = now_start_time
result.end = mins_to_time(1440);
return result
}
if (indez == pick_length - 1) {
result.end = mins_to_time(1440);
temp_arr = [].concat(time_to_mins(now_start_time), time_to_mins(prev_end_time));
result.start = mins_to_time(Math.max(...temp_arr))
} else {
temp_arr = [].concat(time_to_mins(now_start_time), time_to_mins(prev_end_time)).filter(_ => _ != 0)
let _start = time_to_mins(now_start_time) == 0 ? 0 : Math.max(...temp_arr);
if (_start < time_to_mins(next_start_time)) {
result.start = mins_to_time(_start)
result.end = next_start_time
}
else {
if (next_start_time == '') {
temp_arr = [].concat(time_to_mins(next_start_time), time_to_mins(next_end_time), time_to_mins(next_next_start_time)).filter(_ => _ != 0);
const _res = Math.max(...temp_arr)
const _end = temp_arr.length > 0 ? _res : 1440
result.start = mins_to_time(_start)
result.end = mins_to_time(_end)
} else {
result.start = ''
result.end = ''
}
}
}
}
return result
},
sel_change(val) {
console.log('sel_change :>> ', val);
},
copy_data_from_up(item, index) {
const source = this.month_picker_data[index - 1].time_picker;
this.$set(this.month_picker_data[index], 'time_picker', JSON.parse(JSON.stringify(source)))
},
handleChange(val) {
console.log(val);
},
del_time_picker(index, indez) {
this.month_picker_data[index].time_picker.length > 1 && this.month_picker_data[index].time_picker.splice(indez, 1)
},
add_time_picker(index, indez) {
const a = {
time_label: 1,
start_time: '',
include_start_time: false,
end_time: '',
include_end_time: false,
single_price: ''
};
this.month_picker_data[index].time_picker.splice(indez + 1, 0, a)
},
is_disable(index, indez, type, itemz) {
if (0 == type) {
if (indez == 0) {
const end_time = this.month_picker_data[index].time_picker.at(-1)?.end_time == '24:00' ? '00:00' : ''
const end_time_include = this.month_picker_data[index].time_picker.at(-1)?.include_end_time;
return itemz.start_time == end_time && end_time_include
}
const prev_end_time = this.month_picker_data[index].time_picker[indez - 1]?.end_time
const prev_include_end_time = this.month_picker_data[index].time_picker[indez - 1]?.include_end_time
return prev_end_time == itemz.start_time && prev_include_end_time
}
if (1 == type) {
if (indez == this.month_picker_data[index].time_picker.length - 1) {
const start_time = this.month_picker_data[index].time_picker.at(0)?.start_time == '00:00' ? '24:00' : ''
const start_time_include = this.month_picker_data[index].time_picker.at(0)?.include_start_time;
return start_time_include && start_time == itemz.end_time
}
const next_start_time = this.month_picker_data[index].time_picker[indez + 1]?.start_time
const next_include_start_time = this.month_picker_data[index].time_picker[indez + 1]?.include_start_time
return next_start_time == itemz.end_time && next_include_start_time
}
},
time_to_mins(time = '00:00') {
if (!time) return 0
const temp_time = time.split(':')
return temp_time.shift() * 60 + temp_time.shift() * 1
},
mins_to_time(time) {
if (!time) return '00:00'
return `${('0' + parseInt(time / 60)).substr(-2)}:${('0' + parseInt(time % 60)).substr(-2)}`
},
data_judgment(result = true) {
this.month_picker_data.forEach((itemx, index) => {
try {
let temp_sum = 0
itemx.time_picker.forEach((itemy) => {
const { time_label, start_time, include_start_time, end_time, include_end_time, single_price } = itemy
if (!time_label || !start_time || !end_time || !single_price) {
throw new Error(`${index + 1}月输入不完整!`)
}
let temp_variable = +include_start_time + (+include_end_time)
temp_sum += this.time_to_mins(end_time) - this.time_to_mins(start_time) + temp_variable;
})
if (temp_sum != 1440 + itemx.time_picker.length) {
throw new Error(`${index + 1}月时间有误,请检查后重新提交`)
}
} catch (error) {
result = false
setTimeout(() => {
this.$message({
type: 'error',
message: error
})
}, 0)
}
})
return result
}
},
//生命周期 - 创建完成(可以访问当前this实例)
created() {
this.create_picker_data();
},
//生命周期 - 挂载完成(可以访问DOM元素)
mounted() {
},
beforeCreate() { }, //生命周期 - 创建之前
beforeMount() { }, //生命周期 - 挂载之前
beforeUpdate() { }, //生命周期 - 更新之前
updated() { }, //生命周期 - 更新之后
beforeDestroy() { }, //生命周期 - 销毁之前
destroyed() { }, //生命周期 - 销毁完成
activated() { }, //如果页面有keep-alive缓存功能,这个函数会触发
}
css
$color: #5859f5;
$bgcColor: #fafafa;
.month-picker {
// ::v-deep .el-collapse{
// // vertical-align: top;
// display: flex;
// justify-content: space-around;
// align-items: center;
// }
.title-content {
// display: flex;
::v-deep .el-input {
margin-top: 3px;
width: 100%;
}
::v-deep .el-input--suffix .el-input__inner {
padding-left: 30px;
}
}
.content-picker-time {
::v-deep .el-input {
width: 100%;
}
table {
border-collapse: collapse;
width: 100%;
}
tr {
padding: 0;
margin: 0;
mask-border-width: 1px;
border: 1px solid #f8f8fb;
border-left-width: 0px;
border-right-width: 0px;
}
td {
&.table-name {
max-width: 140px;
padding-right: 8px;
}
text-align: center;
}
th {
background-color: $bgcColor;
height: 5vh;
// width: 30px;
}
}
}
效果图
时间起止限制
插入时间限制
版权归原作者 David_leil 所有, 如有侵权,请联系我们删除。