1

el-menu使用递归组件实现多级菜单组件 - Li_pk

 1 year ago
source link: https://www.cnblogs.com/lpkshuai/p/17310220.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1. 效果:

image
image
image

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; /* padding-left: 36px !important; */}.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的情况的话,那暂时还没想好应对策略。😂

image
<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"), } ] }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK