
在日常開發中,項目中的菜單欄都是已經實作好了的。如果需要添加新的菜單,隻需要在`路由配置`中新增一條路由,就可以實作菜單的添加。
相信大家和我一樣,有時候會躍躍欲試自己去實作一個菜單欄。那今天我就将自己實作的菜單欄的整個思路和代碼分享給大家。

作者:小洋芋biubiubiu
部落格園:https://www.cnblogs.com/HouJiao/
掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d
微信公衆号:洋芋媽的碎碎念(掃碼關注,一起吸貓,一起聽故事,一起學習前端技術)
作者文章的内容均來源于自己的實踐,如果覺得有幫助到你的話,可以點贊給個鼓勵或留下寶貴意見
前言
在日常開發中,項目中的菜單欄都是已經實作好了的。如果需要添加新的菜單,隻需要在
路由配置
中新增一條路由,就可以實作菜單的添加。
相信大家和我一樣,有時候會躍躍欲試自己去實作一個菜單欄。那今天我就将自己實作的菜單欄的整個思路和代碼分享給大家。
本篇文章重在總結和分享菜單欄的一個,
遞歸實作方式
、
代碼的優化
菜單權限
等不在本篇文章範圍之内,在文中的相關部分也會做一些提示,有個别不推薦的寫法希望大家不要參考哦。
同時可能會存在一些細節的功能沒有處理或者沒有提及到,忘知曉。
最終的效果
本次實作的這個菜單欄包含有
一級菜單
、
二級菜單
和
三級菜單
這三種類型,基本上已經可以覆寫項目中不同的菜單需求。
後面會一步一步從易到難去實作這個菜單。
簡單實作
我們都知道到
element
提供了
NavMenu
導航菜單元件,是以我們直接按照文檔将這個菜單欄做一個簡單的實作。
基本的布局架構圖如下:
菜單首頁-menuIndex
首先要實作的是
菜單首頁
這個元件,根據前面的布局架構圖并且參考官方文檔,實作起來非常簡單。
<!-- src/menu/menuIndex.vue -->
<template>
<div id="menu-index">
<el-container>
<el-header>
<TopMenu :logoPath="logoPath" :name="name"></TopMenu>
</el-header>
<el-container id="left-container">
<el-aside width="200px">
<LeftMenu></LeftMenu>
</el-aside>
<el-main>
<router-view/>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
import LeftMenu from './leftMenu';
import TopMenu from './topMenu';
export default {
name: 'MenuIndex',
components: {LeftMenu, TopMenu},
data() {
return {
logoPath: require("../../assets/images/logo1.png"),
name: '員工管理系統'
}
}
}
</script>
<style lang="scss">
#menu-index{
.el-header{
padding: 0px;
}
}
</style>
頂部菜單欄-topMenu
頂部菜單欄主要就是一個
logo
和
産品名稱
。
邏輯代碼也很簡單,我直接将代碼貼上。
<!-- src/menu/leftMenu.vue -->
<template>
<div id="top-menu">
<img class="logo" :src="logoPath" />
<p class="name">{{name}}</p>
</div>
</template>
<script>
export default {
name: 'topMenu',
props: ['logoPath', 'name']
}
</script>
<style lang="scss" scoped>
$topMenuWidth: 80px;
$logoWidth: 50px;
$bg-color: #409EFF;
$name-color: #fff;
$name-size: 18px;
#top-menu{
height: $topMenuWidth;
text-align: left;
background-color: $bg-color;
padding: 20px 20px 0px 20px;
.logo {
width: $logoWidth;
display: inline-block;
}
.name{
display: inline-block;
vertical-align: bottom;
color: $name-color;
font-size: $name-size;
}
}
</style>
這段代碼中包含了
父元件
傳遞給
子元件
的兩個資料。
props: ['logoPath', 'name']
這個是父元件
menuIndex
傳遞給子元件
topMenu
的兩個資料,分别是
logo圖示的路徑
和
産品名稱
。
完成後的界面效果如下。
左側菜單欄-leftMenu
首先按照官方文檔實作一個簡單的菜單欄。
<!-- src/menu/leftMenu.vue -->
<template>
<div id="left-menu">
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
:collapse="false">
<el-menu-item index="1">
<i class="el-icon-s-home"></i>
<span slot="title">首頁</span>
</el-menu-item>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-user-solid"></i>
<span slot="title">員工管理</span>
</template>
<el-menu-item index="2-1">員工統計</el-menu-item>
<el-menu-item index="2-2">員工管理</el-menu-item>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-s-claim"></i>
<span slot="title">考勤管理</span>
</template>
<el-menu-item index="3-1">考勤統計</el-menu-item>
<el-menu-item index="3-2">考勤清單</el-menu-item>
<el-menu-item index="3-2">異常管理</el-menu-item>
</el-submenu>
<el-submenu index="4">
<template slot="title">
<i class="el-icon-location"></i>
<span slot="title">工時管理</span>
</template>
<el-menu-item index="4-1">工時統計</el-menu-item>
<el-submenu index="4-2">
<template slot="title">工時清單</template>
<el-menu-item index="4-2-1">選項一</el-menu-item>
<el-menu-item index="4-2-2">選項二</el-menu-item>
</el-submenu>
</el-submenu>
</el-menu>
</div>
</template>
<script>
export default {
name: 'LeftMenu'
}
</script>
<style lang="scss">
// 使左邊的菜單外層的元素高度充滿螢幕
#left-container{
position: absolute;
top: 100px;
bottom: 0px;
// 使菜單高度充滿螢幕
#left-menu, .el-menu-vertical-demo{
height: 100%;
}
}
</style>
注意菜單的樣式代碼,設定了,并且設定
絕對定位
、
top
使菜單高度撐滿螢幕。
bottom
此時在看下界面效果。
基本上算是實作了一個簡單的菜單布局。
不過在實際項目在設計的時候,菜單欄的内容有可能來自後端給我們傳回的資料,其中包含
菜單名稱
、
菜單圖示
以及
菜單之間的層級關系
。
總而言之,我們的菜單是動态生成的,而不是像前面那種固定的寫法。是以下面我将實作一個動态生成的菜單,菜單的資料來源于我們的
路由配置
。
結合路由配置實作動态菜單
路由配置
首先,我将項目的路由配置代碼貼出來。
import Vue from 'vue';
import Router from "vue-router";
// 菜單
import MenuIndex from '@/components/menu/menuIndex.vue';
// 首頁
import Index from '@/components/homePage/index.vue';
// 人員統計
import EmployeeStatistics from '@/components/employeeManage/employeeStatistics.vue';
import EmployeeManage from '@/components/employeeManage/employeeManage.vue'
// 考勤
// 考勤統計
import AttendStatistics from '@/components/attendManage/attendStatistics';
// 考勤清單
import AttendList from '@/components/attendManage/attendList.vue';
// 異常管理
import ExceptManage from '@/components/attendManage/exceptManage.vue';
// 工時
// 工時統計
import TimeStatistics from '@/components/timeManage/timeStatistics.vue';
// 工時清單
import TimeList from '@/components/timeManage/timeList.vue';
Vue.use(Router)
let routes = [
// 首頁(儀表盤、快速入口)
{
path: '/index',
name: 'index',
component: MenuIndex,
redirect: '/index',
meta: {
title: '首頁', // 菜單标題
icon: 'el-icon-s-home', // 圖示
hasSubMenu: false, // 是否包含子菜單,false 沒有子菜單;true 有子菜單
},
children:[
{
path: '/index',
component: Index
}
]
},
// 員工管理
{
path: '/employee',
name: 'employee',
component: MenuIndex,
redirect: '/employee/employeeStatistics',
meta: {
title: '員工管理', // 菜單标題
icon: 'el-icon-user-solid', // 圖示
hasSubMenu: true, // 是否包含子菜單
},
children: [
// 員工統計
{
path: 'employeeStatistics',
name: 'employeeStatistics',
meta: {
title: '員工統計', // 菜單标題,
hasSubMenu: false // 是否包含子菜單
},
component: EmployeeStatistics,
},
// 員工管理(增删改查)
{
path: 'employeeManage',
name: 'employeeManage',
meta: {
title: '員工管理', // 菜單标題
hasSubMenu: false // 是否包含子菜單
},
component: EmployeeManage
}
]
},
// 考勤管理
{
path: '/attendManage',
name: 'attendManage',
component: MenuIndex,
redirect: '/attendManage/attendStatistics',
meta: {
title: '考勤管理', // 菜單标題
icon: 'el-icon-s-claim', // 圖示
hasSubMenu: true, // 是否包含子節點,false 沒有子菜單;true 有子菜單
},
children:[
// 考勤統計
{
path: 'attendStatistics',
name: 'attendStatistics',
meta: {
title: '考勤統計', // 菜單标題
hasSubMenu: false // 是否包含子菜單
},
component: AttendStatistics,
},
// 考勤清單
{
path: 'attendList',
name: 'attendList',
meta: {
title: '考勤清單', // 菜單标題
hasSubMenu: false // 是否包含子菜單
},
component: AttendList,
},
// 異常管理
{
path: 'exceptManage',
name: 'exceptManage',
meta: {
title: '異常管理', // 菜單标題
hasSubMenu: false // 是否包含子菜單
},
component: ExceptManage,
}
]
},
// 工時管理
{
path: '/timeManage',
name: 'timeManage',
component: MenuIndex,
redirect: '/timeManage/timeStatistics',
meta: {
title: '工時管理', // 菜單标題
icon: 'el-icon-message-solid', // 圖示
hasSubMenu: true, // 是否包含子菜單,false 沒有子菜單;true 有子菜單
},
children: [
// 工時統計
{
path: 'timeStatistics',
name: 'timeStatistics',
meta: {
title: '工時統計', // 菜單标題
hasSubMenu: false // 是否包含子菜單
},
component: TimeStatistics
},
// 工時清單
{
path: 'timeList',
name: 'timeList',
component: TimeList,
meta: {
title: '工時清單', // 菜單标題
hasSubMenu: true // 是否包含子菜單
},
children: [
{
path: 'options1',
meta: {
title: '選項一', // 菜單标題
hasSubMenu: false // 是否包含子菜單
},
},
{
path: 'options2',
meta: {
title: '選項二', // 菜單标題
hasSubMenu: false // 是否包含子菜單
},
},
]
}
]
},
];
export default new Router({
routes
})
在這段代碼的最開始部分,我們引入了需要使用的元件,接着就對路由進行了配置。
此處使用了直接引入元件的方式,項目開發中這種寫法,應該使用
不推薦
的方式
懶加載
路由配置除了最基礎的
path
、
component
以及
children
之外,還配置了一個
meta
資料項。
meta: {
title: '工時管理', // 菜單标題
icon: 'el-icon-message-solid', // 圖示
hasSubMenu: true, // 是否包含子節點,false 沒有子菜單;true 有子菜單
}
meta
資料包含的配置有
菜單标題
(
title
)、
圖示的類名
(
icon
)和
是否包含子節點
(
hasSubMenu
)。
根據
title
、
icon
這兩個配置項,可以展示目前菜單的
标題
和
圖示
。
hasSubMenu
表示目前的菜單項是否有子菜單,如果目前菜單包含有子菜單(
hasSubMenu
為
true
),那目前菜單對應的标簽元素就是
el-submenu
;否則目前菜單對應的菜單标簽元素就是
el-menu-item
。
是否包含子菜單是一個非常關鍵的邏輯,我在實作的時候是直接将其配置到了 meta.hasSubMenu
這個參數裡面。
根據路由實作多級菜單
路由配置完成後,我們就需要根據路由實作菜單了。
擷取路由配置
既然要根據路由配置實作多級菜單,那第一步就需要擷取我們的路由資料。這裡我使用簡單粗暴的方式去擷取路由配置資料:
this.$router.options.routes
。
這種方式也不太适用日常的項目開發,因為無法在擷取的時候對路由做進一步的處理,比如 權限控制
。
我們在元件加載時列印一下這個資料。
// 代碼位置:src/menu/leftMenu.vue
mounted(){
console.log(this.$router.options.routes);
}
列印結果如下。
可以看到這個資料就是我們在
router.js
中配置的路由資料。
為了友善使用,我将這個資料定義到計算屬性中。
// 代碼位置:src/menu/leftMenu.vue
computed: {
routesInfo: function(){
return this.$router.options.routes;
}
}
一級菜單
首先我們來實作
一級菜單
。
主要的邏輯就是循環路由資料
routesInfo
,在循環的時候判斷目前路由
route
是否包含子菜單,如果包含則目前菜單使用
el-submenu
實作,否則目前菜單使用
el-menu-item
實作。
<!-- src/menu/leftMenu.vue -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
:collapse="false">
<!-- 一級菜單 -->
<!-- 循環路由資料 -->
<!-- 判斷目前路由route是否包含子菜單 -->
<el-submenu
v-for="route in routesInfo"
v-if="route.meta.hasSubMenu"
:index="route.path">
<template slot="title">
<i :class="route.meta.icon"></i>
<span slot="title">{{route.meta.title}}</span>
</template>
</el-submenu>
<el-menu-item :index="route.path" v-else>
<i :class="route.meta.icon"></i>
<span slot="title">{{route.meta.title}}</span>
</el-menu-item>
</el-menu>
結果:
可以看到,我們第一級菜單已經生成了,
員工管理
、
考勤管理
、
工時管理
這三個菜單是有子菜單的,是以會有一個下拉按鈕。
不過目前點開是沒有任何内容的,接下來我們就來實作這三個菜單下的
二級菜單
。
二級菜單
二級菜單
的實作和
一級菜單
的邏輯是相同的:循環子路由
route.children
,在循環的時候判斷子路由
childRoute
是否包含子菜單,如果包含則目前菜單使用
el-submenu
實作,否則目前菜單使用
el-menu-item
實作。
那話不多說,直接上代碼。
<!-- src/menu/leftMenu.vue -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
:collapse="false">
<!-- 一級菜單 -->
<!-- 循環路由資料 -->
<!-- 判斷目前路由route是否包含子菜單 -->
<el-submenu
v-for="route in routesInfo"
v-if="route.meta.hasSubMenu"
:index="route.path">
<template slot="title">
<i :class="route.meta.icon"></i>
<span slot="title">{{route.meta.title}}</span>
</template>
<!-- 二級菜單 -->
<!-- 循環子路由`route.children` -->
<!-- 循環的時候判斷子路由`childRoute`是否包含子菜單 -->
<el-submenu
v-for="childRoute in route.children"
v-if="childRoute.meta.hasSubMenu"
:index="childRoute.path">
<template slot="title">
<i :class="childRoute.meta.icon"></i>
<span slot="title">{{childRoute.meta.title}}</span>
</template>
</el-submenu>
<el-menu-item :index="childRoute.path" v-else>
<i :class="childRoute.meta.icon"></i>
<span slot="title">{{childRoute.meta.title}}</span>
</el-menu-item>
</el-submenu>
<el-menu-item :index="route.path" v-else>
<i :class="route.meta.icon"></i>
<span slot="title">{{route.meta.title}}</span>
</el-menu-item>
</el-menu>
結果如下:
可以看到
二級菜單
成功實作。
三級菜單
三級菜單
就不用多說了,和
一級
、
二級
邏輯相同,這裡還是直接上代碼。
<!-- src/menu/leftMenu.vue -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
:collapse="false">
<!-- 一級菜單 -->
<!-- 循環路由資料 -->
<!-- 判斷目前路由route是否包含子菜單 -->
<el-submenu
v-for="route in routesInfo"
v-if="route.meta.hasSubMenu"
:index="route.path">
<template slot="title">
<i :class="route.meta.icon"></i>
<span slot="title">{{route.meta.title}}</span>
</template>
<!-- 二級菜單 -->
<!-- 循環子路由`route.children` -->
<!-- 循環的時候判斷子路由`childRoute`是否包含子菜單 -->
<el-submenu
v-for="childRoute in route.children"
v-if="childRoute.meta.hasSubMenu"
:index="childRoute.path">
<template slot="title">
<i :class="childRoute.meta.icon"></i>
<span slot="title">{{childRoute.meta.title}}</span>
</template>
<!-- 三級菜單 -->
<!-- 循環子路由`childRoute.children` -->
<!-- 循環的時候判斷子路由`child`是否包含子菜單 -->
<el-submenu
v-for="child in childRoute.children"
v-if="child.meta.hasSubMenu"
:index="child.path">
<template slot="title">
<i :class="child.meta.icon"></i>
<span slot="title">{{child.meta.title}}</span>
</template>
</el-submenu>
<el-menu-item :index="child.path" v-else>
<i :class="child.meta.icon"></i>
<span slot="title">{{child.meta.title}}</span>
</el-menu-item>
</el-submenu>
<el-menu-item :index="childRoute.path" v-else>
<i :class="childRoute.meta.icon"></i>
<span slot="title">{{childRoute.meta.title}}</span>
</el-menu-item>
</el-submenu>
<el-menu-item :index="route.path" v-else>
<i :class="route.meta.icon"></i>
<span slot="title">{{route.meta.title}}</span>
</el-menu-item>
</el-menu>
可以看到
工時清單
下的
三級菜單
已經顯示了。
總結
此時我們已經結合
路由配置
實作了這個動态的菜單。
不過這樣的代碼在邏輯上相關于
三層嵌套
的
for
循環,對應的是我們有三層的菜單。
假如我們有
四層
、
五層
甚至更多層的菜單時,那我們還得在嵌套更多層
for
循環。很顯然這樣的方式暴露了前面多層
for
循環的缺陷,是以我們就需要對這樣的寫法進行一個改進。
遞歸實作動态菜單
前面我們一直在說
一級
、
二級
、
三級
菜單的實作邏輯都是相同的:循環子路由,在循環的時候判斷子路由是否包含子菜單,如果包含則目前菜單使用
el-submenu
實作,否則目前菜單使用
el-menu-item
實作。那這樣的邏輯最适合的就是使用
遞歸
去實作。
是以我們需要将這部分共同的邏輯抽離出來作為一個獨立的元件,然後遞歸的調用這個元件。
邏輯拆分
<!-- src/menu/menuItem.vue -->
<template>
<div>
<el-submenu
v-for="child in route"
v-if="child.meta.hasSubMenu"
:index="child.path">
<template slot="title">
<i :class="child.meta.icon"></i>
<span slot="title">{{child.meta.title}}</span>
</template>
</el-submenu>
<el-menu-item :index="child.path" v-else>
<i :class="child.meta.icon"></i>
<span slot="title">{{child.meta.title}}</span>
</el-menu-item>
</div>
</template>
<script>
export default {
name: 'MenuItem',
props: ['route']
}
</script>
需要注意的是,這次抽離出來的元件循環的時候直接循環的是
route
資料,那這個
route
資料是什麼呢。
我們先看一下前面三層循環中循環的資料源分别是什麼。
為了看得更清楚,我将前面代碼中一些不相關的内容進行了删減。
<!-- src/menu/leftMenu.vue -->
<!-- 一級菜單 -->
<el-submenu
v-for="route in routesInfo"
v-if="route.meta.hasSubMenu">
<!-- 二級菜單 -->
<el-submenu
v-for="childRoute in route.children"
v-if="childRoute.meta.hasSubMenu">
<!-- 三級菜單 -->
<el-submenu
v-for="child in childRoute.children"
v-if="child.meta.hasSubMenu">
</el-submenu>
</el-submenu>
</el-submenu>
從上面的代碼可以看到:
一級菜單循環的是`routeInfo`,即最初我們擷取的路由資料`this.$router.options.routes`,循環出來的每一項定義為`route`
二級菜單循環的是`route.children`,循環出來的每一項定義為`childRoute`
三級菜單循環的是`childRoute.children`,循環出來的每一項定義為`child`
按照這樣的邏輯,可以發現
二級菜單
、
三級菜單
循環的資料源都是相同的,即前一個循環結果項的
children
,而一級菜單的資料來源于
this.$router.options.routes
。
前面我們抽離出來的
menuItem
元件,循環的是
route
資料,即不管是
一層菜單
還是
二層
、
三層菜單
,都是同一個資料源,是以我們需要統一資料源。那當然也非常好實作,我們在調用元件的時候,為元件傳遞不同的值即可。
代碼實作
前面公共元件已經拆分出來了,後面的代碼就非常好實作了。
首先是抽離出來的
meunItem
元件,實作的是
邏輯判斷
以及
遞歸調用自身
。
<!-- src/menu/menuItem.vue -->
<template>
<div>
<el-submenu
v-for="child in route"
v-if="child.meta.hasSubMenu"
:index="child.path">
<template slot="title">
<i :class="child.meta.icon"></i>
<span slot="title">{{child.meta.title}}</span>
</template>
<!--遞歸調用元件自身 -->
<MenuItem :route="child.children"></MenuItem>
</el-submenu>
<el-menu-item :index="child.path" v-else>
<i :class="child.meta.icon"></i>
<span slot="title">{{child.meta.title}}</span>
</el-menu-item>
</div>
</template>
<script>
export default {
name: 'MenuItem',
props: ['route']
}
</script>
接着是
leftMenu
元件,調用
menuIndex
元件,傳遞原始的路由資料
routesInfo
。
<!-- src/menu/leftMenu.vue -->
<template>
<div id="left-menu">
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
:collapse="false">
<MenuItem :route="routesInfo"></MenuItem>
</el-menu>
</div>
</template>
<script>
import MenuItem from './menuItem'
export default {
name: 'LeftMenu',
components: { MenuItem }
}
</script>
<style lang="scss">
// 使左邊的菜單外層的元素高度充滿螢幕
#left-container{
position: absolute;
top: 100px;
bottom: 0px;
// 使菜單高度充滿螢幕
#left-menu, .el-menu-vertical-demo{
height: 100%;
}
}
</style>
最終的結果這裡就不展示了,和我們需要實作的結果是一緻的。
功能完善
到此,我們
結合路由配置實作了菜單欄
這個功能基本上已經完成了,不過這是一個缺乏靈魂的菜單欄,因為沒有設定菜單的跳轉,我們點選菜單欄還無法路由跳轉到對應的
元件
,是以接下來就來實作這個功能。
菜單跳轉的實作方式有兩種,第一種是
NavMenu
元件提供的跳轉方式。
第二種是在菜單上添加
router-link
實作跳轉。
那本次我選擇的是第一種方式實作跳轉,這種實作方式需要兩個步驟才能完成,第一步是啟用
el-menu
上的
router
;第二步是設定導航的
index
屬性。
那下面就來實作這兩個步驟。
啟用el-menu上的router
<!-- src/menu/leftMenu.vue -->
<!-- 省略其餘未修改代碼-->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
router
:collapse="false">
<MenuItem :route="routesInfo">
</MenuItem>
</el-menu>
設定導航的index屬性
首先我将每一個菜單标題對應需要設定的
index
屬性值列出來。
值對應的是每個菜單在路由中配置的
index
值
path
首頁
員工管理
員工統計 index="/employee/employeeStatistics"
員工管理 index="/employee/employeeManage"
考勤管理
考勤統計 index="/attendManage/attendStatistics"
考勤清單 index="/attendManage/attendList"
異常管理 index="/attendManage/exceptManage"
員工統計
員工統計 index="/timeManage/timeStatistics"
員工統計 index="/timeManage/timeList"
選項一 index="/timeManage/timeList/options1"
選項二 index="/timeManage/timeList/options2"
接着在回顧前面遞歸調用的元件,導航菜單的
index
設定的是
child.path
,為了看清楚
child.path
的值,我将其添加菜單标題的右側,讓其顯示到界面上。
<!-- src/menu/menuItem.vue -->
<!-- 省略其餘未修改代碼-->
<el-submenu
v-for="child in route"
v-if="child.meta.hasSubMenu"
:index="child.path">
<template slot="title">
<i :class="child.meta.icon"></i>
<span slot="title">{{child.meta.title}} | {{child.path}}</span>
</template>
<!--遞歸調用元件自身 -->
<MenuItem :route="child.children"></MenuItem>
</el-submenu>
<el-menu-item :index="child.path" v-else>
<i :class="child.meta.icon"></i>
<span slot="title">{{child.meta.title}} | {{child.path}}</span>
</el-menu-item>
同時将菜單欄的寬度由
200px
設定為
400px
。
<!-- src/menu/menuIndex.vue -->
<!-- 省略其餘未修改代碼-->
<el-aside width="400px">
<LeftMenu></LeftMenu>
</el-aside>
然後我們看一下效果。
可以發現,
child.path
的值就是目前菜單在路由中配置
path
值(
router.js
中配置的
path
值)。
那麼問題就來了,前面我們整理了每一個菜單标題對應需要設定的
index
屬性值,就目前來看,現在設定的
index
值是不符合要求的。不過仔細觀察現在菜單設定的
index
值和正常值是有一點接近的,隻是缺少了上一級菜單的
path
值,如果能将
上一級菜單
的
path
值和目前菜單的
path
值進行一個拼接,就能得到正确的
index
值了。
那這個思路實作的方式依然是在遞歸時将目前菜單的
path
作為參數傳遞給
menuItem
元件。
<!-- src/menu/menuIndex.vue -->
<!--遞歸調用元件自身 -->
<MenuItem
:route="child.children"
:basepath="child.path">
</MenuItem>
将目前菜單的
path
作為參數傳遞給
menuItem
元件之後,在下一級菜單實作時,就能拿到上一級菜單的
path
值。然後元件中将
basepath
的值和目前菜單的
path
值做一個拼接,作為目前菜單的
index
值。
<!-- src/menu/menuIndex.vue -->
<el-menu-item :index="getPath(child.path)" v-else>
</el-menu-item>
<script>
import path from 'path'
export default {
name: 'MenuItem',
props: ['route','basepath'],
data(){
return {
}
},
methods :{
// routepath 為目前菜單的path值
// getpath: 拼接 目前菜單的上一級菜單的path 和 目前菜單的path
getPath: function(routePath){
return path.resolve(this.basepath, routePath);
}
}
}
</script>
再看一下界面。
我們可以看到二級菜單的
index
值已經沒問題了,但是仔細看,發現
工時管理
-
工時清單
下的兩個三級菜單
index
值還是有問題,缺少了
工時管理
這個一級菜單的
path
。
那這個問題是因為我們在調用元件自身是傳遞的
basepath
有問題。
<!--遞歸調用元件自身 -->
<MenuItem
:route="child.children"
:basepath="child.path">
</MenuItem>
basepath
傳遞的隻是上一級菜單的
path
,在遞歸
二級菜單
時,
index
的值是
一級菜單的path值
+
二級菜單的path值
;那當我們遞歸
三級菜單
時,
index
的值就是
二級菜單的path值
+
三級菜單的path值
,這也就是為什麼
工時管理-工時清單
下的兩個三級菜單
index
值存在問題。
是以這裡的
basepath
值在遞歸的時候應該是
累積
的,而不隻是上一級菜單的
path
值。是以借助遞歸算法的優勢,
basepath
的值也需要通過
getPath
方法進行處理。
<MenuItem
:route="child.children"
:basepath="getPath(child.path)">
</MenuItem>
最終完整的代碼如下。
<!-- src/menu/menuIndex.vue -->
<template>
<div>
<el-submenu
v-for="child in route"
v-if="child.meta.hasSubMenu"
:key="child.path"
:index="getPath(child.path)">
<template slot="title">
<i :class="child.meta.icon"></i>
<span slot="title">
{{child.meta.title}}
</span>
</template>
<!--遞歸調用元件自身 -->
<MenuItem
:route="child.children"
:basepath="getPath(child.path)">
</MenuItem>
</el-submenu>
<el-menu-item :index="getPath(child.path)" v-else>
<i :class="child.meta.icon"></i>
<span slot="title">
{{child.meta.title}}
</span>
</el-menu-item>
</div>
</template>
<script>
import path from 'path'
export default {
name: 'MenuItem',
props: ['route','basepath'],
data(){
return {
}
},
methods :{
// routepath 為目前菜單的path值
// getpath: 拼接 目前菜單的上一級菜單的path 和 目前菜單的path
getPath: function(routePath){
return path.resolve(this.basepath, routePath);
}
}
}
</script>
删除其餘用來調試的代碼
最終效果
文章的最後呢,将本次實作的最終效果在此展示一下。
和
選項一
這兩個三級菜單在路由配置中沒有設定
選項二
component
,這兩個菜單隻是為了實作三級菜單,在最後的結果示範中,我已經删除了路由中配置的這兩個三級菜單
此處在
元件中為
leftMenu
開啟了
el-menu
在
unique-opened
元件中,将左側菜單欄的寬度改為
menuIndex
200px
關于
作者
小洋芋biubiubiu
一個努力學習的前端小菜鳥,知識是無限的。堅信隻要不停下學習的腳步,總能到達自己期望的地方
同時還是一個喜歡小貓咪的人,家裡有一隻美短小母貓,名叫洋芋
部落格園
https://www.cnblogs.com/HouJiao/
掘金
https://juejin.im/user/58c61b4361ff4b005d9e894d
微信公衆号
洋芋媽的碎碎念
微信公衆号的初衷是記錄自己和身邊的一些故事,同時會不定期更新一些技術文章
歡迎大家掃碼關注,一起吸貓,一起聽故事,一起學習前端技術
作者寄語
小小總結,歡迎大家指導~