本篇基于 有来商城 youlai-mall 微服务项目,经过对vue-element-admin的权限菜单模块理解个性定制其后台接口,实现对vue-element-admin工程几乎不作改动的状况下,无缝接入后台接口实现动态权限菜单的加载。html
在进行接下来的工做前,咱们须要对原生的vue-element-admin项目改造,移除mock连通后台接口,具体可参考我这篇文章 vue-element-admin实战 | 第一篇: 移除mock接入后台,搭建有来商城youlai-mall先后端分离管理平台,若是在过程当中有遇到问题,欢迎下方留言。前端
至于上文提到的对vue-element-admin几乎不作改动即可实现咱们此篇文章的目的是否是我在扯,决定权给各位,我把对vue-element-admin项目改动的地方经过比对工具比对截图放上来。vue
先声明vue-element-admin这次改动的地方除了一个获取权限菜单的接口以外,剩余的改动全在 src/store/modules/permission.js 文件中。webpack
看到了吗,能够说仅对vue-element-admin作两处改动,再加上对getRoutes调用后台接口返回的菜单数据的分析,就能够理解和实现动态权限菜单的加载了。git
改动代码片断 + 注释说明github
import {list as getRoutes} from '@/api/admin/menu' import Layout from '@/layout' generateRoutes({commit}, roles) { return new Promise(resolve => { // 请求后台数据替换src/router/index.js的asyncRoutes异步路由 getRoutes({mode: 3}).then(response => { // filterAsyncRoutes方法做权限过滤和数据转换,roles为登陆用户角色ID集合,如:[1,2] let accessedRoutes = filterAsyncRoutes(response.data, roles) commit('SET_ROUTES', accessedRoutes) resolve(accessedRoutes) }) }) } // 递归权限过滤和数据转换 export function filterAsyncRoutes(routes, roles) { const res = [] routes.forEach(route => { const tmp = {...route} if (hasPermission(roles, tmp)) { const component = tmp.component if (route.component) { if (component == 'Layout') { tmp.component = Layout } else { // 接口组件字符串转换成组件对象 tmp.component = (resolve) => require([`@/views/${component}`], resolve) } if (tmp.children) { tmp.children = filterAsyncRoutes(tmp.children, roles) } } res.push(tmp) } }) return res }
接下来经过后台接口替换配置在src/router/index.js文件中asyncRoutes异步路由。web
首先分析下异步路由的数据结构:数据库
经过对上图异步路由的数据观察和了解,得出如下几点:json
完成以上的数据分析,接下来就是接口的具体实现了,有关SQL和完整代码请点击 有来商城 youlai-mall 拉取便可,如下仅仅贴出关键代码和分析。后端
SysMenuMapper
获取菜单列表和对应访问权限的角色ID的集合
SysMenuServiceImpl
将菜单转换成路由,递归生成父子结构树
SysMenuController
REST对外提供接口
使用接口测试工具测试, http://localhost:9999/youlai-admin/menus?mode=3 ,这里经过网关方式访问,详情请知悉有来商城 youlai-mall 项目。
完整返回数据,看看是否是很匹配asyncRoutes异步路由的数据格式了
{ "code": "00000", "data": [{ "path": "/admin", "component": "Layout", "alwaysShow": true, "name": "系统管理", "meta": { "title": "系统管理", "icon": "documentation", "roles": [2, 1] }, "children": [{ "path": "user", "component": "admin/user", "alwaysShow": false, "name": "用户管理", "meta": { "title": "用户管理", "icon": "user", "roles": [1] } }, { "path": "role", "component": "admin/role", "alwaysShow": false, "name": "角色管理", "meta": { "title": "角色管理", "icon": "peoples", "roles": [2, 1] } }, { "path": "dept", "component": "admin/dept", "alwaysShow": false, "name": "部门管理", "meta": { "title": "部门管理", "icon": "tree", "roles": [1, 2] } }, { "path": "menu", "component": "admin/menu", "alwaysShow": false, "name": "菜单管理", "meta": { "title": "菜单管理", "icon": "tree-table", "roles": [1, 2] } }, { "path": "dict", "component": "admin/dict", "alwaysShow": false, "name": "字典管理", "meta": { "title": "字典管理", "icon": "education", "roles": [1, 2] } }] }], "msg": "一切ok" }
最后要作的就是将组件(component)路径字符串转换成组件对象便可,再次贴出在上文permission.js改造的代码:
if (component == 'Layout') { tmp.component = Layout } else { // 接口组件字符串转换成组件对象 tmp.component = (resolve) => require([`@/views/${component}`], resolve) }
有一点须要注意的是上面组件动态导入不能使用import,以下:
tmp.component = () => import(`@/views/${component}`)
否则会报如下错误
Error: Cannot find module '@/views/***'
缘由是webpack不支持import动态导入了,可是以前确实是能够的,因此有些让人想不通,这里使用require动态导入组件便可。
参看当前用户拥有的角色ID为2
用户管理要求只有角色ID为1才能有其访问权限
测试下用户登陆控制台可否看到“用户管理”菜单
怎么样,只拥有角色ID为2的角色是看不到须要角色ID为1才能访问的“用户管理”菜单的,也就证实了咱们成功经过接入后台接口实现了权限菜单的动态加载的目的。
其实有个问题值得去思考下的。为何咱们在从后台获取所有菜单列表的时候须要关联查询出有对应访问权限的角色ID的集合,而不是说动态传入当前登陆用户的信息去查询该用户拥有的菜单集合呢?这实际上是两种实现方式,区别在于第一种每一个用户都要获取所有菜单数据,而第二种方式针对每一个用户加载其拥有权限的菜单数据。单看的话第二种方式确定优于第一种,但这里咱们选用的是第一种加载所有菜单数据。若是你结合缓存去看的话天然就想的通了,全部用户共享缓存的同一份菜单数据,仍是说缓存针对每一个用户存放一份菜单缓存又或者用户每次都去请求数据库。后面计划把菜单数据存放到缓存Redis中而后作一个分析比较。
附完整代码:
有来商城管理前端:youlai-mall-admin-web
写了这么多其实也是想给本身的项目打个广告,更但愿对你们有所帮助,对于技术人来讲少走弯路真的很重要,喜欢的朋友给个star,对我来讲这份帮助更是本身继续下去的动力,因此谢谢了。有啥问题下方留言,或直接联系我(微信号:haoxianrui)。