generator-mico-cli 0.2.1 → 0.2.2-8.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +199 -15
  2. package/bin/mico.js +232 -27
  3. package/generators/micro-react/index.js +200 -18
  4. package/generators/micro-react/meta.json +13 -0
  5. package/generators/micro-react/templates/.commitlintrc.js +1 -0
  6. package/generators/micro-react/templates/.cursor/rules/always-read-docs.mdc +14 -4
  7. package/generators/micro-react/templates/.cursor/rules/cicd-deploy.mdc +10 -8
  8. package/generators/micro-react/templates/.cursor/rules/coding-conventions.mdc +1 -1
  9. package/generators/micro-react/templates/.cursor/rules/development-guide.mdc +3 -4
  10. package/generators/micro-react/templates/.cursor/rules/layout-app.mdc +38 -31
  11. package/generators/micro-react/templates/.cursor/rules/project-overview.mdc +7 -4
  12. package/generators/micro-react/templates/.cursor/rules/theme-system.mdc +10 -12
  13. package/generators/micro-react/templates/.eslintrc.js +25 -1
  14. package/generators/micro-react/templates/AGENTS.md +5 -2
  15. package/generators/micro-react/templates/CICD/before_build.sh +76 -0
  16. package/generators/micro-react/templates/CICD/start_dev.sh +27 -3
  17. package/generators/micro-react/templates/CICD/start_prod.sh +26 -3
  18. package/generators/micro-react/templates/CICD/start_test.sh +28 -3
  19. package/generators/micro-react/templates/CICD/wangsu_fresh_dev.sh +4 -4
  20. package/generators/micro-react/templates/CICD/wangsu_fresh_prod.sh +4 -4
  21. package/generators/micro-react/templates/CICD/wangsu_fresh_test.sh +4 -4
  22. package/generators/micro-react/templates/CLAUDE.md +16 -9
  23. package/generators/micro-react/templates/README.md +42 -4
  24. package/generators/micro-react/templates/_gitignore +4 -0
  25. package/generators/micro-react/templates/_npmrc +4 -0
  26. package/generators/micro-react/templates/apps/layout/config/config.dev.ts +32 -16
  27. package/generators/micro-react/templates/apps/layout/config/config.prod.development.ts +24 -29
  28. package/generators/micro-react/templates/apps/layout/config/config.prod.testing.ts +25 -6
  29. package/generators/micro-react/templates/apps/layout/config/config.prod.ts +16 -7
  30. package/generators/micro-react/templates/apps/layout/config/config.ts +27 -4
  31. package/generators/micro-react/templates/apps/layout/config/routes.ts +5 -5
  32. package/generators/micro-react/templates/apps/layout/docs/arch-/346/227/245/345/277/227/344/270/216/345/270/270/351/207/217.md +2 -2
  33. package/generators/micro-react/templates/apps/layout/docs/common-intl.md +372 -0
  34. package/generators/micro-react/templates/apps/layout/docs/feat-/346/236/204/345/273/272define/344/270/216/345/205/215/350/256/244/350/257/201/345/210/235/345/247/213/346/200/201.md +44 -0
  35. package/generators/micro-react/templates/apps/layout/docs/feature-404/351/241/265/351/235/242.md +103 -0
  36. package/generators/micro-react/templates/apps/layout/docs/feature-/344/270/273/351/242/230/350/211/262/345/210/207/346/215/242.md +22 -26
  37. package/generators/micro-react/templates/apps/layout/docs/feature-/345/276/256/345/211/215/347/253/257/346/250/241/345/274/217.md +185 -28
  38. package/generators/micro-react/templates/apps/layout/docs/feature-/350/217/234/345/215/225/346/235/203/351/231/220/346/216/247/345/210/266.md +308 -63
  39. package/generators/micro-react/templates/apps/layout/docs/feature-/350/267/257/347/224/261/344/270/216/350/217/234/345/215/225/350/247/243/350/200/246.md +179 -0
  40. package/generators/micro-react/templates/apps/layout/docs/fix-SSO/346/227/240/351/231/220/351/207/215/345/256/232/345/220/221.md +88 -0
  41. package/generators/micro-react/templates/apps/layout/docs/utils-timezone.md +324 -0
  42. package/generators/micro-react/templates/apps/layout/mock/api.mock.ts +81 -61
  43. package/generators/micro-react/templates/apps/layout/mock/menus.ts +114 -4
  44. package/generators/micro-react/templates/apps/layout/mock/pages.ts +86 -0
  45. package/generators/micro-react/templates/apps/layout/package.json +7 -4
  46. package/generators/micro-react/templates/apps/layout/src/app.tsx +111 -76
  47. package/generators/micro-react/templates/apps/layout/src/common/auth/index.ts +3 -0
  48. package/generators/micro-react/templates/apps/layout/src/common/helpers.ts +177 -0
  49. package/generators/micro-react/templates/apps/layout/src/common/locale.ts +22 -17
  50. package/generators/micro-react/templates/apps/layout/src/common/menu/parser.ts +192 -42
  51. package/generators/micro-react/templates/apps/layout/src/common/menu/types.ts +69 -5
  52. package/generators/micro-react/templates/apps/layout/src/common/micro/index.ts +34 -0
  53. package/generators/micro-react/templates/apps/layout/src/common/micro-prefetch.ts +109 -0
  54. package/generators/micro-react/templates/apps/layout/src/common/portal-data.ts +45 -0
  55. package/generators/micro-react/templates/apps/layout/src/common/request/config.ts +72 -10
  56. package/generators/micro-react/templates/apps/layout/src/common/request/index.ts +2 -2
  57. package/generators/micro-react/templates/apps/layout/src/common/request/interceptors.ts +31 -3
  58. package/generators/micro-react/templates/apps/layout/src/common/request/sso.ts +29 -11
  59. package/generators/micro-react/templates/apps/layout/src/common/request/url-resolver.ts +1 -1
  60. package/generators/micro-react/templates/apps/layout/src/common/route-guard.ts +345 -0
  61. package/generators/micro-react/templates/apps/layout/src/common/theme.ts +2 -4
  62. package/generators/micro-react/templates/apps/layout/src/common/upload/oss.ts +3 -4
  63. package/generators/micro-react/templates/apps/layout/src/common/upload/types.ts +1 -1
  64. package/generators/micro-react/templates/apps/layout/src/common/uploadFiles.ts +1 -1
  65. package/generators/micro-react/templates/apps/layout/src/components/AppTabs/index.less +8 -3
  66. package/generators/micro-react/templates/apps/layout/src/components/AppTabs/index.tsx +25 -8
  67. package/generators/micro-react/templates/apps/layout/src/components/HeaderDropdown/index.tsx +20 -0
  68. package/generators/micro-react/templates/apps/layout/src/components/IconFont/index.tsx +5 -6
  69. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.less +21 -6
  70. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.tsx +83 -149
  71. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/micro-app-manager.ts +569 -0
  72. package/generators/micro-react/templates/apps/layout/src/components/RightContent/AvatarDropdown.tsx +383 -0
  73. package/generators/micro-react/templates/apps/layout/src/components/RightContent/avatar-dropdown.less +35 -0
  74. package/generators/micro-react/templates/apps/layout/src/components/RightContent/index.ts +2 -0
  75. package/generators/micro-react/templates/apps/layout/src/constants/index.ts +170 -6
  76. package/generators/micro-react/templates/apps/layout/src/global.less +18 -9
  77. package/generators/micro-react/templates/apps/layout/src/hooks/useMenu.ts +3 -2
  78. package/generators/micro-react/templates/apps/layout/src/hooks/useRoutePermissionRefresh.ts +72 -0
  79. package/generators/micro-react/templates/apps/layout/src/layouts/components/header/index.less +3 -1
  80. package/generators/micro-react/templates/apps/layout/src/layouts/components/header/index.tsx +10 -55
  81. package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.less +34 -4
  82. package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.tsx +24 -8
  83. package/generators/micro-react/templates/apps/layout/src/layouts/index.less +84 -13
  84. package/generators/micro-react/templates/apps/layout/src/layouts/index.tsx +156 -69
  85. package/generators/micro-react/templates/apps/layout/src/locales/en-US.ts +12 -0
  86. package/generators/micro-react/templates/apps/layout/src/locales/zh-CN.ts +12 -0
  87. package/generators/micro-react/templates/apps/layout/src/pages/403/index.tsx +8 -2
  88. package/generators/micro-react/templates/apps/layout/src/pages/404/index.tsx +78 -0
  89. package/generators/micro-react/templates/apps/layout/src/pages/Home/index.less +3 -0
  90. package/generators/micro-react/templates/apps/layout/src/pages/Home/index.tsx +7 -1
  91. package/generators/micro-react/templates/apps/layout/src/pages/User/Login/index.less +1 -1
  92. package/generators/micro-react/templates/apps/layout/src/pages/User/Login/index.tsx +3 -3
  93. package/generators/micro-react/templates/apps/layout/src/requestErrorConfig.ts +1 -1
  94. package/generators/micro-react/templates/apps/layout/src/services/config/index.ts +63 -0
  95. package/generators/micro-react/templates/apps/layout/src/services/config/type.ts +30 -0
  96. package/generators/micro-react/templates/apps/layout/src/services/user.ts +29 -2
  97. package/generators/micro-react/templates/apps/layout/tailwind.config.js +3 -0
  98. package/generators/micro-react/templates/deployDesc.md +3 -3
  99. package/generators/micro-react/templates/dev.preset.json +14 -0
  100. package/generators/micro-react/templates/docs/dev-preset.md +130 -0
  101. package/generators/micro-react/templates/package.json +21 -6
  102. package/generators/micro-react/templates/packages/common-intl/README.md +427 -0
  103. package/generators/micro-react/templates/packages/common-intl/package.json +34 -0
  104. package/generators/micro-react/templates/packages/common-intl/src/index.ts +7 -0
  105. package/generators/micro-react/templates/packages/common-intl/src/indexedDBUtils.ts +51 -0
  106. package/generators/micro-react/templates/packages/common-intl/src/intl.ts +50 -0
  107. package/generators/micro-react/templates/packages/common-intl/src/utils.ts +482 -0
  108. package/generators/micro-react/templates/packages/common-intl/tsconfig.json +22 -0
  109. package/generators/micro-react/templates/packages/common-intl/vite.config.ts +25 -0
  110. package/generators/micro-react/templates/scripts/apply-sentry-plugin.ts +45 -0
  111. package/generators/micro-react/templates/scripts/collect-dist.js +10 -0
  112. package/generators/micro-react/templates/scripts/dev-preset.js +265 -0
  113. package/generators/micro-react/templates/scripts/dev-preset.schema.json +39 -0
  114. package/generators/micro-react/templates/turbo.json +4 -1
  115. package/generators/subapp-react/index.js +326 -40
  116. package/generators/subapp-react/meta.json +10 -0
  117. package/generators/subapp-react/templates/homepage/.env +2 -1
  118. package/generators/subapp-react/templates/homepage/README.md +3 -3
  119. package/generators/subapp-react/templates/homepage/config/config.dev.ts +14 -7
  120. package/generators/subapp-react/templates/homepage/config/config.prod.development.ts +16 -5
  121. package/generators/subapp-react/templates/homepage/config/config.prod.testing.ts +16 -5
  122. package/generators/subapp-react/templates/homepage/config/config.prod.ts +14 -5
  123. package/generators/subapp-react/templates/homepage/config/config.ts +21 -0
  124. package/generators/subapp-react/templates/homepage/config/routes.ts +2 -2
  125. package/generators/subapp-react/templates/homepage/mock/api.mock.ts +2 -2
  126. package/generators/subapp-react/templates/homepage/package.json +7 -4
  127. package/generators/subapp-react/templates/homepage/src/app.tsx +18 -27
  128. package/generators/subapp-react/templates/homepage/src/common/request.ts +29 -2
  129. package/generators/subapp-react/templates/homepage/src/global.less +6 -5
  130. package/generators/subapp-react/templates/homepage/src/pages/index.less +3 -3
  131. package/generators/subapp-react/templates/homepage/src/pages/index.tsx +99 -60
  132. package/generators/subapp-react/templates/homepage/src/styles/theme.less +1 -1
  133. package/generators/subapp-umd/ignore-list.json +5 -0
  134. package/generators/subapp-umd/index.js +309 -0
  135. package/generators/subapp-umd/meta.json +11 -0
  136. package/generators/subapp-umd/templates/README.md +94 -0
  137. package/generators/subapp-umd/templates/package.json +35 -0
  138. package/generators/subapp-umd/templates/public/index.html +34 -0
  139. package/generators/subapp-umd/templates/src/App.less +15 -0
  140. package/generators/subapp-umd/templates/src/App.tsx +13 -0
  141. package/generators/subapp-umd/templates/src/index.ts +2 -0
  142. package/generators/subapp-umd/templates/tsconfig.json +27 -0
  143. package/generators/subapp-umd/templates/webpack.config.js +70 -0
  144. package/lib/utils.js +332 -2
  145. package/package.json +15 -2
  146. package/generators/micro-react/templates/apps/layout/mock/menus.json +0 -100
  147. package/generators/micro-react/templates/apps/layout/src/common/constants.ts +0 -38
  148. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/container-manager.ts +0 -202
  149. package/generators/micro-react/templates/packages/shared-styles/README.md +0 -124
  150. package/generators/micro-react/templates/packages/shared-styles/arco-design-mobile-override.less +0 -91
  151. package/generators/micro-react/templates/packages/shared-styles/arco-override.less +0 -119
  152. package/generators/micro-react/templates/packages/shared-styles/index.d.ts +0 -44
  153. package/generators/micro-react/templates/packages/shared-styles/index.less +0 -13
  154. package/generators/micro-react/templates/packages/shared-styles/package.json +0 -30
  155. package/generators/micro-react/templates/packages/shared-styles/theme-inject.less +0 -10
  156. package/generators/micro-react/templates/packages/shared-styles/themes/dark/custom-var.less +0 -290
  157. package/generators/micro-react/templates/packages/shared-styles/themes/normal/custom-var.less +0 -269
  158. package/generators/micro-react/templates/packages/shared-styles/variables-only.less +0 -433
  159. package/generators/micro-react/templates/packages/shared-styles/variables.less +0 -452
