赞
踩
需求背景:需要实现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) { // Chrome selection.empty(); } else if (selection.removeAllRanges) { // Firefox selection.removeAllRanges(); } } } else if (document.selection && document.selection.empty) { // IE document.selection.empty(); } }; export const ResizableTitle = (props: any) => { const { onResize, width, minWidth, maxWidth, ...restProps } = props; // 没有原始宽度的列,不支持伸缩;会出现从自适应宽度一下子跳到拖动位置;也可以自行增加参数,如 disableResize if (!width) { return <th {...restProps} />; } const minConstraints: [number, number] | undefined = minWidth ? [minWidth, -Infinity] : undefined; const maxConstraints: [number, number] | undefined = maxWidth ? [maxWidth, +Infinity] : undefined; return ( <Resizable width={width} height={0} // 不需要调整高度,设为 0 minConstraints={minConstraints} maxConstraints={maxConstraints} handle={ <span className="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 的 props minWidth: 50, // maxWidth: 1000, onResize: handleResize(index), }), })); useEffect(() => { console.log('变化', curColumns); if (curColumns) { setColumns(curColumns); } }, [curColumns]); return ( <div className={styles.resizeTable}> <Table size="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; // 设置不能拖动列的最小宽度 默认 120 minConstraints?: number; // 拖动最小宽度 默认 60 maxConstraints?: number; // 拖动最大宽度 默认800 可设置无穷 } export default function ResizableTable(props: ResizableTableProps) { const { title, defaultWidth, minConstraints, maxConstraints } = props; const columns = props?.columns || []; // 组件传过来的colums const { components, resizableColumns, tableWidth } = useAntdResizableHeader({ columns, defaultWidth: defaultWidth || 120, minConstraints: minConstraints || 60, maxConstraints: maxConstraints || 800, }); return ( <div className="resizableTable"> <Table title={title} size="small" dataSource={data} // 组件传过来的data columns={resizableColumns} components={components} scroll={{ x: tableWidth }} /> </div> ); }
使用方便,效果理想,推荐使用这个插件。
11.29 更新
升级到v2.9.0
,不需要引入css文件
注意: 依赖包的名字也变了
pnpm add use-antd-resizable-header
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。