coder-common-thin-frontend/doc/api/permission/菜单管理API.md
Leo d09a400ad0 feat(user-management): 实现用户管理导入导出功能
- 新增用户数据导出功能:
  * 支持导出当前查询条件下的数据
  * 支持导出全部用户数据
  * 支持下载用户导入模板
  * 自动生成带时间戳的文件名

- 新增用户数据导入功能:
  * 拖拽上传Excel文件支持
  * 文件类型和大小验证(.xlsx/.xls, 最大10MB)
  * 覆盖已存在数据选项
  * 实时上传进度显示
  * 详细的导入结果反馈

- 完善API接口:
  * downloadExcelTemplate: 下载Excel模板
  * exportExcelData: 导出用户数据(支持查询参数)
  * importUserData: 导入用户数据(支持覆盖选项)

- UI/UX优化:
  * 下拉菜单式导出按钮设计
  * 专业的导入模态框界面
  * 完善的错误处理和用户反馈
  * 导入说明和操作指导

技术实现:
- 使用Blob处理文件下载
- FormData处理文件上传
- Naive UI组件深度集成
- TypeScript类型安全保障
2025-07-06 00:58:26 +08:00

1315 lines
30 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.

# 菜单管理API
## 概述
菜单管理模块是权限系统的核心组成部分,负责管理系统菜单、权限配置和路由生成。支持无限级菜单嵌套,提供目录、菜单、按钮三种类型的权限控制。
## 权限说明
菜单管理接口需要相应的权限才能访问:
| 操作 | 权限码 | 说明 |
|------|--------|------|
| 查询菜单列表 | `system:menu:list` | 查看菜单列表权限 |
| 新增菜单 | `system:menu:add` | 新增菜单权限 |
| 修改菜单 | `system:menu:edit` | 修改菜单信息权限 |
| 删除菜单 | `system:menu:remove` | 删除菜单权限 |
| 分配权限 | `system:menu:assign` | 分配菜单权限 |
## 菜单类型说明
| 类型 | 值 | 说明 | 用途 |
|------|---|------|------|
| 目录 | 1 | 菜单目录 | 用于菜单分组,不对应具体页面 |
| 菜单 | 2 | 菜单项 | 对应具体的页面路由 |
| 按钮 | 3 | 按钮权限 | 用于控制页面内的按钮显示 |
## 接口列表
### 1. 分页查询菜单列表
**接口地址**: `GET /coder/sysMenu/listPage`
**接口描述**: 分页查询系统菜单列表
**是否需要认证**: 是
**权限要求**: `system:menu:list`
**请求头**:
```
Authorization: your-token-value
```
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|--------|------|------|------|------|
| pageNo | Integer | 否 | 页码 | 1 |
| pageSize | Integer | 否 | 每页大小 | 10 |
| menuName | String | 否 | 菜单名称 | 用户管理 |
| menuStatus | String | 否 | 菜单状态 | 0 |
| auth | String | 否 | 权限标识 | system:user:list |
**响应示例**:
```json
{
"code": 1,
"success": true,
"msg": "操作成功",
"data": [
{
"menuId": 1,
"menuName": "系统管理",
"enName": "System Manage",
"parentId": 0,
"menuType": "1",
"name": "systemPage",
"path": "/system",
"component": "",
"icon": "Tools",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "/system/user",
"activeMenu": null
},
{
"menuId": 2,
"menuName": "用户管理",
"enName": "User Manage",
"parentId": 1,
"menuType": "2",
"name": "userPage",
"path": "/system/user",
"component": "system/user/index",
"icon": "UserFilled",
"isHide": "1",
"isLink": "",
"isKeepAlive": "1",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": null
},
{
"menuId": 12,
"menuName": "角色管理",
"enName": "Role Manage",
"parentId": 1,
"menuType": "2",
"name": "rolePage",
"path": "/system/role",
"component": "system/role/index",
"icon": "Avatar",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": null
},
{
"menuId": 72,
"menuName": "文件管理",
"enName": "Files Manage",
"parentId": 70,
"menuType": "2",
"name": "filePage",
"path": "/tools/file",
"component": "system/file/index",
"icon": "FolderOpened",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": ""
},
{
"menuId": 19,
"menuName": "菜单管理",
"enName": "Menu Manage",
"parentId": 1,
"menuType": "2",
"name": "menuPage",
"path": "/system/menu",
"component": "system/menu/index",
"icon": "Grid",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": null
},
{
"menuId": 77,
"menuName": "图库管理",
"enName": "Pictures Manage",
"parentId": 70,
"menuType": "2",
"name": "picturePage",
"path": "/tools/picture",
"component": "system/picture/index",
"icon": "Picture",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": ""
},
{
"menuId": 39,
"menuName": "登录日志",
"enName": "Login Logs",
"parentId": 1,
"menuType": "2",
"name": "loginlogPage",
"path": "/system/loginlog",
"component": "system/loginlog/index",
"icon": "Calendar",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": null
},
{
"menuId": 42,
"menuName": "操作日志",
"enName": "Operate Logs",
"parentId": 1,
"menuType": "2",
"name": "operlogPage",
"path": "/system/operlog",
"component": "system/operlog/index",
"icon": "Notebook",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": null
},
{
"menuId": 50,
"menuName": "个人中心",
"enName": "Personage Center",
"parentId": 1,
"menuType": "2",
"name": "personagePage",
"path": "/system/personage",
"component": "system/personage/index",
"icon": "User",
"isHide": "0",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": null,
"activeMenu": null
},
{
"menuId": 166,
"menuName": "外部链接",
"enName": "External Link",
"parentId": 0,
"menuType": "1",
"name": "linkPage",
"path": "/link",
"component": "",
"icon": "Link",
"isHide": "0",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": null,
"activeMenu": null
}
]
}
```
**调用示例**:
```bash
curl -X GET \
"http://localhost:18099/coder/sysMenu/listPage?pageNo=1&pageSize=10&menuName=系统管理" \
-H "Authorization: your-token-value"
```
---
### 2. 查询菜单列表
**接口地址**: `GET /coder/sysMenu/list`
**接口描述**: 查询系统菜单列表(树形结构,不分页)
**是否需要认证**: 是
**权限要求**: `system:menu:list`
**请求参数**: 同分页查询除pageNo、pageSize外
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": [
{
"menuId": 1,
"menuName": "系统管理",
"enName": "System",
"parentId": 0,
"menuType": "1",
"path": "/system",
"name": "system",
"component": "Layout",
"icon": "system",
"auth": "",
"menuStatus": "0",
"activeMenu": "",
"isHide": "1",
"isLink": "1",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"isSpread": "0",
"sorted": 1,
"createBy": "admin",
"createTime": "2024-01-01 10:00:00",
"updateBy": "admin",
"updateTime": "2024-01-01 10:00:00",
"children": [
// 子菜单...
]
}
],
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X GET \
"http://localhost:18099/coder/sysMenu/list" \
-H "Authorization: your-token-value"
```
---
### 3. 根据ID查询菜单
**接口地址**: `GET /coder/sysMenu/getById/{id}`
**接口描述**: 根据菜单ID查询菜单详细信息
**是否需要认证**: 是
**权限要求**: `system:menu:list`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | Long | 是 | 菜单ID |
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": {
"menuId": 1,
"menuName": "系统管理",
"enName": "System",
"parentId": 0,
"menuType": "1",
"path": "/system",
"name": "system",
"component": "Layout",
"icon": "system",
"auth": "",
"menuStatus": "0",
"activeMenu": "",
"isHide": "1",
"isLink": "1",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"isSpread": "0",
"sorted": 1,
"createBy": "admin",
"createTime": "2024-01-01 10:00:00",
"updateBy": "admin",
"updateTime": "2024-01-01 10:00:00"
},
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X GET \
http://localhost:18099/coder/sysMenu/getById/1 \
-H "Authorization: your-token-value"
```
---
### 4. 新增菜单
**接口地址**: `POST /coder/sysMenu/add`
**接口描述**: 新增系统菜单
**是否需要认证**: 是
**权限要求**: `system:menu:add`
**请求参数**:
```json
{
"menuName": "用户管理",
"enName": "User Management",
"parentId": 1,
"menuType": "2",
"path": "/system/user",
"name": "user",
"component": "/system/user/index",
"icon": "user",
"auth": "system:user:list",
"menuStatus": "0",
"activeMenu": "",
"isHide": "1",
"isLink": "1",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"isSpread": "0",
"sorted": 1
}
```
**请求参数说明**:
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|--------|------|------|------|----------|
| menuName | String | 是 | 菜单名称 | 不能为空 |
| enName | String | 否 | 英文名称 | 可为空 |
| parentId | Long | 是 | 父菜单ID | 不能为空0表示根菜单 |
| menuType | String | 是 | 菜单类型 | 1-目录 2-菜单 3-按钮 |
| path | String | 否 | 路由地址 | 菜单类型为2时必填 |
| name | String | 否 | 路由名称 | 菜单类型为2时必填 |
| component | String | 否 | 组件路径 | 菜单类型为2时必填 |
| icon | String | 否 | 菜单图标 | 可为空 |
| auth | String | 否 | 权限标识 | 按钮类型时必填 |
| menuStatus | String | 是 | 菜单状态 | 0-启用 1-停用 |
| activeMenu | String | 否 | 选中路由 | 可为空 |
| isHide | String | 是 | 是否隐藏 | 0-隐藏 1-显示 |
| isLink | String | 否 | 是否外链 | 0-是 1-否 |
| isKeepAlive | String | 否 | 是否缓存 | 0-是 1-否 |
| isFull | String | 否 | 是否全屏 | 0-是 1-否 |
| isAffix | String | 否 | 是否固定 | 0-是 1-否 |
| isSpread | String | 否 | 是否展开 | 0-是 1-否 |
| sorted | Integer | 是 | 显示顺序 | 不能为空 |
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": "新增成功",
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X POST \
http://localhost:18099/coder/sysMenu/add \
-H "Content-Type: application/json" \
-H "Authorization: your-token-value" \
-d '{
"menuName": "用户管理",
"enName": "User Management",
"parentId": 1,
"menuType": "2",
"path": "/system/user",
"name": "user",
"component": "/system/user/index",
"icon": "user",
"auth": "system:user:list",
"menuStatus": "0",
"activeMenu": "",
"isHide": "1",
"isLink": "1",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"isSpread": "0",
"sorted": 1
}'
```
---
### 5. 修改菜单信息
**接口地址**: `POST /coder/sysMenu/update`
**接口描述**: 修改系统菜单信息
**是否需要认证**: 是
**权限要求**: `system:menu:edit`
**请求参数**:
```json
{
"menuId": 1,
"menuName": "用户管理",
"enName": "User Management",
"parentId": 1,
"menuType": "2",
"path": "/system/user",
"name": "user",
"component": "/system/user/index",
"icon": "user",
"auth": "system:user:list",
"menuStatus": "0",
"activeMenu": "",
"isHide": "1",
"isLink": "1",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"isSpread": "0",
"sorted": 1
}
```
**请求参数说明**:
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|--------|------|------|------|----------|
| menuId | Long | 是 | 菜单ID | 必须是有效的菜单ID |
| 其他参数 | - | - | 同新增菜单 | - |
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": "修改成功",
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X POST \
http://localhost:18099/coder/sysMenu/update \
-H "Content-Type: application/json" \
-H "Authorization: your-token-value" \
-d '{
"menuId": 1,
"menuName": "用户管理",
"parentId": 1,
"menuType": "2",
"menuStatus": "0",
"sorted": 1
}'
```
---
### 6. 删除菜单
**接口地址**: `POST /coder/sysMenu/deleteById/{id}`
**接口描述**: 根据ID删除菜单
**是否需要认证**: 是
**权限要求**: `system:menu:remove`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | Long | 是 | 菜单ID |
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": "删除成功",
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X POST \
http://localhost:18099/coder/sysMenu/deleteById/1 \
-H "Authorization: your-token-value"
```
---
### 7. 批量删除菜单
**接口地址**: `POST /coder/sysMenu/batchDelete`
**接口描述**: 批量删除菜单
**是否需要认证**: 是
**权限要求**: `system:menu:remove`
**请求参数**:
```json
{
"ids": [1, 2, 3]
}
```
**请求参数说明**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | Long[] | 是 | 菜单ID数组 |
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": "删除成功",
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X POST \
http://localhost:18099/coder/sysMenu/batchDelete \
-H "Content-Type: application/json" \
-H "Authorization: your-token-value" \
-d '{
"ids": [1, 2, 3]
}'
```
---
### 8. 修改菜单状态
**接口地址**: `POST /coder/sysMenu/updateStatus/{id}/{menuStatus}`
**接口描述**: 修改菜单状态(启用/停用)
**是否需要认证**: 是
**权限要求**: `system:menu:edit`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | Long | 是 | 菜单ID |
| menuStatus | String | 是 | 菜单状态0-启用 1-停用) |
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": "修改成功",
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X POST \
http://localhost:18099/coder/sysMenu/updateStatus/1/0 \
-H "Authorization: your-token-value"
```
---
### 9. 修改菜单展开状态
**接口地址**: `POST /coder/sysMenu/updateSpread/{id}/{isSpread}`
**接口描述**: 修改菜单展开状态
**是否需要认证**: 是
**权限要求**: `system:menu:edit`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | Long | 是 | 菜单ID |
| isSpread | String | 是 | 展开状态0-是 1-否) |
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": "修改成功",
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X POST \
http://localhost:18099/coder/sysMenu/updateSpread/1/0 \
-H "Authorization: your-token-value"
```
---
### 10. 菜单级联下拉框
**接口地址**: `GET /coder/sysMenu/cascaderList`
**接口描述**: 获取菜单级联下拉框数据
**是否需要认证**: 是
**权限要求**: `system:menu:list`
**请求参数**: 无
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": [
{
"label": "系统管理",
"value": 1,
"parentId": "0",
"children": [
{
"label": "用户管理",
"value": 2,
"parentId": "1"
},
{
"label": "角色管理",
"value": 3,
"parentId": "1"
}
]
}
],
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X GET \
http://localhost:18099/coder/sysMenu/cascaderList \
-H "Authorization: your-token-value"
```
---
### 11. 生成用户菜单路由
**接口地址**: `GET /coder/sysMenu/listRouters`
**接口描述**: 根据当前用户权限生成前端菜单路由
**是否需要认证**: 是
**权限要求**: 无(已登录用户可访问)
**请求参数**: 无
**响应示例**:
```json
{
"code": 1,
"success": true,
"msg": "操作成功",
"data": [
{
"menuId": 1,
"menuName": "系统管理",
"enName": "System Manage",
"parentId": 0,
"menuType": "1",
"name": "systemPage",
"path": "/system",
"component": "",
"icon": "Tools",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "/system/user",
"activeMenu": null
},
{
"menuId": 2,
"menuName": "用户管理",
"enName": "User Manage",
"parentId": 1,
"menuType": "2",
"name": "userPage",
"path": "/system/user",
"component": "system/user/index",
"icon": "UserFilled",
"isHide": "1",
"isLink": "",
"isKeepAlive": "1",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": null
},
{
"menuId": 70,
"menuName": "系统工具",
"enName": "System Tools",
"parentId": 0,
"menuType": "1",
"name": "toolsPage",
"path": "/tools",
"component": "",
"icon": "Tools",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "/tools/file",
"activeMenu": null
},
{
"menuId": 12,
"menuName": "角色管理",
"enName": "Role Manage",
"parentId": 1,
"menuType": "2",
"name": "rolePage",
"path": "/system/role",
"component": "system/role/index",
"icon": "Avatar",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": null
},
{
"menuId": 72,
"menuName": "文件管理",
"enName": "Files Manage",
"parentId": 70,
"menuType": "2",
"name": "filePage",
"path": "/tools/file",
"component": "system/file/index",
"icon": "FolderOpened",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": ""
},
{
"menuId": 19,
"menuName": "菜单管理",
"enName": "Menu Manage",
"parentId": 1,
"menuType": "2",
"name": "menuPage",
"path": "/system/menu",
"component": "system/menu/index",
"icon": "Grid",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": null
},
{
"menuId": 77,
"menuName": "图库管理",
"enName": "Pictures Manage",
"parentId": 70,
"menuType": "2",
"name": "picturePage",
"path": "/tools/picture",
"component": "system/picture/index",
"icon": "Picture",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": ""
},
{
"menuId": 39,
"menuName": "登录日志",
"enName": "Login Logs",
"parentId": 1,
"menuType": "2",
"name": "loginlogPage",
"path": "/system/loginlog",
"component": "system/loginlog/index",
"icon": "Calendar",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": null
},
{
"menuId": 42,
"menuName": "操作日志",
"enName": "Operate Logs",
"parentId": 1,
"menuType": "2",
"name": "operlogPage",
"path": "/system/operlog",
"component": "system/operlog/index",
"icon": "Notebook",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": "",
"activeMenu": null
},
{
"menuId": 50,
"menuName": "个人中心",
"enName": "Personage Center",
"parentId": 1,
"menuType": "2",
"name": "personagePage",
"path": "/system/personage",
"component": "system/personage/index",
"icon": "User",
"isHide": "0",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": null,
"activeMenu": null
},
{
"menuId": 166,
"menuName": "外部链接",
"enName": "External Link",
"parentId": 0,
"menuType": "1",
"name": "linkPage",
"path": "/link",
"component": "",
"icon": "Link",
"isHide": "0",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": null,
"activeMenu": null
}
]
}
```
**调用示例**:
```bash
curl -X GET \
http://localhost:18099/coder/sysMenu/listRouters \
-H "Authorization: your-token-value"
```
---
### 12. 查询正常菜单列表
**接口地址**: `GET /coder/sysMenu/listMenuNormal`
**接口描述**: 查询状态正常的菜单列表
**是否需要认证**: 是
**权限要求**: `system:menu:list`
**请求参数**: 无
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": [
{
"menuId": 1,
"menuName": "系统管理",
"enName": "System",
"parentId": 0,
"menuType": "1",
"path": "/system",
"name": "system",
"component": "Layout",
"icon": "system",
"auth": "",
"menuStatus": "0",
"activeMenu": "",
"isHide": "1",
"isLink": "1",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"isSpread": "0",
"sorted": 1,
"children": [
// 子菜单...
]
}
],
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X GET \
http://localhost:18099/coder/sysMenu/listMenuNormal \
-H "Authorization: your-token-value"
```
---
### 13. 根据角色ID查询菜单
**接口地址**: `GET /coder/sysMenu/listMenuIdsByRoleId/{roleId}`
**接口描述**: 根据角色ID查询该角色拥有的菜单ID列表
**是否需要认证**: 是
**权限要求**: `system:menu:list`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| roleId | Long | 是 | 角色ID |
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": [1, 2, 3, 4, 5],
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X GET \
http://localhost:18099/coder/sysMenu/listMenuIdsByRoleId/1 \
-H "Authorization: your-token-value"
```
---
### 14. 保存角色菜单权限
**接口地址**: `POST /coder/sysMenu/saveRoleMenu/{roleId}/{menuIds}`
**接口描述**: 保存角色的菜单权限
**是否需要认证**: 是
**权限要求**: `system:menu:assign`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| roleId | Long | 是 | 角色ID |
| menuIds | String | 是 | 菜单ID列表逗号分隔 |
**响应示例**:
```json
{
"status": 200,
"msg": "SUCCESS",
"data": "保存成功",
"traceId": "trace-123456"
}
```
**调用示例**:
```bash
curl -X POST \
http://localhost:18099/coder/sysMenu/saveRoleMenu/1/1,2,3,4,5 \
-H "Authorization: your-token-value"
```
---
## 菜单字段说明
### 基础字段
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| menuId | Long | 菜单ID | 1 |
| menuName | String | 菜单名称 | 系统管理 |
| enName | String | 英文名称 | System |
| parentId | Long | 父菜单ID | 0根菜单 |
| menuType | String | 菜单类型 | 1-目录 2-菜单 3-按钮 |
| sorted | Integer | 显示顺序 | 1 |
### 路由字段
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| path | String | 路由地址 | /system/user |
| name | String | 路由名称 | user |
| component | String | 组件路径 | /system/user/index |
| redirect | String | 重定向地址 | /system/user |
| activeMenu | String | 选中路由 | /system/user |
### 权限字段
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| auth | String | 权限标识 | system:user:list |
| menuStatus | String | 菜单状态 | 0-启用 1-停用 |
### 显示字段
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| icon | String | 菜单图标 | user |
| isHide | String | 是否隐藏 | 0-隐藏 1-显示 |
| isLink | String | 是否外链 | 0-是 1-否 |
| isKeepAlive | String | 是否缓存 | 0-是 1-否 |
| isFull | String | 是否全屏 | 0-是 1-否 |
| isAffix | String | 是否固定 | 0-是 1-否 |
| isSpread | String | 是否展开 | 0-是 1-否 |
---
## 前端路由配置
### Vue Router 配置
```javascript
// 根据后端返回的菜单数据生成路由
function generateRoutes(menuData) {
const routes = [];
menuData.forEach(menu => {
const route = {
path: menu.path,
name: menu.name,
component: () => import(`@/views${menu.component}.vue`),
meta: {
title: menu.menuName,
icon: menu.icon,
isHide: menu.isHide === '0',
isLink: menu.isLink === '0',
isKeepAlive: menu.isKeepAlive === '0',
isFull: menu.isFull === '0',
isAffix: menu.isAffix === '0',
activeMenu: menu.activeMenu
}
};
if (menu.children && menu.children.length > 0) {
route.children = generateRoutes(menu.children);
}
if (menu.redirect) {
route.redirect = menu.redirect;
}
routes.push(route);
});
return routes;
}
```
### 权限控制
```javascript
// 页面权限控制
router.beforeEach((to, from, next) => {
const userPermissions = store.getters.permissions;
if (to.meta.auth) {
if (userPermissions.includes(to.meta.auth) || userPermissions.includes('*:*:*')) {
next();
} else {
next('/403');
}
} else {
next();
}
});
// 按钮权限控制
Vue.directive('permission', {
inserted(el, binding) {
const { value } = binding;
const permissions = store.getters.permissions;
if (value) {
const hasPermission = permissions.includes(value) || permissions.includes('*:*:*');
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el);
}
}
}
});
```
---
## 错误码说明
| 错误码 | 错误信息 | 说明 |
|--------|----------|------|
| 400 | 菜单名称不能为空 | 菜单名称为空 |
| 400 | 上级菜单不能为空 | 父菜单ID为空 |
| 400 | 菜单类型不能为空 | 菜单类型为空 |
| 400 | 菜单状态不能为空 | 菜单状态为空 |
| 400 | 是否隐藏菜单不能为空 | 隐藏状态为空 |
| 400 | 显示顺序不能为空 | 排序值为空 |
| 400 | 菜单不存在 | 菜单ID不存在 |
| 400 | 存在子菜单,不允许删除 | 菜单有子菜单时不能删除 |
| 400 | 菜单已分配,不允许删除 | 菜单已分配给角色时不能删除 |
| 400 | 不能选择自己作为父菜单 | 父菜单不能是自己 |
| 400 | 路由地址不能为空 | 菜单类型为菜单时路由地址必填 |
| 400 | 组件路径不能为空 | 菜单类型为菜单时组件路径必填 |
| 400 | 权限标识不能为空 | 菜单类型为按钮时权限标识必填 |
| 401 | 当前会话未登录 | 未登录或Token无效 |
| 403 | 权限不足 | 没有相应的操作权限 |
| 500 | 系统异常 | 服务器内部错误 |
---
## 使用建议
### 1. 菜单设计规范
- **层级结构**: 建议不超过3级菜单
- **命名规范**: 使用有意义的英文名称作为路由名称
- **权限标识**: 使用模块:功能:操作的格式
- **图标使用**: 统一使用Element UI或其他图标库
### 2. 权限设计
- **粒度控制**: 按钮级别的权限控制
- **角色分离**: 不同角色分配不同的菜单权限
- **继承关系**: 子菜单权限依赖于父菜单权限
- **缓存策略**: 用户权限信息缓存到前端
### 3. 性能优化
- **懒加载**: 菜单组件使用懒加载
- **缓存机制**: 菜单数据缓存到本地存储
- **权限缓存**: 权限信息缓存到内存中
- **树形结构**: 使用高效的树形数据结构
### 4. 安全考虑
- **权限验证**: 前后端都要进行权限验证
- **敏感操作**: 重要操作需要二次确认
- **日志记录**: 记录权限变更操作日志
- **最小权限**: 遵循最小权限原则
---
## 注意事项
1. **菜单删除**: 删除菜单前需要检查是否有子菜单和角色关联
2. **权限继承**: 子菜单的权限依赖于父菜单
3. **缓存更新**: 菜单权限变更后需要清理相关缓存
4. **前端同步**: 菜单结构变更后前端需要同步更新
5. **权限验证**: 前后端都需要进行权限验证
6. **状态管理**: 菜单状态变更会影响用户访问
7. **排序规则**: 菜单按照sorted字段升序排列
8. **数据完整性**: 保证菜单数据的完整性和一致性