@@ -1,58 +1,193 @@
1
1
  # 菜单权限控制
2
2
 
3
- > 创建时间:2026-01-24
3
+ > 创建时间:2026-01-24 更新时间:2026-02-08
4
4
 
5
5
  ## 功能概述
6
6
 
7
7
  基于用户信息中的 `side_menus` 字段实现菜单和路由的白名单权限控制。非超级用户只能看到和访问 `side_menus` 中配置的菜单项,访问无权限路由时显示 403 页面。
8
8
 
9
+ **v2 更新**:支持认证(Authentication)与授权(Authorization)分离配置,可独立控制"跳过 SSO 登录"和"跳过菜单权限校验"。
10
+
11
+ ## 核心概念
12
+
13
+ ### 认证 vs 授权
14
+
15
+ | 概念 | 英文 | 作用 | 配置项 |
16
+ | ---- | -------------- | ------------------------ | ----------------------- |
17
+ | 认证 | Authentication | 确认用户身份(是否登录) | `noAuthRouteList` |
18
+ | 授权 | Authorization | 确认用户权限(能否访问) | `noPermissionRouteList` |
19
+
20
+ ### 四种路由场景
21
+
22
+ | 场景 | noAuthRouteList | noPermissionRouteList | accessControlEnabled | 示例 |
23
+ | --- | --- | --- | --- | --- |
24
+ | 公开页面 | ✅ | ✅ | `false` | 首页、活动页 |
25
+ | 登录后公共页面 | ❌ | ✅ | - | 个人设置、帮助中心 |
26
+ | 需要权限的页面 | ❌ | ❌ | `true` | 后台管理、业务功能 |
27
+ | 登录页等特殊页面 | ✅ | ✅ | `false` | /login、/403 |
28
+
9
29
  ## 技术方案
