react中使用react-resizable实现antd的table表格列拖拽调整宽度
前言
本篇文章主要讲述如何是使用react-resizable实现antd的table表格列拖拽调整宽度,其实这个功能在antd的3.x版本是有的,但是到了4以后,antd的官网就没有示例了。
本篇文章和3.x的示例有所不同,不是实时更新拖拽位置,我会在拖拽结束的时候更新列的位置,并且拖拽的时候,自定义加了一条线用于方便查看拖拽位置。
先上实现效果图:
我会在下面将demo的源码都贴上,以供大家参考。
1.安装react-resizable
npm install react-resizable
2.源码
import React, { useState, useRef, useEffect } from "react";
import { useSafeState } from "ahooks";
import styled from "styled-components";
import { Resizable } from "react-resizable";
import { Table } from "antd";
const ResizeTable = styled.div`
position: relative;
background-color: #fff;
overflow: hidden;
.ant-table-thead {
th {
font-weight: 600 !important;
-moz-user-select: none;
-o-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
}
.ant-table-cell {
.superform-field-control-readonly {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
word-break: keep-all;
display: block;
line-height: 32px;
}
}
.ant-table-thead,
.ant-table-tbody {
tr {
td,
th {
padding: 8px;
}
}
}
.react-resizable {
position: relative;
background-clip: padding-box;
}
.react-resizable-handle {
width: 10px;
height: 100%;
cursor: ew-resize;
position: absolute;
bottom: 0;
right: 0px;
z-index: 1;
}
`;
const StyledLine = styled.div`
display: none;
height: 100%;
width: 1px;
background: #bfbfbf;
position: absolute;
z-index: 999;
left: 0px;
`;
// 调整table表头
const ResizeableTitle = (props) => {
const { onResize, width, datagridRef, stretchRef, ...restProps } = props;
// 添加偏移量
const [offset, setOffset] = useSafeState(0);
if (!width) {
return <th {...restProps} />;
}
// 获取要减去的父元素距离左侧的宽度
const getPosition = (element) => {
let actualLeft = element.offsetLeft;
let current = element.offsetParent;
while (current !== null) {
//当它上面有元素时就继续执行
actualLeft += current.offsetLeft;
current = current.offsetParent; //继续找父元素
}
return { x: actualLeft };
};
const handleResize = (e, { size }) => {
// 这里只更新偏移量,数据列表其实并没有伸缩
setOffset(size.width - width);
let parentNodeX = getPosition(datagridRef.current).x;
let stretchDom = stretchRef.current;
stretchDom.style.display = "block";
stretchDom.style.left = `${e.clientX - parentNodeX + 2}px`;
};
const handleResizeStop = (...arg) => {
// 拖拽结束以后偏移量归零
setOffset(0);
let stretchDom = stretchRef.current;
stretchDom.style.display = "none";
stretchDom.style.left = `0px`;
// props传进来的事件,在外部是列数据中的onHeaderCell方法提供的事件
onResize(...arg);
};
const _ResizableSpan = (
<span
className="react-resizable-handle"
// 拖拽层偏移
style={{ transform: `translateX(${offset}px)` }}
onClick={(e) => {
// 取消冒泡,不取消貌似容易触发排序事件
e.stopPropagation();
e.preventDefault();
}}
/>
);
return (
<Resizable
width={width + offset}
handle={_ResizableSpan}
height={0}
// 拖拽事件实时更新
onResize={handleResize}
// 拖拽结束更新
onResizeStop={handleResizeStop}
draggableOpts={{ enableUserSelectHack: false }}
>
<th {...restProps} />
</Resizable>
);
};
// table 数据
let dataSource = [];
for (let i = 1; i <= 100; i++) {
dataSource.push({
key: i,
number: i,
text: `单行${i}`,
textarea: `多行${i}`,
});
}
const ResizTableColumns = () => {
const [cols, setCols] = useState([
{
title: "数值输入框",
dataIndex: "number",
key: "number",
width: 300,
ellipsis: true,
},
{
title: "单行文本框",
dataIndex: "text",
key: "text",
width: 300,
ellipsis: true,
},
{
title: "多行文本框",
dataIndex: "textarea",
key: "textarea",
ellipsis: true,
},
]);
const [columns, setColumns] = useState([]);
const datagridRef = useRef(null);
const stretchRef = useRef(null);
useEffect(() => {
setColumns(
(cols || []).map((col, index) => ({
...col,
onHeaderCell: (column) => ({
width: column.width,
stretchRef,
datagridRef,
onResize: handleResize(index),
}),
}))
);
}, [cols]); //eslint-disable-line
// 定义头部组件
const components = {
header: {
cell: ResizeableTitle,
},
};
// 处理拖拽
const handleResize =
(index) =>
(e, { size }) => {
const nextColumns = [...cols];
// 拖拽是调整宽度
nextColumns[index] = {
...nextColumns[index],
width: size.width,
};
setCols([...nextColumns]);
};
return (
<ResizeTable ref={datagridRef}>
<Table
dataSource={dataSource}
rowKey={(record) => record.key}
columns={columns}
components={components}
bordered
// pagination={false}
/>
{/* 拖拽列的线样式 */}
<StyledLine ref={stretchRef} style={{ bottom: 64 }} />
</ResizeTable>
);
};
export default ResizTableColumns;