[Боевая статья проекта] Vue объединяет конфигурацию маршрутизации для рекурсивной реализации строки меню

Vue.js
[Боевая статья проекта] Vue объединяет конфигурацию маршрутизации для рекурсивной реализации строки меню

Автор: маленькая картошка biubiubiu

Блог Сад:www.cnblogs.com/HouJiao/

Самородки:Талант /user/243617…

Публичный аккаунт WeChat: программа «Неизвестное сокровище» Юань

Содержание статей автора получено из его собственной практики.Если вы считаете, что это полезно для вас, вы можете поставить лайк для поощрения или оставить ценные комментарии.

предисловие

В ежедневной разработке реализована панель меню в проекте. Если вам нужно добавить новое меню, просто路由配置Добавьте маршрут к маршруту, чтобы реализовать добавление меню.

Я считаю, что все такие же, как я, и иногда вам захочется реализовать строку меню самостоятельно. Тогда сегодня я поделюсь с вами всей идеей и кодом строки меню, которую я реализовал.

В этой статье основное внимание уделяется обобщению и совместному использованию одной из строк меню.递归实现方式,代码的优化,菜单权限Если это выходит за рамки данной статьи, некоторые советы также будут даны в соответствующих частях статьи.Существуют отдельные способы написания, которые не рекомендуются, и я надеюсь, что вы не будете на них ссылаться.

В то же время могут быть некоторые подробные функции, которые не были обработаны или упомянуты, забудьте знать.

Окончательный эффект

Реализация этой строки меню содержит一级菜单,二级菜单а также三级菜单Эти три типа могут в основном охватывать различные требования к меню в проекте.

Это меню будет реализовано шаг за шагом от простого к сложному позже.

Простая реализация

мы все знаем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="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图标的路径а также产品名称.

Эффект интерфейса после завершения выглядит следующим образом.

Левая строка меню - 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>

Обратите внимание на код стиля меню, установите绝对定位, и установите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

о

автор

маленький картофель биубиубиу

Новичок в области фронтенда, который усердно учится, знания безграничны. Я твердо верю, что пока ты не перестаешь учиться, ты всегда можешь достичь того, чего хочешь.

В то же время он также человек, который любит котят.В доме есть красивая невысокая кошка по имени Тудо.

Блог Парк

www.cnblogs.com/HouJiao/

Наггетс

Талант /user/243617…

Публичный аккаунт WeChat

Неизвестная сокровищница Юань

Я хочу поделиться фронтенд-знаниями со всеми на максимально простом и понятном языке.
Решив серьезно заняться делом и быть стойким человеком, я надеюсь, что вы многого здесь добьетесь.

Примечание автора

Небольшое резюме, приветствую всеобщее руководство~