1、先看效果
2、环境准备
"dependencies": {
"antd": "^5.4.0",
"react-resizable": "^3.0.4",
},
"devDependencies": {
"@types/react": "^18.0.33",
"@types/react-resizable": "^3.0.1",
}
3、功能实现
一、先把拖拽组件写好
/**
* 公共组件:实现拖拽
*/
import { isNumber } from 'lodash';
import { StyleHTMLAttributes } from 'react';
import { Resizable, ResizableProps } from 'react-resizable';
type ResizableTitleProps = ResizableProps & {
resizable?: boolean;
style: StyleHTMLAttributes<any>;
};
const ResizableTitle = (props: ResizableTitleProps) => {
const { onResize, width, resizable, ...restProps } = props;
if (!width || !resizable) {
return <th {...restProps} />;
}
let resizeWidth: any = width;
if (!isNumber(resizeWidth)) {
resizeWidth = Number(resizeWidth.replace('px', ''));
}
return (
<Resizable
width={resizeWidth}
height={0}
handle={
<span
className="react-resizable-handle"
onClick={(e) => {
e.stopPropagation();
}}
/>
}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: true }}
// maxConstraints={[800, 800]}
>
<th
{...restProps}
style={{
...restProps?.style,
width: `${resizeWidth}px`,
borderRight: '1px solid rgba(2, 9, 23, 70%)',
}}
/>
</Resizable>
);
};
export default ResizableTitle;
二、在antd写入tab,并引用拖拽组件
/**
* 公共组件:静态表格
*/
import { Table } from 'antd';
import clsx from 'clsx';
import React, { useEffect, useState } from 'react';
import ResizableTitle from './ResizableTitle';
import styles from './index.less';
export interface ListTableProps {
className?: any;
rowClassName?: any;
dimension?: number;
columns?: any;
dataSource?: any;
pagination?: any;
scroll?: object;
virtual?: boolean;
rowKey?: any;
isShowScrollX?: boolean;
vid?: string;
isResizable?: boolean; //是否可退拽
onChange?: (pagination: any, filters: any, sorter: any) => void;
}
// 暂无数据组件
const NoDataComponent = () => {
return (
<div className={clsx(['h-[250px]', 'flex justify-center items-center'])}>
<div
className={clsx([
'w-[76px] h-[94px]',
'bg-[url("/images/no-data.svg")] bg-no-repeat',
])}
/>
</div>
);
};
const ListTable: React.FC<ListTableProps> = ({
className,
rowClassName = () => '',
onChange,
dataSource,
isShowScrollX,
defaultFixedNode,
columns: initCols,
isResizable,
vid = 'resize_table',
...props
}) => {
const [currentColumns, setCurrentColumns] = useState([]);
const [leftRightNodeIsFixed, setLeftRightNodeIsFixe] =
useState(defaultFixedNode); // 左右节点是否固定
useEffect(() => {
setCurrentColumns(initCols);
}, [initCols]);
useEffect(() => {
setCurrentColumns(initCols);
}, [initCols]);
// 计算宽度,当出现底部滚动条时,最左最右节点固定
const computedWidth = (columns: any) => {
const widthAll =
document.getElementsByClassName('ant-table-body')?.[0]?.clientWidth;
const currentTabWidth = (columns || []).reduce((pre: number, cur: any) => {
return Number(pre) + (Number(cur?.width) || 0);
}, 0);
setLeftRightNodeIsFixe(currentTabWidth > widthAll);
};
// 拖拽后更新表格宽度
const handleResize =
(index: number, colDataIndex?: string) =>
(e: any, { size }: { size: { width: number } }) => {
if (!colDataIndex) {
return;
}
setCurrentColumns((pre) => {
let temp = [...pre];
temp[index] = {
...temp[index],
width: size.width < 50 ? 50 : size.width,
};
computedWidth(temp);
return temp;
});
};
const getColumns = (columns: any) => {
return (columns || []).map((col: any, idx: number) => {
return {
...col,
onHeaderCell: (column: any) => ({
width: column.width || 100,
resizable: isResizable && !column?.fixed,
onResize: handleResize(idx, col.dataIndex as string),
}),
};
});
};
return (
<Table
rowClassName={(record, index) => {
return rowClassName(record, index);
}}
locale={{ emptyText: <NoDataComponent /> }}
{...(isResizable
? {
components: {
header: {
cell: ResizableTitle, // 动态拖拽设置列宽
},
},
}
: {})}
columns={getColumns(currentColumns)}
onChange={onChange}
dataSource={dataSource}
{...props}
/>
);
};
export default ListTable;
4、常见问题:
1、拖拽时,鼠标离开,拖拽被还原,80%原因是因为父组件触发了useState更新,column被还原成初始态,
2、拖拽要设置最小宽度和最大宽度,防止拖拽过程中找不到元素