在Vue.js框架的系统中,菜单和路由一般是对应的,当你在系统中点击一个菜单的时候,这个菜单背后一定配置了一个对应的路由,通过路由才能完成页面之间的相互切换。
路由鉴权
在访问菜单的时候要通过路由守卫进行拦截,判断当前所访问的路由也就是该路由所对应的菜单有没有访问权限,这个过程称之为 路由鉴权。
路由鉴权主要有两种模式,前端路由鉴权 与 动态路由鉴权
前端路由鉴权
前端路由鉴权有两种模式:
- 完全由前端控制,给每个菜单绑定一个权限组,登录人员会有一个权限组名,一般跟角色绑定,例如:管理员(admin)、普通人员等。所有菜单和路由在前端配置,路由切换的时候进行路由拦截,判断当前要访问的路由所属权限组与当前登录人所属权限组是否一致,一致则通过,否则就不通过。
- 逻辑大同小异,在方式1的基础上更加细化,当前登录人能访问什么菜单直接由后端接口返回,在人员管理的时候会给当前人员分配菜单,然后登录的时候通过接口查询当前登录人能访问的所有菜单。对于路由还是在前端配置,路由切换的时候进行路由拦截,判断当前要访问的路由所对应的菜单是不是在登录后接口查询返回的菜单列表中,如果在就通过,不在就不通过。
前端路由鉴权的缺点:
- 需要写大量的逻辑判断代码,用于判断哪些能通过,哪些不能通过。
- 每次访问模块都需要鉴定权限,模块数量过多时会影响系统性能。
- 前端需要把所有的路由都提前考虑好,需要写全量路由表,不方便路由调整。
动态路由鉴权
通过前面的前端路由鉴权以及其缺点,于是我们产生了另外一种思路,动态路由,后端返回了哪些菜单,就自动根据这些菜单生成对应的路由,这样路由拦截的时候也不用考虑权限,不用去做大量的逻辑判断,也做到了真正的权限隔离,登录返回了几个菜单,你的系统里面就只有这么几个路由,其他任何的访问都是404。
流程如下图:
菜单结构
在clzy-web 中,菜单需要遵循以下的结构:
[{
path: '/',
name: '_home',
component: 'BasicLayout',
title: '首页',
meta: {
},
children: [{
path: '/home',
name: 'home',
component: 'dashboard/console',
title: '首页',
meta: {
icon: 'dashboard'
}
}]
},
{
path: '/example',
component: 'BasicLayout',
name: 'fsfse',
title: '案例',
meta: {
icon: 'example'
},
children: [{
path: 'table',
name: '23dd',
title: '案例二级菜单1',
component: '',
meta: {
icon: 'table'
},
children: [{
path: 'third',
name: 'd334',
title: '表格示例页面',
component: 'example/table/third',
meta: {
}
}]
}
]
},
{
path: '/_form',
component: 'BasicLayout',
name: 'ijjj',
title: '表单',
meta: {
icon: ''
},
children: [{
path: '/form',
name: 'vvddd',
title: '表单',
component: 'form',
meta: {
icon: 'form'
}
}]
},
{
path: '/blank',
component: 'blank',
name: 'ijadwadwajj',
title: '新页签打开路由',
meta: {
icon: '',
target: 'blank'
}
},
{
path: '/_frame',
component: 'BasicLayout',
name: 'ddfffafefe',
title: '外部链接',
meta: {
icon: '',
title: ''
},
children: [{
path: '/iview',
name: 'dwdjdjdjd',
title: '内嵌IView',
component: 'https://www.iviewui.com?tf=99',
meta: {
icon: '',
target: 'self'
}
}, {
path: 'http://www.baidu.com',
name: 'gdhjahgddwyw',
title: '外部打开百度',
component: '',
type: 'blankHref',
meta: {
href: '',
icon: '',
target: 'blank'
}
}]
}]
菜单参数
path: 完整的页面路径,可以为 http:// 或 https:// 开头的外部链接,将直接打开外部链接
name: 不重复的菜单名称,注意:一定不能重复,后续会用到对菜单的判定会使用该属性,所以一定不能为空或者重复。
title: 菜单标题
icon: (选填)菜单图标,该选项传入的图标需要在 src/assets/icons 中
img:(选填)菜单图标,该选项设置的是一个具体的图片文件,使用该选项,不可以设置 icon 选项
meta: 路由元信息, 此项不能为空。
- title: 浏览器标签页会读取的当前路由的名称,一般跟上述 title 属性一致即可
- auth: 支持Boolean 和 Array两种类型,如果此属性设置为 false ,则该菜单对应的路由不需要鉴权,可以直接访问。
- href: 外部链接地址
- target: 打开方式,blank为新页签窗口打开,self为当前页签窗口打开
component: 该菜单所对应的路由指向的组件
- BasicLayout: 当值为BasicLayout时,该菜单所对应的路由指向的组件是主框架。
- -:当值不为BasicLayout的时候,就表明该菜单所对应的路由指向的组件是我们自己开发的页面,路径为 src/pages/ + component的值 + /index.vue
- 可以为 http:// 或 https:// 开头的在线链接,打开外链
children: 子菜单
静态菜单配置
采用 静态菜单 的模式需要在系统中手动配置菜单和路由,当然在进行路由鉴权的时候也只能采用 静态路由鉴权 的模式。
配置菜单
静态菜单配置在 src/menu 目录下,入口文件是 sider.js,菜单所需参数与上述 菜单参数 除开可以不配置 component 以外,其他保持一致。因为静态菜单的方式,路由也需要在本地配置, component 参数会在路由配置的时候来指定。
菜单一般比较多,所以建议拆分模块,便于维护。以 example.js 为例,创建文件 src/menu/modules/example.js:
export default {
path: '/example',
title: '案例',
name: 'example',
icon: '',
meta: {
title: '案例'
},
children: [{
path: 'example1',
title: '案例二级菜单1',
name: 'example1',
icon: '',
meta: {
title: '案例二级菜单1'
},
children: [{
path: 'table',
title: '表格示例页面',
name: 'table',
icon: '',
meta: {
title: '表格示例页面'
}
}]
}, {
path: 'example2',
title: '案例二级菜单2',
name: 'example2',
icon: '',
meta: {
title: '案例二级菜单2'
}
}]
}
并将它引入菜单入口文件 src/menu/sider.js 中
// 案例模块的路由
import example from './modules/example';
export default [
home,
example,
outlink,
log,
{
title: '新页签打开路由',
path: '/blank',
name: 'blank',
meta: {
frame: 'blank',
title: '新页签打开路由'
}
}
];
配置路由
本地路由配置的入口文件为:src/router/modules/local-routes.js 跟本地菜单配置一样,也可以分模块进行配置。
import BasicLayout from '@/layouts/basic-layout/layout.vue';
import EmptyLayout from '@/layouts/empty-layout';
export default [{
path: '/_home',
name: '_home',
type: 'menu',
component: BasicLayout,
title: '首页',
meta: {
title: '首页'
},
children: [{
path: '/home',
name: 'path',
title: '首页',
type: 'menu',
meta: {
title: '首页'
},
component: () => import('@/pages/dashboard/console/index')
}]
}, {
path: '/example',
title: '案例',
name: 'example',
type: 'menu',
component: BasicLayout,
meta: {
title: '案例'
},
children: [{
path: 'example1',
title: '案例二级菜单1',
type: 'menu',
name: 'example1',
meta: {
title: '案例二级菜单1'
},
component: EmptyLayout,
children: [{
path: 'table',
title: '表格示例页面',
type: 'menu',
name: 'table',
component: () => import('@/pages/example/table/third/index.vue'),
meta: {
title: '表格示例页面'
}
}]
}, {
path: 'example2',
title: '案例二级菜单2',
type: 'menu',
name: 'example2',
component: () => import('@/pages/example/tree/index'),
meta: {
title: '案例二级菜单2'
}
}]
}]
动态菜单配置
在clzy-web中已经内置支持了动态菜单和动态路由的配置,只需要在 src/setting.js 中开启 dynamicSiderMenu 参数。然后在登录的时候调用接口获取出动态的菜单,菜单格式与上述 菜单结构 章节中描述的结构保持一致即可,然后调用根据菜单动态生成动态路由的方法动态生成路由,无需在在本地进行配置即可以达到与 静态菜单和静态路由相同的效果。
如何自动生成动态路由,以及菜单和路由数据是如何存储交互等相关具体信息请查阅
培训/分享-Vue动态路由
章节。