generator-mico-cli 0.1.20 → 0.1.21
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 +139 -14
- package/generators/micro-react/templates/apps/layout/src/common/auth/type.ts +3 -0
- package/generators/micro-react/templates/apps/layout/src/common/micro/index.ts +365 -0
- package/generators/micro-react/templates/apps/layout/src/common/micro/types.ts +184 -0
- package/package.json +1 -1
|
@@ -1,18 +1,92 @@
|
|
|
1
|
-
import { history } from '@umijs/max';
|
|
1
|
+
import { history, type RequestConfig } from '@umijs/max';
|
|
2
|
+
import { prefetchApps } from 'qiankun';
|
|
3
|
+
import { errorConfig } from './requestErrorConfig';
|
|
2
4
|
// 解决「React19 中无法使用 Message/Notification」的问题。 @see https://github.com/arco-design/arco-design/issues/2900#issuecomment-2796571653
|
|
3
5
|
import * as arco from '@arco-design/web-react';
|
|
4
6
|
import '@arco-design/web-react/dist/css/arco.css';
|
|
5
7
|
import '@arco-design/web-react/es/_util/react-19-adapter';
|
|
6
8
|
import React from 'react';
|
|
7
|
-
import ReactDOM from 'react-dom';
|
|
9
|
+
import ReactDOM from 'react-dom/client';
|
|
8
10
|
|
|
9
11
|
import { getAuthInfo } from './common/auth/tool';
|
|
10
|
-
import {
|
|
12
|
+
import { NO_AUTH_ROUTE_LIST } from './constants';
|
|
11
13
|
import { extractRoutes, getWindowMenus } from './common/menu';
|
|
14
|
+
import {
|
|
15
|
+
clearMicroAppProps,
|
|
16
|
+
type IMicroAppProps,
|
|
17
|
+
setMicroAppProps,
|
|
18
|
+
} from './common/micro';
|
|
12
19
|
import { initTheme } from './common/theme';
|
|
13
20
|
import MicroAppLoader from './components/MicroAppLoader';
|
|
14
21
|
import './global.less';
|
|
15
22
|
|
|
23
|
+
// ==================== 微应用预加载 ====================
|
|
24
|
+
// 预加载所有微应用资源,避免快速切换时的竞态条件
|
|
25
|
+
// 当资源已预加载时,loadMicroApp 的异步加载会快速完成,减少容器不存在的错误
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 是否启用微应用预加载
|
|
29
|
+
* - 可通过 URL 参数 ?prefetch=false 禁用(方便调试加载时序问题)
|
|
30
|
+
* - 可通过 localStorage.setItem('DISABLE_MICRO_APP_PREFETCH', 'true') 禁用
|
|
31
|
+
* - 默认启用
|
|
32
|
+
*/
|
|
33
|
+
const isPrefetchEnabled = (): boolean => {
|
|
34
|
+
if (typeof window === 'undefined') return false;
|
|
35
|
+
|
|
36
|
+
// URL 参数优先级最高
|
|
37
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
38
|
+
const prefetchParam = urlParams.get('prefetch');
|
|
39
|
+
if (prefetchParam === 'false') {
|
|
40
|
+
console.log('[App] Prefetch disabled via URL parameter');
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// localStorage 开关
|
|
45
|
+
if (localStorage.getItem('DISABLE_MICRO_APP_PREFETCH') === 'true') {
|
|
46
|
+
console.log('[App] Prefetch disabled via localStorage');
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const prefetchMicroApps = () => {
|
|
54
|
+
if (!isPrefetchEnabled()) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const menus = getWindowMenus();
|
|
60
|
+
const routes = extractRoutes(menus);
|
|
61
|
+
|
|
62
|
+
// 筛选出所有微应用路由
|
|
63
|
+
const microApps = routes
|
|
64
|
+
.filter((route) => route.loadType === 'microapp' && route.entry)
|
|
65
|
+
.map((route) => ({
|
|
66
|
+
name: route.path,
|
|
67
|
+
entry: route.entry!,
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
if (microApps.length > 0) {
|
|
71
|
+
console.log('[App] Prefetching micro apps:', microApps);
|
|
72
|
+
prefetchApps(microApps);
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.warn('[App] Failed to prefetch micro apps:', error);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// 在页面加载后预加载微应用
|
|
80
|
+
if (typeof window !== 'undefined') {
|
|
81
|
+
// 使用 requestIdleCallback 在浏览器空闲时预加载,避免影响首屏渲染
|
|
82
|
+
if ('requestIdleCallback' in window) {
|
|
83
|
+
window.requestIdleCallback(() => prefetchMicroApps(), { timeout: 3000 });
|
|
84
|
+
} else {
|
|
85
|
+
// 降级方案:延迟 1 秒后预加载
|
|
86
|
+
setTimeout(prefetchMicroApps, 1000);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
16
90
|
// ==================== 微前端共享依赖 ====================
|
|
17
91
|
// 将公共库暴露到 window,供子应用复用,避免重复打包
|
|
18
92
|
// 子应用通过 externals 配置引用这些全局变量
|
|
@@ -30,20 +104,20 @@ initTheme();
|
|
|
30
104
|
* @see https://umijs.org/docs/api/runtime-config#getinitialstate
|
|
31
105
|
*/
|
|
32
106
|
export async function getInitialState(): Promise<{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
userid: string;
|
|
37
|
-
status: string;
|
|
38
|
-
};
|
|
107
|
+
// FIXME: 待修改类型
|
|
108
|
+
settings?: any;
|
|
109
|
+
currentUser?: any;
|
|
39
110
|
loading?: boolean;
|
|
40
111
|
fetchUserInfo?: () => Promise<void>;
|
|
41
112
|
}> {
|
|
42
113
|
const fetchUserInfo = async () => {};
|
|
43
|
-
|
|
44
114
|
// 如果不是登录页面,执行
|
|
45
115
|
const { location } = history;
|
|
46
|
-
if (
|
|
116
|
+
if (
|
|
117
|
+
!NO_AUTH_ROUTE_LIST.includes(
|
|
118
|
+
location.pathname,
|
|
119
|
+
)
|
|
120
|
+
) {
|
|
47
121
|
const authInfo = getAuthInfo();
|
|
48
122
|
const currentUser = {
|
|
49
123
|
name: authInfo.nickname || '未知用户',
|
|
@@ -51,7 +125,6 @@ export async function getInitialState(): Promise<{
|
|
|
51
125
|
authInfo.avatar ||
|
|
52
126
|
'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
|
|
53
127
|
userid: authInfo.uid,
|
|
54
|
-
status: 'UNKNOWN',
|
|
55
128
|
};
|
|
56
129
|
return {
|
|
57
130
|
fetchUserInfo,
|
|
@@ -63,6 +136,17 @@ export async function getInitialState(): Promise<{
|
|
|
63
136
|
};
|
|
64
137
|
}
|
|
65
138
|
|
|
139
|
+
/**
|
|
140
|
+
* @name request 配置,可以配置错误处理
|
|
141
|
+
* 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
|
|
142
|
+
* @doc https://umijs.org/docs/max/request#配置
|
|
143
|
+
*/
|
|
144
|
+
export const request: RequestConfig = {
|
|
145
|
+
// 在开发环境下使用本地 mock,生产环境使用实际 API
|
|
146
|
+
baseURL: '',
|
|
147
|
+
...errorConfig,
|
|
148
|
+
};
|
|
149
|
+
|
|
66
150
|
/**
|
|
67
151
|
* Umi 路由类型定义
|
|
68
152
|
*/
|
|
@@ -84,9 +168,14 @@ export function patchClientRoutes({ routes }: { routes: UmiRoute[] }) {
|
|
|
84
168
|
const menus = getWindowMenus();
|
|
85
169
|
const dynamicRoutes = extractRoutes(menus);
|
|
86
170
|
|
|
87
|
-
|
|
171
|
+
console.log('[app.tsx] patchClientRoutes:', { menus, dynamicRoutes, routes });
|
|
172
|
+
|
|
173
|
+
// 找到根路由(全局布局)
|
|
88
174
|
const rootRoute = routes.find((r) => r.path === '/');
|
|
89
|
-
if (!rootRoute)
|
|
175
|
+
if (!rootRoute) {
|
|
176
|
+
console.warn('[app.tsx] Root route not found');
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
90
179
|
|
|
91
180
|
// 为每个动态路由添加配置
|
|
92
181
|
dynamicRoutes.forEach((route) => {
|
|
@@ -121,3 +210,39 @@ export function patchClientRoutes({ routes }: { routes: UmiRoute[] }) {
|
|
|
121
210
|
});
|
|
122
211
|
});
|
|
123
212
|
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @name qiankun 微前端子应用生命周期
|
|
216
|
+
* @description 当应用作为 qiankun 子应用运行时,这些生命周期会被调用
|
|
217
|
+
* @doc https://umijs.org/docs/max/micro-frontend#子应用配置
|
|
218
|
+
*/
|
|
219
|
+
export const qiankun = {
|
|
220
|
+
/** 子应用加载前 */
|
|
221
|
+
async bootstrap() {
|
|
222
|
+
console.log('[micro-app] conversation bootstrap');
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
/** 子应用挂载时,接收主应用传递的 props */
|
|
226
|
+
async mount(props: IMicroAppProps) {
|
|
227
|
+
console.log('[micro-app] conversation mount, props:', props);
|
|
228
|
+
|
|
229
|
+
// 存储主应用传递的 props(认证信息、API 地址、回调函数等)
|
|
230
|
+
setMicroAppProps(props);
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
/** 子应用卸载时 */
|
|
234
|
+
async unmount() {
|
|
235
|
+
console.log('[micro-app] conversation unmount');
|
|
236
|
+
// 清理存储的 props 数据
|
|
237
|
+
clearMicroAppProps();
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
/** 子应用更新时(可选) */
|
|
241
|
+
async update(props: IMicroAppProps) {
|
|
242
|
+
console.log('[micro-app] conversation update, props:', props);
|
|
243
|
+
// 更新时重新存储 props
|
|
244
|
+
if (props) {
|
|
245
|
+
setMicroAppProps(props);
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
};
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 微前端环境检测和 Props 管理工具
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { setAuthInfo } from '@/common/auth/tool';
|
|
6
|
+
import {
|
|
7
|
+
MICRO_ENV_KEY,
|
|
8
|
+
} from '@/common/auth/type';
|
|
9
|
+
import { getFromStorage, removeStorage, setStorage } from '@/common/helpers';
|
|
10
|
+
import { useEffect, useState } from 'react';
|
|
11
|
+
import type {
|
|
12
|
+
IConversationTab,
|
|
13
|
+
IHostCallbacks,
|
|
14
|
+
IMicroAppError,
|
|
15
|
+
IMicroAppProps,
|
|
16
|
+
IMicroAppState,
|
|
17
|
+
IRequestHeaders,
|
|
18
|
+
MicroAppErrorSource,
|
|
19
|
+
} from './types';
|
|
20
|
+
import { EMicroAppErrorStatus } from './types';
|
|
21
|
+
|
|
22
|
+
// 导出类型
|
|
23
|
+
export { EMicroAppErrorStatus } from './types';
|
|
24
|
+
export type {
|
|
25
|
+
IConversationTab,
|
|
26
|
+
IHostCallbacks,
|
|
27
|
+
IMicroAppError,
|
|
28
|
+
IMicroAppProps,
|
|
29
|
+
IMicroAppState,
|
|
30
|
+
IRequestHeaders,
|
|
31
|
+
ISharedServices,
|
|
32
|
+
MicroAppErrorSource,
|
|
33
|
+
} from './types';
|
|
34
|
+
|
|
35
|
+
declare global {
|
|
36
|
+
interface Window {
|
|
37
|
+
/** qiankun 注入的全局变量,表示当前运行在 qiankun 子应用环境中 */
|
|
38
|
+
__POWERED_BY_QIANKUN__?: boolean;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ============================================
|
|
43
|
+
// 环境检测
|
|
44
|
+
// ============================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 是否为微应用构建模式
|
|
48
|
+
* 通过 pnpm start:micro 或 pnpm build:micro 构建时为 true
|
|
49
|
+
*
|
|
50
|
+
* 注意:process.env.MICRO_APP 在编译时会被 UmiJS define 替换为字面量 true/false
|
|
51
|
+
*/
|
|
52
|
+
export const isMicroBuild = (): boolean => {
|
|
53
|
+
// 编译时 define 替换,类型断言避免 TS 报错
|
|
54
|
+
return (process.env.MICRO_APP as unknown) === true;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 是否运行在 qiankun 主应用中(被 qiankun 加载)
|
|
59
|
+
* 只有被 qiankun 主应用加载时才为 true
|
|
60
|
+
*/
|
|
61
|
+
export const isQiankunEnv = (): boolean => {
|
|
62
|
+
return window.__POWERED_BY_QIANKUN__ === true;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 是否为微应用模式(构建或运行时)
|
|
67
|
+
* - 通过微应用配置构建(isMicroBuild)
|
|
68
|
+
* - 或被 qiankun 主应用加载(isQiankunEnv)
|
|
69
|
+
*/
|
|
70
|
+
export const isMicroApp = (): boolean => {
|
|
71
|
+
return isMicroBuild() || isQiankunEnv();
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 获取微应用运行模式
|
|
76
|
+
*/
|
|
77
|
+
export type MicroAppMode = 'standalone' | 'qiankun' | 'main-app';
|
|
78
|
+
|
|
79
|
+
export const getMicroAppMode = (): MicroAppMode => {
|
|
80
|
+
if (isQiankunEnv()) {
|
|
81
|
+
return 'qiankun'; // 被 qiankun 主应用加载
|
|
82
|
+
}
|
|
83
|
+
if (isMicroBuild()) {
|
|
84
|
+
return 'standalone'; // 微应用独立运行
|
|
85
|
+
}
|
|
86
|
+
return 'main-app'; // 主应用模式
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// ============================================
|
|
90
|
+
// Props 存储管理
|
|
91
|
+
// ============================================
|
|
92
|
+
|
|
93
|
+
/** 模块内存状态(存储不可序列化的数据) */
|
|
94
|
+
let microAppState: IMicroAppState = {};
|
|
95
|
+
|
|
96
|
+
/** 微应用 props 是否已接收(mount 是否已被调用) */
|
|
97
|
+
let microAppPropsReceived = false;
|
|
98
|
+
|
|
99
|
+
/** 微应用就绪状态变更的监听器集合 */
|
|
100
|
+
const readyListeners = new Set<() => void>();
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 订阅微应用就绪状态变更
|
|
104
|
+
* @returns 取消订阅函数
|
|
105
|
+
*/
|
|
106
|
+
export const subscribeMicroAppReady = (listener: () => void): (() => void) => {
|
|
107
|
+
readyListeners.add(listener);
|
|
108
|
+
return () => readyListeners.delete(listener);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 通知所有监听器微应用已就绪
|
|
113
|
+
*/
|
|
114
|
+
const notifyMicroAppReady = (): void => {
|
|
115
|
+
readyListeners.forEach((listener) => {
|
|
116
|
+
listener();
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/** 微应用模式下添加到 body 的 class 名称,用于样式区分 */
|
|
121
|
+
export const MICRO_APP_BODY_CLASS = 'micro-app-mode';
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 存储主应用传递的 props
|
|
125
|
+
* - 认证信息存储到 localStorage
|
|
126
|
+
* - 回调函数等存储到模块内存
|
|
127
|
+
*/
|
|
128
|
+
export const setMicroAppProps = (props: IMicroAppProps): void => {
|
|
129
|
+
console.log('[micro-app] setMicroAppProps:', props);
|
|
130
|
+
|
|
131
|
+
// 标记 props 已接收
|
|
132
|
+
microAppPropsReceived = true;
|
|
133
|
+
|
|
134
|
+
// 给 body 添加微应用标识 class,用于样式区分
|
|
135
|
+
document.body.classList.add(MICRO_APP_BODY_CLASS);
|
|
136
|
+
|
|
137
|
+
// 通知监听器微应用已就绪
|
|
138
|
+
notifyMicroAppReady();
|
|
139
|
+
|
|
140
|
+
// 1. 存储认证信息到 localStorage(复用现有的 setAuthInfo)
|
|
141
|
+
setAuthInfo({
|
|
142
|
+
token: props.authToken || '',
|
|
143
|
+
wsToken: props.wsToken || '',
|
|
144
|
+
uid: props.uid || '',
|
|
145
|
+
avatar: props.avatar || '',
|
|
146
|
+
nickname: props.nickname || '',
|
|
147
|
+
refreshToken: '', // 主应用模式下不需要 refreshToken
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// 2. 存储环境标识到 localStorage(用于环境判断)
|
|
151
|
+
if (props.env) {
|
|
152
|
+
setStorage(MICRO_ENV_KEY, props.env);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 4. 存储不可序列化的数据到模块内存
|
|
156
|
+
microAppState = {
|
|
157
|
+
hostCallbacks: props.appConfig?.props?.fromHost,
|
|
158
|
+
conversationTabs: props.appConfig?.props?.conversation_tabs,
|
|
159
|
+
requestHeaders: props.requestHeaders,
|
|
160
|
+
services: props.services,
|
|
161
|
+
rawProps: props,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// 5. 日志输出自定义请求头配置
|
|
165
|
+
if (props.requestHeaders && Object.keys(props.requestHeaders).length > 0) {
|
|
166
|
+
console.log(
|
|
167
|
+
'[setMicroAppProps] requestHeaders:',
|
|
168
|
+
Object.keys(props.requestHeaders),
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* 清理存储的 props 数据
|
|
175
|
+
*/
|
|
176
|
+
export const clearMicroAppProps = (): void => {
|
|
177
|
+
console.log('[micro-app] clearMicroAppProps');
|
|
178
|
+
|
|
179
|
+
// 重置 props 接收标记
|
|
180
|
+
microAppPropsReceived = false;
|
|
181
|
+
|
|
182
|
+
// 移除 body 上的微应用标识 class
|
|
183
|
+
document.body.classList.remove(MICRO_APP_BODY_CLASS);
|
|
184
|
+
|
|
185
|
+
// 清理 localStorage 中的微应用特有数据
|
|
186
|
+
removeStorage(MICRO_ENV_KEY);
|
|
187
|
+
|
|
188
|
+
// 清理模块内存状态
|
|
189
|
+
microAppState = {};
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 获取模块内存中的微应用状态
|
|
194
|
+
*/
|
|
195
|
+
export const getMicroAppState = (): IMicroAppState => {
|
|
196
|
+
return microAppState;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* 获取主应用传递的回调函数
|
|
201
|
+
*/
|
|
202
|
+
export const getHostCallbacks = (): IHostCallbacks => {
|
|
203
|
+
return microAppState.hostCallbacks || {};
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 获取会话 Tab 配置
|
|
208
|
+
*/
|
|
209
|
+
export const getConversationTabs = (): IConversationTab[] | undefined => {
|
|
210
|
+
return microAppState.conversationTabs;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 获取主应用传入的自定义请求头
|
|
215
|
+
* 用于在 HTTP 请求时自动注入额外的 headers
|
|
216
|
+
*/
|
|
217
|
+
export const getRequestHeaders = (): IRequestHeaders | undefined => {
|
|
218
|
+
return microAppState.requestHeaders;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 获取主应用共享服务
|
|
223
|
+
* 子应用可以直接使用这些服务,无需重复初始化
|
|
224
|
+
*/
|
|
225
|
+
export const getSharedServices = (): IMicroAppState['services'] => {
|
|
226
|
+
return microAppState.services;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* 获取存储的环境标识
|
|
231
|
+
*/
|
|
232
|
+
export const getMicroEnv = (): string | null => {
|
|
233
|
+
return getFromStorage(MICRO_ENV_KEY);
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* 微应用是否已初始化完成(props 已接收)
|
|
238
|
+
* 用于判断是否应该跳过某些请求
|
|
239
|
+
*
|
|
240
|
+
* - 非微应用模式:始终返回 true(无需等待初始化)
|
|
241
|
+
* - 微应用模式:返回 props 是否已接收
|
|
242
|
+
*/
|
|
243
|
+
export const isMicroAppReady = (): boolean => {
|
|
244
|
+
// 非微应用模式,无需等待初始化
|
|
245
|
+
if (!isMicroApp()) {
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
// 微应用模式,检查 props 是否已接收
|
|
249
|
+
return microAppPropsReceived;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// ============================================
|
|
253
|
+
// React Hooks
|
|
254
|
+
// ============================================
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* React Hook: 订阅微应用就绪状态
|
|
258
|
+
* 当微应用 props 接收后会触发组件重新渲染
|
|
259
|
+
*
|
|
260
|
+
* @returns 微应用是否已就绪
|
|
261
|
+
*/
|
|
262
|
+
export const useMicroAppReady = (): boolean => {
|
|
263
|
+
const initialReady = isMicroAppReady();
|
|
264
|
+
console.log(
|
|
265
|
+
'[useMicroAppReady] initial check:',
|
|
266
|
+
'isMicroApp=',
|
|
267
|
+
isMicroApp(),
|
|
268
|
+
'propsReceived=',
|
|
269
|
+
microAppPropsReceived,
|
|
270
|
+
'ready=',
|
|
271
|
+
initialReady,
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
const [ready, setReady] = useState(initialReady);
|
|
275
|
+
|
|
276
|
+
useEffect(() => {
|
|
277
|
+
// 如果已就绪,无需订阅
|
|
278
|
+
if (ready) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
console.log('[useMicroAppReady] subscribing to ready state changes');
|
|
283
|
+
// 订阅就绪状态变更
|
|
284
|
+
const unsubscribe = subscribeMicroAppReady(() => {
|
|
285
|
+
const newReady = isMicroAppReady();
|
|
286
|
+
console.log('[useMicroAppReady] ready state changed:', newReady);
|
|
287
|
+
setReady(newReady);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
return unsubscribe;
|
|
291
|
+
}, [ready]);
|
|
292
|
+
|
|
293
|
+
return ready;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
// ============================================
|
|
297
|
+
// 错误通知
|
|
298
|
+
// ============================================
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* 通知主应用发生错误
|
|
302
|
+
* 仅在微应用模式下且主应用提供了 onError 回调时才会调用
|
|
303
|
+
*
|
|
304
|
+
* @param error 错误信息
|
|
305
|
+
* @returns 是否成功通知(false 表示非微应用模式或主应用未提供回调)
|
|
306
|
+
*/
|
|
307
|
+
export const notifyHostError = (error: IMicroAppError): boolean => {
|
|
308
|
+
// 非微应用模式,不通知
|
|
309
|
+
if (!isMicroApp()) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const callbacks = getHostCallbacks();
|
|
314
|
+
if (!callbacks.onError) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 先打印日志,再调用回调(避免回调抛错导致日志丢失)
|
|
319
|
+
console.log('[micro-app] notifyHostError:', error);
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
callbacks.onError(error);
|
|
323
|
+
return true;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
console.error('[micro-app] onError callback failed:', err);
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* 创建鉴权失败错误
|
|
332
|
+
* @param message 错误消息
|
|
333
|
+
* @param code 原始错误码
|
|
334
|
+
* @param source 错误来源
|
|
335
|
+
*/
|
|
336
|
+
export const createAuthError = (
|
|
337
|
+
message: string,
|
|
338
|
+
code?: number | string,
|
|
339
|
+
source?: MicroAppErrorSource,
|
|
340
|
+
): IMicroAppError => ({
|
|
341
|
+
status: EMicroAppErrorStatus.AUTH_FAILURE,
|
|
342
|
+
message,
|
|
343
|
+
source,
|
|
344
|
+
code,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 创建业务错误
|
|
349
|
+
* @param message 错误消息
|
|
350
|
+
* @param code 原始错误码
|
|
351
|
+
* @param source 错误来源
|
|
352
|
+
* @param details 额外详情
|
|
353
|
+
*/
|
|
354
|
+
export const createBusinessError = (
|
|
355
|
+
message: string,
|
|
356
|
+
code?: number | string,
|
|
357
|
+
source?: MicroAppErrorSource,
|
|
358
|
+
details?: Record<string, unknown>,
|
|
359
|
+
): IMicroAppError => ({
|
|
360
|
+
status: EMicroAppErrorStatus.BUSINESS_ERROR,
|
|
361
|
+
message,
|
|
362
|
+
source,
|
|
363
|
+
code,
|
|
364
|
+
details,
|
|
365
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 微前端 Props 类型定义
|
|
3
|
+
* 主应用通过 qiankun 传递给子应用的配置数据
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 微应用错误状态码
|
|
8
|
+
* - 1: 鉴权失败(token 无效/过期),需要主应用重新获取 token
|
|
9
|
+
* - 2: 业务错误(参数错误、逻辑拒绝、网络超时、权限不足等)
|
|
10
|
+
*/
|
|
11
|
+
export enum EMicroAppErrorStatus {
|
|
12
|
+
/** 鉴权失败,需重获 token */
|
|
13
|
+
AUTH_FAILURE = 1,
|
|
14
|
+
/** 业务错误 */
|
|
15
|
+
BUSINESS_ERROR = 2,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 错误来源
|
|
20
|
+
*/
|
|
21
|
+
export type MicroAppErrorSource = 'websocket' | 'http';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 微应用错误信息
|
|
25
|
+
* 用于通知主应用子应用内部发生的错误
|
|
26
|
+
*/
|
|
27
|
+
export interface IMicroAppError {
|
|
28
|
+
/** 错误状态码 */
|
|
29
|
+
status: EMicroAppErrorStatus;
|
|
30
|
+
/** 错误消息 */
|
|
31
|
+
message: string;
|
|
32
|
+
/** 错误来源 */
|
|
33
|
+
source?: MicroAppErrorSource;
|
|
34
|
+
/** 原始错误码(如 HTTP 状态码、WebSocket 错误码等) */
|
|
35
|
+
code?: number | string;
|
|
36
|
+
/** 额外的错误详情 */
|
|
37
|
+
details?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 主应用回调函数
|
|
42
|
+
*/
|
|
43
|
+
export interface IHostCallbacks {
|
|
44
|
+
/** 点击会话时的回调 */
|
|
45
|
+
onClickConversation?: (conversation: unknown) => void;
|
|
46
|
+
/** 子应用内部错误回调,用于通知主应用进行统一处理 */
|
|
47
|
+
onError?: (error: IMicroAppError) => void;
|
|
48
|
+
/** 其他回调可按需扩展 */
|
|
49
|
+
[key: string]: ((...args: any[]) => void) | undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 会话 Tab 配置
|
|
54
|
+
*/
|
|
55
|
+
export interface IConversationTab {
|
|
56
|
+
/** Tab 唯一标识,同时用于关联接口的 status 参数 */
|
|
57
|
+
key: string;
|
|
58
|
+
/** Tab 显示标题 */
|
|
59
|
+
title: string;
|
|
60
|
+
/** 是否显示数量 */
|
|
61
|
+
showCount?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 子应用配置
|
|
66
|
+
*/
|
|
67
|
+
export interface IAppConfig {
|
|
68
|
+
/** 子应用名称 */
|
|
69
|
+
name: string;
|
|
70
|
+
/** 子应用自定义配置 */
|
|
71
|
+
props?: {
|
|
72
|
+
/** 主应用回调函数 */
|
|
73
|
+
fromHost?: IHostCallbacks;
|
|
74
|
+
/** 会话 Tab 配置 */
|
|
75
|
+
conversation_tabs?: IConversationTab[];
|
|
76
|
+
/** 其他配置可按需扩展 */
|
|
77
|
+
[key: string]: unknown;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* API 端点配置
|
|
83
|
+
* 主应用可以传入自定义的 API 端点路径
|
|
84
|
+
*/
|
|
85
|
+
export interface IApiEndpoints {
|
|
86
|
+
/** OSS 文件上传签名 URL */
|
|
87
|
+
ossSignUrl?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 主应用共享服务
|
|
92
|
+
* 包含请求服务和 WebSocket 服务
|
|
93
|
+
*/
|
|
94
|
+
export interface ISharedServices {
|
|
95
|
+
/** HTTP 请求服务 */
|
|
96
|
+
request: {
|
|
97
|
+
request: <T = any>(
|
|
98
|
+
url: string,
|
|
99
|
+
options?: Record<string, any>,
|
|
100
|
+
) => Promise<T>;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/** WebSocket 服务 */
|
|
104
|
+
websocket: Record<string, (...args: any[]) => any>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 自定义请求头配置
|
|
109
|
+
* 主应用可以传入额外的请求头,会在每次 HTTP 请求时自动注入
|
|
110
|
+
*/
|
|
111
|
+
export type IRequestHeaders = Record<string, string>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 主应用传递给微应用的 Props
|
|
115
|
+
*/
|
|
116
|
+
export interface IMicroAppProps {
|
|
117
|
+
/** 子应用路由基础路径 */
|
|
118
|
+
base?: string;
|
|
119
|
+
/** 环境标识(testing/production) */
|
|
120
|
+
env?: string;
|
|
121
|
+
/** 业务类型(crm/cs),决定布局配置 */
|
|
122
|
+
businessType?: string;
|
|
123
|
+
/** HTTP 请求的鉴权 token */
|
|
124
|
+
authToken?: string;
|
|
125
|
+
/** CDN 文件的前缀 host */
|
|
126
|
+
filehost?: string;
|
|
127
|
+
/** WebSocket 建立连接的 Authorization */
|
|
128
|
+
wsToken?: string;
|
|
129
|
+
/** 用户头像 URL */
|
|
130
|
+
avatar?: string;
|
|
131
|
+
/** 当前客服 ID */
|
|
132
|
+
uid?: string;
|
|
133
|
+
/** 当前客服昵称 */
|
|
134
|
+
nickname?: string;
|
|
135
|
+
/**
|
|
136
|
+
* API 基础 URL
|
|
137
|
+
* @example 'https://dashboard-api-test.micoplatform.com'
|
|
138
|
+
*/
|
|
139
|
+
apiBaseUrl?: string;
|
|
140
|
+
/**
|
|
141
|
+
* API 代理路径前缀
|
|
142
|
+
* @example '/proxy/mibot_svr'
|
|
143
|
+
*/
|
|
144
|
+
proxyPath?: string;
|
|
145
|
+
/**
|
|
146
|
+
* 自定义 API 端点路径
|
|
147
|
+
* 如果 CRM 使用不同的端点路径,可通过此字段配置
|
|
148
|
+
*/
|
|
149
|
+
apiEndpoints?: IApiEndpoints;
|
|
150
|
+
/**
|
|
151
|
+
* 自定义请求头
|
|
152
|
+
* 主应用传入的额外请求头,会在每次 HTTP 请求时自动注入
|
|
153
|
+
* @example { 'X-Custom-Header': 'value', 'X-Tenant-Id': '123' }
|
|
154
|
+
*/
|
|
155
|
+
requestHeaders?: IRequestHeaders;
|
|
156
|
+
/** 子应用配置 */
|
|
157
|
+
appConfig?: IAppConfig;
|
|
158
|
+
/** qiankun 注入的容器元素 */
|
|
159
|
+
container?: HTMLElement;
|
|
160
|
+
/**
|
|
161
|
+
* 主应用共享服务
|
|
162
|
+
* 包含 HTTP 请求服务和 WebSocket 服务
|
|
163
|
+
* 子应用可以直接使用这些服务,无需重复初始化
|
|
164
|
+
*/
|
|
165
|
+
services?: ISharedServices;
|
|
166
|
+
/** 其他扩展字段 */
|
|
167
|
+
[key: string]: unknown;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 存储在内存中的子应用配置(不可序列化的部分)
|
|
172
|
+
*/
|
|
173
|
+
export interface IMicroAppState {
|
|
174
|
+
/** 主应用回调函数 */
|
|
175
|
+
hostCallbacks?: IHostCallbacks;
|
|
176
|
+
/** 会话 Tab 配置 */
|
|
177
|
+
conversationTabs?: IConversationTab[];
|
|
178
|
+
/** 主应用传入的自定义请求头 */
|
|
179
|
+
requestHeaders?: IRequestHeaders;
|
|
180
|
+
/** 主应用共享服务 */
|
|
181
|
+
services?: ISharedServices;
|
|
182
|
+
/** 原始 props(用于调试) */
|
|
183
|
+
rawProps?: IMicroAppProps;
|
|
184
|
+
}
|