实现功能
1:表格列宽初始自动分配、列宽总和不能超过容器宽度(无横向滚动条,公司项目特殊需求)
2:当容器宽度变化时,保持当前列宽的分配比例,等比缩小
3:拖动过程中不进行列宽的调整,只有释放之后再进行列宽调整
效果图见
目录结构:
在这里插入代码片
useTableCol.tsx: 处理表格列的宽度计算等相关逻辑

import { useMemoizedFn, useSafeState } from 'ahooks';
import type { ColumnType } from 'antd/es/table/interface';
import { useEffect, useRef, useCallback } from 'react';const useTableCol = (wrapperWidth: number | undefined, columns: ColumnType<any>[]) => {const [isInit, setInit] = useSafeState<boolean>(false);// 保存每一列宽度的百分比,用来当容器的宽度变化时,计算每列的宽度const titleWidthMapRef = useRef<{ titleWidthMap: Record<string, number> | undefined }>({ titleWidthMap: undefined });// 每一列的宽度转换成数字之后的列配置const [tableShowColumns, setTableShowColumns] = useSafeState<ColumnType<any>[]>([]);// 初始时,将传入的表格配置数据进行转换// 将百分比、字符串格式的宽度配置转换成对应的数字宽度// 并根据容器的宽度做自适应const getTableNumberWidthCol = useMemoizedFn(() => {let resultTableColumesList: ColumnType<any>[] = [];if (wrapperWidth && columns) {// TODO: 筛选出所有显示的列const showCols = columns.filter((col) => col);const newColumesList = showCols.map((col) => {const { width } = col;const newCol = { ...col };// 当配置了width属性,且width为字符串类型时,计算具体的宽度值if (width && typeof width === 'string') {newCol.width = width.endsWith('%') ? (wrapperWidth * parseFloat(width)) / 100 : parseFloat(width);}return newCol;});// 表格总的宽度const totalWidth = newColumesList.filter((item) => typeof item.width === 'number').reduce((sum, current) => sum + Number(current.width), 0);// 查找出未配置宽度的列const noWidthColumes = newColumesList.filter((col) => !col.width);// 如果存在未配置宽度的列,则将容器未分配的宽度,等分给未分配宽度的列if (noWidthColumes.length > 0) {const otherWidth = wrapperWidth - totalWidth;if (otherWidth > 0) {// 为了简单,向下取整,并将差值放到最后一列const commonWidth = Math.floor(otherWidth / noWidthColumes.length);const resultColumes = newColumesList.map((col) => {if (!col.width) {// 最后一个未配置宽度的列宽取差值if (col.title === noWidthColumes[noWidthColumes.length - 1].title) {col.width = otherWidth - commonWidth * (noWidthColumes.length - 1);} else {// 非最后一个未配置宽度的列,则取均值的向下取整值col.width = commonWidth;}}return col;});resultTableColumesList = resultColumes;} else {// 存在未分配宽度的列,但是列的已分配宽度大于容器宽度,此处正常情况下不应出现// 若出现了此情况,则给无列宽的列都分配60px的宽度,其他有列宽的需要同等缩小const needWidth = 60 * noWidthColumes.length + Math.abs(otherWidth);const showColWithWidth = newColumesList.length - noWidthColumes.length;if (showColWithWidth > 0) {const averageWidth = Math.floor(needWidth / showColWithWidth);const lastWidth = needWidth - averageWidth * (showColWithWidth - 1);const resultColumes = newColumesList.map((col) => {if (!col.width) {// 最后一个未配置宽度的列宽取差值if (col.title === noWidthColumes[noWidthColumes.length - 1].title) {col.width = lastWidth;} else {// 非最后一个未配置宽度的列,则取均值的向下取整值col.width = averageWidth;}}return col;});resultTableColumesList = resultColumes;}}} else {const otherWidth = totalWidth - wrapperWidth;const averageWidth = Math.floor(otherWidth / newColumesList.length);const lastWidth = otherWidth - averageWidth * (newColumesList.length - 1);const resultColumes = newColumesList.map((col, index) => {if (index !== newColumesList.length - 1) {return { ...col, width: Number(col.width) - averageWidth };}return { ...col, width: Number(col.width) - lastWidth };});resultTableColumesList = resultColumes;}}return resultTableColumesList;});// 更新列宽占容器百分比的方法,若表格列支持拖拽,则需提供给拖拽方法,每次拖拽结束后,更新值const updateTitleWidthMap = useCallback((result: Record<string, number>) => {titleWidthMapRef.current.titleWidthMap = result;},[titleWidthMapRef],);// 将数字列宽所占百分比保存下来,用以当容器的宽度变更时,做自适应处理const setTitleWidthMapMethod = useMemoizedFn((colList: ColumnType<any>[], allWidth?: number) => {if (allWidth) {const result: Record<string, number> = {};colList.forEach(({ width }, index) => {result[`_${index}`] = parseFloat(((width as number) / allWidth).toFixed(2));});updateTitleWidthMap(result);}});// 此useEffect为第一次执行表格渲染时,生成对应的列配置useEffect(() => {// 初始化时,根据配置项,设置表格列的宽度,并记录对应百分比if (wrapperWidth && !isInit) {const resultTableCol = getTableNumberWidthCol();setTitleWidthMapMethod(resultTableCol, wrapperWidth);setTableShowColumns(resultTableCol);setInit(true);}}, [isInit,wrapperWidth,tableShowColumns,setInit,setTableShowColumns,getTableNumberWidthCol,setTitleWidthMapMethod,]);// 当容器宽度变化时,根据每列所占的比例,重新结算列宽useEffect(() => {if (wrapperWidth && isInit) {setTableShowColumns((oldColumns) => {const result: ColumnType<any>[] = [];const titleWidthMap = titleWidthMapRef?.current?.titleWidthMap;oldColumns.forEach((col, index) => {const pervent = titleWidthMap?.[`_${index}`];result.push({...col,width: wrapperWidth * pervent!,});});const totalWidth = result.reduce((sum, cur) => sum + parseFloat(`${cur.width!}`), 0);result[result.length - 1].width = wrapperWidth + parseFloat(`${result[result.length - 1].width!}`) - totalWidth;return result;});}}, [isInit, wrapperWidth, titleWidthMapRef, setTableShowColumns]);return {tableShowColumns,isInit,setTitleWidthMapMethod,} as const;
};export default useTableCol;

useResizeTableCol.tsx:将表格的列转成可拖拽列,增加相关方法

import { useMemoizedFn, useSafeState } from 'ahooks';
import type { ColumnType } from 'antd/lib/table';
import { useState, useEffect } from 'react';
import useTableCol from './useTableCol';const useResizeTableCol = (wrapperWidth: number | undefined, tableRef: any, columns: ColumnType<any>[]) => {const [colIsInit, setColInit] = useSafeState<boolean>(false);const [tableColumns, setTableColumns] = useState<ColumnType<any>[]>(columns);const { tableShowColumns, isInit, setTitleWidthMapMethod } = useTableCol(wrapperWidth, columns);const handleResize = useMemoizedFn((index: number) => (e: any, { size }: any) => {e.stopImmediatePropagation();if (tableRef.current) {const widthList = [...(tableRef.current as HTMLElement).querySelectorAll('.ant-table-thead th.react-resizable'),].map((th) => {return (th as HTMLElement).getBoundingClientRect().width;});setTableColumns((col) => {const nextColumns = [...col];const { width: oldWidth } = nextColumns[index];// 此次平移的宽度const offsetWidth = size.width - Number(oldWidth || 0);// 当前列得宽度const currentWidth = widthList[index] + offsetWidth;const nextWidth = widthList[index + 1] - offsetWidth;// 左移,当前宽度小于42if (currentWidth < 42) {widthList[index] = 42;widthList[index + 1] = nextWidth - 42 + currentWidth;} else if (nextWidth < 42) {// 右移,下一列得宽度小于42widthList[index] = currentWidth - 42 + nextWidth;widthList[index + 1] = 42;} else {widthList[index] = currentWidth;widthList[index + 1] = nextWidth;}console.log(widthList);const resultColumns = nextColumns.map((nextCol, _index) => ({...nextCol,width: widthList[_index],onHeaderCell:_index !== nextColumns.length - 1? () => ({width: widthList[_index],onResize: handleResize(_index),}): undefined,}));setTitleWidthMapMethod(resultColumns, wrapperWidth);return resultColumns;});}});useEffect(() => {if (isInit) {setTableColumns(tableShowColumns.map((col, index) => ({...col,onHeaderCell:index !== tableShowColumns.length - 1? () => ({width: col.width,onResize: handleResize(index),}): undefined,})),);setColInit(true);}}, [tableShowColumns, isInit, setTableColumns, handleResize, setColInit]);return {colIsInit,tableColumns,} as const;
};export default useResizeTableCol;

ResizeableTitle.tsx:自定义可拖动的表头th

import React, { useMemo, useState } from 'react';
import type { ResizeCallbackData } from 'react-resizable';
import { Resizable } from 'react-resizable';const ResizeableTitle: React.FC<any> = (props) => {const { width, className, children, onResize, style = {}, ...resetProps } = props;const [offset, setOffset] = useState<number>(0);const [nextWidth, setNextWidth] = useState<number>(58);const getTranslateX = useMemo(() => {if (offset >= nextWidth + 42) {return nextWidth - 42;}return offset;}, [offset, nextWidth]);if (className?.includes('ant-table-selection-column')) {return (<th className={className} {...resetProps}>{children}</th>);}// console.log(props);if (onResize) {return (<Resizablewidth={width + offset}height={0}handle={<spanclassName={`react-resizable-handle ${offset ? 'active' : ''}`}style={{ transform: `translateX(${getTranslateX}px)` }}onClick={(e) => {e.stopPropagation();e.preventDefault();}}/>}// onResizeStart={() => (this.resizing = true)}onResizeStop={(...arg: any[]) => {setOffset(0);onResize(...arg);}}onResizeStart={(e: any) => {const _nextWidth = e.target.parentNode.nextSibling.getBoundingClientRect().width;setNextWidth(_nextWidth);}}onResize={(e: any, { size }: ResizeCallbackData) => {const currentOffset = size.width - width;if (currentOffset > nextWidth - 42) {setOffset(nextWidth - 42);} else {setOffset(currentOffset);}}}draggableOpts={{enableUserSelectHack: true,minConstraints: [width - 42, 0],maxConstraints: [width + nextWidth, 0],}}><th className={className} style={{ ...style, width: width + 'px' }} {...resetProps}><divstyle={{ width: width + 'px' }}className="ofs-table-cell-wrapper"title={typeof children.join('') === 'string' ? children.join('') : ''}><div className="ofs-table-cell">{children}</div></div></th></Resizable>);}return (<th className={className} style={{ ...style, width: width + 'px' }}><divstyle={{ width: width + 'px' }}className="ofs-table-cell-wrapper"title={typeof children.join('') === 'string' ? children.join('') : ''}><div {...resetProps} className="ofs-table-cell">{children}</div></div></th>);
};export default ResizeableTitle;

index.less:表格样式

.react-resizable {position: relative;
}.react-resizable-handle {position: absolute;z-index: 999;bottom: 0;right: 0;width: 2px;height: 100%;cursor: col-resize;&.active::before {position: absolute;top: 0;bottom: 0;left: 50%;height: 1000px;border-left: 2px solid #d0d0d0;content: '';}
}
.ant-table-wrapper {position: relative;overflow: hidden;
}
.ofs-table-row {display: flex;flex-direction: row;width: 100%;overflow: hidden;
}
.ofs-table-cell-wrapper {width: 100%;overflow: hidden;
}
.ofs-table-cell {padding: 0 5px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;
}
.ant-table-thead > tr > th,
.ant-table-tbody > tr > td,
.ant-table tfoot > tr > th,
.ant-table tfoot > tr > td {padding: 16px 0px;
}
.ant-table-thead > tr > th:last-child span.react-resizable-handle {display: none;
}
.ant-table-thead {.ant-table-cell-ellipsis {overflow: visible;& > div {overflow: hidden;text-overflow: ellipsis;white-space: nowrap;word-break: keep-all;}}
}

index.tsx:表格实现

import React, { useEffect, useRef, useState } from 'react';
import { Table } from 'antd';
import ResizeableTitle from './components/ResizeableTitle';
import type { ColumnType } from 'antd/lib/table/interface';
import './index.less';
import useResizeTableCol from './hooks/useResizeTableCol';
import { useSize } from 'ahooks';
const columes = [{title: 'Full Name',width: '10%',dataIndex: 'name',key: 'name',},{title: 'Age',width: '10%',dataIndex: 'age',key: 'age',},{ title: 'Column 1', dataIndex: 'address', ellipsis: true, width: '10%', key: '1' },{ title: 'Column 2', dataIndex: 'address', ellipsis: true, width: '10%', key: '2' },{ title: 'Column 3', dataIndex: 'address', ellipsis: true, width: '10%', key: '3' },{ title: 'Column 4', dataIndex: 'address', ellipsis: true, width: '10%', key: '4' },{ title: 'Column 5', dataIndex: 'address', ellipsis: true, width: '20%', key: '5' },{ title: 'Column 6', dataIndex: 'address', ellipsis: true, width: '20%', key: '6' },{ title: 'Column 7', dataIndex: 'address', ellipsis: true, width: 100, key: '7' },{ title: 'Column 8', dataIndex: 'address', ellipsis: true, width: 100, key: '8' },{title: 'aa',key: 'operation',ellipsis: true,width: 100,// fixed: 'right',render: () => <a>action</a>,},
];
const data = [{key: '1',name: 'John Brown',age: 32,address: 'New York Park',},{key: '2',name: 'Jim Green',age: 40,address: 'London Park',},
];
const AntdTableTest: React.FC = () => {const tableRef = useRef(null);const tableWrapperSize = useSize(tableRef);const [wrapperWidth, setWrapperWidth] = useState<number>();const { colIsInit, tableColumns } = useResizeTableCol(wrapperWidth, tableRef, columes);useEffect(() => {console.log(tableWrapperSize);if (tableWrapperSize) {setWrapperWidth(tableWrapperSize.width);}}, [tableRef, tableWrapperSize]);return (<><div ref={tableRef}>{colIsInit ? (<TablerowSelection={{type: 'checkbox',}}columns={tableColumns as ColumnType<any>[]}components={{header: {cell: ResizeableTitle,},}}dataSource={data}/>) : null}</div></>);
};export default AntdTableTest;

React基于antd Table实现可拖拽调整列宽的表格相关推荐

  1. vue 表格左右拖拽调整列宽_解决 | iview低版本实现表格拖拽,滚动条列宽计算问题...

    文 / 景朝霞 来源公号 / 朝霞的光影笔记 ID / zhaoxiajingjing 点个赞,让我知道你来过~ 如果大佬觉得我的方案太low,请打脸轻一点~ 如果大佬有更好的方案,请不吝赐教~ 0 ...

  2. Ant Design + react-drag-listview实现Table拖拽变换列位置

    Ant Design + react-drag-listview实现Table拖拽变换列位置 Ant Design + react-drag-listview + react-resizable 实现 ...

  3. iview table 自定义列_案例 | iview中Table:拖拽适配列、自定义固定列、合并行

    文 / 景朝霞 来源公号 / 朝霞的光影笔记 ID / zhaoxiajingjing ❥❥❥❥点个赞,让我知道你来过~❥❥❥❥ 0 / 更新Table "iview": &quo ...

  4. 使用jQuery开发一个基于HTML5的漂亮图片拖拽上传web应用

    昨天我们介绍了一款HTML5文件上传的jQuery插件:jQuery HTML5 uploader,今天我们将开发一个简单的叫upload center的图片上传程序,允许用户使用拖拽方式来上传电脑上 ...

  5. vuedraggable嵌套块拖拽_Vue 基于 vuedraggable 实现选中、拖拽、排序效果

    今天有个朋友说要做个效果:Vue实现拖拽排序,要有 checked,输出结果是排序后的,要全选,未选中的不能拖动. 其实我之前基于 Sortable 做过一个类似的效果.也给他看过了,没看太明白,他就 ...

  6. css拖拽调整高度,两种为wangEditor添加拖拽调整高度的方式:CSS3和jQuery UI

    wangEditor是一款优秀的Web富文本编辑器,但如果能像KindEditor那样支持拖拽调整高度就更好了.有两种方式可以为wangEditor添加这一功能,这里使用的wangEditor版本为2 ...

  7. Element UI表格拖拽(vue中) —— 行拖拽、列拖拽

    目录 安装依赖 vuedraggable 实现拖拽的要点 行拖拽要点 列拖拽要点 完整范例代码 安装依赖 vuedraggable 安装  vuedraggable 的同时,会自动安装 sortabl ...

  8. vue中实现拖拽调整顺序功能

    一.使用vuedraggable是标准的组件式封装 或 vue-dragging 指令方式 实现拖拽调整顺序功能. 1:安装依赖 npm install vuedraggable或yarn add v ...

  9. 鼠标拖拽调整div大小

    鼠标拖拽调整div大小 实现思路 根据鼠标位置改变鼠标样式 当鼠标在div的边缘和四个角时显示不同的样式,通过cursor修改 当鼠标在div的边缘和四个角按下时记录具体坐标点位置, 并开始根据鼠标的 ...

最新文章

  1. 多线程共享全局变量以及锁机制
  2. 耗时很长的程序忘加nohup就运行了怎么办?
  3. Trie树检索字符串
  4. cefsharp 手机模式_微信“蓝光模式”保护眼睛
  5. 计算机网络按功能自底而上划分,大连理工大学2011计算机期末模拟题3
  6. Stanford UFLDL教程 白化
  7. 刚刚,Python 3.10 正式发布了!我发现了一个可怕的功能...
  8. 各种有用的东西留言板
  9. 动脑2017android_您肯定要在2017年初尝试的25个新Android库
  10. 2015 - Deep recurrent q-learning for partially observable MDPs
  11. 数据库问题6-將系統資料表對應至系統檢視
  12. 东华大学python题库_2020尔雅纺纱学(东华大学)完整答案
  13. wordpress搜索ajax,基于wordpress的ajax写法详解
  14. 两相四线混合式步进电机用双H桥驱动电路之Multisim仿真及优化
  15. 2020建模穿越沙漠第一关python代码
  16. 计算机专业学生的必备文具,初一新生必备文具100件 学霸用的文具清单
  17. 未知的软件异常0xc0000409解决办法
  18. Linux python 虚拟环境搭建与配置
  19. Debug Diagnostic Tool
  20. 《寂静之声》口琴版,惊艳,有链接

热门文章

  1. 宫崎骏动画里的新垣结衣见过没?这个开源动漫生成器让你的照片秒变手绘日漫...
  2. 分布式事务 - Seata - TCC模式
  3. JVM内存Xmx和Xmn设置
  4. mysql+'@'%_mysql全局权限账户%登录不上ERROR 1045 (28000): Access denied for user 'zzq'@'localhost' (usi......
  5. 陈越 数据结构第一节
  6. 基于nodejs+selenium自动过滑动验证码的QQ刷赞
  7. 一点直播卡顿的处理思考
  8. 如何查询电脑的是ip地址
  9. Vue——计算属性与侦听器
  10. 热点书库小说多线程下载器 V1.0