You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 08:54:29