Bootstrap

快速实现一个table组件的拖拽排序功能(antd)

antd官方给的示例里引入了一堆三方库,写法更是花里胡哨,感到极其的繁琐,这了介绍一下自己实现的方法。(经过反馈官方已在2023.1月更新了新的demo,现在优雅了很多,不喜欢三方库的同学可以看看以下纯原生写法)

方法封装

const onRow = (state: [], setState: any) => ({
  draggable: true,
  style: { cursor: 'move' },
  onDragStart: (ev: any) => {
    ev.dataTransfer.effectAllowed = 'move';
    ev.dataTransfer.setData('text', ev.target.getAttribute('data-row-key'));
  },
  onDragEnter: (ev: any) => {
    const nodes = ev.target.parentNode.childNodes;
    nodes.forEach((item: any) => (item.style.borderTop = '2px dashed #1890ff'));
  },
  onDragLeave: (ev: any) => {
    const nodes = ev.target.parentNode.childNodes;
    nodes.forEach((item: any) => (item.style.borderTop = ''));
  },
  onDrop: (ev: any) => {
    ev.preventDefault();
    ev.stopPropagation();
    const dragId = Number(ev.dataTransfer.getData('text'));
    const dropCol =
      ev.target.tagName !== 'TR' ? ev.target.parentNode : ev.target;
    // dropCol.parentNode.insertBefore(dragCol, dropCol); // DOM操作
    const dropId = Number(dropCol.getAttribute('data-row-key'));
    const dragIndex = state.findIndex((item: any) => item.id === dragId); // 注意这里的id
    const dropIndex = state.findIndex((item: any) => item.id === dropId);
    const data = [...state];
    const item = data.splice(dragIndex, 1); // 移除
    data.splice(dropIndex, 0, item[0]); // 插入
    setState(data);
    dropCol.childNodes.forEach((item: any) => (item.style.borderTop = ''));
  },
  onDragOver: (ev: any) => ev.preventDefault(),
});

方法使用


<Table
  rowKey="id"
  dataSource={query}
  pagination={false}
  onRow={() => onRow(query, setQuery)}
>
  <Table.Column
    title="查询条件"
    dataIndex="label"
  />
</Table>

其他代码


const [query, setQuery] = useState<any>([]);

说明


antd的table组件有个onRow字段,该字段就是表格对应的tr标签。

首先开启draggable属性让元素可以拖拽,在onDragStart里使用事件对象的dataTransfer.setData方法记录拖拽元素的data-row-key,这个key即在table组件设置的rowKey,要保证其唯一性;在onDrop里使用getData读取拖拽元素的key,通过移除和插入更新数组的顺序重新render;在onDragOver阻止浏览器的默认事件。三个拖拽事件相互配合即可轻松快速的完成一款纯天然的拖拽效果,无需引入其它三方库。

onDragEnter和onDragLeave用来添加一些交互展示效果,也可以不用。

另外需要注意:

在onDrop方法中获取dragIndex和dropIndex时,比较时的字段需要和Table的rowKey一致。比如你的rowKey是uid,那么就要写成const dragIndex = state.findIndex((item:any) => item.uid === dragId)。

如果你的单元格里嵌套了多个元素,那么获取dropCol就要使用递归写法,一直找到tagName为TR为止。const dropCol = ev.target.tagName !== 'TR' ? ev.target.parentNode : ev.target

在实际生产中还需要更严谨一点,比如判断是否放在拖动元素上,以及向下拖拽插入时,需要记录clientY并和放置的元素的clientY进行比较,来决定样式加borderTop还是bordeBottom等。

文章原文:快速实现一个antd的table组件的拖拽排序功能https://juejin.cn/post/7181679788774391863

;