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
@@ -0,0 +1,88 @@
1
+ # 修复 SSO 无限重定向循环
2
+
3
+ > 创建时间:2026-03-12
4
+
5
+ ## 功能概述
6
+
7
+ 修复 SSO 登录流程中 `redirect_count` 防护机制被过早清除,导致 `fetchUserInfo` 返回 401 时可能触发无限 SSO 重定向循环的问题。
8
+
9
+ ## 问题分析
10
+
11
+ ### 原始流程(存在缺陷)
12
+
13
+ ```
14
+ SSO 回跳(URL 含 redirect_count=1 & ticket)
15
+ → ensureSsoSession() ticket 换 token 成功
16
+ → 立即删除 redirect_count ← 问题根因
17
+ → fetchUserInfo() 返回 401
18
+ → refreshAuthToken() 失败
19
+ → handleAuthFailureRedirect() 检查 redirect_count = 0(已被删除)
20
+ → 允许重定向 → 跳转 SSO
21
+ → SSO 自动登录回跳 → 重复以上流程 → 无限循环
22
+ ```
23
+
24
+ ### 根因
25
+
26
+ `ensureSsoSession()` 在 ticket 换取 token 成功后**立即删除** URL 中的 `redirect_count` 参数,但后续的 `fetchUserInfo` 尚未执行。当 `fetchUserInfo` 返回 401 且 token 刷新失败时,`handleAuthFailureRedirect()` 因检测不到 `redirect_count` 而误判为首次重定向,触发循环。
27
+
28
+ ## 技术方案
29
+
30
+ ### 核心思路
31
+
32
+ 将 `redirect_count` 的清除时机从"SSO ticket 换 token 成功后"延后到"整个认证流程(SSO + fetchUserInfo)完全成功后"。
33
+
34
+ ### 修复后流程
35
+
36
+ ```
37
+ SSO 回跳(URL 含 redirect_count=1 & ticket)
38
+ → ensureSsoSession() ticket 换 token 成功
39
+ → 保留 redirect_count ← 修复点
40
+ → fetchUserInfo() 返回 401
41
+ → refreshAuthToken() 失败
42
+ → handleAuthFailureRedirect() 检查 redirect_count = 1
43
+ → 已达最大重定向次数 → 停止重定向 ✅
44
+ ```
45
+
46
+ ```
47
+ SSO 回跳(URL 含 redirect_count=1 & ticket)
48
+ → ensureSsoSession() ticket 换 token 成功
49
+ → 保留 redirect_count
50
+ → fetchUserInfo() 成功
51
+ → clearRedirectCount() 清除 redirect_count ✅
52
+ ```
53
+
54
+ ## 文件清单
55
+
56
+ ### 修改文件
57
+
58
+ | 文件路径 | 修改内容 |
59
+ | --- | --- |
60
+ | `src/common/request/sso.ts` | 移除 `ensureSsoSession` 中过早删除 `redirect_count` 的逻辑;新增 `clearRedirectCount()` 函数 |
61
+ | `src/app.tsx` | 导入 `clearRedirectCount`,在 `fetchUserInfo` 成功后调用 |
62
+
63
+ ## API / 组件接口
64
+
65
+ ### clearRedirectCount
66
+
67
+ ```typescript
68
+ export const clearRedirectCount = (): void
69
+ ```
70
+
71
+ 清除 URL 中的 `redirect_count` 参数。仅在整个认证流程完全成功后调用。
72
+
73
+ ## 设计决策
74
+
75
+ | 决策点 | 选择 | 理由 |
76
+ | --- | --- | --- |
77
+ | redirect_count 清除时机 | 延后到 fetchUserInfo 成功后 | 最小改动,不改变现有 URL 参数机制,仅调整调用时序 |
78
+ | clearRedirectCount 放置位置 | `sso.ts` | 与 `handleAuthFailureRedirect` 同属 SSO 模块,保持内聚 |
79
+
80
+ ## 注意事项
81
+
82
+ - `handleAuthFailureRedirect` 可能在 `request/index.ts`(refresh 失败时)和 `app.tsx`(fetchUserInfo 返回 null 时)被双重调用,但修复后两处检查到 `redirect_count >= 1` 均会停止重定向,不影响正确性
83
+ - `redirect_count` 最大值为 1,即只允许一次自动 SSO 重定向尝试
84
+
85
+ ## 相关文档
86
+
87
+ - [请求模块架构](./arch-请求模块.md)
88
+ - [菜单权限控制](./feature-菜单权限控制.md)
@@ -0,0 +1,324 @@
1
+ # 时区工具函数
2
+
3
+ > 创建时间:2025-01-27
4
+
5
+ ## 概述
6
+
7
+ 本模块提供时区管理工具函数,支持 UTC-12 到 UTC+14 的全时区范围。基于 `dayjs` 及其 `utc`、`timezone` 插件实现,时区偏移量存储在 `localStorage` 中,支持用户切换时区。
8
+
9
+ **文件位置**:`apps/layout/src/common/helpers.ts`
10
+
11
+ ## 核心概念
12
+
13
+ ### 时区表示法
14
+
15
+ 本模块使用两种时区表示法:
16
+
17
+ | 表示法 | 格式 | 示例 | 说明 |
18
+ |--------|------|------|------|
19
+ | **UTC Offset** | `UTC+N` / `UTC-N` | `UTC+8`、`UTC-5` | 用于用户界面显示 |
20
+ | **IANA 时区** | `地区/城市` | `Asia/Shanghai`、`America/New_York` | 用于 dayjs 时区计算 |
21
+
22
+ ### 存储机制
23
+
24
+ 时区信息通过 `localStorage` 持久化:
25
+
26
+ | 存储键 | 常量 | 说明 |
27
+ |--------|------|------|
28
+ | `mico-cs-timezone` | `STORAGE_KEYS.TIMEZONE` | 存储数字(如 `8`)或字符串(如 `UTC+8`) |
29
+ | `mico-cs-timezone-region` | `STORAGE_KEYS.TIMEZONE_REGION` | 存储 IANA 时区标识(如 `Asia/Shanghai`) |
30
+
31
+ ## 常量定义
32
+
33
+ ### TIMEZONE
34
+
35
+ UTC 偏移量键的枚举映射,覆盖 UTC-12 到 UTC+14:
36
+
37
+ ```typescript
38
+ export const TIMEZONE = {
39
+ 'UTC-12': 'UTC-12',
40
+ 'UTC-11': 'UTC-11',
41
+ // ... 省略中间值
42
+ 'UTC+12': 'UTC+12',
43
+ 'UTC+13': 'UTC+13',
44
+ 'UTC+14': 'UTC+14',
45
+ } as const;
46
+
47
+ export type TTimezone = keyof typeof TIMEZONE;
48
+ ```
49
+
50
+ ### TIMEZONE_HASH
51
+
52
+ UTC 偏移量到 IANA 时区标识的映射表:
53
+
54
+ ```typescript
55
+ export const TIMEZONE_HASH: Record<TTimezone, string> = {
56
+ [TIMEZONE['UTC-12']]: 'Etc/GMT+12', // 无居住区
57
+ [TIMEZONE['UTC-11']]: 'Pacific/Pago_Pago', // 帕果帕果,美属萨摩亚
58
+ [TIMEZONE['UTC-10']]: 'Pacific/Honolulu', // 檀香山,夏威夷
59
+ [TIMEZONE['UTC-9']]: 'America/Anchorage', // 安克雷奇,阿拉斯加
60
+ [TIMEZONE['UTC-8']]: 'America/Los_Angeles', // 洛杉矶
61
+ [TIMEZONE['UTC-7']]: 'America/Denver', // 丹佛
62
+ [TIMEZONE['UTC-6']]: 'America/Chicago', // 芝加哥
63
+ [TIMEZONE['UTC-5']]: 'America/New_York', // 纽约
64
+ [TIMEZONE['UTC-4']]: 'America/Puerto_Rico', // 波多黎各
65
+ [TIMEZONE['UTC-3']]: 'America/Sao_Paulo', // 圣保罗
66
+ [TIMEZONE['UTC-2']]: 'America/Noronha', // 费尔南多岛
67
+ [TIMEZONE['UTC-1']]: 'Atlantic/Azores', // 亚速尔群岛
68
+ [TIMEZONE['UTC+0']]: 'Europe/London', // 伦敦
69
+ [TIMEZONE['UTC+1']]: 'Europe/Paris', // 巴黎
70
+ [TIMEZONE['UTC+2']]: 'Europe/Kyiv', // 基辅
71
+ [TIMEZONE['UTC+3']]: 'Europe/Moscow', // 莫斯科
72
+ [TIMEZONE['UTC+4']]: 'Asia/Dubai', // 迪拜
73
+ [TIMEZONE['UTC+5']]: 'Asia/Tashkent', // 塔什干
74
+ [TIMEZONE['UTC+6']]: 'Asia/Dhaka', // 达卡
75
+ [TIMEZONE['UTC+7']]: 'Asia/Bangkok', // 曼谷
76
+ [TIMEZONE['UTC+8']]: 'Asia/Shanghai', // 上海(默认)
77
+ [TIMEZONE['UTC+9']]: 'Asia/Tokyo', // 东京
78
+ [TIMEZONE['UTC+10']]: 'Australia/Sydney', // 悉尼
79
+ [TIMEZONE['UTC+11']]: 'Pacific/Noumea', // 努美阿
80
+ [TIMEZONE['UTC+12']]: 'Pacific/Auckland', // 奥克兰
81
+ [TIMEZONE['UTC+13']]: 'Pacific/Tongatapu', // 汤加
82
+ [TIMEZONE['UTC+14']]: 'Pacific/Kiritimati', // 基里巴斯圣诞岛
83
+ };
84
+ ```
85
+
86
+ > **说明**:地理上有 24 个时区,但实际应用中存在 UTC+13 和 UTC+14(太平洋岛国),因此本模块覆盖 27 个时区。
87
+
88
+ ## API 参考
89
+
90
+ ### 时区转换函数
91
+
92
+ #### utcOffsetToTimezone
93
+
94
+ 将 UTC 偏移量数字转换为 `UTC+N` 格式字符串。
95
+
96
+ ```typescript
97
+ function utcOffsetToTimezone(utcOffset: number): string;
98
+ ```
99
+
100
+ | 参数 | 类型 | 说明 |
101
+ |------|------|------|
102
+ | `utcOffset` | `number` | UTC 偏移量,如 `8` 表示 UTC+8 |
103
+
104
+ **示例**:
105
+
106
+ ```typescript
107
+ utcOffsetToTimezone(8); // => 'UTC+8'
108
+ utcOffsetToTimezone(-5); // => 'UTC-5'
109
+ utcOffsetToTimezone(0); // => 'UTC+0'
110
+ ```
111
+
112
+ #### utcOffsetToTimeZoneStr
113
+
114
+ 将 UTC 偏移量转换为 IANA 时区标识。
115
+
116
+ ```typescript
117
+ function utcOffsetToTimeZoneStr(utcOffset: number): string;
118
+ ```
119
+
120
+ **回退逻辑**:
121
+
122
+ 1. 查找 `TIMEZONE_HASH` 映射
123
+ 2. 若无匹配,使用 `dayjs.tz.guess()` 推断浏览器时区
124
+ 3. 最终回退到 `Asia/Shanghai`
125
+
126
+ **示例**:
127
+
128
+ ```typescript
129
+ utcOffsetToTimeZoneStr(8); // => 'Asia/Shanghai'
130
+ utcOffsetToTimeZoneStr(-5); // => 'America/New_York'
131
+ ```
132
+
133
+ ### 时区读写函数
134
+
135
+ #### getTimezoneOffset
136
+
137
+ 从 `localStorage` 获取时区偏移量数字。
138
+
139
+ ```typescript
140
+ function getTimezoneOffset(): number;
141
+ ```
142
+
143
+ **返回值**:偏移量数字,默认 `8`(UTC+8)
144
+
145
+ #### getTimezone
146
+
147
+ 获取当前时区的 IANA 标识。
148
+
149
+ ```typescript
150
+ function getTimezone(): string;
151
+ ```
152
+
153
+ **返回值**:IANA 时区标识(如 `Asia/Shanghai`)
154
+
155
+ **回退逻辑**:
156
+
157
+ 1. 从 `localStorage` 读取存储值
158
+ 2. 若为数字字符串,转换为 `UTC+N` 格式后查找 IANA 映射
159
+ 3. 若已是 `UTC+N` 格式,直接查找 IANA 映射
160
+ 4. 若无匹配,使用 `dayjs.tz.guess()` 推断
161
+ 5. 最终回退到 `Asia/Shanghai`
162
+
163
+ #### getCurrentTimezone
164
+
165
+ 获取当前时区的 `UTC+N` 格式字符串。
166
+
167
+ ```typescript
168
+ function getCurrentTimezone(): string;
169
+ ```
170
+
171
+ **返回值**:UTC 偏移量字符串(如 `UTC+8`),默认 `UTC+8`
172
+
173
+ #### setTimezone
174
+
175
+ 设置时区到 `localStorage`。
176
+
177
+ ```typescript
178
+ function setTimezone(timezone: string): void;
179
+ ```
180
+
181
+ | 参数 | 类型 | 说明 |
182
+ |------|------|------|
183
+ | `timezone` | `string` | 时区字符串,如 `UTC+8` 或偏移量数字字符串如 `8` |
184
+
185
+ #### getTimezoneRegion
186
+
187
+ 获取时区地区(IANA 标识)。
188
+
189
+ ```typescript
190
+ function getTimezoneRegion(): string;
191
+ ```
192
+
193
+ **返回值**:时区地区字符串(如 `Asia/Shanghai`),未设置则返回空字符串
194
+
195
+ #### setTimezoneRegion
196
+
197
+ 设置时区地区到 `localStorage`。
198
+
199
+ ```typescript
200
+ function setTimezoneRegion(region: string): void;
201
+ ```
202
+
203
+ | 参数 | 类型 | 说明 |
204
+ |------|------|------|
205
+ | `region` | `string` | IANA 时区标识,如 `Asia/Shanghai` |
206
+
207
+ ## 使用示例
208
+
209
+ ### 初始化时区
210
+
211
+ ```typescript
212
+ import { setTimezone, setTimezoneRegion } from '@/common/helpers';
213
+
214
+ // 用户选择 UTC+9 (东京)
215
+ setTimezone('9');
216
+ setTimezoneRegion('Asia/Tokyo');
217
+ ```
218
+
219
+ ### 获取当前时区
220
+
221
+ ```typescript
222
+ import { getCurrentTimezone, getTimezoneRegion } from '@/common/helpers';
223
+
224
+ const timezone = getCurrentTimezone(); // => 'UTC+8'
225
+ const region = getTimezoneRegion(); // => 'Asia/Shanghai'
226
+ ```
227
+
228
+ ### 用户时区切换(AvatarDropdown)
229
+
230
+ **文件**:`apps/layout/src/components/RightContent/AvatarDropdown.tsx`
231
+
232
+ 用户头像下拉菜单中的时区选择器,允许用户切换时区并刷新页面:
233
+
234
+ ```typescript
235
+ import {
236
+ getCurrentTimezone,
237
+ getTimezoneRegion,
238
+ setTimezoneRegion as setTimezoneRegionToStorage,
239
+ setTimezone as setTimezoneToStorage,
240
+ utcOffsetToTimezone,
241
+ } from '@/common/helpers';
242
+
243
+ // 获取当前时区用于显示
244
+ const [currentTimezone, setCurrentTimezone] = useState(getCurrentTimezone());
245
+
246
+ // 用户切换时区时
247
+ const handleTimezoneChange = (key: string) => {
248
+ const lastSlashIndex = key.lastIndexOf('/');
249
+ const region = key.substring(0, lastSlashIndex); // 如 "Asia/Shanghai"
250
+ const utcOffsetStr = key.substring(lastSlashIndex + 1); // 如 "8"
251
+
252
+ // 保存到 localStorage
253
+ setTimezoneToStorage(utcOffsetStr);
254
+ setTimezoneRegionToStorage(region);
255
+
256
+ // 刷新页面使时区生效
257
+ window.location.reload();
258
+ };
259
+ ```
260
+
261
+ ## 数据流
262
+
263
+ ```
264
+ 用户选择时区
265
+
266
+ setTimezone('9') / setTimezoneRegion('Asia/Tokyo')
267
+
268
+ localStorage 存储
269
+
270
+ getCurrentTimezone() / getTimezone()
271
+
272
+ 返回 'UTC+9' / 'Asia/Tokyo'
273
+ ```
274
+
275
+ ## 注意事项
276
+
277
+ 1. **默认时区为 UTC+8**:如果 `localStorage` 中没有存储时区信息,所有函数默认使用 `Asia/Shanghai`(UTC+8)。
278
+
279
+ 2. **存储兼容性**:时区存储支持两种格式:
280
+ - 数字字符串(如 `"8"`):推荐格式
281
+ - UTC 格式(如 `"UTC+8"`):兼容格式
282
+
283
+ 读取时会自动兼容两种格式。
284
+
285
+ 3. **时区列表来源**:时区列表支持两种模式:
286
+
287
+ **模式一:静态列表(默认)**
288
+
289
+ 未配置 `timezoneListUrl` 时,使用内置的静态时区列表(覆盖 UTC-12 到 UTC+14 共 27 个时区)。
290
+
291
+ **模式二:API 获取**
292
+
293
+ 通过 `window.__MICO_CONFIG__.timezoneListUrl` 配置 API 地址:
294
+
295
+ ```javascript
296
+ // 在 index.html 或应用初始化时配置
297
+ window.__MICO_CONFIG__ = {
298
+ timezoneListUrl: '/custom/api/getTimezoneList',
299
+ };
300
+ ```
301
+
302
+ API 返回格式:
303
+ ```typescript
304
+ interface ITimezone {
305
+ utc_offset: number; // UTC 偏移量
306
+ region: string; // IANA 时区标识
307
+ }
308
+ ```
309
+
310
+
311
+ ## 文件清单
312
+
313
+ | 文件路径 | 说明 |
314
+ | --- | --- |
315
+ | `apps/layout/src/common/helpers.ts` | 时区工具函数实现 |
316
+ | `apps/layout/src/constants/index.ts` | 存储键常量定义(`TIMEZONE`、`STORAGE_KEYS` 等) |
317
+ | `apps/layout/src/services/config/index.ts` | 时区列表 API 服务 |
318
+ | `apps/layout/src/services/config/type.ts` | 时区类型定义 |
319
+ | `apps/layout/src/components/RightContent/AvatarDropdown.tsx` | 时区切换 UI 组件 |
320
+
321
+ ## 相关文档
322
+
323
+ - [AvatarDropdown 组件](../apps/layout/docs/feature-avatar-dropdown.md)
324
+
@@ -6,73 +6,93 @@
6
6
 
7
7
  export default {
8
8
  // 获取用户信息
9
- // 'GET /api/user/info': {
9
+ 'GET /api/user/info': {
10
+ "code": 200,
11
+ "data": {
12
+ "id": 381,
13
+ "last_login": "2026-03-06T02:37:10.050735Z",
14
+ "is_superuser": true,
15
+ "username": "本地测试mock用户",
16
+ "first_name": "",
17
+ "last_name": "",
18
+ "email": "本地测试mock用户@micous.com",
19
+ "is_staff": true,
20
+ "is_active": true,
21
+ "type": 1,
22
+ "phone": "",
23
+ "agency_id": 0,
24
+ "avatar": "https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png",
25
+ "user_name": "Easton",
26
+ "permission_tree": [
27
+ ],
28
+ "miss_permissions": [],
29
+ "side_menus": [
30
+ '首页',
31
+ '示例模块.示例页面',
32
+ '微应用示例.子应用页面',
33
+ '外部链接',
34
+ '权限管理',
35
+ ],
36
+ "region_permissions": []
37
+ },
38
+ "msg": "ok"
39
+ },
40
+
41
+ // 获取统计数据
42
+ // 'GET /api/dashboard/stats': {
10
43
  // success: true,
11
44
  // data: {
12
- // id: 1001,
13
- // name: '张三',
14
- // email: 'zhangsan@example.com',
15
- // avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=homepage',
16
- // role: 'admin',
17
- // department: '技术部',
45
+ // totalUsers: 12580,
46
+ // activeUsers: 3420,
47
+ // pendingTasks: 156,
48
+ // completedTasks: 8934,
49
+ // lastUpdated: new Date().toISOString(),
18
50
  // },
19
51
  // },
20
52
 
21
- // 获取统计数据
22
- 'GET /api/dashboard/stats': {
23
- success: true,
24
- data: {
25
- totalUsers: 12580,
26
- activeUsers: 3420,
27
- pendingTasks: 156,
28
- completedTasks: 8934,
29
- lastUpdated: new Date().toISOString(),
30
- },
31
- },
32
-
33
53
  // 获取列表数据
34
- 'GET /api/items': {
35
- success: true,
36
- data: {
37
- list: [
38
- {
39
- id: 1,
40
- title: '待审核内容 A',
41
- status: 'pending',
42
- createdAt: '2025-12-27 10:00:00',
43
- },
44
- {
45
- id: 2,
46
- title: '待审核内容 B',
47
- status: 'pending',
48
- createdAt: '2025-12-27 09:30:00',
49
- },
50
- {
51
- id: 3,
52
- title: '已通过内容 C',
53
- status: 'approved',
54
- createdAt: '2025-12-26 15:00:00',
55
- },
56
- {
57
- id: 4,
58
- title: '已拒绝内容 D',
59
- status: 'rejected',
60
- createdAt: '2025-12-26 14:00:00',
61
- },
62
- ],
63
- total: 4,
64
- page: 1,
65
- pageSize: 10,
66
- },
67
- },
54
+ // 'GET /api/items': {
55
+ // success: true,
56
+ // data: {
57
+ // list: [
58
+ // {
59
+ // id: 1,
60
+ // title: '待审核内容 A',
61
+ // status: 'pending',
62
+ // createdAt: '2025-12-27 10:00:00',
63
+ // },
64
+ // {
65
+ // id: 2,
66
+ // title: '待审核内容 B',
67
+ // status: 'pending',
68
+ // createdAt: '2025-12-27 09:30:00',
69
+ // },
70
+ // {
71
+ // id: 3,
72
+ // title: '已通过内容 C',
73
+ // status: 'approved',
74
+ // createdAt: '2025-12-26 15:00:00',
75
+ // },
76
+ // {
77
+ // id: 4,
78
+ // title: '已拒绝内容 D',
79
+ // status: 'rejected',
80
+ // createdAt: '2025-12-26 14:00:00',
81
+ // },
82
+ // ],
83
+ // total: 4,
84
+ // page: 1,
85
+ // pageSize: 10,
86
+ // },
87
+ // },
68
88
 
69
89
  // POST 请求示例
70
- 'POST /api/items/approve': (req: any, res: any) => {
71
- const { id } = req.body || {};
72
- res.json({
73
- success: true,
74
- message: `Item ${id} approved successfully`,
75
- data: { id, status: 'approved' },
76
- });
77
- },
90
+ // 'POST /api/items/approve': (req: any, res: any) => {
91
+ // const { id } = req.body || {};
92
+ // res.json({
93
+ // success: true,
94
+ // message: `Item ${id} approved successfully`,
95
+ // data: { id, status: 'approved' },
96
+ // });
97
+ // },
78
98
  };
