ElementUI - elTable 实现树形结构拖拽功能

问题描述:当一个表格,有很多层嵌套数据时,想实现拖拽改变顺序

使用方法:sortablejs

第一步:安装sortablejs

npm install sortablejs --save

第二步:在需要的地方引入

import Sortable from 'sortablejs'

第三步:准备需要的表格

<template>
  <div>
    <el-table ref="dragTable" row-key="id" :data="treeTableData" :expand-row-keys="treeTableExpandKeys" border @expand-change="treeTableExpandChange">
      <el-table-column prop="name" label="名字"></el-table-column>
      <el-table-column prop="age" label="年龄"></el-table-column>
      <el-table-column prop="desc" label="描述"></el-table-column>
    </el-table>
</div>
</template>

第四步:data定义数据

 data() {
    return {
      treeTableData: [
        {
          id: '1',
          name: '张三',
          age: 11,
          desc: '法外狂徒',
          children: [
            {
              id: '2',
              name: '月尊大人',
              age: 15,
              desc: '小兰花',
              children: [
                {
                  id: '6',
                  name: '仓盐海',
                  age: 15,
                  desc: '云梦泽'
                }
              ]
            }
          ]
        },
        {
          id: '3',
          name: '凌不疑',
          age: 18,
          desc: '三三',
          children: [
            {
              id: '4',
              name: '四四',
              age: 25,
              desc: '五五'
            },
            {
              id: '5',
              name: '不知道叫什么了',
              age: 26,
              desc: '哈哈哈'
            }
          ]
        }
      ],
      flatTreeData: [],
      treeTableExpandKeys: [],
      sortableObj: null
	}
}

初始化

注意 如果table数据是后端返回则mounted里面这三个方法应在数据获取完之后调用

mounted(){
   this.transverseNode(this.treeTableData, 1, (node, level, parentNode) => {
      node.level = level
      node.parentId = parentNode ? parentNode.id : -1
    })
   this.initSortable()
   this.getFlatNode(this.treeTableData, this.flatTreeData)
}

方法

​
beforeDestroy() {
    if (this.sortableObj) {
      this.sortableObj.destroy()
    }
  },
 methods: {
    initSortable() {
      // 获取容器元素
      const el = this.$refs.dragTable.$el.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
 
      if (!el) return
 
      this.sortableObj = new Sortable(el, {
        group: 'dragName',
        draggable: '.el-table__row',
        onEnd: evt => {
          const { newIndex, oldIndex } = evt
          const dragRow = this.flatTreeData[oldIndex]
          const relatedRow = this.flatTreeData[newIndex]
          if (dragRow.parentId !== relatedRow.parentId) {
            this.$message.warning('只能同层级内排序')
            this.reRender(null, 'treeTableData')
          } else {
            // 都无children
            if (!dragRow.children && !relatedRow.children) {
              const oldData = this.flatTreeData.splice(oldIndex, 1)[0]
              this.flatTreeData.splice(newIndex, 0, oldData)
            }
 
            // drag有, relate无
            if (dragRow.children && !relatedRow.children) {
              const oldData = this.flatTreeData.splice(oldIndex, 1)[0]
              this.flatTreeData.splice(newIndex, 0, oldData)
 
              if (newIndex < oldIndex) {
                // 有子元素的,子元素需要同样跟上来
                const flatChildren = []
 
                this.getFlatNode(oldData.children || [], flatChildren)
                if (flatChildren.length > 0) {
                  for (let i = 1, len = flatChildren.length; i <= len; i++) {
                    const childData = this.flatTreeData.splice(oldIndex + i, 1)[0]
                    this.flatTreeData.splice(newIndex + i, 0, childData)
                  }
                }
              } else {
                // 有子元素的,子元素需要同样跟下来
                const flatChildren = []
 
                this.getFlatNode(oldData.children || [], flatChildren)
                if (flatChildren.length > 0) {
                  for (let i = 1, len = flatChildren.length; i <= len; i++) {
                    const childData = this.flatTreeData.splice(oldIndex, 1)[0]
                    this.flatTreeData.splice(newIndex, 0, childData)
                  }
                }
              }
            }
 
            // drag无, relate有
            if (!dragRow.children && relatedRow.children) {
              const oldData = this.flatTreeData.splice(oldIndex, 1)[0]
              this.flatTreeData.splice(newIndex, 0, oldData)
 
              if (newIndex > oldIndex) {
                // 有子元素的,子元素需要同样跟上来
                const flatChildren = []
 
                this.getFlatNode(relatedRow.children || [], flatChildren)
                if (flatChildren.length > 0) {
                  for (let i = 1, len = flatChildren.length; i <= len; i++) {
                    const childData = this.flatTreeData.splice(newIndex + i, 1)[0]
                    this.flatTreeData.splice(newIndex + i - 1, 0, childData)
                  }
                }
              }
            }
 
            // drag有, relate有
            if (dragRow.children && relatedRow.children) {
              if (newIndex < oldIndex) {
                const oldData = this.flatTreeData.splice(oldIndex, 1)[0]
                this.flatTreeData.splice(newIndex, 0, oldData)
 
                // 有子元素的,子元素需要同样跟上来
                const flatChildren = []
 
                this.getFlatNode(oldData.children || [], flatChildren)
                if (flatChildren.length > 0) {
                  for (let i = 1, len = flatChildren.length; i <= len; i++) {
                    const childData = this.flatTreeData.splice(oldIndex + i, 1)[0]
                    this.flatTreeData.splice(newIndex + i, 0, childData)
                  }
                }
              } else {
                const oldData = this.flatTreeData.splice(oldIndex, 1)[0]
 
                // relateRow的children数
                const relateFlatChildren = []
                this.getFlatNode(relatedRow.children || [], relateFlatChildren)
 
                this.flatTreeData.splice(newIndex + relateFlatChildren.length, 0, oldData)
 
                // 有子元素的,子元素需要同样跟下来
                const flatChildren = []
 
                this.getFlatNode(oldData.children || [], flatChildren)
                if (flatChildren.length > 0) {
                  for (let i = 1, len = flatChildren.length; i <= len; i++) {
                    const childData = this.flatTreeData.splice(oldIndex, 1)[0]
                    this.flatTreeData.splice(newIndex + relateFlatChildren.length, 0, childData)
                  }
                }
              }
            }
 
            // 重新生成树的数据
            const data = this.getTreeData(this.flatTreeData, [])
            console.log('this.flatTreeData', this.flatTreeData, data)
            // 页面重新渲染
            this.reRender(data, 'treeTableData')
          }
        }
      })
    },
    getFlatNode(nodes, flatList, childrenKey = 'children') {
      nodes.forEach(node => {
        flatList.push(node)
        if (node[childrenKey] && node[childrenKey].length) {
          this.getFlatNode(node[childrenKey], flatList)
        }
      })
    },
    getTreeData(nodes, resultList) {
      const childStack = []
      let lastNode = {}
      nodes.forEach(node => {
        delete node.children
 
        const pushNode = dataNode => {
          const parentNode = childStack[childStack.length - 1]
          if (parentNode) {
            parentNode.children.push(dataNode)
          } else {
            resultList.push(dataNode)
          }
        }
 
        if (node.level < lastNode.level) {
          childStack.length = node.level - 1
          pushNode(node)
        } else if (node.level === lastNode.level) {
          pushNode(node)
        } else if (node.level > lastNode.level) {
          if (!lastNode.children) {
            lastNode.children = []
          }
          childStack.push(lastNode)
          pushNode(node)
        } else {
          resultList.push(node)
        }
 
        lastNode = node
      })
 
      return resultList
    },
    reRender(data, dataKey) {
      if (data) {
        this[dataKey] = []
        this.$nextTick(() => {
          this[dataKey] = data
        })
      } else {
        const origin = [].concat(this[dataKey])
        this[dataKey] = []
        this.$nextTick(() => {
          this[dataKey] = origin
        })
      }
    },
    transverseNode(nodes, level = 1, cb, parentNode, childKey = 'children') {
      nodes.forEach(node => {
        if (node[childKey] && node[childKey].length > 0) {
          this.transverseNode(node[childKey], level + 1, cb, node, childKey)
        }
        cb(node, level, parentNode)
      })
      return nodes
    },
    treeTableExpandChange(row, expanded) {
      if (expanded) {
        this.treeTableExpandKeys.push(row.id)
      } else {
        const idx = this.treeTableExpandKeys.indexOf(row.id)
        this.treeTableExpandKeys.splice(idx, 1)
      }
    }
  }
