最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue.js与ElementUI搭建无限级联层级表格组件

    正文概述 掘金(Vam的金豆之路)   2021-02-10   817

    前言

    今天,回老家了。第一件事就是回家把大屏安排上,写作的感觉太爽了,终于可以专心地写文章了。我们今天要做的项目是怎么样搭建一个无限级联层级表格组件,好了,多了不多说,赶快行动起来吧!

    项目一览

    到底是啥样子来?我们来看下。

    Vue.js与ElementUI搭建无限级联层级表格组件

    正如你所看到的那样,这个组件涉及添加、删除、编辑功能,并且可以无限级嵌套。那么怎样实现的?我们来看下。

    源码

    直接给出源码,就是这么直接。

    <template>
        <div class="container">
            <el-button
                type="primary"
                size="small"
                @click="handleCreate"
                icon="el-icon-circle-plus-outline"
                style="margin: 10px 0"
                >添加</el-button
            >
            <el-table
                :data="tableData"
                style="width: 100%; margin-bottom: 20px"
                border
                row-key="value"
                stripe
                size="medium"
                :tree-props="{ children: 'children' }"
            >
                <el-table-column prop="label" label="标签名称"> </el-table-column>
                <el-table-column prop="location" label="层级"> </el-table-column>
                <el-table-column label="操作" :align="alignDir" width="180">
                    <template slot-scope="scope">
                        <el-button
                            type="text"
                            size="small"
                            @click="handleUpdate(scope.row)"
                            >编辑</el-button
                        >
                        <el-button
                            type="text"
                            size="small"
                            @click="deleteClick(scope.row)"
                            >删除</el-button
                        >
                    </template>
                </el-table-column>
            </el-table>
            <el-dialog
                :
                :visible.sync="dialogFormVisible"
                width="30%"
            >
                <el-form
                    ref="dataForm"
                    :rules="rules"
                    :model="temp"
                    label-position="left"
                    label-width="120px"
                    style="margin-left: 50px"
                >
                    <el-form-item
                        label="层级:"
                        prop="location"
                        v-if="dialogStatus !== 'update'"
                    >
                        <el-select
                            v-model="temp.location"
                            placeholder="请选择层级"
                            @change="locationChange"
                            size="small"
                        >
                            <el-option
                                v-for="item in locationData"
                                :key="item.id"
                                :label="item.name"
                                :value="item.id"
                            />
                        </el-select>
                    </el-form-item>
                    <el-form-item
                        v-if="sonStatus && dialogStatus !== 'update'"
                        label="子位置:"
                        prop="children"
                    >
                        <el-cascader
                            size="small"
                            :key="isResouceShow"
                            v-model="temp.children"
                            placeholder="请选择子位置"
                            :label="'label'"
                            :value="'value'"
                            :options="tableData"
                            :props="{ checkStrictly: true }"
                            clearable
                            @change="getCasVal"
                        ></el-cascader>
                    </el-form-item>
                    <el-form-item label="标签名称:" prop="label">
                        <el-input
                            v-model="temp.label"
                            size="small"
                            autocomplete="off"
                            placeholder="请输入标签名称"
                        ></el-input>
                    </el-form-item>
                </el-form>
                <div slot="footer" class="dialog-footer">
                    <el-button @click="dialogFormVisible = false" size="small">
                        取消
                    </el-button>
                    <el-button
                        type="primary"
                        size="small"
                        @click="
                            dialogStatus === 'create' ? createData() : updateData()
                        "
                    >
                        确认
                    </el-button>
                </div>
            </el-dialog>
        </div>
    </template>
    
    <script>
    export default {
        name: 'Tag',
        data() {
            return {
                alignDir: 'center',
                textMap: {
                    update: '编辑',
                    create: '添加',
                },
                dialogStatus: '',
                dialogFormVisible: false,
                temp: {},
                isResouceShow: 1,
                sonStatus: false,
                casArr: [],
                idx: '',
                childKey: [],
                rules: {
                    location: [
                        {
                            required: true,
                            message: '请选择层级',
                            trigger: 'blur',
                        },
                    ],
                    label: [
                        { required: true, message: '请输入名称', trigger: 'blur' },
                    ],
                    children: [
                        {
                            required: true,
                            message: '请选择子位置',
                            trigger: 'blur',
                        },
                    ],
                },
                locationData: [
                    {
                        id: '1',
                        name: '顶',
                    },
                    {
                        id: '2',
                        name: '子',
                    },
                ],
                tableData: [
                    {
                        tagId: '1', // 标签id
                        label: '第0', // 标签名称
                        parent: '', // 父级名称
                        location: '1', // 层级
                        value: '0', // 标识位
                        children: [
                            {
                                tagId: '1', // 子标签id
                                childKey: ['0', '0'], // 子标识位
                                label: '第0-0',
                                parent: '第0',
                                location: '2',
                                value: '0-0',
                                children: [],
                            },
                            {
                                tagId: '2', // 子标签id
                                childKey: ['0', '1'],
                                label: '第0-1',
                                parent: '第0',
                                location: '2',
                                value: '0-1',
                                children: [],
                            },
                        ],
                    },
                ]
            };
        },
        methods: {
            // 递归寻找同级
            findSameTable(arr, i, casArr) {
                if (i == casArr.length - 1) {
                    return arr;
                } else {
                    return this.findTable(
                        arr[casArr[i].substr(casArr[i].length - 1, 1)].children,
                        (i += 1),
                        casArr
                    );
                }
            },
            // 寻找父级
            findTable(arr, i, casArr) {
                if (i == casArr.length - 1) {
                    let index = casArr[i].substr(casArr[i].length - 1, 1);
                    return arr[index];
                } else {
                    return this.findTable(
                        arr[casArr[i].substr(casArr[i].length - 1, 1)].children,
                        (i += 1),
                        casArr
                    );
                }
            },
            // 递归表格数据(添加)
            find(arr, i) {
                if (i == this.casArr.length - 1) {
                    return arr[this.casArr[i].substr(this.casArr[i].length - 1, 1)]
                        .children;
                } else {
                    return this.find(
                        arr[this.casArr[i].substr(this.casArr[i].length - 1, 1)]
                            .children,
                        (i += 1)
                    );
                }
            },
            // 递归表格数据(编辑)
            findSd(arr, i, casArr) {
                if (i == casArr.length - 1) {
                    let index = casArr[i].substr(casArr[i].length - 1, 1);
                    return arr.splice(index, 1, this.temp);
                } else {
                    return this.findSd(
                        arr[casArr[i].substr(casArr[i].length - 1, 1)].children,
                        (i += 1),
                        casArr
                    );
                }
            },
            // 递归寻找同步名称
            findLable(arr, i, casArr) {
                if (i == casArr.length - 1) {
                    let index = casArr[i].substr(casArr[i].length - 1, 1);
                    return arr[index];
                } else {
                    return this.findLable(
                        arr[casArr[i].substr(casArr[i].length - 1, 1)].children,
                        (i += 1),
                        casArr
                    );
                }
            },
            // 同步子名称
            useChildLable(arr) {
                if (arr !== []) {
                    arr.forEach((item) => {
                        item.parent = this.temp.label;
                    });
                }
            },
            // 递归表格数据(删除)
            findDel(arr, i, item) {
                let casArr = item.childKey;
                if (i == casArr.length - 2) {
                    let index = casArr[i].substr(casArr[i].length - 1, 1);
                    arr[index].children.forEach((it, ix, arrs) => {
                        if (it == item) {
                            return arrs.splice(ix, 1);
                        }
                    });
                } else {
                    return this.findDel(
                        arr[casArr[i].substr(casArr[i].length - 1, 1)].children,
                        (i += 1),
                        item
                    );
                }
            },
          // 置空
            resetTemp() {
                this.temp = {};
            },
          // 打开添加
            handleCreate() {
                this.resetTemp();
                this.dialogFormVisible = true;
                this.dialogStatus = 'create';
                this.$nextTick(() => {
                    this.$refs['dataForm'].clearValidate();
                });
            },
          // 添加
            createData() {
                this.$refs['dataForm'].validate((valid) => {
                    if (valid) {
                        if (this.sonStatus == false) {
                            this.temp.value = String(this.tableData.length);
                            const obj = Object.assign({}, this.temp);
                            obj.children = [];
                            obj.parent = '';
                            this.tableData.push(obj);
                            this.$message({
                                type: 'success',
                                message: '添加成功',
                            });
                            this.dialogFormVisible = false;
                        } else {
                            let arr = this.find(this.tableData, 0);
                            this.temp.value =
                                String(this.casArr[this.casArr.length - 1]) +
                                '-' +
                                String(arr.length);
                            delete this.temp.children;
    
                            const obj = Object.assign({}, this.temp);
                            obj.children = [];
                            obj.childKey = [...this.casArr, String(arr.length)];
                            obj.parent = this.findTable(
                                this.tableData,
                                0,
                                this.casArr
                            ).label;
                            if (this.temp.location === '2') {
                                obj.location = String(
                                    [...this.casArr, String(arr.length)].length
                                );
                            }
                            arr.push(obj);
                            this.$message({
                                type: 'success',
                                message: '添加成功',
                            });
                            this.dialogFormVisible = false;
                        }
                    } else {
                        return false;
                    }
                });
            },
          // 打开更新
            handleUpdate(row) {
                console.log(row);
                row.value.length != 1
                    ? (this.sonStatus = true)
                    : (this.sonStatus = false);
                this.temp = Object.assign({}, row); // copy obj
                if (row.childKey) {
                    this.childKey = row.childKey;
                    this.idx = row.childKey[row.childKey.length - 1];
                } else {
                    this.idx = row.value;
                }
                console.log(this.idx);
    
                this.dialogStatus = 'update';
                this.dialogFormVisible = true;
                this.$nextTick(() => {
                    this.$refs['dataForm'].clearValidate();
                });
            },
          // 更新
            updateData() {
                this.$refs['dataForm'].validate((valid) => {
                    if (valid) {
                        if (this.temp.location === '1') {
                            console.log(this.temp);
                            this.tableData.splice(this.idx, 1, this.temp);
                            this.useChildLable(this.tableData[this.idx].children);
                            this.$message({
                                type: 'success',
                                message: '编辑成功',
                            });
                            this.dialogFormVisible = false;
                        } else {
                            this.findSd(this.tableData, 0, this.childKey);
                            this.useChildLable(
                                this.findLable(this.tableData, 0, this.childKey)
                                    .children
                            );
                            this.$message({
                                type: 'success',
                                message: '编辑成功',
                            });
                            this.dialogFormVisible = false;
                        }
                    } else {
                        return false;
                    }
                });
            },
            // 删除父级节点
            deleteParent(item) {
                this.tableData.forEach((it, ix, arrs) => {
                    if (it == item) {
                        return arrs.splice(ix, 1);
                    }
                });
            },
            // 删除
            deleteClick(item) {
                this.$confirm(`此操作将删除该标签, 是否继续?`, '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning',
                })
                    .then(() => {
                        if (item.children.length != 0) {
                            this.$message.warning({
                                message: '请删除子节点',
                                duration: 1000,
                            });
                        } else {
                            ++this.isResouceShow;
                            if (item.value.length == 1) {
                                this.deleteParent(item);
                                this.$message({
                                    type: 'success',
                                    message: '删除成功',
                                });
                            } else {
                                this.findDel(this.tableData, 0, item);
                                this.$message({
                                    type: 'success',
                                    message: '删除成功',
                                });
                            }
                        }
                    })
                    .catch((err) => {
                        console.log(err);
                        this.$message({
                            type: 'info',
                            message: '已取消删除',
                        });
                    });
            },
            // 是否显示次位置
            locationChange(v) {
                if (v == 2) {
                    this.sonStatus = true;
                } else {
                    this.sonStatus = false;
                }
            },
            // 获取次位置
            getCasVal(v) {
                this.casArr = v;
            },
        },
    };
    </script>
    
    

    代码可以直接拿来用,但是要注意事先要安装下ElementUI框架。无限层级的核心算法是递归算法,掌握了这一点,任何难题都可以解决。

    下面,我们就这个项目来回顾下前端中的递归算法。

    递归简而言之就是函数调用自己。递归算法中有两个条件:基线条件和递归条件。基线条件用于控制递归啥时候暂停,而递归条件是控制调用自己的方式。

    最简单的一个例子是5的阶乘。

    var func = function(i){
        if(i === 1){
            return 1;
        }else{
            return i*func(i-1);
        }
    
    }
    func(5);
    

    这样就很简单的实现了一个递归算法,我们将上述例子拆解下。

    // 递
    5*func(4);
    5*4*func(3);
    5*4*3*func(2);
    5*4*3*2*func(1);
    // 归
    5*4*3*2*1;
    5*4*3*2;
    5*4*6;
    5*24;
    120
    

    递归其实可以理解成两个操作递与归。可以这样比喻,比如你在做一道数学题时,有一个知识点你不懂,你需要查资料。但是,通过查资料你发现这个知识点中你又有另一个不明白的知识点,你又开始继续查,直到你没有不懂的知识点,这样递的操作已经完成。然后,你把已经查过的这些知识点又从尾到头复习了一遍,这样归的操作已经完成。最后,你明白了最初那个知识点。

    结语

    谢谢阅读,希望我没有浪费你的时间。如果觉得对你有帮助,欢迎点赞关注我啊!


    起源地下载网 » Vue.js与ElementUI搭建无限级联层级表格组件

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元