以 Element 树表格为例,实现了树表格的同级之间排序(跨级也有代码,但还有问题)
踩坑点:
- onMove回调中 oldIndex 会随时变化,若想拿到最开始拖动的Row,需要使用onChoose 。
- 在onChoose需将 oldIndex 或者 oldRow 存储一下,供后续onMove和onEnd使用。
- onMove 中可以使用return false 阻止拖动
- 因为是树形结构,所以回调中返回的 Index 并不准确,所以要预先在onChoose 时将树数据扁平化,才能根据返回的Index获取到对应的Row。
- 拖动结束更改树表格源数据,虽然数据有更改,但视图可能会乱掉,所以要使用nexttick 更新数据
- 更新数据后默认树会缩合,所以要设置需要展开的节点
上代码
<el-table
ref="ruleSelectTableRef"
v-loading="isLoading"
row-key="ruleTagId"
:data="tableData"
border
:max-height="500"
>
<el-table-column label="" width="45"></el-table-column>
<el-table-column width="40" :render-header="selectAllHeaderRender">
<template slot-scope="scope">
<el-checkbox
v-model="scope.row.isSelected"
:disabled="scope.row.isDisabled"
@change="val => selectRow(val, scope.row)"
></el-checkbox>
</template>
</el-table-column>
<el-table-column label="设备类" prop="packageName"></el-table-column>
<el-table-column label="操作" prop="handles" width="80" fixed="right">
<template slot-scope="scope">
<el-button
v-if="scope.row.level !== 1"
type="text"
:class="{ 'text-btn-red': scope.row.compareOnline }"
@click="showLogModule(scope.row)"
>
查看日志
</el-button>
</template>
</el-table-column>
</el-table>
import Sortable from 'sortablejs';
import { deepClone } from '@/utils/util';
import { nextTick } from 'process';
private selectedTableData: IListRuleCollectionRuleSelectInfo[] = [];
private flatSelectedTableData: any[] = [];
private drawOldIndex: any = -1;
// 将树数据转化为平铺数据
flatTreeData(treeData: any[], childKey = 'children') {
const arr: any[] = [];
const expanded = (data: any) => {
if (data && data.length > 0) {
data
.filter((d: any) => d)
.forEach((e: any) => {
arr.push(e);
expanded(e[childKey] || []);
});
}
};
expanded(treeData);
return arr;
}
// 行拖拽
rowDrop() {
// 此时找到的元素是要拖拽元素的父容器
const tbody = document.querySelector('#selected-table .el-table__body-wrapper tbody');
const that = this;
Sortable.create(tbody, {
// 指定父元素下可被拖拽的子元素
draggable: '.el-table__row',
setData: function (dataTransfer: any) {
dataTransfer.setData('Text', '');
},
onChoose({ oldIndex }: any) {
// 把树形的结构转为列表再进行拖拽
that.$set(that, 'flatSelectedTableData', that.flatTreeData(that.selectedTableData));
that.drawOldIndex = oldIndex;
},
onMove({ related }: any) {
const oldRow: any = that.flatSelectedTableData[that.drawOldIndex];
const newRow: any = that.flatSelectedTableData[related.rowIndex];
// if (!newRow || !newRow.id || !oldRow || !oldRow.id) return false;
// 拖动的是一个包
if (oldRow.level === 1) {
if (newRow.level === 1 && oldRow.id === newRow.id) return false;
// 还在自己的包里面
if (oldRow.rulePackageId === newRow.newPackageId) return false;
} else {
// 不是包不可以挪到最上面
if (related.rowIndex === 0) return false;
// 跨包不允许
if (oldRow.newPackageId !== newRow.newPackageId || newRow.level === 1) return false;
}
},
onEnd({ newIndex, oldIndex }: any) {
if (oldIndex !== newIndex) {
const oldRow: any = that.flatSelectedTableData[oldIndex];
const newRow: any = that.flatSelectedTableData[newIndex];
const selectedTableData = deepClone(that.selectedTableData);
that.selectedTableData = [];
const expansionRowKey: any[] = [];
// 拖动的是一个包
if (oldRow.level === 1) {
expansionRowKey.push(oldRow.id);
// 找到拖动的这个包
const currPackageIndex = selectedTableData.findIndex((p: any) => p.id === oldRow.id);
// 找到新包的位置塞进去
// 新位置是一个包的位置 (包 -> 包)
if (newRow.level === 1) {
const newPackageIndex = selectedTableData.findIndex((p: any) => p.id === newRow.id);
const currPackage = selectedTableData.splice(currPackageIndex, 1)[0];
selectedTableData.splice(newPackageIndex, 0, currPackage);
expansionRowKey.push(newRow.id);
// 新位置是一个行的位置 (包 -> 行)
} else {
// 找到这个行的父级包
const newPackageIndex = selectedTableData.findIndex((p: any) => p.newPackageId === newRow.newPackageId);
// 删掉原来的包,放到新包的后面
const currPackage = selectedTableData.splice(currPackageIndex, 1)[0];
selectedTableData.splice(newPackageIndex, 0, currPackage);
expansionRowKey.push(selectedTableData[newPackageIndex].id);
}
// 拖动的是一个行
} else {
// 新目标是一个包 (行 -> 包)
if (newRow.level === 1) {
expansionRowKey.push(newRow.id);
// 如果还是原来的包(包内排序移到开头)
if (oldRow.newPackageId === newRow.newPackageId) {
// 找到当前包位置
const newPackageIndex = selectedTableData.findIndex((p: any) => p.id === newRow.id);
const currPackage = selectedTableData[newPackageIndex];
expansionRowKey.push(currPackage.id);
const currPackageChildren = currPackage?.children || [];
// 找到拖动的行在children 中的 index
const currRuleIndex = currPackageChildren.findIndex((rule: any) => rule.ruleTagId === oldRow.ruleTagId);
const currRule = currPackageChildren.splice(currRuleIndex, 1)[0];
// 放到最开头
currPackageChildren.splice(0, 0, currRule);
currPackage.children = currPackageChildren;
// 替换掉原来的包
selectedTableData.splice(newPackageIndex, 1, currPackage);
// 如果在另外一个包开头(废弃,目前不允许,还有问题)
} else {
return false;
// 找到旧包
// const oldPackageIndex = selectedTableData.findIndex((p: any) => p.newPackageId === oldRow.newPackageId);
// const oldPackage = selectedTableData[oldPackageIndex];
// expansionRowKey.push(oldPackage.id);
// const oldPackageChildren = oldPackage?.children || [];
// // 删掉旧包中的 old rule
// const oldRuleIndex = oldPackageChildren.findIndex((rule: any) => rule.ruleTagId === oldRow.ruleTagId);
// const oldRule = oldPackageChildren.splice(oldRuleIndex, 1)[0];
// // 找到新包
// const newPackageIndex = selectedTableData.findIndex((p: any) => p.id === newRow.id);
// const newPackage = selectedTableData[newPackageIndex];
// const newPackageChildren = newPackage?.children || [];
// // 新包中增加 该 rule
// newPackageChildren.splice(0, 0, oldRule);
// // 覆盖原包
// oldPackage.children = oldPackageChildren;
// newPackage.children = newPackageChildren;
// selectedTableData.splice(oldPackageIndex, 1, oldPackage);
// selectedTableData.splice(newPackageIndex, 1, newPackage);
}
// 新位置是一个行的位置 (行 -> 行)
} else {
// 还在原来的包内(包内排序)
if (oldRow.newPackageId === newRow.newPackageId) {
// 找到当前包位置
const newPackageIndex = selectedTableData.findIndex((p: any) => p.newPackageId === newRow.newPackageId);
const currPackage = selectedTableData[newPackageIndex];
expansionRowKey.push(currPackage.id);
const currPackageChildren = currPackage?.children || [];
// 找到拖动的行在children 中的 index
const oldRuleIndex = currPackageChildren.findIndex((rule: any) => rule.ruleTagId === oldRow.ruleTagId);
// 找到将要排序到的目标位置
const newRuleIndex = currPackageChildren.findIndex((rule: any) => rule.ruleTagId === newRow.ruleTagId);
// 排序
const oldRule = currPackageChildren.splice(oldRuleIndex, 1)[0];
currPackageChildren.splice(newRuleIndex, 0, oldRule);
currPackage.children = currPackageChildren;
// 替换掉原来的包
selectedTableData.splice(newPackageIndex, 1, currPackage);
// 在另外一个包内(废弃,目前不允许,还有问题)
} else {
return false;
// 找到旧包
// const oldPackageIndex = selectedTableData.findIndex((p: any) => p.newPackageId === oldRow.newPackageId);
// const oldPackage = selectedTableData[oldPackageIndex];
// expansionRowKey.push(oldPackage.id);
// const oldPackageChildren = oldPackage?.children || [];
// // 删掉旧包中的 old rule
// const oldRuleIndex = oldPackageChildren.findIndex((rule: any) => rule.ruleTagId === oldRow.ruleTagId);
// // 找到新包
// const newPackageIndex = selectedTableData.findIndex((p: any) => p.newPackageId === newRow.newPackageId);
// const newPackage = selectedTableData[newPackageIndex];
// expansionRowKey.push(newPackage.id);
// const newPackageChildren = newPackage?.children || [];
// // 找到 newRule 在 新包中的 index
// const newRuleIndex = newPackageChildren.findIndex((rule: any) => rule.ruleTagId === newRow.ruleTagId);
// const oldRule = oldPackageChildren.splice(oldRuleIndex, 1)[0];
// // 新包中增加 该 rule
// newPackageChildren.splice(newRuleIndex, 0, oldRule);
// // 覆盖原包
// oldPackage.children = oldPackageChildren;
// newPackage.children = newPackageChildren;
// selectedTableData.splice(oldPackageIndex, 1, oldPackage);
// selectedTableData.splice(newPackageIndex, 1, newPackage);
}
}
}
that.$nextTick(() => {
that.selectedTableData = selectedTableData;
that.$nextTick(() => {
that.selectedTableData.forEach((item: any) => {
if (expansionRowKey.includes(item.id)) {
(that.$refs.ruleSelectedTableRef as any).toggleRowExpansion(item, true);
}
});
});
});
}
},
});
}
觉得有帮助的小伙伴记得点个赞鼓励下~
扫描上方二维码关注我的订阅号~