​
### 使用 `table-draggers` 实现树形结构 为了实现带有拖拽功能树形表格,可以结合 Element Plus 的 `el-table` 组件以及第三方库 `vue-dndrop` 或者其他类似的拖拽插件。下面是一个具体的实现方法: #### 安装依赖项 首先安装所需的 npm 包: ```bash npm install vue-dndrop element-plus ``` #### HTML 结构与 Vue 组件定义 创建一个基本的 Vue 组件来展示可拖动的树形表。 ```html <template> <div class="tree-table"> <el-table :data="tableData" style="width: 100%" @row-click="handleRowClick" row-key="id" default-expand-all border lazy load="load"> <!-- 表格--> <el-table-column prop="name" label="名称"></el-table-column> <!-- 拖拽手柄 --> <el-table-column width="50"> <template slot-scope="{ row }"> <span v-if="!isDragging(row)" class="drag-handle">...</span> </template> </el-table-column> <!-- 子节点渲染 --> <el-table-column type="expand"> <template slot-scope="props"> <el-table :data="props.row.children || []" style="margin-left: 20px;"> <el-table-column prop="name" label="子节点名称"></el-table-column> </el-table> </template> </el-table-column> </el-table> <!-- 拖拽指示器 --> <transition name="fade"> <div v-show="showIndicator" ref="indicator" class="drop-indicator">{{ dropMessage }}</div> </transition> </div> </template> ``` #### JavaScript 部分 在脚本部分处理逻辑交互,包括初始化数据、监听拖拽事件等操作。 ```javascript <script setup lang="ts"> import { ref, onMounted } from 'vue'; import { ElTable, ElTableColumn } from 'element-plus'; const tableData = ref([ { id: 1, name: "根节点", hasChildren: true, children: [ { id: 2, name: "子节点A", parentId: 1 }, { id: 3, name: "子节点B", parentId: 1 } ] } ]); // 处理行点击事件 function handleRowClick(row) {} // 判断当前行是否处于被拖拽状态 function isDragging(row) { return draggedId.value === row.id; } let showIndicator = ref(false); let dropMessage = ref(''); let draggedId = ref(null); onMounted(() => { // 初始化拖拽行为... }); </script> ``` 此代码片段展示了如何构建基础框架并引入必要的组件和样式[^1]。需要注意的是,在实际应用中还需要进一步完善细节,比如添加真实的 API 请求获取异步加载的数据源,优化用户体验等方面的工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值