前端的权限管理通常是左侧菜单部分的展示权限,以及基于功能的权限
vben实际上是动态生成了路由,然后再展示在左边的菜单栏,所以要编写权限相关的代码可以从动态生成路由处着手
buildRoutesAction方法分析
vben通过动态生成路由,控制菜单栏展示权限。核心方法是buildRoutesAction,该方法根据路由的meta.role属性过滤路由,从而生成不同角色的菜单。
搜索这个方法的签名可以发现被多个地方调用
此外,该方法还将多级路由简化为两级路由。
通过调试路由生成过程可以发现方法会检查路由下的meta.role属性来过滤路由,最后生成菜单
并且还对路由进行了操作,将多级路由转为两级路由
首先我们获取角色信息,在user.ts下的getUserInfoAction中vben已经撰写好了用户信息的获取逻辑。我们从这里开始编写代码。
1.将roles改为role
后端动态生成前端菜单方案
由于前端路由列表中的component字段无法直接转换为JSON并传递给后端,我们需要在router/route-map.ts中映射路由与组件的关系。
如果把前端的routeList直接转换成JSON,由后端来下发数据会有一个component字段丢失的问题。所以我们可以在router下建立一个route-map.ts文件用于映射路由与组件的关系
1 2 3 4 5 6 7 8 9
| import { EXCEPTION_COMPONENT, LAYOUT } from '@/router/constant';
export const ROUTE_MAP = { Dashboard: LAYOUT, Analysis: () => import('@/views/dashboard/analysis/index.vue'), Workbench: () => import('@/views/dashboard/workbench/index.vue'), NOT_FOUND: EXCEPTION_COMPONENT, };
|
在user.ts文件中兼容数据库角色字段的逻辑
接下来,通过硬编码的方式配置路由,并使用wrapperRouteComponent函数,将路由的name与组件关联。parseRouteRoles函数则解析roles字段,将其转为数组。
用硬编码的方式写入路由配置,通过wrapperRouteComponent函数将路由配置中的name属性与route-map中的component属性关联起来。并通过parseRouteRoles函数将roles字段文本转数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const wrapperRouteComponent = (routes) => { return routes.map((route) => { if (route.children && route.children.length > 0) { route.children = wrapperRouteComponent(route.children); } route.component = ROUTE_MAP[route.name] || ROUTE_MAP.NOT_FOUND; return route; }); };
const parseRouteRoles = (routes) => { return routes.map((route) => { if (route.children && route.children.length > 0) { route.children = parseRouteRoles(route.children); } if (route?.meta?.roles) { try { route.meta.roles = JSON.parse(route.meta.roles) } catch (e) { console.error(e) } } return route; }); }
|
对于PAGE_NOT_FOUND这类父路由名与子路由名相同的情况,我们可以通过addPageNotFoundAtFirst函数特殊处理:
1 2 3 4
| const addPageNotFoundAtFirst = (routes) => { routes.unshift(PAGE_NOT_FOUND_ROUTE); return routes; };
|
优化:使用现有的路由映射
手动添加路由与组件的映射关系会很繁琐。为简化操作,我们可以利用asyncRoutes变量,该变量已经包含了完整的路由和组件映射信息。通过遍历asyncRoutes,我们可以自动生成ROUTE_MAP。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import {asyncRoutes} from "@/router/routes";
const flatRoutes = {};
function generateRouteMap(routes) { return routes.map((item) => { if (item.children && item.children.length > 0) { generateRouteMap(item.children); } flatRoutes[item.name] = item['component']; }); }
generateRouteMap(asyncRoutes);
export const ROUTE_MAP = flatRoutes
|
后端菜单数据查询API开发
在menu目录下建立好menu entity、module、data文件并做好接口开发,然后在前端中对接
src/api/sys/menu 目录下现有获取菜单接口,所以在此处改造即可