一、超管端设计优化 - 文档管理SOP体系建立,docs目录重组 - 统一用户管理:跨租户全局视角,合并用户管理+公众用户 - 活动监管全模块重构:全部活动(统计卡片+阶段筛选+SuperDetail详情页)、报名数据/作品数据/评审进度(两层合一扁平列表)、成果发布(去Tab+统计+隐藏写操作) - 菜单精简:移除评委管理/评审规则/通知管理 - Bug修复:租户编辑丢失隐藏菜单、pageSize限制、主色统一 二、UGC绘本创作社区P0 - 数据库:10张新表(user_works/user_work_pages/work_tags等) - 子女账号独立化:Child升级为独立User,家长切换+独立登录 - 用户作品库:CRUD+发布审核,8个API - AI创作流程:提交→生成→保存到作品库,4个API - 作品广场:首页改造为推荐流,标签+搜索+排序 - 内容审核(超管端):作品审核+作品管理+标签管理 - 活动联动:WorkSelector作品选择器 - 布局改造:底部5Tab(发现/创作/活动/作品库/我的) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.0 KiB
7.0 KiB
菜单权限控制说明
📋 概述
系统通过 权限编码(Permission Code) 来控制用户对菜单的访问。菜单权限控制分为两个层面:
- 菜单显示控制:根据用户权限过滤菜单,只显示用户有权限访问的菜单
- 路由访问控制:通过路由守卫检查用户是否有权限访问某个页面
🔄 权限控制流程
用户登录
↓
获取用户角色和权限
↓
调用 /api/menus/user-menus 获取用户菜单
↓
后端根据用户权限过滤菜单
↓
前端动态生成路由和菜单
↓
路由守卫检查页面访问权限
🎯 如何配置菜单权限
1. 创建权限
首先需要在权限管理中创建权限,例如:
menu:read- 查看菜单权限user:read- 查看用户权限role:read- 查看角色权限
2. 将权限分配给角色
在角色管理中,为角色分配相应的权限。例如:
- 管理员角色:拥有所有权限
- 普通用户角色:只拥有
user:read权限
3. 为用户分配角色
在用户管理中,为用户分配角色。用户会继承角色的所有权限。
4. 为菜单设置权限编码
在菜单管理中,为菜单设置 权限编码 字段:
示例配置
| 菜单名称 | 路径 | 权限编码 | 说明 |
|---|---|---|---|
| 用户管理 | /system/users | user:read |
只有拥有 user:read 权限的用户才能看到此菜单 |
| 角色管理 | /system/roles | role:read |
只有拥有 role:read 权限的用户才能看到此菜单 |
| 权限管理 | /system/permissions | - | 不设置权限编码,所有用户都可以看到 |
| 仪表盘 | /dashboard | - | 不设置权限编码,所有用户都可以看到 |
5. 权限编码规则
权限编码格式:资源:操作
常见示例:
user:read- 查看用户user:create- 创建用户user:update- 更新用户user:delete- 删除用户role:read- 查看角色menu:read- 查看菜单
💻 技术实现
后端实现
1. 菜单权限过滤(MenusService.findUserMenus)
// 获取用户的所有权限
const userPermissions = await this.authService.getUserPermissions(userId);
// 过滤菜单:如果菜单有permission字段,检查用户是否有该权限
const filterMenus = (menus: any[]): any[] => {
return menus
.filter((menu) => {
// 如果菜单没有设置权限要求,则显示
if (!menu.permission) {
return true;
}
// 如果设置了权限要求,检查用户是否有该权限
return userPermissions.includes(menu.permission);
})
.map((menu) => {
// 递归过滤子菜单
if (menu.children && menu.children.length > 0) {
menu.children = filterMenus(menu.children);
}
return menu;
});
};
2. 用户权限获取(AuthService.getUserPermissions)
async getUserPermissions(userId: number): Promise<string[]> {
const user = await this.usersService.findOne(userId);
const permissions = new Set<string>();
// 遍历用户的所有角色
user.roles?.forEach((ur: any) => {
// 遍历角色的所有权限
ur.role.permissions?.forEach((rp: any) => {
permissions.add(rp.permission.code);
});
});
return Array.from(permissions);
}
前端实现
1. 菜单转换为路由(convertMenusToRoutes)
const route: RouteRecordRaw = {
path: routePath,
name: routeName,
meta: {
title: menu.name,
requiresAuth: true,
// 如果菜单有权限要求,添加到路由meta中
...(menu.permission && { permissions: [menu.permission] }),
},
component: componentLoader,
};
2. 路由守卫检查(router.beforeEach)
// 检查权限
const requiredPermissions = to.meta.permissions;
if (requiredPermissions && requiredPermissions.length > 0) {
if (!authStore.hasAnyPermission(requiredPermissions)) {
// 没有所需权限,跳转到 403 页面
next({ name: "Forbidden" });
return;
}
}
3. 权限检查方法(authStore)
// 检查是否有指定权限
const hasPermission = (permission: string): boolean => {
return user.value?.permissions?.includes(permission) ?? false;
};
// 检查是否有任一权限
const hasAnyPermission = (permissions: string[]): boolean => {
if (!permissions || permissions.length === 0) return true;
return permissions.some((perm) => hasPermission(perm));
};
📝 使用示例
示例 1:创建需要权限的菜单
- 登录系统,进入 菜单管理
- 点击 新增菜单
- 填写菜单信息:
- 菜单名称:
用户管理 - 路由路径:
/system/users - 组件路径:
system/users/Index - 权限编码:
user:read⭐ - 父菜单:选择
系统管理
- 菜单名称:
- 保存菜单
示例 2:创建公开菜单(所有用户可见)
- 在菜单管理中新增菜单
- 权限编码字段留空
- 这样所有用户都可以看到此菜单
示例 3:为用户分配权限
-
进入 权限管理,创建权限:
- 权限名称:
查看用户 - 权限编码:
user:read - 资源:
user - 操作:
read
- 权限名称:
-
进入 角色管理,编辑角色:
- 为角色分配
user:read权限
- 为角色分配
-
进入 用户管理,编辑用户:
- 为用户分配该角色
⚠️ 注意事项
- 权限编码必须唯一:每个权限编码在系统中是唯一的
- 菜单权限为空则公开:如果菜单的
权限编码字段为空,所有用户都可以看到 - 子菜单继承父菜单权限:子菜单会独立检查权限,不会自动继承父菜单权限
- 路由和菜单双重控制:
- 菜单显示控制:控制菜单是否在侧边栏显示
- 路由访问控制:控制用户是否可以直接访问页面(通过 URL)
- 权限变更后需重新登录:权限变更后,用户需要重新登录才能看到新的菜单
🔍 调试技巧
1. 查看用户权限
在浏览器控制台执行:
// 查看当前用户权限
console.log(useAuthStore().user?.permissions);
// 检查是否有特定权限
console.log(useAuthStore().hasPermission("user:read"));
2. 查看用户菜单
// 查看当前用户的菜单
console.log(useAuthStore().menus);
3. 后端调试
在后端日志中查看:
- 用户权限列表
- 菜单过滤结果