项目背景 : react + ant
单纯实现拖拽确实不难 , 我的需求是根据后台接口返回 , 生成对应的父子表格 , 并只可以拖拽子的位置 , 如图
后台返回的数据结构 (pid为0说明是父 , 子的pid等于父的id , 说明是父的子)
1 , 我先转成了树形结构且自己加上了key (注意 : key一定得是唯一的 , 否则会出现第一列数据拖拽不动 , 其他列数据可以拖拽的bug)
2 , 生成对应的父子表格 , 父要有对应的子 , 如图
3 , 更改onDragEnd的逻辑 , 实现效果
首先用.findIndex+.some来寻找当前子所属的父元素 , 然后再在父元素中用arrayMove将子调整顺序 , 最后创建一个新的副本仅仅更新当前父元素的子(注意 : 很关键 , 否则数据不更新) , 实现效果
否则就会如下图 , 有拖拽无更新 (图不太清晰 , 但"三里屯一直最上方")
整体代码
// 第一步的步骤 , 主要是生成我需要的树形结构和key , key很关键 , 必须唯一 !!!
const res = await getList()console.log('res', res)const parentRes = res.filter(item => item.parentId == 0)const parentRes1 = [...new Map(parentRes.map(item => [item.locationId, item])).values()] //拿到去重后的parentId为0且locationId不同的数据const parentRes2 = parentRes1.map((item, index) => ({...item, // 保留原有属性children: [],key: item.locationId}))const sonRes = res.filter(item => item.parentId !== 0)const sonRes1 = sonRes.map((item, index) => ({...item, // 保留原有属性key: item.locationId}))setSonData(sonRes1)//用俩个for循环遍历将sonRes根据parentId插入到对应的父节点children中for (let i = 0; i < sonRes1.length; i++) {for (let j = 0; j < parentRes2.length; j++) {if (sonRes1[i].parentId === parentRes2[j].locationId) {parentRes2[j].children.push(sonRes1[i])}}}//俩个for循环才能让children里的locationName也换成placefor (let i = 0; i < parentRes2.length; i++) {parentRes2[i].place = parentRes2[i].locationNamedelete parentRes2[i].locationNamefor (let j = 0; j < parentRes2[i].children.length; j++) {parentRes2[i].children[j].place = parentRes2[i].children[j].locationNamedelete parentRes2[i].children[j].locationName}}console.log('data', parentRes2)setData(parentRes2)// 第三步的逻辑
const onDragEnd = ({ active, over }) => {console.log('active', active)console.log('over', over)if (active.id !== over?.id) {setData(prevData => {// 寻找当前拖拽元素所属的父元素const parentIndex = prevData.findIndex(parent =>parent.children.some(child => child.key === active.id))if (parentIndex !== -1) {const parent = prevData[parentIndex]// 在父元素的children中调整顺序const activeChildIndex = parent.children.findIndex(child => child.key === active.id)const overChildIndex = parent.children.findIndex(child => child.key === over?.id)if (activeChildIndex > -1 && overChildIndex > -1) {const newChildren = arrayMove(parent.children,activeChildIndex,overChildIndex)// 创建一个新的prevData副本,仅更新当前父元素的childrenconst newData = [...prevData]newData[parentIndex] = { ...parent, children: newChildren }// 收集newData中所有对象的id和sort值传递给后端const updatedItems = newChildren.map(item => ({id: item.locationId,sort: item.sort}))getSortMethod({ sorts: updatedItems }) // 传递给后端return newData}}console.log('prevData', prevData)return prevData // 如果没有找到或无法处理,返回原数据})}}// 这里是第二步的内容 , 生成对应的父子表格
{data.map((item, index) => { return (<div key={item.key}><DndContextmodifiers={[restrictToVerticalAxis]}onDragEnd={onDragEnd}><SortableContextitems={SonData.map(i => i?.key)}strategy={verticalListSortingStrategy}><TablerowKey='key'columns={updatedColumns} // 确保此处的columns是根据item动态调整后的dataSource={item.children} // 此处确保使用当前item的children作为dataSourcepagination={false}components={{body: {row: Row1}}}isSelectAll={isSelectAll}rowSelection={{...rowSelection2,selectedRowKeys: selectedRowKeys2,checkStrictly}}/></SortableContext></DndContext></div>)})}