做者:小土豆biubiubiujavascript
博客園:https://www.cnblogs.com/HouJiao/css
掘金:https://juejin.im/user/58c61b4361ff4b005d9e894dhtml
微信公衆號:土豆媽的碎碎念(掃碼關注,一塊兒吸貓,一塊兒聽故事,一塊兒學習前端技術)前端
做者文章的內容均來源於本身的實踐,若是以爲有幫助到你的話,能夠點贊給個鼓勵或留下寶貴意見vue
在平常開發中,項目中的菜單欄都是已經實現好了的。若是須要添加新的菜單,只須要在路由配置
中新增一條路由,就能夠實現菜單的添加。java
相信你們和我同樣,有時候會躍躍欲試本身去實現一個菜單欄。那今天我就將本身實現的菜單欄的整個思路和代碼分享給你們。算法
本篇文章重在總結和分享菜單欄的一個
遞歸實現方式
,代碼的優化
、菜單權限
等不在本篇文章範圍以內,在文中的相關部分也會作一些提示,有個別不推薦的寫法但願你們不要參考哦。vue-router同時可能會存在一些細節的功能沒有處理或者沒有說起到,忘知曉。後端
本次實現的這個菜單欄包含有一級菜單
、二級菜單
和三級菜單
這三種類型,基本上已經能夠覆蓋項目中不一樣的菜單需求。微信
後面會一步一步從易到難去實現這個菜單。
咱們都知道到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="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圖標的路徑
和產品名稱
。
完成後的界面效果以下。
首先按照官方文檔實現一個簡單的菜單欄。
<!-- 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
值對應的是每一個菜單在路由中配置的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
土豆媽的碎碎念
微信公衆號的初衷是記錄本身和身邊的一些故事,同時會不按期更新一些技術文章
歡迎你們掃碼關注,一塊兒吸貓,一塊兒聽故事,一塊兒學習前端技術
小小總結,歡迎你們指導~