纯前端实现树形表格拖拽,无需把树形结构转换为列表数组,如果你有这样的需求或觉得有用,请给个关注或收藏一下吧,方便后期查看使用。
准备事项
安装拖拽插件
可直接安装vuedraggable,因安装 vuedraggable 的同时,会自动安装 sortablejs
npm i -S vuedraggable
或者直接安装sortablejs
npm install sortablejs --save
行拖拽要点
需在 el-table 标签中,根据行的内容指定行的唯一标识 row-key="id"
# 列拖拽要点
需额外定义两个数组,分别存储拖拽前的列顺序和拖拽后的列顺序
完整代码
<template>
<div class="draggable" style="padding: 20px">
<el-table row-key="id" :data="tableData" style="width: 100%" border>
<el-table-column
v-for="(item, index) in oldList"
:key="`col_${index}`"
:prop="newList[index].prop"
:label="item.label"
align="center"
>
</el-table-column>
</el-table>
</div>
</template>
<script>
import Sortable from "sortablejs";
import { ElMessage, ElMessageBox } from "element-plus";
export default {
components: {
ElMessage,
ElMessageBox,
},
mounted() {
//列
this.oldList = JSON.parse(JSON.stringify(this.tableConfig.tableItems));
this.newList = JSON.parse(JSON.stringify(this.tableConfig.tableItems));
//行 拖拽与 列拖拽
this.columnDrop();
this.rowDrop();
//树形结构转数组
this.tableDataArray = this.toArray(this.tableData);
},
data() {
return {
oldList: [],
newList: [],
//树形结构数据转的数组
tableDataArray: [],
//表格树形结构数据
tableData: [
{
pid: 0,
id: 1,
date: "2016-05-01",
name: "1",
address: "上海市普陀区金沙江路 1518 弄",
},
{
pid: 0,
id: 2,
date: "2016-05-02",
name: "2",
address: "上海市普陀区金沙江路 1517 弄",
},
{
pid: 0,
id: 3,
date: "2016-05-03",
name: "3",
address: "上海市普陀区金沙江路 1519 弄",
children: [
{
pid: 3,
id: 31,
date: "2016-05-03",
name: "3-1",
address: "上海市普陀区金沙江路 1519 弄",
},
{
pid: 3,
id: 32,
date: "2016-05-03",
name: "3-2",
address: "上海市普陀区金沙江路 1519 弄",
},
],
},
{
pid: 0,
id: 4,
date: "2016-05-04",
name: "4",
address: "上海市普陀区金沙江路 1516 弄",
children: [
{
pid: 4,
id: 41,
date: "2016-05-04",
name: "4-1",
address: "上海市普陀区金沙江路 1519 弄",
},
{
pid: 4,
id: 42,
date: "2016-05-04",
name: "4-2",
address: "上海市普陀区金沙江路 1519 弄",
},
],
},
],
tableConfig: {
tableItems: [
{
label: "日期",
prop: "date",
},
{
label: "姓名",
prop: "name",
},
{
label: "地址",
prop: "address",
},
],
},
};
},
methods: {
//查看树中v2 是否 为 v1的底级
isTreeBottom(tree, v1, v2) {
let result = false;
function recursion(tree, v1, v2, flag) {
for (let i = 0; i < tree.length; i++) {
const item = tree[i];
if (item.id === v2) {
result = flag;
return;
} else if (item.id === v1) {
item.children ? recursion(item.children, v1, v2, true) : "";
} else {
item.children ? recursion(item.children, v1, v2, flag) : "";
}
}
}
recursion(tree, v1, v2, false);
return result;
},
//数组转树
arrToTree(arr, parentId) {
return arr
.filter((item) => (parentId === undefined ? item.pid === 0 : item.pid === parentId))
.map((item) => {
item.children = this.arrToTree(arr, item.id);
return item;
});
},
//树转数组
toArray(data) {
const result = [];
data.forEach((item) => {
const loop = (data) => {
result.push({
id: data.id,
name: data.name,
pid: data.pid,
date: data.date,
address: data.address,
});
let child = data.children;
if (child) {
for (let i = 0; i < child.length; i++) {
loop(child[i]);
}
}
};
loop(item);
});
return result;
},
//行结束拖拽事件
rowDropEnd(newIndex, oldIndex) {
//判断是否没有更换位置,如果没更换,则不做任何操作
if (newIndex === oldIndex) return;
//判断是否把该行 拖拽到 自己的底级中
if (
this.isTreeBottom(
this.tableData,
this.tableDataArray[oldIndex].id,
this.tableDataArray[newIndex].id
)
) {
ElMessage({
type: "info",
message: "不能放到自己的底级",
});
let tableData = this.tableData;
this.tableData = [];
this.$nextTick(() => {
this.tableData = tableData;
});
return;
}
//弹窗提示二次确认
ElMessageBox.confirm("确定要更改其位置?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
ElMessage({
type: "success",
message: "已 确 定",
});
//拿到当前行数据,并从数组中删除该数据
const currRow = this.tableDataArray.splice(oldIndex, 1)[0];
//把当前行数据的 父id 给 赋值为结束位置的父id,因为当前行与结束行平级.父id相同.
currRow.pid = this.tableDataArray[newIndex].pid;
//把更改了父id的当前行数据给插入到数组中
this.tableDataArray.splice(newIndex, 0, currRow);
//表格显示的数据清空
this.tableData = [];
this.$nextTick(() => {
//表格显示的数据 为 修改了位置的数组转换成的属性结构
this.tableData = this.arrToTree(this.tableDataArray);
//新的数组为 表格显示的数据转换
this.tableDataArray = this.toArray(this.tableData);
});
})
.catch((err) => {
console.log("err", err);
ElMessage({
type: "info",
message: "已 取 消",
});
//取消则恢复之前的拖拽位置
let tableData = this.tableData;
this.tableData = [];
this.$nextTick(() => {
this.tableData = tableData;
});
});
},
// 行拖拽
rowDrop() {
// 此时找到的元素是要拖拽元素的父容器
const tbody = document.querySelector(".draggable .el-table__body-wrapper tbody");
const _this = this;
Sortable.create(tbody, {
// 指定父元素下可被拖拽的子元素
draggable: ".draggable .el-table__row",
// 拖拽方法
onEnd({ newIndex, oldIndex }) {
_this.rowDropEnd(newIndex, oldIndex);
// const currRow = _this.tableData.splice(oldIndex, 1)[0];
// _this.tableData.splice(newIndex, 0, currRow);
},
});
},
// 列拖拽
columnDrop() {
// function isTreeBottom(a)
const wrapperTr = document.querySelector(".draggable .el-table__header-wrapper tr");
this.sortable = Sortable.create(wrapperTr, {
animation: 180,
delay: 0,
onEnd: (evt) => {
const oldItem = this.newList[evt.oldIndex];
this.newList.splice(evt.oldIndex, 1);
this.newList.splice(evt.newIndex, 0, oldItem);
},
});
},
},
};
</script>
<style scoped></style>
感谢CSDN作者"朝阳39"的Element UI表格拖拽(vue中) —— 行拖拽、列拖拽提供思路.谢谢.