静态路由:
公共路由(没有permissions)

动态路由:
后端新增菜单表,前端自动生成
┌─────────────────────────────────────────────────────────────────┐
│ 第一步:用户登录 │
└─────────────────────────────────────────────────────────────────┘
↓
POST /login { username: 'ry', password: 'xxx' }
↓
返回: { token: 'eyJhbGc...' }
↓
localStorage.setItem('Admin-Token', token)
┌─────────────────────────────────────────────────────────────────┐
│ 第二步:路由守卫拦截 (permission.js) │
└─────────────────────────────────────────────────────────────────┘
↓
检查 useUserStore().roles.length === 0
↓
是 → 执行第三步
┌─────────────────────────────────────────────────────────────────┐
│ 第三步:获取用户信息和权限 │
└─────────────────────────────────────────────────────────────────┘
↓
useUserStore().getInfo() → GET /getInfo
↓
返回数据:
{
user: { userId: 2, userName: 'ry', ... },
roles: ['common'], ← 角色列表
permissions: [ ← 权限标识列表
'pm:project:query', ✅ 项目列表权限
'pm:project:edit',
// 注意:没有 'pm:project:add' ❌ 没有新增权限
]
}
↓
存储到 Store:
-
userStore.roles = ['common']
-
userStore.permissions = ['pm:project:query', ...]
┌─────────────────────────────────────────────────────────────────┐
│ 第四步:生成动态路由 │
└─────────────────────────────────────────────────────────────────┘
↓
usePermissionStore().generateRoutes()
↓
GET /getRouters → 返回该用户可访问的菜单/路由
↓
后端返回 JSON (根据角色权限过滤后):
{
"data": [
{
"name": "ProjectManagement",
"path": "/projectManagement",
"component": "Layout",
"meta": { "title": "项目管理", "icon": "project" },
"children": [
{
"name": "Project",
"path": "project",
"component": "projectManagement/project/index",
"meta": {
"title": "项目列表",
"icon": "list"
}
}
]
}
]
}
↓
filterAsyncRouter() 转换组件:
'Layout' → Layout组件
'projectManagement/project/index' →
() => import('@/views/projectManagement/project/index.vue')
↓
router.addRoute(route) 动态注册到路由表
↓
渲染侧边栏菜单(显示"项目管理" → "项目列表")
┌─────────────────────────────────────────────────────────────────┐
│ 第五步:用户访问项目列表页面 │
└─────────────────────────────────────────────────────────────────┘
↓
访问: /projectManagement/project
↓
加载页面组件: projectManagement/project/index.vue

页面级权限控制
javascript
<template>
<div>
<!-- 查询表单 -->
<el-form>...</el-form>
<!-- 操作按钮区 -->
<el-row>
<!-- ✅ 新增按钮 - 需要 pm:project:add 权限 -->
<el-button
type="primary"
icon="Plus"
@click="handleAdd"
v-hasPermi="['pm:project:add']"
>新增项目</el-button>
<!-- ✅ 导出按钮 - 需要 pm:project:export 权限 -->
<el-button
type="warning"
icon="Download"
@click="handleExport"
v-hasPermi="['pm:project:export']"
>导出</el-button>
</el-row>
<!-- 数据表格 -->
<el-table :data="projectList">
<el-table-column label="项目名称" prop="projectName" />
<el-table-column label="操作">
<template #default="scope">
<!-- ✅ 编辑按钮 - 需要 pm:project:edit 权限 -->
<el-button
link
type="primary"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['pm:project:edit']"
>修改</el-button>
<!-- ✅ 删除按钮 - 需要 pm:project:remove 权限 -->
<el-button
link
type="danger"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['pm:project:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
按钮级-v-hasPermi 指令工作原理
根据已读取的代码,v-hasPermi 指令的实现逻辑:
javascript
// src/directive/permission/hasPermi.js
export default {
mounted(el, binding) {
const { value } = binding
const all_permission = "*:*:*"
const permissions = useUserStore().permissions // 从store获取权限列表
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
// 检查是否有对应的权限
const hasPermissions = permissions.some(permission => {
return all_permission === permission || permissionFlag.includes(permission)
})
// 如果没有权限,移除DOM元素
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
}
}
}

两级权限控制
第一级:路由级(菜单显示)
// 后端 /getRouters 接口会检查:
// 用户是否有 'pm:project:list' 权限
if (hasPermission('pm:project:list')) {
// ✅ 返回"项目列表"菜单
return { path: 'project', component: '...' }
} else {
// ❌ 不返回该菜单,侧边栏不显示
}
第二级:按钮级(功能控制)
<!-- 前端 v-hasPermi 指令检查 -->
<el-button v-hasPermi="['pm:project:add']">新增</el-button>
<!-- 如果 permissions 中没有 'pm:project:add',按钮DOM被移除 -->
总结
javascript
// 1. 登录成功,存储token
localStorage.setItem('Admin-Token', 'xxx')
// 2. 路由守卫触发
router.beforeEach((to, from, next) => {
if (useUserStore().roles.length === 0) {
// 3. 获取用户信息
useUserStore().getInfo().then(() => {
// → roles: ['common']
// → permissions: ['pm:project:query', 'pm:project:edit']
// 4. 生成动态路由
usePermissionStore().generateRoutes().then(routes => {
// 5. 注册路由
routes.forEach(route => router.addRoute(route))
next({ ...to, replace: true })
})
})
}
})
// 6. 进入页面,按钮权限控制
// v-hasPermi="['pm:project:add']"
// → permissions中没有 → 按钮被移除