济宁亿蜂网站建设,风机 东莞网站建设,安徽建设厅官方网站,新闻发布会稿件星期-时间范围选择器 功能介绍属性说明事件说明实现代码使用范例 根据业务需要#xff0c;实现了一个可选择时间范围的周视图。用户可以通过鼠标拖动来选择时间段#xff0c;并且可以通过快速选择组件来快速选择特定的时间范围。 功能介绍
时间范围选择#xff1a;用户可以… 星期-时间范围选择器 功能介绍属性说明事件说明实现代码使用范例 根据业务需要实现了一个可选择时间范围的周视图。用户可以通过鼠标拖动来选择时间段并且可以通过快速选择组件来快速选择特定的时间范围。 功能介绍
时间范围选择用户可以通过鼠标拖动来选择时间段。快速选择提供快速选择组件用户可以通过点击快速选择特定的时间范围如上午、下午、工作日、周末等。自定义样式可以通过selectionColor 属性自定义选中区域的颜色。数据绑定通过 modelValue属性与父组件进行数据绑定实时更新选择的时间范围。
属性说明
modelValue绑定的时间范围数据类型为数组。 selectionColor选中区域的颜色类型为字符串默认为 ‘rgba(5, 146, 245, 0.6)’。 showQuickSelect是否显示快速选择组件类型为布尔值默认为 true。
事件说明
update:modelValue当选择的时间范围发生变化时触发返回更新后的时间范围数据。
实现代码
index.vue
templatediv classzt-weektimediv :class{ zt-schedule-notransi: mode } :style[styleValue, { backgroundColor: selectionColor }] classzt-schedule/divtable classzt-weektime-tablethead classzt-weektime-headtrtd classweek-td rowspan8/tdtd v-fort in theadArr :keyt :colspan2{{ t }}:00/td/tr!-- tr--!-- td v-fort in 48 :keyt classhalf-hour--!-- {{ t % 2 0 ? 00 : 30 }}--!-- /td--!-- /tr--/theadtbody classzt-weektime-bodytr v-fort in weekData :keyt.rowtd{{ t.value }}/tdtdv-forn in t.child:key${n.row}-${n.col}:class[weektime-atom-item, { selected: isSelected(n) }]:data-timen.col:data-weekn.row:style{ --selection-color: selectionColor }mousedowncellDown(n)mouseentercellEnter(n)mouseupcellUp(n)/td/trtrtd classzt-weektime-preview colspan49QuickSelect v-ifshowQuickSelect stylepadding: 10px 0 selecthandleQuickSelect /!-- div classg-clearfix zt-weektime-con--!-- span classg-pull-left{{ hasSelection ? 已选择时间段 : 可拖动鼠标选择时间段 }}/span--!-- /div--!-- div v-ifhasSelection classzt-weektime-time--!-- div v-for(ranges, week) in formattedSelections :keyweek--!-- p v-ifranges.length--!-- span classg-tip-text{{ week }}/span--!-- span{{ ranges.join(、) }}/span--!-- /p--!-- /div--!-- /div--/td/tr/tbody/table/div
/template
script setup
import { computed, defineEmits, defineProps, onMounted, ref, watch } from vue;
import QuickSelect from ./quickSelect.vue;defineOptions({name: ZtWeekTimeRange
});const props defineProps({modelValue: {type: Array,required: true,default: () []},selectionColor: {type: String,default: rgba(5, 146, 245, 0.6)},showQuickSelect: {type: Boolean,default: true}
});const emit defineEmits([update:modelValue]);const weekData ref([{ row: 0, value: 周一, child: [] },{ row: 1, value: 周二, child: [] },{ row: 2, value: 周三, child: [] },{ row: 3, value: 周四, child: [] },{ row: 4, value: 周五, child: [] },{ row: 5, value: 周六, child: [] },{ row: 6, value: 周日, child: [] }
]);// UI State
const width ref(0);
const height ref(0);
const left ref(0);
const top ref(0);
const mode ref(0);
const startRow ref(0);
const startCol ref(0);
const endRow ref(0);
const endCol ref(0);
const isDragging ref(false);
const theadArr ref([]);onMounted(() {theadArr.value Array.from({ length: 24 }, (_, i) i);initializeGridData();syncSelectionFromValue();
});watch(() props.modelValue, syncSelectionFromValue, { deep: true });function handleQuickSelect({ type, start, end, days }) {if (type morning || type afternoon) {// 清除现有选择weekData.value.forEach((week) {week.child.forEach((slot) {if (slot.col start slot.col end) {slot.selected true;}});});} else if (type workdays || type weekend) {days.forEach((dayIndex) {weekData.value[dayIndex].child.forEach((slot) {slot.selected true;});});} else if (type all) {weekData.value.forEach((week) {week.child.forEach((slot) {slot.selected true;});});} else if (type clean) {weekData.value.forEach((week) {week.child.forEach((slot) {slot.selected false;});});}emitSelectionChange();
}function formatTimeRange(start, end) {const formatTime (slots) {const hours Math.floor(slots / 2);const minutes (slots % 2) * 30;return ${String(hours).padStart(2, 0)}:${String(minutes).padStart(2, 0)};};return ${formatTime(start)}-${formatTime(end)};
}function initializeGridData() {weekData.value.forEach((week) {week.child Array.from({ length: 48 }, (_, i) ({row: week.row,col: i,selected: false}));});
}function syncSelectionFromValue() {weekData.value.forEach((week) {week.child.forEach((slot) {slot.selected false;});});props.modelValue.forEach((selection) {const { week, ranges } selection;const weekIndex weekData.value.findIndex((w) w.value week);if (weekIndex ! -1) {ranges.forEach((range) {const [start, end] range.split(-).map((time) {const [hours, minutes] time.split(:).map(Number);return hours * 2 (minutes 30 ? 1 : 0);});for (let i start; i end; i) {const slot weekData.value[weekIndex].child[i];if (slot) slot.selected true;}});}});
}const styleValue computed(() ({width: ${width.value}px,height: ${height.value}px,left: ${left.value}px,top: ${top.value}px
}));const hasSelection computed(() {return weekData.value.some((week) week.child.some((slot) slot.selected));
});const formattedSelections computed(() {const selections {};weekData.value.forEach((week) {const ranges [];let start null;week.child.forEach((slot, index) {if (slot.selected start null) {start index;} else if (!slot.selected start ! null) {ranges.push(formatTimeRange(start, index - 1));start null;}});if (start ! null) {ranges.push(formatTimeRange(start, week.child.length - 1));}if (ranges.length) {selections[week.value] ranges;}});return selections;
});function isSelected(slot) {return slot.selected;
}function cellDown(item) {isDragging.value true;startRow.value item.row;startCol.value item.col;endRow.value item.row;endCol.value item.col;const ele document.querySelector(td[data-week${item.row}][data-time${item.col}]);if (ele) {width.value ele.offsetWidth;height.value ele.offsetHeight;left.value ele.offsetLeft;top.value ele.offsetTop;}mode.value 1;
}function cellEnter(item) {if (!isDragging.value) return;endRow.value item.row;endCol.value item.col;const ele document.querySelector(td[data-week${item.row}][data-time${item.col}]);if (!ele) return;const minRow Math.min(startRow.value, endRow.value);const maxRow Math.max(startRow.value, endRow.value);const minCol Math.min(startCol.value, endCol.value);const maxCol Math.max(startCol.value, endCol.value);const startEle document.querySelector(td[data-week${minRow}][data-time${minCol}]);if (startEle) {left.value startEle.offsetLeft;top.value startEle.offsetTop;width.value (maxCol - minCol 1) * ele.offsetWidth;height.value (maxRow - minRow 1) * ele.offsetHeight;}
}function cellUp() {if (!isDragging.value) return;const minRow Math.min(startRow.value, endRow.value);const maxRow Math.max(startRow.value, endRow.value);const minCol Math.min(startCol.value, endCol.value);const maxCol Math.max(startCol.value, endCol.value);const isDeselecting weekData.value[minRow].child[minCol].selected;weekData.value.forEach((week, weekIndex) {if (weekIndex minRow weekIndex maxRow) {week.child.forEach((slot, slotIndex) {if (slotIndex minCol slotIndex maxCol) {slot.selected !isDeselecting;}});}});isDragging.value false;width.value 0;height.value 0;mode.value 0;emitSelectionChange();
}function emitSelectionChange() {const selections weekData.value.map((week) {const ranges [];let start null;week.child.forEach((slot, index) {if (slot.selected start null) {start index;} else if (!slot.selected start ! null) {ranges.push(formatTimeRange(start, index - 1));start null;}});if (start ! null) {ranges.push(formatTimeRange(start, week.child.length - 1));}return {week: week.value,ranges};}).filter((week) week.ranges.length 0);emit(update:modelValue, selections);
}
/scriptstyle scoped
.zt-weektime {min-width: 600px;position: relative;display: inline-block;
}.zt-schedule {position: absolute;width: 0;height: 0;pointer-events: none;transition: background-color 0.3s ease;
}.zt-schedule-notransi {transition:width 0.12s cubizt-bezier(0.4, 0, 0.2, 1),height 0.12s cubizt-bezier(0.4, 0, 0.2, 1),top 0.12s cubizt-bezier(0.4, 0, 0.2, 1),left 0.12s cubizt-bezier(0.4, 0, 0.2, 1);
}.zt-weektime-table {border-collapse: collapse;width: 100%;
}.zt-weektime-table th,
.zt-weektime-table td {user-select: none;border: 1px solid #dee4f5;text-align: center;min-width: 12px;line-height: 1.8em;transition: background 0.2s ease;
}.zt-weektime-table tr {height: 30px;
}.zt-weektime-head {font-size: 12px;
}.zt-weektime-head .week-td {min-width: 40px;width: 70px;
}.half-hour {color: #666;font-size: 10px;
}.zt-weektime-body {font-size: 12px;
}.weektime-atom-item {user-select: unset;background-color: #f5f5f5;cursor: pointer;width: 20px;transition: background-color 0.3s ease;
}.weektime-atom-item.selected {background-color: var(--selection-color, rgba(5, 146, 245, 0.6));animation: selectPulse 0.3s ease-out;
}keyframes selectPulse {0% {transform: scale(0.95);opacity: 0.7;}50% {transform: scale(1.02);opacity: 0.85;}100% {transform: scale(1);opacity: 1;}
}.zt-weektime-preview {line-height: 2.4em;padding: 0 10px;font-size: 14px;
}.zt-weektime-preview .zt-weektime-con {line-height: 46px;user-select: none;
}.zt-weektime-preview .zt-weektime-time {text-align: left;line-height: 2.4em;
}.zt-weektime-preview .zt-weektime-time p {max-width: 625px;line-height: 1.4em;word-break: break-all;margin-bottom: 8px;
}.g-clearfix:after,
.g-clearfix:before {clear: both;content: ;display: table;
}.g-pull-left {float: left;
}.g-tip-text {color: #999;
}
/style
quickSelect.vue
templatediv classquick-selectel-button-groupel-button v-foroption in quickOptions :keyoption.key sizesmall clickhandleQuickSelect(option.key){{ option.label }}/el-button/el-button-groupel-button-groupel-button sizesmall clickhandleQuickSelect(all) 全选/el-buttonel-button sizesmall clickhandleQuickSelect(clean) 清空/el-button/el-button-group/div
/templatescript setup
const props defineProps({show: {type: Boolean,default: true}
});const emit defineEmits([select]);const quickOptions [{ key: morning, label: 上午 },{ key: afternoon, label: 下午 },{ key: workdays, label: 工作日 },{ key: weekend, label: 周末 }
];const timeRanges {morning: { start: 16, end: 23 }, // 8:00-12:00afternoon: { start: 26, end: 35 }, // 13:00-18:00workdays: { days: [0, 1, 2, 3, 4] }, // 周一到周五weekend: { days: [5, 6] }, // 周六周日all: {}, // 全选clean: {} // 清空
};function handleQuickSelect(type) {emit(select, { type, ...timeRanges[type] });
}
/scriptstyle scoped
.quick-select {display: flex;justify-content: space-between;
}
/style
使用范例
效果
实现代码
templatedivh1时间段选择示例/h1div classcolor-pickerspan选择颜色/spanel-color-picker v-modelselectedColor :predefinepredefineColors show-alpha //divZtWeekTimeRangev-modelselectedTimeRanges:selection-colorselectedColor:show-quick-selecttrueupdate:modelValuehandleTimeRangeChange/div classselected-rangesh3选中的时间段/h3pre styleheight: 200px{{ JSON.stringify(selectedTimeRanges, null, 2) }}/pre/divdiv classdemo-controlsbutton classdemo-button clicksetDemoData加载示例数据/buttonbutton classdemo-button clickclearSelection清除选择/button/div/div
/templatescript setup
import { ref } from vue;defineOptions({name: 星期时间范围选择器,
});const selectedTimeRanges ref([]);
const selectedColor ref(rgba(5, 146, 245, 0.6));const predefineColors [rgba(5, 146, 245, 0.6),rgba(64, 158, 255, 0.6),rgba(103, 194, 58, 0.6),rgba(230, 162, 60, 0.6),rgba(245, 108, 108, 0.6)
];function handleTimeRangeChange(newRanges) {console.log(Time ranges updated:, newRanges);
}function setDemoData() {selectedTimeRanges.value [{week: 周一,ranges: [09:00-12:00, 14:00-18:30]},{week: 周三,ranges: [10:30-16:00]},{week: 周五,ranges: [09:30-12:00, 13:30-17:30]}];
}function clearSelection() {selectedTimeRanges.value [];
}
/scriptstyle
.selected-ranges {padding: 15px;background: #f5f5f5;border-radius: 4px;
}pre {background: #fff;padding: 10px;border-radius: 4px;overflow-x: auto;
}.demo-controls {margin-top: 20px;display: flex;gap: 10px;
}.demo-button {padding: 8px 16px;background-color: #0592f5;color: white;border: none;border-radius: 4px;cursor: pointer;transition: background-color 0.2s;
}.demo-button:hover {background-color: #0481d9;
}
/style