Vue el-tree树状图进行懒加载功能实现

Vue el-tree树状图进行懒加载功能实现

动态加载
当选中某一级时,动态加载该级下的选项:
通过lazy开启动态加载,并通过lazyload来设置加载数据源的方法。 lazyload方法有两个参数,第一个参数node为当前点击的节点,第二个resolve为数据加载完成的回调(必须调用)。 为了更准确的显示节点的状态,还可以对节点数据添加是否为叶子节点的标志位 (默认字段为leaf,可通过props.leaf修改)。 否则,将以有无子节点来判断其是否为叶子节点。
实际操作示例:

<template id="TYPESJ">
    <div class="com-tree" style="margin: 0; padding: 0; height: 100%">
        <div class="fytypemgs">
            <!--隐藏组织架构后显示的图标-->
            <div v-show="Display" @click="Donotopen()" style="width:25px;height:50px;background-color:rgb(0 191 255/0.4);position:absolute;z-index:999">
                <a class="el-icon-arrow-right" style=" line-height: 50px;"></a>
            </div>
            <el-container v-show="Hide" style="width:250px; height: 100%;border-left: 1px solid #eee !important;border-right: 1px solid #eee !important">
                <el-header id="color" style="border-bottom: 1px solid #80c2fc; background-color: #a0d1fc;width:100% !important;display:flex;justify-content:space-between; flex-flow:wrap;height:25px;padding:2px;">
                    <div>
                        <span style="line-height:24px;color:#399cee;font-weight:bold;font-size:12px !important;">报价产品类型</span>
                    </div>
                    <div>
                        <a class="el-icon-arrow-left" style="border: 2px solid #399cee;font-size:12px;color:black;margin-top:2px" @click="Unfold()"></a>
                        <a class="el-icon-refresh" style="border: 2px solid #399cee;font-size:12px;color:black;margin-top:2px" @click="shouzhan(treeData)"></a>
                        <a class="el-icon-arrow-down" style="border: 2px solid #399cee;font-size:12px;color:black;margin-top:2px" @click="unFoldAll2(treeData)"></a>
                        <a class="el-icon-arrow-up" style="border: 2px solid #399cee;font-size:12px;color:black;margin-top:2px" @click="collapseAll2(treeData)"></a>
                    </div>
                </el-header>
                <div style=" height: 95%;">
                    <el-scrollbar style="height: 95%;width:100%">
                        <el-tree :data="treeData"
                                 @node-click="nodeClick"
                                 node-key="value"
                                 :props="{
                                    value: 'value',
                                    label: 'label',
                                    children: 'children',
                                    isLeaf: 'leaf',
                                  }"
                                 highlight-current
                                 ref="staffTree"
                                 lazy :load="handleLoad"
                                 :default-expand-all="false"
                                 :default-expanded-keys="idArr"
                                 :expand-on-click-node="false">
                            <template #default="scope">
                                <div class="action-group">
                                    <div class="action-text"
                                         :class="{actived:data.FITEMID==selectId,'node-text':data.parentId!==0}"
                                         style="font-size:12px">
                                        <Icon v-if="data.parentId!==0"
                                              :type="data.FITEMID==selectId?'ios-radio-button-on':'ios-radio-button-off'" />
                                        {{ scope.data.label }}
                                    </div>
                                </div>
                            </template>
                        </el-tree>
                    </el-scrollbar>
                </div>
            </el-container>
        </div>
        <div class="fytypetable" style="width:100%">
            <V_QUOTE_ERPITEMTYPE ref="QuoteErp" style="width:100%"></V_QUOTE_ERPITEMTYPE>
        </div>
    </div>
