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>
              )
            })}
相关推荐
你脑门上的脚印5 分钟前
Vue 项目从零实现语音转文字、文字转语音功能(完整可用 + 踩坑指南)
前端·vue.js
paopaokaka_luck26 分钟前
基于springboot3+vue3的车间生产管理系统(Echarts图形化分析、BI报表)
java·前端·spring boot·学习·echarts
程序员鱼皮32 分钟前
3 大 DeepSeek Harness 进阶玩法,招多个大肥鱼帮我干活!
前端·后端·ai编程
KoPa35 分钟前
HeySmart:大模型开源网关基座-请求生命周期与钩子引擎
前端·后端
七牛开发者1 小时前
Coding Agent 如何跑稳长任务?从上下文管理到运行时状态
前端·javascript·后端
半个落月1 小时前
从 CSR 到 Server Component:吃透 Next.js 16 App Router 路由、布局与 SEO
前端·next.js
七牛开发者1 小时前
拆解 DeepSeek Harness:Profile 与 Bundle 如何装配运行时
前端·javascript·后端
葡萄城技术团队1 小时前
表格智能体系列 · 1:一句话怎么变成一次表格操作
前端
梦曦i1 小时前
Vue-Router 2.4.0 新增可控重定向功能
前端·uni-app
deli0070072 小时前
Markdown 实时预览器:左边写右边看,排版效率翻倍
前端·ai编程