antd官方给的示例里引入了一堆三方库,写法更是花里胡哨,感到极其的繁琐,这了介绍一下自己实现的方法。(经过反馈官方已在.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); // 注意这里的idconst 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(),});
方法使用
<TablerowKey="id"dataSource={query}pagination={false}onRow={() => onRow(query, setQuery)}><Table.Columntitle="查询条件"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组件的拖拽排序功能/post/7181679788774391863