【Ant Design Table + React】表格列伸缩实现

需求背景:需要实现Antd Table 组件的列伸缩,宽度可以拖拽

在Antd 3.x 的版本中是保留的列伸缩的Demo例子的:
在这里插入图片描述
借助 react-resizable 可以实现伸缩列。

# npm 安装
npm install react-resizable --save

# yarn 安装
yarn add react-resizable

参考官方的Demo,封装一个ResizableTable组件:

import { Table } from 'antd';
import type { ColumnsType } from 'antd/lib/table';
import { useEffect,useState } from 'react';
import { Resizable } from 'react-resizable';
import styles from './resizableTable.less';

/**
 * 处理松开鼠标还会拖动的问题
 * 参考思路:在点击拖动时,使用浏览器API Selection.removeAllRanges 清空原本误选的文本。
 */
const clearSelection = () => {
  if (window.getSelection) {
    const selection = window.getSelection();
    if (selection) {
      if (selection.empty) {
        // Chrome
        selection.empty();
      } else if (selection.removeAllRanges) {
        // Firefox
        selection.removeAllRanges();
      }
    }
  } 
   else if (document.selection && document.selection.empty) {
     // IE
     document.selection.empty();
   }
};

export const ResizableTitle = (props: any) => {
  const { onResize, width, minWidth, maxWidth, ...restProps } = props;

  // 没有原始宽度的列,不支持伸缩;会出现从自适应宽度一下子跳到拖动位置;也可以自行增加参数,如 disableResize
  if (!width) {
    return <th {...restProps} />;
  }

  const minConstraints: [number, number] | undefined = minWidth
    ? [minWidth, -Infinity]
    : undefined;
  const maxConstraints: [number, number] | undefined = maxWidth
    ? [maxWidth, +Infinity]
    : undefined;
  return (
    <Resizable
      width={width}
      height={0} // 不需要调整高度,设为 0
      minConstraints={minConstraints}
      maxConstraints={maxConstraints}
      handle={
        <span
          className="react-resizable-handle"
          onClick={(e) => {
            // 阻止冒泡
            e.stopPropagation();
          }}
        />
      }
      onResize={onResize}
      draggableOpts={{
        enableUserSelectHack: false,
        onMouseDown: () => {
          // 处理在 Windows Chrome 和 Edge 松开鼠标依然能拖动
          clearSelection();
        },
      }}
    >
      <th {...restProps} />
    </Resizable>
  );
};

interface DataType {
  name: {
    first: string;
    last: string;
  };
  gender: string;
  email: string;
  login: {
    uuid: string;
  };
}
const columnsData: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    sorter: true,
    render: (name) => `${name.first} ${name.last}`,
    width: '20%',
  },
  {
    title: 'Gender',
    dataIndex: 'gender',
    filters: [
      { text: 'Male', value: 'male' },
      { text: 'Female', value: 'female' },
    ],
    width: '20%',
  },
  {
    title: 'Email',
    dataIndex: 'email',
  },
];

const ResizableTable = () => {
  const curColumns: ColumnsType<DataType> = columnsData; // 可以是通过props 传进来的,这里用常量做例子
  const [column, setColumns] = useState<ColumnsType<any>>([]);
  // 拖动时更新表格列
  const handleResize = (index: number) => {
    return (_e: any, { size }: any) => {
      const newCols = [...column];
      newCols[index] = {
        ...newCols[index],
        width: size.width || '100%',
      };
      setColumns(newCols);
    };
  };

  const mergeColumns = column.map((col, index) => ({
    ...col,
    onHeaderCell: (column: any) => ({
      width: column.width ?? 100,
      // 每一列增加 minWidth, maxWidth 作为 ResizableTitle 的 props
      minWidth: 50,
      // maxWidth: 1000,
      onResize: handleResize(index),
    }),
  }));

  useEffect(() => {
    console.log('变化', curColumns);
    if (curColumns) {
      setColumns(curColumns);
    }
  }, [curColumns]);

  return (
    <div className={styles.resizeTable}>
      <Table
        size="small"
        components={{
          header: {
            cell: ResizableTitle,
          },
        }}
        columns={mergeColumns}
        dataSource={[]}
      />
    </div>
  );
};
export default ResizableTable;


必须引入样式 resizableTable.less

.resizeTable {
  :global {
    .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;
    }

    .ant-table-filter-column,
    .ant-table-column-sorters {
      display: flex;

      /* co1umn 从上到下 */
      align-items: center;

      /* center代表水平方向 */
      justify-content: space-around;
      min-width: 70px;
    }

    .ant-table-thead>tr>th .ant-table-column-sorter {
      // margin-top: -21px;
      display: table-cell;
      vertical-align: middle;
    }
  }
}

必须保持一列宽度不设置,自适应。否则效果不对。

但我用这个插件后还是不太 OK,总有一些bug,比如如果拖动了不设置宽的列,整个伸缩就会变形;而且如果列数很多的情况下,自适应列效果不理想。

所有这个方案能用但不是很好用。
可以参考:https://juejin.cn/post/7182423243553734717


后续解决方案:

在查阅资料时,看到有个大佬封装好了一个伸缩hook use-antd-resizable-header,使用起来方便简单。遂引入项目。
https://github.com/hemengke1997/use-antd-resizable-header

pnpm add @minko-fe/use-antd-resizable-header

在这里插入图片描述
引入封装组件示例:

import { Table } from 'antd';
import { useAntdResizableHeader } from '@minko-fe/use-antd-resizable-header';
import '@minko-fe/use-antd-resizable-header/dist/style.css';

/** 自定义函数 */
import { isLocaleEn } from '@/utils/commont_rely';

/** type 类申明 */
import type { IProps } from '..'; // 自己封装的表格propsType, 仅作参考

/** 自定义样式 */
import './style.less';

/** ===================================
 * @name: 可伸缩列的表格组件
 * 注意:至少一列不能拖动(width 不设置即可),请保持至少一列的宽自适应
 *======================================*/

interface ResizableTableProps extends IProps {
  // 特殊配置
  defaultWidth?: number; // 设置不能拖动列的最小宽度 默认 120
  minConstraints?: number; // 拖动最小宽度 默认 60
  maxConstraints?: number; // 拖动最大宽度 默认800 可设置无穷
}

export default function ResizableTable(props: ResizableTableProps) {
  const { title, defaultWidth, minConstraints, maxConstraints } = props;
  const columns = props?.columns || []; // 组件传过来的colums
  const { components, resizableColumns, tableWidth } = useAntdResizableHeader({
    columns,
    defaultWidth: defaultWidth || 120,
    minConstraints: minConstraints || 60,
    maxConstraints: maxConstraints || 800,
  });

  return (
    <div className="resizableTable">
      <Table
        title={title}
        size="small"
        dataSource={data} // 组件传过来的data
        columns={resizableColumns}
        components={components}
        scroll={{ x: tableWidth }}
      />
    </div>
  );
}

使用方便,效果理想,推荐使用这个插件。


11.29 更新
升级到v2.9.0 ,不需要引入css文件
注意: 依赖包的名字也变了
pnpm add use-antd-resizable-header

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值