路由文档 - 路由配置和权限控制
概述
本文档详细介绍了 VJSP Vue3 框架的路由配置机制和权限控制系统。框架采用基于 Vue Router 4 的路由架构,结合动态路由生成和细粒度权限控制,为企业级应用提供完整的路由解决方案。
路由架构
核心特性
- 动态路由生成:支持前端控制和后端控制两种路由生成模式
- 权限集成:路由与权限系统深度集成,实现菜单级和按钮级权限控制
- 路由缓存:智能路由缓存机制,提升页面切换性能
- 多级路由支持:支持多级嵌套路由,自动扁平化处理
- 类型安全:完整的 TypeScript 类型定义支持
技术栈
- Vue Router 4:核心路由库
- Pinia 状态管理:路由状态和权限状态管理
- 动态导入:路由组件懒加载,优化首屏加载性能
- 权限验证:基于角色的访问控制(RBAC)
路由配置机制
路由模式
框架支持两种路由配置模式:
- 前端控制模式:路由配置完全由前端定义,权限系统负责过滤可访问路由
- 后端控制模式:路由结构由后端返回,前端根据后端数据动态生成路由
路由结构
常量路由
常量路由是应用启动时必须加载的基础路由,包括:
- 登录页面
- 404 错误页面
- 重定向路由
- 系统基础页面(如仪表盘)
动态路由
动态路由根据用户权限动态生成,包括:
- 业务功能模块路由
- 系统管理路由
- 个人中心路由
路由懒加载
框架采用动态导入实现路由组件的懒加载,通过 createSafeDynamicImport 函数确保组件加载的安全性:
- 组件按需加载,减少初始包大小
- 错误边界处理,防止加载失败影响应用
- 支持组件预加载,提升用户体验
权限控制机制
权限层级
框架实现三级权限控制:
- 路由级权限:控制用户能否访问特定页面
- 菜单级权限:控制侧边栏菜单的显示
- 按钮级权限:控制页面内操作按钮的显示
权限验证流程
1. 路由守卫验证
路由守卫在每次路由跳转时执行权限验证:
- 检查用户登录状态
- 验证用户权限信息
- 生成可访问的动态路由
- 添加路由到路由器实例
2. 权限检查函数
框架提供多种权限检查函数:
hasPermission:检查是否拥有特定权限hasAnyPermission:检查是否拥有任意指定权限hasAllPermissions:检查是否拥有所有指定权限
3. 权限指令
通过 v-permission 指令实现按钮级权限控制,支持多种使用方式和严格权限模式:
基础用法
<!-- 单个权限检查 -->
<el-button v-permission="'system:user:add'">新增用户</el-button>
<el-button v-permission="'system:user:edit'">编辑用户</el-button>
<!-- 多个权限检查(满足任意一个权限即可) -->
<el-button v-permission="['system:user:add', 'system:user:edit']">
新增或编辑用户
</el-button>严格权限模式
严格权限模式通过修饰符实现,当用户没有权限时会完全移除DOM元素:
<!-- 严格模式:无权限时移除元素 -->
<el-button v-permission.strict="'system:user:delete'">删除用户</el-button>
<!-- 严格模式结合多个权限 -->
<el-button v-permission.strict="['system:user:export', 'system:user:import']">
数据导入导出
</el-button>权限指令特性
- 默认行为:无权限时禁用元素(透明度降低、不可点击)
- 严格模式:使用
.strict修饰符,无权限时完全移除元素 - 动态更新:权限变化时自动更新元素状态
- 多种权限检查:支持单个权限或权限数组(满足任意权限即可)
动态路由生成
前端控制模式
在前端控制模式下,路由生成流程如下:
- 获取用户权限列表
- 过滤预定义的路由配置
- 生成可访问的路由树
- 添加到路由器实例
后端控制模式
在后端控制模式下,路由生成流程如下:
- 从后端获取菜单数据
- 转换菜单数据为路由配置
- 验证路由组件路径有效性
- 生成并添加动态路由
路由工具函数
路由生成工具
框架提供 generateRoutesByFrontEnd 和 generateRoutesByServer 函数分别处理前端和后端控制模式的路由生成。
路由处理工具
pathResolve:路径解析和规范化flatMultiLevelRoutes:多级路由扁平化处理isValidRoute:路由配置验证
权限检查工具
filterAccessibleRoutes:过滤可访问路由findRouteByPath:根据路径查找路由findRouteByName:根据名称查找路由
业务模块开发指南(Product 模块)
1. 创建路由配置
在 src/router/modules/ 目录下创建 product.ts 路由配置文件:
import type { AppRouteRecordRaw } from '@/router/types'
const ProductRoute: AppRouteRecordRaw = {
path: '/product',
name: 'Product',
component: () => import('@/layout/index.vue'),
meta: {
title: '产品管理',
icon: 'product',
permission: ['product:view'],
},
children: [
{
path: 'list',
name: 'ProductList',
component: () => import('@/views/modules/product/list.vue'),
meta: {
title: '产品列表',
permission: ['product:list'],
},
},
{
path: 'add',
name: 'ProductAdd',
component: () => import('@/views/modules/product/add.vue'),
meta: {
title: '新增产品',
permission: ['product:add'],
hidden: true,
},
},
{
path: 'edit/:id',
name: 'ProductEdit',
component: () => import('@/views/modules/product/edit.vue'),
meta: {
title: '编辑产品',
permission: ['product:edit'],
hidden: true,
},
},
],
}
export default ProductRoute2. 注册路由模块
在 src/router/index.ts 中导入并注册产品路由模块:
import ProductRoute from './modules/product'
// 在动态路由配置中添加
const asyncRoutes: AppRouteRecordRaw[] = [
// ... 其他路由
ProductRoute,
]3. 创建视图组件
创建对应的 Vue 组件文件:
src/views/modules/product/list.vue
<template>
<div class="product-list">
<SearchForm :fields="searchFields" @search="handleSearch" />
<DataTable
:columns="tableColumns"
:data="productList"
:loading="loading"
@refresh="loadProductList"
>
<template #action="{ row }">
<el-button v-permission="'product:edit'" @click="handleEdit(row)"> 编辑 </el-button>
<el-button v-permission="'product:delete'" @click="handleDelete(row)"> 删除 </el-button>
</template>
</DataTable>
<el-button v-permission="'product:add'" type="primary" @click="handleAdd"> 新增产品 </el-button>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useProductApi } from '@/api/modules/product'
const router = useRouter()
const productList = ref([])
const loading = ref(false)
const searchFields = [
{ label: '产品名称', prop: 'productName', type: 'input' },
{ label: '产品编码', prop: 'productCode', type: 'input' },
]
const tableColumns = [
{ label: '产品名称', prop: 'productName' },
{ label: '产品编码', prop: 'productCode' },
{ label: '价格', prop: 'price' },
{ label: '状态', prop: 'status' },
]
const loadProductList = async (params = {}) => {
loading.value = true
try {
const response = await useProductApi().getList(params)
productList.value = response.data.list
} finally {
loading.value = false
}
}
const handleAdd = () => {
router.push({ name: 'ProductAdd' })
}
const handleEdit = row => {
router.push({ name: 'ProductEdit', params: { id: row.id } })
}
const handleDelete = async row => {
// 删除确认和操作
}
onMounted(() => {
loadProductList()
})
</script>4. 权限配置
在权限管理系统中配置产品模块的权限标识:
product:view:查看产品模块product:list:查看产品列表product:add:新增产品product:edit:编辑产品product:delete:删除产品
路由缓存机制
路由状态管理
框架使用 Pinia 管理路由状态,包括:
- 当前激活的路由
- 标签页路由列表
- 侧边栏菜单状态
- 路由缓存标识
组件缓存
通过 Vue Router 的 <keep-alive> 和组件 name 属性实现路由组件缓存:
- 智能缓存管理,避免内存泄漏
- 支持最大缓存数量限制
- 手动清除缓存功能
路由持久化
路由状态自动持久化到 localStorage,确保:
- 页面刷新后保持路由状态
- 标签页状态恢复
- 侧边栏展开状态记忆
错误处理
路由错误处理
框架实现完整的路由错误处理机制:
- 404 页面处理
- 权限不足页面
- 路由加载失败处理
- 网络错误重试机制
降级方案
当动态路由加载失败时,框架提供降级方案:
- 使用静态路由作为后备
- 显示友好的错误提示
- 提供重试机制
最佳实践
路由命名规范
- 使用驼峰命名法命名路由
- 保持路由名称与组件名称一致
- 使用有意义的路径结构
权限配置建议
- 权限标识采用模块化命名
- 保持权限粒度适中
- 定期清理未使用的权限
性能优化
- 合理使用路由懒加载
- 避免过度嵌套路由
- 定期检查路由配置
常见问题
Q: 如何添加新的业务模块路由?
A: 在 src/router/modules/ 目录下创建路由配置文件,并在主路由文件中导入注册。
Q: 如何控制按钮级权限?
A: 使用 v-permission 指令,传入对应的权限标识。
Q: 路由缓存不生效怎么办?
A: 确保组件设置了正确的 name 属性,并检查路由配置中的 meta.keepAlive 设置。
Q: 如何实现动态菜单?
A: 使用后端控制模式,从后端获取菜单数据并转换为路由配置。
