IT俱乐部 JavaScript vue elementUi+sortable.js实现嵌套表格拖拽问题

vue elementUi+sortable.js实现嵌套表格拖拽问题

vue elementUi+sortable.js嵌套表格拖拽

首先说下项目需求,一个多层嵌套的表格,行可以进行拖拽排序,但不能跨主级去拖拽,下拉可以异步获取数据,考虑了很久,还是用最熟悉的vue+element来做,但是element没有拖拽排序的功能,所以在此基础上加入sortable.js去实现拖拽功能。

后台返回的排序规则是 每个数据都带有3个属性 id next prev 

用这3个属性实时的去更新排序

  • id表示这一条数据的id
  • next表示下一条数据的id
  • prev表示上一条数据的id

html部分

data部分

1
2
3
4
5
flattArray:[],
originalData:[],
tableData: [],
arr:[],
maps:new Map()

我这里定义了四个数组和一个map

flattArray是平铺的数据 originalData数组也是平铺的数据(以防数据出错,没有啥实际用途) tableData是表格渲染的数据 arr是点击下拉异步请求的数据

maps是tableData改变后去重新渲染数据用的

methods部分

首先我定义了一个方法去深拷贝,这样改变一个数据的时候,其他数据不会改变

1
2
3
4
5
//深拷贝
getNewObject(val) {
    let resObj = JSON.stringify(val);
    return JSON.parse(resObj);
},

加载页面从接口 获取 tableData数据 并深拷贝 tableData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
getDetails(id){
    axios.get(url).then(res =>{
        if(res.data.code === 0){
           this.tableData = res.data.data
           this.tableData.map( item =>{
             item.hasChildren = true
             delete item.children
           })
           this.flattArray = this.getNewObject(this.tableData)
           this.originalData = this.getNewObject(this.tableData)
         }else{
             this.$message.error('网络错误');
        }
   }).catch(function (error) {
      this.$message.error('网络错误');
   });
},

这里表格就已经渲染出来了 ,因为下拉的数据要异步去获取,所以在element提供的下拉事件里面去请求数据,这里去获取子级的数据,并给maps赋值,利用递归,把数据平铺、组装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//点击下拉图标异步加载数据
load(tree, treeNode, resolve) {
            this.maps.set(tree.id, { tree, treeNode, resolve })
            axios.get(`url` + tree.id).then(res => {
                if (res.data.code == 0) {
                    this.arr = res.data.data
                    this.arr.map(item => {
                        item.hasChildren = true
                        delete item.children
                        this.flattArray.push(item)
                    })
                    resolve(this.arr)
                    const tree = buildTree(this.flattArray, 1);
                    //组装tree
                    function buildTree(nodes, parent) {
                        const res = [];
                        for (let item of nodes) {
                            if (item.parentId === parent) {
                                const children = buildTree(nodes, item.id);
                                if (children.length) {
                                    item.children = children;
                                }
                                res.push(item);
                            }
                        }
                        return res;
                    }
                    //平铺tree
                    let result = [];
  
                    function flatTree(nodes, parentId) {
                        if (!nodes || nodes.length === 0) return [];
                        nodes.forEach(node => {
                            result.push(node);
                            return flatTree(node.children, node.id);
                        });
                    }
                    flatTree(tree, 1);
  
                    this.originalData = result
                    this.getNewObject(this.originalData)
                } else {
                    this.$message.error('没有更多消息了');
                }
            })
        },

定义的行拖拽方法,因为排序是用的3个属性去排序的,所以有3种情况去考虑

  • 一是拖动到最上级,这样prev的值就为空
  • 二是拖动到最下级,next为空
  • 三是正常的拖动,next prev都不为空
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
rowDrop() {
           this.$nextTick(() => {
               const tbody = document.querySelector('.el-table__body-wrapper tbody')
               const _this = this
               Sortable.create(tbody, {
                   animation: 300,
                   sort: true,
                   onEnd({
                             oldIndex,
                             newIndex,
                             item
                         }) {
                       const sourceObj = _this.originalData[oldIndex - 1] // 原本的位置
                       const targetObj = _this.originalData[newIndex - 1] // 移动到的位置
                       const frontObj = _this.originalData[newIndex - 2] //移动后位置的上一级
                       const behindObj = _this.originalData[newIndex] //移动后位置的下一级
                       if(sourceObj.parentId != targetObj.parentId) {
                           _this.$message.error("不支持跨级拖动,请拖回原来位置")
                           location.reload();
                           return;
                       }
                       let data = [];
                       if( oldIndex  newIndex ){//向上移动
                           //上一级
                           let predata = null
                           if (frontObj != undefined && sourceObj.parentId == frontObj.parentId) {
                               predata = {
                                   id: frontObj.id,
                                   next: sourceObj.id,
                                   prev: frontObj.prev
                               }
                           }
                           //自己
                           let curdata = {
                               id: sourceObj.id,
                               next: targetObj.id,
                               prev: targetObj.prev,
                               groupId: sourceObj.groupId
                           }
                           //下一级
                           let nextdata = {
                               id: targetObj.id,
                               next: targetObj.next,
                               prev: sourceObj.id
                           }
 
                           if(predata){
                               data.push(curdata, predata, nextdata)
                           }else{
                               data.push(curdata, nextdata)
                           }
                           _this.postdata(data, sourceObj.parentId)
                       }
                   }
               })
           })
       },

mounted 里面去加载 改变行的方法

1
2
3
mounted() {
         this.rowDrop()
},

最后再去动态的更新数据

1
2
3
4
5
6
7
8
refreshLoadTree(parentId) {
            // 根据父级id取出对应节点数据
            const {tree, treeNode, resolve} = this.maps.get(parentId)
            this.$set(this.$refs.tableData.store.states.lazyTreeNodeMap, parentId, [])
            if (tree) {
                this.load(tree, treeNode, resolve)
            }
        },

到这里需求已经实现了,就没有去深入的挖掘,可能会有问题,但是隔的时间太久了,基本忘光了。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持IT俱乐部。

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/navsub/js/11682.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部