如何在ASP.NET生成的表格中手动实现跨列着色单元格的宽度调整功能
如何在ASP.NET生成的表格中手动实现跨列着色单元格的宽度调整功能
嘿,我看你已经用ASP.NET生成了年度周计划表格,还完成了同色单元格合并、加了resize样式,但原生的resize: horizontal只能拉伸单元格本身,没法实现你想要的「跨列调整宽度(改变占用周数)」的效果。接下来我给你一步步讲解怎么用原生JS手动实现这个拖拽调整功能,完全不用框架,纯原生就能搞定:
一、先给合并后的单元格标记关键信息
首先,我们需要在合并单元格的时候,给每个可调整的单元格加上起始周和结束周的自定义属性,这样后面拖拽时能准确知道它覆盖的范围。修改你现有JS里的合并逻辑:
function mergeAndEnableResizingForColoredCells(row) { let lastColor = null; let mergeStart = null; let colspan = 0; let startIndex = 0; // 记录合并起始的单元格索引(从0开始) row.querySelectorAll('td').forEach((cell, index) => { const currentColor = cell.style.backgroundColor; if (currentColor === lastColor && currentColor !== '') { colspan++; } else { finalizeMerge(mergeStart, colspan, startIndex); colspan = 1; mergeStart = cell; startIndex = index; // 更新合并起始索引 } lastColor = currentColor; // 处理行尾的最后一组单元格 if (index === row.cells.length - 1) { finalizeMerge(mergeStart, colspan, startIndex); } }); } function finalizeMerge(cell, colSpan, startIndex) { if (cell && colSpan > 1) { cell.colSpan = colSpan; // 标记起始周和结束周(周数从1开始,对应索引+1) cell.dataset.startWeek = startIndex + 1; cell.dataset.endWeek = startIndex + colSpan; // 删除被合并的多余单元格 for (let i = 1; i < colSpan; i++) { cell.parentElement.deleteCell(cell.cellIndex + 1); } applyResizing(cell); } else if (cell && cell.style.backgroundColor !== '') { // 单个着色单元格也标记周数信息 cell.dataset.startWeek = cell.cellIndex + 1; cell.dataset.endWeek = cell.cellIndex + 1; applyResizing(cell); } }
二、实现拖拽调整核心逻辑
接下来我们要给单元格添加拖拽功能,核心是监听鼠标的mousedown、mousemove、mouseup事件,计算拖动距离对应的列数,然后动态更新单元格的colspan:
function applyResizing(cell) { // 给单元格添加拖拽提示样式 cell.style.cursor = 'col-resize'; cell.style.position = 'relative'; // 绑定拖拽触发事件 cell.addEventListener('mousedown', startResize); } // 全局变量存储拖拽状态,避免污染其他逻辑 let resizeState = { isResizing: false, currentCell: null, startX: 0, originalColspan: 0, startWeek: 0, colWidth: 0 // 表格每一列的基准宽度 }; function startResize(e) { const cellRect = this.getBoundingClientRect(); // 只在单元格右侧边缘10px范围内触发拖拽,避免误操作 if (e.clientX - cellRect.right > -10 && e.clientX - cellRect.right < 0) { resizeState.isResizing = true; resizeState.currentCell = this; resizeState.startX = e.clientX; resizeState.originalColspan = parseInt(this.colSpan); resizeState.startWeek = parseInt(this.dataset.startWeek); // 获取表头列的基准宽度(用第二列,因为第一列是Planung) const headerCol = document.querySelector('#planningTable thead th:nth-child(2)'); resizeState.colWidth = headerCol.getBoundingClientRect().width; // 添加全局鼠标事件监听 document.addEventListener('mousemove', handleResize); document.addEventListener('mouseup', stopResize); } } function handleResize(e) { if (!resizeState.isResizing) return; // 计算鼠标移动距离,换算成对应的列数 const deltaX = e.clientX - resizeState.startX; const deltaCols = Math.round(deltaX / resizeState.colWidth); // 限制colspan的范围:不能小于1,也不能超出剩余可占用的列数 const totalWeeks = 52; const maxPossibleCols = totalWeeks - resizeState.startWeek + 1; let newColspan = Math.max(1, Math.min(resizeState.originalColspan + deltaCols, maxPossibleCols)); // 只有当colspan变化时才更新DOM if (newColspan !== resizeState.originalColspan) { updateCellColspan(resizeState.currentCell, newColspan); } } function updateCellColspan(cell, newColspan) { const row = cell.parentElement; const startWeek = parseInt(cell.dataset.startWeek); const oldColspan = parseInt(cell.colSpan); const delta = newColspan - oldColspan; // 更新单元格的colspan和周数标记 cell.colSpan = newColspan; cell.dataset.endWeek = startWeek + newColspan - 1; // 根据colspan的增减,调整行内的单元格数量 if (delta < 0) { // 拆分出-delta个空单元格,补充到当前单元格后面 for (let i = 0; i < -delta; i++) { const newCell = document.createElement('td'); row.insertBefore(newCell, cell.nextSibling); } } else if (delta > 0) { // 删除当前单元格后面的delta个空单元格(如果存在) for (let i = 0; i < delta; i++) { if (cell.nextSibling) { row.deleteCell(cell.cellIndex + 1); } } } } function stopResize() { // 结束拖拽,清理状态和事件监听 resizeState.isResizing = false; document.removeEventListener('mousemove', handleResize); document.removeEventListener('mouseup', stopResize); }
三、添加样式优化拖拽体验
为了让用户更直观地看到可拖拽区域,我们可以给着色单元格的右侧边缘加一个视觉提示:
.sprint-cell, .firefighting-cell { position: relative; } .sprint-cell::after, .firefighting-cell::after { content: ''; position: absolute; right: 0; top: 0; height: 100%; width: 5px; background-color: rgba(0,0,0,0.1); cursor: col-resize; }
补充说明
- 这个逻辑的核心是通过鼠标移动距离换算成列数,动态调整单元格的
colspan,同时维护表格的DOM结构正确性。 - 如果需要把调整后的结果保存到后端,你可以遍历所有着色单元格,收集
data-start-week、data-end-week和单元格类型,然后提交到ASP.NET的接口进行保存。
备注:内容来源于stack exchange,提问作者DevinEst
相关产品推荐
相关产品推荐

