基于 React-Resizable 的 ProTable 表格列宽拖拽实践

技术背景

本方案将演示如何构建可复用的高阶ProTable表格列拖拽组件,并提供完整的样式优化指南。

技术实现路径

1. 环境准备
csharp 复制代码
# 使用任意包管理器安装核心依赖
yarn add react-resizable
# 或
npm install react-resizable --save
2. 构建高阶组件

创建ResizableTable.jsx实现拖拽逻辑封装:

ini 复制代码
import React, { useEffect, useState } from 'react';
import { Resizable } from 'react-resizable';
import { ProTable } from '@ant-design/pro-components';
import './index.less';

/**
 * 可伸缩表头单元格组件
 * @param {Object} props - 组件属性
 * @param {Function} props.onResize - 列宽调整时的回调函数
 * @param {number} props.width - 列的宽度
 */
const ResizableTitle = (props) => {
  const { onResize, width, ...restProps } = props;

  // 如果未提供宽度,则使用普通表头单元格
  if (!width) {
    return <th {...restProps} />;
  }

  return (
    <Resizable
      width={width}
      height={0}
      handle={
        <span
          className="react-resizable-handle"
          onClick={(e) => {
            // 阻止事件冒泡和默认行为
            e.stopPropagation();
            e.preventDefault();
          }}
        />
      }
      onResize={onResize}
      draggableOpts={{ enableUserSelectHack: false }}
    >
      <th {...restProps} />
    </Resizable>
  );
};

/**
 * ProTable 可伸缩列组件
 * @param {Object} props - ProTable 的属性
 * @param {Array} props.columns - 表格列配置
 */
const ResizableTable = (props) => {
  const [columns, setColumns] = useState([]);

  // 当传入的 columns 发生变化时,更新本地 columns 状态
  useEffect(() => {
    if (props.columns) {
      setColumns(props.columns);
    }
  }, [props.columns]);

  /**
   * 处理列宽调整的回调函数
   * @param {number} index - 列的索引
   * @returns {Function} - 列宽调整时的实际处理函数
   */
  const handleResize = (index) => (_, { size }) => {
    const newColumns = [...columns];
    newColumns[index] = {
      ...newColumns[index],
      width: size.width,
    };
    setColumns(newColumns);
  };

  const mergeColumns = columns.map((col, index) => ({
    ...col,
    onHeaderCell: (column) => ({
      // 如果未提供列宽,默认使用 80
      width: column.width || 80, 
      onResize: handleResize(index),
    }),
  }));

  return (
    <div className="resizeTable">
      <ProTable
        {...props}
        components={{
          header: {
            cell: ResizableTitle,
          },
        }}
        scroll={{ x: 900 }}
        columns={mergeColumns}
      />
    </div>
  );
};

export default ResizableTable;
    
3. 业务组件集成
ini 复制代码
import ResizableTable from '@/components/ResizableTable';

const BusinessComponent = () => {
  // 列配置需包含精确宽度
  const columns = [
    { title: '区域机构', dataIndex: 'orgName', width: 200 },
    { title: '传片数', dataIndex: 'uploads', width: 80 },
    { title: 'AI阳性率', dataIndex: 'positiveRate', width: 120 },
  ];

  return (
    <div className="data-container">
      <ResizableTable
        size="small"
        rowKey="id"
        columns={columns}
        dataSource={dataset}
        pagination={{ pageSize: 50 }}
      />
    </div>
  );
};
4. 样式优化方案
css 复制代码
.resizeTable {
  .react-resizable {
    position: relative;
    background-clip: padding-box;
    user-select: none;
  }

  // 防止默认出现横向滚动条
  .ant-table-content > table {
    min-width: calc(100% - 5px) !important;
  }

  .react-resizable-handle {
    position: absolute;
    width: 10px;
    height: 100%;
    bottom: 0;
    right: -5px;
    cursor: col-resize;
    background-image: none;
    z-index: 1;
  }
}

方案优势

  1. 逻辑解耦:将拖拽逻辑封装为独立高阶组件,实现业务零侵入
  2. 精准控制:要求列定义必须包含数值型 width 属性,避免渲染抖动
  3. 样式隔离:通过容器类名限制样式作用域,防止全局污染
相关推荐
@大迁世界2 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路11 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug15 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213816 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中38 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路42 分钟前
GDAL 实现矢量合并
前端
hxjhnct44 分钟前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端