# Nova Admin 路由系统文档 ## 概述 Nova Admin 基于 Vue Router 4 构建了一套完整的路由管理系统,支持静态路由和动态路由,具备完善的路由守卫、权限控制、菜单生成等功能。 ## 🏗️ 路由架构 ### 核心组件 1. **路由配置** (`src/router/`) 2. **路由守卫** (`src/router/guard.ts`) 3. **路由状态管理** (`src/store/router/`) 4. **菜单生成** (`src/store/router/helper.ts`) 5. **标签页管理** (`src/store/tab.ts`) ### 文件结构 ``` src/router/ ├── index.ts # 路由主配置文件 ├── guard.ts # 路由守卫 ├── routes.inner.ts # 内置路由(登录、错误页等) └── routes.static.ts # 静态路由配置 src/store/router/ ├── index.ts # 路由状态管理 └── helper.ts # 路由处理辅助函数 ``` ## 🛣️ 路由类型 ### 1. 内置路由 (Inner Routes) 位置:`src/router/routes.inner.ts` 包含系统基础路由: - 根路由 (`/`) - 登录页 (`/login`) - 错误页 (`/403`, `/404`, `/500`) - 通配符路由 (404 处理) ```typescript export const routes: RouteRecordRaw[] = [ { path: '/', name: 'root', redirect: '/appRoot', }, { path: '/login', name: 'login', component: () => import('@/views/login/index.vue'), meta: { title: '登录', withoutTab: true, }, }, // ... 错误页路由 ] ``` ### 2. 静态路由 (Static Routes) 位置:`src/router/routes.static.ts` 业务功能路由配置: ```typescript export const staticRoutes: AppRoute.RowRoute[] = [ { name: 'monitor', path: '/dashboard/monitor', title: '仪表盘', requiresAuth: true, icon: 'icon-park-outline:anchor', menuType: 'page', componentPath: '/dashboard/monitor/index.vue', id: 3, pid: null, }, // ... 其他业务路由 ] ``` ### 3. 动态路由 (Dynamic Routes) 通过 API 从后端获取的路由配置,结构与静态路由相同。 ## 📋 路由数据结构 ### AppRoute.RowRoute 接口 ```typescript interface AppRoute.RowRoute { /** 路由名称 */ name: string /** 路由路径 */ path: string /** 页面标题 */ title: string /** 是否需要认证 */ requiresAuth?: boolean /** 访问角色 */ roles?: Entity.RoleType[] /** 图标 */ icon?: string /** 菜单类型 */ menuType?: 'dir' | 'page' /** 组件路径 */ componentPath?: string | null /** 路由ID */ id: number /** 父路由ID */ pid: number | null /** 是否隐藏 */ hide?: boolean /** 排序权重 */ order?: number /** 外链地址 */ href?: string /** 激活菜单 */ activeMenu?: string /** 不显示标签页 */ withoutTab?: boolean /** 固定标签页 */ pinTab?: boolean /** 是否缓存 */ keepAlive?: boolean } ``` ### 路由元信息 (Meta) ```typescript interface RouteMeta { /** 页面标题 */ title: string /** 图标 */ icon?: string /** 是否需要认证 */ requiresAuth?: boolean /** 访问角色 */ roles?: Entity.RoleType[] /** 是否缓存 */ keepAlive?: boolean /** 是否隐藏 */ hide?: boolean /** 排序权重 */ order?: number /** 外链地址 */ href?: string /** 激活菜单 */ activeMenu?: string /** 不显示标签页 */ withoutTab?: boolean /** 固定标签页 */ pinTab?: boolean /** 菜单类型 */ menuType?: 'dir' | 'page' } ``` ## 🔄 路由处理流程 ### 1. 路由初始化流程 ```mermaid sequenceDiagram participant App as 应用启动 participant Router as 路由系统 participant Guard as 路由守卫 participant Store as RouteStore participant API as 后端API App->>Router: 创建路由实例 Router->>Guard: 安装路由守卫 App->>Store: 初始化路由状态 Note over Router,Store: 用户访问路由时 Router->>Guard: beforeEach 触发 Guard->>Store: 检查路由是否初始化 Store->>Store: initAuthRoute() alt 动态路由模式 Store->>API: fetchUserRoutes() API-->>Store: 返回用户路由数据 else 静态路由模式 Store->>Store: 使用 staticRoutes end Store->>Store: createRoutes() 生成路由 Store->>Router: addRoute() 注册路由 Store->>Store: createMenus() 生成菜单 Guard->>Router: next() 继续导航 ``` ### 2. 路由守卫执行流程 ```mermaid flowchart TD A[路由跳转] --> B{外链路由?} B -->|是| C[打开新窗口] B -->|否| D[开始 LoadingBar] D --> E{登录页?} E -->|是| F[直接放行] E -->|否| G{requiresAuth=false?} G -->|是| F G -->|否| H{需要认证且未登录?} H -->|是| I[重定向到登录页] H -->|否| J{路由已初始化?} J -->|否| K[初始化路由] K --> L{是404页面?} L -->|是| M[重新导航到正确路径] L -->|否| N[继续导航] J -->|是| O{已登录访问登录页?} O -->|是| P[重定向到首页] O -->|否| N F --> N N --> Q[设置菜单高亮] Q --> R[添加标签页] R --> S[设置页面标题] S --> T[结束 LoadingBar] ``` ## 🎯 路由守卫详解 ### beforeEach 守卫 位置:`src/router/guard.ts` #### 主要功能 1. **外链处理**: 检测并打开外部链接 2. **进度条控制**: 开始 LoadingBar 3. **登录验证**: 检查用户认证状态 4. **路由初始化**: 动态加载和注册路由 5. **重定向处理**: 处理各种重定向场景 #### 核心逻辑 ```typescript router.beforeEach(async (to, from, next) => { // 1. 外链处理 if (to.meta.href) { window.open(to.meta.href) next(false) return } // 2. 开始进度条 appStore.showProgress && window.$loadingBar?.start() // 3. 登录验证 const isLogin = Boolean(local.get('accessToken')) if (to.meta.requiresAuth === true && !isLogin) { const redirect = to.name === '404' ? undefined : to.fullPath next({ path: '/login', query: { redirect } }) return } // 4. 路由初始化 if (!routeStore.isInitAuthRoute) { await routeStore.initAuthRoute() if (to.name === '404') { next({ path: to.fullPath, replace: true }) return } } // 5. 登录页重定向 if (to.name === 'login' && isLogin) { next({ path: '/' }) return } next() }) ``` ### beforeResolve 守卫 ```typescript router.beforeResolve((to) => { // 设置菜单高亮 routeStore.setActiveMenu(to.meta.activeMenu ?? to.fullPath) // 添加标签页 tabStore.addTab(to) // 设置当前标签页 tabStore.setCurrentTab(to.fullPath) }) ``` ### afterEach 守卫 ```typescript router.afterEach((to) => { // 设置页面标题 document.title = `${to.meta.title} - ${title}` // 结束进度条 appStore.showProgress && window.$loadingBar?.finish() }) ``` ## 🏪 路由状态管理 ### RouteStore 状态 ```typescript interface RoutesStatus { /** 路由是否已初始化 */ isInitAuthRoute: boolean /** 菜单数据 */ menus: MenuOption[] /** 原始路由数据 */ rowRoutes: AppRoute.RowRoute[] /** 当前激活菜单 */ activeMenu: string | null /** 缓存路由 */ cacheRoutes: string[] } ``` ### 核心方法 #### initAuthRoute() 初始化认证路由的完整流程: ```typescript async initAuthRoute() { this.isInitAuthRoute = false // 1. 获取路由数据 const rowRoutes = await this.initRouteInfo() if (!rowRoutes) return this.rowRoutes = rowRoutes // 2. 生成实际路由并注册 const routes = createRoutes(rowRoutes) router.addRoute(routes) // 3. 生成侧边菜单 this.menus = createMenus(rowRoutes) // 4. 生成路由缓存 this.cacheRoutes = generateCacheRoutes(rowRoutes) this.isInitAuthRoute = true } ``` #### initRouteInfo() 根据配置模式获取路由信息: ```typescript async initRouteInfo() { if (import.meta.env.VITE_ROUTE_LOAD_MODE === 'dynamic') { // 动态路由:从 API 获取 const userInfo = local.get('userInfo') const { data } = await fetchUserRoutes({ id: userInfo.id }) return data } else { // 静态路由:使用本地配置 this.rowRoutes = staticRoutes return staticRoutes } } ``` ## 🎨 菜单生成 ### 菜单生成流程 ```typescript export function createMenus(routes: AppRoute.RowRoute[]) { const { hasPermission } = usePermission() // 1. 权限过滤 let resultMenus = routes.filter(i => hasPermission(i.roles)) // 2. 隐藏项过滤 resultMenus = resultMenus.filter(i => !i.hide) // 3. 转换菜单格式 const menus = resultMenus.map(transformRouteToMenu) // 4. 生成菜单树 return arrayToTree(menus) } ``` ### 菜单数据结构 ```typescript interface MenuOption { label: string // 菜单标题 key: string // 菜单键值 icon?: () => VNode // 菜单图标 children?: MenuOption[] // 子菜单 disabled?: boolean // 是否禁用 } ``` ## 🏷️ 标签页系统 ### TabStore 管理 标签页系统与路由系统紧密集成: ```typescript // 添加标签页 tabStore.addTab(route) // 设置当前标签页 tabStore.setCurrentTab(route.fullPath) // 关闭标签页 tabStore.closeTab(fullPath) ``` ### 特殊标签页类型 1. **固定标签页** (`pinTab: true`): 不可关闭的标签页 2. **无标签页** (`withoutTab: true`): 不显示在标签栏的页面 ## ⚙️ 配置选项 ### 环境变量 ```bash # 路由加载模式 VITE_ROUTE_LOAD_MODE=static|dynamic # 默认首页路径 VITE_HOME_PATH=/dashboard/monitor # 应用名称 VITE_APP_NAME=Nova Admin ``` ### 路由模式配置 ```typescript // vite.config.ts 或 .env 文件 const { VITE_ROUTE_MODE = 'hash', VITE_BASE_URL } = import.meta.env export const router = createRouter({ history: VITE_ROUTE_MODE === 'hash' ? createWebHashHistory(VITE_BASE_URL) : createWebHistory(VITE_BASE_URL), routes, }) ``` ## 🛠️ 开发指南 ### 1. 添加新路由 #### 静态路由方式 在 `src/router/routes.static.ts` 中添加: ```typescript { name: 'newPage', path: '/new-page', title: '新页面', requiresAuth: true, icon: 'icon-park-outline:new', menuType: 'page', componentPath: '/new/page/index.vue', id: 100, pid: null, } ``` #### 动态路由方式 通过后端 API 返回路由配置,格式与静态路由相同。 ### 2. 路由权限配置 ```typescript { name: 'adminPage', path: '/admin', title: '管理页面', requiresAuth: true, roles: ['admin', 'super'], // 权限控制 // ... } ``` ### 3. 嵌套路由 ```typescript // 父路由 { name: 'parent', path: '/parent', title: '父级菜单', menuType: 'dir', componentPath: null, id: 1, pid: null, } // 子路由 { name: 'child', path: '/parent/child', title: '子级菜单', menuType: 'page', componentPath: '/parent/child/index.vue', id: 2, pid: 1, // 指向父路由ID } ``` ### 4. 路由缓存 ```typescript { name: 'cachedPage', path: '/cached', title: '缓存页面', keepAlive: true, // 启用路由缓存 // ... } ``` ## 🐛 常见问题 ### 1. 动态路由不生效 检查: - 路由数据格式是否正确 - 组件路径是否存在 - 权限配置是否正确 ### 2. 菜单不显示 检查: - `hide` 属性是否为 `true` - 权限验证是否通过 - `menuType` 是否正确设置 ### 3. 404 页面循环 确保: - 静态路由已正确配置 - 路由初始化完成 - 通配符路由放在最后 ### 4. 标签页问题 检查: - `withoutTab` 设置 - 路由路径是否正确 - TabStore 状态是否正常 ## 🎯 最佳实践 ### 1. 路由命名规范 ```typescript // 推荐命名方式 name: 'userManagement' // 驼峰命名 name: 'user-management' // 中划线命名 name: 'user_management' // 下划线命名 ``` ### 2. 路径规范 ```typescript // 路径应该语义化 path: '/user/management' // ✅ 好的 path: '/user/mgmt' // ❌ 避免缩写 path: '/u/m' // ❌ 避免单字符 ``` ### 3. 权限设计 ```typescript // 明确的权限配置 roles: ['admin'] // ✅ 单角色 roles: ['admin', 'manager'] // ✅ 多角色 requiresAuth: true // ✅ 明确需要认证 ``` ## 📚 相关文档 - [登录鉴权文档](./登录鉴权系统文档) - [权限系统文档](./权限管理系统文档) - [整体架构文档](./整体架构文档)