技术概要
自从antd 3.x 之后官网上没有对应的例子了,于是采用了同样的方式来实现,并封装成比较通用的组件。
通过 Antd 中 Table 提供的components 属性来重写header并集成 react-resizable 来实现可伸缩列。
技术细节
- 安装react-resizable
- table的columns需要给每一项设置width或者在ResizableTable组件中使用默认宽度。
组件源码
- 表头单元格组件:TableHeaderCell组件借助react-resizable库来实现列宽调整功能。对于特定的列,像选择列或者滚动条列,会把调整功能隐藏起来。
- 状态管理:ResizeTable组件内部维护着tableColumns状态,这个状态会在列宽改变时进行更新。
- 列宽调整处理:handleResize函数会更新对应的列宽,同时触发外部传入的回调函数onTitleResize。
- 列配置:通过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;
使用示例
代码关键点
- 数据与列配置:定义了表格的初始列配置。
- 防抖处理:运用ahooks库的useDebounceFn函数(可以替换成任何防抖函数)来避免频繁更新列宽状态。
- 列宽状态更新:在列宽发生变化时,通过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的方式一样,加了一些本次开发中遇到的问题的处理方法
示例代码是最小可用版本,需要按照自己业务需求去修改。