Author: Little potato Biubiubiu

Blog Park: www.cnblogs.com/HouJiao/

The Denver nuggets: juejin. Cn/user / 243617…

Wechat public number: unknown treasure program yuan

The content of the author’s article is derived from his own practice, if you feel helpful, you can like to give an encouragement or leave valuable comments

preface

In daily development, the menu bar in a project is already implemented. To add a new menu, you only need to add a route in the route configuration.

If you’re like me, you’ll sometimes be tempted to implement a menu bar yourself. That today I will realize their own menu bar of the whole idea and code to share with you.

This article focuses on summarizing and sharing a recursive implementation of the menu bar, code optimization, menu permissions are not within the scope of this article, in the relevant part of the article will also do some tips, there are individual not recommended writing method I hope you do not refer to oh.

At the same time, there may be some details of the function is not dealt with or not mentioned, forget to know.

End result

The menu bar implemented this time includes three types: first-level menu, second-level menu and third-level menu, which can basically cover different menu requirements in the project.

This menu will be implemented step by step from easy to difficult.

Simple implementation

We all know that Element provides the NavMenu navigation menu component, so let’s make a simple implementation of the menu bar directly from the documentation.

The basic layout diagram is as follows:

Menu home page -menuIndex

The first thing to achieve is the menu home page this component, according to the previous layout diagram and reference to the official document, the implementation is very simple.

<! -- 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: 'Staff Management System'}}}</script>
<style lang="scss">
    #menu-index{
        .el-header{
            padding: 0px; }}</style>
Copy the code

TopMenu bar -topMenu

The top menu bar is basically a logo and product name.

The logic code is pretty simple, so I’ll just paste it in.

<! -- 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>
Copy the code

This code contains two pieces of data that the parent component passes to the child component.

props: ['logoPath'.'name']
Copy the code

This is the two data that the parent component menuIndex passes to the child component topMenu, namely the path of the logo icon and the product name.

The finished interface looks like this.

LeftMenu bar -leftMenu

First implement a simple menu bar according to the official document.

<! -- 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">Home page</span>
            </el-menu-item>
            <el-submenu index="2">
                <template slot="title">
                    <i class="el-icon-user-solid"></i>
                    <span slot="title">Staff management</span>
                </template>
                <el-menu-item index="2-1">Employees statistics</el-menu-item>
                <el-menu-item index="2-2">Staff management</el-menu-item>
            </el-submenu>
            <el-submenu index="3">
                <template slot="title">
                    <i class="el-icon-s-claim"></i>
                    <span slot="title">The attendance management</span>
                </template>
                <el-menu-item index="3-1">Attendance statistics</el-menu-item>
                <el-menu-item index="3-2">Attendance list</el-menu-item>
                <el-menu-item index="3-2">Exception management</el-menu-item>
            </el-submenu>
            <el-submenu index="4">
                <template slot="title">
                    <i class="el-icon-location"></i>
                    <span slot="title">Time management</span>
                </template>
                <el-menu-item index="4-1">Working hours</el-menu-item>
                <el-submenu index="4-2">
                    <template slot="title">List of working hours</template>
                    <el-menu-item index="4-2-1">Option a</el-menu-item>
                    <el-menu-item index="4-2-2">Option 2</el-menu-item>
                </el-submenu>
            </el-submenu>
        </el-menu>
    </div>
</template>
<script>
export default {
    name: 'LeftMenu'
}
</script>
<style lang="scss">// Make the outer elements of the left menu height to fill the screen#left-container{
        position: absolute;
        top: 100px;
        bottom: 0px; // Make the menu height fill the screen#left-menu..el-menu-vertical-demo{
            height: 100%; }}</style>
Copy the code

Note the style code for the menu, setting absolute positioning, and setting top and bottom to make the menu height fill the screen.

Now look at the interface effect.

Basically a simple menu layout.

However, when the actual project is designed, the content of the menu bar may come from the data returned to us by the back end, including the menu name, menu icon and hierarchical relationship between menus.

In short, our menus are dynamically generated, not fixed. So I’m going to implement a dynamically generated menu using data from our routing configuration.

Dynamic menu with route configuration

The routing configuration

First, I post the routing configuration code for the project.

