generator-mico-cli 0.2.4 → 0.2.5
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.
- package/generators/micro-react/templates/apps/layout/src/app.tsx +0 -69
- package/generators/micro-react/templates/apps/layout/src/common/micro-prefetch.ts +108 -0
- package/generators/micro-react/templates/apps/layout/src/common/request/index.ts +2 -2
- package/generators/micro-react/templates/apps/layout/src/common/route-guard.ts +31 -2
- package/generators/micro-react/templates/apps/layout/src/common/theme.ts +1 -1
- package/generators/micro-react/templates/apps/layout/src/common/upload/oss.ts +1 -1
- package/generators/micro-react/templates/apps/layout/src/common/upload/types.ts +1 -1
- package/generators/micro-react/templates/apps/layout/src/common/uploadFiles.ts +1 -1
- package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/micro-app-manager.ts +28 -10
- package/generators/micro-react/templates/apps/layout/src/constants/index.ts +63 -4
- package/generators/subapp-react/templates/homepage/src/app.tsx +4 -24
- package/generators/subapp-react/templates/homepage/src/global.less +4 -4
- package/package.json +1 -1
|
@@ -13,7 +13,6 @@ import { extractRoutes, getWindowMenus } from './common/menu';
|
|
|
13
13
|
import { ensureSsoSession } from './common/request/sso';
|
|
14
14
|
import {
|
|
15
15
|
clearMicroAppProps,
|
|
16
|
-
getAppNameFromEntry,
|
|
17
16
|
type IMicroAppProps,
|
|
18
17
|
setMicroAppProps,
|
|
19
18
|
} from './common/micro';
|
|
@@ -76,74 +75,6 @@ if (typeof window !== 'undefined') {
|
|
|
76
75
|
});
|
|
77
76
|
}
|
|
78
77
|
|
|
79
|
-
// ==================== 微应用预加载 ====================
|
|
80
|
-
// 预加载所有微应用资源,避免快速切换时的竞态条件
|
|
81
|
-
// 当资源已预加载时,loadMicroApp 的异步加载会快速完成,减少容器不存在的错误
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* 是否启用微应用预加载
|
|
85
|
-
* - 可通过 URL 参数 ?prefetch=false 禁用(方便调试加载时序问题)
|
|
86
|
-
* - 可通过 localStorage.setItem('DISABLE_MICRO_APP_PREFETCH', 'true') 禁用
|
|
87
|
-
* - 默认启用
|
|
88
|
-
*/
|
|
89
|
-
const isPrefetchEnabled = (): boolean => {
|
|
90
|
-
if (typeof window === 'undefined') return false;
|
|
91
|
-
|
|
92
|
-
// URL 参数优先级最高
|
|
93
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
94
|
-
const prefetchParam = urlParams.get('prefetch');
|
|
95
|
-
if (prefetchParam === 'false') {
|
|
96
|
-
console.log('[App] Prefetch disabled via URL parameter');
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// localStorage 开关
|
|
101
|
-
if (localStorage.getItem('DISABLE_MICRO_APP_PREFETCH') === 'true') {
|
|
102
|
-
console.log('[App] Prefetch disabled via localStorage');
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return true;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const prefetchMicroApps = () => {
|
|
110
|
-
if (!isPrefetchEnabled()) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
const menus = getWindowMenus();
|
|
116
|
-
const routes = extractRoutes(menus);
|
|
117
|
-
|
|
118
|
-
// 筛选出所有微应用路由
|
|
119
|
-
// 使用 getAppNameFromEntry 生成 name,与 loadMicroApp 保持一致,确保预加载缓存命中
|
|
120
|
-
const microApps = routes
|
|
121
|
-
.filter((route) => route.loadType === 'microapp' && route.entry)
|
|
122
|
-
.map((route) => ({
|
|
123
|
-
name: getAppNameFromEntry(route.entry!),
|
|
124
|
-
entry: route.entry!,
|
|
125
|
-
}));
|
|
126
|
-
|
|
127
|
-
if (microApps.length > 0) {
|
|
128
|
-
console.log('[App] Prefetching micro apps:', microApps);
|
|
129
|
-
prefetchApps(microApps);
|
|
130
|
-
}
|
|
131
|
-
} catch (error) {
|
|
132
|
-
console.warn('[App] Failed to prefetch micro apps:', error);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// 在页面加载后预加载微应用
|
|
137
|
-
if (typeof window !== 'undefined') {
|
|
138
|
-
// 使用 requestIdleCallback 在浏览器空闲时预加载,避免影响首屏渲染
|
|
139
|
-
if ('requestIdleCallback' in window) {
|
|
140
|
-
window.requestIdleCallback(() => prefetchMicroApps(), { timeout: 3000 });
|
|
141
|
-
} else {
|
|
142
|
-
// 降级方案:延迟 1 秒后预加载
|
|
143
|
-
setTimeout(prefetchMicroApps, 1000);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
78
|
// ==================== 微前端共享依赖 ====================
|
|
148
79
|
// 将公共库暴露到 window,供子应用复用,避免重复打包
|
|
149
80
|
// 子应用通过 externals 配置引用这些全局变量
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 微应用预加载管理
|
|
3
|
+
*
|
|
4
|
+
* 策略:当前子应用挂载成功后,再逐个预加载其他子应用
|
|
5
|
+
* 避免与当前加载的子应用竞争网络带宽
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { prefetchApps } from 'qiankun';
|
|
9
|
+
import { extractRoutes, getWindowMenus } from './menu';
|
|
10
|
+
import { getAppNameFromEntry } from './micro';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 是否启用微应用预加载
|
|
14
|
+
* - 可通过 URL 参数 ?prefetch=false 禁用(方便调试加载时序问题)
|
|
15
|
+
* - 可通过 localStorage.setItem('DISABLE_MICRO_APP_PREFETCH', 'true') 禁用
|
|
16
|
+
* - 默认启用
|
|
17
|
+
*/
|
|
18
|
+
export const isPrefetchEnabled = (): boolean => {
|
|
19
|
+
if (typeof window === 'undefined') return false;
|
|
20
|
+
|
|
21
|
+
// URL 参数优先级最高
|
|
22
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
23
|
+
const prefetchParam = urlParams.get('prefetch');
|
|
24
|
+
if (prefetchParam === 'false') {
|
|
25
|
+
console.log('[Prefetch] Disabled via URL parameter');
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// localStorage 开关
|
|
30
|
+
if (localStorage.getItem('DISABLE_MICRO_APP_PREFETCH') === 'true') {
|
|
31
|
+
console.log('[Prefetch] Disabled via localStorage');
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return true;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** 已预加载的应用 entry 集合 */
|
|
39
|
+
const prefetchedApps = new Set<string>();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 获取所有需要预加载的微应用(排除已预加载的)
|
|
43
|
+
*/
|
|
44
|
+
export const getMicroAppsForPrefetch = (
|
|
45
|
+
excludeEntry?: string,
|
|
46
|
+
): Array<{ name: string; entry: string }> => {
|
|
47
|
+
try {
|
|
48
|
+
const menus = getWindowMenus();
|
|
49
|
+
const routes = extractRoutes(menus);
|
|
50
|
+
|
|
51
|
+
return routes
|
|
52
|
+
.filter(
|
|
53
|
+
(route) =>
|
|
54
|
+
route.loadType === 'microapp' &&
|
|
55
|
+
route.entry &&
|
|
56
|
+
route.entry !== excludeEntry &&
|
|
57
|
+
!prefetchedApps.has(route.entry),
|
|
58
|
+
)
|
|
59
|
+
.map((route) => ({
|
|
60
|
+
name: getAppNameFromEntry(route.entry!),
|
|
61
|
+
entry: route.entry!,
|
|
62
|
+
}));
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.warn('[Prefetch] Failed to get micro apps:', error);
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 预加载微应用(低优先级,在浏览器空闲时并发预加载)
|
|
71
|
+
* @param currentEntry 当前正在加载的应用 entry,会被排除
|
|
72
|
+
*/
|
|
73
|
+
export const prefetchMicroAppsLowPriority = (currentEntry?: string): void => {
|
|
74
|
+
if (!isPrefetchEnabled()) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const apps = getMicroAppsForPrefetch(currentEntry);
|
|
79
|
+
if (apps.length === 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 标记为已预加载,避免重复
|
|
84
|
+
apps.forEach((app) => prefetchedApps.add(app.entry));
|
|
85
|
+
|
|
86
|
+
console.log(
|
|
87
|
+
'[Prefetch] Will prefetch micro apps:',
|
|
88
|
+
apps.map((a) => a.name),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// 使用 requestIdleCallback 确保在浏览器空闲时执行,不阻塞当前渲染
|
|
92
|
+
const doPrefetch = () => {
|
|
93
|
+
prefetchApps(apps); // qiankun 会并发预加载所有应用
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if ('requestIdleCallback' in window) {
|
|
97
|
+
window.requestIdleCallback(doPrefetch, { timeout: 5000 });
|
|
98
|
+
} else {
|
|
99
|
+
setTimeout(doPrefetch, 100);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 标记应用已加载(避免重复预加载)
|
|
105
|
+
*/
|
|
106
|
+
export const markAppAsPrefetched = (entry: string): void => {
|
|
107
|
+
prefetchedApps.add(entry);
|
|
108
|
+
};
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { request as rawRequest } from '@umijs/max';
|
|
18
18
|
import { setStoredAuthToken } from '../auth/auth-manager';
|
|
19
|
-
import {
|
|
19
|
+
import { NO_AUTH_ROUTE_LIST } from '@/constants';
|
|
20
20
|
|
|
21
21
|
// 配置相关
|
|
22
22
|
import {
|
|
@@ -64,7 +64,7 @@ initDefaultInterceptors(isFetchingToken, addToPendingQueue);
|
|
|
64
64
|
* 判断当前路由是否跳过认证
|
|
65
65
|
*/
|
|
66
66
|
const shouldSkipAuth = (): boolean => {
|
|
67
|
-
return
|
|
67
|
+
return NO_AUTH_ROUTE_LIST.includes(location.pathname);
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
/**
|
|
@@ -57,6 +57,9 @@ let isInUserInteraction = false;
|
|
|
57
57
|
/** 交互结束时间(用于 grace period) */
|
|
58
58
|
let interactionEndTime = 0;
|
|
59
59
|
|
|
60
|
+
/** 当前用户交互开始时间(用于判断意图是否在当前交互期间设置的) */
|
|
61
|
+
let currentInteractionStartTime = 0;
|
|
62
|
+
|
|
60
63
|
/** 调试模式 */
|
|
61
64
|
const DEBUG = true;
|
|
62
65
|
|
|
@@ -180,8 +183,33 @@ export function getIntentDebugInfo(): object {
|
|
|
180
183
|
* 判断是否应该拦截此次导航
|
|
181
184
|
*/
|
|
182
185
|
function shouldIntercept(targetPath: string): boolean {
|
|
183
|
-
|
|
186
|
+
const targetBase = getBasePath(targetPath);
|
|
187
|
+
|
|
188
|
+
// 1. 在用户交互上下文中
|
|
184
189
|
if (isInInteractionContext()) {
|
|
190
|
+
// 检查是否有有效意图,且意图是在当前用户交互期间由 setUserIntent 设置的
|
|
191
|
+
// 只有这种情况才需要检查匹配,以防止微应用在 mount 过程中推送错误的路由
|
|
192
|
+
//
|
|
193
|
+
// 关键区分:
|
|
194
|
+
// - 意图在当前交互之前设置(如页面加载时):允许用户的新导航覆盖旧意图
|
|
195
|
+
// - 意图在当前交互期间由 setUserIntent 设置:正在进行微应用切换,需要保护
|
|
196
|
+
if (isIntentValid() && !currentIntent!.fromInteraction) {
|
|
197
|
+
// 检查意图是否在当前交互期间设置的
|
|
198
|
+
const isIntentFromCurrentInteraction = currentIntent!.timestamp >= currentInteractionStartTime;
|
|
199
|
+
if (isIntentFromCurrentInteraction) {
|
|
200
|
+
const intentBase = getBasePath(currentIntent!.path);
|
|
201
|
+
if (targetBase !== intentBase) {
|
|
202
|
+
warn('❌ 拦截不匹配意图的导航(交互上下文中):', {
|
|
203
|
+
targetPath,
|
|
204
|
+
targetBase,
|
|
205
|
+
intentPath: currentIntent!.path,
|
|
206
|
+
intentBase,
|
|
207
|
+
});
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// 目标匹配意图、无有效意图、或意图是旧的,允许导航并更新意图
|
|
185
213
|
log('用户交互触发导航:', targetPath);
|
|
186
214
|
currentIntent = {
|
|
187
215
|
path: targetPath,
|
|
@@ -198,7 +226,6 @@ function shouldIntercept(targetPath: string): boolean {
|
|
|
198
226
|
}
|
|
199
227
|
|
|
200
228
|
// 3. 有有效意图,检查目标路径是否匹配
|
|
201
|
-
const targetBase = getBasePath(targetPath);
|
|
202
229
|
const intentBase = getBasePath(currentIntent!.path);
|
|
203
230
|
|
|
204
231
|
if (targetBase === intentBase) {
|
|
@@ -273,6 +300,8 @@ if (typeof window !== 'undefined') {
|
|
|
273
300
|
|
|
274
301
|
log('用户交互开始:', event.type);
|
|
275
302
|
isInUserInteraction = true;
|
|
303
|
+
// 记录交互开始时间,用于判断意图是否在当前交互期间设置的
|
|
304
|
+
currentInteractionStartTime = Date.now();
|
|
276
305
|
|
|
277
306
|
// 在当前事件循环结束后重置标记
|
|
278
307
|
// 使用 setTimeout 0 确保同步代码执行完毕
|
|
@@ -3,7 +3,7 @@ import type { UploadProps } from '@arco-design/web-react';
|
|
|
3
3
|
import { Message } from '@arco-design/web-react';
|
|
4
4
|
import type { UploadItem } from '@arco-design/web-react/es/Upload';
|
|
5
5
|
import SparkMD5 from 'spark-md5';
|
|
6
|
-
import type { TDirCategory } from '
|
|
6
|
+
import type { TDirCategory } from '@/constants';
|
|
7
7
|
import type {
|
|
8
8
|
OssUploadError,
|
|
9
9
|
UploadLifecycleEvent,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ! 这个函数不能放到 ./helpers.ts 中,因为 ./request/index.ts 从 ./helpers.ts 导入了 getFromStorage, safeParseJSON,如果放到 ./helpers.ts 中,会导致循环引入
|
|
2
2
|
// ! 开发态下,发生循环引入时, mako 不会有编译时报错,但运行时会报错
|
|
3
3
|
|
|
4
|
-
import type { TDirCategory } from '
|
|
4
|
+
import type { TDirCategory } from '@/constants';
|
|
5
5
|
import { uploadToOss } from './upload/oss';
|
|
6
6
|
|
|
7
7
|
/** 上传文件到 OSS 并获取 URL */
|
|
@@ -14,11 +14,14 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import type { MicroApp } from 'qiankun';
|
|
17
|
-
import { loadMicroApp
|
|
17
|
+
import { loadMicroApp } from 'qiankun';
|
|
18
18
|
import { microAppLogger } from '@/common/logger';
|
|
19
19
|
// 导入路由守卫(会自动初始化)
|
|
20
20
|
import { refreshUserIntent, setUserIntent } from '@/common/route-guard';
|
|
21
21
|
|
|
22
|
+
// 导入低优先级预加载函数
|
|
23
|
+
import { markAppAsPrefetched, prefetchMicroAppsLowPriority } from '@/common/micro-prefetch';
|
|
24
|
+
|
|
22
25
|
// ============================================================================
|
|
23
26
|
// 类型定义
|
|
24
27
|
// ============================================================================
|
|
@@ -171,6 +174,8 @@ class MicroAppManager {
|
|
|
171
174
|
if (cached.container.parentElement !== config.target) {
|
|
172
175
|
activateContainer(cached.container, config.target);
|
|
173
176
|
}
|
|
177
|
+
// 通知组件当前状态(重要:组件可能在 cleanup 后重新挂载,需要同步状态)
|
|
178
|
+
this.updateState({ loading: false, error: null, mounted: true });
|
|
174
179
|
}
|
|
175
180
|
return;
|
|
176
181
|
}
|
|
@@ -273,14 +278,6 @@ class MicroAppManager {
|
|
|
273
278
|
this.updateState({ loading: true, error: null, mounted: false });
|
|
274
279
|
|
|
275
280
|
try {
|
|
276
|
-
// 预加载资源
|
|
277
|
-
if (!this.appCache.has(request.name)) {
|
|
278
|
-
try {
|
|
279
|
-
prefetchApps([{ name: request.name, entry: request.entry }]);
|
|
280
|
-
} catch (error) {
|
|
281
|
-
microAppLogger.warn('Prefetch failed:', error);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
281
|
|
|
285
282
|
if (this.shouldAbort(request.name, mySeq)) {
|
|
286
283
|
console.log('🔍[路由调试] ⚠️ Aborted before load', { name: request.name, mySeq, operationSeq: this.operationSeq });
|
|
@@ -317,13 +314,21 @@ class MicroAppManager {
|
|
|
317
314
|
console.log('🔍[路由调试] 等待缓存实例 mountPromise...', { name: request.name });
|
|
318
315
|
await withTimeout(appInstance.microApp.mountPromise, MOUNT_TIMEOUT, '子应用挂载超时');
|
|
319
316
|
console.log('🔍[路由调试] 缓存实例 mountPromise 完成', { name: request.name, status: appInstance.microApp.getStatus() });
|
|
317
|
+
// 重要:mount 完成后立即更新 props,确保子应用使用最新的 routePath
|
|
318
|
+
// qiankun 的 mount() 使用的是创建实例时的原始 props,可能包含过期的 routePath
|
|
319
|
+
appInstance.microApp.update?.(request.props);
|
|
320
|
+
console.log('🔍[路由调试] 缓存实例 props 已更新(BOOTSTRAPPING 后)', { name: request.name });
|
|
320
321
|
} else if (status === 'NOT_MOUNTED') {
|
|
321
322
|
// 实例之前被 unmount 过,需要重新 mount
|
|
322
323
|
console.log('🔍[路由调试] 开始 mount 缓存实例', { name: request.name });
|
|
323
324
|
await withTimeout(appInstance.microApp.mount(), MOUNT_TIMEOUT, '子应用挂载超时');
|
|
324
325
|
console.log('🔍[路由调试] 缓存实例 mount 完成', { name: request.name, status: appInstance.microApp.getStatus() });
|
|
326
|
+
// 重要:mount 完成后立即更新 props,确保子应用使用最新的 routePath
|
|
327
|
+
// qiankun 的 mount() 使用的是创建实例时的原始 props,可能包含过期的 routePath
|
|
328
|
+
appInstance.microApp.update?.(request.props);
|
|
329
|
+
console.log('🔍[路由调试] 缓存实例 props 已更新(NOT_MOUNTED 后)', { name: request.name });
|
|
325
330
|
}
|
|
326
|
-
// 如果 status === 'MOUNTED'
|
|
331
|
+
// 如果 status === 'MOUNTED',则无需操作(已在 switchTo 入口处处理)
|
|
327
332
|
|
|
328
333
|
// 刷新意图,保护 mount 成功后的短暂窗口期
|
|
329
334
|
refreshUserIntent();
|
|
@@ -344,6 +349,10 @@ class MicroAppManager {
|
|
|
344
349
|
console.log('🔍[路由调试] ✅ 缓存实例挂载成功', { name: request.name, qiankunName: appInstance.qiankunName, status: appInstance.microApp.getStatus() });
|
|
345
350
|
this.updateState({ loading: false, error: null, mounted: true });
|
|
346
351
|
|
|
352
|
+
// 挂载成功后,标记当前应用已加载,并触发其他应用的低优先级预加载
|
|
353
|
+
markAppAsPrefetched(request.entry);
|
|
354
|
+
prefetchMicroAppsLowPriority(request.entry);
|
|
355
|
+
|
|
347
356
|
} else {
|
|
348
357
|
// 创建新实例
|
|
349
358
|
// 使用唯一的 qiankun 实例名称,避免 qiankun 内部状态冲突
|
|
@@ -400,6 +409,11 @@ class MicroAppManager {
|
|
|
400
409
|
await withTimeout(microApp.mountPromise, MOUNT_TIMEOUT, '子应用挂载超时');
|
|
401
410
|
console.log('🔍[路由调试] mountPromise 完成', { name: request.name, qiankunName, status: microApp.getStatus() });
|
|
402
411
|
|
|
412
|
+
// 重要:mount 完成后调用 update,触发子应用的路由同步
|
|
413
|
+
// 由于子应用 mount 生命周期不再调用 syncRoute,需要通过 update 来同步路由
|
|
414
|
+
microApp.update?.(request.props);
|
|
415
|
+
console.log('🔍[路由调试] 新实例 props 已更新', { name: request.name });
|
|
416
|
+
|
|
403
417
|
// 刷新意图,保护 mount 成功后的短暂窗口期
|
|
404
418
|
refreshUserIntent();
|
|
405
419
|
|
|
@@ -421,6 +435,10 @@ class MicroAppManager {
|
|
|
421
435
|
// 这样可以防止被 abort 的子应用在后台执行时修改路由
|
|
422
436
|
console.log('🔍[路由调试] ✅ 新实例挂载成功', { name: request.name, qiankunName, status: microApp.getStatus() });
|
|
423
437
|
this.updateState({ loading: false, error: null, mounted: true });
|
|
438
|
+
|
|
439
|
+
// 挂载成功后,标记当前应用已加载,并触发其他应用的低优先级预加载
|
|
440
|
+
markAppAsPrefetched(request.entry);
|
|
441
|
+
prefetchMicroAppsLowPriority(request.entry);
|
|
424
442
|
}
|
|
425
443
|
|
|
426
444
|
if (this.pendingRequest) {
|
|
@@ -1,15 +1,74 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 应用常量定义
|
|
3
|
+
*/
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
6
|
+
* 路由路径常量
|
|
5
7
|
*/
|
|
6
|
-
export const
|
|
8
|
+
export const ROUTES = {
|
|
9
|
+
/** 登录页 */
|
|
7
10
|
LOGIN: '/user/login',
|
|
11
|
+
/** 注册页 */
|
|
12
|
+
REGISTER: '/user/register',
|
|
13
|
+
/** 注册结果页 */
|
|
14
|
+
REGISTER_RESULT: '/user/register-result',
|
|
15
|
+
/** 403 无权限页 */
|
|
8
16
|
FORBIDDEN: '/403',
|
|
17
|
+
/** 404 未找到页 */
|
|
9
18
|
NOT_FOUND: '/404',
|
|
10
19
|
} as const;
|
|
11
20
|
|
|
12
21
|
/**
|
|
13
22
|
* 无需认证的路由列表
|
|
14
23
|
*/
|
|
15
|
-
export const NO_AUTH_ROUTE_LIST =
|
|
24
|
+
export const NO_AUTH_ROUTE_LIST: string[] = [
|
|
25
|
+
ROUTES.LOGIN,
|
|
26
|
+
ROUTES.REGISTER,
|
|
27
|
+
ROUTES.REGISTER_RESULT,
|
|
28
|
+
ROUTES.FORBIDDEN,
|
|
29
|
+
ROUTES.NOT_FOUND,
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 主题相关常量
|
|
34
|
+
*/
|
|
35
|
+
export const THEME = {
|
|
36
|
+
/** localStorage 存储键 */
|
|
37
|
+
STORAGE_KEY: 'audit-center-theme',
|
|
38
|
+
/** 默认主题 */
|
|
39
|
+
DEFAULT: 'light' as const,
|
|
40
|
+
/** 可选主题值 */
|
|
41
|
+
VALUES: ['light', 'dark'] as const,
|
|
42
|
+
} as const;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 时区相关常量
|
|
46
|
+
*/
|
|
47
|
+
export const TIMEZONE = {
|
|
48
|
+
/** localStorage 存储键(IANA 时区,如 Asia/Shanghai) */
|
|
49
|
+
STORAGE_KEY: 'audit-center-timezone',
|
|
50
|
+
/** localStorage 存储键(用于展示的地区/名称,可选) */
|
|
51
|
+
REGION_STORAGE_KEY: 'audit-center-timezone-region',
|
|
52
|
+
} as const;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 在线状态相关常量
|
|
56
|
+
*/
|
|
57
|
+
export const PRESENCE = {
|
|
58
|
+
/** localStorage 存储键 */
|
|
59
|
+
STORAGE_KEY: 'audit-center-presence-status',
|
|
60
|
+
} as const;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 存储键常量
|
|
64
|
+
*/
|
|
65
|
+
export const STORAGE_KEYS = {
|
|
66
|
+
APP_INFO: 'appInfo',
|
|
67
|
+
IS_SUPERUSER: 'is_superuser',
|
|
68
|
+
GROUPS: 'groups',
|
|
69
|
+
} as const;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* OSS 上传目录分类
|
|
73
|
+
*/
|
|
74
|
+
export type TDirCategory = number;
|
|
@@ -11,25 +11,6 @@ import { history } from '@umijs/max';
|
|
|
11
11
|
import { appLogger } from './common/logger';
|
|
12
12
|
import { type IMicroAppProps, setMainAppProps } from './common/mainApp';
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* @name 独立运行时加载主题
|
|
16
|
-
* @description 仅在开发环境且非 qiankun 环境中加载主题样式
|
|
17
|
-
*
|
|
18
|
-
* 实现原理:
|
|
19
|
-
* - 生产构建时 process.env.NODE_ENV !== 'development',整个 if 块被 tree-shake
|
|
20
|
-
* - 开发环境独立运行时加载主题,支持本地预览
|
|
21
|
-
* - 作为微应用运行时(无论开发还是生产),使用主应用的主题
|
|
22
|
-
*/
|
|
23
|
-
if (process.env.NODE_ENV === 'development') {
|
|
24
|
-
// 开发环境:运行时检测是否独立运行
|
|
25
|
-
if (typeof window !== 'undefined' && !window.__POWERED_BY_QIANKUN__) {
|
|
26
|
-
// 直接导入主题样式(开发环境下会被打包)
|
|
27
|
-
import('./styles/theme.less');
|
|
28
|
-
appLogger.log('Running standalone in dev mode, theme loaded');
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
// 生产环境:作为微应用时,主题由主应用注入,无需加载
|
|
32
|
-
|
|
33
14
|
/**
|
|
34
15
|
* @name 路由同步工具
|
|
35
16
|
* @description 处理主应用与子应用之间的路由同步,支持通配符路由模式
|
|
@@ -73,11 +54,10 @@ export const qiankun = {
|
|
|
73
54
|
// 保存主应用传递的 props,包括 request 实例
|
|
74
55
|
setMainAppProps(props);
|
|
75
56
|
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
57
|
+
// 注意:不在 mount 中调用 syncRoute
|
|
58
|
+
// 原因:当子应用从缓存复用时,mount 时的 props 可能是旧的(包含过期的 routePath)
|
|
59
|
+
// 主应用的 MicroAppManager 会在 mount 完成后立即调用 update() 传递正确的 props
|
|
60
|
+
// 路由同步完全由 update 生命周期处理,避免 mount 中的旧 routePath 覆盖正确的路由
|
|
81
61
|
},
|
|
82
62
|
|
|
83
63
|
/**
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* @description homepage 子应用的全局样式
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
@import '<%= packageScope %>/shared-styles
|
|
6
|
+
// 导入共享样式入口(包含 CSS 变量 + Less 变量 + Arco 覆盖样式)
|
|
7
|
+
// 注意:作为微前端子应用运行时,主应用会先加载这些样式
|
|
8
|
+
// 独立运行时,需要自己加载完整样式(包括 Arco 组件覆盖)
|
|
9
|
+
@import '<%= packageScope %>/shared-styles';
|
|
10
10
|
|
|
11
11
|
* {
|
|
12
12
|
box-sizing: border-box;
|