element-ui导航菜单(el-menu)实现树形结构侧边栏


前言

提示:
一般侧边菜单数据都是静态、少量数据。如何实现动态可配置?
可以通过接口返回的树形结构数据动态更新菜单条目


1、先上效果图

  • 树形结构数据(模拟数据,实际开发中通过后台接口获取即可)
[
    {
        "nodeid": "777718a5-18a8-4955-928a-64b683513590",
        "parentid": "",
        "nodename": "目录1",
        "children": [
            {
                "nodeid": "b278006e-cb28-4459-8f1a-e42202a96ce8",
                "parentid": "777718a5-18a8-4955-928a-64b683513590",
                "nodename": "目录1-1"
            }
        ]
    },
    {
        "nodeid": "c47d3f14-9b78-46fc-ad76-52e5d4df2ef7",
        "parentid": "",
        "nodename": "目录2",
        "children": [
            {
                "nodeid": "7c913e5d-0ec0-4152-b183-b001d6d92ed8",
                "parentid": "c47d3f14-9b78-46fc-ad76-52e5d4df2ef7",
                "nodename": "目录2-1"
            },
            {
                "nodeid": "f1844722-ecaa-4e10-86f1-b64e3352d5ea",
                "parentid": "c47d3f14-9b78-46fc-ad76-52e5d4df2ef7",
                "nodename": "目录2-2"
            },
            {
                "nodeid": "ffff9811-ac84-41d9-b4d6-bcb98bf0196f",
                "parentid": "c47d3f14-9b78-46fc-ad76-52e5d4df2ef7",
                "nodename": "目录2-3"
            }
        ]
    },
    {
        "nodeid": "f59286d3-1fdc-4b7d-8fa3-c5df26e86e32",
        "parentid": "",
        "nodename": "目录3",
        "children": [
            {
                "nodeid": "127eb83b-2c58-4313-96ee-aa35c01b07e6",
                "parentid": "f59286d3-1fdc-4b7d-8fa3-c5df26e86e32",
                "nodename": "目录3-1",
                "children": [
                    {
                        "nodeid": "4749d51d-db82-4302-8f99-3c51bd1bd093",
                        "parentid": "127eb83b-2c58-4313-96ee-aa35c01b07e6",
                        "nodename": "目录3-1-1"
                    },
                    {
                        "nodeid": "f73a4b82-cc2a-4b5e-8015-c28478718fca",
                        "parentid": "127eb83b-2c58-4313-96ee-aa35c01b07e6",
                        "nodename": "目录3-1-2"
                    },
                    {
                        "nodeid": "9d38b195-e5d8-43c5-8c75-71ef52a1e1ec",
                        "parentid": "127eb83b-2c58-4313-96ee-aa35c01b07e6",
                        "nodename": "目录3-1-3"
                    }
                ]
            }
        ]
    },
    {
        "nodeid": "f4872c95-9e15-4d9a-81f8-4c04c7b275a8",
        "parentid": "",
        "nodename": "目录4",
        "children": [
            {
                "nodeid": "4bbbb72c-848a-4046-97fe-40939a335299",
                "parentid": "f4872c95-9e15-4d9a-81f8-4c04c7b275a8",
                "nodename": "目录4-1"
            },
            {
                "nodeid": "ccb75eba-1355-457a-8c67-a53af258e024",
                "parentid": "f4872c95-9e15-4d9a-81f8-4c04c7b275a8",
                "nodename": "目录4-2"
            },
            {
                "nodeid": "bec8cc74-84e7-4909-8cdc-9650aabf75ad",
                "parentid": "f4872c95-9e15-4d9a-81f8-4c04c7b275a8",
                "nodename": "目录4-3"
            },
            {
                "nodeid": "b7c6e31b-ce62-40a7-83d0-0363d48ad0a5",
                "parentid": "f4872c95-9e15-4d9a-81f8-4c04c7b275a8",
                "nodename": "目录4-4"
            }
        ]
    },
    {
        "nodeid": "5e54c8f3-502a-4bed-8542-1e7208d18dad",
        "parentid": "",
        "nodename": "目录5",
        "children": [
            {
                "nodeid": "4264fae0-e7e9-4d85-826e-f195e178704a",
                "parentid": "5e54c8f3-502a-4bed-8542-1e7208d18dad",
                "nodename": "目录5-1"
            },
            {
                "nodeid": "01d8e744-8b29-42e1-8f59-829dcf773644",
                "parentid": "5e54c8f3-502a-4bed-8542-1e7208d18dad",
                "nodename": "目录5-2"
            }
        ]
    }
]

  • 效果
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200928112855284.png#pic_cente

2、具体实现

  • 使用element-ui 导航菜单组件NavMenu ,递归实现。