</template>
<script>
    import V_QUOTE_ERPITEMTYPE from "@/views/quote/quote/V_QUOTE_ERPITEMTYPE.vue";
    var vueParam = {
        components: {
            V_QUOTE_ERPITEMTYPE
        },
        data() {
            return {
                Hide: true,
                Display: false,
                istree: true,
                //树状图
                tableData: [],
                data: [],
                treeData: [],
                openKeys: [],
                idArr: [],
                count: 1,
                res: [],

                List_ID: [],//当前选中组织架构的id及其子级的id
                //点亮图标
                selectId: -1,
            }
        },
        created() {
            //初始化一个对象全局缓存起来,当点击左边树形菜单时,把点击菜单的treeId(角色id)存到treeDemo1里
            this.$nextTick(function () {
                this.load(1);
                this.$refs.QuoteErp.$refs.grid.$refs.table.issorting = false;
                this.$refs.QuoteErp.$refs.grid.List_ID = this.List_ID.join(",");
                console.log(this.List_ID.join(","))
                this.$refs.QuoteErp.$refs.grid.refresh();
                let that = this;
                this.$refs.QuoteErp.$refs.grid.TreeLoad = function (val) {
                    that.load(1);
                }
            })
        },
        methods: {
            //隐藏显示组织架构
            Unfold() {
                this.Hide = false;
                this.Display = true;
                document.getElementsByClassName("fytypemgs")[0].style.width = "0px";
                document.getElementsByClassName("fytypetable")[0].style.width = "100%";
            },
            //显示
            Donotopen() {
                this.Display = false;
                this.Hide = true;
                document.getElementsByClassName("fytypemgs")[0].style.width = "240px";
                document.getElementsByClassName("fytypetable")[0].style.width = "85%";
            },
            //全部展开
            unFoldAll2(data) {
                let self = this;
                data.forEach((el) => {
                    self.$refs.staffTree.store.nodesMap[el.value].expanded = true;
                    el.children && el.children.length > 0
                        ? self.unFoldAll2(el.children)
                        : ""; // 子级递归
                });
            },
            // 全部折叠
            collapseAll2(data) {
                let self = this;
                data.forEach((el) => {
                    self.$refs.staffTree.store.nodesMap[el.value].expanded = false;
                    el.children && el.children.length > 0
                        ? self.collapseAll2(el.children)
                        : ""; // 子级递归
                });
            },
            //收展
            shouzhan(data) {
                let self = this;
                data.forEach((el) => {
                    self.$refs.staffTree.store.nodesMap[el.value].expanded = true;
                    el.children && el.children.length > 0
                        ? self.collapseAll2(el.children)
                        : ""; // 子级递归
                });
            },
            //通知分类树形控件绑定
            load(val) {
                let that = this;
                this.http.post("后台接口", null, "加载中")
                    .then(result => {
                        that.data = JSON.parse(result.data);
                        console.log(result)
                        console.log(that.data)
                        let data = {
                            value: 0,
                            label: "全部",
                            FMATERIALGROUPID: -1,
                            FLEVEL: -1,
                            children: that.data.map(x => {
                                return {
                                    FID: x.FID,
                                    value: x.FITEMID,
                                    label: x.FNAME,
                                    FLEVEL: x.FLEVEL,
                                    FGROUP1: x.FGROUP1,
                                    FGROUP2: x.FGROUP2,
                                    leaf: false,
                                    FMATERIALGROUPID: x.FMATERIALGROUPID,
                                }
                            })
                        }
                        this.idArr = [];
                        this.idArr.push(0);
                        this.treeData = [data];
                    });
            },
            //懒加载
            handleLoad(node, resolve) {
                let that = this;
                const { level } = node;
                console.log(level)
                if (level > 0) {
                    const url = `后台接口?parentId=${node.data.value}`;
                    this.http.post(url, null, "加载中").then(reslut => {
                        that.data = JSON.parse(reslut.data);
                        console.log(that.data)
                        if (reslut.status) {
                            let data = that.data.map(x => {
                                return {
                                    leaf: level,
                                    FID: x.FID,
                                    value: x.FITEMID,
                                    label: x.FNAME,
                                    FLEVEL: x.FLEVEL,
                                    FGROUP1: x.FGROUP1,
                                    FGROUP2: x.FGROUP2,
                                    FMATERIALGROUPID: x.FMATERIALGROUPID,
                                };
                            })
                            resolve(data)

                        }
                    })
                }
            },
            // 树节点点击事件
            nodeClick(node) {
                this.selectId = node.value; // 使用 props.value 作为唯一标识(与 node-key 一致)
                this.List_ID = []; // 清空旧数据

                // 收集当前节点及其所有已加载子节点的 ID
                this.collectChildrenIds(node);

                // 传递给表格组件(逗号分隔的 ID 字符串)
                this.$refs.QuoteErp.$refs.grid.List_ID = this.List_ID.join(",");
                console.log(this.List_ID.join(","))
                this.$refs.QuoteErp.$refs.grid.refresh();
            },

            // 递归收集子节点 ID(核心逻辑)
            collectChildrenIds(currentNode) {
                // 1. 添加当前节点 ID(使用 value 字段,与 node-key 一致)
                if (!this.List_ID.includes(currentNode.value)) {
                    this.List_ID.push(currentNode.value);
                }

                // 2. 递归遍历已加载的子节点(children 可能为空,取决于懒加载是否完成)
                if (currentNode.children && currentNode.children.length > 0) {
                    currentNode.children.forEach(childNode => {
                        this.collectChildrenIds(childNode); // 递归子节点
                    });
                }
            },
            右侧树状图点击方法
            //nodeClick(node, selected) {
            //    this.List_ID = [];
            //    console.log(node)
            //    this.selectId = node.FITEMID;
            //    this.List_ID = [node.FITEMID]; // 初始化为当前节点 ID
            //    // 递归获取所有子节点 ID(仅需遍历已加载的节点)
            //    this.collectChildrenIds(node);
            //    this.$refs.QuoteErp.$refs.grid.List_ID = this.List_ID.join(",");
            //    console.log(this.List_ID.join(","))
            //    this.$refs.QuoteErp.$refs.grid.refresh();

            //    this.List_ID.push(node.BusinessSort);
            //    //let LISTJIHE = []
            //    //this.selectId = node.FITEMID;
            //    //this.$refs.QuoteErp.$refs.grid.FITEMID = parseInt(node.FITEMID);
            //    缓存当前选中的节点
            //    //LISTJIHE.push(node)
            //    //this.CyclicNodeList(LISTJIHE)
            //    //this.$refs.QuoteErp.$refs.grid.List_ID = this.List_ID.join(',');
            //    //this.$refs.QuoteErp.$refs.grid.FID = node.FITEMID;
            //    //this.$refs.QuoteErp.$refs.grid.FID = this.selectId;
            //    //this.$refs.QuoteErp.$refs.grid.refresh();
            //},
            CyclicNodeList(data) {
                data.forEach((el) => {
                    this.List_ID.push(el.FITEMID);
                    el.children && el.children.length > 0 ? this.CyclicNodeList(el.children) : ''; // 子级递归
                });
            },
            onPositionChange(treeId) {
                //调用table(viewGird.vue)刷新方法
                this.$store.getters.data().vGStaff.nodeClick(treeId);
            },
            树形控件加载事件
            //load(val) {
            //    let that = this;
            //    this.List_ID = [];
            //    this.treeData = [];
            //    this.http.ajax2({
            //        url: "后台接口", postdata: true, success: function (result) {
            //            if (result.status) {
            //                that.data = JSON.parse(result.data);
            //                console.log(that.data)
            //                that.data.push({ FITEMID: 0, FNAME: "全部", FMATERIALGROUPID: -1 })
            //                that.data.forEach((x, index) => {
            //                    console.log(x)
            //                    if (x.FMATERIALGROUPID == -1) {
            //                        //只有页面首次加载才对选中节点改变
            //                        if (val == 1) {
            //                            that.selectId = x.FITEMID;
            //                        }
            //                        x.children = [];
            //                        that.treeData.push(x);
            //                        that.idArr.push(x.FITEMID); // 收集一级节点ID
            //                        that.getTree(x.FITEMID, x, 1);
            //                    }
            //                });

            //                that.$nextTick(function () {
            //                    that.$refs.QuoteErp.$refs.grid.List_ID = that.List_ID.join(",");
            //                    that.$refs.staffTree.setCurrentKey(that.selectId)
            //                    that.$refs.QuoteErp.$refs.grid.refresh();
            //                    that.Donotopen()
            //                })
            //            }
            //        },
            //        type: "post",
            //        async: false,
            //    });
            //},
            接收下级的值
            //getTree(val, data, index) {
            //    if (this.List_ID.indexOf(val) == -1) {
            //        this.List_ID.push(val)
            //    }
            //    this.data.forEach(q => {
            //        if (q.FMATERIALGROUPID == val) {
            //            //q.children = [];
            //            if (!data.children) data.children = [];
            //            data.children.push(q);
            //            this.getTree(q.FITEMID, q);
            //        }
            //    })
            //},
        },
    }
    export default vueParam;
