VUE-Element组件(二)NavMenu导航菜单--层级关系
NavMenu第一篇的几个例子,固定的菜单中菜单层级是在<el-menu>只写死的,动态菜单权限中层级也是由后台代码排好的(父子关系),到前端接收的menuList可以直接循环渲染了。现在后端只传回有权限的菜单编码,层级关系放在前端路由配置,然后根据路由与后端返回的菜单编码过滤用户权限。一、多级路由:之前的路由均为二级路由(当然也可以写成多级路由),如果需要配置多级路由,嵌套多层chi
NavMenu第一篇的几个例子,固定的菜单中菜单层级是在<el-menu>只写死的,动态菜单权限中层级也是由后台代码排好的(父子关系),到前端接收的menuList可以直接循环渲染了。现在后端只传回有权限的菜单编码,层级关系放在前端路由配置,然后根据路由与后端返回的菜单编码过滤用户权限。
一、多级路由:之前的路由均为二级路由(当然也可以写成多级路由),如果需要配置多级路由,嵌套多层children就可以了,其次要注意component。最外层component指向导航页即NavMenu菜单页面,其他的父级路由component指向一个空的路由跳转页面(否则会出现多个菜单)。如三级菜单:
children:[
{
path: '/my/user',
name: 'myUser',
meta: {
title: '用户',
noControl: true,
icon: 'el-icon-help',
},
component: MultiLevelMenu,
children: [
{
path: '/my/user1',
name: 'user1',
meta: {
title: '用户1页面',
noControl: true,
showAlways: true
},
component: MultiLevelMenu,
children: [
{
path: '/my/user1/user11',
name: 'overview',
meta: {
title: '用户11页面',
headerConfig: { isStartFlag: true, isUserCenter: true },
},
component: resolve => require(['@/views/user/index11'], resolve)
},
{
path: '/my/user1/user12',
name: 'overview',
meta: {
title: '用户12页面',
headerConfig: { isStartFlag: true, isUserCenter: true },
},
component: resolve => require(['@/views/user/index12'], resolve)
},
]
},
//详情页面
{
path: '/my/userDetail',
name: 'userDetail',
meta: {
title: '详情',
headerConfig: { isStartFlag: true, isUserCenter: true },
noControl: true,
hideInMenu: true,
isDetailPage: true,
},
component: resolve => require(['@/views/user/detail/index'], resolve)
}
]
}
]
空页面代码:
<!-- @format -->
<template>
<keep-alive><router-view></router-view></keep-alive>
</template>
<script>
export default {
name: 'midMenu'
};
</script>
<style scoped></style>
二、动态权限:后端返回有权限的菜单编码,前端匹配权限和菜单层级关系。
代码:
1、route路由:
这里我把新增的navbar菜单新建了一个asyncCodeMenu目录
(1)asyncCodeMenu/index.js:
/** @format */
//导航页
import MenuIndex from '@/views/asyncCodeNavbar';
//用户菜单
import UserMenu from './userMenu';
import SchoolMenu from './schoolMenu';
export default [
{
// 进入平台后的默认首页
path: '/asyncCodeNavbar',
name: 'asyncCodeNavbar',
code:'asyncCodeNavbar',
meta: {
title: '菜单',
noControl: true,
hideInMenu: true,
},
redirect: '/asyncCodeNavbar/main',
component: MenuIndex,
children: [
{
path: '/asyncCodeNavbar/main',
name: 'asyncCodeNavbarMain',
code:'asyncCodeNavbarMain',
meta: {
title: '首页',
noControl: true,
hideInMenu: true,
},
component: resolve => require(['@/views/asyncCodeNavbar/main/index'], resolve)
},
{
path: '/asyncCodeNavbar/address',
name: 'asyncCodeNavbarAddress',
code:'asyncCodeNavbarAddress',
meta: {
title: '地址',
icon: 'el-icon-check',
},
component: resolve => require(['@/views/asyncCodeNavbar/address/index'], resolve)
},
UserMenu,SchoolMenu
]
}
];
(2)asyncCodeMenu/schoolMenu.js:
/** @format */
import MultiLevelMenu from '@/views/asyncCodeNavbar/multiLevelMenu';
export default {
path: '/asyncCodeNavbar/school',
name: 'asyncCodeNavbarSchoolManage',
code:'asyncCodeNavbarSchoolManage',
meta: {
title: '学校',
noControl: true,
icon: 'el-icon-check',
showAlways: true
},
component: MultiLevelMenu,
children: [
{
path: '/asyncCodeNavbar/schoolInfo',
name: 'asyncCodeNavbarSchoolInfo',
code:'asyncCodeNavbarSchoolInfo',
meta: {
title: '学校管理',
noControl: true,
showAlways: true
},
component: MultiLevelMenu,
children: [
{
path: '/asyncCodeNavbar/schoolInfo/school1',
name: 'asyncCodeNavbarSchool1',
code:'asyncCodeNavbarSchool1',
meta: {
title: '学校1',
},
component: resolve => require(['@/views/asyncCodeNavbar/schoolManage/schoolInfo/school1/index'], resolve)
},
{
path: '/asyncCodeNavbar/schoolInfo/school2',
name: 'asyncCodeNavbarSchool2',
code:'asyncCodeNavbarSchool2',
meta: {
title: '学校2',
},
component: resolve => require(['@/views/asyncCodeNavbar/schoolManage/schoolInfo/school2/index'], resolve)
},
]
},
//详情页面
]
};
(3)asyncCodeMenu/userMenu.js:
/** @format */
import MultiLevelMenu from '@/views/asyncCodeNavbar/multiLevelMenu';
export default {
// 用户信息
path: '/asyncCodeNavbar/user',
name: 'asyncCodeNavbarUserInfo',
code:'asyncCodeNavbarUserInfo',
meta: {
title: '用户信息',
noControl: true,
icon: 'el-icon-check',
showAlways: true
},
component: MultiLevelMenu,
children: [
{
path: '/asyncCodeNavbar/user/user1',
name: 'asyncCodeNavbarUser1',
code:'asyncCodeNavbarUser1',
meta: {
title: '用户信息1',
},
component: resolve => require(['@/views/asyncCodeNavbar/user/user1/index'], resolve)
},
{
path: '/asyncCodeNavbar/user/user2',
name: 'asyncCodeNavbarUser2',
code:'asyncCodeNavbarUser2',
meta: {
title: '用户信息2',
},
component: resolve => require(['@/views/asyncCodeNavbar/user/user2/index'], resolve)
},
//详情页面
{
path: '/asyncCodeNavbar/user/userDetail',
name: 'asyncCodeNavbarUserDetail',
code:'asyncCodeNavbarUserDetail',
meta: {
title: '用户详情',
noControl: true,
hideInMenu: true,
isDetailPage: true,
},
component: resolve => require(['@/views/asyncCodeNavbar/user/user1/detail/index'], resolve)
}
]
};
(4)引用到router/index.js下:import AsyncCodeNavbar from './asyncCodeMenu/index'
import Vue from 'vue'
import Router from 'vue-router'
// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading
Vue.use(Router)
/* Layout */
import Layout from '../views/layout/Layout'
import AsyncCodeNavbar from './asyncCodeMenu/index'
export const constantRouterMap = [
{ path: '/404', component: () => import('@/views/404'), hidden: true },
{ path: '/login', component: () => import('@/views/login/index'), hidden: true },
{
path: '/midtest',
name: 'midtest',
hidden:true,
component: () => import('@/views/midtest/index'),
children: [
{
path: '/midtest',
redirect: 'midtest1'
},
{
path: '/midtest/midtest1',
name: 'midtest1',
component: () => import('@/views/midtest/midtest1')
},
{
path: '/midtest/midtest2',
name: 'midtest2',
component: () => import('@/views/midtest/midtest2')
},
{
path: 'midtest3',
name: 'midtest3',
component: () => import('@/views/midtest/midtest3')
},
{
path: 'midtest41',
name: 'midtest41',
component: () => import('@/views/midtest/midtest4/midtest41')
},
{
path: 'midtest42',
name: 'midtest42',
component: () => import('@/views/midtest/midtest4/midtest42')
},
{
path: 'midtest51',
name: 'midtest51',
component: () => import('@/views/midtest/midtest5/midtest51')
},
{
path: 'midtest521',
name: 'midtest521',
component: () => import('@/views/midtest/midtest5/midtest521')
},
{
path: 'midtest522',
name: 'midtest522',
component: () => import('@/views/midtest/midtest5/midtest522')
}
]
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
name: '首页',
icon: '首页',
hidden: true,
children: [{
path: '/dashboard',
component: () => import('@/views/dashboard/index')
}]
},
...AsyncCodeNavbar
]
export const asyncRouterMap = [
// { path: '/login', component: () => import('@/views/login/index'), hidden: true },
{
path: '/roleManage',
redirct: '/roleManage/index',
name: 'roleManage',
component: Layout,
meta: { title: '角色管理', authority: ['role_manage'] },
noDropdown: true,
children: [
{
path: 'index',
name: 'index',
component: () => import('@/views/roleManage/index'),
meta: { title: '首页', authority: ['role_manage'], keepAlive: false }
},
{
path: 'detail',
name: 'detail',
// hidden: true,
component: () => import('@/views/roleManage/detail'),
meta: { title: '详情', authority: ['role_manage'], keepAlive: false }
}
]
},
{
path: '/userManage',
redirct: '/userManage/index',
name: 'userManage',
component: Layout,
meta: { title: '用户管理', authority: ['user_manage'] },
noDropdown: true,
children: [
{
path: 'index',
name: 'index',
component: () => import('@/views/userManage/index'),
meta: { title: '用户管理', authority: ['user_manage'], keepAlive: false }
}
]
},
//分页查询
{
path: '/userPager',
redirct: '/pager/userpager',
name: 'userpager',
component: Layout,
meta: { title: '用户分页', authority: ['user_manage'] },
noDropdown: true,
children: [
{
path: 'index',
name: 'index',
component: () => import('@/views/pager/userpager'),
meta: { title: '用户分页', authority: ['user_manage'], keepAlive: false }
}
]
},
//...其他权限菜单...
]
export default new Router({
// mode: 'history', // 后端支持可开
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
})
2、页面:
(1)导航页:
<!-- @format -->
<template>
<el-container class="container">
<el-aside class="aside" width="200px">
<el-header style="height:50px">
</el-header>
<el-menu
style="width:200px"
:default-active="$route.path"
router
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
background-color="#538dd5"
text-color="#FFFFFF"
active-text-color="#7d9ec6"
>
<div v-for="item in menuList" :key="item.id">
<!--无子菜单-->
<el-menu-item :index="item.path" v-if="isNoChildrenMenuShow(item)" class="menuname">
<i :class="item.meta.icon"></i>
{{ item.meta.title }}
</el-menu-item>
<!--有二级菜单-->
<el-submenu v-if="item.children && item.children.length > 0" :index="item.id">
<template slot="title">
<i :class="item.meta.icon"></i>
<span class="menuname">{{ item.meta.title }}</span>
</template>
<!--是否有三级菜单-->
<div v-for="second in item.children" :key="second.id">
<!--无三级菜单-->
<el-menu-item :index="second.path" v-show="isNoChildrenMenuShow(second)" class="mini-menuname">
<i :class="second.meta.icon"></i><span>{{ second.meta.title }}</span>
</el-menu-item>
<!--有三级菜单-->
<el-submenu v-show="second.children && second.children.length > 0" :index="second.id" class="menuname">
<template slot="title"> <i :class="second.meta.icon"></i>{{ second.meta.title }} </template>
<el-menu-item
:index="third.path"
v-for="third in second.children"
:key="third.id"
class="mini-menuname"
>
<i :class="third.meta.icon"></i>{{ third.meta.title }}
</el-menu-item>
</el-submenu>
</div>
</el-submenu>
</div>
</el-menu>
</el-aside>
<el-container>
<el-header class="main-header" height="30px">
<div>顶部</div>
</el-header>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
import menus from '@/router/asyncCodeMenu/index.js'
export default {
data() {
return {
menuList: []
};
},
created() {
//这里省略接口请求,menuCodes为接口返回的权限
let menuCodes = [
'asyncCodeNavbarAddress',
'asyncCodeNavbarSchool2',
'asyncCodeNavbarUser2',
'asyncCodeNavbarSchoolManage'
];
this.menuList = this.filterAsyncRouter(menus, menuCodes);
console.info('菜单' + JSON.stringify(this.menuList));
console.info(this.menuList);
},
methods: {
/**
* 权限过滤
* routes所有路由
* userMenus 用户权限集合,菜单编码
*/
filterAsyncRouter(routes, userMenus) {
let res = [];
for (let index = 0; index < routes.length; index++) {
let route = routes[index];
if (!this.hasPermission(userMenus, route)) {
continue;
}
//有权限
//1、无子菜单
if (!route.children) {
const tmp = { ...route };
res.push(tmp);
continue;
}
//2、有子菜单
//2.1、父菜单是否需要展示
if (!route.meta.hideInMenu) {
const tmp = { ...route };
tmp.children = this.filterAsyncRouter(route.children, userMenus);
res.push(tmp);
continue;
}
//2.2、父菜单不展示,直接处理子菜单
route.children.forEach(child => {
let tmp = { ...child };
tmp = this.filterAsyncRouter(new Array(child), userMenus);
res = res.concat(tmp);
});
}
return res;
},
// hasPermission1(urlList, route) {
// //debugger;
// if (route.meta.noControl) return true; //该页面不需要权限控制
// return urlList.includes(route.path);
// },
/**
* 是否有权限看到
* userMenus 用户菜单权限集合
* route 路由对象
*/
hasPermission(userMenus, route) {
debugger;
//1、该路由不需要权限控制,且不是详情页面
if (route.meta.noControl && !route.meta.isDetailPage) {
//1.1该路由无子路由,则放开
if (!route.children || route.children.length === 0) {
return true;
}
//1.2有子路由,且用户权限含有子路由,则可见
let hasChildPermission = false;
for (let childIndex = 0; childIndex < route.children.length; childIndex++) {
hasChildPermission = this.hasPermission(userMenus, route.children[childIndex]);
if (hasChildPermission) {
return hasChildPermission;
}
}
return hasChildPermission;
}
//2、该路由需要权限控制
return userMenus.includes(route.code);
},
isNoChildrenMenuShow(item) {
if (!item.children || item.children.length === 0) {
if (item.meta) {
return !item.meta.hideInMenu;
}
return true;
}
return false;
},
goToMain() {
this.$router.push({ name: 'main' });
},
goToJgpt() {
this.$router.push({ name: 'jgpt' });
},
goToDashboardHandler() {
this.$router.replace({ name: 'dashboard' });
}
}
};
</script>
<style scoped >
::-webkit-scrollbar {
width: 1px;
}
::-webkit-scrollbar-track {
--webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
background: #538dd5;
--webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
}
::-webkit-scrollbar-thumb:window-inactive {
background: #538dd5;
}
.container {
height: 100%;
}
.aside {
height: 100%;
background-color: #538dd5;
}
.el-header {
padding: 0px;
}
.header.white {
background-color: #538dd5;
}
.el-icon-right {
font-size: 16px;
}
i {
color: #ffffff;
font-size: 16px;
}
.el-menu-item.is-active {
background-color: #366092 !important;
}
.main-header {
padding: 0px;
padding-right: 10px;
}
.btn-txt {
height: 100%;
line-height: 30px;
font-size: 16px;
float: right;
padding-left: 15px;
text-decoration: none;
padding-top: 0px;
font-weight: bold;
}
.el-main {
padding: 20px;
padding-top: 0px;
}
.menuname {
color: #ffffff;
font-size: 16px;
}
.mini-menuname {
color: #ffffff;
font-size: 12px;
}
</style>
(2)默认页面:/main/index
<template>
<div>首页</div>
</template>
(3)用户页:
<template>
<div>用户2-二级菜单
<el-button @click="goToDetail">详情</el-button>
</div>
</template>
<script>
export default {
methods:{
goToDetail(){
this.$router.push({path:'/asyncCodeNavbar/user/userDetail'})
}
}
}
</script>
(4)空路由页:/multiLevelMenu/index
<!-- @format -->
<template>
<keep-alive><router-view></router-view></keep-alive>
</template>
<script>
export default {
name: 'mid'
};
</script>
<style scoped></style>
三、测试:如果不设置中间空路由,如
则点击用户菜单会出现菜单嵌套
如果空路由改成具体的页面,如
则点击用户菜单也跳转到该页
更多推荐
所有评论(0)