2.1 定义Menu组件

  • 思路 :
    1、递归,如果数据元素存在子树(children),调用Menu组件自身,传递子树数据(item.children)。
    2、 有子树用el-submenu,反之用 el-menu-item 。
<template>
    <div>
        <div v-for="(item, index) in menuData" :key="index">
            <!-- 思路: 有子元素使用el-submenu 没有子元素使用el-menu-item -->
            <el-submenu :index="item.nodeid" v-if="item.children && item.children.length > 0">
                <template slot="title">
                    {{ item.nodename }}
                </template>
                //递归调用
                <Menu :menuData="item.children"></Menu>
            </el-submenu>
            <el-menu-item :index="item.nodeid" v-else>
                {{ item.nodename }}
            </el-menu-item>
        </div>
    </div>
</template>

<script>
export default {
    //递归 这个名称要保持驼峰转短横线链接规则
    name: "Menu",
    props: {
        menuData: {
            type: Array,
            default: () => {
                return [];
            }
        }
    },
    components: {},
    data() {
        return {};
    },
    created() {},
    mounted() {},
    methods: {}
};
</script>
<style lang='less' scoped>
</style>

2.2 定义Slider组件,及树结构数据处理

<template>
    <div style="width:200px;height:100%">
        <el-menu
            :default-active="defaultActive"
            :default-openeds="openeds"
            class="el-menu-vertical-demo"
            @open="handleOpen"
            @close="handleClose"
        >
            <Menu :menuData="menuData"></Menu>
        </el-menu>
    </div>
</template>

<script>
import Menu from "@/views/elementUi/pages/Menu";
import { uuid } from "@/common/utils.js";
export default {
    name: "elSlider",
    components: { Menu },
    data() {
        return {
            menuData: [],
            nodePathData: [] //节点路径数据
        };
    },
    created() {},
    computed: {
        //默认展开的子菜单
        openeds() {
            let openedList = [];
            let resList = this.nodePathData.filter(el => {
                return el.some(item => {
                    return item.nodename === "目录3-1-2";
                });
            });
            resList = resList[0];
            if (resList) {
                resList.forEach(item => {
                    openedList.unshift(item.nodeid);
                });
            }
            console.log(JSON.stringify(openedList));
            return openedList;
        },
        //默认激活的子菜单
        defaultActive() {
            return this.openeds[this.openeds.length - 1];
        }
    },

    mounted() {
        this.$axios.get("JsonFile/opitions.json").then(res => {
            this.menuData = res.data;

            console.time("x");
			let nodeData = this.getNodeMsg(this.menuData, "目录3-1-2", "", []);
			console.log('节点数据:',JSON.stringify(nodeData));
            this.nodePathData = this.getAllNodePath(this.menuData, nodeData);
            console.log(JSON.stringify(this.nodePathData));
            console.timeEnd("x");
        });
    },
    methods: {
        handleOpen(key, keyPath) {
            console.log(key, keyPath);
        },
        handleClose(key, keyPath) {
            console.log(key, keyPath);
        },
        /**
         * @author: DuHui
         * @description:通过节点名称 递归获取节点信息
         * @param {array} data   树结构数据
         * @param {string} nodename  结点名称
         * @param {string} nodeid   节点id
         * @param {array}  result 查询结果,可能会查到多个节点 格式见return
         * @returns {array}
         *  [
         *   {
         *       nodename,       //节点名称 通过名称查找结果不唯一
         *       nodeid,         //节点id   通过id查找结果唯一
         *       parentId,       //父节点id
         *   }
         *   ...
         *  ]
         */
        getNodeMsg(data, nodename, nodeid, result) {
            data.forEach(item => {
                if (item.nodename === nodename || item.nodeid === nodeid) {
                    let { nodename, nodeid, parentid } = item;
                    result.push({ nodename, nodeid, parentid });
                }
                if (item.children && item.children.length > 0) {
                    this.getNodeMsg(item.children, nodename, nodeid, result);
                }
            });
            return result;
        },
        /**
         * @author: DuHui
         * @description: 获取节点路径:通过节点parentid找父节点,再通过父节点找祖父节点,以此类推
         * @param {array}  data 树形结构数据
         * @param {object}  node 当前节点对象
         * @param {array}  result 含有当前节点的数组
         * @returns {array} 当前节点的全路径节点数组
         */
        getNodeFullPath(data, node, result) {
            //查找当前节点的父节点,
            let parent = this.getNodeMsg(data, "", node.parentid, []);
            result.push(parent[0]);
            if (parent[0] && parent[0].parentid) {
                this.getNodeFullPath(data, parent[0], result);
            }
            return result;
        },
        /**
         * @author: DuHui
         * @description: 一个或多个节点得全路径
         * @param {array} data 原始树形结构数据
         * @param {array} nodeData 节点数据集含parentid
         * @returns {array}
         */
        getAllNodePath(data, nodeData) {
            let result = [];
            nodeData.forEach(item => {
                //查找当前节点的全路径
                let path = this.getNodeFullPath(data, item, [item]);
                result.push(path);
            });
            return result;
        }
    }
};
</script>
<style lang='less' scoped>
</style>

  • 详解:
    1、定义Slider组件,引入上面定义Menu组件,并通过 props 传递树结构数据。
    2、getNodeMsg(data, nodename, nodeid, result):通过nodename或nodeid,递归获取当前结点数据。如获取"目录3-1-2",结果:

[{“nodename”:“目录3-1-2”,“nodeid”:“f73a4b82-cc2a-4b5e-8015-c28478718fca”,“parentid”:“127eb83b-2c58-4313-96ee-aa35c01b07e6”}]

       3、getNodeFullPath(data, node, result):获取当前节点到根节点的路径。含有每个节点基本数据。如下:

[[{“nodename”:“目录3-1-2”,“nodeid”:“f73a4b82-cc2a-4b5e-8015-c28478718fca”,“parentid”:“127eb83b-2c58-4313-96ee-aa35c01b07e6”},{“nodename”:“目录3-1”,“nodeid”:“127eb83b-2c58-4313-96ee-aa35c01b07e6”,“parentid”:“f59286d3-1fdc-4b7d-8fa3-c5df26e86e32”},{“nodename”:“目录3”,“nodeid”:“f59286d3-1fdc-4b7d-8fa3-c5df26e86e32”,“parentid”:""}]]

       4、getAllNodePath(data, nodeData) 通过nodename查到节点数据可能存在重复情况,通过循环获取所有满足节点路径即可。

 

3、激活菜单和展开菜单

在这里插入图片描述

3.1 激活菜单

  • default-acivite: 激活当前菜单。设置后高亮并展开当前节点
    在这里插入图片描述
  • 效果
    在这里插入图片描述

3.2 展开菜单

  • default-openeds :设置默认展开菜单。这里default-openeds
    是一个key数组,就是在组件中设置的Index值(我们index绑定值为nodeid)。
    没有顺序要求,但是节点不能中断,含有其父节点、祖父节点、一直到根节点
    怎么理解? 比如要你要展开3-1-2,那么也要添加3-1 和 3 这两个节点,不能只设置3-1-2。
    如下:
    在这里插入图片描述
  • 效果
    在这里插入图片描述

3.3 动态实现。

       3.1 、3.2 为了了解组件展开机制,使用了静态数据演示,方便理解其组件内部原理。那么如何动态实现?
       到这里就很简单了 ,2.2中getNodeFullPath和getAllNodePath 这两个方法我们已经可以获取到节点路径,使用vue计算属性,做简单计算即可。
在这里插入图片描述

  • 代码实现
 computed: {
        //默认展开的子菜单
        openeds() {
            let openedList = [];
            let resList = this.nodePathData.filter(el => {
                return el.some(item => {
                    return item.nodename === "目录3-1-2";  //要展开的节点
                });
            });
            resList = resList[0];
            if (resList) {
                resList.forEach(item => {
                    openedList.unshift(item.nodeid);
                });
            }
            console.log(JSON.stringify(openedList));
            return openedList;
        },
        //默认激活的子菜单
        defaultActive() {
            return this.openeds[this.openeds.length - 1]; 
        }
    },

4、总结

每天记录一点,从小小菜鸟变小菜鸟!!!
  • 18
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
要解决element ui el-menu侧边栏乱码的问题,你可以尝试以下几个步骤: 1. 确保你已经正确引入了Element UI组件库并且样式文件已经被正确加载。根据引用\[2\]中的代码,在main.js文件中导入Element UI并引入样式文件。确保路径正确,样式文件被正确加载。 2. 检查你的页面编码是否正确设置为UTF-8。在HTML文件的<head>标签中添加<meta charset="UTF-8">来确保页面使用UTF-8编码。 3. 确保你的字体文件被正确加载。Element UI的样式中使用了一些特定的字体图标,如果字体文件没有正确加载,可能会导致乱码。你可以在浏览器的开发者工具中检查网络请求,确保字体文件被正确加载。 4. 如果以上步骤都没有解决问题,你可以尝试在el-menu标签中添加一个属性:collapse-transition,设置为false。这个属性可以解决一些特定情况下的乱码问题。 希望以上方法能够帮助你解决element ui el-menu侧边栏乱码的问题。如果问题仍然存在,请提供更多的细节和代码,以便我们能够更好地帮助你解决问题。 #### 引用[.reference_title] - *1* *2* *3* [谷粒商城——第一篇 前后端基础](https://blog.csdn.net/qwqgood/article/details/125572140)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hui-1018

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值