外观
路由与菜单
概述
前端路由基于 vue-router 构建,并复用 Vben Admin 的 @vben/access 能力实现「权限驱动的动态路由 + 菜单」。
本项目的菜单采用后端驱动模式(preferences.app.accessMode = 'backend'):用户登录后,后端按角色返回菜单树,前端将其转换为路由与侧边菜单,真正做到「菜单、路由、权限」由后台统一配置。
路由分层与类型
平台把路由分为三类,分别对应不同的处理方式:
- 核心路由(core):登录、注册、404 等页面,挂在不同于业务布局的
AuthPageLayout下,不参与权限校验,始终可用。 - 访问路由(access):包含
routes/modules/*.ts的动态路由与可选的静态路由,是业务页面所在,需要登录且按权限动态注册。 - 外部路由(external):可脱离主布局独立渲染(如内嵌到其他系统),默认未启用,不出现在菜单中。
root 路由(path: '/')使用 BasicLayout,是所有业务页面的父级容器;子级无需再声明布局,渲染时自动嵌套其中。
目录结构
路由相关代码集中在 apps/web-ele/src/router/:
index.ts:创建vue-router实例,挂载守卫与百度统计,支持hash/history两种模式(VITE_ROUTER_HISTORY)。guard.ts:路由守卫,包含通用守卫(进度条、页面加载标记)与权限访问守卫(登录校验、动态路由生成)。access.ts:封装generateAccessible,提供pageMap(views/**页面映射)、layoutMap(BasicLayout/IFrameView)以及后端菜单拉取逻辑。tongji.ts:百度统计集成。routes/core.ts:核心路由定义(根布局、认证页、404)。routes/index.ts:汇总核心/外部/404 路由,并通过import.meta.glob自动聚合modules/下的动态路由。routes/modules/*.ts:按业务域拆分的本地路由声明(dashboard、system、ai、bpm、infra 等)。
布局文件在 apps/web-ele/src/layouts/:
布局
BasicLayout:业务主框架,含侧边菜单、顶部导航、标签页(Tabbar)、通知、用户下拉、租户切换、锁屏等。本项目在layouts/basic.vue中基于@vben/layouts的BasicLayout做定制(站内信轮询、租户切换、水印等)。AuthPageLayout:登录/注册/找回密码等认证页布局。IFrameView:通过layoutMap注册,用于渲染后端菜单中指定的内嵌 iframe 页面(meta.iframeSrc)。
权限模式与后端菜单
项目覆盖偏好配置 accessMode: 'backend',完整链路如下:
- 登录成功后
authStore.fetchUserInfo()调用getAuthPermissionInfoApi(),拿到user、roles、menus、permissions。 menus写入accessStore.accessMenus(此时为后端AppRouteRecordRaw形态)。- 首次访问业务页面时,
guard.ts的权限守卫触发generateAccess:- 读取
accessStore.accessMenus,经convertServerMenuToRouteRecordStringComponent把后端菜单(含path/component/icon/sort/visible)转换成带component字符串的路由树。 generateRoutesByBackend把字符串组件映射到真实组件(layoutMap处理布局、pageMap处理views/**页面,匹配失败回退 404)。generateMenus基于最终路由树生成菜单(按order排序、过滤hideInMenu)。
- 读取
- 生成的路由挂到
root下,菜单写入accessStore.setAccessMenus/Routes,并标记isAccessChecked,避免重复生成。
后端菜单的特殊处理(见 generate-menus.ts):
- 外链菜单(
isHttpUrl(path))若带_iframe参数则走iframeSrc内嵌,否则作为link新窗口跳转。 component = 'Layout'或顶级节点会被替换为BasicLayout;非顶级且含子级的节点清空组件,仅作目录。component可带?key=value形式的query,自动解析为meta.query。
路由 Meta 字段
菜单与页面行为主要由 RouteMeta 控制,常用字段:
title:菜单/标签页标题(建议用$t('...')支持国际化)。icon:菜单图标(iconify 名称,如lucide:layout-dashboard)。order:同级排序,数值越小越靠前。hideInMenu:是否在菜单中隐藏(页面仍可访问)。hideInTab:是否在标签页中隐藏。hideInBreadcrumb:是否在面包屑中隐藏。affixTab:是否固定标签页。keepAlive:是否开启页面缓存。link/iframeSrc/openInNewWindow:外链与内嵌页面。hideChildrenInMenu:折叠子菜单,自身作为可点击项。authority:限定可访问的角色标识。ignoreAccess:跳过权限校验直接可访问。menuVisibleWithForbidden:菜单可见但访问时重定向到 403(提示用户申请权限)。activePath:高亮指定父级菜单。
完整定义参见 packages/@core/base/typings/src/vue-router.d.ts。
前端本地路由(modules)
除后端菜单外,routes/modules/*.ts 用于声明前端本地路由。典型场景:首页仪表盘、个人中心、以及希望在前端固定、不依赖后台配置即可访问的页面。
示例(routes/modules/dashboard.ts):
ts
const routes: RouteRecordRaw[] = [
{
meta: {
icon: 'lucide:layout-dashboard',
order: -1,
title: $t('page.dashboard.title'),
},
name: 'Dashboard',
path: '/dashboard',
children: [
{
name: 'Workspace',
path: '/workspace',
component: () => import('#/views/dashboard/workspace/index.vue'),
meta: { icon: 'carbon:workspace', title: $t('page.dashboard.workspace') },
},
],
},
];提示:本项目以后端菜单为主。本地
modules路由在accessMode=backend时一般只承载少量固定页面(如 Dashboard、Profile);业务功能菜单应在后台「系统管理 → 菜单」配置。
新增页面与菜单
方式一:后端菜单(推荐,业务功能)
- 在
views/对应目录下创建.vue页面。 - 后台「系统管理 → 菜单」新增菜单:填写名称、路由
path、组件component(页面相对于views的路径,如system/user/index)、图标、排序、是否显示等;目录型菜单component填Layout。 - 配置对应角色的菜单权限,刷新即可在侧边菜单看到并访问。
方式二:前端本地路由(固定页面)
- 在
views/对应目录创建页面。 - 在
routes/modules/新建或编辑.ts,按上面的格式声明path、name、component、meta。 - 路由会经
mergeRouteModules自动聚合,无需手动注册。
相关文件速查
- 路由入口:
apps/web-ele/src/router/ - 布局:
apps/web-ele/src/layouts/ - 后端菜单类型与接口:
apps/web-ele/src/api/system/menu/ - 菜单/路由生成工具:
packages/utils/src/helpers/generate-menus.ts、generate-routes-backend.ts - 动态生成核心:
packages/effects/access/src/accessible.ts - 访问状态:
packages/stores/src/modules/access.ts - 权限模式配置:
apps/web-ele/src/preferences.ts、packages/@core/preferences/