手把手教你实现一个Vue无限级联树形表格(增删改)

前言

平时我们可能在做项目时,会遇到一个业务逻辑。实现一个无限级联树形表格,什么叫做无限级联树形表格呢?就是下图所展示的内容,有一个祖元素,然后下面可能有很多子孙元素,你可以实现添加、编辑、删除这样几个功能。

资源

  • JavaScript框架:vue.js

  • UI框架:Element UI

源码

这里需要重点说明的是,主要使用了递归的算法以及给数据标识的重要性。详细说明可以在源码中查看注释,也可以通过删改代码融会贯通。

1<template> 2    <div class="container"> 3        <div class="btn-r"> 4            <el-button 5                type="primary" 6                size="small" 7                @click="addView = true" 8                icon="el-icon-circle-plus-outline" 9                class="add" 10                >添加</el-button 11            > 12        </div> 13        <el-table 14            :data="tableData" 15            style="width: 100%; margin-bottom: 20px" 16            row-key="value" 17            border 18            default-expand-all 19            size="medium" 20            :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" 21        > 22            <el-table-column prop="label" label="名称" sortable> 23            </el-table-column> 24            <el-table-column label="操作" align="center" width="180"> 25                <template slot-scope="scope"> 26                    <el-button 27                        type="text" 28                        size="small" 29                        @click="handleClick(scope.row, scope.$index)" 30                        >编辑</el-button 31                    > 32                    <el-button 33                        type="text" 34                        size="small" 35                        @click="deleteClick(scope.row, scope.$index)" 36                        >删除</el-button 37                    > 38                </template> 39            </el-table-column> 40        </el-table> 41 42        <!-- 添加窗口 --> 43        <el-dialog 44            title="添加" 45            :visible.sync="addView" 46            :close-on-click-modal="false" 47            width="30%" 48            @close="closeView" 49        > 50            <el-form :model="form" ref="form" :rules="rules"> 51                <el-form-item 52                    label="位置" 53                    :label-width="formLabelWidth" 54                    prop="location" 55                > 56                    <el-select 57                        v-model="form.location" 58                        placeholder="请选择位置" 59                        @change="locationChange" 60                        size="small" 61                    > 62                        <el-option 63                            v-for="item in locationData" 64                            :key="item.id" 65                            :label="item.name" 66                            :value="item.id" 67                        /> 68                    </el-select> 69                </el-form-item> 70                <el-form-item 71                    v-if="sonStatus" 72                    label="子位置" 73                    :label-width="formLabelWidth" 74                    prop="childArr" 75                > 76                    <el-cascader 77                        size="small" 78                        :key="isResouceShow" 79                        v-model="form.childArr" 80                        placeholder="请选择子位置" 81                        :label="'name'" 82                        :value="'id'" 83                        :options="tableData" 84                        :props="{ checkStrictly: true }" 85                        clearable 86                        @change="getCasVal" 87                    ></el-cascader> 88                </el-form-item> 89                <el-form-item 90                    label="名称" 91                    :label-width="formLabelWidth" 92                    prop="label" 93                > 94                    <el-input 95                        v-model="form.label" 96                        size="small" 97                        autocomplete="off" 98                        placeholder="请输入名称" 99                    ></el-input> 100                </el-form-item> 101            </el-form> 102            <span slot="footer" class="dialog-footer"> 103                <el-button @click="addView = false" size="small" 104                    >取 消</el-button 105                > 106                <el-button type="primary" @click="okAdd('form')" size="small" 107                    >确 定</el-button 108                > 109            </span> 110        </el-dialog> 111 112        <!-- 编辑窗口 --> 113        <el-dialog 114            title="编辑" 115            :visible.sync="editView" 116            :close-on-click-modal="false" 117            width="30%" 118        > 119            <el-form :model="data" ref="data" :rules="rules"> 120                <el-form-item 121                    label="位置" 122                    :label-width="formLabelWidth" 123                    prop="location" 124                > 125                    <el-select 126                        v-model="data.location" 127                        placeholder="请选择位置" 128                        size="small" 129                        @change="locationChange" 130                    > 131                        <el-option 132                            v-for="item in locationData" 133                            :key="item.id" 134                            :label="item.name" 135                            :value="item.id" 136                        /> 137                    </el-select> 138                </el-form-item> 139                <el-form-item 140                    v-if="sonStatus" 141                    label="子位置" 142                    :label-width="formLabelWidth" 143                    prop="childArr" 144                > 145                    <el-cascader 146                        :key="isResouceShow" 147                        v-model="data.childArr" 148                        placeholder="请选择子位置" 149                        size="small" 150                        :label="'name'" 151                        :value="'id'" 152                        :options="tableData" 153                        :props="{ checkStrictly: true }" 154                        clearable 155                        @change="getCasVal" 156                    ></el-cascader> 157                </el-form-item> 158                <el-form-item 159                    label="名称" 160                    :label-width="formLabelWidth" 161                    prop="label" 162                > 163                    <el-input 164                        v-model="data.label" 165                        autocomplete="off" 166                        placeholder="请输入名称" 167                        size="small" 168                    ></el-input> 169                </el-form-item> 170            </el-form> 171            <span slot="footer" class="dialog-footer"> 172                <el-button @click="editView = false" size="small" 173                    >取 消</el-button 174                > 175                <el-button type="primary" @click="okEdit('data')" size="small" 176                    >确 定</el-button 177                > 178            </span> 179        </el-dialog> 180    </div> 181</template> 182 183<script> 184export default { 185    name: 'Tag', 186    data() { 187        return { 188            location: '', 189            isResouceShow: 1, 190            addView: false, 191            sonStatus: false, 192            editView: false, 193            casArr: [], 194            childArr: [], 195            form: {}, 196            data: {}, 197            idx: '', 198            childkey: [], 199            formLabelWidth: '80px', 200            rules: { 201                label: [ 202                    { required: true, message: '请输入名称', trigger: 'blur' } 203                ] 204            }, 205            locationData: [ 206                { 207                    id: 1, 208                    name: '顶' 209                }, 210                { 211                    id: 2, 212                    name: '子' 213                } 214            ], 215            tableData: [] 216        }; 217    }, 218    methods: { 219        // 监听关闭窗口 220        closeView() { 221            this.$refs['form'].resetFields(); // 关闭窗口,清空填写的内容 222        }, 223        // 打开编辑 224        handleClick(item, index) { 225            item.value.length != 1 226                ? (this.sonStatus = true) 227                : (this.sonStatus = false); 228            this.editView = true; 229            const obj = Object.assign({}, item); 230            this.childkey = item.childkey; 231            this.casArr = item.childArr; 232            this.idx = index; 233            this.data = obj; 234        }, 235        // 递归表格数据(编辑) 236        findSd(arr, i, casArr) { 237            if (== casArr.length - 1) { 238                let index = casArr[i].substr(casArr[i].length - 1, 1); 239                return arr.splice(index, 1, this.data); 240            } else { 241                return this.findSd( 242                    arr[casArr[i].substr(casArr[i].length - 1, 1)].children, 243                    (+= 1), 244                    casArr 245                ); 246            } 247        }, 248        // 确定编辑 249        okEdit(data) { 250            this.$refs[data].validate(valid => { 251                if (valid) { 252                    if (this.data.value.length == 1) { 253                        this.tableData.splice(this.idx, 1, this.data); 254                        this.$message({ 255                            type: 'success', 256                            message: '编辑成功' 257                        }); 258                        this.editView = false; 259                    } else { 260                        this.findSd(this.tableData, 0, this.childkey); 261                        this.$message({ 262                            type: 'success', 263                            message: '编辑成功' 264                        }); 265                        this.editView = false; 266                    } 267                } else { 268                    return false; 269                } 270            }); 271        }, 272        // 递归表格数据(删除) 273        findDel(arr, i, item) { 274            let casArr = item.childkey; 275            if (== casArr.length - 1) { 276                let index = casArr[i].substr(casArr[i].length - 1, 1); 277                return arr.splice(index, 1); 278            } else { 279                return this.findDel( 280                    arr[casArr[i].substr(casArr[i].length - 1, 1)].children, 281                    (+= 1), 282                    item 283                ); 284            } 285        }, 286        // 删除 287        deleteClick(item) { 288            this.$confirm(`此操作将删除该项, 是否继续?`, '提示', { 289                confirmButtonText: '确定', 290                cancelButtonText: '取消', 291                type: 'warning' 292            }) 293                .then(() => { 294                    if (item.children.length != 0) { 295                        this.$message.warning({ 296                            message: '请删除子节点', 297                            duration: 1000 298                        }); 299                    } else { 300                        this.casArr = item.childArr; 301                        ++this.isResouceShow; // 给级联控件绑定一个key,防止报错。 302                        if (item.value.length == 1) { // 删除的是顶节点 303                            console.log(1); 304                            this.tableData.splice(item.value, 1); 305                            this.$message({ 306                                type: 'success', 307                                message: '删除成功' 308                            }); 309                        } else { // 删除的是子节点 310                            console.log(2); 311                            this.findDel(this.tableData, 0, item); 312                            this.$message({ 313                                type: 'success', 314                                message: '删除成功' 315                            }); 316                        } 317                    } 318                }) 319                .catch(err => { 320                    console.log(err); 321                    this.$message({ 322                        type: 'info', 323                        message: '已取消删除' 324                    }); 325                }); 326        }, 327        // 是否显示次位置 328        locationChange(v) { 329            if (== 2) { 330                this.sonStatus = true; 331            } else { 332                this.sonStatus = false; 333            } 334        }, 335        // 获取次位置 336        getCasVal(v) { 337            this.casArr = v; 338            this.form.childArr = v; 339        }, 340        // 递归表格数据(添加) 341        find(arr, i) { 342            if (== this.casArr.length - 1) { 343                return arr[this.casArr[i].substr(this.casArr[i].length - 1, 1)] 344                    .children; 345            } else { 346                return this.find( 347                    arr[this.casArr[i].substr(this.casArr[i].length - 1, 1)] 348                        .children, 349                    (+= 1) 350                ); 351            } 352        }, 353        // 确定添加 354        okAdd(form) { 355            this.$refs[form].validate(valid => { 356                if (valid) { 357                    if (this.sonStatus == false) { 358                        this.form.value = String(this.tableData.length); 359                        const obj = Object.assign({}, this.form); 360                        obj.children = []; 361                        obj.childArr = []; 362                        this.tableData.push(obj); 363                        this.$message({ 364                            type: 'success', 365                            message: '添加成功' 366                        }); 367                        this.addView = false; 368                    } else { 369                        let arr = this.find(this.tableData, 0); 370                        this.childArr = [...this.casArr, String(arr.length)]; 371                        this.form.value = 372                            String(this.casArr[this.casArr.length - 1]) + 373                            String(arr.length); 374                        delete this.form.children; 375                        const obj = Object.assign({}, this.form); 376                        obj.children = []; 377                        obj.childkey = [...this.casArr, String(arr.length)]; 378                        arr.push(obj); 379                        this.$message({ 380                            type: 'success', 381                            message: '添加成功' 382                        }); 383                        this.addView = false; 384                    } 385                } else { 386                    return false; 387                } 388            }); 389        } 390    } 391}; 392</script> 393 394<style lang="scss" scoped> 395::v-deep .el-form-item__content { 396    width: 203px; 397} 398</style> 399 400

结语

  • 欢迎关注我的公众号前端历劫之路

  • 回复关键词电子书,即可获取12本前端热门电子书。

  • 回复关键词红宝书第4版,即可获取最新《JavaScript高级程序设计》(第四版)电子书。

  • 关注公众号后,点击下方菜单即可加我微信,我拉拢了很多IT大佬,创建了一个技术交流、文章分享群,期待你的加入。

  • 作者:Vam的金豆之路

  • 微信公众号:前端历劫之路

- END -

本文转转自微信公众号前端历劫之路原创https://mp.weixin.qq.com/s/uUmzBeCq3PL1PZyYQDlFBw,如有侵权,请联系删除。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

js 数组 转为树形结构

需要转换为树形的数组vardata{"orderById":null,"platformCommissionProportion":1,"name":"添加剂","pid":13,"id":26