@zxiaosi/sdk 0.5.2 → 1.0.0-beta.2

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/dist/index.d.ts CHANGED
@@ -1,295 +1,191 @@
1
- import * as zustand from "zustand";
2
1
  import { ComponentType, ReactElement } from "react";
3
- import { Location, NavigateFunction, RouteObject, UIMatch } from "react-router-dom";
4
- import { AxiosInstance, AxiosRequestConfig, CreateAxiosDefaults } from "axios";
5
- import { ConfigProviderProps } from "antd";
6
- import * as react_intl_universal0 from "react-intl-universal";
7
- import intl from "react-intl-universal";
8
- import { MenuDataItem, ProLayoutProps } from "@ant-design/pro-layout";
2
+ import { NavigateFunction, UIMatch } from "react-router-dom";
9
3
  import { MicroApp, ObjectType, RegistrableApp } from "qiankun";
4
+ import { StateCreator } from "zustand";
10
5
 
11
- //#region src/plugins/api/http.d.ts
12
- interface ApiRequestOption extends AxiosRequestConfig {
13
- /** 请求唯一key(默认自动生成) */
14
- requestId?: string;
15
- /** 是否取消重复请求 */
16
- isCancelRequest?: boolean;
17
- /** 是否需要原始数据 */
18
- isOriginalData?: boolean;
19
- /** 是否显示错误信息 */
20
- isShowFailMsg?: boolean;
21
- }
22
- //#endregion
23
6
  //#region src/plugins/api/index.d.ts
24
- interface ApiOptions {
25
- /** Axios配置 */
26
- config?: CreateAxiosDefaults;
27
- /** 取消请求控制器 */
28
- controllers?: Map<string, AbortController>;
29
- /**
30
- * 自定义请求实例
31
- * - 替换 SDK 内置的请求实例
32
- * @example instance = axios.create(options)
33
- */
34
- instance?: AxiosInstance;
35
- /**
36
- * 获取用户信息
37
- * {@link UserInfo}
38
- * @example { code: 0, data: { user: {}, permissions: [], roles: [], settings: {} } }
39
- */
40
- getUserInfoApi?(): Promise<any>;
41
- /**
42
- * 获取路由数据
43
- * {@link RouteObject}
44
- * @example { code: 0, data: [{path: '/', name: '首页', component: 'Home'}] }
45
- */
46
- getRoutesApi?(): Promise<any>;
47
- /**
48
- * 登录接口
49
- * @example { code: 0, data: { token: 'xxxx' } }
50
- */
51
- loginApi?(params: any): Promise<any>;
52
- }
53
- interface ApiResults extends Required<ApiOptions> {
54
- /**
55
- * 请求
56
- * @param url 请求地址
57
- * @param options 自定义配置项
58
- */
59
- request(url: string, options?: ApiRequestOption): Promise<any>;
60
- }
7
+ interface ApiOptions {}
61
8
  /**
62
9
  * 请求插件
63
- * - 详情参考 {@link ApiOptions} {@link ApiResults}
64
- * - 内置了请求, 通过 sdk.api.request 发起请求
65
- * - 可通过外部传入 instance 自定义请求实例
66
- * - 预置了获取用户信息, 获取路由接口, 以便组件使用
67
- * @example sdk.api.request('/getTemp', { method: 'POST', ... })
68
- * @example sdk.api.request('/getTemp', { method: 'POST', isOriginalData: true }) // 返回原始数据
69
- * @example sdk.api.request('/getTemp', { method: 'POST', isShowFailMsg: false }) // 不显示错误信息
70
- * @example sdk.api.request('/getTemp', { method: 'POST', isCancelRequest: false }) // 不自动取消重复请求
71
10
  */
72
- declare const SdkApiPlugin: Plugin<'api'>;
11
+ declare const SDKApiPlugin: SDKPlugin;
73
12
  //#endregion
74
13
  //#region src/plugins/app/index.d.ts
75
14
  interface AppOptions {
76
15
  /** 菜单数据 */
77
- menuData?: MenuDataItem[];
16
+ menuData: any[];
78
17
  /** 所有路由信息 */
79
- allRoutes?: RouteObject[];
18
+ allRoutes: any[];
80
19
  /** 微应用信息 */
81
- microApps?: RegistrableApp<ObjectType>[];
20
+ microApps: RegistrableApp<ObjectType>[];
82
21
  /** 微应用实例 */
83
- microAppsInstance?: Map<string, MicroApp>;
22
+ microAppsInstance: Map<string, MicroApp>;
84
23
  /** 用户信息 */
85
- user?: UserInfo['user'];
24
+ user: UserInfo['user'];
86
25
  /** 用户权限 */
87
- permissions?: UserInfo['permissions'];
88
- /** 用户角色 */
89
- roles?: UserInfo['roles'];
26
+ permissions: UserInfo['permissions'];
90
27
  /** 用户设置 */
91
- settings?: UserInfo['settings'];
92
- }
93
- interface AppResults extends Required<AppOptions> {
28
+ settings: UserInfo['settings'];
94
29
  /**
95
- * 初始化数据
96
- * - sdk.config.qiankunMode = 'load' 时, 登录时用
30
+ * 获取国际化默认值
31
+ * - 1. 本地缓存 `sdk.storage.getItem(sdk.storage.localeKey)`
32
+ * - 2. sdk中国际化 `sdk.config.locale`
33
+ * - 3. 浏览器语言 `navigator.language`
34
+ * - 4. 默认 `zh-CN`
97
35
  */
98
- initData(): void;
36
+ getDefaultLocale(): LocaleProps;
99
37
  /**
100
- * 清空数据
101
- * - sdk.config.qiankunMode = 'load' 时, 登出时用
38
+ * 获取主题默认值
39
+ * - 1. 本地缓存 `sdk.storage.getItem(sdk.storage.themeKey)`
40
+ * - 2. sdk中主题 `sdk.config.theme`
41
+ * - 3. 系统主题 `window.matchMedia('(prefers-color-scheme: dark)').matches`
42
+ * - 4. 默认 `light`
102
43
  */
103
- clearData(): void;
44
+ getDefaultTheme(): ThemeProps;
45
+ /**
46
+ * 获取重定向路径
47
+ * - 1. 优先使用指定值 `sdk.config.defaultPath`
48
+ * - 2. 其次使用重定向的值 `sdk.config.redirectField`
49
+ * - 3. 最后使用菜单中第一项 `/`
50
+ */
51
+ getRedirectPath(): string;
52
+ /**
53
+ * 初始化数据
54
+ */
55
+ initData(): void | Promise<void>;
104
56
  /**
105
57
  * 跳转登录页
58
+ * - 1. 清除 Token
59
+ * - 2. 清除用户信息
60
+ * - 3. 获取当前页路由
61
+ * - 4. 清空微应用
106
62
  */
107
63
  pageToLogin(): void;
108
64
  /**
109
- * 获取重定向路径
65
+ * 卸载微应用
66
+ * - 默认卸载所有微应用
67
+ * - 传入名称数组则卸载指定微应用
110
68
  */
111
- getRedirectPath(): string;
69
+ unmountMicroApp(names?: string[]): void;
112
70
  }
113
71
  /**
114
- * 项目信息
115
- * - 详情参考 {@link AppOptions} {@link AppResults}
116
- * - 菜单信息 sdk.app.menuData
117
- * - 路由信息 sdk.app.allRoutes
118
- * - 微应用信息 sdk.app.microApps
119
- * - 用户信息 sdk.app.user
120
- * - 用户权限 sdk.app.permissions
121
- * - 用户角色 sdk.app.roles
122
- * - 用户设置 sdk.app.settings
72
+ * 应用插件
73
+ *
74
+ * @example
75
+ * sdk.use(SDKAppPlugin, { menuData: [...] }).mount('xxx');
76
+ * sdk.app.unmountMicroApp();
123
77
  */
124
- declare const SdkAppPlugin: Plugin<'app'>;
78
+ declare const SDKAppPlugin: SDKPlugin;
125
79
  //#endregion
