Antd Table 表格 拖拽列宽

antd 的表格组件的列宽,是通过width属性去初始化的,有时候渲染的内容不固定,这个宽做不到通用所以研究怎么实现表格列宽拖动,主要的实现步骤如下:

  1. 使用table的components API修改表格头部为 react-resizable提供的组件
  2. 并在columns设置侦听函数,用于动态修改宽度 (onHeaderCell API)
  3. 还需要设置css,让控制组件显示在正确位置

在实际的应用中,Table组件直接再次封装为可以拖动的组件供所有人使用,而且拖动后的布局是可以本地存储的,或者用接口去存也是一样,实现效果如下
Antd Table 表格 拖拽列宽_第1张图片

下面是实现的具体代码,封装了一个MyTable 组件,在这个组件中可以自定义antd Table组件的交互样式等

1、安装插件
npm install react-resizable
2、写MyTable组件

目录结构
Antd Table 表格 拖拽列宽_第2张图片

这里使用了.tsx 来写,里面的校验随便写的 方便大家看,同文件夹下的ResizableTitle.tsx 文件就是自定义的表头,通过antd Table组件的components重写表格,每次拖动中不断更新表格的布局不断渲染,看起来就是一个连续的过程

// Mytable/index.tsx
import { Table } from 'antd';
import { useState } from 'react';

import ResizableTitle from './ResizableTitle'
import './index.css'

export default function MyTable(){
  const [columns, setColumns] = useState([
    {
      title: 'Date',
      dataIndex: 'date',
      width: 200,
    },
    {
      title: 'Amount',
      dataIndex: 'amount',
      width: 100,
      sorter: (a, b) => a.amount - b.amount,
    },
    {
      title: 'Type',
      dataIndex: 'type',
      width: 100,
    },
    {
      title: 'Note',
      dataIndex: 'note',
      width: 100,
    },
    {
      title: 'Action',
      key: 'action',
      render: () => <a>Delete</a>,
    },
  ]);

  const dataSource = [
    {
      key: 0,
      date: '2018-02-11',
      amount: 120,
      type: 'income',
      note: 'transfer',
    },
    {
      key: 1,
      date: '2018-03-11',
      amount: 243,
      type: 'income',
      note: 'transfer',
    },
    {
      key: 2,
      date: '2018-04-11',
      amount: 98,
      type: 'income',
      note: 'transfer',
    },
  ];

  const mergeColumns:any = columns.map((col, index) => {
    return {
      ...col,
      onHeaderCell: (column:any) => ({
        width: column.width,
        onResize: (_:any, { size }: any) => {
          const newColumns = [...columns];
          newColumns[index] = {
            ...newColumns[index],
            width: size.width,
          };
          setColumns(newColumns);
        },
      }),
    }
  });

  return (
    <Table
      components={{
        header: {
          cell: ResizableTitle,
        },
      }}
      columns={mergeColumns}
      dataSource={dataSource}
    />
  );
}
// Mytable/ResizableTitle.tsx
import { Resizable } from 'react-resizable';

export default function ResizableTitle(props:any){
  const { onResize, width, ...restProps } = props;

  return (
    ! width ?
      <th {...restProps} />
    : <Resizable
        width={width}
        height={0}
        handle={
          <span
            className="react-resizable-handle"
            onClick={e => {
              e.stopPropagation();
            }}
          />
        }
        onResize={onResize}
        draggableOpts={{ enableUserSelectHack: false }}
      >
        <th {...restProps} />
      </Resizable>
  );
};
/* Mytable/index.css */
.react-resizable { 
  position: relative;
  background-clip: padding-box;
  } 
  .react-resizable-handle { 
  position: absolute;
  right: -5px;
  bottom: 0;
  z-index: 1;
  width: 10px;
  height: 100%;
  cursor: col-resize;
  }
  
3、其他说明

作者在使用该方法的时候遇到性能不佳,出现了明显的卡顿现象,一时没查到原因,因为是在业务组件中使用,表格非常大,而且有很多其他的渲染,电脑的性能也有限,卡成了PPT,作了以下调整,下面是简单的讲解这个过程:

  1. 不再使用mergeColumns,不停的去渲染Table组件,每次只与普通表格一样只渲染一次
  2. 直接操作DOM,在onResize中找到计算鼠标拖动的距离,通过类名的形式找到table 的布局器并改变宽度Antd Table 表格 拖拽列宽_第3张图片
2024.6.26补充

找到项目中使用拖拽卡顿的原因了,最后一列操作栏使用了本地的一个组件,里面关联了权限的逻辑,这个拖拽不停更新columns,导致这个每一列的 Action 组件被大量重新渲染,还有复杂的逻辑导致了性能问题

2024.9.02补充

单独排了时间去优化拖动的时候表格卡顿的问题,改为操作DOM的宽度,找到表格的布局的div,监听拖拽时鼠标的移动事件,计算鼠标X轴移动的距离加到原来的width上去,这样表格的宽度就可以跟随鼠标的移动
对 onHeaderCell 进行了更新

  const onHeaderCell = useCallback((column:any) => {
    let key = String(column.dataIndex || column.key || column.index)
    return {
      width: (columnsTemp[key] || column).width || 180,
      onResizeStart: (e:any) => {
        let domSortArr = sortColumnsState()
        let handleIndex = domSortArr.findIndex(item => item.key===key)
        if(rowSelection) handleIndex += 1 //如果有选择框操作的index+1
        let table:any = document.querySelector(`#${tableId}`)
        if(!props.scroll){
          let cols = table.querySelector('colgroup').querySelectorAll('col')
          lastWidth = parseInt(cols[handleIndex].style.width)
        }
        if(props.scroll){
          let header = table.querySelector('.ant-table-header') 
          let h_cols = header.querySelector('colgroup').querySelectorAll('col')
          lastWidth = parseInt(h_cols[handleIndex].style.width)
        }
        lastX = e.pageX
      } ,
      onResize: (e:any, { size }:any) => {
        // 找到操作项,分别设置表头宽度,表格宽度
        let domSortArr = sortColumnsState()
        let handleIndex = domSortArr.findIndex(item => item.key===key)
        if(rowSelection) handleIndex += 1 //如果有选择框操作的index+1
        let xMove = e.x - lastX
        let width = lastWidth + xMove < 60 ? 60 : lastWidth + xMove
        let table:any = document.querySelector(`#${tableId}`)
        if(!props.scroll){
          let cols = table.querySelector('colgroup').querySelectorAll('col')
          cols[handleIndex].style.width = width + 'px'
        }
        if(props.scroll){
          let body = table.querySelector('.ant-table-body') 
          let b_cols = body.querySelector('colgroup').querySelectorAll('col')
          if(body.scrollWidth<=body.clientWidth){
            // 只用设置 b_cols就行,h_cols他自己会动
            b_cols[handleIndex].style.width = width + 'px'
          }else{
            // 优化:下面的代码让滚动更加流程
            let header = table.querySelector('.ant-table-header')
            let h_cols = header.querySelector('colgroup').querySelectorAll('col')
            h_cols[handleIndex].style.width = width + 'px'
            b_cols[handleIndex].style.width = width + 'px'
          }
        }
        columnsTemp[key].width = width
        saveColumnsConfig()
      }
    }
  }, [])

你可能感兴趣的:(前端,Antd,Table)