</script>
<style lang="less" scoped>
    .el-scrollbar__view {
        height: 100% !important
    }

    .com-tree {
        display: flex;
        > .fytypemgs

    {
        width: 240px;
        z-index: 999;
        font-size: 14px;
    }

    /* > .City_Table {
        flex: 1;
        width: 50%
    }*/
    }
</style>

提示:

  1. 限制加载层级:在 handleLoad 中,判断当前节点的层级,只加载到指定层级,比如三级,避免加载更深层级的数据,减少请求次数和数据量。
  2. 缓存已加载的节点:避免重复加载同一节点的子节点,可以使用一个对象来记录哪些节点的子节点已经加载过,下次再展开时直接使用缓存的数据,而不是重新请求。
  3. 优化数据请求:确保每次请求只获取必要的数据,比如只传递 parentId,并且后端接口能够高效返回子节点数据,可能需要添加索引或优化查询语句。
  4. 移除 load 方法中的 getTree 调用:因为使用了懒加载,初始化时只需要加载根节点,子节点由 handleLoad 动态加载,所以 load 中的 getTree 递归获取子节点的逻辑可以移除,避免初始化时加载过多数据
  5. 原代码中的 getTree 方法在 load 中被调用,可能用于初始化子节点,但在懒加载模式下,这可能不必要,可以移除,改为完全由 handleLoad 来动态加载子节点,避免初始化时加载过多数据
