antd table 可伸缩列 react-resizable 多表头实现

1,015 阅读1分钟
import { Table } from 'antd';
import styles from './index.less';
import type { ResizeCallbackData } from 'react-resizable';
import { Resizable } from 'react-resizable';
import { ColumnsType, ColumnType } from 'antd/es/table';
import { useState } from 'react';

interface CostListTableProps {
  dataSource?: Record<string, any>[];
  isLoading?: boolean;
}
const ResizableTitle = (
  props: React.HTMLAttributes<any> & {
    onResize: (e: React.SyntheticEvent<Element>, data: ResizeCallbackData) => void;
    width: number;
  },
) => {
  const { onResize, width, ...restProps } = props;

  if (!width) {
    return <th {...restProps} />;
  }

  return (
    <Resizable
      width={width}
      height={0}
      handle={
        <span
          className="react-resizable-handle"
          onClick={(e) => {
            e.stopPropagation();
          }}
        />
      }
      onResize={onResize}
      draggableOpts={{ enableUserSelectHack: false }}
    >
      <th {...restProps} />
    </Resizable>
  );
};

const CostListTable = (props: CostListTableProps) => {
  const { dataSource = [], isLoading } = props;

  const [columns, setColumns] = useState<any[]>([
    {
      title: '序号',
      dataIndex: 'index',
      key: 'index',
      width: 100,
    },
    {
      title: '子目名称',
      dataIndex: 'name',
      width: 220,
    },
    {
      title: '工程量',
      children: [
        {
          title: '客餐厅',
          dataIndex: 'dinnerRoom',
          width: 120,
        },
        {
          title: '房间1',
          dataIndex: 'bedroom1',
          width: 120,
        },
        {
          title: '房间2',
          dataIndex: 'bedroom2',
          width: 120,
        },
        {
          title: '厨房',
          dataIndex: 'kitchen',
          width: 120,
        },
        {
          title: '卫生间1',
          dataIndex: 'bathroom1',
          width: 120,
        },
        {
          title: '卫生间2',
          dataIndex: 'bathroom2',
          width: 120,
        },
        {
          title: '阳台',
          dataIndex: 'balcony',
          width: 120,
        },
        {
          title: '合计',
          dataIndex: 'projectTotal',
          width: 120,
        },
      ],
    }
  ]);

  // 动态修改宽度
  const getNewColumnsWithWidth = (
    _columns: ColumnsType<any>[],
    indexArray: number[],
    currentIndex: number,
    width: any,
  ) => {
    const index = indexArray[currentIndex];
    const newColumns = [..._columns];

    if (currentIndex === indexArray.length - 1) {
      newColumns[index].width = width;
    } else {
      newColumns[index].children = getNewColumnsWithWidth(
        _columns[index].children,
        indexArray,
        currentIndex + 1,
        width,
      );
    }
    return newColumns;
  };
  const handleResize =
    (indexArray: number[]) =>
    (_: React.SyntheticEvent<Element>, { size }: ResizeCallbackData) => {
      console.log('indexArray size', indexArray, size);

      // const newColumns = [...columns];
      // newColumns[index] = {
      //   ...newColumns[index],
      //   width: size.width,
      // };
      const newColumns = getNewColumnsWithWidth(columns, indexArray, 0, size.width);
      setColumns(newColumns);
    };

  const mergeColumn = (_columns: ColumnsType<any>[], indexArray: number[]) => {
    return _columns.map((col, index) => {
      const nextIndexArray = indexArray.map((v) => v);
      nextIndexArray.push(index);
      const mergedColumn = {
        ...col,
        onHeaderCell: (column) => ({
          width: (column as ColumnType<any>).width,
          onResize: handleResize(nextIndexArray),
        }),
      };
      if (col.children && col.children.length) {
        mergedColumn.children = mergeColumn(mergedColumn.children, nextIndexArray);
      }
      return mergedColumn;
    });
  };
  // 设置侦听函数
  // const mergedColumns: ColumnsType<any> = columns.map((col, index) => ({
  //   ...col,
  //   onHeaderCell: column => ({
  //     width: (column as ColumnType<any>).width,
  //     onResize: handleResize(index),
  //   }),
  // }));
  const mergedColumns = mergeColumn(columns, []);

  return (
    <>
      <Table
        size="small"
        rowKey="id"
        bordered
        columns={mergedColumns}
        components={{
          header: {
            cell: ResizableTitle,
          },
        }}
        loading={isLoading}
        className={styles.table}
        dataSource={dataSource || []}
        scroll={{ y: 'calc(100vh - 332px)' }}
        pagination={false}
      />
    </>
  );
};

export default CostListTable;