- 需求背景
- 解决效果
- 视频效果
- balancedTimeElement.vue
需求背景
实现一个分片的时间间隔选择器,需要把显示时间段显示成图表,涉及一下集中数据转换
- [“02:30-05:30”,“07:30-10:30”,“14:30-17:30”]
- ‘[(2,5),(7,10),(14,17)]’
- [4, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17, 18, 19, 20, 28, 29, 30, 31, 32, 33, 34]
解决效果
视频效果
时间间隔选择器
balancedTimeElement.vue
<!--/*** @author: liuk* @date: 2023/11/28* @describe: 时间间隔选择器* @CSDN:https://blog.csdn.net/hr_beginner?type=blog*/-->
<template><div><div class="hours-container"><div class="hours-item-header-box"><div class="hours-item-header" v-for="(_, i) in hours.slice(0,24)" :key="i">{{(i + 1 + '').padStart(2, 0)}}</div></div><div class="hours-item-box" ref="hoursItemRef"><template v-for="(_, i) in hours" :key="i"><div class="hours-item" :class="compClass(i)" @click="handleClick(i)" @mouseover="handleHover(i)"></div></template></div></div><div class="tips">提示: 向右选中,向左取消选择</div></div>
</template><script lang="ts" setup>
import {reactive, toRefs, ref, watch} from "vue";// Props
const props = defineProps(['manual_period'])// Emits
const emit = defineEmits(['data-passed'])// Ref
const hoursItemRef = ref(null)type numOrstr = number | string
type arrOrstr = any[] | stringinterface ModelType {hours: number[]selectStart: booleanstartIndex: numOrstrtimeRangeList: string[]timeRangeListIndex: numOrstr[]tempRangeIndex: number[]tips: arrOrstr
}const model: ModelType = reactive({hours: new Array(48).fill('').map((_, i) => i),selectStart: false,// 开始startIndex: '',// 开始下标timeRangeList: [],// 选择的时间段timeRangeListIndex: [],// 选中的下标tempRangeIndex: [],// 预选下标tips: '',})
const {hours,selectStart,startIndex,timeRangeList,timeRangeListIndex,tempRangeIndex,tips,
} = toRefs(model)watch(() => props.manual_period, (data) => {//'[(2,5),(7,10),(14,17)]'const str = data.replace(/\(|\)/g, (val) => { // '[[2,5],[7,10],[14,17]]'switch (val) {case "(":return "["case ")":return "]"}})model.timeRangeListIndex = JSON.parse(str).map(item => {const [x, y] = itemreturn new Array(2 * y - 2 * x + 1).fill(0).map((_, i) => i + 2 * x)}).flat()// [4, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17, 18, 19, 20, 28, 29, 30, 31, 32, 33, 34]Array.from(hoursItemRef.value.children).forEach((dom: HTMLDivElement, index) => {if (model.timeRangeListIndex.includes(index)) {dom.className += ' selected'}})
})// 下标区间转换成时间区间
const transformedSection = () => {model.timeRangeList = [];let startTime = '', endTime = '', len = model.hours.length;for (let index = model.hours[0] * 2; index < 2 * (len + 1); index++) {if (model.timeRangeListIndex.indexOf(index) > -1) {if (startTime) {// 如果有开始时间,直接确定结束时间let endHour = Math.floor((index + 1) / 2);let endMin = (index + 1) % 2 === 0 ? "00" : "30";endTime = `${endHour < 10 ? '0' + endHour : endHour}:${endMin}`;} else {// 没有开始时间,确定当前点为开始时间let startHour = Math.floor(index / 2);let startMin = index % 2 === 0 ? "00" : "30";startTime = `${startHour < 10 ? '0' + startHour : startHour}:${startMin}`;}if (index === 2 * model.hours.length + 1) { // 如果是最后一格,直接结束endTime = `${Math.floor((index + 1) / 2)}:00`;model.timeRangeList.push(`${startTime ? startTime : "23:30"}-${endTime}`);startTime = '';endTime = '';}} else { // 若这个点不在选择区间,确定一个时间段if (startTime && endTime) {model.timeRangeList.push(`${startTime}-${endTime}`);startTime = '';endTime = '';} else if (startTime && !endTime) {// 这里可能只选半个小时let endHour = Math.floor(index / 2);let endMin = index % 2 === 0 ? "00" : "30";endTime = `${endHour < 10 ? '0' + endHour : endHour}:${endMin}`;model.timeRangeList.push(`${startTime}-${endTime}`);startTime = '';endTime = '';}}}model.tips = model.timeRangeList && model.timeRangeList.length > 0 ? model.timeRangeList : '';emit('data-passed', model.tips);
}// 点击事件
const handleClick = (index) => {if (model.selectStart) {if (index === model.startIndex) {// 双击取反if (model.timeRangeListIndex.indexOf(index) > -1) {model.timeRangeListIndex.splice(model.timeRangeListIndex.indexOf(index), 1);} else {model.timeRangeListIndex.push(model.startIndex);}} else if (index > model.startIndex) {// 选取数据--向右添加,向左取消while (index >= model.startIndex) {model.timeRangeListIndex.push(model.startIndex);model.startIndex = +model.startIndex + 1;}model.timeRangeListIndex = Array.from(new Set(model.timeRangeListIndex));} else {// 删除数据while (model.startIndex >= index) {if (model.timeRangeListIndex.indexOf(index) > -1) {model.timeRangeListIndex.splice(model.timeRangeListIndex.indexOf(index), 1);}index++;}}model.startIndex = '';transformedSection();model.tempRangeIndex = [];} else {model.startIndex = index;}model.selectStart = !model.selectStart;
}
// 预选区间
const handleHover = (index) => {if (model.selectStart) {model.tempRangeIndex = [];if (index > model.startIndex) {// 选取数据--向右添加,向左取消while (index >= model.startIndex) {model.tempRangeIndex.push(index);index--;}} else {// 删除数据while (model.startIndex >= index) {model.tempRangeIndex.push(index);index++;}}}
}
// 是否选中,计算className
const compClass = (index) => {if (index === model.startIndex) {return 'hours-item-left preSelected';}if (index >= model.startIndex) {if (model.tempRangeIndex.indexOf(index) > -1) {return 'hours-item-left preSelected';}} else {if (model.tempRangeIndex.indexOf(index) > -1) {return 'hours-item-left unSelected';}}return model.timeRangeListIndex.indexOf(index) > -1 ? 'hours-item-left selected' : 'hours-item-left';
}
</script><style lang="scss" scoped>
.hours-container {cursor: pointer;color: slategray;.hours-item-header-box {display: flex;width: 100%;height: 30px;.hours-item-header {width: 30px;height: 30px;text-align: center;box-sizing: border-box;line-height: 30px;border: 1px solid #5a5a5a;border-left: none;border-bottom: none;&:first-child {border-left: 1px solid #5a5a5a;}}}.hours-item-box {display: flex;width: 100%;.hours-item {width: 15px;height: 30px;border: 1px solid #474747;box-sizing: border-box;&.selected {background-color: #ffbf00 !important;border-bottom: 1px solid #c2d0f3;}&.preSelected {background-color: rgb(255, 191, 0);border-bottom: 1px solid #c2d0f3;}&.unSelected {background-color: #ffffff;border-bottom: 1px solid #c2d0f3;}}}
}.tips {width: 100%;line-height: 30px;margin-top: 10px;
}</style>