美文网首页
react-dnd + antd table实现可拖拽的树状表格

react-dnd + antd table实现可拖拽的树状表格

作者: 秘果_li | 来源:发表于2022-07-05 16:23 被阅读0次

ant design 官方组件有树状表格实现,也有可拖拽的表格,要实现这两者的结合,官方可拖拽的表格支持的是平铺数据结构,对于树状结构需要做一些处理

  • 这个 demo 可以自定义拖拽的范围,是否跨层级拖拽等自定义功能

react-dnd 的新版本中没有导出 DragSource, DropTarget,这里用的版本是
"react-dnd": "^14.0.1"
"react-dnd-html5-backend": "^14.0.1"

import { DndProvider, DragSource, DropTarget } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';

BodyRow 为可拖动表格行,props可接收拖动的数据,及函数信息,不同的层级可设置不同的style

class BodyRow extends React.Component {
  render() {
    const {
      isOver,
      connectDragSource,
      connectDropTarget,
      record,
      moveRow,
      ...restProps
    }: any = this.props;
    const style = {
      ...restProps.style,
      cursor: 'move',
      background: record?.parentId ? '#f7f8fa' : '#FFFFFF',
    };

    let { className } = restProps;
    if (isOver) {
      if (restProps.index > dragingIndex) {
        className += ' drop-over-downward';
      }
      if (restProps.index < dragingIndex) {
        className += ' drop-over-upward';
      }
    }
    return connectDragSource(
      connectDropTarget(<tr {...restProps} className={className} style={style} />)
    );
  }
}

DragableBodyRow 为可拖动行的封装组件,封装拖动行为逻辑,
data-row-key 为表格中的 rowKey 作为唯一标识

const rowSource = {
  beginDrag(props: any) {
    const dragingKey = props['data-row-key'];
    dragingIndex = props.index;
    return {
      'data-row-key': dragingKey,
      index: props.index,
    };
  },
};

const rowTarget = {
  drop(props: any, monitor: any) {
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;
    const dragKey = monitor.getItem()['data-row-key'];
    const hoverKey = props['data-row-key'];
    if (dragKey === hoverKey) {
      return;
    }
    props.moveRow(dragIndex, hoverIndex, dragKey, hoverKey);
    monitor.getItem().index = hoverIndex;
    monitor.getItem()['data-row-key'] = hoverKey;
  },
};

const DragableBodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({
  connectDropTarget: connect.dropTarget(),
  isOver: monitor.isOver(),
}))(
  DragSource('row', rowSource, connect => ({
    connectDragSource: connect.dragSource(),
  }))(BodyRow)
);

moveRow 函数封装拖拽控制逻辑,dragKey, hoverKey 为行内 id,替代默认的 index 控制移动(如果用index作为唯一标识,树状结构移动后 index 会改变,无法用作唯一识别,在平铺结构中可以直接用index做唯一标识符)

  const moveRow = (dragIndex: number, hoverIndex: number, dragKey: number, hoverKey: number) => {
   
    const dragObject = data.filter(ele => ele.id === dragKey)[0];
    const hoverObject = data.filter(ele => ele.id === hoverKey)[0];

    if (dragObject.parentId === hoverObject.parentId) {
      let newData = data.filter((ele: any) => ele.id !== dragKey);
      let insertIndex = 0;
      newData.forEach((ele: any, index) => {
        if (ele.id === hoverKey) {
          if (dragIndex > hoverIndex) {
            // 上移:dragKey表示的元素   放到 hoverKey表示的元素之前
            insertIndex = index;
          } else {
            // 下移  dragKey表示的元素 放到  hoverKey表示的元素之后
            insertIndex = index + 1;
          }
        }
      });
      newData.splice(insertIndex, 0, dragObject);
      // console.log(newData);
      setData(newData);
    } else {
      let newData = data.filter((ele: any) => ele.id !== dragKey);
      let insertIndex = 0;
      newData.forEach((ele: any, index) => {
        if (ele.id === hoverKey) {
          if (dragIndex > hoverIndex) {
            insertIndex = index;
          } else {
            insertIndex = index + 1;
          }
        }
      });
      if (dragObject.parentId && hoverObject.parentId) {
        dragObject.parentId = hoverObject.parentId;
        newData.splice(insertIndex, 0, dragObject);
        setData(newData);
      } else if (dragObject.parentId && !hoverObject.parentId) {
        dragObject.parentId = hoverKey;
        newData.splice(insertIndex, 0, dragObject);
        setData(newData);
      }
    }
  };

toTree 函数将平铺的数组变为树状结构,具体参考https://www.jianshu.com/p/639856724e92

export default () => {
  const [data, setData] = useState([]);
  const [expandedRowKeys, setExpandedRowKeys] = useState([]);
  const components = {
    body: {
      row: DragableBodyRow,
    },
  };

  const expandedRowsChange = (expandedRowKeys: any) => {
    setExpandedRowKeys(expandedRowKeys);
  }; // 设置默认展开的行

  return (
       <DndProvider backend={HTML5Backend}>
          <Table
            pagination={false}
            defaultExpandAllRows={true}
            rowKey="id"
            columns={columns}
            dataSource={toTree(data)}
            components={components}
            expandedRowKeys={expandedRowKeys}
            onExpandedRowsChange={expandedRowsChange}
            onRow={(record, index) => ({
              record,
              index,
              moveRow: moveRow,
            })}
          />
        </DndProvider>
    )
}

相关文章

网友评论

      本文标题:react-dnd + antd table实现可拖拽的树状表格

      本文链接:https://www.haomeiwen.com/subject/deusbrtx.html