126
- //#region src/plugins/client/index.d.ts
127
- interface ClientOptions {}
128
- interface ClientResults extends Required<ClientOptions> {
129
- /** 主应用 location */
130
- location: Location;
131
- /** 路由匹配(用于面包屑) */
132
- matches: UIMatch[];
133
- /** 主应用navigate(解决微应用跳转问题) */
134
- navigate: NavigateFunction;
80
+ //#region src/plugins/components/index.d.ts
81
+ interface ComponentsOptions {
82
+ /** 组件 */
83
+ [key: string]: any;
84
+ /**
85
+ * 设置组件
86
+ * @param component 组件
87
+ * @param name 组件名称
88
+ */
89
+ setComponent(component: ComponentType, name?: string): void;
90
+ /**
91
+ * 获取组件
92
+ * @param name 组件名称
93
+ */
94
+ getComponent(name: string): ComponentType | null;
95
+ /**
96
+ * 渲染组件
97
+ * @param name 组件名称
98
+ */
99
+ renderComponent(name: string, props?: any): ReactElement | null;
135
100
  }
136
101
  /**
137
- * 全局路由信息
138
- * - 详情参考 {@link ClientOptions} {@link ClientResults}
139
- * - 路由信息 sdk.client.location
140
- * - 路由跳转 sdk.client.navigate
141
- * - 面包屑信息 sdk.client.matches
102
+ * 组件插件
103
+ *
104
+ * @example
105
+ * sdk.use(SDKComponentsPlugin).mount('xxx');
106
+ * sdk.components.setComponent(组件, '组件名称');
107
+ * sdk.components.renderComponent('组件名称', props);
142
108
  */
143
- declare const SdkClientPlugin: Plugin<'client'>;
109
+ declare const SDKComponentsPlugin: SDKPlugin;
144
110
  //#endregion
145
111
  //#region src/plugins/config/index.d.ts
146
112
  interface ConfigOptions {
147
113
  /** 环境变量(主应用共享给微应用变量) */
148
- env?: Record<string, any>;
114
+ env: Record<string, any>;
149
115
  /** 主题 */
150
- theme?: ThemeProps;
116
+ theme: ThemeProps;
151
117
  /** 语言 */
152
- locale?: LocaleProps;
153
- /**
154
- * Qiankun模式(切换模式后请重新打开页面)
155
- * - 'router': 基于路由模式
156
- * - 登录时,刷新页面,会自动调用 getUserInfoApi、getRoutesApi 获取数据
157
- * - 拿到 routes 数据之后,需要 registerMicroApps 注册微应用 和 start 启动微应用
158
- * - 系统退出时,刷新页面,自动销毁 qiankun 声明周期和缓存的数据
159
- * - 系统登录和系统退出仅有一次刷新页面即可,为了销毁 qiankun 的声明周期,但数据需要手动加载或者清除
160
- * - 'load': 手动加载模式
161
- * - 登录时,不需刷新页面, 但需要手动调用 getUserInfoApi、getRoutesApi 获取数据
162
- * - 拿到 routes 数据之后,在 Microapp 组件中使用 loadMicroApp 手动加载微应用
163
- * - 系统退出时,不刷新页面,但需要手动销毁每个微应用,并清除缓存数据
164
- */
165
- qiankunMode?: 'router' | 'load';
118
+ locale: LocaleProps;
166
119
  /** 登录页路由 */
167
- loginPath?: string;
168
- /**
169
- * 登录后跳转的路由
170
- * - 优先使用指定值
171
- * - 其次使用重定向的值
172
- * - 最后使用菜单中第一项
173
- */
174
- defaultPath?: string;
175
- /**
176
- * 自定义路由信息
177
- * - 目前只支持最外层路由自定义
178
- * - 会合并到 sdk.app.allRoutes 中
179
- */
180
- customRoutes?: RouteObject[];
181
- /** Antd 配置 */
182
- antdConfig?: ConfigProviderProps;
183
- /** ProLayout 配置 */
184
- proLayoutConfig?: ProLayoutProps;
120
+ loginPath: string;
121
+ /** 登录后跳转的路由 */
122
+ defaultPath: string;
123
+ /** 重定向字段 */
124
+ redirectField?: string;
185
125
  }
186
- interface ConfigResults extends Required<ConfigOptions> {}
187
126
  /**
188
- * Sdk 配置信息
189
- * - 详情参考 {@link ConfigOptions} {@link ConfigResults}
190
- * - 配置 env 环境变量
191
- * - 配置 默认主题、语言
192
- * - 配置 Qiankun 模式
193
- * - 配置 默认登录路径、跳转路径、自定义路由
194
- * - 配置 Antd 配置、ProLayout 配置
127
+ * 配置插件
128
+ *
129
+ * @example
130
+ * sdk.use(SDKConfigPlugin, { theme: 'light' }).mount('xxx');
131
+ * console.log(sdk.api.theme); // 'light'
195
132
  */
196
- declare const SdkConfigPlugin: Plugin<'config'>;
133
+ declare const SDKConfigPlugin: SDKPlugin;
197
134
  //#endregion
198
135
  //#region src/plugins/i18n/index.d.ts
199
- interface I18nOptions {
200
- /**
201
- * React Intl Universal 实例
202
- * - 不要解构使用, const { get } = useIntl() 会报错
203
- * - 如果项目不使用 React Compiler, 可以直接使用 sdk.i18n.intl
204
- * @example const intl = useIntl(); intl.get(key).d(defaultValue)
205
- */
206
- intl?: typeof intl;
207
- /**
208
- * 自定义语言包
209
- * @example
210
- * {
211
- * 'zh-CN': {
212
- * test: '测试国际化'
213
- * },
214
- * 'en-US': {
215
- * test: 'Test Intl'
216
- * }
217
- * }
218
- */
219
- intlConfig?: Record<string, any>;
220
- /**
221
- * 加载语言包
222
- * @param locale 语言包名
223
- * @example
224
- * import enUS from 'antd/es/locale/en_US';
225
- * import zhCN from 'antd/es/locale/zh_CN';
226
- * import dayjs from 'dayjs';
227
- * import 'dayjs/locale/en';
228
- * import 'dayjs/locale/zh';
229
- *
230
- * const loadLocale = (locale: string) => {
231
- * switch (locale) {
232
- * case 'en-US':
233
- * dayjs.locale('en');
234
- * return enUS;
235
- * case 'zh-CN':
236
- * dayjs.locale('zh');
237
- * return zhCN;
238
- * default:
239
- * return undefined;
240
- * }
241
- * }
242
- */
243
- loadLocale?(locale: string): any;
136
+ interface I18nOptions {}
137
+ /**
138
+ * 国际化插件
139
+ *
140
+ * @example
141
+ * sdk.use(SDKI18nPlugin).mount('xxx');
142
+ */
143
+ declare const SDKI18nPlugin: SDKPlugin;
144
+ //#endregion
145
+ //#region src/plugins/router/index.d.ts
146
+ interface RouterOptions {
147
+ /** 主应用 location */
148
+ location: Location;
149
+ /** 路由匹配(用于面包屑) */
150
+ matches: UIMatch[];
151
+ /** 主应用navigate(解决微应用跳转问题) */
152
+ navigate: NavigateFunction;
244
153
  }
245
- interface I18nResults extends Required<I18nOptions> {}
246
154
  /**
247
- * 多语言
248
- * - 详情参考 {@link I18nOptions} {@link I18nResults}
249
- * - 集成 React Intl Universal 和 Antd 国际化
250
- * - 需要从外部引入语言包, 详见 intlConfig 和 loadLocale 配置项
155
+ * 路由插件
156
+ *
157
+ * @example
158
+ * sdk.use(SDKI18nPlugin).mount('xxx');
159
+ * sdk.router.location; // 路由信息
160
+ * sdk.router.navigate; // 路由跳转
161
+ * sdk.router.matches; // 面包屑信息
251
162
  */
252
- declare const SdkI18nPlugin: Plugin<'i18n'>;
163
+ declare const SDKRouterPlugin: SDKPlugin;
253
164
  //#endregion
254
165
  //#region src/plugins/storage/index.d.ts
255
166
  interface StorageOptions {
256
167
  /** 语言存储名称 */
257
- localeKey?: string;
168
+ localeKey: string;
258
169
  /** 主题存储名称 */
259
- themeKey?: string;
170
+ themeKey: string;
260
171
  /** Token存储名称 */
261
- tokenKey?: string;
262
- }
263
- interface StorageResults extends Required<StorageOptions> {
264
- /** 获取当前语言 */
265
- getLocale(): LocaleProps;
266
- /** 设置/切换语言 */
267
- setLocale(locale: LocaleProps): void;
268
- /** 清空语言 */
269
- clearLocale(): void;
270
- /** 获取当前主题 */
271
- getTheme(): ThemeProps;
272
- /** 设置/切换主题 */
273
- setTheme(theme: ThemeProps): void;
274
- /** 清空主题 */
275
- clearTheme(): void;
276
- /** 获取当前 Token */
277
- getToken(): string;
278
- /** 设置 Token */
279
- setToken(token: string): void;
280
- /** 清空 Token */
281
- clearToken(): void;
172
+ tokenKey: string;
173
+ /** 设置缓存 */
174
+ setItem(key: string, value: string): void;
175
+ /** 获取缓存 */
176
+ getItem(key: string): string | null;
177
+ /** 删除缓存 */
178
+ removeItem(key: string): void;
179
+ /** 清空缓存 */
180
+ clear(): void;
282
181
  }
283
182
  /**
284
- * 本地缓存
285
- * - 详情参考 {@link StorageOptions} {@link StorageResults}
286
- * - 配置 localStorage 变量名称
287
- * - 提供 语言、主题、Token 的 get、change、clear 方法
288
- * @example sdk.storage.getToken() // 获取 Token
289
- * @example sdk.storage.setTheme('dark') // 设置主题
290
- * @example sdk.storage.clearLocale() // 清空语言
183
+ * 本地缓存插件
184
+ *
185
+ * @example
186
+ * sdk.use(SDKStoragePlugin).mount('xxx');
291
187
  */
292
- declare const SdkStoragePlugin: Plugin<'storage'>;
188
+ declare const SDKStoragePlugin: SDKPlugin;
293
189
  //#endregion
294
190
  //#region src/plugins/store/createLocale.d.ts
295
191
  interface LocaleStoreProps {
@@ -330,65 +226,33 @@ interface UserInfoStoreProps {
330
226
  /** 创建用户信息切片 */
331
227
  //#endregion
332
228
  //#region src/plugins/store/index.d.ts
333
- type StoreOptions = LocaleStoreProps & MicroAppLoadingStoreProps & ThemeStoreProps & UserInfoStoreProps;
334
- type StoreResults = typeof globalStore;
229
+ interface StoreProps extends LocaleStoreProps, MicroAppLoadingStoreProps, ThemeStoreProps, UserInfoStoreProps {}
230
+ type StoreSlice<T = any> = StateCreator<T>;
231
+ /** 插件 options */
232
+ type StorePluginOptions = Record<string, StoreSlice>;
335
233
  /**
336
234
  * 创建 Store
337
- * - 这里单独声明变量, 主要是为了使用返回类型 StoreResults 🤔
235
+ * - 这里单独声明变量, 主要是为了使用返回类型 StoreOptions 🤔
338
236
  */
339
- declare const globalStore: Omit<zustand.StoreApi<StoreOptions>, "subscribe"> & {
237
+ declare const createGlobalStore: (options?: StorePluginOptions) => Omit<import("zustand").StoreApi<StoreProps>, "subscribe"> & {
340
238
  subscribe: {
341
- (listener: (selectedState: StoreOptions, previousSelectedState: StoreOptions) => void): () => void;
342
- <U>(selector: (state: StoreOptions) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: {
239
+ (listener: (selectedState: StoreProps, previousSelectedState: StoreProps) => void): () => void;
240
+ <U>(selector: (state: StoreProps) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: {
343
241
  equalityFn?: (a: U, b: U) => boolean;
344
242
  fireImmediately?: boolean;
345
243
  }): () => void;
346
244
  };
347
245
  };
246
+ type StoreOptions = ReturnType<typeof createGlobalStore>;
348
247
  /**
349
- * 全局状态管理
350
- * - 详情参考 {@link StoreOptions} {@link StoreResults}
351
- * - 此插件不会合并传入属性
248
+ * 状态管理插件
352
249
  * @example const setTheme = useStore(sdk.store, (state) => state.setTheme)
353
250
  * @example const { theme, setTheme } = useStore(sdk.store, useShallow((state) => { theme: state.theme, setTheme: state.setTheme }))
354
251
  * @example const [theme, setTheme] = useStore(sdk.store, useShallow((state) => [state.theme, state.setTheme]))
355
252
  * @example sdk.store?.getState()?.setTheme('light')
356
253
  * @example sdk.store.subscribe((state) => state.theme, (theme) => { console.log('theme', theme) }, { fireImmediately: true }) // fireImmediately 立即变更
357
254
  */
358
- declare const SdkStorePlugin: Plugin<'store'>;
359
- //#endregion
360
- //#region src/plugins/ui/index.d.ts
361
- interface UIOptions {
362
- /** 组件 */
363
- [key: string]: ComponentType | ((name: string) => ComponentType) | ((component: ComponentType, name?: string) => void);
364
- }
365
- interface UIResults extends Required<UIOptions> {
366
- /**
367
- * 设置组件
368
- * @param component 组件
369
- * @param name 组件名称
370
- */
371
- setComponent(component: ComponentType, name?: string): void;
372
- /**
373
- * 获取组件
374
- * @param name 组件名称
375
- */
376
- getComponent(name: string): ComponentType;
377
- /**
378
- * 渲染组件
379
- * @param name 组件名称
380
- */
381
- renderComponent(name: string, props?: any): ReactElement;
382
- }
383
- /**
384
- * 可复用组件
385
- * - 详情参考 {@link UIOptions} {@link UIResults}
386
- * - 内置了 Layout、Loading、Login、Mainapp、Microapp、NotFound 等组件, 可传入覆盖
387
- * - 组件共享
388
- * - 在主应用中, 可通过 sdk.use(SdkUIPlugin, { MyComponent }) 传入组件
389
- * - 在微应用中, 可通过 sdk.ui.renderComponent('MyComponent') 使用组件
390
- */
391
- declare const SdkUIPlugin: Plugin<'ui'>;
255
+ declare const SDKStorePlugin: SDKPlugin;
392
256
  //#endregion
393
257
  //#region src/types.d.ts
394
258
  type ThemeProps = 'light' | 'dark' | (string & {});
@@ -398,137 +262,72 @@ interface UserInfo {
398
262
  user?: any;
399
263
  /** 用户权限 */
400
264
  permissions?: string[];
401
- /** 用户角色 */
402
- roles?: string[];
403
265
  /** 用户设置 */
404
266
  settings?: {
405
267
  theme?: ThemeProps;
406
268
  locale?: LocaleProps;
407
269
  };
408
270
  }
409
- interface PluginOptions {
410
- /** 全局请求 */
411
- api?: ApiOptions;
412
- /** 项目信息 */
413
- app?: AppOptions;
414
- /** 全局路由信息 */
415
- client?: ClientOptions;
416
- /** Sdk 配置信息 */
417
- config?: ConfigOptions;
418
- /** 多语言 */
419
- i18n?: I18nOptions;
420
- /** 本地缓存 */
421
- storage?: StorageOptions;
422
- /** 全局状态管理 */
423
- store?: StoreOptions;
424
- /** 可复用组件 */
425
- ui?: UIOptions;
271
+ declare global {
272
+ interface Window {
273
+ [key: string]: any;
274
+ }
426
275
  }
427
- interface PluginResults {
428
- /** 全局请求 */
429
- api: ApiResults;
430
- /** 项目信息 */
431
- app: AppResults;
432
- /** 全局路由信息 */
433
- client: ClientResults;
434
- /** Sdk 配置信息 */
435
- config: ConfigResults;
436
- /** 多语言 */
437
- i18n: I18nResults;
276
+ interface SDKPlugins {
277
+ /** 请求 */
278
+ api: ApiOptions;
279
+ /** 应用 */
280
+ app: AppOptions;
281
+ /** 组件 */
282
+ components: ComponentsOptions;
283
+ /** 配置 */
284
+ config: ConfigOptions;
285
+ /** 国际化 */
286
+ i18n: I18nOptions;
287
+ /** 路由 */
288
+ router: RouterOptions;
438
289
  /** 本地缓存 */
439
- storage: StorageResults;
440
- /** 全局状态管理 */
441
- store: StoreResults;
442
- /** 可复用组件 */
443
- ui: UIResults;
290
+ storage: StorageOptions;
291
+ /** 状态管理 */
292
+ store: StoreOptions;
444
293
  }
445
- type PluginName = keyof PluginOptions;
446
- interface Plugin<K extends PluginName> {
447
- /** 插件名字 */
448
- name: K;
294
+ interface SDKPlugin {
295
+ /** 插件名称 */
296
+ name: string;
449
297
  /** 插件安装方法 */
450
- install(sdk: SdkResult, options?: PluginOptions[K]): void;
298
+ install(sdk: SDKInstance, options?: Record<string, any>): void;
451
299
  /** 插件配置项 */
452
- options?: PluginOptions[K];
453
- }
454
- interface SdkBase {
455
- /** SDK 名称 */
456
- name: string;
457
- /** 插件列表 */
458
- _plugins: Map<string, any>;
459
- /** 挂载sdk - 主应用挂载 SDK 到 Window */
460
- mount(name: string): void;
461
- /** 继承sdk - 微应用从 Window 上继承 SDK */
462
- extend(name: string): void;
463
- /** 使用插件 */
464
- use<K extends PluginName>(plugin: Plugin<K>, options?: PluginOptions[K]): this;
300
+ options?: Record<string, any>;
465
301
  }
466
- type SdkResult = SdkBase & PluginResults;
302
+ type SDKPluginOptions = Record<string, any | ((...args: any[]) => any)>;
303
+ type SDKInstance = SDKCore & SDKPlugins;
467
304
  //#endregion
468
305
  //#region src/core/index.d.ts
469
- declare class Sdk implements SdkResult {
470
- name: SdkResult['name'];
471
- _plugins: SdkResult['_plugins'];
472
- api: SdkResult['api'];
473
- app: SdkResult['app'];
474
- client: SdkResult['client'];
475
- config: SdkResult['config'];
476
- i18n: SdkResult['i18n'];
477
- storage: SdkResult['storage'];
478
- store: SdkResult['store'];
479
- ui: SdkResult['ui'];
480
- constructor();
306
+ /** SDK */
307
+ declare class SDKCore {
308
+ /** 名称 */
309
+ name?: string;
310
+ /** 插件列表 */
311
+ _plugins: Map<string, any>;
312
+ /**
313
+ * 将实例挂载到 Window 对象上
314
+ * @param name - 要挂载的属性名
315
+ */
481
316
  mount(name: string): void;
317
+ /**
318
+ * 继承实例, 从 Window 对象上获取指定名称的实例并合并属性
319
+ * @param name - 要从 Window 对象上获取的实例名称
320
+ * @throws 当指定的 SDK 实例不存在时抛出错误
321
+ */
482
322
  extend(name: string): void;
483
- use<K extends keyof PluginOptions>(plugin: Plugin<K>, options?: PluginOptions[K]): this;
323
+ /**
324
+ * 使用插件
325
+ * @param plugin - 插件对象 {@link SDKPlugin}
326
+ * @param options - 插件选项
327
+ */
328
+ use(plugin: SDKPlugin, options?: SDKPlugin['options']): this;
484
329
  }
485
- /**
486
- * sdk 实例
487
- */
488
- declare const sdk: Sdk;
489
- //#endregion
490
- //#region src/hooks/useUserInfo.d.ts
491
- /** 用户信息 */
492
- declare const useUserInfo: () => {
493
- userInfo: UserInfo;
494
- setUserInfo: (userInfo: UserInfo) => void;
495
- resetUserInfo: () => void;
496
- };
497
- //#endregion
498
- //#region src/hooks/useIntl.d.ts
499
- /**
500
- * React Intl Universal
501
- * - 如果项目不使用 React Compiler, 可以直接使用 sdk.i18n.intl
502
- * - 不要解构使用, const { get } = useIntl() 会报错
503
- * @example const intl = useIntl(); intl.get(key).d(defaultValue)
504
- */
505
- declare const useIntl: () => {
506
- determineLocale: typeof react_intl_universal0.determineLocale;
507
- formatHTMLMessage: typeof react_intl_universal0.formatHTMLMessage;
508
- formatMessage: typeof react_intl_universal0.formatMessage;
509
- get: typeof react_intl_universal0.get;
510
- getHTML: typeof react_intl_universal0.getHTML;
511
- getInitOptions: typeof react_intl_universal0.getInitOptions;
512
- init: typeof react_intl_universal0.init;
513
- load: typeof react_intl_universal0.load;
514
- formatList: typeof react_intl_universal0.formatList;
515
- formatParentheses: typeof react_intl_universal0.formatParentheses;
516
- getColon: typeof react_intl_universal0.getColon;
517
- formatNumber: typeof react_intl_universal0.formatNumber;
518
- };
519
- //#endregion
520
- //#region src/hooks/useCrumb.d.ts
521
- /**
522
- * 获取面包屑
523
- * @see https://reactrouter.com/6.30.3/hooks/use-matches
524
- */
525
- declare const useCrumb: () => any[];
526
- //#endregion
527
- //#region src/hooks/usePermission.d.ts
528
- /**
529
- * 判断是否有权限
530
- * @param code 权限code (默认为当前路由)
531
- */
532
- declare const usePermission: (code?: string) => boolean;
330
+ /** 创建 SDK 实例 */
331
+ declare const sdk: SDKInstance;
533
332
  //#endregion
534
- export { type ApiOptions, type ApiResults, type AppOptions, type AppResults, type ClientOptions, type ClientResults, type ConfigOptions, type ConfigResults, type I18nOptions, type I18nResults, SdkApiPlugin, SdkAppPlugin, SdkClientPlugin, SdkConfigPlugin, SdkI18nPlugin, SdkStoragePlugin, SdkStorePlugin, SdkUIPlugin, type StorageOptions, type StorageResults, type StoreOptions, type StoreResults, type UIOptions, type UIResults, sdk, useCrumb, useIntl, usePermission, useUserInfo };
333
+ export { type ApiOptions, type AppOptions, type ComponentsOptions, type ConfigOptions, type I18nOptions, LocaleProps, type RouterOptions, SDKApiPlugin, SDKAppPlugin, SDKComponentsPlugin, SDKConfigPlugin, SDKCore, SDKI18nPlugin, SDKInstance, SDKPlugin, SDKPluginOptions, SDKPlugins, SDKRouterPlugin, SDKStoragePlugin, SDKStorePlugin, type StorageOptions, type StoreOptions, type StoreProps, type StoreSlice, ThemeProps, UserInfo, sdk };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{createStore as e,useStore as t}from"zustand";import{useShallow as n}from"zustand/shallow";import{Suspense as r,createElement as i,memo as a,useEffect as o,useMemo as s,useState as c}from"react";import{Navigate as ee,Outlet as l,RouterProvider as te,createBrowserRouter as ne,useLocation as u,useMatches as d,useNavigate as f}from"react-router-dom";import{Fragment as p,jsx as m,jsxs as h}from"react/jsx-runtime";import g from"axios";import{Button as _,ConfigProvider as re,Flex as v,Form as y,Input as b,message as x,theme as S}from"antd";import C from"react-intl-universal";import{subscribeWithSelector as w}from"zustand/middleware";import T from"@ant-design/pro-layout";import{loadMicroApp as E,registerMicroApps as ie,start as ae}from"qiankun";const D=new class{name;_plugins;api;app;client;config;i18n;storage;store;ui;constructor(){this.name=``,this._plugins=new Map}mount(e){if(window[e])throw Error(`The SDK already exists - ${e}`);console.log(`%c SDK mounted:`,`color: pink; font-weight: bold;`,e,D),this.name=e;let t=new Proxy(this,{get:(e,t,n)=>e?Reflect.get(e,t,n):null,set:()=>(console.error(`The SDK cannot be modified.`),!1),deleteProperty:()=>(console.error(`The SDK cannot be deleted.`),!1)});window[this.name]=t}extend(e){if(!window[e])throw Error(`The SDK not found - ${e}`);console.log(`%c SDK extended:`,`color: pink; font-weight: bold;`,e),Object.assign(this,window[e])}use(e,t){let{name:n,install:r}=e;if(!n)throw Error(`${n} plugin has no name`);if(typeof r!=`function`)throw Error(`${n} plugin is not a function`);return r(this,t),this._plugins.set(n,{...e,options:t}),this}},O=()=>{let[e,r,i]=t(D.store,n(e=>[e.userInfo,e.setUserInfo,e.resetUserInfo]));return{userInfo:e,setUserInfo:r,resetUserInfo:i}};function k(e){return e==null||typeof e!=`object`&&typeof e!=`function`}function A(e){return ArrayBuffer.isView(e)&&!(e instanceof DataView)}function oe(e){return Object.getOwnPropertySymbols(e).filter(t=>Object.prototype.propertyIsEnumerable.call(e,t))}function se(e){return e==null?e===void 0?`[object Undefined]`:`[object Null]`:Object.prototype.toString.call(e)}function j(e,t,n,r=new Map,i=void 0){let a=i?.(e,t,n,r);if(a!==void 0)return a;if(k(e))return e;if(r.has(e))return r.get(e);if(Array.isArray(e)){let t=Array(e.length);r.set(e,t);for(let a=0;a<e.length;a++)t[a]=j(e[a],a,n,r,i);return Object.hasOwn(e,`index`)&&(t.index=e.index),Object.hasOwn(e,`input`)&&(t.input=e.input),t}if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp){let t=new RegExp(e.source,e.flags);return t.lastIndex=e.lastIndex,t}if(e instanceof Map){let t=new Map;r.set(e,t);for(let[a,o]of e)t.set(a,j(o,a,n,r,i));return t}if(e instanceof Set){let t=new Set;r.set(e,t);for(let a of e)t.add(j(a,void 0,n,r,i));return t}if(typeof Buffer<`u`&&Buffer.isBuffer(e))return e.subarray();if(A(e)){let t=new(Object.getPrototypeOf(e)).constructor(e.length);r.set(e,t);for(let a=0;a<e.length;a++)t[a]=j(e[a],a,n,r,i);return t}if(e instanceof ArrayBuffer||typeof SharedArrayBuffer<`u`&&e instanceof SharedArrayBuffer)return e.slice(0);if(e instanceof DataView){let t=new DataView(e.buffer.slice(0),e.byteOffset,e.byteLength);return r.set(e,t),M(t,e,n,r,i),t}if(typeof File<`u`&&e instanceof File){let t=new File([e],e.name,{type:e.type});return r.set(e,t),M(t,e,n,r,i),t}if(typeof Blob<`u`&&e instanceof Blob){let t=new Blob([e],{type:e.type});return r.set(e,t),M(t,e,n,r,i),t}if(e instanceof Error){let t=structuredClone(e);return r.set(e,t),t.message=e.message,t.name=e.name,t.stack=e.stack,t.cause=e.cause,t.constructor=e.constructor,M(t,e,n,r,i),t}if(e instanceof Boolean){let t=new Boolean(e.valueOf());return r.set(e,t),M(t,e,n,r,i),t}if(e instanceof Number){let t=new Number(e.valueOf());return r.set(e,t),M(t,e,n,r,i),t}if(e instanceof String){let t=new String(e.valueOf());return r.set(e,t),M(t,e,n,r,i),t}if(typeof e==`object`&&ce(e)){let t=Object.create(Object.getPrototypeOf(e));return r.set(e,t),M(t,e,n,r,i),t}return e}function M(e,t,n=e,r,i){let a=[...Object.keys(t),...oe(t)];for(let o=0;o<a.length;o++){let s=a[o],c=Object.getOwnPropertyDescriptor(e,s);(c==null||c.writable)&&(e[s]=j(t[s],s,n,r,i))}}function ce(e){switch(se(e)){case`[object Arguments]`:case`[object Array]`:case`[object ArrayBuffer]`:case`[object DataView]`:case`[object Boolean]`:case`[object Date]`:case`[object Float32Array]`:case`[object Float64Array]`:case`[object Int8Array]`:case`[object Int16Array]`:case`[object Int32Array]`:case`[object Map]`:case`[object Number]`:case`[object Object]`:case`[object RegExp]`:case`[object Set]`:case`[object String]`:case`[object Symbol]`:case`[object Uint8Array]`:case`[object Uint8ClampedArray]`:case`[object Uint16Array]`:case`[object Uint32Array]`:return!0;default:return!1}}function N(e){return j(e,void 0,e,new Map,void 0)}function P(e){if(!e||typeof e!=`object`)return!1;let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype||Object.getPrototypeOf(t)===null?Object.prototype.toString.call(e)===`[object Object]`:!1}function le(e){return e===`__proto__`}function F(e,t){let n=Object.keys(t);for(let r=0;r<n.length;r++){let i=n[r];if(le(i))continue;let a=t[i],o=e[i];I(a)&&I(o)?e[i]=F(o,a):Array.isArray(a)?e[i]=F([],a):P(a)?e[i]=F({},a):(o===void 0||a!==void 0)&&(e[i]=a)}return e}function I(e){return P(e)||Array.isArray(e)}const ue=()=>s(()=>N(D.i18n.intl),[t(D.store,e=>e.locale)]),L=()=>s(()=>D.client.matches,[u()]).filter(e=>!!e.handle?.crumb).map(e=>e.handle.crumb(e.data)),R=e=>{let{requestId:t,url:n,method:r,params:i,data:a}=e;return t||`${r}:${n}?${JSON.stringify(i)}&${JSON.stringify(a)}`},z=e=>{let t=R(e),n=D.api.controllers.get(t);n&&(n.abort(),D.api.controllers.delete(t))},B={beforeLoad:[async e=>{console.log(`[LifeCycle] before load %c%s`,`color: green;`,e.name)}],beforeMount:[async e=>{console.log(`[LifeCycle] before mount %c%s`,`color: green;`,e.name)}],afterUnmount:[async e=>{console.log(`[LifeCycle] after unmount %c%s`,`color: green;`,e.name)}]},V=e=>{let t=e.storage.getTheme();if(t)return t;let n=e.config?.theme;if(n)return n;let r=window.matchMedia(`(prefers-color-scheme: dark)`);return r.matches&&r.matches?`dark`:`light`},H=e=>e.storage.getLocale()||e.config?.locale||navigator.language||`zh-CN`,U=e=>D.app.permissions?.includes?.(e),de=e=>{let t=new Map,n=W(e,t);return{microApps:[...t.values()],menuData:n}},W=(e,t)=>!e||e?.length===0?[]:e.map(e=>{let n=null,{locale:r,path:i,icon:a,component:o,routeAttr:s,children:c}=e;if(s){let e={};try{e=JSON.parse(s)}catch(e){console.error(`Sdk: initData - Subapp routeAttr error: `,e)}let{name:r,rootId:i,...a}=e,o={...a,name:r,container:`#${i}`,props:{sdk:D},loader:e=>D.store.getState().setMicroAppLoading(e)};t.set(r,o),n=D.ui.renderComponent(`Microapp`,{name:r,rootId:i})}else if(o===`Microapp`)n=D.ui.renderComponent(`Microapp`);else if(o===`Outlet`)n=m(l,{});else{let e=U(i);n=D.ui.renderComponent(e?o:`NoPermission`)}return{...e,key:`${i}_${a}_${r}`,element:n,children:W(c,t),handle:{crumb:(t={})=>({...e,...t})}}}),G=e=>{let t=`/`;return!e||e.length===0?t:(t=e?.[0]?.path,e?.[0]?.children&&e?.[0]?.children.length>0&&(t=G(e?.[0]?.children)),t)},fe=e=>s(()=>U(e||D.client.location.pathname),[u().pathname,e]);var pe=class{instance;constructor(e={}){this.instance=g.create(e),this.defaultRequestInterceptor(),this.defaultResponseInterceptor()}defaultRequestInterceptor(){this.instance.interceptors.request.use(function(e){if(e?.isCancelRequest){let t=R(e);z(e);let n=new AbortController;D.api.controllers.set(t,n),e.requestId=t,e.signal=n.signal}let t=D.storage.getToken();return e.headers.lang=D.config.locale,e.headers.Authorization=t,F(e.headers,D.api.config.headers||{}),e},function(e){return console.error(`Sdk: defaultRequestInterceptor - 请求错误`),Promise.reject(e)})}defaultResponseInterceptor(){this.instance.interceptors.response.use(function(e){let{data:t,config:n}=e,{isOriginalData:r,isShowFailMsg:i,isCancelRequest:a}=n,{code:o,msg:s}=t;return o!==0&&(i&&x.error(s),console.error(`Sdk: defaultResponseInterceptor - Response error: `,n.url,s),o==20041&&D.app.pageToLogin()),a&&D.api.controllers.delete(n.requestId),r?e:e.data},function(e){let{response:t,config:n}=e,{isShowFailMsg:r}=n;if(g.isCancel(e))return Promise.reject(e);if(t){let{status:e,data:n,statusText:i}=t;r&&x.error(n.msg||i),e==401&&D.app.pageToLogin()}else r&&x.error(`请求超时或服务器异常,请检查网络或联系管理员`),console.error(`Sdk: defaultResponseInterceptor - Request error:`,n.url,e);return Promise.reject(e)})}getInstance(){return this.instance}};const me={name:`api`,install(e,t={}){let n={baseURL:`/`,timeout:0,...t.config},r=t?.instance||new pe(n).getInstance();e.api=F({config:n,controllers:new Map,instance:null,request:(e,t={})=>r.request({url:e,isOriginalData:!1,isShowFailMsg:!0,isCancelRequest:!0,...t}),getUserInfoApi:()=>e.api.request(`/getUserInfo`,{method:`GET`}),getRoutesApi:()=>e.api.request(`/routes`,{method:`GET`}),loginApi:()=>e.api.request(`/login`,{method:`POST`})},t)}},he={name:`app`,install(e,t={}){e.app=F({menuData:[],allRoutes:[],microApps:[],microAppsInstance:new Map,user:null,permissions:[],roles:[],settings:{},initData:null,clearData:()=>{e.app.menuData=[],e.app.allRoutes=e.app.allRoutes.filter(e=>e.path!==`/`),e.app.microApps=[],e.app.microAppsInstance.forEach(e=>e.unmount()),e.app.microAppsInstance.clear(),e.store.getState().resetUserInfo()},pageToLogin:()=>{e.storage.clearToken();let t=window.location.pathname||`/`,n=e.config.loginPath,r=t===n?n:`${n}?redirect=${encodeURIComponent(t)}`;e.config.qiankunMode===`router`?window.location.replace(r):(e.app.clearData(),e.client.navigate(r,{replace:!0}))},getRedirectPath:()=>{let t=e.config.defaultPath;if(t)return t;let n=new URLSearchParams(window.location.search);return decodeURIComponent(n.get(`redirect`)||``)||`/`}},t)}},K=`client`,ge={name:K,install(e,t={}){e[K]=F({location:null,navigate:null,matches:null},t)}},q=`config`,_e={name:q,install(e,t={}){e[q]=F({env:{},qiankunMode:`router`,theme:null,locale:null,loginPath:`/login`,defaultPath:``,customRoutes:[],antdConfig:{},proLayoutConfig:{title:`Demo`}},t)}},J=`i18n`,ve={name:J,install(e,t={}){e[J]=F({intl:C,intlConfig:{},loadLocale:e=>void 0},t)}},Y=`storage`,ye={name:Y,install(e,t={}){e[Y]=F({localeKey:`locale`,themeKey:`theme`,tokenKey:`token`,getLocale:()=>localStorage.getItem(e.storage.localeKey),setLocale:t=>{localStorage.setItem(e.storage.localeKey,t)},clearLocale:()=>{localStorage.removeItem(e.storage.localeKey)},getTheme:()=>localStorage.getItem(e.storage.themeKey),setTheme:t=>{localStorage.setItem(e.storage.themeKey,t)},clearTheme:()=>{localStorage.removeItem(e.storage.themeKey)},getToken:()=>localStorage.getItem(e.storage.tokenKey),setToken:t=>{localStorage.setItem(e.storage.tokenKey,t)},clearToken:()=>{localStorage.removeItem(e.storage.tokenKey)}},t)}},be=(e,t)=>({locale:null,setLocale:t=>{e(()=>({locale:t})),D.config.locale=t,D.storage.setLocale(t),document.documentElement.setAttribute(`lang`,t);let n=D.i18n.intlConfig;C.init({currentLocale:t,locales:n});try{let e=D.i18n.loadLocale?.(t)||void 0;F(D.config.antdConfig,{locale:e})}catch(e){console.error(`Sdk: createLocaleSlice - sdk.i18n.loadLocale error:`,e)}}}),xe=(e,t)=>({microAppLoading:!1,setMicroAppLoading:t=>e(()=>({microAppLoading:t}))}),{defaultAlgorithm:Se,darkAlgorithm:Ce}=S,we=(e,t)=>({theme:null,setTheme:t=>{e(()=>({theme:t})),D.config.theme=t,D.storage.setTheme(t),document.documentElement.setAttribute(`theme`,t);let n=t===`light`?Se:Ce;F(D.config.antdConfig,{theme:{algorithm:n}})}}),X={user:{},permissions:[],roles:[],settings:{}},Te=(e,t)=>({userInfo:X,setUserInfo:t=>{e(()=>({userInfo:t})),D.app={...D.app,...t}},resetUserInfo:()=>{e(()=>({userInfo:X}))}}),Ee=e()(w((...e)=>({...be(...e),...xe(...e),...we(...e),...Te(...e)}))),Z=`store`,De={name:Z,install(e,t={}){e[Z]=Ee}},Oe=()=>{let e=f(),n=u(),i=d(),a=t(D.store,e=>e.locale),o=i[i.length-1]?.handle?.crumb()||{},s=JSON.parse(o?.routeAttr||`{}`)?.noLayout,c=t=>{e(t.path)};return m(T,{locale:a,formatMessage:({id:e,defaultMessage:t})=>D.i18n.intl.get(e).d(t),location:n,menuItemRender:(e,t)=>m(`div`,{onClick:()=>c(e),children:t}),onMenuHeaderClick:()=>{e(`/`)},onPageChange:e=>{if(!D.app.user||Object.keys(D.app.user).length===0)return D.app.pageToLogin()},...s&&{headerRender:!1,footerRender:!1,menuRender:!1},menu:{request:async()=>D.app.menuData||[],...D.config.proLayoutConfig.menu},...D.config.proLayoutConfig,children:m(r,{fallback:D.ui.renderComponent(`Loading`,{isSuspense:!0}),children:m(l,{})})})},{useToken:Q}=S,ke=({isInitData:e=!1,isSuspense:t=!1,isMicroApp:n=!1})=>{let{token:r}=Q();return m(`div`,{style:e?{width:`100%`,height:`100%`,display:`flex`,alignItems:`center`,justifyContent:`center`,background:r.colorBgContainer,color:r.colorText}:{},children:`Loading...`})},{useToken:Ae}=S,je=()=>{let{token:e}=Ae(),[t,n]=c(!1);return m(v,{style:{width:`100%`,height:`100%`,background:e.colorBgContainer},justify:`center`,align:`center`,children:h(y,{labelCol:{span:8},labelAlign:`left`,wrapperCol:{span:16},style:{maxWidth:600},initialValues:{username:`admin`,password:`admin`},onFinish:async e=>{try{n(()=>!0);let t=await D.api.loginApi(e);n(()=>!1);let r=t?.data?.token||``;if(!r)return;D.storage.setToken(r);let i=D.app.getRedirectPath();D.config.qiankunMode===`load`?(D.client.navigate(i,{replace:!0}),D.app.initData?.()):window.location.replace(i)}catch(e){console.log(`Sdk: Login - handleFinish: `,e),n(()=>!1)}},autoComplete:`off`,children:[m(y.Item,{label:`用户名`,name:`username`,rules:[{required:!0,message:`请输入用户名!`}],children:m(b,{})}),m(y.Item,{label:`密码`,name:`password`,rules:[{required:!0,message:`请输入密码!`}],children:m(b.Password,{})}),m(y.Item,{noStyle:!0,children:m(_,{block:!0,type:`primary`,htmlType:`submit`,loading:t,children:`登录`})})]})})},$=({children:e})=>{let t=u(),n=d(),r=f();return D.client.location=t,D.client.matches=n,D.client.navigate=r,e},Me=()=>{let e=D.config.loginPath,i=D.config.customRoutes,a=D.config.qiankunMode===`router`,l=e=>D.ui.renderComponent(`Loading`,e),u=D.ui.renderComponent(`Layout`),d=D.ui.renderComponent(`Login`),f=D.ui.renderComponent(`NotFound`),p=[{path:e,element:d},{path:`*`,element:f},...i].map(e=>({...e,element:m($,{children:e.element})})),[h,g]=c(!1),[_,v]=c(p),[y,b,x,S,C]=t(D.store,n(e=>[e.locale,e.setLocale,e.theme,e.setTheme,e.setUserInfo])),w=s(()=>N(D.config.antdConfig),[y,x]),T=(e,t)=>{S(e||V(D)),b(t||H(D))},E=async()=>{try{g(()=>!0);let[{data:e={}},{data:t=[]}]=await Promise.all([D.api.getUserInfoApi(),D.api.getRoutesApi()]);g(()=>!1),C(e);let{theme:n,locale:r}=e?.settings||{};T(n,r);let{microApps:i=[],menuData:o=[]}=de(t);a&&(ie(i,B),ae());let s=G(o),c=[...p,{path:`/`,element:m(ee,{to:s,replace:!0})},{path:`/`,element:m($,{children:u}),children:o,errorElement:f}];v(c),D.app={...D.app,allRoutes:c,microApps:i,menuData:o}}catch(e){console.error(e),g(()=>!1)}};return o(()=>{D.app.initData=E,D.app.allRoutes=p;let t=D.config.customRoutes?.map(e=>e.path),n=window.location.pathname;[e,...t]?.includes(n)?T():E()},[]),m(re,{...w,children:m(r,{fallback:l({isSuspense:!0}),children:h?l({isInitData:!0}):m(te,{router:ne(_,{basename:`/`}),future:{v7_startTransition:!1}})})})};var Ne=a(({name:e,rootId:r})=>{let[i,a]=t(D.store,n(e=>[e.microAppLoading,e.setMicroAppLoading]));return o(()=>{if(!e||D.config.qiankunMode!==`load`)return;let t=D.app.microAppsInstance.get(e);if(t)t?.mount();else{let n=D.app.microApps.find(t=>t.name===e);if(!n)return;a(!0),t=E(n,{},B),t?.mountPromise?.finally(()=>{a(!1)}),D.app.microAppsInstance.set(e,t)}return()=>{t?.unmount()}},[e]),h(p,{children:[i&&D.ui.renderComponent(`Loading`,{isMicroApp:!0}),m(`main`,{id:r})]})});const Pe=()=>m(`div`,{children:`无权限`}),{useToken:Fe}=S,Ie=()=>{let{token:e}=Fe();return m(`div`,{style:{width:`100%`,height:`100%`,display:`flex`,alignItems:`center`,justifyContent:`center`,background:e.colorBgContainer},children:`找不到页面`})},Le={name:`ui`,install(e,t={}){e.ui=F({Layout:Oe,Loading:ke,Login:je,Mainapp:Me,Microapp:Ne,NotFound:Ie,NoPermission:Pe,setComponent:(t,n)=>{if(!t){console.error(`Sdk: SdkUIPlugin - component cannot be empty`);return}let r=n||t.displayName||t.name;if(!r){console.error(`Sdk: SdkUIPlugin - Component name cannot be empty`);return}e.ui[r]=t},getComponent:t=>t?e.ui[t]:(console.error(`Sdk: SdkUIPlugin - Component name cannot be empty`),null),renderComponent:(t,n={})=>{let r=e.ui.getComponent(t);return r?i(r,n):(console.error(`Sdk: SdkUIPlugin - Component ${t} not found`),null)}},t)}};export{me as SdkApiPlugin,he as SdkAppPlugin,ge as SdkClientPlugin,_e as SdkConfigPlugin,ve as SdkI18nPlugin,ye as SdkStoragePlugin,De as SdkStorePlugin,Le as SdkUIPlugin,D as sdk,L as useCrumb,ue as useIntl,fe as usePermission,O as useUserInfo};
1
+ import{Suspense as e,createElement as t,memo as n,useEffect as r,useState as i}from"react";import{Outlet as a,useLocation as o,useNavigate as s}from"react-router-dom";import{Fragment as c,jsx as l,jsxs as u}from"react/jsx-runtime";import{loadMicroApp as d}from"qiankun";import{createStore as f,useStore as p}from"zustand";import{useShallow as m}from"zustand/shallow";import{subscribeWithSelector as h}from"zustand/middleware";var g=class{name;_plugins=new Map;mount(e){this.name=e,window[e]=this}extend(e){let t=window[e];if(!t)throw Error(`SDK "${e}" not found`);Object.assign(this,t)}use(e,t={}){let{name:n,install:r}=e;if(!n)throw Error(`SDK - The plugin requires a name`);if(typeof r!=`function`)throw Error(`SDK - The plugin "${n}" requires an install function`);return r(this,t),this._plugins.set(n,{...e,options:t}),this}};const _=new g,v={name:`api`,install(e,t={}){e.api=t}};function y(e){if(!e||typeof e!=`object`)return!1;let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype||Object.getPrototypeOf(t)===null?Object.prototype.toString.call(e)===`[object Object]`:!1}function b(e){return e===`__proto__`}function x(e,t){let n=Object.keys(t);for(let r=0;r<n.length;r++){let i=n[r];if(b(i))continue;let a=t[i],o=e[i];S(a)&&S(o)?e[i]=x(o,a):Array.isArray(a)?e[i]=x([],a):y(a)?e[i]=x({},a):(o===void 0||a!==void 0)&&(e[i]=a)}return e}function S(e){return y(e)||Array.isArray(e)}const C={name:`app`,install(e,t={}){e.app=x({menuData:[],allRoutes:[],microApps:[],microAppsInstance:new Map,user:null,permissions:[],settings:{},getDefaultLocale(){return e.storage.getItem(e.storage.localeKey)||e.config?.locale||navigator.language||`zh-CN`},getDefaultTheme(){let t=e.storage.getItem(e.storage.themeKey);if(t)return t;let n=e.config?.theme;if(n)return n;let r=window.matchMedia(`(prefers-color-scheme: dark)`);return r.matches&&r.matches?`dark`:`light`},getRedirectPath(){let t=e.config.defaultPath;if(t)return t;let n=new URLSearchParams(window.location.search),r=e.config.redirectField||`redirect`;return decodeURIComponent(n.get(r)||``)||`/`},initData:()=>{},pageToLogin(){e.storage.removeItem(e.storage.tokenKey),e.app.user=null,e.app.permissions=[],e.app.settings={};let t=location.pathname,n=e.config.loginPath,r=e.config.redirectField||`redirect`,i=t===n?n:`${n}?${r}=${encodeURIComponent(t||`/`)}`;e.router.navigate(i,{replace:!0}),e.app.unmountMicroApp()},unmountMicroApp(t){t?t.forEach(t=>{let n=e.app.microAppsInstance.get(t);n&&(n.unmount(),e.app.microAppsInstance.delete(t),e.app.microApps=e.app.microApps.filter(e=>e.name!==t))}):(e.app.microAppsInstance.forEach(e=>e.unmount()),e.app.microAppsInstance.clear(),e.app.microApps=[])}},t)}},w={layout:{height:`100vh`,display:`flex`,flexDirection:`column`,background:`#f5f7fb`},header:{height:64,padding:`0 24px`,background:`#fff`,borderBottom:`1px solid #e5e7eb`,display:`flex`,alignItems:`center`,justifyContent:`space-between`,boxShadow:`0 2px 8px rgba(0,0,0,0.04)`,zIndex:10},logo:{fontSize:20,fontWeight:700,color:`#1677ff`,cursor:`pointer`,userSelect:`none`},logoutBtn:{height:36,padding:`0 16px`,border:`none`,borderRadius:8,background:`#ff4d4f`,color:`#fff`,cursor:`pointer`,transition:`0.2s`},main:{flex:1,display:`flex`,overflow:`hidden`},sidebar:{width:240,padding:16,background:`#fff`,borderRight:`1px solid #e5e7eb`,overflowY:`auto`},content:{flex:1,padding:20,overflowY:`auto`},contentCard:{minHeight:`100%`,padding:24,background:`#fff`,borderRadius:16,boxShadow:`0 2px 12px rgba(0,0,0,0.04)`},menuItem:{marginBottom:4},menuTitle:{height:42,padding:`0 14px`,display:`flex`,alignItems:`center`,borderRadius:10,color:`#1f2937`,cursor:`pointer`,transition:`0.2s`},menuChildren:{marginLeft:12,paddingLeft:12,borderLeft:`1px solid #e5e7eb`}},T=({items:e=[]})=>{let t=o(),n=s();return e?.length?e.map(e=>{let{key:r,name:i,path:a,children:o,hideInMenu:s=!1}=e;if(s)return null;let c=o&&o.filter(e=>!e.hideInMenu).length>0,d=t.pathname===a,f=e=>{d||(e.currentTarget.style.background=`#f3f4f6`)},p=e=>{d||(e.currentTarget.style.background=`transparent`)},m=()=>{c||n(a)};return u(`div`,{style:w.menuItem,children:[l(`div`,{style:{...w.menuTitle,...d?{background:`#e8f3ff`,color:`#1677ff`}:{},...c?{cursor:`not-allowed`}:{}},onMouseEnter:f,onMouseLeave:p,onClick:m,children:i}),c&&l(`div`,{style:w.menuChildren,children:l(T,{items:o})})]},r)}):null},E=()=>{let t=s(),n=o(),i=()=>{t(`/`)},c=()=>{_.app.pageToLogin()};return r(()=>{if(!_.app.user||Object.keys(_.app.user).length===0)return c();t(n.pathname)},[n.pathname]),u(`div`,{style:w.layout,children:[u(`header`,{style:w.header,children:[l(`div`,{style:w.logo,onClick:i,children:`Logo`}),l(`button`,{style:w.logoutBtn,onClick:c,onMouseEnter:e=>{e.currentTarget.style.opacity=`0.75`},onMouseLeave:e=>{e.currentTarget.style.opacity=`1`},children:`退出登录`})]}),u(`div`,{style:w.main,children:[l(`aside`,{style:w.sidebar,children:l(T,{items:_.app.menuData||[]})}),l(`main`,{style:w.content,children:l(`div`,{style:w.contentCard,children:l(e,{fallback:_.components.renderComponent(`Loading`,{isSuspense:!0}),children:l(a,{})})})})]})]})},D=({isInitData:e=!1,isSuspense:t=!1,isMicroApp:n=!1})=>l(`div`,{style:e?{width:`100%`,height:`100%`,display:`flex`,alignItems:`center`,justifyContent:`center`}:{},children:`Loading...`}),O={page:{height:`100vh`,display:`flex`,flexDirection:`column`,justifyContent:`center`,alignItems:`center`,gap:16},btn:{height:36,padding:`0 60px`,border:`none`,borderRadius:8,background:`#1677ff`,color:`#fff`,cursor:`pointer`,transition:`0.2s`}},k=()=>{let[e,t]=i(!1);return u(`div`,{style:O.page,children:[l(`h2`,{children:`欢迎登录系统`}),l(`button`,{style:O.btn,onClick:async()=>{if(e)return;t(()=>!0),await new Promise(e=>setTimeout(()=>e(!0),500)),t(()=>!1),_.storage.setItem(_.storage.tokenKey,`123456`);let n=_.app.getRedirectPath();_.router.navigate(n,{replace:!0}),await _.app.initData?.()},onMouseEnter:e=>{e.currentTarget.style.opacity=`0.75`},onMouseLeave:e=>{e.currentTarget.style.opacity=`1`},children:e?`登录中...`:`登录`})]})},A={beforeLoad:[async e=>{console.log(`[LifeCycle] before load %c%s`,`color: green;`,e.name)}],beforeMount:[async e=>{console.log(`[LifeCycle] before mount %c%s`,`color: green;`,e.name)}],afterUnmount:[async e=>{console.log(`[LifeCycle] after unmount %c%s`,`color: green;`,e.name)}]};var j=n(({name:e,rootId:t})=>{let[n,i]=p(_.store,m(e=>[e.microAppLoading,e.setMicroAppLoading]));return r(()=>{if(!e)return;let t=_.app.microAppsInstance.get(e);if(t)t?.mount();else{let n=_.app.microApps.find(t=>t.name===e);if(!n)return;i(!0),t=d(n,{},A),t?.mountPromise?.finally(()=>{i(!1)}),_.app.microAppsInstance.set(e,t)}return()=>{t?.unmount()}},[e]),u(c,{children:[n&&_.components.renderComponent(`Loading`,{isMicroApp:!0}),l(`main`,{id:t})]})});const M=`components`,N={name:M,install(e,n={}){e[M]=x({Layout:E,Loading:D,Login:k,Microapp:j,setComponent:(t,n)=>{if(!t){console.error(`SDKComponentsPlugin - Component cannot be empty`);return}let r=n||t.displayName||t.name;if(!r){console.error(`SDKComponentsPlugin - Component name cannot be empty`);return}e.components[r]=t},getComponent:t=>t?e.components[t]:(console.error(`SDKComponentsPlugin - Component name cannot be empty`),null),renderComponent:(n,r={})=>{let i=e.components.getComponent(n);return i?t(i,r):(console.error(`SDKComponentsPlugin - Component ${n} not found`),null)}},n)}},P=`config`,F={name:P,install(e,t){e[P]=x({env:{},theme:``,locale:``,loginPath:`/login`,defaultPath:``,redirectField:`redirect`},t)}},I=`i18n`,L={name:I,install(e,t){e[I]=x({},t)}},R=`router`,z={name:R,install(e,t){e[R]=x({location:null,navigate:null,matches:null},t)}},B=`storage`,V={name:B,install(e,t){e[B]=x({localeKey:`locale`,themeKey:`theme`,tokenKey:`token`,setItem(e,t){localStorage.setItem(e,t)},getItem(e){return localStorage.getItem(e)},removeItem(e){localStorage.removeItem(e)},clear(){localStorage.clear()}},t)}},H=(e,t)=>({locale:``,setLocale:t=>{e(()=>({locale:t})),_.config.locale=t,_.storage.setItem(_.storage.localeKey,t),document.documentElement.setAttribute(`lang`,t)}}),U=(e,t)=>({microAppLoading:!1,setMicroAppLoading:t=>e(()=>({microAppLoading:t}))}),W=(e,t)=>({theme:``,setTheme:t=>{e(()=>({theme:t})),_.config.theme=t,_.storage.setItem(_.storage.themeKey,t),document.documentElement.setAttribute(`theme`,t)}}),G={user:{},permissions:[],settings:{}},K=(e,t)=>({userInfo:G,setUserInfo:t=>{e(()=>({userInfo:t})),_.app={..._.app,...t}},resetUserInfo:()=>{e(()=>({userInfo:G}))}}),q=e=>f()(h((...t)=>({...H(...t),...U(...t),...W(...t),...K(...t),...Object.values(e||{}).reduce((e,n)=>({...e,...n(...t)}),{})}))),J=`store`,Y={name:J,install(e,t={}){e[J]=q(t)}};export{v as SDKApiPlugin,C as SDKAppPlugin,N as SDKComponentsPlugin,F as SDKConfigPlugin,g as SDKCore,L as SDKI18nPlugin,z as SDKRouterPlugin,V as SDKStoragePlugin,Y as SDKStorePlugin,_ as sdk};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zxiaosi/sdk",
3
- "version": "0.5.2",
3
+ "version": "1.0.0-beta.2",
4
4
  "description": "A micro frontend kit",
5
5
  "keywords": [
6
6
  "micro frontend",
@@ -18,8 +18,11 @@
18
18
  "dist"
19
19
  ],
20
20
  "type": "module",
21
- "main": "./dist/index.js",
22
21
  "types": "dist/index.d.ts",
22
+ "exports": {
23
+ ".": "./dist/index.js",
24
+ "./package.json": "./package.json"
25
+ },
23
26
  "publishConfig": {
24
27
  "access": "public",
25
28
  "registry": "https://registry.npmjs.org/"
@@ -29,20 +32,18 @@
29
32
  "dev": "tsdown --watch"
30
33
  },
31
34
  "dependencies": {
32
- "@ant-design/pro-layout": "^7.22.7",
33
- "axios": "^1.13.6",
34
- "qiankun": "^2.10.16",
35
- "react-intl-universal": "^2.13.4"
35
+ "axios": "^1.13.6"
36
36
  },
37
37
  "devDependencies": {
38
+ "@tsdown/css": "^0.22.1",
38
39
  "@types/react": "^18.3.12",
39
40
  "@types/react-dom": "^18.3.1",
40
- "es-toolkit": "^1.45.1",
41
- "tsdown": "^0.21.4",
41
+ "es-toolkit": "^1.47.0",
42
+ "tsdown": "^0.22.1",
42
43
  "typescript": "^5.9.3"
43
44
  },
44
45
  "peerDependencies": {
45
- "antd": "^5.29.0 || ^6.0.0",
46
+ "qiankun": "^2.10.16",
46
47
  "react": "^18.3.1 || >=19.0.0",
47
48
  "react-dom": "^18.3.1 || >=19.0.0",
48
49
  "react-router-dom": "^6.30.0",