react 表格实现拖拽功能

项目背景 : react + ant

单纯实现拖拽确实不难 , 我的需求是根据后台接口返回 , 生成对应的父子表格 , 并只可以拖拽子的位置 , 如图

后台返回的数据结构 (pid为0说明是父 , 子的pid等于父的id , 说明是父的子)

1 , 我先转成了树形结构且自己加上了key (注意 : key一定得是唯一的 , 否则会出现第一列数据拖拽不动 , 其他列数据可以拖拽的bug)

2 , 生成对应的父子表格 , 父要有对应的子 , 如图

3 , 更改onDragEnd的逻辑 , 实现效果
首先用.findIndex+.some来寻找当前子所属的父元素 , 然后再在父元素中用arrayMove将子调整顺序 , 最后创建一个新的副本仅仅更新当前父元素的子(注意 : 很关键 , 否则数据不更新) , 实现效果
否则就会如下图 , 有拖拽无更新 (图不太清晰 , 但"三里屯一直最上方")

整体代码

javascript 复制代码
 

// 第一步的步骤 , 主要是生成我需要的树形结构和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也换成place
    for (let i = 0; i < parentRes2.length; i++) {
      parentRes2[i].place = parentRes2[i].locationName
      delete parentRes2[i].locationName
      for (let j = 0; j < parentRes2[i].children.length; j++) {
        parentRes2[i].children[j].place = parentRes2[i].children[j].locationName
        delete 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副本,仅更新当前父元素的children
            const 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}>
                  <DndContext
                    modifiers={[restrictToVerticalAxis]}
                    onDragEnd={onDragEnd}
                  >
                    <SortableContext
                      items={SonData.map(i => i?.key)}
                      strategy={verticalListSortingStrategy}
                    >
                      <Table
                        rowKey='key'
                        columns={updatedColumns} // 确保此处的columns是根据item动态调整后的
                        dataSource={item.children} // 此处确保使用当前item的children作为dataSource
                        pagination={false}
                        components={{
                          body: {
                            row: Row1
                          }
                        }}
                        isSelectAll={isSelectAll}
                        rowSelection={{
                          ...rowSelection2,
                          selectedRowKeys: selectedRowKeys2,
                          checkStrictly
                        }}
                      />
                    </SortableContext>
                  </DndContext>
                </div>
              )
            })}
相关推荐
Heo几秒前
深入 React19 Diff 算法
前端·javascript·面试
滕青山1 分钟前
个人所得税计算器 在线工具核心JS实现
前端·javascript·vue.js
小怪点点2 分钟前
手写promise
前端·promise
国思RDIF框架11 分钟前
RDIFramework.NET Web 敏捷开发框架 V6.3 发布 (.NET8+、Framework 双引擎)
前端
Mintopia12 分钟前
如何在有限的时间里,活出几倍的人生
前端
炫饭第一名13 分钟前
速通Canvas指北🦮——变形、渐变与阴影篇
前端·javascript·程序员
Neptune114 分钟前
让我带你迅速吃透React组件通信:从入门到精通(上篇)
前端·javascript
阿懂在掘金14 分钟前
Vue 表单避坑(一):为什么 v-model 绑定对象属性会偷偷修改父组件数据?
前端·vue.js
小码哥_常19 分钟前
Android与JS交互:解锁混合开发的魔法之门
前端
leafyyuki23 分钟前
如何优雅地上传大文件?分片上传实战指南
前端·音视频开发