generator-mico-cli 0.1.18

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 (155) hide show
  1. package/README.md +84 -0
  2. package/bin/mico.js +316 -0
  3. package/generators/micro-react/ignore-list.json +8 -0
  4. package/generators/micro-react/index.js +158 -0
  5. package/generators/micro-react/templates/.commitlintrc.js +6 -0
  6. package/generators/micro-react/templates/.cursor/rules/always-read-docs.mdc +129 -0
  7. package/generators/micro-react/templates/.cursor/rules/cicd-deploy.mdc +143 -0
  8. package/generators/micro-react/templates/.cursor/rules/coding-conventions.mdc +206 -0
  9. package/generators/micro-react/templates/.cursor/rules/commit-conventions.mdc +111 -0
  10. package/generators/micro-react/templates/.cursor/rules/development-guide.mdc +295 -0
  11. package/generators/micro-react/templates/.cursor/rules/layout-app.mdc +275 -0
  12. package/generators/micro-react/templates/.cursor/rules/micro-frontend.mdc +196 -0
  13. package/generators/micro-react/templates/.cursor/rules/project-overview.mdc +128 -0
  14. package/generators/micro-react/templates/.cursor/rules/request-auth.mdc +220 -0
  15. package/generators/micro-react/templates/.cursor/rules/theme-system.mdc +206 -0
  16. package/generators/micro-react/templates/.editorconfig +16 -0
  17. package/generators/micro-react/templates/.env +3 -0
  18. package/generators/micro-react/templates/.eslintrc.js +30 -0
  19. package/generators/micro-react/templates/.husky/commit-msg +2 -0
  20. package/generators/micro-react/templates/.husky/pre-commit +2 -0
  21. package/generators/micro-react/templates/.lintstagedrc +5 -0
  22. package/generators/micro-react/templates/.stylelintrc.js +25 -0
  23. package/generators/micro-react/templates/AGENTS.md +39 -0
  24. package/generators/micro-react/templates/CICD/start_dev.sh +30 -0
  25. package/generators/micro-react/templates/CICD/start_local.sh +30 -0
  26. package/generators/micro-react/templates/CICD/start_prod.sh +30 -0
  27. package/generators/micro-react/templates/CICD/start_test.sh +30 -0
  28. package/generators/micro-react/templates/CICD/wangsu_fresh_dev.sh +19 -0
  29. package/generators/micro-react/templates/CICD/wangsu_fresh_prod.sh +19 -0
  30. package/generators/micro-react/templates/CICD/wangsu_fresh_test.sh +19 -0
  31. package/generators/micro-react/templates/CLAUDE.md +106 -0
  32. package/generators/micro-react/templates/README.md +84 -0
  33. package/generators/micro-react/templates/_gitignore +57 -0
  34. package/generators/micro-react/templates/_npmrc +2 -0
  35. package/generators/micro-react/templates/apps/layout/.env +4 -0
  36. package/generators/micro-react/templates/apps/layout/.eslintrc.js +10 -0
  37. package/generators/micro-react/templates/apps/layout/.lintstagedrc +17 -0
  38. package/generators/micro-react/templates/apps/layout/.prettierignore +3 -0
  39. package/generators/micro-react/templates/apps/layout/.prettierrc +8 -0
  40. package/generators/micro-react/templates/apps/layout/.stylelintrc.js +20 -0
  41. package/generators/micro-react/templates/apps/layout/README.md +37 -0
  42. package/generators/micro-react/templates/apps/layout/config/config.dev.ts +54 -0
  43. package/generators/micro-react/templates/apps/layout/config/config.prod.ts +37 -0
  44. package/generators/micro-react/templates/apps/layout/config/config.testing.ts +27 -0
  45. package/generators/micro-react/templates/apps/layout/config/config.ts +132 -0
  46. package/generators/micro-react/templates/apps/layout/config/routes.ts +13 -0
  47. package/generators/micro-react/templates/apps/layout/mock/api.mock.ts +78 -0
  48. package/generators/micro-react/templates/apps/layout/mock/menus.json +100 -0
  49. package/generators/micro-react/templates/apps/layout/mock/menus.ts +11 -0
  50. package/generators/micro-react/templates/apps/layout/mock/user.mock.ts +20 -0
  51. package/generators/micro-react/templates/apps/layout/package.json +45 -0
  52. package/generators/micro-react/templates/apps/layout/public/font/ar-SA.js +54 -0
  53. package/generators/micro-react/templates/apps/layout/public/font/default.js +54 -0
  54. package/generators/micro-react/templates/apps/layout/src/app.tsx +123 -0
  55. package/generators/micro-react/templates/apps/layout/src/assets/.gitkeep +0 -0
  56. package/generators/micro-react/templates/apps/layout/src/common/auth/cs-auth-manager.ts +220 -0
  57. package/generators/micro-react/templates/apps/layout/src/common/auth/index.ts +41 -0
  58. package/generators/micro-react/templates/apps/layout/src/common/auth/tool.ts +3 -0
  59. package/generators/micro-react/templates/apps/layout/src/common/auth/type.ts +6 -0
  60. package/generators/micro-react/templates/apps/layout/src/common/constants.ts +38 -0
  61. package/generators/micro-react/templates/apps/layout/src/common/env.ts +73 -0
  62. package/generators/micro-react/templates/apps/layout/src/common/helpers.ts +69 -0
  63. package/generators/micro-react/templates/apps/layout/src/common/locale.ts +123 -0
  64. package/generators/micro-react/templates/apps/layout/src/common/logger.ts +45 -0
  65. package/generators/micro-react/templates/apps/layout/src/common/menu/index.ts +2 -0
  66. package/generators/micro-react/templates/apps/layout/src/common/menu/parser.ts +143 -0
  67. package/generators/micro-react/templates/apps/layout/src/common/menu/types.ts +92 -0
  68. package/generators/micro-react/templates/apps/layout/src/common/request/config.ts +73 -0
  69. package/generators/micro-react/templates/apps/layout/src/common/request/index.ts +188 -0
  70. package/generators/micro-react/templates/apps/layout/src/common/request/interceptors.ts +186 -0
  71. package/generators/micro-react/templates/apps/layout/src/common/request/sso.ts +132 -0
  72. package/generators/micro-react/templates/apps/layout/src/common/request/token-refresh.ts +136 -0
  73. package/generators/micro-react/templates/apps/layout/src/common/request/types.ts +44 -0
  74. package/generators/micro-react/templates/apps/layout/src/common/request/url-resolver.ts +75 -0
  75. package/generators/micro-react/templates/apps/layout/src/common/theme.ts +107 -0
  76. package/generators/micro-react/templates/apps/layout/src/common/types.ts +7 -0
  77. package/generators/micro-react/templates/apps/layout/src/common/upload/index.ts +2 -0
  78. package/generators/micro-react/templates/apps/layout/src/common/upload/oss.ts +401 -0
  79. package/generators/micro-react/templates/apps/layout/src/common/upload/types.ts +47 -0
  80. package/generators/micro-react/templates/apps/layout/src/common/uploadFiles.ts +35 -0
  81. package/generators/micro-react/templates/apps/layout/src/components/IconFont/index.tsx +25 -0
  82. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.less +44 -0
  83. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.tsx +121 -0
  84. package/generators/micro-react/templates/apps/layout/src/constants/index.ts +15 -0
  85. package/generators/micro-react/templates/apps/layout/src/global.less +13 -0
  86. package/generators/micro-react/templates/apps/layout/src/hooks/index.ts +3 -0
  87. package/generators/micro-react/templates/apps/layout/src/hooks/useAuth.ts +75 -0
  88. package/generators/micro-react/templates/apps/layout/src/hooks/useMenu.ts +35 -0
  89. package/generators/micro-react/templates/apps/layout/src/hooks/useMenuState.ts +112 -0
  90. package/generators/micro-react/templates/apps/layout/src/hooks/useTheme.ts +124 -0
  91. package/generators/micro-react/templates/apps/layout/src/layouts/components/header/index.less +109 -0
  92. package/generators/micro-react/templates/apps/layout/src/layouts/components/header/index.tsx +97 -0
  93. package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.less +164 -0
  94. package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.tsx +165 -0
  95. package/generators/micro-react/templates/apps/layout/src/layouts/index.less +71 -0
  96. package/generators/micro-react/templates/apps/layout/src/layouts/index.tsx +91 -0
  97. package/generators/micro-react/templates/apps/layout/src/locales/en-US.ts +20 -0
  98. package/generators/micro-react/templates/apps/layout/src/locales/zh-CN.ts +19 -0
  99. package/generators/micro-react/templates/apps/layout/src/models/global.ts +13 -0
  100. package/generators/micro-react/templates/apps/layout/src/pages/Home/index.less +3 -0
  101. package/generators/micro-react/templates/apps/layout/src/pages/Home/index.tsx +7 -0
  102. package/generators/micro-react/templates/apps/layout/src/requestErrorConfig.ts +171 -0
  103. package/generators/micro-react/templates/apps/layout/src/services/auth.ts +37 -0
  104. package/generators/micro-react/templates/apps/layout/src/services/oss.ts +40 -0
  105. package/generators/micro-react/templates/apps/layout/src/styles/arco-override.less +78 -0
  106. package/generators/micro-react/templates/apps/layout/src/styles/themes/dark/custom-var.less +244 -0
  107. package/generators/micro-react/templates/apps/layout/src/styles/themes/normal/custom-var.less +195 -0
  108. package/generators/micro-react/templates/apps/layout/src/styles/variables.less +5 -0
  109. package/generators/micro-react/templates/apps/layout/src/utils/format.ts +4 -0
  110. package/generators/micro-react/templates/apps/layout/tailwind.config.js +7 -0
  111. package/generators/micro-react/templates/apps/layout/tailwind.css +3 -0
  112. package/generators/micro-react/templates/apps/layout/tsconfig.json +3 -0
  113. package/generators/micro-react/templates/apps/layout/typings.d.ts +1 -0
  114. package/generators/micro-react/templates/deployDesc.md +60 -0
  115. package/generators/micro-react/templates/docs/commit-message.md +98 -0
  116. package/generators/micro-react/templates/package.json +35 -0
  117. package/generators/micro-react/templates/packages/shared-styles/README.md +125 -0
  118. package/generators/micro-react/templates/packages/shared-styles/arco-override.less +78 -0
  119. package/generators/micro-react/templates/packages/shared-styles/index.less +14 -0
  120. package/generators/micro-react/templates/packages/shared-styles/package.json +27 -0
  121. package/generators/micro-react/templates/packages/shared-styles/theme-inject.less +10 -0
  122. package/generators/micro-react/templates/packages/shared-styles/themes/dark/custom-var.less +246 -0
  123. package/generators/micro-react/templates/packages/shared-styles/themes/normal/custom-var.less +195 -0
  124. package/generators/micro-react/templates/packages/shared-styles/variables-only.less +301 -0
  125. package/generators/micro-react/templates/packages/shared-styles/variables.less +363 -0
  126. package/generators/micro-react/templates/pnpm-workspace.yaml +9 -0
  127. package/generators/micro-react/templates/scripts/collect-dist.js +68 -0
  128. package/generators/micro-react/templates/scripts/create-umi-app.sh +61 -0
  129. package/generators/micro-react/templates/scripts/dev.js +133 -0
  130. package/generators/micro-react/templates/turbo.json +68 -0
  131. package/generators/subapp-react/ignore-list.json +7 -0
  132. package/generators/subapp-react/index.js +189 -0
  133. package/generators/subapp-react/templates/homepage/.env +4 -0
  134. package/generators/subapp-react/templates/homepage/README.md +116 -0
  135. package/generators/subapp-react/templates/homepage/_gitignore +9 -0
  136. package/generators/subapp-react/templates/homepage/config/config.dev.ts +59 -0
  137. package/generators/subapp-react/templates/homepage/config/config.prod.ts +41 -0
  138. package/generators/subapp-react/templates/homepage/config/config.testing.ts +40 -0
  139. package/generators/subapp-react/templates/homepage/config/config.ts +102 -0
  140. package/generators/subapp-react/templates/homepage/config/routes.ts +7 -0
  141. package/generators/subapp-react/templates/homepage/mock/api.mock.ts +59 -0
  142. package/generators/subapp-react/templates/homepage/package.json +30 -0
  143. package/generators/subapp-react/templates/homepage/src/app.tsx +80 -0
  144. package/generators/subapp-react/templates/homepage/src/assets/yay.jpg +0 -0
  145. package/generators/subapp-react/templates/homepage/src/common/logger.ts +42 -0
  146. package/generators/subapp-react/templates/homepage/src/common/mainApp.ts +53 -0
  147. package/generators/subapp-react/templates/homepage/src/common/request.ts +49 -0
  148. package/generators/subapp-react/templates/homepage/src/global.less +26 -0
  149. package/generators/subapp-react/templates/homepage/src/pages/index.less +139 -0
  150. package/generators/subapp-react/templates/homepage/src/pages/index.tsx +342 -0
  151. package/generators/subapp-react/templates/homepage/src/styles/theme.less +6 -0
  152. package/generators/subapp-react/templates/homepage/tsconfig.json +3 -0
  153. package/generators/subapp-react/templates/homepage/typings.d.ts +17 -0
  154. package/lib/utils.js +165 -0
  155. package/package.json +31 -0
