(一)实现效果
24小时的区间选择,间隔为0.5小时,选择后的显示时间段文字。选择方式为点击,点击一次为开始选择,鼠标右移为增加选择区间,左移为清除选中。
(二)代码
<template>
<div class="time-select-body" style="" :class="{ onlyReadClass: props.readonly }">
<div style="display: flex">
<span style="width: 60px; line-height: 60px">{{ props.periodName }}</span>
<div class="hours-container">
<div v-for="(item, index) in hours" :key="index" class="hours-item">
<div class="hours-item-header">{{ compItem(item) }}</div>
<div class="hours-item-value">
<div :class="compClass(2 * item)" @click="handleClick(2 * item)" @mouseover="handleHover(2 * item)"></div>
<div
:class="compClass(2 * item + 1)"
@click="handleClick(2 * item + 1)"
@mouseover="handleHover(2 * item + 1)"
></div>
</div>
</div>
</div>
</div>
<div style="margin-left: 60px">
<div v-if="tips[0] === '向'">向右选中,向左取消选择</div>
<div v-else>
<span v-for="option in tips" :key="option">{{ option }} </span>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch, defineEmits } from 'vue'
const emit = defineEmits(['change', 'el.form.change'])
const props = defineProps({
readonly: {
type: Boolean,
default: false
},
periodName: String,
data: Array
})
const hours = ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23])
const selectStart = ref(false)
const startIndex = ref('')
const timeRangeListIndex = ref([]) // 选中的下标
const tempRangeIndex = ref([]) // 预选下标
const tips = ref('向右选中,向左取消选择')
const sendTimeList = ref([]) // 你的初始值
const timeRangeList = ref([])
const transformedIndex = () => {
timeRangeListIndex.value = []
timeRangeList.value = sendTimeList.value
timeRangeList.value.forEach((element) => {
const [startTime, endTime] = element.match(/\d+\:\d+/g)
if (startTime && endTime) {
let [startHour, startMin] = startTime.split(':')
let [endHour, endMin] = endTime.split(':')
if (startHour && startMin && endHour && endMin) {
let startNum, endNum
if (startMin === '00') {
startNum = 2 * parseInt(startHour)
} else {
startNum = 2 * parseInt(startHour) + 1
}
if (endMin === '00') {
endNum = 2 * parseInt(endHour) - 1
} else {
endNum = 2 * parseInt(endHour)
}
while (endNum >= startNum) {
timeRangeListIndex.value.push(startNum)
startNum++
}
} else {
console.error('时间段格式不正确')
}
} else {
console.error('没有拿到开始时间或结束时间或者时间段格式不对')
}
})
tips.value = timeRangeList.value && timeRangeList.value.length > 0 ? timeRangeList.value : '向右选中,向左取消选择'
}
const transformedSection = () => {
timeRangeList.value = []
let startTime = '',
endTime = '',
len = hours.value.length
for (let index = hours.value[0] * 2; index < 2 * (len + 1); index++) {
if (timeRangeListIndex.value.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 * hours.value.length + 1) {
endTime = `${Math.floor((index + 1) / 2)}:00`
timeRangeList.value.push(`${startTime ? startTime : '23:30'}-${endTime}`)
startTime = ''
endTime = ''
}
} else {
if (startTime && endTime) {
timeRangeList.value.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}`
timeRangeList.value.push(`${startTime}-${endTime}`)
startTime = ''
endTime = ''
}
}
}
tips.value = timeRangeList.value && timeRangeList.value.length > 0 ? timeRangeList.value : '向右选中,向左取消选择'
}
const handleClick = (index) => {
if (selectStart.value) {
if (index === startIndex.value) {
const idx = timeRangeListIndex.value.indexOf(index)
if (idx > -1) {
timeRangeListIndex.value.splice(idx, 1)
} else {
timeRangeListIndex.value.push(startIndex.value)
}
} else if (index > startIndex.value) {
while (index >= startIndex.value) {
timeRangeListIndex.value.push(startIndex.value)
startIndex.value++
}
timeRangeListIndex.value = Array.from(new Set(timeRangeListIndex.value))
} else {
while (startIndex.value >= index) {
const idx = timeRangeListIndex.value.indexOf(index)
if (idx > -1) {
timeRangeListIndex.value.splice(idx, 1)
}
index++
}
}
startIndex.value = ''
tempRangeIndex.value = []
transformedSection()
} else {
startIndex.value = index
}
selectStart.value = !selectStart.value
}
const handleHover = (index) => {
if (selectStart.value) {
tempRangeIndex.value = []
if (index > startIndex.value) {
while (index >= startIndex.value) {
tempRangeIndex.value.push(index)
index--
}
} else {
while (startIndex.value >= index) {
tempRangeIndex.value.push(index)
index++
}
}
}
}
const compClass = (index) => {
if (index === startIndex.value) {
return 'hours-item-left preSelected'
}
if (index >= startIndex.value) {
if (tempRangeIndex.value.indexOf(index) > -1) {
return 'hours-item-left preSelected'
}
} else {
if (tempRangeIndex.value.indexOf(index) > -1) {
return 'hours-item-left unSelected'
}
}
return timeRangeListIndex.value.indexOf(index) > -1 ? 'hours-item-left selected' : 'hours-item-left'
}
const compItem = (item) => {
return item < 10 ? `0${item}` : item
}
const formList = ref([])
onMounted(() => {
transformedIndex()
// 通过timeRangeListIndex控制显示选择
// timeRangeListIndex.value = [20, 22, 39]
// console.log('props.periodName', props.data)
})
watch(
() => timeRangeList.value,
(value) => {
emit('change', [timeRangeListIndex.value, props.periodName])
// emit('change', periodName.value) // 触发父组件的校验规则
}
)
watch(() => sendTimeList.value, {
handler() {
transformedIndex()
},
deep: true
})
</script>
<style scoped>
.time-select-body {
min-width: 1045px;
/* min-height: 80px; */
}
.hours-container {
display: flex;
cursor: pointer;
}
.hours-item {
width: 40px;
height: 60px;
border: 1px solid #c2d0f3;
/* border-right: none; */
text-align: center;
}
.hours-item .selected {
background-color: #4e84fe;
border-bottom: 1px solid #c2d0f3;
}
.hours-item .preSelected {
background-color: #8eaffc;
border-bottom: 1px solid #c2d0f3;
}
.hours-item .unSelected {
background-color: #ffffff;
border-bottom: 1px solid #c2d0f3;
}
.hours-item &:last-child {
border-right: 1px solid #c2d0f3;
}
.hours-item-header {
width: 100%;
height: 30px;
line-height: 30px;
border-bottom: 1px solid #c2d0f3;
}
.hours-item-value {
width: 100%;
height: 30px;
box-sizing: border-box;
display: flex;
}
.hours-item-value .hours-item-left,
.hours-item-right {
width: 50%;
height: 100%;
border-right: 1px solid #c2d0f3;
box-sizing: border-box;
}
.hours-item-left,
.hours-item-right &:last-child {
border-right: none;
}
.tips {
width: 100%;
line-height: 30px;
}
.onlyReadClass {
user-select: none; /* 禁止选择文本 */
pointer-events: none; /* 禁止鼠标拖动 */
outline: none; /* 禁止键盘焦点 */
}
</style>