<think>好的,用户想了解在Vue3和Element-Plus中使用树状图组件的方法或示例。我需要先回顾一下Element-Plus的文档,确认树形组件的基本用法和属性。记得用户提到了树状图,可能是指树形控件,即Tree组件。 首先,需要确保用户正确安装并引入了Element-Plus。根据引用[1]和[2],他们的项目是基于Vue3、Element Plus等技术栈的,所以应该已经配置好了环境。但可能需要指导用户如何单独引入ElTree组件。 接下来,基本用法部分,应该展示一个最简单的树结构,使用data属性传入数据。数据格式通常是包含label和children的数组。同时,要说明如何通过props配置字段别名,比如使用label作为节点名称,children表示子节点。 然后,用户可能需要节点选择的功能。这里需要介绍show-checkbox属性,以及如何通过v-model绑定选中的节点。此外,事件处理也很重要,比如node-click事件,可以演示如何在点击节点时触发方法。 高级功能方面,比如懒加载Element-Plus的Tree组件支持load属性,可以动态加载子节点。需要提供一个示例函数,说明如何按需加载数据。另外,自定义节点内容可以通过插槽实现,使用#default插槽来定制显示内容,比如添加图标或按钮。 引用中的项目如vue3-element-admin和vue-element-plus-admin可能在实际使用中有更复杂的例子,可以参考他们的实现方式。例如,引用[3]提到的最佳实践可能包括树形组件在后台管理系统中的应用,比如菜单管理或权限配置。这时候可能需要结合后端接口动态渲染树结构,或者实现节点的增删改查。 另外,注意用户可能需要代码示例,所以需要提供Vue3的语法,比如使用<script setup>和组合式API。同时,确保代码中的ElTree组件正确引入,并展示数据绑定的方式。 最后,相关问题部分需要考虑用户可能遇到的进一步需求,比如如何与后端API对接、如何实现拖拽排序、如何展开特定节点等。这些问题可以帮助用户深入使用树形组件。 总结步骤:安装引入组件、基本数据渲染、复选框和事件处理、懒加载、自定义内容、实际应用示例,以及相关问题的扩展。需要确保所有代码示例符合Vue3和Element-Plus的语法,并引用提供的参考资料,特别是在实际项目中的应用场景。</think>### Vue3 Element-Plus 树状图组件使用教程 #### 一、环境准备 确保已正确安装 Element-Plus(Vue3 版本): ```bash npm install element-plus ``` 在入口文件全局引入: ```javascript import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' const app = createApp(App) app.use(ElementPlus) app.mount('#app') ``` #### 二、基础树形结构 ```html <template> <el-tree :data="treeData" /> </template> <script setup> const treeData = [ { label: '一级节点', children: [ { label: '二级节点-1' }, { label: '二级节点-2' } ] } ] </script> ``` *通过 `data` 属性传递树形数据,默认使用 `label` 显示节点名称,`children` 表示子节点[^2]* #### 三、核心功能实现 1. **节点选择与事件** ```html <el-tree :data="treeData" show-checkbox node-key="id" :props="defaultProps" @node-click="handleNodeClick" v-model:checked-keys="checkedKeys" /> <script setup> const defaultProps = { children: 'children', label: 'label' } const handleNodeClick = (nodeData) => { console.log('点击节点:', nodeData) } </script> ``` 2. **懒加载模式** ```html <el-tree :props="lazyProps" :load="loadNode" lazy /> <script setup> const lazyProps = { label: 'name', isLeaf: 'leaf' } const loadNode = (node, resolve) => { if (node.level === 0) { return resolve([{ name: '根节点' }]) } // 模拟异步加载 setTimeout(() => { resolve([{ name: '动态子节点', leaf: true }]) }, 500) } </script> ``` #### 四、高级功能示例 1. **自定义节点内容** ```html <el-tree :data="treeData"> <template #default="{ node }"> <span class="custom-node"> <el-icon><document /></el-icon> {{ node.label }} <el-button size="mini" @click="appendNode(node)">+</el-button> </span> </template> </el-tree> ``` 2. **完整功能示例** ```html <template> <el-tree ref="treeRef" :data="data" show-checkbox default-expand-all :expand-on-click-node="false" > <template #default="{ node, data }"> <span>{{ node.label }}</span> <el-button-group> <el-button size="small" @click="append(data)">添加</el-button> <el-button size="small" @click="remove(node, data)">删除</el-button> </el-button-group> </template> </el-tree> </template> <script setup> import { ref } from 'vue' const treeRef = ref() const data = ref([...]) // 树形数据结构 const append = (data) => { if (!data.children) data.children = [] data.children.push({ label: '新节点' }) } const remove = (node, data) => { const parent = node.parent const children = parent.data.children || parent.data const index = children.findIndex(d => d === data) children.splice(index, 1) } </script> ``` #### 五、实际应用场景 在后台管理系统中,树状图常用于: 1. 权限管理系统中的多级权限分配 2. 组织架构展示(部门-子部门结构) 3. 文件目录管理系统 4. 分类多级选择器(如商品分类)[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值