@@ -0,0 +1,123 @@
1
+ import { history } from '@umijs/max';
2
+ // 解决「React19 中无法使用 Message/Notification」的问题。 @see https://github.com/arco-design/arco-design/issues/2900#issuecomment-2796571653
3
+ import * as arco from '@arco-design/web-react';
4
+ import '@arco-design/web-react/dist/css/arco.css';
5
+ import '@arco-design/web-react/es/_util/react-19-adapter';
6
+ import React from 'react';
7
+ import ReactDOM from 'react-dom';
8
+
9
+ import { getAuthInfo } from './common/auth/tool';
10
+ import { ROUTES } from './common/constants';
11
+ import { extractRoutes, getWindowMenus } from './common/menu';
12
+ import { initTheme } from './common/theme';
13
+ import MicroAppLoader from './components/MicroAppLoader';
14
+ import './global.less';
15
+
16
+ // ==================== 微前端共享依赖 ====================
17
+ // 将公共库暴露到 window,供子应用复用,避免重复打包
18
+ // 子应用通过 externals 配置引用这些全局变量
19
+ if (typeof window !== 'undefined') {
20
+ const win = window as unknown as Record<string, unknown>;
21
+ win.React = React;
22
+ win.ReactDOM = ReactDOM;
23
+ win.arco = arco;
24
+ }
25
+
26
+ // 初始化主题(在页面加载时立即执行,避免闪烁)
27
+ initTheme();
28
+
29
+ /**
30
+ * @see https://umijs.org/docs/api/runtime-config#getinitialstate
31
+ */
32
+ export async function getInitialState(): Promise<{
33
+ currentUser?: {
34
+ name: string;
35
+ avatar: string;
36
+ userid: string;
37
+ status: string;
38
+ };
39
+ loading?: boolean;
40
+ fetchUserInfo?: () => Promise<void>;
41
+ }> {
42
+ const fetchUserInfo = async () => {};
43
+
44
+ // 如果不是登录页面,执行
45
+ const { location } = history;
46
+ if (!ROUTES.NO_AUTH_ROUTES.includes(location.pathname)) {
47
+ const authInfo = getAuthInfo();
48
+ const currentUser = {
49
+ name: authInfo.nickname || '未知用户',
50
+ avatar:
51
+ authInfo.avatar ||
52
+ 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
53
+ userid: authInfo.uid,
54
+ status: 'UNKNOWN',
55
+ };
56
+ return {
57
+ fetchUserInfo,
58
+ currentUser,
59
+ };
60
+ }
61
+ return {
62
+ fetchUserInfo,
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Umi 路由类型定义
68
+ */
69
+ interface UmiRoute {
70
+ path: string;
71
+ name?: string;
72
+ element?: React.ReactNode;
73
+ routes?: UmiRoute[];
74
+ meta?: Record<string, unknown>;
75
+ [key: string]: unknown;
76
+ }
77
+
78
+ /**
79
+ * @name 动态路由配置
80
+ * 从 window.__MICO_MENUS__ 获取菜单数据,生成动态路由
81
+ * @doc https://umijs.org/docs/max/router#%E5%8A%A8%E6%80%81%E8%B7%AF%E7%94%B1
82
+ */
83
+ export function patchClientRoutes({ routes }: { routes: UmiRoute[] }) {
84
+ const menus = getWindowMenus();
85
+ const dynamicRoutes = extractRoutes(menus);
86
+
87
+ // 找到根路由
88
+ const rootRoute = routes.find((r) => r.path === '/');
89
+ if (!rootRoute) return;
90
+
91
+ // 为每个动态路由添加配置
92
+ dynamicRoutes.forEach((route) => {
93
+ // 检查是否已存在
94
+ const exists = rootRoute.routes?.some((r) => r.path === route.path);
95
+ if (exists) return;
96
+
97
+ // 根据加载类型决定如何添加路由
98
+ // 对于 microapp 类型,使用 MicroAppLoader 组件
99
+ // 对于 internal 类型,不需要添加 element(由本地路由处理)
100
+ rootRoute.routes = rootRoute.routes || [];
101
+
102
+ // 为 microapp 类型创建渲染元素
103
+ const element =
104
+ route.loadType === 'microapp' && route.entry
105
+ ? React.createElement(MicroAppLoader, {
106
+ entry: route.entry,
107
+ name: route.path,
108
+ displayName: route.name,
109
+ })
110
+ : undefined;
111
+
112
+ rootRoute.routes.push({
113
+ path: route.path,
114
+ name: route.name,
115
+ element,
116
+ // 动态路由的元数据
117
+ meta: {
118
+ loadType: route.loadType,
119
+ entry: route.entry,
120
+ },
121
+ });
122
+ });
123
+ }
@@ -0,0 +1,220 @@
1
+ import { getFromStorage, removeStorage, setStorage } from '../helpers';
2
+ import {
3
+ AUTH_TOKEN_KEY,
4
+ AVATAR,
5
+ NICKNAME,
6
+ REFRESH_TOKEN_KEY,
7
+ UID,
8
+ WS_TOKEN_KEY,
9
+ } from './type';
10
+
11
+ /**
12
+ * 认证信息类型
13
+ */
14
+ export interface IAuthInfo {
15
+ avatar: string;
16
+ nickname: string;
17
+ uid: string;
18
+ token: string;
19
+ wsToken: string;
20
+ refreshToken: string;
21
+ }
22
+
23
+ /**
24
+ * Mibot Token 信息类型
25
+ */
26
+ interface MibotTokenInfo {
27
+ token?: string;
28
+ staff_uid?: string;
29
+ staff_prop?: {
30
+ avatar?: string;
31
+ nickname?: string;
32
+ };
33
+ }
34
+
35
+ /**
36
+ * 认证响应类型
37
+ */
38
+ interface AuthResponseData {
39
+ access?: string;
40
+ refresh?: string;
41
+ mibot_token_info?: MibotTokenInfo;
42
+ }
43
+
44
+ interface AuthResponse {
45
+ code?: number;
46
+ data?: AuthResponseData;
47
+ refresh?: string;
48
+ mibot_token_info?: MibotTokenInfo;
49
+ }
50
+
51
+ /**
52
+ * 获取存储的认证 token
53
+ * @returns 认证 token,如果不存在则返回 undefined
54
+ */
55
+ export const getStoredAuthToken = (): string | undefined => {
56
+ return getFromStorage(AUTH_TOKEN_KEY) ?? undefined;
57
+ };
58
+
59
+ /**
60
+ * 获取存储的刷新 token
61
+ * @returns 刷新 token,如果不存在则返回 undefined
62
+ */
63
+ export const getStoredRefreshToken = (): string | undefined => {
64
+ return getFromStorage(REFRESH_TOKEN_KEY) ?? undefined;
65
+ };
66
+
67
+ /**
68
+ * 获取存储的 WebSocket token
69
+ * @returns WebSocket token,如果不存在则返回 undefined
70
+ */
71
+ export const getStoredWsToken = (): string | undefined => {
72
+ return getFromStorage(WS_TOKEN_KEY) ?? undefined;
73
+ };
74
+
75
+ /**
76
+ * 设置存储的认证 token
77
+ * @param token - 认证 token,如果为 null 或 undefined 则删除
78
+ */
79
+ export const setStoredAuthToken = (token?: string | null): void => {
80
+ if (!token) {
81
+ removeStorage(AUTH_TOKEN_KEY);
82
+ return;
83
+ }
84
+ setStorage(AUTH_TOKEN_KEY, token);
85
+ };
86
+
87
+ /**
88
+ * 设置存储的刷新 token
89
+ * @param token - 刷新 token,如果为 null 或 undefined 则删除
90
+ */
91
+ export const setStoredRefreshToken = (token?: string | null): void => {
92
+ if (!token) {
93
+ removeStorage(REFRESH_TOKEN_KEY);
94
+ return;
95
+ }
96
+ setStorage(REFRESH_TOKEN_KEY, token);
97
+ };
98
+
99
+ /**
100
+ * 设置存储的 WebSocket token
101
+ * @param token - WebSocket token,如果为 null 或 undefined 则删除
102
+ */
103
+ export const setStoredWsToken = (token?: string | null): void => {
104
+ if (!token) {
105
+ removeStorage(WS_TOKEN_KEY);
106
+ return;
107
+ }
108
+ setStorage(WS_TOKEN_KEY, token);
109
+ };
110
+
111
+ /**
112
+ * 清除所有存储的认证相关 token 和信息
113
+ */
114
+ export const clearStoredTokens = (): void => {
115
+ removeStorage(AUTH_TOKEN_KEY);
116
+ removeStorage(REFRESH_TOKEN_KEY);
117
+ removeStorage(WS_TOKEN_KEY);
118
+ removeStorage(AVATAR);
119
+ removeStorage(NICKNAME);
120
+ removeStorage(UID);
121
+ };
122
+
123
+ /**
124
+ * 获取登录态用户信息,包括文件host、头像、昵称、uid
125
+ * @returns 认证信息对象
126
+ */
127
+ export const getAuthInfo = (): IAuthInfo => {
128
+ return {
129
+ avatar: getFromStorage(AVATAR) || '',
130
+ nickname: getFromStorage(NICKNAME) || '',
131
+ uid: getFromStorage(UID) || '',
132
+ token: getFromStorage(AUTH_TOKEN_KEY) || '',
133
+ wsToken: getFromStorage(WS_TOKEN_KEY) || '',
134
+ refreshToken: getFromStorage(REFRESH_TOKEN_KEY) || '',
135
+ };
136
+ };
137
+
138
+ /**
139
+ * 设置登录态用户信息
140
+ * @param info - 认证信息对象
141
+ */
142
+ export const setAuthInfo = (info: IAuthInfo): void => {
143
+ if (info.avatar) {
144
+ setStorage(AVATAR, info.avatar);
145
+ }
146
+ if (info.nickname) {
147
+ setStorage(NICKNAME, info.nickname);
148
+ }
149
+ if (info.uid) {
150
+ setStorage(UID, info.uid);
151
+ }
152
+ if (info.token) {
153
+ setStorage(AUTH_TOKEN_KEY, info.token);
154
+ }
155
+ if (info.wsToken) {
156
+ setStorage(WS_TOKEN_KEY, info.wsToken);
157
+ }
158
+ if (info.refreshToken) {
159
+ setStorage(REFRESH_TOKEN_KEY, info.refreshToken);
160
+ }
161
+ };
162
+
163
+ /**
164
+ * 持久化 Mibot Token 信息(提取的公共函数)
165
+ */
166
+ const persistMibotTokenInfo = (mibotTokenInfo: MibotTokenInfo): void => {
167
+ if (mibotTokenInfo.token) {
168
+ setStoredWsToken(mibotTokenInfo.token);
169
+ }
170
+ if (mibotTokenInfo.staff_uid) {
171
+ setStorage(UID, mibotTokenInfo.staff_uid);
172
+ }
173
+ if (mibotTokenInfo.staff_prop?.avatar) {
174
+ setStorage(AVATAR, mibotTokenInfo.staff_prop.avatar);
175
+ }
176
+ if (mibotTokenInfo.staff_prop?.nickname) {
177
+ setStorage(NICKNAME, mibotTokenInfo.staff_prop.nickname);
178
+ }
179
+ };
180
+
181
+ /**
182
+ * 从响应数据中持久化 token 信息
183
+ * 支持多种响应格式:
184
+ * - payload.code === 0 格式
185
+ * - payload.code === 200 格式(登录接口)
186
+ * @param payload - 响应数据
187
+ */
188
+ export const maybePersistTokens = (payload: AuthResponse | null): void => {
189
+ if (!payload) return;
190
+
191
+ // 处理 code === 0 格式的响应
192
+ if (payload.code === 0) {
193
+ const refresh = payload.refresh ?? payload.data?.refresh;
194
+ if (refresh) {
195
+ setStoredRefreshToken(refresh);
196
+ }
197
+
198
+ const mibotTokenInfo =
199
+ payload.mibot_token_info ?? payload.data?.mibot_token_info;
200
+ if (mibotTokenInfo) {
201
+ console.info('设置用户TOKEN信息', mibotTokenInfo);
202
+ persistMibotTokenInfo(mibotTokenInfo);
203
+ }
204
+ }
205
+
206
+ // 处理 code === 200 格式的响应(登录接口)
207
+ if (payload.code === 200 && payload.data) {
208
+ const { data } = payload;
209
+
210
+ if (data.access) {
211
+ setStoredAuthToken(data.access);
212
+ }
213
+ if (data.refresh) {
214
+ setStoredRefreshToken(data.refresh);
215
+ }
216
+ if (data.mibot_token_info) {
217
+ persistMibotTokenInfo(data.mibot_token_info);
218
+ }
219
+ }
220
+ };
@@ -0,0 +1,41 @@
1
+ import { authLogger } from '@/common/logger';
2
+ import { checkStaffAuth } from '@/services/auth';
3
+ import { handleAuthFailureRedirect } from '../request';
4
+ import { clearStoredTokens, getAuthInfo } from './cs-auth-manager';
5
+
6
+ export function logout(): void {
7
+ clearStoredTokens();
8
+ }
9
+
10
+ /**
11
+ * 检查登录信息是否完整
12
+ */
13
+ const isAuthInfoComplete = (
14
+ authInfo: ReturnType<typeof getAuthInfo>,
15
+ ): boolean => {
16
+ return Object.values(authInfo).every(Boolean);
17
+ };
18
+
19
+ export async function checkLogin(): Promise<void> {
20
+ const authInfo = getAuthInfo();
21
+
22
+ if (!isAuthInfoComplete(authInfo)) {
23
+ authLogger.warn('登录信息不完整,清除所有token,重新走登录流程');
24
+ clearStoredTokens();
25
+ }
26
+
27
+ await checkStaffAuth();
28
+ }
29
+
30
+ let hasTriggeredSsoReLogin = false;
31
+
32
+ export function triggerSsoReLogin(reason?: string) {
33
+ if (hasTriggeredSsoReLogin) {
34
+ return;
35
+ }
36
+ hasTriggeredSsoReLogin = true;
37
+ const message = reason ? `,原因:${reason}` : '';
38
+ console.warn(`[Auth] trigger SSO re-login${message}`);
39
+ logout();
40
+ handleAuthFailureRedirect();
41
+ }
@@ -0,0 +1,3 @@
1
+ // localStorage 相关的认证函数已迁移到 @/common/auth/cs-auth-manager
2
+ // 重新导出以保持向后兼容
3
+ export { getAuthInfo, setAuthInfo, type IAuthInfo } from './cs-auth-manager';
@@ -0,0 +1,6 @@
1
+ export const AUTH_TOKEN_KEY = 'auth';
2
+ export const WS_TOKEN_KEY = 'ws_auth';
3
+ export const REFRESH_TOKEN_KEY = 'refresh';
4
+ export const AVATAR = 'avatar';
5
+ export const NICKNAME = 'nickname';
6
+ export const UID = 'uid';
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 应用常量定义
3
+ */
4
+
5
+ /**
6
+ * 路由路径常量
7
+ */
8
+ export const ROUTES = {
9
+ /** 登录页 */
10
+ LOGIN: '/user/login',
11
+ /** 注册页 */
12
+ REGISTER: '/user/register',
13
+ /** 注册结果页 */
14
+ REGISTER_RESULT: '/user/register-result',
15
+ /** 无需认证的路由列表 */
16
+ NO_AUTH_ROUTES: ['/user/login', '/user/register', '/user/register-result'],
17
+ } as const;
18
+
19
+ /**
20
+ * 主题相关常量
21
+ */
22
+ export const THEME = {
23
+ /** localStorage 存储键 */
24
+ STORAGE_KEY: '<%= projectName %>-theme',
25
+ /** 默认主题 */
26
+ DEFAULT: 'light' as const,
27
+ /** 可选主题值 */
28
+ VALUES: ['light', 'dark'] as const,
29
+ } as const;
30
+
31
+ /**
32
+ * 存储键常量
33
+ */
34
+ export const STORAGE_KEYS = {
35
+ APP_INFO: 'appInfo',
36
+ IS_SUPERUSER: 'is_superuser',
37
+ GROUPS: 'groups',
38
+ } as const;
@@ -0,0 +1,73 @@
1
+ export enum EEnv {
2
+ Development = 'development',
3
+ Testing = 'testing',
4
+ Production = 'production',
5
+ }
6
+
7
+ export type CanonicalEnv = `${EEnv}`;
8
+
9
+ const ENV_NORMALIZED: Record<string, EEnv> = {
10
+ development: EEnv.Development,
11
+ testing: EEnv.Testing,
12
+ production: EEnv.Production,
13
+ };
14
+
15
+ const normalizeEnv = (value?: string): EEnv | undefined => {
16
+ if (!value) return undefined;
17
+ return ENV_NORMALIZED[value.toLowerCase()];
18
+ };
19
+
20
+ export const getEnvKeys = (env: EEnv): string[] => [env];
21
+
22
+ let cachedEnv: EEnv | null = null;
23
+
24
+ /**
25
+ * 获取当前环境标识
26
+ * @returns 'development' | 'testing' | 'production'
27
+ */
28
+ export const getEnv = (): EEnv => {
29
+ if (cachedEnv) {
30
+ return cachedEnv;
31
+ }
32
+
33
+ const envFromNode = normalizeEnv(process.env.NODE_ENV);
34
+ if (envFromNode) {
35
+ cachedEnv = envFromNode;
36
+ return cachedEnv;
37
+ }
38
+
39
+ if (typeof window !== 'undefined') {
40
+ const { hostname } = window.location;
41
+ const segments = hostname.split('.');
42
+ // 至少三级域名且第一级含 'test' 视为测试环境
43
+ if (segments.length >= 3 && segments[0].toLowerCase().includes('test')) {
44
+ cachedEnv = EEnv.Testing;
45
+ return cachedEnv;
46
+ }
47
+ }
48
+
49
+ cachedEnv = EEnv.Production;
50
+ return cachedEnv;
51
+ };
52
+
53
+ /**
54
+ * 判断是否为开发环境
55
+ */
56
+ export const isDevelopment = getEnv() === EEnv.Development;
57
+
58
+ /**
59
+ * 判断是否为测试环境
60
+ */
61
+ export const isTesting = getEnv() === EEnv.Testing;
62
+
63
+ /**
64
+ * 判断是否为生产环境
65
+ */
66
+ export const isProduction = getEnv() === EEnv.Production;
67
+
68
+ /**
69
+ * 判断是否为开发或测试环境(用于兼容旧代码)
70
+ */
71
+ export const isDevOrTest = (() => {
72
+ return isDevelopment || isTesting;
73
+ })();
@@ -0,0 +1,69 @@
1
+ import dayjs from 'dayjs';
2
+ import timezone from 'dayjs/plugin/timezone';
3
+ import utc from 'dayjs/plugin/utc';
4
+
5
+ // 扩展 dayjs
6
+ dayjs.extend(utc);
7
+ dayjs.extend(timezone);
8
+
9
+ /**
10
+ * 获取静态资源的完整 URL
11
+ * 用于 public 目录下的静态资源,自动拼接 publicPath 前缀
12
+ *
13
+ * @param path - 资源路径(相对于 public 目录,如 '/empty-states/404.png')
14
+ * @returns 完整的资源 URL
15
+ *
16
+ * @example
17
+ * withPublicPath('/empty-states/404.png')
18
+ * // 开发环境 => 'http://localhost:8000/empty-states/404.png'
19
+ * // 生产环境 => 'https://cdn.example.com/path/empty-states/404.png'
20
+ */
21
+ export const withPublicPath = (path: string): string => {
22
+ const publicPath =
23
+ process.env.NODE_ENV === 'development'
24
+ ? ''
25
+ : (window as any).publicPath || '';
26
+
27
+ // 移除 publicPath 末尾的斜杠
28
+ const base = publicPath.replace(/\/+$/, '');
29
+ // 确保 path 以 / 开头
30
+ const normalizedPath = path.startsWith('/') ? path : `/${path}`;
31
+
32
+ return `${base}${normalizedPath}`;
33
+ };
34
+
35
+ export const safeParseJSON = <T = any>(value: string | null): T | undefined => {
36
+ if (!value) return undefined;
37
+ try {
38
+ return JSON.parse(value) as T;
39
+ } catch {
40
+ return undefined;
41
+ }
42
+ };
43
+
44
+ export const getFromStorage = (key: string): string | null => {
45
+ if (typeof window === 'undefined') return null;
46
+ try {
47
+ return window.localStorage?.getItem(key) ?? null;
48
+ } catch {
49
+ return null;
50
+ }
51
+ };
52
+
53
+ export const setStorage = (key: string, value: string) => {
54
+ if (typeof window === 'undefined') return;
55
+ try {
56
+ window.localStorage?.setItem(key, value);
57
+ } catch {
58
+ // ignore
59
+ }
60
+ };
61
+
62
+ export const removeStorage = (key: string) => {
63
+ if (typeof window === 'undefined') return;
64
+ try {
65
+ window.localStorage?.removeItem(key);
66
+ } catch {
67
+ // ignore
68
+ }
69
+ };
@@ -0,0 +1,123 @@
1
+ import { withPublicPath } from './helpers';
2
+
3
+ /**
4
+ * 语言管理工具
5
+ * 支持三种配置渠道,优先级从高到低:
6
+ * 1. URL 参数 (lang)
7
+ * 2. localStorage 配置 (umi_locale)
8
+ * 3. 默认浏览器语言
9
+ */
10
+
11
+ const LOCALE_STORAGE_KEY = 'umi_locale';
12
+ const URL_PARAM_KEY = 'lang';
13
+
14
+ /**
15
+ * 支持的语言列表
16
+ */
17
+ export const SUPPORTED_LOCALES = ['zh-CN', 'en-US', 'ar-SA'] as const;
18
+ export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
19
+
20
+ /**
21
+ * 默认语言
22
+ */
23
+ const DEFAULT_LOCALE: SupportedLocale = 'en-US';
24
+
25
+ /**
26
+ * 从 URL 参数获取语言
27
+ */
28
+ function getLocaleFromUrl(): string | null {
29
+ if (typeof window === 'undefined') {
30
+ return null;
31
+ }
32
+ const urlParams = new URLSearchParams(window.location.search);
33
+ return urlParams.get(URL_PARAM_KEY);
34
+ }
35
+
36
+ /**
37
+ * 从 localStorage 获取语言
38
+ */
39
+ function getLocaleFromStorage(): string | null {
40
+ if (typeof window === 'undefined') {
41
+ return null;
42
+ }
43
+ try {
44
+ return localStorage.getItem(LOCALE_STORAGE_KEY);
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * 获取浏览器默认语言
52
+ */
53
+ function getBrowserLocale(): string {
54
+ if (typeof window === 'undefined') {
55
+ return DEFAULT_LOCALE;
56
+ }
57
+ const browserLang = navigator.language || (navigator as any).userLanguage;
58
+ // 检查是否支持该语言
59
+ if (SUPPORTED_LOCALES.includes(browserLang as SupportedLocale)) {
60
+ return browserLang;
61
+ }
62
+ // 检查语言前缀(如 zh-TW -> zh-CN)
63
+ const langPrefix = browserLang.split('-')[0];
64
+ const matchedLocale = SUPPORTED_LOCALES.find((locale) =>
65
+ locale.startsWith(langPrefix),
66
+ );
67
+ return matchedLocale || DEFAULT_LOCALE;
68
+ }
69
+
70
+ /**
71
+ * 验证语言是否支持
72
+ */
73
+ function isValidLocale(locale: string | null): locale is SupportedLocale {
74
+ if (!locale) {
75
+ return false;
76
+ }
77
+ return SUPPORTED_LOCALES.includes(locale as SupportedLocale);
78
+ }
79
+
80
+ /**
81
+ * 获取当前语言(按优先级)
82
+ * 优先级:URL 参数 > localStorage > 浏览器默认语言
83
+ */
84
+ export function getCurrentLocale(): SupportedLocale {
85
+ // 1. 优先从 URL 参数获取
86
+ const urlLocale = getLocaleFromUrl();
87
+ if (isValidLocale(urlLocale)) {
88
+ return urlLocale;
89
+ }
90
+
91
+ // 2. 从 localStorage 获取
92
+ const storageLocale = getLocaleFromStorage();
93
+ if (isValidLocale(storageLocale)) {
94
+ return storageLocale;
95
+ }
96
+
97
+ // 3. 使用浏览器默认语言
98
+ return getBrowserLocale() as SupportedLocale;
99
+ }
100
+
101
+ /**
102
+ * 设置语言到 localStorage
103
+ */
104
+ export function setLocaleToStorage(locale: SupportedLocale): void {
105
+ if (typeof window === 'undefined') {
106
+ return;
107
+ }
108
+ try {
109
+ localStorage.setItem(LOCALE_STORAGE_KEY, locale);
110
+ } catch (error) {
111
+ console.error('Failed to save locale to localStorage:', error);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * 根据语言获取图标库路径
117
+ */
118
+ export function getIconFontPath(locale: SupportedLocale): string {
119
+ if (locale === 'ar-SA') {
120
+ return withPublicPath('/font/ar-SA.js');
121
+ }
122
+ return withPublicPath('/font/default.js');
123
+ }