【前端】React + Antd5 + react-resizable 实现 Table 拖拽调整列宽源码

技术概要

自从antd 3.x 之后官网上没有对应的例子了,于是采用了同样的方式来实现,并封装成比较通用的组件。

通过 Antd 中 Table 提供的components 属性来重写header并集成 react-resizable 来实现可伸缩列。

技术细节

  1. 安装react-resizable
  2. table的columns需要给每一项设置width或者在ResizableTable组件中使用默认宽度。

组件源码

  1. 表头单元格组件:TableHeaderCell组件借助react-resizable库来实现列宽调整功能。对于特定的列,像选择列或者滚动条列,会把调整功能隐藏起来。
  2. 状态管理:ResizeTable组件内部维护着tableColumns状态,这个状态会在列宽改变时进行更新。
  3. 列宽调整处理:handleResize函数会更新对应的列宽,同时触发外部传入的回调函数onTitleResize。
  4. 列配置:通过onHeaderCell属性为每个表头单元格添加调整功能。
import { Table } from "antd";
import type { ColumnsType, TableProps } from "antd/lib/table";
import React, { useEffect } from "react";
import { Resizable } from "react-resizable";
import "react-resizable/css/styles.css";

const TableHeaderCell: React.FC<Record<string, any>> = (props) => {
  const { onResize, width, hiddenResize, className, ...restProps } = props;

  if (
    hiddenResize ||
    className.includes("ant-table-selection-column") ||
    className.includes("ant-table-cell-scrollbar")
  ) {
    return <th className={`${className} select-none`} {...restProps} />;
  }

  return (
    <Resizable
      width={width}
      height={0}
      onResize={onResize}
      draggableOpts={{ enableUserSelectHack: false }}
      minConstraints={[50, 0]} // 最小宽度为50px
    >
      <th className={`${className} select-none`} {...restProps} />
    </Resizable>
  );
};

const ResizeTable: React.FC<
  TableProps<any> & {
    onTitleResize?: (column: any, width: number, index: number) => void;
  }
> = (props) => {
  const { onTitleResize, columns = [], ...rest } = props;
  const [tableColumns, setTableColumns] =
    React.useState<ColumnsType<any>>(columns);

  const handleResize =
    (index: number) =>
    (e: any, { node, size }: any) => {
      const nextColumns = [...(tableColumns || [])];
      nextColumns[index] = {
        ...nextColumns[index],
        width: size.width,
      };
      setTableColumns(nextColumns);

      // 触发列宽变化回调
      onTitleResize?.(nextColumns[index], size.width, index);
    };

  useEffect(() => {
    setTableColumns(columns);
  }, [columns]);

  return (
    <Table
      {...rest}
      components={{
        header: {
          cell: TableHeaderCell,
        },
      }}
      columns={tableColumns.map((col: any, index: any) => ({
        ...col,
        onHeaderCell: (column: any) => {
          let width = col.width;
          return {
            width: width || 200,
            onResize: handleResize(index),
            hiddenResize: col.hiddenResize || false, // 是否隐藏调整大小的功能
          };
        },
      }))}
    />
  );
};

export default ResizeTable;

使用示例

代码关键点

  1. 数据与列配置:定义了表格的初始列配置。
  2. 防抖处理:运用ahooks库的useDebounceFn函数(可以替换成任何防抖函数)来避免频繁更新列宽状态。
  3. 列宽状态更新:在列宽发生变化时,通过onTitleResize回调更新tableColumn状态,这个操作是为了防止组件外操作某些内容使table重新渲染,会出现宽度变为初始化问题
"use client";

import React, { useState } from "react";
import { useDebounceFn } from "ahooks";
import ResizableTable from "@/components/resizeable-table";
import { ColumnType } from "antd/es/table";

const UserDataTable: React.FC<{}> = () => {
  const [tableData, setTableData] =
    useState<{ title: string; name: string; age: number; address: string }[]>();
  const [tableColumn, setTableColumn] = useState<ColumnType[]>([
    {
      title: "姓名",
      dataIndex: "name",
      key: "name",
    },
    {
      title: "年龄",
      dataIndex: "age",
      key: "age",
    },
    {
      title: "住址",
      dataIndex: "address",
      key: "address",
    },
  ]);

  // 动态记录table列宽变化
  const { run: debouncedWidthRun } = useDebounceFn(
    (index: number, width: number) => {
      const newCols = [...tableColumn];
      newCols[index].width = width;
      setTableColumn(newCols);
    },
    {
      wait: 200,
    }
  );

  return (
    <div className="flex flex-col h-full">
      <ResizableTable
        columns={tableColumn}
        dataSource={tableData}
        pagination={false}
        scroll={(tableData || [])?.length > 0 ? { y: 370 } : undefined}
        bordered
        onTitleResize={(column: any, width: number, index: number) => {
          debouncedWidthRun(index, width);
        }}
      />
    </div>
  );
};

export default UserDataTable;

小结

跟之前Antd3.x的方式一样,加了一些本次开发中遇到的问题的处理方法

示例代码是最小可用版本,需要按照自己业务需求去修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值