主頁 > 企業開發 > Vue結合路由配置遞回實作選單欄

Vue結合路由配置遞回實作選單欄

2020-09-17 07:40:42 企業開發

作者:小土豆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=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圖示的路徑產品名稱

完成后的界面效果如下,

左側選單欄-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使選單高度撐滿螢屏,

此時在看下界面效果,

基本上算是實作了一個簡單的選單布局,

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

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

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

路由配置

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

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
})

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

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

路由配置除了最基礎的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);
}

列印結果如下,

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

微信公眾號

土豆媽的碎碎念

微信公眾號的初衷是記錄自己和身邊的一些故事,同時會不定期更新一些技術文章

歡迎大家掃碼關注,一起吸貓,一起聽故事,一起學習前端技術

作者寄語

小小總結,歡迎大家指導~

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/63851.html

標籤:JavaScript

上一篇:ES6核心問題是什么?本文詳解

下一篇:Vue組件通信的六種方式,你會幾個?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more