需求背景:需要实现Antd Table 组件的列伸缩,宽度可以拖拽
在Antd 3.x 的版本中是保留的列伸缩的Demo例子的:
借助 react-resizable
可以实现伸缩列。
# npm 安装
npm install react-resizable --save# yarn 安装
yarn add react-resizable
参考官方的Demo,封装一个ResizableTable组件:
import { Table } from 'antd';
import type { ColumnsType } from 'antd/lib/table';
import { useEffect,useState } from 'react';
import { Resizable } from 'react-resizable';
import styles from './resizableTable.less';/*** 处理松开鼠标还会拖动的问题* 参考思路:在点击拖动时,使用浏览器API Selection.removeAllRanges 清空原本误选的文本。*/
const clearSelection = () => {if (window.getSelection) {const selection = window.getSelection();if (selection) {if (selection.empty) {// Chromeselection.empty();} else if (selection.removeAllRanges) {// Firefoxselection.removeAllRanges();}}} else if (document.selection && document.selection.empty) {// IEdocument.selection.empty();}
};export const ResizableTitle = (props: any) => {const { onResize, width, minWidth, maxWidth, ...restProps } = props;// 没有原始宽度的列,不支持伸缩;会出现从自适应宽度一下子跳到拖动位置;也可以自行增加参数,如 disableResizeif (!width) {return <th {...restProps} />;}const minConstraints: [number, number] | undefined = minWidth? [minWidth, -Infinity]: undefined;const maxConstraints: [number, number] | undefined = maxWidth? [maxWidth, +Infinity]: undefined;return (<Resizablewidth={width}height={0} // 不需要调整高度,设为 0minConstraints={minConstraints}maxConstraints={maxConstraints}handle={<spanclassName="react-resizable-handle"onClick={(e) => {// 阻止冒泡e.stopPropagation();}}/>}onResize={onResize}draggableOpts={{enableUserSelectHack: false,onMouseDown: () => {// 处理在 Windows Chrome 和 Edge 松开鼠标依然能拖动clearSelection();},}}><th {...restProps} /></Resizable>);
};interface DataType {name: {first: string;last: string;};gender: string;email: string;login: {uuid: string;};
}
const columnsData: ColumnsType<DataType> = [{title: 'Name',dataIndex: 'name',sorter: true,render: (name) => `${name.first} ${name.last}`,width: '20%',},{title: 'Gender',dataIndex: 'gender',filters: [{ text: 'Male', value: 'male' },{ text: 'Female', value: 'female' },],width: '20%',},{title: 'Email',dataIndex: 'email',},
];const ResizableTable = () => {const curColumns: ColumnsType<DataType> = columnsData; // 可以是通过props 传进来的,这里用常量做例子const [column, setColumns] = useState<ColumnsType<any>>([]);// 拖动时更新表格列const handleResize = (index: number) => {return (_e: any, { size }: any) => {const newCols = [...column];newCols[index] = {...newCols[index],width: size.width || '100%',};setColumns(newCols);};};const mergeColumns = column.map((col, index) => ({...col,onHeaderCell: (column: any) => ({width: column.width ?? 100,// 每一列增加 minWidth, maxWidth 作为 ResizableTitle 的 propsminWidth: 50,// maxWidth: 1000,onResize: handleResize(index),}),}));useEffect(() => {console.log('变化', curColumns);if (curColumns) {setColumns(curColumns);}}, [curColumns]);return (<div className={styles.resizeTable}><Tablesize="small"components={{header: {cell: ResizableTitle,},}}columns={mergeColumns}dataSource={[]}/></div>);
};
export default ResizableTable;
必须引入样式 resizableTable.less
:
.resizeTable {:global {.react-resizable {position: relative;background-clip: padding-box;}.react-resizable-handle {position: absolute;width: 10px;height: 100%;bottom: 0;right: -5px;cursor: col-resize;background-image: none;z-index: 1;}.ant-table-filter-column,.ant-table-column-sorters {display: flex;/* co1umn 从上到下 */align-items: center;/* center代表水平方向 */justify-content: space-around;min-width: 70px;}.ant-table-thead>tr>th .ant-table-column-sorter {// margin-top: -21px;display: table-cell;vertical-align: middle;}}
}
必须保持一列宽度不设置,自适应。否则效果不对。
但我用这个插件后还是不太 OK,总有一些bug,比如如果拖动了不设置宽的列,整个伸缩就会变形;而且如果列数很多的情况下,自适应列效果不理想。
所有这个方案能用但不是很好用。
可以参考:https://juejin.cn/post/7182423243553734717
后续解决方案:
在查阅资料时,看到有个大佬封装好了一个伸缩hook use-antd-resizable-header
,使用起来方便简单。遂引入项目。
https://github.com/hemengke1997/use-antd-resizable-header
pnpm add @minko-fe/use-antd-resizable-header
引入封装组件示例:
import { Table } from 'antd';
import { useAntdResizableHeader } from '@minko-fe/use-antd-resizable-header';
import '@minko-fe/use-antd-resizable-header/dist/style.css';/** 自定义函数 */
import { isLocaleEn } from '@/utils/commont_rely';/** type 类申明 */
import type { IProps } from '..'; // 自己封装的表格propsType, 仅作参考/** 自定义样式 */
import './style.less';/** ===================================* @name: 可伸缩列的表格组件* 注意:至少一列不能拖动(width 不设置即可),请保持至少一列的宽自适应*======================================*/interface ResizableTableProps extends IProps {// 特殊配置defaultWidth?: number; // 设置不能拖动列的最小宽度 默认 120minConstraints?: number; // 拖动最小宽度 默认 60maxConstraints?: number; // 拖动最大宽度 默认800 可设置无穷
}export default function ResizableTable(props: ResizableTableProps) {const { title, defaultWidth, minConstraints, maxConstraints } = props;const columns = props?.columns || []; // 组件传过来的columsconst { components, resizableColumns, tableWidth } = useAntdResizableHeader({columns,defaultWidth: defaultWidth || 120,minConstraints: minConstraints || 60,maxConstraints: maxConstraints || 800,});return (<div className="resizableTable"><Tabletitle={title}size="small"dataSource={data} // 组件传过来的datacolumns={resizableColumns}components={components}scroll={{ x: tableWidth }}/></div>);
}
使用方便,效果理想,推荐使用这个插件。