前两天和产品哥哥说好的列表拖拽排序,突然就变成了树列表拖拽排序,还要限制不能跨父级拖拽,没法子,头发抓掉一把之后写出来了,不难,就是数学不太好,算了老半天
从引入sortable.js开始讲起吧,从0到1 (纯手打,可能会有字母写错,大家擦亮眼睛)
<template>
<div class="attribute-manage padding-lg full-h over-hide">
<VTable
ref="vTable"
:key="tableKey"
:data="treeData"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
max-height="calc(100vh - 260px)"
row-key="id"
@search="onSearch"
>
<el-table-column prop="categoryName" label="分类名称" min-width="100px" show-overflow-tooltip />
<el-table-column prop="level" label="级别" min-width="40px" show-overflow-tooltip />
<el-table-column label="是否显示" min-width="50px">
<template slot-scope="{ row }">
<span>{{ row.isSpecialColumn === '1' ? '是' : '否' }}</span>
</template>
</el-table-column>
<el-table-column prop="leaderName" label="品类负责人" min-width="50px" />
</VTable>
</div>
</template>
注: VTable只是对el-table组件做的二次封装,统一样式而已,与el-table无太大差异
<script>
import Sortable from 'sortablejs'
export default {
data () {
return {
treeData: [], // 展示数据
page: 1,
limit: 15,
sortable: null, // sortable对象
activeRows: new Array(0), // 排序时用来跟后端进行数据交互
tableKey: '' // 这个蛮重要的,让dom刷新
}
},
created () {
this.initData()
},
methods: {
async initData () {
if (this.sortable && this.sortable.el) {
this.sortable.destroy()
}
const { data } = await itemCategory.getAllList({ source: 1 })
// 这里因为后端给了平级数据,所以要把它格式化成树结构
this.$set(this, 'treeData', this.$utils.formatTree(data, { nodeKey: 'id', parentKey: 'parentId' }))
// 这里是把树结构的数据再按照从上到下的顺序转化成平级数据,因为排序接口,后端要求是发送从上到下的平铺的id集合
this.$set(this, 'activeRows', this.$utils.treeToTile(this.treeData))
// 这里是给tableKey一个随机数,Math.random也可以的,只要跟上一次的值不一样,能让dom刷新就可以
this.tableKey = new Date().getTime()
this.$nextTick(() => {
this.setSort()
})
},
setSort () {
const el = document.querySelectorAll('table.el-table__body > tbody')[0]
if (!el) {
return
}
let that = this
this.sortable = Sortable.create(el, {
ghostClass: 'sortable-ghost',
setData: function (dataTransfer) {
dataTransfer.setData('Text', '')
},
// 拖拽移动的时候
onMove: function ({ dragged, related }) {
// 对了,树数据中不管是哪一层,都一定要有level字段表示当前是第几层哦
/*
evt.dragged; // 被拖拽的对象
evt.related; // 被替换的对象
*/
const oldRow = that.activeRows[dragged.rowIndex]
const newRow = that.activeRows[related.rowIndex]
if (oldRow.level !== newRow.level || oldRow.parentId !== newRow.parentId) {
return false
}
},
onEnd: async ({ oldIndex, newIndex }) => {
const oldRow = that.activeRows[oldIndex]
const newRow = that.activeRows[newIndex]
if (oldIndex !== newIndex && oldRow.level === newRow.level && oldRow.parentId === newRow.parentId) {
const oldRow = that.activeRows[oldIndex]
const newRow = that.activeRows[newIndex]
let oldRowSuffixData = that.activeRows.slice(oldIndex)
let newRowSuffixData = that.activeRows.slice(newIndex)
oldRowSuffixData = oldRowSuffixData.filter((d, i) => i < that.getLeastIndex(oldRowSuffixData.findIndex((_d, _i) => _d.level === oldRow.level && _i !== 0)))
newRowSuffixData = newRowSuffixData.filter((d, i) => i < that.getLeastIndex(newRowSuffixData.findIndex((_d, _i) => _d.level === newRow.level && _i !== 0)))
const targetRows = that.activeRows.splice(oldIndex, oldRowSuffixData.length)
if (oldIndex > newIndex) {
that.activeRows.splice(newIndex, 0, ...targetRows)
} else if (oldIndex < newIndex) {
that.activeRows.splice(newIndex + newRowSuffixData.length - oldRowSuffixData.length, 0, ...targetRows)
}
await itemCategory.sort(that.activeRows.map(d => d.id))
that.initData()
}
},
onSort: function (/** Event */evt) {
console.log('触发!')
}
})
console.log()
},
getLeastIndex (index) {
return index >= 1 ? index : 1
}
}
}
</script>
好人做到底吧!
// 将树数据转化为平铺数据
export function treeToTile (treeData, childKey = 'children') {
let arr = []
const expanded = data => {
if (data && data.length > 0) {
data.filter(d => d).forEach(e => {
arr.push(e)
expanded(e[childKey] || [])
})
}
}
expanded(treeData)
return arr
}