今天小编给大家分享一下怎么使用el-menu递归实现多级菜单组件的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
1. 效果:
2. 实现:
创建外层菜单AsideMenu.vue
组件和子菜单项AsideSubMenu.vue
组件,在AsideSubMenu
中进行递归操作。
AsideMenu.vue文件内容如下:
<template> <aside class="wrap"> <el-menu :default-active="activeMenu" router :class="'menu-left'" :default-openeds="openedsArr" text-color="#fff" > <AsideSubMenu :menuData="menuData"></AsideSubMenu> </el-menu> </aside></template> <script>import AsideSubMenu from "./AsideSubMenu.vue";export default { name: "AsideMenu", components: { AsideSubMenu, }, props: { menuData: { type: Array, }, }, computed: { activeMenu() { const route = this.$route; const { meta, path } = route; // 此处添加判断的原因见说明 if (meta.matchPath) { return meta.matchPath; } else { return path; } }, // 设置默认展开菜单项 openedsArr() { // const arr = this.menuData.map((item) => { // return item.title; // }); // return arr; return []; }, },};</script>
判断高亮状态的activeMenu方法中的判断matchPath属性可以让多个路由不同的页面匹配同一个菜单高亮状态,因为菜单高亮状态是根据路由地址匹配的。如果两个不同的路由页面想公用同一个菜单高亮状态(如详情页面和列表页)就可以使用该方法实现。在router文件里设置meta对象,添加matchPath属性设置为想要共用的高亮状态的页面的路由地址。(有点绕????)
样式根据需求修改,示例中的样式如下(此处包含深浅两种主题的菜单样式):
<style scoped>aside { height: 100%; text-align: center;}.el-menu { padding: 16px; box-sizing: border-box;}.menu-left,.menu-left /deep/ .el-menu { min-height: 100%; background-color: #222653;}.menu-left .icon { width: 20px; margin-right: 9px;}.menu-left /deep/.el-submenu__title,.menu-left /deep/.el-menu-item { box-sizing: border-box; font-size: 14px; font-family: PingFangSC-Regular, PingFang SC; text-align: left; color: #b3c0e7 !important; background-color: #222653 !important;}.menu-left /deep/.el-submenu__title i { color: #b3c0e7 !important;}.menu-left /deep/.el-submenu__title { height: 54px; line-height: 54px; }.menu-left /deep/ .el-menu-item { height: 52px; line-height: 52px;}.menu-left /deep/.el-submenu .el-menu-item { padding-left: 45px !important;}.menu-left /deep/.el-submenu.is-active,.menu-left /deep/.el-submenu.is-active .el-menu-item,.menu-left /deep/.el-submenu.is-active .el-submenu__title,.menu-left /deep/.el-menu-item.is-active { background-color: #4880ff !important;}.menu-left /deep/.el-submenu.is-active { border-radius: 10px; overflow: hidden;}.menu-left /deep/ .el-menu-item.is-active { border-radius: 10px;}.menu-left /deep/ .el-menu--inline .el-menu-item.is-active,.menu-left /deep/ .el-submenu.noIcon { border-radius: 0;}.menu-left /deep/ .el-submenu.noIcon .el-submenu__title { padding-left: 45px !important;}.menu-left /deep/ .el-submenu.noIcon .el-menu-item { padding-left: 58px !important;}.menu-left /deep/.el-submenu.is-active > .el-submenu__title,.menu-left /deep/.el-submenu.is-active > .el-submenu__title i { color: #ffffff !important;}.menu-left /deep/.el-menu-item:focus,.menu-left /deep/.el-menu-item:hover,.menu-left /deep/.el-menu-item.is-active { color: #ffffff !important; font-weight: 500;}.menu-left /deep/.el-menu-item.is-disabled { padding-left: 45px !important; color: #ffffff !important;} .menu-left-light { height: 100%; background-color: #f8f8f8;}.menu-left-light .icon { width: 20px; margin-right: 9px;}.menu-left-light /deep/.el-submenu__title,.menu-left-light /deep/.el-menu-item { box-sizing: border-box; font-size: 16px; font-family: PingFangSC-Regular, PingFang SC; text-align: left; color: #333333;}.menu-left-light /deep/.el-submenu__title { height: 52px; line-height: 52px; padding-left: 56px !important;}.menu-left-light /deep/.el-submenu__title:hover { background-color: #ffffff !important;}.menu-left-light /deep/.el-submenu__icon-arrow { right: 85px;}.menu-left-light /deep/.el-submenu__title i { color: #333333;}.menu-left-light /deep/.el-menu-item { height: 40px; line-height: 40px; padding-left: 82px !important; border-left: 4px solid #ffffff;}.menu-left-light /deep/.el-menu-item:focus,.menu-left-light /deep/.el-menu-item:hover,.menu-left-light /deep/.el-menu-item.is-active { background: rgba(31, 65, 219, 0.1) !important; color: #1f41db !important; border-color: #1f41db;}.menu-left-light /deep/.el-menu-item.is-disabled { background: #ffffff !important; color: #333333 !important;}</style>
AsideMenu.vue文件内容如下:
<template> <div> <template v-for="item in menuData"> <el-submenu :key="item.path" v-if="item.children && item.children.length > 0" :index="item.path" :class="item.icon ? '' : 'noIcon'" > <template slot="title"> <img class="icon mr-r-10" :src=" curRoute.indexOf(item.path) != -1 ? item.iconActive : item.icon " /> <span>{{ item.title }}</span> </template> <AsideSubMenu :menuData="item.children"></AsideSubMenu> </el-submenu> <el-menu-item :key="item.id" v-else :index="item.path" :disabled="item.disabled" > <template slot="title"> <img class="icon mr-r-10" :src=" curRoute.indexOf(item.path) != -1 ? item.iconActive : item.icon " /> <span>{{ item.title }}</span> </template> </el-menu-item> </template> </div></template>
判断如果有子菜单则进行遍历操作。同时此处根据是否有icon给el-submenu
动态添加了一个类名:class="item.icon ? '' : 'noIcon'"
,这么做是由于高亮状态下的.el-submenu添加了圆角效果,在存在多层子菜单嵌套的情况下如果不清除圆角效果则会出现问题(见下图)。这个状态下不好用选择器选中需要操作的元素,因此根据是否有icon这个区别进行了区分。如果是整个菜单都没有icon的情况的话,那暂时还没想好应对策略。????
<script>import AsideSubMenu from "./AsideSubMenu.vue";export default { name: "AsideSubMenu", components: { AsideSubMenu, }, props: { menuData: { type: Array, default: () => { return []; }, }, }, computed: { curRoute() { return this.$route.path; }, },};</script>
3. 使用组件:
添加路由配置;
引入并挂载组件;
传入菜单数据;
代码如下:
<template> <el-container class="container"> <el-aside width="320px"> <AsideMenu :menuData="menuData"></AsideMenu> </el-aside> <el-main> <keep-alive :exclude="[]"> <router-view></router-view> </keep-alive> </el-main> </el-container></template> <script>import AsideMenu from '@/components/AsideMenu.vue';export default { name: 'MenuTest', components: { AsideMenu }, data() { return { menuData: [ { title: '菜单一', path: '/menutest/menu1', icon: require('@/assets/icons/apply.svg'), iconActive: require('@/assets/icons/apply_active.svg'), children: [ { title: '子菜单一', path: '/menutest/menu1/menu1-1', // disabled: true, }, { title: '子菜单二', path: '/menutest/menu1/menu1-2' } ] }, { title: '菜单二', path: '/menutest/menu2', icon: require('@/assets/icons/apply.svg'), iconActive: require('@/assets/icons/apply_active.svg'), children: [ { title: '子菜单一', path: '/menutest/menu2/menu2-1' }, { title: '子菜单二', path: '/menutest/menu2/menu2-2', children: [ { title: '孙子菜单一', path: '/menutest/menu2/menu2-2/menu2-1-1' }, { title: '孙子菜单二', path: '/menutest/menu2/menu2-2/menu2-2-2' } ] }, { title: '子菜单三', path: '/menutest/menu2/menu2-3' } ] }, { title: '菜单三', path: '/menutest/menu3', icon: require('@/assets/icons/apply.svg'), iconActive: require('@/assets/icons/apply_active.svg'), } ] }; }};</script> <style scoped>.container { min-height: 800px;}.el-main { padding: 32px 40px; box-sizing: border-box; background: #f5f5fa; overflow-y: auto;}</style>
示例中的路由配置如下:
{ path: "/menutest", name: "Menu", component: () => import("../views/MenuTest.vue"), redirect: "/menutest/menu1", children: [{ path: '/menutest/menu1', component: () => import("../views/menuPages/menu1.vue"), children: [{ path: '/menutest/menu1/menu1-1', component: () => import("../views/menuPages/menu1-1.vue"), }, { path: '/menutest/menu1/menu1-2', component: () => import("../views/menuPages/menu1-2.vue"), } ] }, { path: '/menutest/menu2', component: () => import("../views/menuPages/menu2.vue"), children: [{ path: '/menutest/menu2/menu2-1', component: () => import("../views/menuPages/menu2-1.vue"), }, { path: '/menutest/menu2/menu2-2', component: () => import("../views/menuPages/menu2-2.vue"), children: [{ path: '/menutest/menu2/menu2-2/menu2-1-1', component: () => import("../views/menuPages/menu2-1-1.vue"), }, { path: '/menutest/menu2/menu2-2/menu2-2-2', component: () => import("../views/menuPages/menu2-1-2.vue"), } ] }, { path: '/menutest/menu2/menu2-3', component: () => import("../views/menuPages/menu2-3.vue"), } ] }, { path: '/menutest/menu3', component: () => import("../views/menuPages/menu3.vue"), } ] }
以上就是“怎么使用el-menu递归实现多级菜单组件”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。