## 1. 组件概述
`PageGuard` 是一个*路由权限守卫组件*,用于保护页面访问权限。它通过检查当前路由路径是否在用户有权限访问的菜单列表中,来决定是否允许用户访问该页面。
## 2. 核心工作流程
## 3. 权限判断逻辑
### 3.1 权限判断核心代码
useEffect(() => { setIsForbidden(!mainMenu3.find((mn) => mn.path === history.location.pathname)); }, [history.location.pathname, mainMenu3]);*逻辑说明*:
- 在 `mainMenu3` 数组中查找是否存在 `path` 等于当前路径的菜单项
- 如果找到(`find` 返回真值),说明用户有权限,`isForbidden = false`
- 如果找不到(`find` 返回 `undefined`),说明用户无权限,`isForbidden = true`
### 3.2 mainMenu3 的生成过程
## 4. 权限检查的详细流程
### 4.1 路由配置结构
每个路由配置包含以下关键字段:
{ path: '/admin/configuration/system/preset', // 路由路径 code: 33620224, // 权限代码 menu: true, // 是否在菜单中显示 component: './Admin/Configuration/System/Preset', // 组件路径 }### 4.2 权限过滤逻辑
在 `useMenu` hook 中,权限检查分为两个层级:
#### 第一层:二级菜单权限检查(第 66 行)
if (!hasItem(menu[idx].second, secondName) && [...myInfo.accessControl, 0].includes(r.code)) { // 添加到二级菜单 }**说明**:
- `myInfo.accessControl` 是用户拥有的权限代码数组
- `[...myInfo.accessControl, 0]` 表示包含所有用户权限 + 公共权限(code=0)
- 只有当路由的 `code` 在用户权限列表中时,才会被添加到菜单
#### 第二层:三级菜单权限检查(第 77 行)
if (thirdIdx !== -1 && [...myInfo.accessControl, 0].includes(r.code)) { // 添加到 mainMenu3 }**说明**:
- 同样检查用户权限
- 只有通过权限检查的路由才会被添加到 `mainMenu3`
### 4.3 特殊权限处理
**角色管理页面特殊处理**(第 92-96 行):
if (m3.name === 'menu.admin.configuration_systemAccess_roleManager') { if (myInfo.name === 'admin') { // 只有超级管理员才能访问 menu3.push(m3); } }**说明**:
- 角色管理页面需要额外的权限检查
- 只有用户名为 `'admin'` 的超级管理员才能访问
- 这是硬编码的特殊权限规则
## 5. 组件渲染逻辑
### 5.1 有权限时(isForbidden = false)
<>{children}</>- 直接渲染传入的子组件
- 用户可以看到页面内容
### 5.2 无权限时(isForbidden = true)
<Box textAlign={'center'} paddingTop={'20vh'} sx={{ userSelect: 'none' }}> <DoNotDisturbAltIcon /> <Typography variant="h5"> {formatMessage({ id: 'component.pageGuard.forbiddenTitle' })} </Typography> <Typography>{formatMessage({ id: 'component.pageGuard.forbiddenMsg' })}</Typography> </Box>- 显示禁止访问的提示页面
- 包含禁止图标和提示文字
- 用户无法看到页面内容
## 6. 数据流图
## 7. 关键依赖关系
### 7.1 依赖的 Hook
1. **useMenu Hook**
- 位置:`@/hooks/useMenu`
- 功能:生成有权限的菜单列表
- 返回:`{ mainMenu, mainMenu3 }`
2. **useMainStore**
- 位置:`@/store`
- 功能:提供用户信息 `myInfo`,包含 `accessControl` 权限数组
### 7.2 依赖的数据源
1. **adminRouteConfig**
- 位置:`config/routes.ts`
- 内容:所有路由配置,包含 `path`、`code`、`menu` 等字段
2. **myInfo.accessControl**
- 来源:用户登录后从后端获取
- 内容:用户拥有的权限代码数组
## 8. 使用场景
### 8.1 典型使用方式
// 在页面组件中使用 const Preset: React.FC = () => { return ( <PageGuard> <PageMargin>{/* 页面内容 */}</PageMargin> </PageGuard> ); };### 8.2 保护范围
- **页面级保护**:整个页面组件被包裹,无权限时无法看到任何内容
- **实时检查**:路由变化时自动重新检查权限
- **动态权限**:权限变更后,菜单更新,PageGuard 自动响应
## 9. 权限检查的时机
### 9.1 初始化检查
- 组件首次渲染时
- `useEffect` 立即执行权限检查
### 9.2 路由变化检查
- 当 `history.location.pathname` 变化时
- `useEffect` 依赖项变化,重新执行检查
### 9.3 权限更新检查
- 当 `mainMenu3` 更新时(用户权限变更)
- `useEffect` 依赖项变化,重新执行检查
## 10. 注意事项
### 10.1 权限代码的作用
- 每个路由都有一个唯一的 `code`(权限代码)
- 用户的 `accessControl` 数组包含用户拥有的所有权限代码
- 只有当路由的 `code` 在用户的 `accessControl` 中时,该路由才会出现在 `mainMenu3` 中
### 10.2 公共权限(code = 0)
- `[...myInfo.accessControl, 0]` 中的 `0` 表示公共权限
- 所有用户都拥有 code=0 的权限
- 这允许某些路由对所有用户开放
### 10.3 菜单显示 vs 页面访问
- `menu: true` 的路由会在菜单中显示
- `menu: false` 的路由不会在菜单中显示,但仍可能被添加到 `mainMenu3`
- PageGuard 只检查路径是否在 `mainMenu3` 中,不关心 `menu` 字段
### 10.4 特殊权限处理
- 角色管理页面需要额外的超级管理员检查
- 这是硬编码的特殊逻辑,可能需要扩展以支持更多特殊权限场景
## 11. 潜在问题和改进建议
### 11.1 潜在问题
1. **路径匹配精确性**
- 当前使用精确路径匹配
- 如果路由配置中的路径与实际访问路径不一致,可能导致权限判断错误
2. **编辑页面权限**
- 编辑页面(如 `/admin/configuration/system/preset/edit`)可能不在 `mainMenu3` 中
- 如果编辑页面没有 `menu: true`,可能无法通过 PageGuard 检查
3. **动态路由权限**
- 对于带参数的路由(如 `/admin/xxx/:id`),当前实现可能无法正确匹配
### 11.2 改进建议
1. **路径匹配优化**
- 支持路径模式匹配(如 `/admin/xxx/:id`)
- 支持路径前缀匹配
2. **权限缓存**
- 可以考虑缓存权限检查结果,减少重复计算
3. **更细粒度的权限控制**
- 支持页面内元素的权限控制
- 支持操作级权限(查看、编辑、删除等)
4. **权限日志**
- 记录权限检查失败的情况,便于调试和审计