vue elementUi+sortable.js嵌套表格拖拽
首先说下项目需求,一个多层嵌套的表格,行可以进行拖拽排序,但不能跨主级去拖拽,下拉可以异步获取数据,考虑了很久,还是用最熟悉的vue+element来做,但是element没有拖拽排序的功能,所以在此基础上加入sortable.js去实现拖拽功能。
后台返回的排序规则是 每个数据都带有3个属性 id next prev
用这3个属性实时的去更新排序
- id表示这一条数据的id
- next表示下一条数据的id
- prev表示上一条数据的id
html部分
data部分
flattArray:[], originalData:[], tableData: [], arr:[], maps:new Map()
我这里定义了四个数组和一个map
flattArray是平铺的数据 originalData数组也是平铺的数据(以防数据出错,没有啥实际用途) tableData是表格渲染的数据 arr是点击下拉异步请求的数据
maps是tableData改变后去重新渲染数据用的
methods部分
首先我定义了一个方法去深拷贝,这样改变一个数据的时候,其他数据不会改变
//深拷贝 getNewObject(val) { let resObj = JSON.stringify(val); return JSON.parse(resObj); },
加载页面从接口 获取 tableData数据 并深拷贝 tableData
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赋值,利用递归,把数据平铺、组装。
//点击下拉图标异步加载数据 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都不为空
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 里面去加载 改变行的方法
mounted() { this.rowDrop() },
最后再去动态的更新数据
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俱乐部。