令人头秃的树形表格自由拖拽实现(vue+ant+Sortable)
最近项目提出目标,须实现树状表格自由拖拽,并可支持antdesign(elementui)一类ui框架表格部分功能,紧张的项目周期让人心情久久难以平复,这是连百度的回答都难以让我释怀的痛。没法自己撸呗,代码核心难点在于拖拽后需同步数据结构及同步表格视图。献祭无数根头发后,终成目标。实现效果如下(额外功能可根据antdesign配置属性自行添加):代码如下 :<template><
·
最近项目提出目标,须实现树状表格自由拖拽,并可支持antdesign(elementui)一类ui框架表格部分功能,紧张的项目周期让人心情久久难以平复,这是连百度的回答都难以让我释怀的痛。没法自己撸呗,代码核心难点在于拖拽后需同步数据结构及同步表格视图。献祭无数根头发后,终成目标。实现效果如下(额外功能可根据antdesign配置属性自行添加):
代码如下 :
<template>
<div>
<div class="func-bed">
<a-button-group>
<a-button @click="upLevel">升级</a-button>
<a-button @click="downLevel" type="primary">降级</a-button>
</a-button-group>
<a-button-group>
<a-button>上移</a-button>
<a-button type="primary">下移</a-button>
</a-button-group>
</div>
<a-table
v-if="tableShow"
:columns="columns"
:data-source="dataList"
:pagination="false"
:rowSelection="rowConfig"
@expandedRowsChange="expandedTable"
:expandedRowKeys="expandKeysList"
rowKey="id"
>
</a-table>
</div>
</template>
<script>
import Sortable from 'sortablejs'
export default {
name: 'commonTable',
data () {
return {
tableShow: true,
columns: [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
scopedSlots: { customRender: 'name' }
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '12%',
scopedSlots: { customRender: 'age' }
},
{
title: 'Address',
dataIndex: 'address',
width: '30%',
key: 'address',
scopedSlots: { customRender: 'address' }
}
],
dataList: [
{
key: 1313123,
id: 1,
name: '1',
age: 60,
address: 'New York No. 1 Lake Park',
children: [
{
key: 11,
name: '2',
age: 42,
address: 'New York No. 2 Lake Park',
id: '11'
},
{
key: 12,
name: '3',
age: 30,
address: 'New York No. 3 Lake Park',
id: '12',
children: [
{
key: 121,
id: '121',
name: '4',
age: 16,
address: 'New York No. 3 Lake Park',
}
],
},
{
key: 13,
id: '13',
name: '5',
age: 72,
address: 'London No. 1 Lake Park',
children: [
{
key: 131,
id: '131',
name: '6',
age: 42,
address: 'London No. 2 Lake Park',
children: [
{
id: '1311',
key: 1311,
name: '7',
age: 25,
address: 'London No. 3 Lake Park',
},
{
id: '1312',
key: 1312,
name: '8',
age: 18,
address: 'London No. 4 Lake Park',
},
],
},
],
},
],
},
{
key: 2,
id: '2',
name: '9',
age: 32,
address: 'Sidney No. 1 Lake Park',
}
],
selectedRowKeys: [],
expandKeysList: []
}
},
computed: {
rowConfig () {
return {
selectedRowKeys: this.selectedRowKeys,
onChange: this.tableChange
}
}
},
methods: {
upLevel () {
this.selectedRowKeys.forEach(item => {
let levelArr = this.getTargeIndex(this.dataList, item.toString())
let levelItem = {}
let uplevelItem = {}
let uplevelIndex = null
if (levelArr.length > 1) {
levelArr.forEach((item, index) => {
item = parseInt(item)
if (index < levelArr.length - 1) {
levelItem = index > 0 ? levelItem.children[item] : this.dataList[item]
if (levelArr.length - 3 >= 0) {
if (levelArr.length - 3 >= index) {
uplevelItem = index > 0 ? uplevelItem.children[item] : this.dataList[item]
}
if (levelArr.length - 2 === index) {
uplevelIndex = item
}
} else {
uplevelItem = this.dataList
uplevelIndex = item
}
} else {
let calcItem = JSON.stringify(levelItem.children[item])
levelItem.children.splice(item, 1)
if (!levelItem.children.length) {
delete levelItem.children
}
levelItem = calcItem
}
})
if (uplevelItem.children) {
uplevelItem.children.splice(uplevelIndex + 1, 0, JSON.parse(levelItem))
} else {
uplevelItem.splice(uplevelIndex + 1, 0, JSON.parse(levelItem))
}
}
})
this.dataList = JSON.parse(JSON.stringify(this.dataList))
},
downLevel () {
this.selectedRowKeys.forEach(item => {
let levelArr = this.getTargeIndex(this.dataList, item.toString())
let levelItem = {}
if (levelArr.length > 0) {
levelArr.forEach((item, index) => {
item = parseInt(item)
if (index < levelArr.length - 1) {
levelItem = index > 0 ? levelItem.children[item] : this.dataList[item]
} else {
if (!index) {
let calcItem = JSON.parse(JSON.stringify(this.dataList[item]))
if (this.dataList[item - 1]) {
if (this.dataList[item - 1].children) {
this.dataList[item - 1].children.push(calcItem)
} else {
this.dataList[item - 1].children = [calcItem]
}
this.dataList.splice(item, 1)
} else if (this.dataList[item + 1]) {
if (this.dataList[item + 1].children) {
this.dataList[item + 1].children.push(calcItem)
} else {
this.dataList[item + 1].children = [calcItem]
}
this.dataList.splice(item, 1)
}
} else {
let calcItem = JSON.parse(JSON.stringify(levelItem.children[item]))
if (levelItem.children.length > 1) {
if (levelItem.children[item - 1]) {
if (levelItem.children[item - 1].children) {
levelItem.children[item - 1].children.push(calcItem)
} else {
levelItem.children[item - 1].children = [calcItem]
}
levelItem.children.splice(item, 1)
} else if (levelItem.children[item + 1]) {
if (levelItem.children[item + 1].children) {
levelItem.children[item + 1].children.push(calcItem)
} else {
levelItem.children[item + 1].children = [calcItem]
}
levelItem.children.splice(item, 1)
}
}
}
}
})
}
})
this.dataList = JSON.parse(JSON.stringify(this.dataList))
},
tableChange (key) {
this.selectedRowKeys = key
},
expandedTable (keys) {
if (this.tableShow) {
this.expandKeysList = keys
}
this.refreshTable()
},
inintDrag () {
let sortDom = document.querySelector('.ant-table-tbody')
let _this = this
Sortable.create(sortDom, {
onStart ({ item }) {
let targetRowKey = item.dataset.rowKey
if (targetRowKey) {
_this.expandKeysList = _this.expandKeysList.filter(item => item.toString() !== targetRowKey.toString())
}
},
onEnd ({ newIndex, item }) {
let targetRowKey = item.dataset.rowKey
let targetArray = []
let dataListItem = {}
let targetItem = {}
let currentRowKey = ''
let currentArray = []
let isNext = false
let notBelong = false
if (targetRowKey) {
targetArray = _this.getTargeIndex(_this.dataList, targetRowKey.toString())
}
if (newIndex > 0) {
currentRowKey = document.querySelectorAll('.ant-table-row')[newIndex - 1].dataset.rowKey
if (document.querySelectorAll('.ant-table-row')[newIndex + 1]) {
currentRowKey = document.querySelectorAll('.ant-table-row')[newIndex + 1].dataset.rowKey
isNext = true
}
}
targetArray.forEach((item, index) => {
item = parseInt(item)
if (index < targetArray.length - 1) {
if (!index) {
targetItem = _this.dataList[item]
} else {
targetItem = targetItem.children[item]
}
} else {
if (!index) {
targetItem = JSON.parse(JSON.stringify(_this.dataList[item]))
if (targetItem.children) {
notBelong = _this.judgeBelong(currentRowKey, targetItem.children)
}
if (!notBelong) {
_this.dataList.splice(item, 1)
}
} else {
let calcItem = JSON.parse(JSON.stringify(targetItem.children[item]))
if (calcItem.children) {
notBelong = _this.judgeBelong(currentRowKey, calcItem.children)
}
if (!notBelong) {
targetItem.children.splice(item, 1)
if (!targetItem.children.length) {
delete targetItem.children
}
}
targetItem = calcItem
}
}
})
if (!notBelong) {
if (newIndex > 0) {
if (currentRowKey) {
currentArray = _this.getTargeIndex(_this.dataList, currentRowKey.toString())
}
currentArray.forEach((item, index) => {
item = parseInt(item)
if (index < currentArray.length - 1) {
if (!index) {
dataListItem = _this.dataList[item]
} else {
dataListItem = dataListItem.children[item]
}
} else {
if (!index) {
if (!isNext) {
_this.dataList.splice(item + 1, 0 , targetItem)
} else {
_this.dataList.splice(item, 0 , targetItem)
}
} else {
if (!isNext) {
dataListItem.children.splice(item + 1, 0, targetItem)
} else {
dataListItem.children.splice(item, 0, targetItem)
}
}
}
})
} else {
_this.dataList.unshift(targetItem)
}
}
_this.dataList = JSON.parse(JSON.stringify(_this.dataList))
_this.refreshTable()
}
})
},
getTargeIndex (list, id, arr = []) {
return list.reduce((total, item, index) => {
if (item.id.toString() === id) {
return [...total, index]
} else {
if (item.children && item.children.length) {
let childArr = this.getTargeIndex(item.children, id, [...arr, index])
if (childArr.length === [...arr, index].length) {
childArr = total
}
return childArr
} else {
return total
}
}
}, arr)
},
judgeBelong (key, list = []) {
return list.find(item => {
if (item.id.toString() === key) {
return true
} else {
if (item.children && item.children.length) {
return this.judgeBelong(key, item.children)
} else {
return false
}
}
})
},
refreshTable () {
this.tableShow = false
this.$nextTick(() => {
this.tableShow = true
this.$nextTick(() => {
this.inintDrag()
})
})
}
},
mounted () {
this.inintDrag()
}
}
</script>
<style lang="less" scoped>
.func-bed{
padding-bottom: 20px;
.ant-btn-group{
margin-left: 10px;
}
}
</style>
经修改后修复已知bug,并加入树形升级与降级功能,位置移动什么的就soeasy啦。
更多推荐
已为社区贡献2条内容
所有评论(0)