vue项目el-tree 懒加载实现增加、修改、删除及刷新当前子节点,render-content自定义样式

本文详细介绍了如何在Vue.js项目中使用Element-UI的el-tree组件实现动态加载和懒加载功能。通过自定义样式、处理加载逻辑以及解决增删改后的刷新问题,实现了一个完整的单位管理功能。关键在于loadNode方法中根据层级动态加载数据,以及使用renderContent渲染节点内容,并在添加、编辑、删除操作后更新树结构。
摘要由CSDN通过智能技术生成

element-ui 中设计层级和动态加载节点的组件el-tree,对于前端来说,用的比较多,否则对于vue项目,要自己写render操作dom。当操作的树形结构层级比较多时,多用懒加载。例如实现如下功能,就是用el-tree实现的。

实现这个功能有几个问题 要解决:

1.改样式。官网提供的样式不能够满足开发的需要,尤其是对tree进行增删改的时候。所以需要用到render-content自定义样式。

2.加载问题。懒加载,如何插入数据。

3.增删改之后刷新问题。

前两个问题很好解决,最后一个问题不好弄,尤其是添加操作刷新问题,是个难点,需要反复读官方文档。

核心代码主要在list列表这个页面,直接附上代码,代码中有详细的注释讲解。

直接复制代码不能运行,因为调用了一些后端的接口,但是功能的主要实现,都已列出并注释。

<template>
    <div class="dept-page">
        <div class="dept-tit">
            <h3>单位管理</h3>
        </div>
        <div class="dept-con" v-loading="isLoading">
            <el-tree
                node-key="deptCode"
                :props="treeProps"
                :load="loadNode"
                lazy
                :render-content="renderContent"
                ref="tree"
            >
            </el-tree>
        </div>
        <AddSubDepartment ref="addSubDepartment" @addReload = 'addReload'></AddSubDepartment>
        <EditDepartment ref="editDepartment" @editReload = 'editReload'></EditDepartment>
    </div>
</template>

<script>
import {mapState} from "vuex";
import AddSubDepartment
    from "@/views/modules/configuration/subModules/department/subModules/addSubDepartment/addBackup";
import EditDepartment
    from "@/views/modules/configuration/subModules/department/subModules/editDepartment/editBackup";
import subDeptList from "@/views/modules/configuration/subModules/department/subModules/subDeptList/subDeptList";
import {deleteDepartment, getDepartmentList} from "@/api/modules/configuration/configuration";

