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


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

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

    # npm 安装
    npm install react-resizable --save
    
    # yarn 安装
    yarn add react-resizable
    
    • 1
    • 2
    • 3
    • 4
    • 5

    参考官方的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;
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157

    必须引入样式 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;
        }
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

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

    但我用这个插件后还是不太 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
    
    • 1

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

    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>
      );
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

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


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

    在这里插入图片描述

  • 相关阅读:
    DeepStream--测试代码
    简历信息粘贴板
    瑞芯微 | I2S-音频基础分享
    软加密真的安全吗?
    计算机网络基础
    Pytorch -> ONNX -> TensorRT 模型转换与部署
    web立体相册
    关于城市旅游的HTML网页设计 HTML+CSS上海博物馆网站 dreamweaver作业静态HTML网页设计 html网页制作期末大作业
    《Python+Kivy(App开发)从入门到实践》自学笔记:高级UX部件——TabbedPanel选项面板
    【leetcode】2404. 出现最频繁的偶数元素(js实现)
  • 原文地址:https://blog.csdn.net/yqdid/article/details/133863232