@@ -2,10 +2,120 @@
2
2
  * Mock 菜单数据
3
3
  * 用于开发环境测试
4
4
  *
5
- * 注意:实际数据存放在 menus.json 中
6
- * 修改菜单数据请编辑 menus.json 文件,不会触发服务器重启
5
+ * 菜单结构说明:
6
+ * - id: 菜单唯一标识
7
+ * - name: 菜单名称
8
+ * - type: 'page' | 'group' (page: 页面, group: 分组)
9
+ * - path: 路由路径 (type=page 时与 PAGES 中的 route 一致, group 类型为 null)
10
+ * - icon: 图标名称
11
+ * - enabled: 是否启用
12
+ * - sortOrder: 排序权重
13
+ * - pageId: 关联的页面 ID (通过 pageId 与 __MICO_PAGES__ 关联)
14
+ * - children: 子菜单数组
7
15
  */
8
- import mockMenusData from './menus.json';
9
16
 
10
- export const mockMenus = mockMenusData;
17
+ import type { MenuItem } from '@/common/menu/types';
18
+
19
+ /** Mock 菜单项 */
20
+ type MockMenuItem = Omit<MenuItem, 'children'> & {
21
+ children: MockMenuItem[];
22
+ };
23
+
24
+ const mockMenus: MockMenuItem[] = [
25
+ {
26
+ id: 1,
27
+ name: '首页',
28
+ nameEn: 'Home',
29
+ nameKey: 'cs_web_menu_home',
30
+ type: 'page',
31
+ path: '/',
32
+ icon: 'Home',
33
+ enabled: true,
34
+ sortOrder: 0,
35
+ pageId: 1,
36
+ children: [],
37
+ },
38
+ {
39
+ id: 2,
40
+ name: '示例模块',
41
+ nameEn: 'Example Module',
42
+ nameKey: 'cs_web_menu_example_module',
43
+ type: 'group',
44
+ path: null,
45
+ icon: 'List',
46
+ enabled: true,
47
+ sortOrder: 1,
48
+ pageId: null,
49
+ children: [
50
+ {
51
+ id: 3,
52
+ name: '示例页面',
53
+ nameEn: 'Example Page',
54
+ nameKey: 'cs_web_menu_example_page',
55
+ type: 'page',
56
+ path: '/example/page',
57
+ icon: 'File',
58
+ enabled: true,
59
+ sortOrder: 1,
60
+ pageId: 45,
61
+ children: [],
62
+ },
63
+ ],
64
+ },
65
+ {
66
+ id: 5,
67
+ name: '微应用示例',
68
+ nameEn: 'Microapp Example',
69
+ nameKey: 'cs_web_menu_microapp_example',
70
+ type: 'group',
71
+ path: null,
72
+ icon: 'Apps',
73
+ enabled: true,
74
+ sortOrder: 2,
75
+ pageId: null,
76
+ children: [
77
+ {
78
+ id: 6,
79
+ name: '子应用页面',
80
+ nameEn: 'Subapp Page',
81
+ nameKey: 'cs_web_menu_subapp_page',
82
+ type: 'page',
83
+ path: '/subapp',
84
+ icon: 'Desktop',
85
+ enabled: true,
86
+ sortOrder: 1,
87
+ pageId: 55,
88
+ children: [],
89
+ },
90
+ ],
91
+ },
92
+ {
93
+ id: 7,
94
+ name: '外部链接',
95
+ nameEn: 'External Link',
96
+ nameKey: 'cs_web_menu_external_link',
97
+ type: 'link',
98
+ path: 'https://github.com',
99
+ icon: 'Link',
100
+ enabled: true,
101
+ sortOrder: 3,
102
+ pageId: null,
103
+ children: [],
104
+ },
105
+ {
106
+ id: 8,
107
+ name: '权限管理',
108
+ nameEn: 'Permission Management',
109
+ nameKey: 'cs_web_menu_permission_management',
110
+ type: 'page',
111
+ path: '/permission',
112
+ icon: 'Permission',
113
+ enabled: true,
114
+ sortOrder: 3,
115
+ pageId: null,
116
+ adminOnly: true,
117
+ children: [],
118
+ },
119
+ ];
120
+
11
121
  export default mockMenus;