网站制作视频教程下载,百度地图api wordpress,专业提供网站建设服务包括,拜年视频制作软件开篇 本篇文章旨在实现一个基于el-table的表格点选和框选功能#xff0c;除此之外#xff0c;还支持多种模式的切换、自定义勾选日期等。且#xff0c;该表格后续可能还会持续优化#xff01; 功能介绍
表格点选和框选功能#xff08;没有点击ctrl键的情况下#xff09;…开篇 本篇文章旨在实现一个基于el-table的表格点选和框选功能除此之外还支持多种模式的切换、自定义勾选日期等。且该表格后续可能还会持续优化 功能介绍
表格点选和框选功能没有点击ctrl键的情况下表格点选和框选功能的互斥功能意思是当现在选择了该单元格后按住ctrl键再次点击就会取消选择当前日期高亮周、双周、月份模式的切换以及自定义日期的选择对于可点选和框选功能的范围判断表头和名称不可选择
代码实现
index.vue
templatediv classcontainer!-- 顶部功能栏区域 --data classtop-bar!-- 切换模式按钮 --el-button sizemini typeprimary clickturnWeek周/el-buttonel-button sizemini typeprimary clickturnTwoWeeks两周/el-buttonel-button sizemini typeprimary clickturnMonth月/el-button!-- 自定义时间范围选择器 --div classtime-range-pickerspan classarrow-iconel-iconArrowLeftBold //el-icon/span!-- 起始日期 --el-date-pickerclasstime-pickerv-modeldateForm.startDatetypedatetime:shortcutsshortcutschange(value) handleDateChange(startDate, value)/span stylepadding: 0 10px-/span!--结束日期 --el-date-pickerclasstime-pickerv-modeldateForm.endDatetypedatetime:shortcutsshortcutschange(value) handleDateChange(endDate, value)/span classarrow-iconel-iconArrowRightBold //el-icon/span/div/data!-- 表格区域 --div classtable-container!-- 月份选择 --month-schedule-table:table-datatableData:switch-modelswitchModel:dateFormdateForm:customDateFlagcustomDateFlagupdateDateFormhandleUpdateDateForm:table-widthtableWidth/month-schedule-table/div/div
/templatescript
import {defineComponent,onMounted,reactive,toRefs,ref,watchEffect,onUpdated,onUnmounted,nextTick,
} from vue;
import MonthScheduleTable from ./components/MonthScheduleTable.vue;
export default defineComponent({name: DepartmentScheduleConfig,components: {MonthScheduleTable,},setup() {const state reactive({tableData: [],switchModel: month,// 自定义日期选择相关dateForm: {startDate: ,endDate: ,},customDateFlag: false, // 是否在自定义切换日期的标识});const tableWidth ref(0);onMounted(() {// 先初始化一些假数据用着initData();nextTick(() {getTableWidth();});});const getTableWidth () {// 求表格宽度let container document.querySelector(.container);if (container) {const observer new ResizeObserver(() {tableWidth.value container.clientWidth;});observer.observe(container);// 初始获取一次宽度tableWidth.value container.clientWidth;}};// 每次更新表格时也要重新获取表格宽度onUpdated(() {getTableWidth();});window.addEventListener(resize, getTableWidth);// 页面关闭时移除事件监听器onUnmounted(() {window.removeEventListener(resize, getTableWidth);});// 切换成周模式const turnWeek () {state.switchModel week;state.customDateFlag false;};// 切换成两周模式const turnTwoWeeks () {state.switchModel twoWeeks;state.customDateFlag false;};// 切换成月模式const turnMonth () {state.switchModel month;state.customDateFlag false;};// 初始化数据const initData () {const obj {};for (let i 0; i 31; i) {obj[date${i}] null;}const arr [];for (let j 1; j 10; j) {const tmpObj { ...obj };tmpObj.id j;tmpObj.name zzz${j};arr.push(tmpObj);}console.log(arr, arr);arr[0].date1 X;arr[3].date3 Y;state.tableData arr;};/*** 自定义日期相关*/// 日期选择面板扩展const shortcuts [{text: 今天,value: new Date(),},{text: 昨天,value: () {const date new Date();date.setDate(date.getDate() - 1);return date;},},{text: 一周前,value: () {const date new Date();date.setDate(date.getDate() - 7);return date;},},];// 日期change方法const handleDateChange (key, value) {state.dateForm[key] new Date(value);console.log(dateForm, state.dateForm);state.customDateFlag true;};// 更新日期选择器const handleUpdateDateForm (val) {console.log(val, val);state.dateForm[startDate] val.startDate;state.dateForm[endDate] val.endDate;};return {...toRefs(state),turnWeek,turnTwoWeeks,turnMonth,tableWidth,shortcuts,handleDateChange,handleUpdateDateForm,};},
});
/scriptstyle stylescss scoped
.container {width: 100%;.top-bar {display: flex;.time-range-picker {margin-left: 5px;.arrow-icon {cursor: pointer;padding: 8px;}}}.table-container {width: 100%;margin-top: 5px;}
}
/style
MonthScheduleTable.vue
templatediv classcontainer!-- 表格区域 --data classwrapmousedownhandleMouseDownmousemovehandleMouseMovemouseuphandleMouseUp!-- 框选矩形 --div v-ifrectVisible classselect-rect:style{ left: rect.left px, top: rect.top px, width: rect.width px, height: rect.height px }/div!-- 表格 --div classconel-table :datatableData border stylewidth: 100% :cell-styleCellStylecell-clickhandleCellClick!-- 姓名 --el-table-column propname label姓名 :widthgetColumnWidth() /el-table-column!-- 日期 这一块日期应该根据月、周、两周三种模式来动态显示不应该写死--el-table-columnclassheaderSelect:label-class-nameisWeekend(item.date, item.day)v-for(item, index) in currDateArr :keyindex :labelitem.dateel-table-column :label-class-nameisWeekend(item.date, item.day):class-nameisWeekend(item.date, item.day):propgetProp(index) :labelitem.day :class{headerSelect: true}:widthgetColumnWidth()template #defaultscopediv :data-rowscope.row.name :data-columngetProp(index) classcell-content{{ scope.row[getProp(index)] }}/div/template/el-table-column/el-table-column/el-table/div/data/div
/templatescript
import { computed } from vue/reactivity;
import { defineComponent, onMounted, reactive, toRefs, watchEffect, ref, onUpdated } from vue
export default defineComponent({name: MonthScheduleTable,emits: [updateDateForm],props: {tableData: {type: Array,default: () []},switchModel: {type: String,default: month},tableWidth: {type: Number,default: 0},dateForm: {type: Object,default: () ({})},customDateFlag: {type: Boolean,default: false}},setup(props, { emit }) {const state reactive({tableData: computed(() props.tableData), // 数组数据源// 鼠标框选功能相关selectedCells: [], // 选择的单元格rectVisible: false, // 是否显示框选矩形rect: {left: 0, top: 0, width: 0, height: 0}, // 矩形的坐标信息downX: 0, // 鼠标按下时的X坐标downY: 0, // 鼠标按下时的Y坐标upX: 0, // 鼠标松开时的X坐标upY: 0, // 鼠标松开时的Y坐标isMouseDown: false, // 鼠标是否移动isCellClick: false,// 是否是单元格被点击了})// 给当前的单元格分配propconst getProp idx {return date${idx}}// 判断鼠标是否在可选区域内const isInSelectableArea event {const target event.target;// 查找最近的表头元素th 或 theadconst headerElement target.closest(th, thead);// 如果目标元素位于表头中返回 falseif (headerElement) return false;const headerSelect document.querySelector(.con)if (!headerSelect) return false;const headerRect headerSelect.getBoundingClientRect()const isInHeader event.clientX headerRect.left event.clientX headerRect.right event.clientY headerRect.top event.clientY headerRect.bottom;const cell target.closest(td, th);const columnIndex cell ? cell.cellIndex : undefined;return isInHeader columnIndex 0; // 从第二列开始}// 判断当前是否只点击了一个单元格// 表格单元格点击事件const handleCellClick (row, column, cell, event) {if (!isInSelectableArea(event)) return;state.isCellClick trueif (event.ctrlKey) { // 判断是否按下了Ctrl键// 当鼠标左键ctrl同时按下时实现多选功能/*** 若当前的cell.classList包含highlight类则移除并把该单元格的数据移除出数组;* 若不包含证明之前并未选择过则添加hightlight类并把数据push进数组*/if (cell.classList.contains(highlight)) {cell.classList.remove(highlight)// 将该单元格的数据移出数组const index state.selectedCells.findIndex(item item.row row.name item.column column.property)if (index -1) {state.selectedCells.splice(index, 1)}} else {cell.classList.add(highlight)// 将数据加入数组state.selectedCells.push({ row: row.name, column: column.property, value: row[column.property] });}} else {// 普通高亮的逻辑// 清除所有已高亮的单元格const highlightedCells document.querySelectorAll(.highlight)highlightedCells.forEach(cell cell.classList.remove(highlight))// 清空当前已选择的数组state.selectedCells []// 将当前单元格高亮cell.classList.add(highlight)// 将数据加入数组state.selectedCells.push({ row: row.name, column: column.property, value: row[column.property] });}// 将单元格点击标识和鼠标移动标识置为falsestate.isCellClick falsestate.isMouseDown false}// 当鼠标落下时const handleMouseDown event {if (!isInSelectableArea(event)) return;// 判断是否在可选区域内state.isMouseDown true/*** 在鼠标落下时应当判断是ctrl鼠标左键触发的事件还是直接由鼠标左键触发的事件* 若是直接由鼠标左键点击触发的事件则应该清空当前的selectedCells数组并移除所有单元格的的高亮*/if (!event.ctrlKey) {const highlightedCells document.querySelectorAll(.highlight)highlightedCells.forEach(cell cell.classList.remove(highlight))state.selectedCells []}state.rectVisible truestate.downX event.clientXstate.downY event.clientYstate.upX event.clientXstate.upY event.clientYstate.rect.left document.querySelector(.wrap).getBoundingClientRect().leftstate.rect.top document.querySelector(.wrap).getBoundingClientRect().topstate.rect.width 0state.rect.height 0}// 当鼠标移动时const handleMouseMove event {if (!state.rectVisible || !isInSelectableArea(event)) return;// 判断是否在可选区域内if (state.rectVisible) {const moveX event.clientXconst moveY event.clientY// 计算框选矩形的宽高state.rect.width Math.abs(moveX - state.downX)state.rect.height Math.abs(moveY - state.downY)state.rect.left Math.min(moveX, state.downX) - event.currentTarget.getBoundingClientRect().leftstate.rect.top Math.min(moveY, state.downY) - event.currentTarget.getBoundingClientRect().top}}// 当鼠标抬起时const handleMouseUp (event) {if (!state.rectVisible || !isInSelectableArea(event)) return;// 判断是否在可选区域内if (state.rectVisible) {state.rectVisible false// 获取所有单元格const cells document.querySelectorAll(.el-table__body-wrapper td)const rect state.rect// 判断是否有一些单元格已经高亮let anyHighlighted false// 用于存放被框选的单元格const selectedCells []cells.forEach(cell {const cellRect cell.getBoundingClientRect()const tableRect document.querySelector(.wrap).getBoundingClientRect()// 计算相对位置const cellLeft cellRect.left - tableRect.leftconst cellTop cellRect.top - tableRect.top// 判断单元格是否在框选区域内const cellInSelection (cellLeft rect.left rect.width cellLeft cellRect.width rect.left cellTop rect.top rect.height cellTop cellRect.height rect.top)if (cellInSelection) {selectedCells.push(cell)}})if (selectedCells.length 1) {selectedCells.forEach(sltCell {// 判断单元格是否已经高亮const isHighlighted sltCell.classList.contains(highlight)if (isHighlighted) {anyHighlighted true}// 若使用ctrl鼠标左键if (event.ctrlKey) {// 若被框选的单元格全都没有高亮则将这些单元格高亮并将数据push到数组中if (!anyHighlighted) {sltCell.classList.add(highlight)state.selectedCells.push(getCellData(sltCell))} else {/*** 若被框选的单元格中有已经高亮的单元格则需要把其中高亮的单元格取消高亮并把这些被取消高亮* 的单元格的数据从数组中移除* 同时把没有高亮的单元格高亮并将数据push到数组中*/if (isHighlighted) {sltCell.classList.remove(highlight)const idxToRemove state.selectedCells.findIndex(sc sc.row getCellData(sltCell).row sc.column getCellData(sltCell).column)if (idxToRemove -1) {state.selectedCells.splice(idxToRemove, 1)}} else {// 若当前没有高亮的则高亮并把数据添加到数组中sltCell.classList.add(highlight)state.selectedCells.push(getCellData(sltCell))}}} else {// 普通点击框选事件sltCell.classList.add(highlight)state.selectedCells.push(getCellData(sltCell))} })}}}// 获取单元格数据const getCellData cell {const cellContent cell.querySelector(.cell-content)if (cellContent) {const row cellContent.dataset.rowconst column cellContent.dataset.columnconst value cellContent.textContentreturn { row, column, value }}}// 根据当前的模式动态获取数据const daysOfWeek [日, 一, 二, 三, 四, 五, 六 ]// 月份模式const getMonthDays () {const days []let currYear new Date().getFullYear()let currMonth new Date().getMonth()// 获取当前月的第一天const startDate new Date(currYear, currMonth, 2)startDate.setHours(0, 0, 0, 0) // 确保是当天的0点// 获取当前月的最后一天const endDate new Date(currYear, currMonth 1, 0)endDate.setHours(23, 59, 59, 999) // 确保是当天的最后一毫秒const date new Date(new Date(currYear, currMonth, 1))while(date.getMonth() currMonth) {days.push({day: date.getDate().toString(),date: daysOfWeek[date.getDay()]})date.setDate(date.getDate() 1)}// 转化为时间选择器可以使用的格式const minDateFormatted startDate.toISOString().slice(0, 10)const maxDateFormatted endDate.toISOString().slice(0, 10)emit(updateDateForm, { startDate: minDateFormatted, endDate: maxDateFormatted })return days}// 一周模式const getWeekDays () {const days []const currDay new Date().getDay()const startDate new Date(new Date()) // 当选择了这些模式之后应该把开始日期和结束日期传给父组件以便父组件上的时间选择器来展示// 找到最小和最大的日期startDate.setDate(new Date().getDate() - currDay 1) // 获取当前周的周一// 获取当前格式的当前周的周日const endDate new Date(startDate)endDate.setDate(startDate.getDate() 6)for (let i 0; i 7; i) {const d new Date(startDate)d.setDate(startDate.getDate() i)days.push({day: d.getDate().toString(),date: daysOfWeek[d.getDay()]})}// 转化为时间选择器可以使用的格式const minDateFormatted startDate.toISOString().slice(0, 10)const maxDateFormatted endDate.toISOString().slice(0, 10)emit(updateDateForm, { startDate: minDateFormatted, endDate: maxDateFormatted })return days}// 两周模式const getTwoWeeksDays () {const days []const currDay new Date().getDay()const startDate new Date(new Date()) startDate.setDate(new Date().getDate() - currDay 1) // 获取当前周的周一// 获取当前格式的当前周的周日const endDate new Date(startDate)endDate.setDate(startDate.getDate() 13)for (let i 0; i 14; i) {const d new Date(startDate)d.setDate(startDate.getDate() i)days.push({day: d.getDate().toString(),date: daysOfWeek[d.getDay()]})}// 转化为时间选择器可以使用的格式const minDateFormatted startDate.toISOString().slice(0, 10)const maxDateFormatted endDate.toISOString().slice(0, 10)emit(updateDateForm, { startDate: minDateFormatted, endDate: maxDateFormatted })return days}// 自定义选择日期模式const getCustomDateRange (startDate, endDate) {const days []const start new Date(startDate)const end new Date(endDate)const date new Date(start)while (date end) {days.push({day: date.getDate().toString(),date: daysOfWeek[date.getDay()]})date.setDate(date.getDate() 1)}return days}// 获取当前日期const isCrrentDay () {let d new Date().getDate()return d.toString()}// 判断是否是周末const isWeekend (date, day) {if (day isCrrentDay()) return currDay;if (date 六 || date 日) return weekend;else return }const headerCellStyle (row, column, rowIndex, columnIndex) {// console.log(row, row);}const CellStyle (row, column) { // console.log(row, row);if (row.column.className weekend) {return {backgroundColor: rgb(116, 107, 230)}}}const currDateArr computed(() {if (!props.customDateFlag props.switchModel month) {return getMonthDays()} else if (!props.customDateFlag props.switchModel week) {return getWeekDays()} else if (!props.customDateFlag props.switchModel twoWeeks) {return getTwoWeeksDays()} else if (props.customDateFlag) {return getCustomDateRange(props.dateForm.startDate, props.dateForm.endDate)}})var currWidth ref(0)watchEffect(() {currWidth.value computed(() props.tableWidth).value// 根据当前日期给表头设置背景if (currDateArr.value.length 0) {const ths document.querySelectorAll(.el-table__header .el-table__cell)if (ths.length 0) {// 获取当前日期let date new Date().getDay()console.log(date, date);}}})onUpdated(() {// 根据当前日期给表头设置背景if (currDateArr.value.length 0) {const ths document.querySelectorAll(.el-table__header .el-table__cell)if (ths.length 0) {// 获取当前日期let date new Date().getDay()}}})// 动态设置列的宽度const getColumnWidth () {const containerWidth currWidth.value // 减去name列的值const columnCount currDateArr.value.length 1return ${Math.floor(containerWidth / columnCount)}px}return {...toRefs(state),handleCellClick,currDateArr,getProp,handleMouseDown,handleMouseMove,handleMouseUp,getColumnWidth,isWeekend,headerCellStyle,CellStyle}}
})
/scriptstyle stylescss scoped
/* 当单元格被选择时高亮 */
::v-deep .el-table td.highlight {background-color: yellow!important;color: red;
}::v-deep .el-table thead th.weekend {background-color: rgb(116, 107, 230)!important;
}::v-deep .el-table th.currDay {background-color: green!important;
}::v-deep .el-table .headerSelect {background-color: green!important;
}.container {width: 100%;.wrap {width: 100%;height: 100vh;position: relative;display: flex;/* 子项超出容器宽度时自动换行 */flex-wrap: wrap;/* 禁止用户复制文本 */user-select: none;.select-rect {position: absolute;border: 1px dashed #999;background-color: rgba(0,0,0,0.1);z-index: 1000;pointer-events: none;}.con {max-width: 100%;}}
}
/style效果演示 注 因为此表格还在持续开发中所以现在代码还有很多有待优化和完善的地方还请谅解。 希望本文对您能有所帮助 感谢阅读