vue elementUi+sortable.js實(shí)現(xiàn)嵌套表格拖拽問(wèn)題
vue elementUi+sortable.js嵌套表格拖拽
首先說(shuō)下項(xiàng)目需求,一個(gè)多層嵌套的表格,行可以進(jìn)行拖拽排序,但不能跨主級(jí)去拖拽,下拉可以異步獲取數(shù)據(jù),考慮了很久,還是用最熟悉的vue+element來(lái)做,但是element沒(méi)有拖拽排序的功能,所以在此基礎(chǔ)上加入sortable.js去實(shí)現(xiàn)拖拽功能。
后臺(tái)返回的排序規(guī)則是 每個(gè)數(shù)據(jù)都帶有3個(gè)屬性 id next prev
用這3個(gè)屬性實(shí)時(shí)的去更新排序
- id表示這一條數(shù)據(jù)的id
- next表示下一條數(shù)據(jù)的id
- prev表示上一條數(shù)據(jù)的id
html部分

data部分
flattArray:[], originalData:[], tableData: [], arr:[], maps:new Map()
我這里定義了四個(gè)數(shù)組和一個(gè)map
flattArray是平鋪的數(shù)據(jù) originalData數(shù)組也是平鋪的數(shù)據(jù)(以防數(shù)據(jù)出錯(cuò),沒(méi)有啥實(shí)際用途) tableData是表格渲染的數(shù)據(jù) arr是點(diǎn)擊下拉異步請(qǐng)求的數(shù)據(jù)
maps是tableData改變后去重新渲染數(shù)據(jù)用的
methods部分
首先我定義了一個(gè)方法去深拷貝,這樣改變一個(gè)數(shù)據(jù)的時(shí)候,其他數(shù)據(jù)不會(huì)改變
//深拷貝
getNewObject(val) {
let resObj = JSON.stringify(val);
return JSON.parse(resObj);
},加載頁(yè)面從接口 獲取 tableData數(shù)據(jù) 并深拷貝 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('網(wǎng)絡(luò)錯(cuò)誤');
}
}).catch(function (error) {
this.$message.error('網(wǎng)絡(luò)錯(cuò)誤');
});
},這里表格就已經(jīng)渲染出來(lái)了 ,因?yàn)橄吕臄?shù)據(jù)要異步去獲取,所以在element提供的下拉事件里面去請(qǐng)求數(shù)據(jù),這里去獲取子級(jí)的數(shù)據(jù),并給maps賦值,利用遞歸,把數(shù)據(jù)平鋪、組裝。
//點(diǎn)擊下拉圖標(biāo)異步加載數(shù)據(jù)
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('沒(méi)有更多消息了');
}
})
},定義的行拖拽方法,因?yàn)榕判蚴怯玫?個(gè)屬性去排序的,所以有3種情況去考慮
- 一是拖動(dòng)到最上級(jí),這樣prev的值就為空
- 二是拖動(dòng)到最下級(jí),next為空
- 三是正常的拖動(dòng),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] // 移動(dòng)到的位置
const frontObj = _this.originalData[newIndex - 2] //移動(dòng)后位置的上一級(jí)
const behindObj = _this.originalData[newIndex] //移動(dòng)后位置的下一級(jí)
if(sourceObj.parentId != targetObj.parentId) {
_this.$message.error("不支持跨級(jí)拖動(dòng),請(qǐng)拖回原來(lái)位置")
location.reload();
return;
}
let data = [];
if( oldIndex < newIndex ){//向下移動(dòng)
//上一級(jí)
let predata = {
id: targetObj.id,
next: sourceObj.id,
prev: targetObj.prev
}
//自己
let curdata = {
id: sourceObj.id,
next: targetObj.next,
prev: targetObj.id,
groupId: sourceObj.groupId
}
//下一級(jí)
let nextdata = null
if (behindObj != undefined && sourceObj.parentId == behindObj.parentId) {
nextdata = {
id: behindObj.id,
next: behindObj.next,
prev: sourceObj.id
}
}
if(nextdata){
data.push(curdata, predata, nextdata)
}else{
data.push(curdata, predata)
}
_this.postdata(data, sourceObj.parentId)
}else if( oldIndex > newIndex ){//向上移動(dòng)
//上一級(jí)
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
}
//下一級(jí)
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()
},最后再去動(dòng)態(tài)的更新數(shù)據(jù)
refreshLoadTree(parentId) {
// 根據(jù)父級(jí)id取出對(duì)應(yīng)節(jié)點(diǎn)數(shù)據(jù)
const {tree, treeNode, resolve} = this.maps.get(parentId)
this.$set(this.$refs.tableData.store.states.lazyTreeNodeMap, parentId, [])
if (tree) {
this.load(tree, treeNode, resolve)
}
},到這里需求已經(jīng)實(shí)現(xiàn)了,就沒(méi)有去深入的挖掘,可能會(huì)有問(wèn)題,但是隔的時(shí)間太久了,基本忘光了。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Vue?+?ElementUI表格內(nèi)實(shí)現(xiàn)圖片點(diǎn)擊放大效果的兩種實(shí)現(xiàn)方式
- vue elementUI table表格自定義樣式滾動(dòng)效果
- Vue+ElementUI表格狀態(tài)區(qū)分,row-class-name屬性詳解
- vue+elementui 表格分頁(yè)限制最大頁(yè)碼數(shù)的操作代碼
- elementui實(shí)現(xiàn)表格(el-table)默認(rèn)選中功能
- Vue+ElementUI踩坑之動(dòng)態(tài)顯示/隱藏表格的列el-table-column問(wèn)題
- 使用elementUI的表格table給列添加樣式
- vue基于ElementUI動(dòng)態(tài)設(shè)置表格高度的3種方法
相關(guān)文章
vue項(xiàng)目中解決 IOS + H5 滑動(dòng)邊界橡皮筋彈性效果(解決思路)
最近遇到一個(gè)問(wèn)題,我們?cè)谄髽I(yè)微信中的 H5 項(xiàng)目中需要用到table表格(支持懶加載 上劃加載數(shù)據(jù)),但是他們?cè)阪i頭、鎖列的情況下,依舊會(huì)出現(xiàn)邊界橡皮筋效果,這篇文章主要介紹了vue項(xiàng)目中解決 IOS + H5 滑動(dòng)邊界橡皮筋彈性效果,需要的朋友可以參考下2023-02-02
mpvue跳轉(zhuǎn)頁(yè)面及注意事項(xiàng)
這篇文章主要介紹了mpvue跳轉(zhuǎn)頁(yè)面及注意事項(xiàng)的相關(guān)資料,需要的朋友可以參考下2018-08-08
前端H5微信支付寶支付實(shí)現(xiàn)方法(uniapp為例)
最近上線一個(gè)項(xiàng)目,手機(jī)網(wǎng)站進(jìn)行調(diào)起支付寶App支付,做起來(lái)還是滿順手的,在此做個(gè)記錄,這篇文章主要給大家介紹了關(guān)于前端H5微信支付寶支付實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2024-04-04
vue中的.capture和.self區(qū)分及初步理解
這篇文章主要介紹了vue中的.capture和.self區(qū)分及初步理解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
Vue使用pdf.js和docx-preview實(shí)現(xiàn)docx和pdf的在線預(yù)覽
這篇文章主要為大家詳細(xì)介紹了在Vue中使用pdf.js和docx-preview實(shí)現(xiàn)docx和pdf的在線預(yù)覽的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),需要的可以參考下2025-03-03