export default {
    name: "departmentList",
    data () {
        return {
            isLoading:false,
            subDeptId:'',
            showSubDept:false,
            departmentList:[],
            treeProps: {
                isLeaf: 'isLowest'
            },

        }
    },
    components:{
        AddSubDepartment,
        EditDepartment,
        subDeptList
    },
    computed:{
        ...mapState('user', ['v_user']),
        ...mapState("departments", ["localDeptTree",'v_globalDeptTree']),
    },
    mounted() {

    },
    methods:{
        loadNode(node,resolve) {
            // console.log('----node------',node);
            // console.log('----resolve------',resolve);
            const _this = this;
            _this.isLoading = true

            if (node.level === 0) {
                const id = _this.localDeptTree[0].id
                getDepartmentList(id).then(res=>{
                    //isLowes后端传回的数据,数字字符串,标识自节点的个数,所以为0时,标识叶子节点
                    //由于isLowest付给了isLeaf,isLeaf的类型是boolean,所以要转化一下,如果后端传的就是Boolean类型,就不用了
                    res.forEach((item,index) => {
                        if(item.isLowest == '0'){
                            item.isLowest = true
                        }else{
                            item.isLowest = false
                        }
                    })
                    return resolve(res);
                    _this.isLoading = false
                }).catch(err=>{
                    console.log(`列表获取失败!${err}`)
                }).finally(() => {
                    _this.isLoading = false
                })
            }
            if (node.level >= 1) {
                getDepartmentList(node.data.deptId).then(res=>{
                    //正常不会出现这种情况,应为有isLeaf的判断,如果是叶子结点,是不能点击的
                    if(res.length == 0){
                        alert('没有子节点')
                        return resolve([]);
                    }else{
                        res.forEach((item,index) => {
                            if(item.isLowest == '0'){
                                item.isLowest = true
                            }else{
                                item.isLowest = false
                            }
                        })
                        return resolve(res);//重要,懒加载中动态加载子集
                    }

                }).catch(err=>{
                    console.log(`列表获取失败!${err}`)
                }).finally(() => {
                    _this.isLoading = false
                })
            }
        },
        //打开添加子节点的弹出层
        openAddDialog(e,node,data){
            e.stopPropagation()
            const _this =this
            _this.$refs.addSubDepartment.show('add',e,node,data);
        },
        //添加操作成功,关闭弹出层后调用刷新列表的方法
        addReload(node,data,e){
            //需求中添加的是子节点,所以点击的当前node应该是添加节点的父节点
            data.isLowest = true   //后端返回的数据,实际上就是isLeaf,新添加的数据是没有子节点的,所以是叶子结点,如果后端给返回了,可以不设置
            this.$refs.tree.append(data,node);  //将新添加数据插入到node中,作为其子节点,append为插件提供的方法,需要通过this.$refs.tree调用
            //将插入的子节点设置为叶子结点。
            node.childNodes.forEach(item => {
                if(item.data.deptId == data.deptId){  //deptId是数据的唯一标识,根据返回数据而定,相当于id
                    item.isLeaf = true;
                }
            });
            node.isLeaf = false; //添加了节点之后,此节点不可能是叶子结点了,设置为false,控制前面那个小三角是否显示
            node.data.isLowest = false;  //控制render-content中删除按钮是否显示,根据系统需求而定,我们的需求中,删除按钮显示需要有些条件判断
            if(!node.loaded){  //loaded表示节点是否有加载过,加载过之后为true
                e.path[3].click();  //loaded=false时,子节点未加载过,添加节点后触发所点击节点的click事件,从而触发@load事件
                                    //click应加在class="custom-tree-node"的节点上,需要遍历点击按钮e这个元素中的path,
                                    // 我的render-content设置的结构中,path[3]就是custom-tree-node,因此这样写。如果结构不同,可能结果不同,但最终都要加在custom-tree-node上
            }else{
                node.expanded = true; //子节点已经load过,添加后也需要展开子节点,expanded控制展开
            }
        },
        //打开修改弹出层
        openEditDialog(e,node,data){
            e.stopPropagation()
            const _this =this
            _this.$refs.editDepartment.show(data,node,e);
        },
        //修改成功后执行刷新方法
        editReload(node,data,e){
            //将修改成功的数据,赋值给当前操作的node.data,数据驱动,页面就会变成修改后的内容
            Object.keys(data).forEach(key=>{node.data[key]=data[key]})
        },
        deleteDept(e,node,data){
            e.stopPropagation()
            const _this =this
            _this.$confirm(`确认要删除已选单位吗?删除此单位将删除此单位的所有用户。`).then(_ => {
                deleteDepartment(data.deptId).then( res => {
                    _this.$toast({
                        duration:'1000',
                        type: 'success',
                        message: '删除成功!'
                    })
                    //el-tree 处理删除后的刷新问题
                    //数据请求删除成功后,要在页面上将数据移除,这样就能实现不刷新页面,数据驱动,动态删除数据的功能
                    //因此,请求删除接口成功后,对el-tree 的节点进行操作
                    const parent = node.parent;
                    const children =[];
                    parent.childNodes.forEach(item => {
                        children.push(item.data)
                    });
                    const index = children.findIndex(d => d.deptId === data.deptId);
                    parent.childNodes.splice(index, 1);
                    if(parent.childNodes.length == 0){ //判断删除的是否是最后一个节点,如果是,父级节点将变成叶子结点
                        parent.isLeaf = true;     //设置叶子节点,用于层级显示
                        parent.data.isLowest = true;//设置叶子节点,用于render-content渲染dom
                    }
                }).catch(err=>{
                    console.log(err)
                }).finally(()=>{
                    this.isLoading = false
                })

            }).catch(_ => {});
        },
        //通过render懒加载单位
        renderContent(h, { node, data, store }) {
            //根据系统需求,通过data中的属性渲染dom,例如有的节点不能显示删除按钮,有点不能显示添加按钮,根据需求而定
            if(data.deptType != 'N01E01E01E01E01E' && data.deptType != 'N01E01E01E02E' && data.deptType != 'N01E02E01E01E'){
                if(data.isLowest){
                    return (
                        <span class="custom-tree-node">
                            <span class="dept-name">{data.deptName}</span>
                            <span class="dept-code">{data.deptId}</span>
                            <span class="dept-oper">
                                <el-button size="mini" type="primary" on-click={ (e) => this.openAddDialog(e,node,data) } >添加下级单位</el-button>
                                <el-button size="mini" type="primary" on-click={ (e) => this.openEditDialog(e,node, data) }>编辑</el-button>
                                <el-button size="mini" type="primary" on-click={ (e) => this.deleteDept(e,node, data) }>删除</el-button>
                            </span>
                        </span>
                    );
                }else{
                    return (
                        <span class="custom-tree-node">
                            <span class="dept-name">{data.deptName}</span>
                            <span class="dept-code">{data.deptId}</span>
                            <span class="dept-oper">
                                <el-button size="mini" type="primary" on-click={ (e) => this.openAddDialog(e,node,data) } >添加下级单位</el-button>
                                <el-button size="mini" type="primary" on-click={ (e) => this.openEditDialog(e,node, data) }>编辑</el-button>
                            </span>
                        </span>
                    );
                }
            }else{
                if(data.isLowest){
                    return (
                        <span class="custom-tree-node">
                            <span class="dept-name">{data.deptName}</span>
                            <span class="dept-code">{data.deptId}</span>
                            <span class="dept-oper">
                                <el-button size="mini" type="primary" on-click={ (e) => this.openEditDialog(e,node, data) }>编辑</el-button>
                                <el-button size="mini" type="primary" on-click={ (e) => this.deleteDept(e,node, data) }>删除</el-button>
                            </span>
                        </span>
                    );
                }else{
                    return (
                        <span class="custom-tree-node">
                            <span class="dept-name">{data.deptName}</span>
                            <span class="dept-code">{data.deptId}</span>
                            <span class="dept-oper">
                                <el-button size="mini" type="primary" on-click={ (e) => this.openEditDialog(e,node, data) }>编辑</el-button>
                            </span>
                        </span>
                    );
                }
            }
        }

    }
}
</script>

<style lang="scss" scoped>
.dept-page{
    .dept-tit{
        height: 48px;
        border-bottom:1px solid #ddd;
        h3{
            margin-left:20px;
            font-size: 14px;
            color: #3169ba;
            line-height: 48px;
        }
    }
    .dept-con{
        padding:20px;
    }
}
//对el-tree样式的改写
.el-tree{
    border:1px solid #ddd;
    overflow: hidden;
    >>>.el-tree-node__content{
        padding:5px;
        border-bottom:1px solid #ddd;
        .custom-tree-node{
            width:100%;
            overflow: hidden;
            height:30px;
            line-height:30px;
            .dept-name{
                width:500px;
                float:left;
                padding:0 10px;
                cursor:pointer;
            }
            .dept-code{
                float:left;
                padding:0 10px;
            }
            .dept-oper{
                float:right;
                width:240px;
                text-align:left;
                padding:0 10px;
            }
        }

    }
}
</style>

  • 17
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值