import Vue from 'vue';
import Router from "vue-router";

/ / the menu
import MenuIndex from '@/components/menu/menuIndex.vue';

/ / home page
import Index from '@/components/homePage/index.vue';

// Personnel statistics
import EmployeeStatistics from '@/components/employeeManage/employeeStatistics.vue';
import EmployeeManage from '@/components/employeeManage/employeeManage.vue'

/ / attendance
// Check attendance statistics
import AttendStatistics from '@/components/attendManage/attendStatistics';
// The attendance list
import AttendList from '@/components/attendManage/attendList.vue';
// Exception management
import ExceptManage from '@/components/attendManage/exceptManage.vue';

/ / working hours
// Time statistics
import TimeStatistics from '@/components/timeManage/timeStatistics.vue';
// Timesheet
import TimeList from '@/components/timeManage/timeList.vue';
Vue.use(Router)


let routes = [
    // Select * from the shortcut menu.
    {
        path: '/index'.name: 'index'.component: MenuIndex,
        redirect: '/index'.meta: {
            title: 'home'.// Menu title
            icon: 'el-icon-s-home'./ / icon
            hasSubMenu: false.// whether to include submenus, false does not have submenus; True has submenus

        },
        children:[
            {
                path: '/index'.component: Index
            }
        ]
    },
    // Staff management
    {
        path: '/employee'.name: 'employee'.component: MenuIndex,
        redirect: '/employee/employeeStatistics'.meta: {
            title: 'Staff Management'.// Menu title
            icon: 'el-icon-user-solid'./ / icon
            hasSubMenu: true.// Whether to include submenus
        },
        children: [
            // Employee statistics
            {
                path: 'employeeStatistics'.name: 'employeeStatistics'.meta: {
                    title: 'Staff Statistics'.// Menu title,
                    hasSubMenu: false    // Whether to include submenus
                },
                component: EmployeeStatistics,
            },
            // Staff management (add, delete, change and check)
            {
                path: 'employeeManage'.name: 'employeeManage'.meta: {
                    title: 'Staff Management'.// Menu title
                    hasSubMenu: false    // Whether to include submenus
                },
                component: EmployeeManage
            }
        ]
    },
    // Attendance management
    {
        path: '/attendManage'.name: 'attendManage'.component: MenuIndex,
        redirect: '/attendManage/attendStatistics'.meta: {
            title: 'Attendance Management'.// Menu title
            icon: 'el-icon-s-claim'./ / icon
            hasSubMenu: true.// Whether to include child nodes, false has no submenu; True has submenus
        },
        children: [// Check attendance statistics
            {
                path: 'attendStatistics'.name: 'attendStatistics'.meta: {
                    title: 'Attendance statistics'.// Menu title
                    hasSubMenu: false    // Whether to include submenus
                },
                component: AttendStatistics,
            },
            // The attendance list
            {
                path: 'attendList'.name: 'attendList'.meta: {
                    title: 'Attendance List'.// Menu title
                    hasSubMenu: false    // Whether to include submenus
                },
                component: AttendList,
            },
            // Exception management
            {
                path: 'exceptManage'.name: 'exceptManage'.meta: {
                    title: 'Exception Management'.// Menu title
                    hasSubMenu: false    // Whether to include submenus
                },
                component: ExceptManage,
            }
        ]
    },
    // Time management
    {
        path: '/timeManage'.name: 'timeManage'.component: MenuIndex,
        redirect: '/timeManage/timeStatistics'.meta: {
            title: 'Time Management'.// Menu title
            icon: 'el-icon-message-solid'./ / icon
            hasSubMenu: true.// whether to include submenus, false does not have submenus; True has submenus
        },
        children: [
            // Time statistics
            {
                path: 'timeStatistics'.name: 'timeStatistics'.meta: {
                    title: 'Hour Statistics'.// Menu title
                    hasSubMenu: false    // Whether to include submenus
                },
                component: TimeStatistics
            },
            // Timesheet
            {
                path: 'timeList'.name: 'timeList'.component: TimeList,
                meta: {
                    title: 'Timesheet'.// Menu title
                    hasSubMenu: true    // Whether to include submenus
                },
                children: [{path: 'options1'.meta: {
                            title: 'Option one'.// Menu title
                            hasSubMenu: false    // Whether to include submenus}, {},path: 'options2'.meta: {
                            title: 'Option two'.// Menu title
                            hasSubMenu: false    // Whether to include submenus},},]}]},];export default new Router({
    routes
})
Copy the code

At the beginning of this code, we introduce the components we need to use, and then configure the routing.

Importing components directly is used here, which is not recommended in project development and should be lazy-loaded instead

The routing configuration configures a meta data item in addition to the basic path, Component, and children.

meta: {
    title: 'Time Management'.// Menu title
    icon: 'el-icon-message-solid'./ / icon
    hasSubMenu: true.// Whether to include child nodes, false has no submenu; True has submenus
}
Copy the code

Meta data contains configurations such as title of menu, icon of icon class, and whether to include child nodes (hasSubMenu).

The title and icon of the current menu can be displayed according to the title and icon configuration items.

HasSubMenu indicates whether the current menu item has a submenu. If the current menu contains a submenu (hasSubMenu is true), the label element corresponding to the current menu is el-Submenu. Otherwise, the menu label element corresponding to the current menu is el-menu-item.

Whether or not to include submenus is a key logic, I implemented it directly in the meta. HasSubMenu parameter.

Implement multi-level menu according to route

Once the route configuration is complete, we need to implement the menu based on the route.

Obtaining route Configuration

Since we are going to implement a multi-level menu based on the routing configuration, the first step is to get our routing data. Here I use a simple and crude way to get the routing configuration data: this.$router.options.routes.

This approach is also not very suitable for everyday project development, because there is no further handling of routes at fetch time, such as permission control.

We print this data when the component loads.

SRC /menu/ leftmenu.vue
 mounted(){
    console.log(this.$router.options.routes);
}
Copy the code

The printed result is as follows.

You can see that this is the routing data that we configured in router.js.

For ease of use, I define this data in the computed properties.

SRC /menu/ leftmenu.vue
computed: {
    routesInfo: function(){
        return this.$router.options.routes; }}Copy the code

Level 1 menu

First let’s implement the first level menu.

The main logic is to loop the routing data routesInfo, which determines whether the current route contains submenus during the loop. If it does, the current menu is implemented using El-Submenu; otherwise, the current menu is implemented using El-menu-item.

<! -- src/menu/leftMenu.vue -->
<el-menu 
    :default-active="$route.path" 
    class="el-menu-vertical-demo" 
    :collapse="false">
    <! -- Level 1 menu -->
    <! -- Loop routing data -->
    <! -- Check whether the route contains submenu -->
    <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>
Copy the code

Results:

It can be seen that the first-level menu has been generated. The three menus of employee management, attendance management and time management have sub-menus, so there is a drop-down button.

However, there is no content at present. Next, we will realize the secondary menu under these three menus.

The secondary menu

The logic of the second-level menu is the same as that of the first-level menu: loop childRoute route.children. During the loop, check whether childRoute contains sub-menus.

Let’s just cut to the code.

<! -- src/menu/leftMenu.vue -->
<el-menu 
    :default-active="$route.path" 
    class="el-menu-vertical-demo" 
    :collapse="false">
    <! -- Level 1 menu -->
    <! -- Loop routing data -->
    <! -- Check whether the route contains submenu -->
    <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>
        <! -- Level 2 menu -->
        <! -- route.children '-->
        <! Check whether the child 'childRoute' contains a submenu -->
        <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>
Copy the code

The results are as follows:

You can see that the secondary menu is successfully implemented.

Level 3 menu

Level 3 menu need not say more, and level 1, level 2 logic is the same, here or directly on the code.

<! -- src/menu/leftMenu.vue -->
<el-menu 
    :default-active="$route.path" 
    class="el-menu-vertical-demo" 
    :collapse="false">
    <! -- Level 1 menu -->
    <! -- Loop routing data -->
    <! -- Check whether the route contains submenu -->
    <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>
        <! -- Level 2 menu -->
        <! -- route.children '-->
        <! Check whether the child 'childRoute' contains a submenu -->
        <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>
            <! -- Level 3 menu -->
            <! -- Childroute. children '-->
            <! -- Check whether child path 'child' contains submenu when loop
            <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>
Copy the code

You can see that the three-level menu under the timesheet is already displayed.

conclusion

At this point we have implemented the dynamic menu in conjunction with the routing configuration.

But this code is logically analogous to a three-level nested for loop, where we have three levels of menus.

If we had four, five or more menus, we would have to nest more for loops. Obviously, this approach exposes the shortcomings of the previous multi-level for loop, so we need to make an improvement to this way of writing.

Recursive implementation of dynamic menu

Previously, we have been saying that the implementation logic of menus at level 1, level 2 and level 3 is the same: loop subpaths. During the loop, determine whether the subpaths contain submenus. If so, the current menu is implemented using El-Submenu; otherwise, the current menu is implemented using El-menu-item. The best way to implement this logic is to use recursion.

So we need to separate this part of common logic as a separate component, and then recursively call this component.

Logical split

<! -- 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>
Copy the code

It is important to note that the extracted component loops through route data directly. What is the route data?

Let’s take a look at the data sources for the loops in the previous three layers.

To make it clearer, I’ve trimmed some of the irrelevant bits from the previous code.

<! -- src/menu/leftMenu.vue -->

<! -- Level 1 menu -->
<el-submenu 
    v-for="route in routesInfo" 
    v-if="route.meta.hasSubMenu">
    <! -- Level 2 menu -->
    
    <el-submenu 
        v-for="childRoute in route.children" 
        v-if="childRoute.meta.hasSubMenu">
        
        <! -- Level 3 menu -->
        <el-submenu 
            v-for="child in childRoute.children" 
            v-if="child.meta.hasSubMenu">
          
        </el-submenu>
    
    </el-submenu>
</el-submenu>
Copy the code

As you can see from the code above:

Level 1 menu cycle is ` routeInfo `, i.e., first we obtain routing data ` enclosing $router. The options. The routes `, circulation of each item is defined as ` route ` secondary menu cycle is ` route. The children `, Each item in the loop is' childRoute 'and each item in the loop is' childroute. children' and each item is' child 'Copy the code

According to this logic, it can be found that the data source of the second level menu and the third level menu loop is the same, namely the children of the result item of the previous loop, while the data of the first level menu comes from this.$router.options.routes.

The menuItem component that we pulled out earlier circulates route data, meaning that whether the tier 1 menu or tier 2 or tier 3 menu is the same data source, so we need to unify the data source. That’s also pretty easy to implement, we just pass different values to the component when we call it.

Code implementation

Now that the common components have been broken out, the following code is pretty easy to implement.

The first is the extracted meunItem component, which implements logical judgments and calls itself recursively.

<! -- 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>
            <! Recursively call the component itself -->
            <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>
Copy the code

Next comes the leftMenu component, which calls the menuIndex component, passing in the raw routing data, 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">// Make the outer elements of the left menu height to fill the screen#left-container{
        position: absolute;
        top: 100px;
        bottom: 0px; // Make the menu height fill the screen#left-menu..el-menu-vertical-demo{
            height: 100%; }}</style>

Copy the code

The final result, which I won’t show you here, is consistent with what we want to achieve.

functional

This function is almost complete, but this is a menu bar lacking soul, because there is no menu jump, we click the menu bar can not route to the corresponding component, so next to implement this function.

There are two ways to realize menu jump, the first is the NavMenu component provided by the jump.

The second option is to add router-link to the menu for redirecting.

This time I choose the first way to realize the jump, which requires two steps to complete, the first step is to enable the router on el-Menu; The second step is to set the navigation’s index property.

So let’s implement these two steps.

Enable the router on el-Menu

<! -- src/menu/leftMenu.vue -->
<! -- omit remaining unmodified code -->
<el-menu 
    :default-active="$route.path" 
    class="el-menu-vertical-demo"
    router
    :collapse="false">
    <MenuItem :route="routesInfo">
    </MenuItem>
</el-menu>
Copy the code

Set the index property of the navigation

First, I listed the index property value corresponding to each menu title.

The index value corresponds to the path value configured in the route for each menu

Homepage staff management staff statistics index = "/ employee/employeeStatistics" staff management index = "/ employee/employeeManage" attendance management Attendance statistics Index = "/ attendManage attendStatistics" attendance list index = "/ attendManage/attendList" exception management index = "/ attendManage/exceptManage" Employees statistics staff statistics index = "/ timeManage/timeStatistics" staff statistics index = "/ timeManage timeList" option on the index = "/ timeManage timeList/options1" Option 2 index = "/ timeManage timeList/options2"Copy the code

Then, recalling the previous recursive call to the component, the index of the navigation menu is set to Child. path. In order to see the value of Child. path, I add it to the right of the menu title to display it on the interface.

<! -- src/menu/menuItem.vue -->
<! -- omit remaining unmodified code -->
<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>
    <! Recursively call the component itself -->
    <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>
Copy the code

Also change the width of the menu bar from 200px to 400px.

<! -- src/menu/menuIndex.vue -->
<! -- omit remaining unmodified code -->
<el-aside width="400px">
    <LeftMenu></LeftMenu>                    
</el-aside>
Copy the code

And then let’s see what happens.

As you can see, the value of child.path is the path value configured in the route by the current menu (router.js).

So the problem comes. We sorted out the index attribute value corresponding to each menu title, but currently, the index value set is not up to the requirements. However, after careful observation, the index value set in the current menu is a little close to the normal value, except that the path value of the upper-level menu is missing. If the path value of the upper-level menu can be spliced with the path value of the current menu, the correct index value can be obtained.

The way to do this is to recursively pass the path of the current menu as an argument to the menuItem component.

<! -- src/menu/menuIndex.vue -->
<! Recursively call the component itself -->
<MenuItem 
    :route="child.children" 
    :basepath="child.path">
</MenuItem>
Copy the code

After passing the path of the current menu as an argument to the menuItem component, the next menu implementation gets the path of the previous menu. The component then concatenates the value of Basepath with the path value of the current menu as the index value of the current menu.

<! -- 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 is the path value of the current menu
        // getPath: Splices the path of the current menu with the path of the current menu
        getPath: function(routePath){
            return path.resolve(this.basepath, routePath); }}}</script>
Copy the code

Let’s look at the interface.

We can see that the index value of the second-level menu is no problem, but after a closer look, we find that the index value of the two third-level menus under time Management-time list is still wrong, and the path of the first-level menu of time management is missing.

The problem is that we have a problem calling basepath when the component itself is passed.

<! Recursively call the component itself -->
<MenuItem 
    :route="child.children" 
    :basepath="child.path">
</MenuItem>
Copy the code

Basepath passes only the path of the upper-level menu. In recursive second-level menus, index is the path value of the first-level menu + the path value of the second-level menu. Then, when we recurse the three-level menu, the value of index is the path value of the two-level menu + the path value of the three-level menu, which is why there are problems with the index values of the two three-level menus under time management – time list.

So the basepath value should be cumulative in recursion, not just the path value of the upper-level menu. So basepath’s value also needs to be processed through the getPath method to take advantage of the recursive algorithm.

<MenuItem 
    :route="child.children" 
    :basepath="getPath(child.path)">
</MenuItem>
Copy the code

The final complete code is shown below.

<! -- 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>
            <! Recursively call the component itself -->
            <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 is the path value of the current menu
        // getPath: Splices the path of the current menu with the path of the current menu
        getPath: function(routePath){
            return path.resolve(this.basepath, routePath); }}}</script>
Copy the code

Remove the rest of the debugging code

The final result

At the end of the article, the realization of the final effect in this show.

The three level menus of option 1 and Option 2 do not set Component in the route configuration. These two menus are only for the three level menus. In the final result demonstration, I have removed the two level menus configured in the route

Here unique-Opened is turned on for el-Menu in the leftMenu component

In the menuIndex component, change the width of the left menu bar to 200px

about

The author

Small potatoes biubiubiu

For a hard-working front-end novice, knowledge is limitless. Believe that you can always get where you want as long as you don’t stop learning

At the same time, I am also a person who likes small cats. There is a beautiful short female cat named Potato at home

Blog garden

www.cnblogs.com/HouJiao/

The Denver nuggets

Juejin. Cn/user / 243617…

Wechat official account

Unknown treasure program daughter

I want to share front-end knowledge with you in the most accessible language. Aspire to serious work, practical life, I hope you can harvest full here.

The author remarks

Small summary, welcome everyone guidance ~