Vue結合路由配置遞回實作選單欄
2020-09-17 07:40:42 企業開發

作者:小土豆biubiubiu
博客園:https://www.cnblogs.com/HouJiao/
掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d
微信公眾號:土豆媽的碎碎念(掃碼關注,一起吸貓,一起聽故事,一起學習前端技術)
作者文章的內容均來源于自己的實踐,如果覺得有幫助到你的話,可以點贊給個鼓勵或留下寶貴意見
前言
在日常開發中,專案中的選單欄都是已經實作好了的,如果需要添加新的選單,只需要在路由配置中新增一條路由,就可以實作選單的添加,
相信大家和我一樣,有時候會躍躍欲試自己去實作一個選單欄,那今天我就將自己實作的選單欄的整個思路和代碼分享給大家,
本篇文章重在總結和分享選單欄的一個遞回實作方式,代碼的優化、選單權限等不在本篇文章范圍之內,在文中的相關部分也會做一些提示,有個別不推薦的寫法希望大家不要參考哦,
同時可能會存在一些細節的功能沒有處理或者沒有提及到,忘知曉,
最終的效果

本次實作的這個選單欄包含有一級選單、二級選單和三級選單這三種型別,基本上已經可以覆寫專案中不同的選單需求,
后面會一步一步從易到難去實作這個選單,
簡單實作
我們都知道到element提供了 NavMenu 導航選單組件,因此我們直接按照檔案將這個選單欄做一個簡單的實作,
基本的布局架構圖如下:

首先要實作的是選單首頁這個組件,根據前面的布局架構圖并且參考官方檔案,實作起來非常簡單,
<!-- 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>
頂部選單欄主要就是一個logo和產品名稱,
邏輯代碼也很簡單,我直接將代碼貼上,
<!-- src/menu/leftMenu.vue -->
<template>
<div id="top-menu">
<img class="logo" :src=https://www.cnblogs.com/HouJiao/p/"logoPath" />
{{name}}
<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圖示的路徑和產品名稱,
完成后的界面效果如下,

首先按照官方檔案實作一個簡單的選單欄,
<!-- 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屬性,
那下面就來實作這兩個步驟,
<!-- 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
微信公眾號
土豆媽的碎碎念
微信公眾號的初衷是記錄自己和身邊的一些故事,同時會不定期更新一些技術文章
歡迎大家掃碼關注,一起吸貓,一起聽故事,一起學習前端技術
作者寄語
小小總結,歡迎大家指導~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/63851.html
標籤:JavaScript
上一篇:ES6核心問題是什么?本文詳解
下一篇:Vue組件通信的六種方式,你會幾個?