react中使用react-resizable实现antd的table表格列拖拽调整宽度

react中使用react-resizable实现antd的table表格列拖拽调整宽度

前言

本篇文章主要讲述如何是使用react-resizable实现antd的table表格列拖拽调整宽度,其实这个功能在antd的3.x版本是有的,但是到了4以后,antd的官网就没有示例了。

本篇文章和3.x的示例有所不同,不是实时更新拖拽位置,我会在拖拽结束的时候更新列的位置,并且拖拽的时候,自定义加了一条线用于方便查看拖拽位置。

先上实现效果图:
在这里插入图片描述
我会在下面将demo的源码都贴上,以供大家参考。

1.安装react-resizable

npm install react-resizable

2.源码

import React, { useState, useRef, useEffect } from "react";

import { useSafeState } from "ahooks";
import styled from "styled-components";
import { Resizable } from "react-resizable";

import { Table } from "antd";

const ResizeTable = styled.div`
  position: relative;
  background-color: #fff;
  overflow: hidden;
  .ant-table-thead {
    th {
      font-weight: 600 !important;
      -moz-user-select: none;
      -o-user-select: none;
      -khtml-user-select: none;
      -webkit-user-select: none;
      -ms-user-select: none;
      user-select: none;
    }
  }

  .ant-table-cell {
    .superform-field-control-readonly {
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
      word-break: keep-all;
      display: block;
      line-height: 32px;
    }
  }
  .ant-table-thead,
  .ant-table-tbody {
    tr {
      td,
      th {
        padding: 8px;
      }
    }
  }
  .react-resizable {
    position: relative;
    background-clip: padding-box;
  }

  .react-resizable-handle {
    width: 10px;
    height: 100%;
    cursor: ew-resize;
    position: absolute;
    bottom: 0;
    right: 0px;
    z-index: 1;
  }
`;

const StyledLine = styled.div`
  display: none;
  height: 100%;
  width: 1px;
  background: #bfbfbf;
  position: absolute;
  z-index: 999;
  left: 0px;
`;

// 调整table表头
const ResizeableTitle = (props) => {
  const { onResize, width, datagridRef, stretchRef, ...restProps } = props;

  // 添加偏移量
  const [offset, setOffset] = useSafeState(0);

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

  // 获取要减去的父元素距离左侧的宽度
  const getPosition = (element) => {
    let actualLeft = element.offsetLeft;
    let current = element.offsetParent;
    while (current !== null) {
      //当它上面有元素时就继续执行
      actualLeft += current.offsetLeft;
      current = current.offsetParent; //继续找父元素
    }
    return { x: actualLeft };
  };

  const handleResize = (e, { size }) => {
    // 这里只更新偏移量,数据列表其实并没有伸缩
    setOffset(size.width - width);
    let parentNodeX = getPosition(datagridRef.current).x;
    let stretchDom = stretchRef.current;
    stretchDom.style.display = "block";
    stretchDom.style.left = `${e.clientX - parentNodeX + 2}px`;
  };

  const handleResizeStop = (...arg) => {
    // 拖拽结束以后偏移量归零
    setOffset(0);
    let stretchDom = stretchRef.current;
    stretchDom.style.display = "none";
    stretchDom.style.left = `0px`;
    // props传进来的事件,在外部是列数据中的onHeaderCell方法提供的事件
    onResize(...arg);
  };

  const _ResizableSpan = (
    <span
      className="react-resizable-handle"
      // 拖拽层偏移
      style={{ transform: `translateX(${offset}px)` }}
      onClick={(e) => {
        // 取消冒泡,不取消貌似容易触发排序事件
        e.stopPropagation();
        e.preventDefault();
      }}
    />
  );

  return (
    <Resizable
      width={width + offset}
      handle={_ResizableSpan}
      height={0}
      // 拖拽事件实时更新
      onResize={handleResize}
      // 拖拽结束更新
      onResizeStop={handleResizeStop}
      draggableOpts={{ enableUserSelectHack: false }}
    >
      <th {...restProps} />
    </Resizable>
  );
};

// table 数据
let dataSource = [];
for (let i = 1; i <= 100; i++) {
  dataSource.push({
    key: i,
    number: i,
    text: `单行${i}`,
    textarea: `多行${i}`,
  });
}

const ResizTableColumns = () => {
  const [cols, setCols] = useState([
    {
      title: "数值输入框",
      dataIndex: "number",
      key: "number",
      width: 300,
      ellipsis: true,
    },
    {
      title: "单行文本框",
      dataIndex: "text",
      key: "text",
      width: 300,
      ellipsis: true,
    },
    {
      title: "多行文本框",
      dataIndex: "textarea",
      key: "textarea",
      ellipsis: true,
    },
  ]);
  const [columns, setColumns] = useState([]);

  const datagridRef = useRef(null);
  const stretchRef = useRef(null);

  useEffect(() => {
    setColumns(
      (cols || []).map((col, index) => ({
        ...col,
        onHeaderCell: (column) => ({
          width: column.width,
          stretchRef,
          datagridRef,
          onResize: handleResize(index),
        }),
      }))
    );
  }, [cols]); //eslint-disable-line

  // 定义头部组件
  const components = {
    header: {
      cell: ResizeableTitle,
    },
  };

  // 处理拖拽
  const handleResize =
    (index) =>
    (e, { size }) => {
      const nextColumns = [...cols];
      // 拖拽是调整宽度
      nextColumns[index] = {
        ...nextColumns[index],
        width: size.width,
      };
      setCols([...nextColumns]);
    };
  return (
    <ResizeTable ref={datagridRef}>
      <Table
        dataSource={dataSource}
        rowKey={(record) => record.key}
        columns={columns}
        components={components}
        bordered
        // pagination={false}
      />
      {/* 拖拽列的线样式 */}
      <StyledLine ref={stretchRef} style={{ bottom: 64 }} />
    </ResizeTable>
  );
};

export default ResizTableColumns;

3.注意事项:

1.需要将表格的头部禁止用户选择,不然拖拽的时候会导致选中文字,松开鼠标,还会继续拖拽
2.本文的分页是放出来的,所以拖拽列的线的bottom是设置了64(分页的高度),如果不需要分页,bottom可以设置为0
3.需要拖拽的列就一定需要设置宽度,不然会导致拖拽不了
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值