vue3+antd 4.x table伸缩列,行高拖拽改变大小

210 阅读4分钟

条件限制没法类似vue2 mixins的写法

blog.csdn.net/jsmeng626/a…

  1. vue3
  2. 因为ant design vue 4.1.2 版本 table自定义表头 cell传出来的参数中缺少列的基础属性,所以没法类似上述的写法

11111.png

所以以下就手写一个表头拖拽以及行高改变的js方法

用法:

  1. 表格添加draggableTable类
  2. useDraggableTable放进使用的组件中执行,参数传递table的columns
  3. 尽可能配置每一列的宽度

待优化的点(行高调整测试过没什么毛病)

  1. 表头拖拽的鼠标与列宽的增长幅度不一致
  • 打个比方 6列,每一列宽度100 但是表格长度1000 则剩下400会等比例分配,这个可能是造成的原因之一
  • 尝试过改变dom的高度,而不用下面改变columns.width去改变宽度,失败,原因可能是因为cell的display为table-cell,宽度改不了,除非你改变了他display,但是会影响table的布局
  1. 尝试通过syncTableWidth函数去更新table,这个可以,但是你需要在下面方法中的onMounted执行他,他带来的问题是 你设置的宽度会因为他重新计算,会造成你设置的宽度不是你初始化的,所以看你需求来

目前这个实现了和官网表头拖拽一样的效果,就是鼠标和增幅不一致的问题而已

import { onMounted, onUnmounted, onUpdated } from 'vue';

