ant.Table拖拽列宽
8 个月前
table中推拽列宽达到扩宽目标列是项目必用组件,使用过Ant的小伙伴应该都在ant.design组件库找过并没有该组件的实现,当然都有尝试百度过,也都找到了答案。
先来点基础code
import * as React from 'react';
import './style.css';
import 'antd/dist/antd.css';
import ResizeTable from './ResizeTable';
const randomString = () => Math.random().toString(36).slice(-6);
const randomNumber = (min, max) =>
min + Math.round(Math.random() * (max - min));
const dataSource = new Array(100).fill('').map((_, i) => ({
id: randomNumber(0, 100000),
name: randomString(),
age: randomNumber(5, 20),
grade: randomNumber(1, 6),
export default function App() {
const columns = [
title: 'ID',
width: 100,
dataIndex: 'id',
dataIndex: 'name',
width: 100,
title: 'name',
dataIndex: 'name',
width: 100,
title: 'name',
dataIndex: 'age',
width: 100,
title: 'age',
dataIndex: 'grade',
width: 100,
title: 'grade',
return (
<ResizeTable rowkey={'id'} dataSource={dataSource} columns={columns} />
该组件大概实现是这样的,为了方便链接里点一点吧
import { Table } from 'antd';
import type { TableProps } from 'antd/lib/table';
import type { TableColumnType } from 'antd';
import React from 'react';
import { Resizable } from 'react-resizable';
import 'react-resizable/css/styles.css';
import styled from 'styled-components';
const ResizeCellStyle = styled(Resizable)`
&::before {
position: absolute;
top: 50%;
right: 0;
width: 1px;
height: 1.6em;
background-color: rgba(0,0,0,.06);
transform: translateY(-50%);
transition: background-color .3s;
content: "";
&:last-child::before {
display: none;
.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;
const TableHeaderCell: React.FC<Record<string, any>> = (props) => {
const { onResize, width, ...restProps } = props;
if (!width) return <th {...restProps} />;
return (
<ResizeCellStyle
width={width}
height={0}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
<th {...restProps} />
</ResizeCellStyle>
const ResizeTable: React.FC<TableProps<any>> = (props) => {
const { columns = [], ...rest } = props;
const [tableColumns, setTableColumns] =
React.useState<TableColumnType<any>>(columns);
const handleResize =
(index: number) =>
(e: any, { size }: any) => {
const nextColumns = [...tableColumns];
nextColumns[index] = {
...nextColumns[index],
width: size.width,
setTableColumns(nextColumns);
return (
<Table
{...rest}
components={{
header: {
cell: TableHeaderCell,
scroll={{
x: 'max-content',
columns={tableColumns.map((col: any, index: any) => ({
...col,
onHeaderCell: (column: any) => ({
width: column.width,
onResize: handleResize(index),
export default ResizeTable;
这个拖拽没什么大的毛病,主要是columns更新带动整个Table更新,这个状态改变影响太大了,数据较多的情况下会比较卡,可以尝试多弄点数据拖拽试一下。
为了让拖拽更加流畅简单优化了一下 React Ts (forked) - StackBlitz 为了让拖拽更加流畅简单优化了一下
import React from 'react';
import { Resizable } from 'react-resizable';
import 'react-resizable/css/styles.css';
import styled from 'styled-components';
import { Table } from 'antd';
import type { TableProps } from 'antd/lib/table';
const Container = styled.div`
position: relative;
width: 100%;
type ResizeLineProps = {
left: number;
const ResizeLine = styled.div.attrs((props: ResizeLineProps) => ({
style: {
left: props.left,
}))<ResizeLineProps>`
width: 0px;
height: 100%;
position: absolute;
left: 0;
top: 0;
border-left: 1px dashed #d9d9d9;
z-index: 9999;
type StyleProps = {
resize?: boolean;
const ResizeTableStyle = styled(Resizable)<StyleProps>`
user-select: none;
&::before {
position: absolute;
top: 50%;
right: 0;
width: 1px;
height: 1.6em;
background-color: rgba(0,0,0,.06);
transform: translateY(-50%);
transition: background-color .3s;
content: "";
&:last-child::before {
display: none;
.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;
const ResizeableTitle = (props: any) => {
const { width, onResizeStart, onResize, onResizeStop, ...restProps } = props;
if (!width) return <th {...restProps} />;
return (
<ResizeTableStyle
width={width}
onResizeStart={onResizeStart}
onResize={onResize}
onResizeStop={onResizeStop}
height={0}
draggableOpts={{ enableUserSelectHack: false }}
<th {...restProps} />
</ResizeTableStyle>
const ResizeTable: React.FC<TableProps<any>> = (props) => {
const { columns = [], ...rest } = props;
const [tableColumns, setTableColumns] = React.useState<any[]>(columns);
const [end, setEnd] = React.useState(0);
const [start, setStart] = React.useState(0);
const [show, setShow] = React.useState(false);
const ref = React.useRef<HTMLDivElement>(null);
const handleResizeStart = React.useCallback(
(index: number) =>
(e: any, { size }: any) => {
const { clientX } = e;
const drageX =
clientX - (ref.current as any)?.getBoundingClientRect().x;
setStart(drageX);
setEnd(drageX);
setShow(true);
[columns, ref]
const handleResizeMove = React.useCallback(
(index: number) => (e: any) => {
const { clientX } = e;
const drageX = clientX - (ref.current as any)?.getBoundingClientRect().x;
setEnd(drageX);
[ref]
const handleResizeStop = React.useCallback(
(index: number) =>
(e: any, { size }: any) => {
const { clientX } = e;
const drageX =
clientX - (ref.current as any)?.getBoundingClientRect().x;
const nextColumns = [...tableColumns];
nextColumns[index] = {
...nextColumns[index],
width: (size.width += drageX - start),
setTableColumns(nextColumns);
console.log('stop');
setEnd(0);
setStart(0);
setShow(false);
[tableColumns, start, ref]
return (
<Container ref={ref}>
<Table
{...rest}
components={{
header: {
cell: ResizeableTitle,
scroll={{
x: 'max-content',
columns={tableColumns.map((col: any, index: any) => ({
...col,
onHeaderCell: (column: any) => ({
width: column.width,
onResizeStart: handleResizeStart(index),
onResize: handleResizeMove(index),
onResizeStop: handleResizeStop(index),