Skip to content

路由与菜单 ​

概述 ​

前端路由基于 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',完整链路如下:

  1. 登录成功后 authStore.fetchUserInfo() 调用 getAuthPermissionInfoApi(),拿到 user、roles、menus、permissions。
  2. menus 写入 accessStore.accessMenus(此时为后端 AppRouteRecordRaw 形态)。
  3. 首次访问业务页面时,guard.ts 的权限守卫触发 generateAccess:
    • 读取 accessStore.accessMenus,经 convertServerMenuToRouteRecordStringComponent 把后端菜单(含 path/component/icon/sort/visible)转换成带 component 字符串的路由树。
    • generateRoutesByBackend 把字符串组件映射到真实组件(layoutMap 处理布局、pageMap 处理 views/** 页面,匹配失败回退 404)。
    • generateMenus 基于最终路由树生成菜单(按 order 排序、过滤 hideInMenu)。
  4. 生成的路由挂到 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);业务功能菜单应在后台「系统管理 → 菜单」配置。

新增页面与菜单 ​

方式一:后端菜单(推荐,业务功能) ​

  1. 在 views/对应目录 下创建 .vue 页面。
  2. 后台「系统管理 → 菜单」新增菜单:填写名称、路由 path、组件 component(页面相对于 views 的路径,如 system/user/index)、图标、排序、是否显示等;目录型菜单 component 填 Layout。
  3. 配置对应角色的菜单权限,刷新即可在侧边菜单看到并访问。

方式二:前端本地路由(固定页面) ​

  1. 在 views/对应目录 创建页面。
  2. 在 routes/modules/ 新建或编辑 .ts,按上面的格式声明 path、name、component、meta。
  3. 路由会经 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/