vue3 多区间时间选择器组件

(一)实现效果

           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 }} &nbsp; </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>

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值