antd表格结合react-dnd、react-dnd-html5-backend、immutability-helper实现表头拖拽功能

桑鸿志
2023-12-01

最近公司打算重构老项目,采用的技术是蚂蚁的umi框架、antd design pro 、react16.8以及dva。重构过程中,遇到了很多坑,解决了也收获了很多,在这里先说一下antd中的表格表头拖拽排序功能。

先去antd官网了解一下table的相关api方法,目前它做了表格内数据的上下拖拽排序,是结合react-dnd、react-dnd-html5-backend、immutability-helper这三个插件来实现的,这三个插件网上都各自有相应的介绍和用法,自己百度哦。

这里我们想横向拖动表头也是借鉴这个方法去实现的,我们需要在表头的onHeaderCell上绑定moveCell函数(拖拽函数)

 onHeaderCell: column => (
        {
          width: column.width,
          onResize: handleResize(index), //表头可伸缩函数
          index:index,
          moveCell: moveCell, //拖拽函数
          column: column,//表头数据
        }
      ), /

在项目中,因为表头还需要有左右滑动增加宽度的功能,所以这里我把这个功能写到了Resizable这个组件中

// 表头编辑配置
const ResizeableTitle = props => {
  const {
    onResize, width, handle = () => {
    }, index, moveCell, column, ...restProps
  } = props;
  const ref = React.useRef();
  const [{ isOver, dropClassName }, drop] = useDrop({
    accept: type,
    collect: monitor => {
      const { index: dragIndex } = monitor.getItem() || {};
      if (dragIndex === index) {
        return {};
      }
      return {
        isOver: monitor.isOver(),
        dropClassName:
          dragIndex < index ? " drop-over-downward" : " drop-over-upward"
      };
    },
    drop: item => {
      // console.log(item)
      moveCell(item.index, index);
    }
  });
  const [, drag] = useDrag({
    item: { type, index },
    collect: monitor => ({
      isDragging: monitor.isDragging()
    })
  });
  drop(drag(ref));
  if (!width) {
    return <th {...restProps} />;
  }
  return (
    <Resizable
      width={width}
      height={0}
      onResize={onResize}
      draggableOpts={{ enableUserSelectHack: false }}
    >
      {//这里是做的特殊区分,有些表头不能拖拽
        column.key == 'eEdit' || column.key == 'taskInfo' || column.key == 'eLock' 
        || column.key == 'material_count' || column.key == 'title' || column.key == 'scoreVOs' 
        || !column.drag_able || column.is_lock ?
        <th
          style={{width:width}}
          {...restProps}
        />
        :
        <th>
          <span ref={ref} {...restProps} style={{ cursor: "pointer !important", width:width }}></span>
        </th>
      } 
    </Resizable>
  );
};

上面的代码中,就将表头拖拽和伸缩功能都ok了,伸缩不用多说,在这里,着重说一下拖拽点的问题,就是ref,因为是将拖拽和伸缩绑定在了一起,为了尽可能地避免两者功能相互干扰,将拖拽点方法了表头的文字上。

然后是moveCell函数,这个和moveRow的函数差不多,不多说

const moveCell = (dragIndex, hoverIndex) => {
    const  _columns  = columns;
    const dragCell = _columns[dragIndex];
    const hoverCell = _columns[hoverIndex];
    let newColumns = update(columns,{$splice:[[dragIndex, 1], [hoverIndex, 0, dragCell]]}); 
    let newHeaders = newColumns.concat(unShowHeaders)
    let j = 0
    let rowData = [];
   
    for (let i = 0; i < newHeaders.length; i++) {
      headers.forEach((header) => {
        if (newHeaders[i].key == header.key) {
          header.order = j;
          j++;
          rowData.push(header)
        }
      })

    }
    props.putRowEdit(rowData, false);
    setColumns(formatRows(newColumns));
  }

剩下的就是使用ResizeableTitle 覆盖table的cell 、DndProvider容器设定以及HTML5Backend的背景

<DndProvider backend={HTML5Backend} >
          <Table
            components={{
              header: {
                cell: ResizeableTitle,
              },
              body: {
                row: DragableBodyRow
              },
            }}
            onRow={(record, index) => {
              return {
                index,
                moveRow: moveRow
              }
            }}
            bordered
            dataSource={dataSource}
            columns={mergedColumns}
            rowClassName={(record, index) => onRowClassName(record, index)}
          />
    </DndProvider>

ok,到这里就能拖拽和伸缩表头了

 类似资料: