Vue + Element 树形表格实现拖拽移位

1,855 阅读1分钟

纯前端实现树形表格拖拽,无需把树形结构转换为列表数组,如果你有这样的需求或觉得有用,请给个关注或收藏一下吧,方便后期查看使用。

准备事项

安装拖拽插件

可直接安装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中) —— 行拖拽、列拖拽提供思路.谢谢.