export function useDraggableTable(columns) {
  let resizingColumnIndex = null;
  let resizingRowIndex = null;
  let initialX = 0;
  let initialY = 0;
  let initialWidth = 0;
  let initialHeight = 0;
  let isResizing = false;
  let tableWidth = 0;
  // 是否是适配体验好一点的表头拖拽方式(鼠标与宽度的增减保持一定范围的一致),需要每个列宽都定义了
  let isGoodAdapter = false;

  // 重置所有状态参数
  const resetParams = () => {
    resizingColumnIndex = null;
    resizingRowIndex = null;
    initialX = 0;
    initialY = 0;
    initialWidth = 0;
    initialHeight = 0;
    isResizing = false;
  };

  // 添加调整大小的监听器
  const addResizeListeners = () => {
    const headers = document.querySelectorAll('.draggableTable .ant-table-thead th');
    const rows = document.querySelectorAll('.draggableTable .ant-table-tbody tr');

    // 为表头添加调整大小的手柄
    headers.forEach((header, index) => {
      addResizeHandle(header, index, startResizeColumn);
    });

    // 为表格行添加鼠标事件监听器
    rows.forEach((row) => {
      row.addEventListener('mouseenter', (e) => handleRowMouseEnter(e, row));
      row.addEventListener('mouseleave', (e) => handleRowMouseLeave(e, row));
      row.addEventListener('mousedown', (e) => handleRowMouseDown(e, row));
      row.addEventListener('mousemove', (e) => handleRowMouseMove(e, row));
    });

    // 添加鼠标抬起事件监听器
    document.addEventListener('mouseup', stopResize);
  };

  // 创建调整大小的手柄
  const createResizeHandle = () => {
    const handle = document.createElement('div');
    handle.className = 'resize-handle';
    handle.style.position = 'absolute';
    handle.style.right = '0';
    handle.style.top = '50%';
    handle.style.transform = 'translateY(-50%)';
    handle.style.width = '2px';
    handle.style.height = '60%';
    handle.style.cursor = 'col-resize';
    handle.style.backgroundColor = '#e5e7eb';
    handle.style.opacity = '0';
    return handle;
  };

  // 添加调整大小的手柄到表头
  const addResizeHandle = (header, index, callback) => {
    const handle = createResizeHandle();
    header.appendChild(handle);
    handle.addEventListener('mousedown', (e) => callback(e, index));
    header.addEventListener('mouseenter', showResizeHandle);
    header.addEventListener('mouseleave', hideResizeHandle);
  };

  // 显示调整大小的手柄
  const showResizeHandle = (e) => {
    const handle = e.target.querySelector('.resize-handle');
    handle.style.opacity = '1'; // 显示手柄
  };

  // 隐藏调整大小的手柄
  const hideResizeHandle = (e) => {
    const handle = e.target.querySelector('.resize-handle');
    handle.style.opacity = '0'; // 隐藏手柄
  };

  // 开始调整列宽
  const startResizeColumn = (e, columnIndex) => {
    e.preventDefault(); // 阻止默认行为

    resizingColumnIndex = columnIndex;
    initialX = e.pageX;

    // 获取初始宽度
    const columnConfig = columns[columnIndex];
    const headerElement = document.querySelector(`.draggableTable .ant-table-thead th:nth-child(${columnIndex + 1})`);

    initialWidth = columnConfig.width ?? headerElement?.offsetWidth; // 默认50

    // if (isGoodAdapter) {
    //   const headerElement = document.querySelector(`.draggableTable .ant-table-thead th:nth-child(${columnIndex + 1})`);
    //   if (headerElement) {
    //     initialWidth = headerElement.offsetWidth;
    //   }
    // }

    document.addEventListener('mousemove', doResizeColumn);
    document.addEventListener('mouseup', stopResize);

    isResizing = true; // 标记正在拖拽
  };

  // 调整列宽
  const doResizeColumn = (e) => {
    if (isResizing && resizingColumnIndex !== null) {
      let newWidth = initialWidth + e.pageX - initialX;

      // 确保新宽度不小于默认最小宽度
      if (newWidth < 50) {
        newWidth = 50;
      }
      // 同步更新表格宽度
      // isGoodAdapter && syncTableWidth();
      // 更新配置中的宽度
      columns[resizingColumnIndex].width = newWidth;
    }
  };

  // 同步表格宽度
  const syncTableWidth = () => {
    const totalWidth = columns.reduce((acc, col) => acc + col.width, 0);
    const diff = tableWidth - totalWidth;
    console.log('diff', diff, totalWidth, tableWidth);

    // 如果总宽度大于表格宽度,平均分配剩余宽度
    if (diff > 0) {
      columns.forEach((col) => {
        col.width += Math.round(diff / columns.length);
      });
    }
  };

  // 处理鼠标进入行
  const handleRowMouseEnter = (e, row) => {
    if (shouldShowRowHandle(e, row)) {
      row.style.cursor = 'row-resize';
    } else {
      row.style.cursor = 'default';
    }
  };

  // 处理鼠标离开行
  const handleRowMouseLeave = (e, row) => {
    row.style.cursor = 'default';
    // 如果鼠标离开且没有处于调整状态,则隐藏手柄
    if (!isResizing) {
      row.style.cursor = 'default';
    }
  };

  // 处理鼠标按下行
  const handleRowMouseDown = (e, row) => {
    e.preventDefault(); // 阻止默认行为

    if (shouldShowRowHandle(e, row)) {
      resizingRowIndex = Array.from(row.parentNode.children).indexOf(row);
      const rowRect = row.getBoundingClientRect();
      initialY = e.clientY - rowRect.top; // 使用 clientY 相对于行的偏移量
      initialHeight = row.offsetHeight;

      document.addEventListener('mousemove', doResizeRow);
      document.addEventListener('mouseup', stopResize);

      isResizing = true; // 标记正在拖拽
    }
  };

  // 判断是否应该显示行调整手柄
  const shouldShowRowHandle = (e, row) => {
    const mouseY = e.clientY - row.getBoundingClientRect().top;
    const curOffsetHeight = row.offsetHeight;
    const threshold = 10;
    return curOffsetHeight < 41 ? 41 - mouseY <= threshold : curOffsetHeight - mouseY <= threshold;
  };

  // 处理鼠标移动行
  const handleRowMouseMove = (e, row) => {
    if (shouldShowRowHandle(e, row)) {
      row.style.cursor = 'row-resize';
    } else {
      row.style.cursor = 'default';
    }
  };

  // 调整行高
  const doResizeRow = (e) => {
    if (isResizing && resizingRowIndex !== null) {
      const resizingRow = document.querySelectorAll('.draggableTable .ant-table-tbody tr')[resizingRowIndex];
      const rowRect = resizingRow.getBoundingClientRect();
      const newHeight = initialHeight + (e.clientY - rowRect.top) - initialY;

      // 更新 DOM 中的高度
      resizingRow.style.height = `${newHeight}px`;

      // 检查是否接近顶部或底部并显示手柄
      if (shouldShowRowHandle(e, resizingRow)) {
        resizingRow.style.cursor = 'row-resize';
      } else {
        resizingRow.style.cursor = 'default';
      }
    }
  };

  // 停止调整大小
  const stopResize = () => {
    document.removeEventListener('mousemove', doResizeColumn);
    document.removeEventListener('mousemove', doResizeRow);
    document.removeEventListener('mouseup', stopResize);
    resetParams();
  };

  // 组件挂载时初始化
  onMounted(() => {
    setTimeout(() => {
      tableWidth = document.querySelector('.draggableTable .ant-table').clientWidth;
      isGoodAdapter = columns.every((col) => ![null, undefined, ''].includes(col.width));
      console.log('列宽是否都已配置', isGoodAdapter);
      addResizeListeners();
      // syncTableWidth();
    }, 1000);
  });

  // 组件更新时重置并重新添加监听器
  onUpdated(() => {
    resetParams();
    stopResize();
    addResizeListeners();
  });

  // 组件卸载时清除监听器
  onUnmounted(() => {
    stopResize();
  });
}