1. 程式人生 > 程式設計 >Vue結合路由配置遞迴實現選單欄功能

Vue結合路由配置遞迴實現選單欄功能

Vue結合路由配置遞迴實現選單欄功能

前言

在日常開發中,專案中的選單欄都是已經實現好了的。如果需要新增新的選單,只需要在路由配置中新增一條路由,就可以實現選單的新增。

相信大家和我一樣,有時候會躍躍欲試自己去實現一個選單欄。那今天我就將自己實現的選單欄的整個思路和程式碼分享給大家。

本篇文章重在總結和分享選單欄的一個遞迴實現方式程式碼的優化選單許可權等不在本篇文章範圍之內,在文中的相關部分也會做一些提示,有個別不推薦的寫法希望大家不要參考哦。

同時可能會存在一些細節的功能沒有處理或者沒有提及到,忘知曉。

最終的效果

Vue結合路由配置遞迴實現選單欄功能

本次實現的這個選單欄包含有一級選單二級選單三級選單這三種類型,基本上已經可以覆蓋專案中不同的選單需求。

後面會一步一步從易到難去實現這個選單。

簡單實現

我們都知道到element提供了 NavMenu 導航選單元件,因此我們直接按照文件將這個選單欄做一個簡單的實現。

基本的佈局架構圖如下:

Vue結合路由配置遞迴實現選單欄功能

選單首頁-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圖示的路徑產品名稱

完成後的介面效果如下。

Vue結合路由配置遞迴實現選單欄功能

左側選單欄-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>

注意選單的樣式程式碼,設定了絕對定位,並且設定topbottom使選單高度撐滿螢幕。

此時在看下介面效果。

Vue結合路由配置遞迴實現選單欄功能

基本上算是實現了一個簡單的選單佈局。

不過在實際專案在設計的時候,選單欄的內容有可能來自後端給我們返回的資料,其中包含選單名稱選單圖示以及選單之間的層級關係

總而言之,我們的選單是動態生成的,而不是像前面那種固定的寫法。因此下面我將實現一個動態生成的選單,選單的資料來源於我們的路由配置

結合路由配置實現動態選單

路由配置

首先,我將專案的路由配置程式碼貼出來。

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',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',redirect: '/attendManage/attendStatistics',meta: {
  title: '考勤管理',// 選單標題
  icon: 'el-icon-s-claim',// 是否包含子節點,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: '異常管理',component: ExceptManage,}
 ]
 },// 工時管理
 {
 path: '/timeManage',name: 'timeManage',redirect: '/timeManage/timeStatistics',meta: {
  title: '工時管理',// 選單標題
  icon: 'el-icon-message-solid',// 是否包含子選單,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: '選項二',]
  }
 ]
 },];
export default new Router({
 routes
})

在這段程式碼的最開始部分,我們引入了需要使用的元件,接著就對路由進行了配置。

此處使用了直接引入元件的方式,專案開發中不推薦這種寫法,應該使用懶載入的方式

路由配置除了最基礎的pathcomponent以及children之外,還配置了一個meta資料項。

meta: {
 title: '工時管理',// 選單標題
 icon: 'el-icon-message-solid',// 圖示
 hasSubMenu: true,// 是否包含子節點,false 沒有子選單;true 有子選單
}

meta資料包含的配置有選單標題(title)、圖示的類名(icon)和是否包含子節點(hasSubMenu)。

根據titleicon這兩個配置項,可以展示當前選單的標題圖示

hasSubMenu表示當前的選單項是否有子選單,如果當前選單包含有子選單(hasSubMenutrue),那當前選單對應的標籤元素就是el-submenu;否則當前選單對應的選單標籤元素就是el-menu-item

是否包含子選單是一個非常關鍵的邏輯,我在實現的時候是直接將其配置到了meta.hasSubMenu這個引數裡面。

根據路由實現多級選單

路由配置完成後,我們就需要根據路由實現選單了。

獲取路由配置

既然要根據路由配置實現多級選單,那第一步就需要獲取我們的路由資料。這裡我使用簡單粗暴的方式去獲取路由配置資料:this.$router.options.routes

這種方式也不太適用日常的專案開發,因為無法在獲取的時候對路由做進一步的處理,比如許可權控制

我們在元件載入時列印一下這個資料。

// 程式碼位置:src/menu/leftMenu.vue
 mounted(){
 console.log(this.$router.options.routes);
}

列印結果如下。

Vue結合路由配置遞迴實現選單欄功能

可以看到這個資料就是我們在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>

結果:

Vue結合路由配置遞迴實現選單欄功能

可以看到,我們第一級選單已經生成了,員工管理考勤管理工時管理這三個選單是有子選單的,所以會有一個下拉按鈕。

Vue結合路由配置遞迴實現選單欄功能

不過目前點開是沒有任何內容的,接下來我們就來實現這三個選單下的二級選單

二級選單

二級選單的實現和一級選單的邏輯是相同的:迴圈子路由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>

結果如下:

Vue結合路由配置遞迴實現選單欄功能

可以看到二級選單成功實現。

三級選單

三級選單就不用多說了,和一級二級邏輯相同,這裡還是直接上程式碼。

<!-- 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>

Vue結合路由配置遞迴實現選單欄功能

可以看到工時列表下的三級選單已經顯示了。

總結

此時我們已經結合路由配置實現了這個動態的選單。

不過這樣的程式碼在邏輯上相關於三層巢狀for迴圈,對應的是我們有三層的選單。

假如我們有四層五層甚至更多層的選單時,那我們還得在巢狀更多層for迴圈。很顯然這樣的方式暴露了前面多層for迴圈的缺陷,所以我們就需要對這樣的寫法進行一個改進。

遞迴實現動態選單

前面我們一直在說一級二級三級選單的實現邏輯都是相同的:迴圈子路由,在迴圈的時候判斷子路由是否包含子選單,如果包含則當前選單使用el-submenu實現,否則當前選單使用el-menu-item實現。那這樣的邏輯最適合的就是使用遞迴去實現。

所以我們需要將這部分共同的邏輯抽離出來作為一個獨立的元件,然後遞迴的呼叫這個元件。

Vue結合路由配置遞迴實現選單欄功能

邏輯拆分

<!-- 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資料,即不管是一層選單還是二層三層選單,都是同一個資料來源,因此我們需要統一資料來源。那當然也非常好實現,我們在呼叫元件的時候,為元件傳遞不同的值即可。

Vue結合路由配置遞迴實現選單欄功能

程式碼實現

前面公共元件已經拆分出來了,後面的程式碼就非常好實現了。

首先是抽離出來的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元件提供的跳轉方式。

Vue結合路由配置遞迴實現選單欄功能

第二種是在選單上新增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>

然後我們看一下效果。

Vue結合路由配置遞迴實現選單欄功能

可以發現,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>

再看一下介面。

Vue結合路由配置遞迴實現選單欄功能

我們可以看到二級選單的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',routePath);
 }
 }
}
</script>

刪除其餘用來除錯的程式碼

最終效果

文章的最後呢,將本次實現的最終效果在此展示一下。

Vue結合路由配置遞迴實現選單欄功能

選項一選項二這兩個三級選單在路由配置中沒有設定component,這兩個菜單只是為了實現三級選單,在最後的結果演示中,我已經刪除了路由中配置的這兩個三級選單

此處在leftMenu元件中為el-menu開啟了unique-opened

menuIndex元件中,將左側選單欄的寬度改為200px

總結

到此這篇關於Vue結合路由配置遞迴實現選單欄功能的文章就介紹到這了,更多相關vue 路由遞迴選單欄內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!