Files
leaudit-platform-frontend/docs/role-permissions/FRONTEND_API_GUIDE.md
T
2026-05-06 09:42:29 +08:00

1094 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 用户权限管理接口 - 前端对接文档
## 📋 文档信息
- **版本**: v3.0
- **更新日期**: 2025-11-26
- **API基础URL**: `http://YOUR_HOST:8000`
- **认证方式**: JWT Bearer Token
---
## ⚠️ 重要提示 - 前端对接必读
### 1. Token过期处理
**问题现象**: API返回 `code: 4001, msg: "token参数无效"`
**原因**: JWT Token已过期(默认有效期1小时)
**解决方案**:
```javascript
// 检查Token是否过期
const isTokenExpired = (token) => {
try {
const payload = JSON.parse(atob(token.split('.')[1]));
return Date.now() >= payload.exp * 1000;
} catch {
return true;
}
};
// 在发送请求前检查
if (isTokenExpired(token)) {
await login(); // 重新登录获取新Token
}
```
### 2. 实际角色ID对应关系
⚠️ **数据库中的实际角色ID** (请以实际数据为准):
| 角色名称 | role_key | 实际role_id | 说明 |
|---------|----------|------------|------|
| 省级管理员 | provincial_admin | **52** | 拥有完整RBAC管理权限 |
| 市级管理员 | admin | **1** | 负责本地区业务管理 |
| 普通员工 | common | **2** | 只能查看和管理自己的数据 |
**调用示例**:
```javascript
// ✅ 正确:使用实际的role_id
const provincialAdminUsers = await getRoleUsers(52);
const adminUsers = await getRoleUsers(1);
const commonUsers = await getRoleUsers(2);
// ❌ 错误:使用Mock数据的ID
const wrongUsers = await getRoleUsers(3); // 数据库中不存在
```
### 3. 响应格式说明
所有接口统一返回格式:
```json
{
"code": 200,
"msg": "success",
"data": { ... }
}
```
部分接口(如用户列表)直接返回数据对象:
```json
{
"users": [...],
"total": 10
}
```
### 4. 权限要求
| 接口模块 | 权限要求 | 说明 |
|---------|----------|------|
| RBAC管理接口 | `system:rbac:manage` | 仅provincial_admin角色 |
| 用户管理接口 | JWT认证即可 | 所有登录用户 |
| 路由权限接口 | JWT认证即可 | 所有登录用户 |
---
## 📚 接口总览
### 一、RBAC管理接口(18个)
**基础路径**: `/api/v3/rbac`
#### 1.1 角色管理(5个接口)
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/roles` | 获取角色列表(支持分页、搜索) |
| GET | `/roles/{role_id}` | 获取角色详情 |
| POST | `/roles` | 创建角色 |
| PUT | `/roles/{role_id}` | 更新角色 |
| DELETE | `/roles/{role_id}` | 删除角色(支持force参数) |
#### 1.2 权限管理(5个接口)
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/permissions` | 获取权限列表(支持树形/平铺) |
| GET | `/permissions/{permission_id}` | 获取权限详情 |
| POST | `/permissions` | 创建权限 |
| PUT | `/permissions/{permission_id}` | 更新权限 |
| DELETE | `/permissions/{permission_id}` | 删除权限 |
#### 1.3 角色权限关联(4个接口)
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/roles/{role_id}/permissions` | 获取角色的所有权限 |
| POST | `/roles/{role_id}/permissions` | 批量分配权限给角色(支持替换/追加) |
| PUT | `/roles/{role_id}/permissions/{permission_id}` | 更新单个权限配置 |
| DELETE | `/roles/{role_id}/permissions/{permission_id}` | 移除角色权限 |
#### 1.4 用户角色管理(4个接口)
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/roles/{role_id}/users` | 获取拥有某角色的用户列表 |
| GET | `/users/{user_id}/roles` | 获取用户的所有角色 |
| POST | `/users/{user_id}/roles` | 为用户分配角色 |
| DELETE | `/users/{user_id}/roles/{role_id}` | 移除用户角色 |
### 二、用户管理接口(3个)
**基础路径**: `/admin/users`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/users` | 获取用户列表(支持分页、搜索) |
| GET | `/organizations` | 获取组织架构(树形结构) |
| GET | `/organizations/flat` | 获取组织列表(扁平结构) |
### 三、路由权限接口(5个)
**基础路径**: `/rbac`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/user/routes` | 获取当前用户可访问路由(树形) |
| GET | `/user/routes/flat` | 获取当前用户可访问路由(扁平) |
| GET | `/roles/{role_id}/routes` | 获取角色可访问路由 |
| PUT | `/roles/{role_id}/routes` | **批量更新角色路由权限** ⭐新增 |
| GET | `/check-route` | 检查路由访问权限 |
---
## 📖 详细接口说明
## 一、RBAC管理接口
### 1.1 获取角色列表
**接口**: `GET /api/v3/rbac/roles`
**Query参数**:
```javascript
{
page: 1, // 页码
page_size: 20, // 每页数量
role_key: "", // 角色标识过滤(可选)
role_name: "", // 角色名称模糊搜索(可选)
include_system: true // 是否包含系统角色(可选)
}
```
**响应示例**:
```json
{
"code": 200,
"message": "success",
"data": {
"total": 3,
"page": 1,
"page_size": 20,
"items": [
{
"id": 52,
"role_key": "provincial_admin",
"role_name": "省级管理员",
"description": "省级权限,可管理所有地区",
"data_scope": "ALL",
"is_system": true,
"user_count": 1,
"permission_count": 15
},
{
"id": 1,
"role_key": "admin",
"role_name": "市级管理员",
"data_scope": "DEPT",
"is_system": true,
"user_count": 6
}
]
}
}
```
---
### 1.2 创建角色
**接口**: `POST /api/v3/rbac/roles`
**请求体参数**:
| 字段 | 类型 | 必填 | 验证规则 | 示例 |
|------|------|------|---------|------|
| role_key | string | ✅ | 必须以小写字母开头,只能包含小写字母、数字、下划线 | `department_leader` |
| role_name | string | ✅ | 任意字符,建议不超过50字 | `部门负责人` |
| description | string | ❌ | 角色描述 | `负责部门日常管理` |
| data_scope | string | ❌ | 可选值:ALL/DEPT/SELF,默认SELF | `DEPT` |
| metadata | object | ❌ | 自定义元数据 | `{}` |
**role_key 验证规则**:
- ✅ 必须以**小写字母**开头:`a-z`
- ✅ 只能包含:小写字母、数字、下划线
- ✅ 正确示例:`admin_role`, `department_leader`, `role123`
- ❌ 错误示例:`1231`(数字开头), `Test_Role`(大写), `admin-role`(连字符)
**请求示例**:
```json
{
"role_key": "department_leader",
"role_name": "部门负责人",
"description": "负责部门日常管理",
"data_scope": "DEPT"
}
```
**响应示例**:
```json
{
"code": 200,
"message": "角色创建成功",
"data": {
"id": 53,
"role_key": "department_leader",
"role_name": "部门负责人",
"data_scope": "DEPT",
"is_system": false
}
}
```
**错误响应示例**:
```json
{
"code": 4002,
"msg": "参数验证错误",
"data": [
{
"type": "value_error",
"loc": ["body", "role_key"],
"msg": "Value error, role_key只能包含小写字母、数字、下划线,且必须以字母开头",
"input": "1231"
}
]
}
```
---
### 1.3 获取角色的所有用户
**接口**: `GET /api/v3/rbac/roles/{role_id}/users`
**Query参数**:
```javascript
{
page: 1,
page_size: 20,
area: "梅州", // 按地区过滤(可选)
username: "admin" // 用户名模糊搜索(可选)
}
```
**响应示例**:
```json
{
"code": 200,
"message": "success",
"data": {
"total": 6,
"page": 1,
"page_size": 20,
"items": [
{
"user_id": 8,
"username": "梅州烟草",
"nick_name": "梅州烟草",
"area": "梅州管理员账号",
"ou_name": "梅州管理员账号",
"phone_number": null,
"email": null,
"assigned_at": "2025-11-18T01:40:25.030949+00:00"
}
]
}
}
```
---
### 1.4 批量分配权限给角色
**接口**: `POST /api/v3/rbac/roles/{role_id}/permissions`
**请求体**:
```json
{
"permissions": [
{
"permission_id": 10,
"grant_type": "GRANT",
"data_scope": "ALL"
},
{
"permission_id": 11,
"grant_type": "GRANT",
"data_scope": "DEPT"
}
],
"replace": true
}
```
**参数说明**:
- `replace`: true=替换全部权限,false=追加权限
- `grant_type`: GRANT(授予)或 DENY(拒绝)
- `data_scope`: ALL(全部数据)/ DEPT(本部门)/ SELF(仅自己)
**响应示例**:
```json
{
"code": 200,
"message": "权限分配成功",
"data": {
"role_id": 2,
"assigned_count": 2
}
}
```
---
### 1.5 为用户分配角色
**接口**: `POST /api/v3/rbac/users/{user_id}/roles`
**请求体**:
```json
{
"role_ids": [1, 2]
}
```
**响应示例**:
```json
{
"code": 200,
"message": "角色分配成功",
"data": {
"user_id": 20,
"username": "test_user",
"assigned_roles": [
{"role_id": 1, "role_name": "市级管理员"},
{"role_id": 2, "role_name": "普通员工"}
]
}
}
```
**注意事项**:
- ⚠️ 不能给自己分配角色(防止提权)
- ⚠️ provincial_admin角色只能由省级管理员分配
- ⚠️ admin角色只能给本地区用户分配角色
---
## 二、用户管理接口
### 2.1 获取用户列表
**接口**: `GET /admin/users/users`
**Query参数**:
```javascript
{
page: 1,
page_size: 20,
ou_id: "000", // 组织ID过滤(可选)
is_leader: true, // 是否领导过滤(可选)
search: "admin" // 搜索关键词(可选)
}
```
**响应示例**:
```json
{
"users": [
{
"id": 5,
"username": "admin",
"nick_name": "admin",
"ou_id": "000",
"ou_name": "test",
"is_leader": true,
"status": 0
}
],
"total": 10
}
```
⚠️ **已知问题**: 当前版本 `total` 字段返回0,请以 `users` 数组长度为准。
---
### 2.2 获取组织架构(树形)
**接口**: `GET /admin/users/organizations`
**Query参数**:
```javascript
{
include_users: true // 是否包含用户信息
}
```
**响应示例**:
```json
{
"organizations": [
{
"ou_id": "000",
"ou_name": "总部",
"parent_ou_id": null,
"level": 0,
"children": [
{
"ou_id": "0000000A1ML",
"ou_name": "梅州市局",
"parent_ou_id": "000",
"level": 1,
"users": [
{
"id": 8,
"username": "梅州烟草",
"nick_name": "梅州烟草",
"ou_id": "0000000A1ML",
"ou_name": "梅州市局"
}
]
}
],
"users": []
}
],
"total_organizations": 5,
"total_users": 10
}
```
---
## 三、路由权限接口
### 3.1 获取当前用户可访问路由
**接口**: `GET /rbac/user/routes`
**响应示例**:
```json
{
"code": 200,
"msg": "success",
"data": {
"user_id": 5,
"username": "admin",
"routes": [
{
"id": 1,
"route_path": "/",
"route_name": "Home",
"route_title": "首页",
"component": "Layout",
"parent_id": null,
"icon": "home",
"sort_order": 0,
"is_hidden": false,
"is_cache": true,
"meta": {},
"children": [
{
"id": 11,
"route_path": "/dashboard",
"route_name": "Dashboard",
"route_title": "工作台",
"parent_id": 1
}
]
}
],
"routes_flat": [
{"id": 1, "route_path": "/", "route_name": "Home"},
{"id": 11, "route_path": "/dashboard", "route_name": "Dashboard"}
]
}
}
```
**说明**:
- `routes`: 树形结构(用于构建菜单)
- `routes_flat`: 扁平结构(用于路由守卫权限检查)
---
### 3.2 获取角色可访问路由(含API权限)⭐v3.0更新
**接口**: `GET /rbac/roles/{role_id}/routes`
**v3.0 重大变更**:
- 返回格式从**扁平列表**改为**树形结构**
- 每个路由节点新增 `permissions` 字段,包含该页面关联的所有API操作权限
- 用于权限管理界面展示「页面 → API权限」的层级关系
**响应示例**:
```json
{
"code": 200,
"msg": "success",
"data": {
"role_id": 1,
"routes": [
{
"id": 31,
"route_path": "/home",
"route_name": "Home",
"route_title": "系统概览",
"parent_id": null,
"icon": "ri-dashboard-line",
"sort_order": 1,
"is_hidden": false,
"component": "views/Home.vue",
"permissions": []
},
{
"id": 41,
"route_path": "/rules",
"route_name": "Rules",
"route_title": "规则管理",
"parent_id": null,
"icon": "ri-book-3-line",
"sort_order": 3,
"is_hidden": false,
"component": "views/rules/Index.vue",
"permissions": [
{
"id": 28,
"permission_key": "evaluation_group:list:read",
"display_name": "查看评查点分组列表",
"api_method": "GET",
"api_path": "/api/v3/evaluation-point-groups"
},
{
"id": 30,
"permission_key": "evaluation_group:create:write",
"display_name": "创建评查点分组",
"api_method": "POST",
"api_path": "/api/v3/evaluation-point-groups"
},
{
"id": 35,
"permission_key": "evaluation_point:list:read",
"display_name": "查看评查点规则列表",
"api_method": "GET",
"api_path": "/api/v3/evaluation-points"
}
],
"children": [
{
"id": 43,
"route_path": "/rule-groups",
"route_name": "RuleGroups",
"route_title": "评查点分组",
"parent_id": 41,
"permissions": []
}
]
}
]
}
}
```
**字段说明**:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | int | 路由ID |
| route_path | string | 前端路由路径 |
| route_name | string | 路由名称(用于Vue router name |
| route_title | string | 页面标题(用于菜单显示) |
| parent_id | int/null | 父路由IDnull表示顶级路由 |
| icon | string | 菜单图标 |
| sort_order | int | 排序顺序 |
| is_hidden | bool | 是否隐藏(不显示在侧边栏) |
| component | string | Vue组件路径 |
| permissions | array | **关联的API权限列表** |
| children | array | 子路由列表(可选) |
**permissions 子字段说明**:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | int | 权限ID |
| permission_key | string | 权限标识(用于权限检查) |
| display_name | string | 权限显示名称 |
| api_method | string | HTTP方法(GET/POST/PUT/DELETE等) |
| api_path | string | API路径 |
---
### 3.3 批量更新角色路由权限
**接口**: `PUT /rbac/roles/{role_id}/routes`
**功能说明**:
- 批量更新指定角色的路由权限
- 采用**替换模式**:先删除现有所有关联,再插入新关联
- 自动清除相关缓存(角色缓存 + 用户缓存)
**请求体**:
```json
{
"route_ids": [1, 11, 12, 3, 31, 32, 2, 21],
"permission": "RW"
}
```
**参数说明**:
- `route_ids`: 路由ID列表(必填)
- `permission`: 权限类型,可选值:
- `R`: 只读权限
- `W`: 只写权限
- `RW`: 读写权限(默认)
**响应示例**:
```json
{
"code": 200,
"msg": "success",
"data": {
"role_id": 2,
"assigned_count": 8,
"removed_count": 5,
"route_ids": [1, 11, 12, 3, 31, 32, 2, 21]
}
}
```
**前端调用示例**:
```javascript
const updateRoleRoutes = async (roleId, routeIds) => {
const response = await fetch(`/rbac/roles/${roleId}/routes`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
route_ids: routeIds,
permission: 'RW'
})
});
const result = await response.json();
if (result.code === 200) {
console.log(`成功分配 ${result.data.assigned_count} 个路由`);
console.log(`移除了 ${result.data.removed_count} 个旧路由`);
}
return result;
};
// 使用示例
await updateRoleRoutes(2, [1, 11, 12, 3, 31, 32, 2, 21]);
```
**注意事项**:
- ⚠️ 所有 `route_ids` 必须存在且未删除
- ⚠️ 使用事务确保数据一致性
- ⚠️ 会自动清除所有受影响用户的路由缓存
---
### 3.4 检查路由访问权限
**接口**: `GET /rbac/check-route`
**Query参数**:
```javascript
{
route_path: "/system/users"
}
```
**响应示例**:
```json
{
"code": 200,
"msg": "success",
"data": {
"route_path": "/system/users",
"has_access": true
}
}
```
---
## 🚨 错误码说明
### HTTP状态码
| 状态码 | 说明 | 处理建议 |
|--------|------|---------|
| 200 | 成功 | - |
| 400 | 请求参数错误 | 检查请求体格式和必填字段 |
| 401 | 未认证 | Token过期或无效,需要重新登录 |
| 403 | 无权限 | 显示权限不足提示 |
| 404 | 资源不存在 | 检查ID是否正确 |
| 409 | 冲突 | 如role_key重复 |
| 500 | 服务器错误 | 联系后端排查 |
### 常见错误处理
```javascript
// 统一错误处理
const handleError = (error) => {
if (error.response?.status === 401) {
// Token过期,跳转登录
localStorage.removeItem('token');
window.location.href = '/login';
} else if (error.response?.status === 403) {
alert('权限不足');
} else if (error.response?.data?.detail) {
alert(error.response.data.detail);
} else {
alert('操作失败,请稍后重试');
}
};
```
---
## 💡 常见问题FAQ
### Q1: 为什么所有接口返回 code: 4001?
**A**: Token已过期,请重新登录获取新Token
### Q2: role_id应该用哪个?文档里写的1,2,3还是52,1,2
**A**: 使用实际数据库中的ID:
- provincial_admin = **52**
- admin = **1**
- common = **2**
### Q3: 需要用Mock数据吗?
**A**: **不需要**,所有接口已完整实现,直接调用真实API
### Q4: 如何判断当前用户是否有RBAC管理权限?
```javascript
// 方式1:检查JWT中的user_role
const payload = JSON.parse(atob(token.split('.')[1]));
const isProvincialAdmin = payload.user_role === 'provincial_admin';
// 方式2:调用权限检查接口(推荐)
const routes = await fetch('/rbac/user/routes');
const hasRBACAccess = routes.data.routes.some(r => r.route_path === '/rbac');
```
### Q5: 如何实现角色权限页面的路由分配功能?
**A**: 使用新的批量更新接口 `PUT /rbac/roles/{role_id}/routes`:
1. 通过 `GET /rbac/roles/{role_id}/routes` 获取当前角色已有路由
2. 用户选择要分配的路由ID列表
3. 调用 `PUT /rbac/roles/{role_id}/routes` 提交更新
4. 接口会自动替换所有路由权限并清除缓存
### Q6: total字段为什么返回0
**A**: `/admin/users/users` 接口的 `total` 字段当前版本存在bug,请以 `users` 数组长度为准。该问题已记录,将在后续版本修复。
### Q7: 批量更新路由权限后,用户需要重新登录吗?
**A**: 不需要。接口会自动清除相关用户的路由缓存,用户刷新页面即可看到新权限。
---
## 📦 前端开发清单
- [ ] 移除所有RBAC相关的Mock数据
- [ ] 使用实际的role_id52, 1, 2
- [ ] 实现Token过期自动刷新机制
- [ ] 处理401/403错误(跳转登录/权限提示)
- [ ] 使用 `PUT /rbac/roles/{role_id}/routes` 实现路由权限分配
- [ ] 处理 `total` 字段为0的情况(用数组长度代替)
- [ ] **v3.0新增**: 适配新的路由权限接口(含permissions字段)
---
## 🎯 v3.0 前端修改思路说明
### 一、架构变更说明
**变更前(v2.0**:
```
sys_routes 表:同时存储页面路由和API接口路由
└── 侧边栏展示所有路由(包括API接口)❌ 不符合预期
```
**变更后(v3.0**:
```
sys_routes 表:仅存储页面路由(侧边栏菜单)
permissions 表:存储API操作权限(通过 route_id 关联到页面)
页面路由
├── 规则管理 (/rules)
│ ├── [权限] 查看评查点分组列表 (GET /api/v3/evaluation-point-groups)
│ ├── [权限] 创建评查点分组 (POST /api/v3/evaluation-point-groups)
│ ├── [权限] 查看评查点规则列表 (GET /api/v3/evaluation-points)
│ └── ...
└── 系统设置 (/settings)
└── [子页面] 角色权限管理 (/role-permissions)
```
### 二、权限管理界面修改思路
#### 2.1 数据结构适配
```typescript
// 新的路由类型定义
interface RouteWithPermissions {
id: number;
route_path: string;
route_name: string;
route_title: string;
parent_id: number | null;
icon: string;
sort_order: number;
is_hidden: boolean;
component: string;
permissions: Permission[]; // 新增:关联的API权限
children?: RouteWithPermissions[];
}
interface Permission {
id: number;
permission_key: string;
display_name: string;
api_method: string;
api_path: string;
}
```
#### 2.2 权限分配界面布局建议
```
┌─────────────────────────────────────────────────────────────┐
│ 角色权限管理 - 市级管理员 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ☑ 系统概览 (/home) │
│ │
│ ☑ 规则管理 (/rules) │
│ ├─ ☑ 查看评查点分组列表 [GET] │
│ ├─ ☑ 创建评查点分组 [POST] │
│ ├─ ☑ 更新评查点分组 [PUT] │
│ ├─ ☐ 删除评查点分组 [DELETE] ← 未勾选 │
│ ├─ ☑ 查看评查点规则列表 [GET] │
│ └─ ... │
│ │
│ ☐ 系统设置 (/settings) │
│ └─ ☑ 角色权限管理 (/role-permissions) │
│ │
└─────────────────────────────────────────────────────────────┘
```
#### 2.3 前端组件实现示例
```vue
<template>
<div class="role-permission-manager">
<h3>角色权限管理 - {{ role.role_name }}</h3>
<!-- 路由权限树 -->
<el-tree
:data="routeTree"
show-checkbox
node-key="id"
:default-checked-keys="checkedRouteIds"
:props="treeProps"
@check="handleRouteCheck"
>
<template #default="{ node, data }">
<span class="route-node">
<i :class="data.icon"></i>
{{ data.route_title }}
<!-- 展示该路由关联的API权限 -->
<div v-if="data.permissions?.length" class="permission-list">
<el-checkbox-group v-model="checkedPermissions[data.id]">
<el-checkbox
v-for="perm in data.permissions"
:key="perm.id"
:label="perm.id"
>
<el-tag :type="getMethodTagType(perm.api_method)" size="small">
{{ perm.api_method }}
</el-tag>
{{ perm.display_name }}
</el-checkbox>
</el-checkbox-group>
</div>
</span>
</template>
</el-tree>
<el-button type="primary" @click="savePermissions">保存权限</el-button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const routeTree = ref([])
const checkedRouteIds = ref([])
const checkedPermissions = ref({}) // { routeId: [permissionId, ...] }
// 获取角色路由权限
const fetchRoleRoutes = async (roleId) => {
const res = await fetch(`/rbac/roles/${roleId}/routes`, {
headers: { 'Authorization': `Bearer ${token}` }
})
const { data } = await res.json()
routeTree.value = data.routes
// 初始化已选中的路由和权限
initCheckedState(data.routes)
}
// 根据HTTP方法返回标签类型
const getMethodTagType = (method) => {
const types = {
'GET': 'success',
'POST': 'primary',
'PUT': 'warning',
'DELETE': 'danger',
'PATCH': 'info'
}
return types[method] || 'info'
}
// 保存权限
const savePermissions = async () => {
// 1. 保存路由权限
await fetch(`/rbac/roles/${roleId}/routes`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
route_ids: checkedRouteIds.value,
permission: 'RW'
})
})
// 2. 保存功能权限(如需细粒度控制)
// await saveRolePermissions(roleId, flattenPermissions())
ElMessage.success('权限保存成功')
}
</script>
```
### 三、权限检查逻辑修改
#### 3.1 按钮级权限控制
```javascript
// 权限指令 v-permission
app.directive('permission', {
mounted(el, binding) {
const { value } = binding // permission_key
const userPermissions = store.state.user.permissions
if (!userPermissions.includes(value)) {
el.parentNode?.removeChild(el)
}
}
})
// 使用示例
<el-button
v-permission="'evaluation_group:create:write'"
@click="handleCreate"
>
新建分组
</el-button>
```
#### 3.2 API请求权限校验
```javascript
// 请求拦截器中添加权限检查
axios.interceptors.request.use((config) => {
const { method, url } = config
const requiredPermission = findPermissionByApi(method, url)
if (requiredPermission && !hasPermission(requiredPermission)) {
ElMessage.error('无权执行此操作')
return Promise.reject(new Error('Permission denied'))
}
return config
})
```
### 四、数据流程图
```
┌──────────────┐ GET /rbac/roles/{id}/routes ┌──────────────┐
│ 前端页面 │ ─────────────────────────────────→ │ 后端API │
│ 权限管理 │ │ │
└──────────────┘ └──────────────┘
│ │
│ ▼
│ ┌──────────────┐
│ │ 数据库查询 │
│ │ sys_routes │
│ │ permissions │
│ │ role_route │
│ └──────────────┘
│ │
│ 返回树形结构(含permissions
│ ←──────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ 渲染权限树 │
│ ├── 页面路由节点(可展开) │
│ │ └── API权限列表(checkbox
│ └── 用户勾选/取消勾选 │
└──────────────────────────────────────────────────────────────────┘
│ PUT /rbac/roles/{id}/routes (保存路由权限)
│ POST /api/v3/rbac/roles/{id}/permissions (保存功能权限)
┌──────────────┐
│ 权限生效 │
│ 用户刷新后 │
│ 看到新权限 │
└──────────────┘
```
### 五、迁移检查清单
| 序号 | 检查项 | 状态 |
|------|--------|------|
| 1 | 更新 `GET /rbac/roles/{role_id}/routes` 接口调用,适配新的树形返回格式 | ⬜ |
| 2 | 权限管理界面支持展示「页面路由 → API权限」层级结构 | ⬜ |
| 3 | 添加 permissions 字段的 TypeScript 类型定义 | ⬜ |
| 4 | 实现按钮级权限控制(使用 permission_key | ⬜ |
| 5 | 测试权限保存和刷新后生效 | ⬜ |
---
## 📞 技术支持
如有问题,请联系后端开发团队。
**文档版本**: v3.0
**最后更新**: 2025-11-26
**维护者**: Backend Team