10
30
 
11
31
  ### 技术栈
12
32
 
13
33
  - 框架:React 18 + @umijs/max
14
- - UI 组件:Arco Design (Result 组件)
34
+ - UI 组件:@mico-platform/ui (Result 组件)
15
35
  - 状态管理:Umi initialState
16
36
 
17
- ### 核心实现
37
+ ### 权限控制流程
18
38
 
19
- 1. 用户信息接口返回 `side_menus` 字段(白名单)
20
- 2. `filterMenuItems` 根据白名单过滤菜单项
21
- 3. Layout 组件对比"所有路由"和"有权限路由"判断 403
22
- 4. 无权限时在 Layout 内直接渲染 403 组件(无跳转)
39
+ ```
40
+ 用户访问路由
41
+
42
+
43
+ ┌──────────────────────────────────────────────┐
44
+ │ 1. SSO 认证检查 (app.tsx) │
45
+ │ isNoAuthRoute(pathname)? │
46
+ │ ├── 是 → 跳过 SSO │
47
+ │ page.accessControlEnabled === false? │
48
+ │ ├── 是 → 跳过 SSO(PAGES 数据驱动) │
49
+ │ └── 否 → 执行 ensureSsoSession() │
50
+ └──────────────────────────────────────────────┘
51
+
52
+
53
+ ┌──────────────────────────────────────────┐
54
+ │ 2. 权限校验 (layouts/index.tsx) │
55
+ │ isNoPermissionRoute(pathname)? │
56
+ │ ├── 是 → 跳过权限校验 │
57
+ │ └── 否 → 双层权限判断 │
58
+ │ Tier 1: 页面在菜单中? │
59
+ │ ├── 是 → 沿用 sideMenus │
60
+ │ │ 白名单逻辑 │
61
+ │ └── 否 → Tier 2: 页面级权限 │
62
+ │ adminOnly / routeKey│
63
+ │ 详见:路由与菜单解耦文档 │
64
+ └──────────────────────────────────────────┘
65
+
66
+
67
+ ┌──────────────────────────────────────────┐
68
+ │ 3. 菜单渲染 (menu/index.tsx) │
69
+ │ 按 side_menus 过滤 │
70
+ │ 每个菜单项独立检查 isNoPermissionRoute│
71
+ │ ├── 匹配 → 该项始终显示 │
72
+ │ └── 不匹配 → 按 sideMenus 白名单过滤 │
73
+ └──────────────────────────────────────────┘
74
+
75
+
76
+ ┌──────────────────────────────────────────────┐
77
+ │ 4. 子应用加载 (MicroAppLoader) │
78
+ │ page.accessControlEnabled === false? │
79
+ │ ├── 是 → 直接加载(PAGES 数据驱动) │
80
+ │ isNoAuthRoute(pathname)? │
81
+ │ ├── 是 → 直接加载(静态配置兜底) │
82
+ │ isNoPermissionRoute(pathname)? │
83
+ │ ├── 是 → 直接加载 │
84
+ │ └── 否 → 等待 currentUser │
85
+ └──────────────────────────────────────────────┘
86
+ ```
23
87
 
24
- ### 权限判断逻辑
88
+ ### 权限判断逻辑(详细)
25
89
 
26
90
  ```
27
91
  用户访问 /some-path
28
-
92
+
93
+
94
+ 是否 disableAuth === true?
95
+ ├── 是 → 允许所有访问(调试模式)
96
+
97
+
98
+ page.accessControlEnabled === false?
99
+ ├── 是 → 跳过 SSO 认证 + 跳过权限校验
100
+
101
+
102
+ 是否在 noPermissionRouteList 中?
103
+ ├── 是 → 允许访问,菜单按各项独立判断
104
+
105
+
106
+ 是否是非动态路由(不在 PAGES 中)?
107
+ ├── 是 → 交给 Umi 处理(404 等静态路由)
108
+
109
+
29
110
  是否是超级用户?
30
- ├── 是 → 允许访问所有菜单
31
- └── 否 → 检查 side_menus 白名单
32
- ├── 精确匹配:menuPath === side_menus[i]
33
- ├── 前缀匹配:side_menus[i].startsWith(menuPath + '.')
34
- └── 都不匹配禁止访问,显示 403
111
+ ├── 是 → 允许访问所有页面
112
+
113
+
114
+ Tier 1: 页面在菜单中?
115
+ ├── 沿用菜单权限逻辑:
116
+ │ 菜单项 adminOnly === true?
117
+ │ ├── 是 → 禁止访问
118
+ │ 检查 side_menus 白名单
119
+ │ ├── 匹配 → 允许访问
120
+ │ └── 不匹配 → 显示 403
121
+
122
+
123
+ Tier 2: 隐藏页面(不在菜单中)
124
+ adminOnly === true?
125
+ ├── 是 → 显示 403
126
+ accessControlEnabled === true?
127
+ ├── 是 → 检查 routeKey ∈ side_menus
128
+ │ ├── 匹配 → 允许访问
129
+ │ └── 不匹配 → 显示 403
130
+ └── 否 → 允许访问
35
131
  ```
36
132
 
37
133
  ## 文件清单
38
134
 
39
135
  ### 新增文件
40
136
 
41
- | 文件路径 | 说明 |
42
- |----------|------|
137
+ | 文件路径 | 说明 |
138
+ | ------------------------- | ------------------ |
43
139
  | `src/pages/403/index.tsx` | 403 无权限页面组件 |
44
140
 
45
141
  ### 修改文件
46
142
 
47
143
  | 文件路径 | 修改内容 |
48
- |----------|----------|
144
+ | --- | --- |
145
+ | `src/common/menu/types.ts` | 新增 `noPermissionRouteList` 类型定义 |
146
+ | `src/constants/index.ts` | 新增 `isNoPermissionRoute`、`getNoPermissionRouteList` 函数 |
49
147
  | `src/common/menu/parser.ts` | 新增 `filterMenuItems`、`isMenuAllowed` 等权限过滤函数 |
50
- | `src/layouts/index.tsx` | 新增 `isForbidden` 判断和 403 渲染逻辑 |
51
- | `src/layouts/components/menu/index.tsx` | 集成菜单过滤 |
148
+ | `src/layouts/index.tsx` | 新增 `isForbidden` 判断,集成 `isNoPermissionRoute` |
149
+ | `src/layouts/components/menu/index.tsx` | 集成菜单过滤,支持免权限校验路由 |
150
+ | `src/components/MicroAppLoader/index.tsx` | 集成 `isNoPermissionRoute`,免权限路由直接加载 |
151
+ | `src/app.tsx` | SSO 认证检查,使用 `isNoAuthRoute` |
52
152
  | `config/routes.ts` | 新增 `/403` 路由 |
53
153
 
54
154
  ## API / 组件接口
55
155
 
156
+ ### Window 配置
157
+
158
+ ```typescript
159
+ interface Window {
160
+ __MICO_CONFIG__?: {
161
+ /**
162
+ * 免认证路由列表(跳过 SSO 登录)
163
+ * 支持精确匹配和前缀匹配(以 /* 结尾)
164
+ */
165
+ noAuthRouteList?: string[];
166
+ /**
167
+ * 免权限校验路由列表(跳过菜单权限检查)
168
+ * 支持精确匹配和前缀匹配(以 /* 结尾)
169
+ * 注意:如果同时需要跳过 SSO,需同时配置 noAuthRouteList
170
+ */
171
+ noPermissionRouteList?: string[];
172
+ /** 关闭权限控制(菜单全部显示,路由不校验权限) */
173
+ disableAuth?: boolean;
174
+ };
175
+ }
176
+ ```
177
+
178
+ ### 路由判断函数
179
+
180
+ ```typescript
181
+ // 判断是否为免认证路由(跳过 SSO)
182
+ function isNoAuthRoute(pathname: string): boolean;
183
+
184
+ // 判断是否为免权限校验路由(跳过菜单权限检查)
185
+ function isNoPermissionRoute(pathname: string): boolean;
186
+
187
+ // 判断是否关闭权限控制
188
+ function isAuthDisabled(): boolean;
189
+ ```
190
+
56
191
  ### MenuFilterOptions
57
192
 
58
193
  ```typescript
@@ -77,26 +212,6 @@ function filterMenuItems(
77
212
  ): MenuItem[];
78
213
  ```
79
214
 
80
- **参数说明**:
81
-
82
- | 参数 | 类型 | 必填 | 说明 |
83
- |------|------|------|------|
84
- | items | MenuItem[] | 是 | 原始菜单数据 |
85
- | options | MenuFilterOptions | 否 | 过滤选项 |
86
- | parentPath | string | 否 | 父级路径(递归用) |
87
-
88
- ### isRouteAllowed
89
-
90
- ```typescript
91
- /**
92
- * 检查路由是否允许访问
93
- */
94
- function isRouteAllowed(
95
- menuPath: string,
96
- options?: MenuFilterOptions,
97
- ): boolean;
98
- ```
99
-
100
215
  ### 用户信息相关字段
101
216
 
102
217
  ```typescript
@@ -111,14 +226,42 @@ interface IUserInfo {
111
226
 
112
227
  ## 使用示例
113
228
 
114
- ### 用户信息接口返回
229
+ ### 配置免认证+免权限路由
230
+
231
+ ```javascript
232
+ // config/config.dev.ts
233
+ window.__MICO_CONFIG__ = {
234
+ // 免认证路由(跳过 SSO 登录)
235
+ noAuthRouteList: [
236
+ '/',
237
+ '/subapp',
238
+ '/public/*', // 前缀匹配
239
+ ],
240
+ // 免权限校验路由(跳过菜单权限检查)
241
+ noPermissionRouteList: [
242
+ '/',
243
+ '/subapp',
244
+ '/settings', // 登录后的公共页面
245
+ ],
246
+ disableAuth: false,
247
+ };
248
+ ```
249
+
250
+ ### 静态配置(代码中)
115
251
 
116
- ```json
117
- {
118
- "is_superuser": false,
119
- "side_menus": ["列队管理.配置队列", "工作台"],
120
- "miss_permissions": ["列队管理.配置队列.查看"]
121
- }
252
+ ```typescript
253
+ // src/constants/index.ts
254
+
255
+ // 静态免认证路由(始终生效)
256
+ export const NO_AUTH_ROUTE_LIST: string[] = [
257
+ '/user/login',
258
+ '/user/register',
259
+ '/403',
260
+ '/404',
261
+ ];
262
+
263
+ // 静态免权限校验路由(始终生效)
264
+ export const NO_PERMISSION_ROUTE_LIST: string[] = ['/403', '/404'];
122
265
  ```
123
266
 
124
267
  ### 菜单过滤结果
@@ -130,46 +273,148 @@ interface IUserInfo {
130
273
  │ └── 配置队列 ✅ 精确匹配 "列队管理.配置队列"
131
274
  ├── 质量管理 ❌ 不在白名单
132
275
  │ └── 抽样检查 ❌ 不在白名单
133
- └── 权限管理 ❌ 硬编码禁止(非超级用户)
276
+ └── 权限管理 ❌ adminOnly=true,非超级用户不可见
134
277
  ```
135
278
 
136
279
  ### Layout 中的权限判断
137
280
 
138
281
  ```tsx
139
- // layouts/index.tsx
282
+ // layouts/index.tsx — 双层权限判断
140
283
  const isForbidden = useMemo(() => {
141
- if (currentRoute) return false; // 在有权限的路由中找到了
142
- const routeInAll = findRouteByPath(allRoutes, location.pathname);
143
- return !!routeInAll; // 在所有路由中存在但无权限
144
- }, [currentRoute, allRoutes, location.pathname]);
145
-
146
- const renderContent = () => {
147
- if (isForbidden) {
148
- return <ForbiddenPage />;
284
+ if (isAuthDisabled()) return false;
285
+ if (isNoPermissionRoute(location.pathname)) return false;
286
+ if (!currentRoute) return false; // 非动态路由
287
+ if (isSuperuserUser(currentUser?.is_superuser)) return false;
288
+
289
+ // Tier 1: 菜单权限交叉引用
290
+ const inAllMenu = findRouteByPath(allMenuRoutes, location.pathname);
291
+ if (inAllMenu) {
292
+ return !findRouteByPath(allowedMenuRoutes, location.pathname);
149
293
  }
150
- // ... 正常渲染
151
- };
294
+
295
+ // Tier 2: 隐藏页面级权限
296
+ if (!hasWindowPages()) return false;
297
+ const page = findPageByPath(getWindowPages(), location.pathname);
298
+ if (!page) return false;
299
+ if (page.adminOnly) return true;
300
+ if (page.accessControlEnabled) {
301
+ const sideMenus = (currentUser?.side_menus || []) as string[];
302
+ return !page.routeKey || !sideMenus.includes(page.routeKey);
303
+ }
304
+ return false;
305
+ }, [...]);
152
306
  ```
153
307
 
308
+ ### MicroAppLoader 中的认证判断
309
+
310
+ ```tsx
311
+ // components/MicroAppLoader/index.tsx
312
+ const isAuthReady =
313
+ isAuthDisabled() ||
314
+ isPageAuthFree(location.pathname) ||
315
+ isNoAuthRoute(location.pathname) ||
316
+ isNoPermissionRoute(location.pathname) ||
317
+ !!initialState?.currentUser;
318
+
319
+ // 未准备好时不加载子应用
320
+ if (!isAuthReady) {
321
+ return; // 继续等待
322
+ }
323
+ ```
324
+
325
+ 判断优先级:
326
+ 1. `isAuthDisabled()` — 全局关闭权限,直接放行
327
+ 2. `isPageAuthFree` — **PAGES 数据驱动**,页面 `accessControlEnabled === false` 时跳过认证和权限校验
328
+ 3. `isNoAuthRoute()` — 静态配置兜底,免认证路由(PAGES 未注入时的降级保护)
329
+ 4. `isNoPermissionRoute()` — 免权限路由(如 403/404),无需等待 currentUser
330
+ 5. `!!initialState?.currentUser` — 已登录,有用户信息
331
+
332
+ **注意**:`accessControlEnabled === false` 同时影响三个阶段:
333
+
334
+ - SSO 认证(`app.tsx`):跳过 `ensureSsoSession()` 和 `handleAuthFailureRedirect()`
335
+ - 权限校验(`layouts/index.tsx`):Tier 2 隐藏页面默认放行
336
+ - 子应用加载(`MicroAppLoader`):不等待 currentUser 直接加载
337
+
154
338
  ## 设计决策
155
339
 
156
340
  | 决策点 | 选择 | 理由 |
157
- |--------|------|------|
341
+ | --- | --- | --- |
342
+ | accessControlEnabled 统一控制 | `false` 同时跳过认证和授权 | PAGES 数据驱动,一个字段即可标记公开页面,无需重复配置 noAuthRouteList |
343
+ | 认证与授权分离 | 两个独立配置项 | 不同场景需要不同组合,如"需要登录但不需要权限"的个人设置页 |
158
344
  | 权限模型 | 白名单 (`side_menus`) | 后端返回的 `side_menus` 是允许列表,比黑名单更安全 |
159
345
  | 403 处理 | 原地渲染组件 | 保持 URL 不变,用户体验更好,便于分享链接 |
160
346
  | 父级菜单显示 | 前缀匹配 | 子菜单有权限时,父级菜单需要作为容器显示 |
161
347
  | 超级用户 | 跳过所有检查 | 管理员需要完整访问权限 |
162
- | 权限管理菜单 | 硬编码禁止 | 非超级用户不应看到权限管理入口 |
348
+ | 免权限路由的菜单 | 按菜单项独立判断 | 每个菜单项根据自身路由是否匹配 noPermissionRouteList 独立决定显示,避免菜单随当前页面变化 |
349
+ | 免认证路由的子应用 | 直接加载 | 不等待 currentUser,免认证路由本身不需要登录 |
350
+ | 免权限路由的子应用 | 直接加载 | 不等待 currentUser,避免加载卡住 |
351
+ | 静态路由不受权限控制 | 默认允许访问 | 见下方"静态路由与动态路由"说明 |
352
+ | adminOnly 判断 | 读取菜单数据字段 | 替代硬编码菜单名称匹配,由后端数据驱动,支持多语言且无需前端维护 |
353
+
354
+ ## 静态路由与动态路由
355
+
356
+ ### 定义
357
+
358
+ | 类型 | 来源 | 示例 |
359
+ | --- | --- | --- |
360
+ | **静态路由** | 代码中定义 (`config/routes.ts`) | `/`, `/403`, `/404`, `/user/login` |
361
+ | **动态路由** | 后端页面配置 (`window.__MICO_PAGES__`,降级 `window.__MICO_MENUS__`) | `/queue-management`, `/quality-check` |
362
+
363
+ ### 权限控制范围
364
+
365
+ **当前设计:权限控制仅作用于动态路由,静态路由默认允许访问。**
366
+
367
+ ```
368
+ 权限判断逻辑(isForbidden):
369
+ 1. 检查 isAuthDisabled() → 关闭则全部允许
370
+ 2. 检查 isNoPermissionRoute() → 在免权限列表中则允许
371
+ 3. 检查 currentRoute(PAGES 中的路由)→ 不存在则非动态路由,交给 Umi
372
+ 4. 检查 isSuperuserUser() → 超级用户放行
373
+ 5. Tier 1: 在菜单中 → 检查 allowedMenuRoutes
374
+ 6. Tier 2: 不在菜单(隐藏页面)→ 检查 adminOnly + accessControlEnabled + routeKey
375
+ 7. 都不匹配 → 放行
376
+ ```
377
+
378
+ ### 设计理由
379
+
380
+ 1. **职责分离**:静态路由是框架层面的基础设施(登录、错误页),动态路由才是业务功能
381
+ 2. **简化配置**:不需要额外配置哪些静态路由需要权限,减少出错概率
382
+ 3. **向后兼容**:现有部署无需任何改动
383
+ 4. **复杂度控制**:避免引入更多配置项增加理解成本
384
+
385
+ ### 影响
386
+
387
+ 当用户 `is_superuser=false` 且 `side_menus=[]` 时:
388
+
389
+ | 路由 | 结果 | 原因 |
390
+ | --- | --- | --- |
391
+ | `/` (Home) | ✅ 正常显示 | 静态路由,不受权限控制 |
392
+ | `/403` | ✅ 正常显示 | 在 `noPermissionRouteList` 中 |
393
+ | `/404` | ✅ 正常显示 | 在 `noPermissionRouteList` 中 |
394
+ | `/user/login` | ✅ 正常显示 | 静态路由 + 在 `noAuthRouteList` 中 |
395
+ | `/queue-management` | ❌ 显示 403 | 动态路由,无权限 |
396
+
397
+ ### 如需控制静态路由
398
+
399
+ 如果业务上需要让某个静态路由(如 `/`)也受权限控制,有以下方案:
400
+
401
+ 1. **方案 A**:将该路由从静态路由改为动态路由(通过菜单配置)
402
+ 2. **方案 B**:在对应页面组件内部自行判断权限并重定向
403
+ 3. **方案 C**:扩展 `isForbidden` 逻辑(需额外开发,当前未实现)
163
404
 
164
405
  ## 注意事项
165
406
 
407
+ - `noAuthRouteList` 和 `noPermissionRouteList` 是独立的,需要根据场景分别配置
408
+ - 两个列表都支持 `/*` 后缀进行前缀匹配
166
409
  - `side_menus` 为空时,非超级用户没有任何菜单权限
167
410
  - `side_menus` 格式为菜单路径,如 `"列队管理.配置队列"`
168
411
  - `miss_permissions` 用于按钮级别权限控制,不影响菜单显示
169
412
  - 403 页面在 Layout 内渲染,不会触发路由跳转
170
- - 调试时可在控制台搜索 `isForbidden check` 查看权限判断日志
413
+ - 调试时可在控制台搜索 `isForbidden (menu check)` 或 `isForbidden (hidden page` 查看权限判断日志
414
+ - **常见问题**:配置了 `noAuthRouteList` 但页面仍显示 403,需同时配置 `noPermissionRouteList`
171
415
 
172
416
  ## 相关文档
173
417
 
174
- - [微前端模式](./feature-微前端模式.md) - 路由和菜单解析
175
- - [用户信息接口](./feature-用户信息接口.md) - 用户数据结构
418
+ - [路由与菜单解耦](./feature-路由与菜单解耦.md) - 路由注册与菜单导航数据源分离、双层权限详细说明
419
+ - [微前端模式](./feature-微前端模式.md) - 微应用加载机制
420
+ - [日志与常量](./arch-日志与常量.md) - 常量管理
@@ -0,0 +1,179 @@
1
+ # 路由与菜单解耦
2
+
3
+ > 创建时间:2026-02-25
4
+
5
+ ## 功能概述
6
+
7
+ 将动态路由注册与菜单导航的数据源解耦。路由注册消费 `window.__MICO_PAGES__`(页面列表),菜单栏消费 `window.__MICO_MENUS__`(菜单树)。两者独立运作,权限通过菜单交叉引用 + 页面级兜底的双层策略控制。
8
+
9
+ ## 技术方案
10
+
11
+ ### 技术栈
12
+
13
+ - 框架:React 18 + @umijs/max
14
+ - 微前端:qiankun
15
+ - 状态管理:Umi initialState
16
+
17
+ ### 数据源关系
18
+
19
+ ```
20
+ __MICO_PAGES__ (扁平列表,所有页面)
21
+ ├── 菜单中的页面(一定能在 __MICO_MENUS__ 中找到对应菜单项)
22
+ └── 隐藏页面(不在菜单中显示,但路由可访问)
23
+
24
+ __MICO_MENUS__ (菜单树,用于导航)
25
+ └── page 字段 → 引用 __MICO_PAGES__ 中的某个页面
26
+ ```
27
+
28
+ ### 数据流
29
+
30
+ ```
31
+ Before:
32
+ patchClientRoutes ← extractRoutes(MENUS)
33
+ layouts 权限校验 ← extractRoutes(MENUS) + filterMenuItems(MENUS)
34
+ 菜单渲染 ← parseMenuItems(MENUS)
35
+
36
+ After:
37
+ patchClientRoutes ← getDynamicRoutes() → PAGES 优先,降级 MENUS
38
+ layouts 权限校验 ← getDynamicRoutes() + extractRoutes(MENUS) + filterMenuItems(MENUS)
39
+ 菜单渲染 ← parseMenuItems(MENUS) ← 不变
40
+ ```
41
+
42
+ ### 权限控制流程
43
+
44
+ ```
45
+ 用户访问路由
46
+
47
+
48
+ disableAuth / noPermissionRoute / superuser?
49
+ ├── 是 → 放行
50
+
51
+
52
+ page.accessControlEnabled === false?
53
+ ├── 是 → 跳过 SSO 认证 + 跳过权限校验(公开页面)
54
+
55
+
56
+ 非动态路由(不在 PAGES 中)?
57
+ ├── 是 → 交给 Umi 处理(404 等)
58
+
59
+
60
+ ┌─────────────────────────────────────────────┐
61
+ │ Tier 1: 菜单权限交叉引用 │
62
+ │ 页面在 __MICO_MENUS__ 中? │
63
+ │ ├── 是 → 沿用 sideMenus 白名单逻辑 │
64
+ │ │ 在 allowedMenuRoutes 中? │
65
+ │ │ ├── 是 → 放行 │
66
+ │ │ └── 否 → 403 │
67
+ │ └── 否 → 进入 Tier 2 │
68
+ ├─────────────────────────────────────────────┤
69
+ │ Tier 2: 隐藏页面级权限 │
70
+ │ (仅 PAGES 数据可用时生效) │
71
+ │ adminOnly: true → 403 │
72
+ │ accessControlEnabled: true │
73
+ │ → routeKey ∈ sideMenus ? 放行 : 403 │
74
+ │ 其他 → 放行 │
75
+ └─────────────────────────────────────────────┘
76
+ ```
77
+
78
+ ### 降级策略
79
+
80
+ 当 `window.__MICO_PAGES__` 未注入时(如旧版部署),`getDynamicRoutes()` 自动降级到从 `window.__MICO_MENUS__` 提取路由,行为与改动前完全一致。
81
+
82
+ ```
83
+ getDynamicRoutes():
84
+ hasWindowPages() ?
85
+ ├── 是 → extractRoutesFromPages(PAGES) ← 新逻辑
86
+ └── 否 → extractRoutes(MENUS) ← 旧逻辑,向后兼容
87
+ ```
88
+
89
+ ## 文件清单
90
+
91
+ ### 修改文件
92
+
93
+ | 文件路径 | 修改内容 |
94
+ | --- | --- |
95
+ | `src/common/menu/types.ts` | `PageConfig` 新增 `accessControlEnabled`、`routeKey` 字段;新增 `PublicPageItem` 类型(`Omit<PageConfig, ...>`);Window 声明新增 `__MICO_PAGES__` |
96
+ | `src/common/menu/parser.ts` | 新增 `getWindowPages`、`hasWindowPages`、`extractRoutesFromPages`、`getDynamicRoutes`、`findPageByPath` 函数;导出 `isSuperuserUser` |
97
+ | `src/app.tsx` | `patchClientRoutes` 改为调用 `getDynamicRoutes()` |
98
+ | `src/layouts/index.tsx` | 路由匹配改用 `allPageRoutes`;权限判断改为双层逻辑(菜单交叉引用 + 隐藏页面级兜底) |
99
+
100
+ ### 未修改文件
101
+
102
+ | 文件路径 | 说明 |
103
+ | --- | --- |
104
+ | `src/layouts/components/menu/index.tsx` | 菜单渲染逻辑不变,继续消费 `__MICO_MENUS__` |
105
+
106
+ ## API / 组件接口
107
+
108
+ ### PublicPageItem
109
+
110
+ ```typescript
111
+ type PublicPageItem = Omit<
112
+ PageConfig,
113
+ 'workspaceSubdomain' | 'publishedBy' | 'versions' | 'createdAt' | 'updatedAt'
114
+ >;
115
+ ```
116
+
117
+ `PageConfig` 新增字段:
118
+
119
+ | 字段 | 类型 | 说明 |
120
+ | --- | --- | --- |
121
+ | `accessControlEnabled` | `boolean` | 是否开启访问控制。`false` 时跳过 SSO 认证和权限校验(公开页面);`true` 时需要登录且通过 `routeKey` 校验权限 |
122
+ | `routeKey` | `string \| null` | 路由权限标识(用于匹配 sideMenus) |
123
+
124
+ ### 页面数据函数
125
+
126
+ ```typescript
127
+ /** 获取 window.__MICO_PAGES__ */
128
+ function getWindowPages(): PublicPageItem[];
129
+
130
+ /** 判断页面数据是否可用 */
131
+ function hasWindowPages(): boolean;
132
+
133
+ /** 从页面数据提取路由配置 */
134
+ function extractRoutesFromPages(pages: PublicPageItem[]): ParsedRoute[];
135
+
136
+ /** 获取动态路由(PAGES 优先,降级 MENUS) */
137
+ function getDynamicRoutes(): ParsedRoute[];
138
+
139
+ /** 根据路径查找页面配置(精确匹配 + 通配符) */
140
+ function findPageByPath(pages: PublicPageItem[], pathname: string): PublicPageItem | undefined;
141
+ ```
142
+
143
+ ### Window 全局变量
144
+
145
+ ```typescript
146
+ interface Window {
147
+ /** 页面列表 — 动态路由注册的数据源 */
148
+ __MICO_PAGES__?: PublicPageItem[];
149
+ /** 菜单树 — 菜单导航的数据源 */
150
+ __MICO_MENUS__?: MenuItem[];
151
+ }
152
+ ```
153
+
154
+ ## 设计决策
155
+
156
+ | 决策点 | 选择 | 理由 |
157
+ | --- | --- | --- |
158
+ | 路由数据源 | `__MICO_PAGES__` 独立于菜单 | 页面和菜单是不同维度:页面定义"有什么路由",菜单定义"导航怎么组织";解耦后支持隐藏页面 |
159
+ | 权限方案 | 菜单交叉引用 + 页面级兜底 | 菜单中的页面复用现有 sideMenus 白名单逻辑,改动最小;隐藏页面用 adminOnly + accessControlEnabled 兜底 |
160
+ | 降级策略 | `getDynamicRoutes()` 自动降级 | `__MICO_PAGES__` 未注入时回退到旧行为,零配置向后兼容 |
161
+ | PublicPageItem 定义 | `Omit<PageConfig, ...>` 派生 | 保持与 PageConfig 的类型继承关系,避免字段重复定义和类型漂移 |
162
+ | accessControlEnabled 语义 | `false` = 公开页面(跳过认证+授权),`true` = 需要权限检查 | 一个字段统一控制 SSO 认证、权限校验、子应用加载等待,减少配置冗余 |
163
+ | 隐藏页面权限 | 先 adminOnly 再 accessControlEnabled | adminOnly 是硬拦截,accessControlEnabled + routeKey 提供用户级粒度控制 |
164
+
165
+ ## 已知限制与待改进
166
+
167
+ - `routeKey` 当前复用 `sideMenus` 做权限匹配,后续可能独立为专门的权限字段
168
+ - 隐藏页面的权限控制依赖 `routeKey` 存在于 `sideMenus` 中,需要后端在下发用户权限时包含隐藏页面的 `routeKey`
169
+
170
+ ## 注意事项
171
+
172
+ - `__MICO_PAGES__` 只包含用户在后台配置的微应用页面,404/403 等静态页面不在其中
173
+ - `__MICO_MENUS__` 中菜单项的 `page` 字段引用的是 `__MICO_PAGES__` 中的某个页面
174
+ - 调试时可在控制台搜索 `isForbidden (menu check)` 或 `isForbidden (hidden page` 查看权限判断日志
175
+
176
+ ## 相关文档
177
+
178
+ - [微前端模式](./feature-微前端模式.md) - 微应用加载机制
179
+ - [菜单权限控制](./feature-菜单权限控制.md) - sideMenus 白名单权限逻辑