当前位置:   article > 正文

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

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"
            }
        ]
    }
]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
'
运行
  • 效果
    ![在这里插入图片描述](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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 详解:
    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]; 
        }
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

4、总结

每天记录一点,从小小菜鸟变小菜鸟!!!
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/971885
推荐阅读
相关标签
  

闽ICP备14008679号