@zxiaosi/sdk 1.0.0-beta.1 → 1.0.0-beta.3

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,259 +1,194 @@
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 { MicroApp, ObjectType, RegistrableApp } from "qiankun";
5
- import { AxiosInstance, AxiosRequestConfig, CreateAxiosDefaults } from "axios";
2
+ import { Location, NavigateFunction, UIMatch } from "react-router-dom";
3
+ import { StateCreator } from "zustand";
6
4
 
7
- //#region src/plugins/api/http.d.ts
8
- interface ApiRequestOption extends AxiosRequestConfig {
9
- /** 请求唯一key(默认自动生成) */
10
- requestId?: string;
11
- /** 是否取消重复请求 */
12
- isCancelRequest?: boolean;
13
- /** 是否需要原始数据 */
14
- isOriginalData?: boolean;
15
- /** 是否显示错误信息 */
16
- isShowFailMsg?: boolean;
17
- }
18
- //#endregion
19
5
  //#region src/plugins/api/index.d.ts
20
- interface ApiOptions {
21
- /** Axios配置 */
22
- config?: CreateAxiosDefaults;
23
- /** 取消请求控制器 */
24
- controllers?: Map<string, AbortController>;
25
- /**
26
- * 自定义请求实例
27
- * - 替换 SDK 内置的请求实例
28
- * @example instance = axios.create(options)
29
- */
30
- instance?: AxiosInstance;
31
- /**
32
- * 获取用户信息
33
- * {@link UserInfo}
34
- * @example { code: 0, data: { user: {}, permissions: [], roles: [], settings: {} } }
35
- */
36
- getUserInfoApi?(): Promise<any>;
37
- /**
38
- * 获取路由数据
39
- * {@link RouteObject}
40
- * @example { code: 0, data: [{path: '/', name: '首页', component: 'Home'}] }
41
- */
42
- getRoutesApi?(): Promise<any>;
43
- /**
44
- * 登录接口
45
- * @example { code: 0, data: { token: 'xxxx' } }
46
- */
47
- loginApi?(params: any): Promise<any>;
48
- }
49
- interface ApiResults extends Required<ApiOptions> {
50
- /**
51
- * 请求
52
- * @param url 请求地址
53
- * @param options 自定义配置项
54
- */
55
- request(url: string, options?: ApiRequestOption): Promise<any>;
56
- }
6
+ interface ApiOptions {}
57
7
  /**
58
8
  * 请求插件
59
- * - 详情参考 {@link ApiOptions} {@link ApiResults}
60
- * - 内置了请求, 通过 sdk.api.request 发起请求
61
- * - 可通过外部传入 instance 自定义请求实例
62
- * - 预置了获取用户信息, 获取路由接口, 以便组件使用
63
- * @example sdk.api.request('/getTemp', { method: 'POST', ... })
64
- * @example sdk.api.request('/getTemp', { method: 'POST', isOriginalData: true }) // 返回原始数据
65
- * @example sdk.api.request('/getTemp', { method: 'POST', isShowFailMsg: false }) // 不显示错误信息
66
- * @example sdk.api.request('/getTemp', { method: 'POST', isCancelRequest: false }) // 不自动取消重复请求
67
9
  */
68
- declare const SdkApiPlugin: Plugin<'api'>;
10
+ declare const SDKApiPlugin: SDKPlugin<'api'>;
69
11
  //#endregion
70
12
  //#region src/plugins/app/index.d.ts
71
13
  interface AppOptions {
72
- /** 菜单数据 */
73
- menuData?: any[];
74
- /** 所有路由信息 */
75
- allRoutes?: RouteObject[];
76
- /** 微应用信息 */
77
- microApps?: RegistrableApp<ObjectType>[];
78
- /** 微应用实例 */
79
- microAppsInstance?: Map<string, MicroApp>;
80
14
  /** 用户信息 */
81
- user?: UserInfo['user'];
15
+ user: UserInfo['user'];
16
+ /** 菜单数据 */
17
+ menus: any[];
82
18
  /** 用户权限 */
83
- permissions?: UserInfo['permissions'];
84
- /** 用户角色 */
85
- roles?: UserInfo['roles'];
19
+ permissions: UserInfo['permissions'];
86
20
  /** 用户设置 */
87
- settings?: UserInfo['settings'];
88
- }
89
- interface AppResults extends Required<AppOptions> {
21
+ settings: UserInfo['settings'];
22
+ /** 微应用信息 */
23
+ microApps: any[];
24
+ /** 微应用实例 */
25
+ microAppsInstance: Map<string, any>;
90
26
  /**
91
- * 初始化数据
92
- * - sdk.config.qiankunMode = 'load' 时, 登录时用
27
+ * 初始化数据(登录跳转不刷新页面用)
93
28
  */
94
- initData(): void;
29
+ initData?(): void | Promise<void>;
95
30
  /**
96
- * 清空数据
97
- * - sdk.config.qiankunMode = 'load' 时, 登出时用
31
+ * 手动加载微应用(与 Qiankun 一致)
98
32
  */
99
- clearData(): void;
33
+ loadMicroApp?(app: LoadableApp, configuration?: LoadMicroAppConfiguration, lifeCycles?: MicroAppLifeCycles): MicroAppInstance;
100
34
  /**
101
- * 跳转登录页
35
+ * 获取国际化默认值
36
+ * - 1. 本地缓存 `sdk.storage.getItem(sdk.storage.localeKey)`
37
+ * - 2. sdk中国际化 `sdk.config.locale`
38
+ * - 3. 浏览器语言 `navigator.language`
39
+ * - 4. 默认 `zh-CN`
102
40
  */
103
- pageToLogin(): void;
41
+ getDefaultLocale(): LocaleProps;
42
+ /**
43
+ * 获取主题默认值
44
+ * - 1. 本地缓存 `sdk.storage.getItem(sdk.storage.themeKey)`
45
+ * - 2. sdk中主题 `sdk.config.theme`
46
+ * - 3. 系统主题 `window.matchMedia('(prefers-color-scheme: dark)').matches`
47
+ * - 4. 默认 `light`
48
+ */
49
+ getDefaultTheme(): ThemeProps;
104
50
  /**
105
51
  * 获取重定向路径
52
+ * - 1. 优先使用指定值 `sdk.config.defaultPath`
53
+ * - 2. 其次使用重定向的值 `sdk.config.redirectField`
54
+ * - 3. 最后使用菜单中第一项 `/`
106
55
  */
107
56
  getRedirectPath(): string;
57
+ /**
58
+ * 跳转登录页
59
+ * - 1. 清除 Token
60
+ * - 2. 清除用户信息
61
+ * - 3. 获取当前页路由
62
+ * - 4. 清空微应用
63
+ */
64
+ pageToLogin(): void;
65
+ /**
66
+ * 卸载微应用
67
+ * - 默认卸载所有微应用
68
+ * - 传入名称数组则卸载指定微应用
69
+ */
70
+ unmountMicroApp(names?: string[]): void;
108
71
  }
109
72
  /**
110
- * 项目信息
111
- * - 详情参考 {@link AppOptions} {@link AppResults}
112
- * - 菜单信息 sdk.app.menuData
113
- * - 路由信息 sdk.app.allRoutes
114
- * - 微应用信息 sdk.app.microApps
115
- * - 用户信息 sdk.app.user
116
- * - 用户权限 sdk.app.permissions
117
- * - 用户角色 sdk.app.roles
118
- * - 用户设置 sdk.app.settings
73
+ * 应用插件
74
+ *
75
+ * @example
76
+ * sdk.use(SDKAppPlugin, { menus: [...] }).mount('xxx');
77
+ * sdk.app.unmountMicroApp();
119
78
  */
120
- declare const SdkAppPlugin: Plugin<'app'>;
79
+ declare const SDKAppPlugin: SDKPlugin<'app'>;
121
80
  //#endregion
122
- //#region src/plugins/client/index.d.ts
123
- interface ClientOptions {}
124
- interface ClientResults extends Required<ClientOptions> {
125
- /** 主应用 location */
126
- location: Location;
127
- /** 路由匹配(用于面包屑) */
128
- matches: UIMatch[];
129
- /** 主应用navigate(解决微应用跳转问题) */
130
- navigate: NavigateFunction;
81
+ //#region src/plugins/components/index.d.ts
82
+ interface ComponentsOptions {
83
+ /** 组件 */
84
+ [key: string]: any;
85
+ /**
86
+ * 设置组件
87
+ * @param component 组件
88
+ * @param name 组件名称
89
+ */
90
+ setComponent(component: ComponentType, name?: string): void;
91
+ /**
92
+ * 获取组件
93
+ * @param name 组件名称
94
+ */
95
+ getComponent(name: string): ComponentType | null;
96
+ /**
97
+ * 渲染组件
98
+ * @param name 组件名称
99
+ */
100
+ renderComponent(name: string, props?: any): ReactElement | null;
131
101
  }
132
102
  /**
133
- * 全局路由信息
134
- * - 详情参考 {@link ClientOptions} {@link ClientResults}
135
- * - 路由信息 sdk.client.location
136
- * - 路由跳转 sdk.client.navigate
137
- * - 面包屑信息 sdk.client.matches
103
+ * 组件插件
104
+ *
105
+ * @example
106
+ * sdk.use(SDKComponentsPlugin).mount('xxx');
107
+ * sdk.components.setComponent(组件, '组件名称');
108
+ * sdk.components.renderComponent('组件名称', props);
138
109
  */
139
- declare const SdkClientPlugin: Plugin<'client'>;
110
+ declare const SDKComponentsPlugin: SDKPlugin<'components'>;
140
111
  //#endregion
141
112
  //#region src/plugins/config/index.d.ts
142
113
  interface ConfigOptions {
143
114
  /** 环境变量(主应用共享给微应用变量) */
144
- env?: Record<string, any>;
115
+ env: ObjectType;
145
116
  /** 主题 */
146
- theme?: ThemeProps;
117
+ theme: ThemeProps;
147
118
  /** 语言 */
148
- locale?: LocaleProps;
149
- /**
150
- * Qiankun模式(切换模式后请重新打开页面)
151
- * - 'router': 基于路由模式
152
- * - 登录时,刷新页面,会自动调用 getUserInfoApi、getRoutesApi 获取数据
153
- * - 拿到 routes 数据之后,需要 registerMicroApps 注册微应用 和 start 启动微应用
154
- * - 系统退出时,刷新页面,自动销毁 qiankun 声明周期和缓存的数据
155
- * - 系统登录和系统退出仅有一次刷新页面即可,为了销毁 qiankun 的声明周期,但数据需要手动加载或者清除
156
- * - 'load': 手动加载模式
157
- * - 登录时,不需刷新页面, 但需要手动调用 getUserInfoApi、getRoutesApi 获取数据
158
- * - 拿到 routes 数据之后,在 Microapp 组件中使用 loadMicroApp 手动加载微应用
159
- * - 系统退出时,不刷新页面,但需要手动销毁每个微应用,并清除缓存数据
160
- */
161
- qiankunMode?: 'router' | 'load';
119
+ locale: LocaleProps;
162
120
  /** 登录页路由 */
163
- loginPath?: string;
164
- /**
165
- * 登录后跳转的路由
166
- * - 优先使用指定值
167
- * - 其次使用重定向的值
168
- * - 最后使用菜单中第一项
169
- */
170
- defaultPath?: string;
171
- /**
172
- * 自定义路由信息
173
- * - 目前只支持最外层路由自定义
174
- * - 会合并到 sdk.app.allRoutes 中
175
- */
176
- customRoutes?: RouteObject[];
177
- [key: string]: any;
121
+ loginPath: string;
122
+ /** 登录后跳转的路由 */
123
+ defaultPath: string;
124
+ /** 重定向字段 */
125
+ redirectField: string;
126
+ /** 微应用生命周期 */
127
+ lifeCycles: MicroAppLifeCycles;
178
128
  }
179
- interface ConfigResults extends Required<ConfigOptions> {}
180
129
  /**
181
- * Sdk 配置信息
182
- * - 详情参考 {@link ConfigOptions} {@link ConfigResults}
183
- * - 配置 env 环境变量
184
- * - 配置 默认主题、语言
185
- * - 配置 Qiankun 模式
186
- * - 配置 默认登录路径、跳转路径、自定义路由
130
+ * 配置插件
131
+ *
132
+ * @example
133
+ * sdk.use(SDKConfigPlugin, { theme: 'light' }).mount('xxx');
134
+ * console.log(sdk.api.theme); // 'light'
187
135
  */
188
- declare const SdkConfigPlugin: Plugin<'config'>;
136
+ declare const SDKConfigPlugin: SDKPlugin<'config'>;
189
137
  //#endregion
190
138
  //#region src/plugins/i18n/index.d.ts
191
- interface I18nOptions {
192
- /**
193
- * Intl 实例
194
- * @example sdk.i18n.intl?.xxx
195
- */
196
- intl?: any;
197
- /**
198
- * 自定义语言包
199
- * @example
200
- * {
201
- * 'zh-CN': {
202
- * test: '测试国际化'
203
- * },
204
- * 'en-US': {
205
- * test: 'Test Intl'
206
- * }
207
- * }
208
- */
209
- intlConfig?: Record<string, any>;
139
+ interface I18nOptions {}
140
+ /**
141
+ * 国际化插件
142
+ *
143
+ * @example
144
+ * sdk.use(SDKI18nPlugin).mount('xxx');
145
+ */
146
+ declare const SDKI18nPlugin: SDKPlugin<'i18n'>;
147
+ //#endregion
148
+ //#region src/plugins/router/index.d.ts
149
+ interface RouterOptions {
150
+ /** 主应用 location */
151
+ location: Location;
152
+ /** 路由匹配(用于面包屑) */
153
+ matches: UIMatch[];
154
+ /** 主应用navigate(解决微应用跳转问题) */
155
+ navigate: NavigateFunction;
210
156
  }
211
- interface I18nResults extends Required<I18nOptions> {}
212
157
  /**
213
- * 多语言
214
- * - 详情参考 {@link I18nOptions} {@link I18nResults}
158
+ * 路由插件
159
+ *
160
+ * @example
161
+ * sdk.use(SDKI18nPlugin).mount('xxx');
162
+ * sdk.router.location; // 路由信息
163
+ * sdk.router.navigate; // 路由跳转
164
+ * sdk.router.matches; // 面包屑信息
215
165
  */
216
- declare const SdkI18nPlugin: Plugin<'i18n'>;
166
+ declare const SDKRouterPlugin: SDKPlugin<'router'>;
217
167
  //#endregion
218
168
  //#region src/plugins/storage/index.d.ts
219
169
  interface StorageOptions {
220
170
  /** 语言存储名称 */
221
- localeKey?: string;
171
+ localeKey: string;
222
172
  /** 主题存储名称 */
223
- themeKey?: string;
173
+ themeKey: string;
224
174
  /** Token存储名称 */
225
- tokenKey?: string;
226
- }
227
- interface StorageResults extends Required<StorageOptions> {
228
- /** 获取当前语言 */
229
- getLocale(): LocaleProps;
230
- /** 设置/切换语言 */
231
- setLocale(locale: LocaleProps): void;
232
- /** 清空语言 */
233
- clearLocale(): void;
234
- /** 获取当前主题 */
235
- getTheme(): ThemeProps;
236
- /** 设置/切换主题 */
237
- setTheme(theme: ThemeProps): void;
238
- /** 清空主题 */
239
- clearTheme(): void;
240
- /** 获取当前 Token */
241
- getToken(): string;
242
- /** 设置 Token */
243
- setToken(token: string): void;
244
- /** 清空 Token */
245
- clearToken(): void;
175
+ tokenKey: string;
176
+ /** 设置缓存 */
177
+ setItem(key: string, value: string): void;
178
+ /** 获取缓存 */
179
+ getItem(key: string): string | null;
180
+ /** 删除缓存 */
181
+ removeItem(key: string): void;
182
+ /** 清空缓存 */
183
+ clear(): void;
246
184
  }
247
185
  /**
248
- * 本地缓存
249
- * - 详情参考 {@link StorageOptions} {@link StorageResults}
250
- * - 配置 localStorage 变量名称
251
- * - 提供 语言、主题、Token 的 get、change、clear 方法
252
- * @example sdk.storage.getToken() // 获取 Token
253
- * @example sdk.storage.setTheme('dark') // 设置主题
254
- * @example sdk.storage.clearLocale() // 清空语言
186
+ * 本地缓存插件
187
+ *
188
+ * @example
189
+ * sdk.use(SDKStoragePlugin).mount('xxx');
255
190
  */
256
- declare const SdkStoragePlugin: Plugin<'storage'>;
191
+ declare const SDKStoragePlugin: SDKPlugin<'storage'>;
257
192
  //#endregion
258
193
  //#region src/plugins/store/createLocale.d.ts
259
194
  interface LocaleStoreProps {
@@ -294,199 +229,194 @@ interface UserInfoStoreProps {
294
229
  /** 创建用户信息切片 */
295
230
  //#endregion
296
231
  //#region src/plugins/store/index.d.ts
297
- type StoreOptions = LocaleStoreProps & MicroAppLoadingStoreProps & ThemeStoreProps & UserInfoStoreProps;
298
- type StoreResults = typeof globalStore;
232
+ interface StoreProps extends LocaleStoreProps, MicroAppLoadingStoreProps, ThemeStoreProps, UserInfoStoreProps {}
233
+ type StoreSlice<T = any> = StateCreator<T>;
234
+ /** 插件 options */
235
+ type StorePluginOptions = Record<string, StoreSlice>;
299
236
  /**
300
237
  * 创建 Store
301
- * - 这里单独声明变量, 主要是为了使用返回类型 StoreResults 🤔
238
+ * - 这里单独声明变量, 主要是为了使用返回类型 StoreOptions 🤔
302
239
  */
303
- declare const globalStore: Omit<_$zustand.StoreApi<StoreOptions>, "subscribe"> & {
240
+ declare const createGlobalStore: (options?: StorePluginOptions) => Omit<import("zustand").StoreApi<StoreProps>, "subscribe"> & {
304
241
  subscribe: {
305
- (listener: (selectedState: StoreOptions, previousSelectedState: StoreOptions) => void): () => void;
306
- <U>(selector: (state: StoreOptions) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: {
242
+ (listener: (selectedState: StoreProps, previousSelectedState: StoreProps) => void): () => void;
243
+ <U>(selector: (state: StoreProps) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: {
307
244
  equalityFn?: (a: U, b: U) => boolean;
308
245
  fireImmediately?: boolean;
309
246
  }): () => void;
310
247
  };
311
248
  };
249
+ type StoreOptions = ReturnType<typeof createGlobalStore>;
312
250
  /**
313
- * 全局状态管理
314
- * - 详情参考 {@link StoreOptions} {@link StoreResults}
315
- * - 此插件不会合并传入属性
251
+ * 状态管理插件
316
252
  * @example const setTheme = useStore(sdk.store, (state) => state.setTheme)
317
253
  * @example const { theme, setTheme } = useStore(sdk.store, useShallow((state) => { theme: state.theme, setTheme: state.setTheme }))
318
254
  * @example const [theme, setTheme] = useStore(sdk.store, useShallow((state) => [state.theme, state.setTheme]))
319
255
  * @example sdk.store?.getState()?.setTheme('light')
320
256
  * @example sdk.store.subscribe((state) => state.theme, (theme) => { console.log('theme', theme) }, { fireImmediately: true }) // fireImmediately 立即变更
321
257
  */
322
- declare const SdkStorePlugin: Plugin<'store'>;
323
- //#endregion
324
- //#region src/plugins/ui/index.d.ts
325
- interface UIOptions {
326
- /** 组件 */
327
- [key: string]: ComponentType | ((name: string) => ComponentType) | ((component: ComponentType, name?: string) => void);
328
- }
329
- interface UIResults extends Required<UIOptions> {
330
- /**
331
- * 设置组件
332
- * @param component 组件
333
- * @param name 组件名称
334
- */
335
- setComponent(component: ComponentType, name?: string): void;
336
- /**
337
- * 获取组件
338
- * @param name 组件名称
339
- */
340
- getComponent(name: string): ComponentType;
341
- /**
342
- * 渲染组件
343
- * @param name 组件名称
344
- */
345
- renderComponent(name: string, props?: any): ReactElement;
346
- }
347
- /**
348
- * 可复用组件
349
- * - 详情参考 {@link UIOptions} {@link UIResults}
350
- * - 内置了 Layout、Loading、Login、Mainapp、Microapp、NotFound 等组件, 可传入覆盖
351
- * - 组件共享
352
- * - 在主应用中, 可通过 sdk.use(SdkUIPlugin, { MyComponent }) 传入组件
353
- * - 在微应用中, 可通过 sdk.ui.renderComponent('MyComponent') 使用组件
354
- */
355
- declare const SdkUIPlugin: Plugin<'ui'>;
258
+ declare const SDKStorePlugin: SDKPlugin<'store'>;
356
259
  //#endregion
357
260
  //#region src/types.d.ts
261
+ type ObjectType = Record<string, any>;
358
262
  type ThemeProps = 'light' | 'dark' | (string & {});
359
263
  type LocaleProps = 'zh-CN' | 'en-US' | (string & {});
360
264
  interface UserInfo {
361
265
  /** 用户信息 */
362
266
  user?: any;
267
+ /** 菜单数据 */
268
+ menus?: any[];
363
269
  /** 用户权限 */
364
270
  permissions?: string[];
365
- /** 用户角色 */
366
- roles?: string[];
367
271
  /** 用户设置 */
368
272
  settings?: {
369
273
  theme?: ThemeProps;
370
274
  locale?: LocaleProps;
371
275
  };
372
276
  }
373
- interface PluginOptions {
374
- /** 全局请求 */
375
- api?: ApiOptions;
376
- /** 项目信息 */
377
- app?: AppOptions;
378
- /** 全局路由信息 */
379
- client?: ClientOptions;
380
- /** Sdk 配置信息 */
381
- config?: ConfigOptions;
382
- /** 多语言 */
383
- i18n?: I18nOptions;
384
- /** 本地缓存 */
385
- storage?: StorageOptions;
386
- /** 全局状态管理 */
387
- store?: StoreOptions;
388
- /** 可复用组件 */
389
- ui?: UIOptions;
277
+ declare global {
278
+ interface Window {
279
+ [key: string]: any;
280
+ }
390
281
  }
391
- interface PluginResults {
392
- /** 全局请求 */
393
- api: ApiResults;
394
- /** 项目信息 */
395
- app: AppResults;
396
- /** 全局路由信息 */
397
- client: ClientResults;
398
- /** Sdk 配置信息 */
399
- config: ConfigResults;
400
- /** 多语言 */
401
- i18n: I18nResults;
282
+ interface SDKPlugins {
283
+ /** 请求 */
284
+ api: ApiOptions;
285
+ /** 应用 */
286
+ app: AppOptions;
287
+ /** 组件 */
288
+ components: ComponentsOptions;
289
+ /** 配置 */
290
+ config: ConfigOptions;
291
+ /** 国际化 */
292
+ i18n: I18nOptions;
293
+ /** 路由 */
294
+ router: RouterOptions;
402
295
  /** 本地缓存 */
403
- storage: StorageResults;
404
- /** 全局状态管理 */
405
- store: StoreResults;
406
- /** 可复用组件 */
407
- ui: UIResults;
296
+ storage: StorageOptions;
297
+ /** 状态管理 */
298
+ store: StoreOptions;
408
299
  }
409
- type PluginName = keyof PluginOptions;
410
- interface Plugin<K extends PluginName> {
411
- /** 插件名字 */
412
- name: K;
300
+ interface SDKPlugin<K extends keyof SDKPlugins> {
301
+ /** 插件名称 */
302
+ name: string;
413
303
  /** 插件安装方法 */
414
- install(sdk: SdkResult, options?: PluginOptions[K]): void;
304
+ install(sdk: SDKInstance, options?: ObjectType): void;
415
305
  /** 插件配置项 */
416
- options?: PluginOptions[K];
417
- }
418
- interface SdkBase {
419
- /** SDK 名称 */
420
- name: string;
421
- /** 插件列表 */
422
- _plugins: Map<string, any>;
423
- /** 挂载sdk - 主应用挂载 SDK 到 Window */
424
- mount(name: string): void;
425
- /** 继承sdk - 微应用从 Window 上继承 SDK */
426
- extend(name: string): void;
427
- /** 使用插件 */
428
- use<K extends PluginName>(plugin: Plugin<K>, options?: PluginOptions[K]): this;
429
- }
430
- type SdkResult = SdkBase & PluginResults;
431
- //#endregion
432
- //#region src/core/index.d.ts
433
- declare class Sdk implements SdkResult {
434
- name: SdkResult['name'];
435
- _plugins: SdkResult['_plugins'];
436
- api: SdkResult['api'];
437
- app: SdkResult['app'];
438
- client: SdkResult['client'];
439
- config: SdkResult['config'];
440
- i18n: SdkResult['i18n'];
441
- storage: SdkResult['storage'];
442
- store: SdkResult['store'];
443
- ui: SdkResult['ui'];
444
- constructor();
445
- mount(name: string): void;
446
- extend(name: string): void;
447
- use<K extends keyof PluginOptions>(plugin: Plugin<K>, options?: PluginOptions[K]): this;
306
+ options?: Partial<SDKPlugins[K]> & ObjectType;
448
307
  }
308
+ type SDKInstance = SDKCore & SDKPlugins;
449
309
  /**
450
- * sdk 实例
310
+ * 微应用状态
451
311
  */
452
- declare const sdk: Sdk;
453
- //#endregion
454
- //#region src/hooks/useUserInfo.d.ts
455
- /** 用户信息 */
456
- declare const useUserInfo: () => {
457
- userInfo: UserInfo;
458
- setUserInfo: (userInfo: UserInfo) => void;
459
- resetUserInfo: () => void;
460
- };
461
- //#endregion
462
- //#region src/hooks/useIntl.d.ts
312
+ type MicroAppStatus = 'NOT_LOADED' | 'LOADING_SOURCE_CODE' | 'NOT_BOOTSTRAPPED' | 'BOOTSTRAPPING' | 'NOT_MOUNTED' | 'MOUNTING' | 'MOUNTED' | 'UPDATING' | 'UNMOUNTING' | 'UNLOADING' | 'SKIP_BECAUSE_BROKEN' | 'LOAD_ERROR';
463
313
  /**
464
- * React Intl Universal
465
- * - 如果项目不使用 React Compiler, 可以直接使用 sdk.i18n.intl
466
- * - 不要解构使用, const { get } = useIntl() 会报错
467
- * @example const intl = useIntl(); intl.get(key).d(defaultValue)
314
+ * 微应用配置
468
315
  */
469
- declare const useIntl: () => any;
470
- //#endregion
471
- //#region src/hooks/useCrumb.d.ts
316
+ type LoadableApp<T extends object = ObjectType> = {
317
+ /** 微应用唯一名称 */name: string; /** 微应用入口 */
318
+ entry: string | {
319
+ scripts?: string[];
320
+ styles?: string[];
321
+ html?: string;
322
+ }; /** 传递给微应用的数据 */
323
+ props?: T;
324
+ } & ({
325
+ /** 自定义函数 */render?: (props: {
326
+ appContent: string;
327
+ loading: boolean;
328
+ }) => any;
329
+ } | {
330
+ /**
331
+ * 容器节点
332
+ * @example '#root'
333
+ * @example document.querySelector('#root')
334
+ */
335
+ container: string | HTMLElement;
336
+ });
472
337
  /**
473
- * 获取面包屑
474
- * @see https://reactrouter.com/6.30.3/hooks/use-matches
338
+ * 生命周期函数
475
339
  */
476
- declare const useCrumb: () => any[];
477
- //#endregion
478
- //#region src/hooks/usePermission.d.ts
340
+ type LifeCycleFn = (app: LoadableApp, global: typeof window) => any;
341
+ /**
342
+ * 生命周期钩子
343
+ */
344
+ interface MicroAppLifeCycles {
345
+ beforeLoad?: LifeCycleFn | Array<LifeCycleFn>;
346
+ beforeMount?: LifeCycleFn | Array<LifeCycleFn>;
347
+ afterMount?: LifeCycleFn | Array<LifeCycleFn>;
348
+ beforeUnmount?: LifeCycleFn | Array<LifeCycleFn>;
349
+ afterUnmount?: LifeCycleFn | Array<LifeCycleFn>;
350
+ }
351
+ /**
352
+ * 加载配置
353
+ */
354
+ interface LoadMicroAppConfiguration {
355
+ /** 开启沙箱 */
356
+ sandbox?: boolean | {
357
+ strictStyleIsolation?: boolean;
358
+ experimentalStyleIsolation?: boolean;
359
+ };
360
+ /** 是否为单实例场景,单实例指的是同一时间只会渲染一个微应用 */
361
+ singular?: boolean | ((app: LoadableApp<any>) => Promise<boolean>);
362
+ /** 自定义的 fetch 方法 */
363
+ prefetch?: boolean | 'all' | string[] | ((apps: any[]) => {
364
+ criticalAppNames: string[];
365
+ minorAppsName: string[];
366
+ });
367
+ /** 指定部分特殊的动态加载的微应用资源(css/js) 不被 qiankun 劫持处理 */
368
+ excludeAssetFilter?: (url: string) => boolean;
369
+ fetch?: typeof window.fetch | {
370
+ fn?: typeof window.fetch;
371
+ autoDecodeResponse?: boolean;
372
+ };
373
+ getPublicPath?: (entry: any) => string;
374
+ getTemplate?: (tpl: string) => string;
375
+ postProcessTemplate?: (tplResult: any) => any;
376
+ urlRerouteOnly?: boolean;
377
+ autoStart?: boolean;
378
+ [key: string]: any;
379
+ }
479
380
  /**
480
- * 判断是否有权限
481
- * @param code 权限code (默认为当前路由)
381
+ * 微应用实例
482
382
  */
483
- declare const usePermission: (code?: string) => boolean;
383
+ interface MicroAppInstance<T extends object = ObjectType> {
384
+ mount(): Promise<null>;
385
+ unmount(): Promise<null>;
386
+ update?(customProps: Partial<T>): Promise<any>;
387
+ getStatus(): MicroAppStatus;
388
+ loadPromise: Promise<null>;
389
+ bootstrapPromise: Promise<null>;
390
+ mountPromise: Promise<null>;
391
+ unmountPromise: Promise<null>;
392
+ }
484
393
  //#endregion
485
- //#region src/hooks/useInitData.d.ts
486
- /** 初始化数据 */
487
- declare const useInitData: () => {
488
- loading: boolean;
489
- routes: RouteObject[];
490
- };
394
+ //#region src/core/index.d.ts
395
+ /** SDK */
396
+ declare class SDKCore {
397
+ /** 名称 */
398
+ name?: string;
399
+ /** 插件列表 */
400
+ _plugins: Map<string, any>;
401
+ /**
402
+ * 将实例挂载到 Window 对象上
403
+ * @param name - 要挂载的属性名
404
+ */
405
+ mount(name: string): void;
406
+ /**
407
+ * 继承实例, 从 Window 对象上获取指定名称的实例并合并属性
408
+ * @param name - 要从 Window 对象上获取的实例名称
409
+ * @throws 当指定的 SDK 实例不存在时抛出错误
410
+ */
411
+ extend(name: string): void;
412
+ /**
413
+ * 使用插件
414
+ * @param plugin - 插件对象 {@link SDKPlugin}
415
+ * @param options - 插件选项
416
+ */
417
+ use<K extends keyof SDKPlugins>(plugin: SDKPlugin<K>, options?: SDKPlugin<K>['options']): this;
418
+ }
419
+ /** 创建 SDK 实例 */
420
+ declare const sdk: SDKInstance;
491
421
  //#endregion
492
- 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, useInitData, useIntl, usePermission, useUserInfo };
422
+ export { type ApiOptions, type AppOptions, type ComponentsOptions, type ConfigOptions, type I18nOptions, LifeCycleFn, LoadMicroAppConfiguration, LoadableApp, LocaleProps, MicroAppInstance, MicroAppLifeCycles, MicroAppStatus, ObjectType, type RouterOptions, SDKApiPlugin, SDKAppPlugin, SDKComponentsPlugin, SDKConfigPlugin, SDKCore, SDKI18nPlugin, SDKInstance, SDKPlugin, SDKPlugins, SDKRouterPlugin, SDKStoragePlugin, SDKStorePlugin, type StorageOptions, type StoreOptions, type StoreProps, type StoreSlice, ThemeProps, UserInfo, sdk };
package/dist/index.js CHANGED
@@ -1,2 +1 @@
1
- import './style.css';
2
- 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 l,Outlet as u,useLocation as d,useMatches as f,useNavigate as p}from"react-router-dom";import{loadMicroApp as m,registerMicroApps as ee,start as te}from"qiankun";import{Fragment as h,jsx as g,jsxs as _}from"react/jsx-runtime";import v from"axios";import{subscribeWithSelector as y}from"zustand/middleware";const b=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,b),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}},x=()=>{let[e,r,i]=t(b.store,n(e=>[e.userInfo,e.setUserInfo,e.resetUserInfo]));return{userInfo:e,setUserInfo:r,resetUserInfo:i}};function S(e){return e==null||typeof e!=`object`&&typeof e!=`function`}function C(e){return ArrayBuffer.isView(e)&&!(e instanceof DataView)}function w(e){return Object.getOwnPropertySymbols(e).filter(t=>Object.prototype.propertyIsEnumerable.call(e,t))}function ne(e){return e==null?e===void 0?`[object Undefined]`:`[object Null]`:Object.prototype.toString.call(e)}function T(e,t,n,r=new Map,i=void 0){let a=i?.(e,t,n,r);if(a!==void 0)return a;if(S(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]=T(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,T(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(T(a,void 0,n,r,i));return t}if(typeof Buffer<`u`&&Buffer.isBuffer(e))return e.subarray();if(C(e)){let t=new(Object.getPrototypeOf(e)).constructor(e.length);r.set(e,t);for(let a=0;a<e.length;a++)t[a]=T(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),E(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),E(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),E(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,E(t,e,n,r,i),t}if(e instanceof Boolean){let t=new Boolean(e.valueOf());return r.set(e,t),E(t,e,n,r,i),t}if(e instanceof Number){let t=new Number(e.valueOf());return r.set(e,t),E(t,e,n,r,i),t}if(e instanceof String){let t=new String(e.valueOf());return r.set(e,t),E(t,e,n,r,i),t}if(typeof e==`object`&&D(e)){let t=Object.create(Object.getPrototypeOf(e));return r.set(e,t),E(t,e,n,r,i),t}return e}function E(e,t,n=e,r,i){let a=[...Object.keys(t),...w(t)];for(let o=0;o<a.length;o++){let s=a[o],c=Object.getOwnPropertyDescriptor(e,s);(c==null||c.writable)&&(e[s]=T(t[s],s,n,r,i))}}function D(e){switch(ne(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 O(e){return T(e,void 0,e,new Map,void 0)}function k(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 A(e){return e===`__proto__`}function j(e,t){let n=Object.keys(t);for(let r=0;r<n.length;r++){let i=n[r];if(A(i))continue;let a=t[i],o=e[i];M(a)&&M(o)?e[i]=j(o,a):Array.isArray(a)?e[i]=j([],a):k(a)?e[i]=j({},a):(o===void 0||a!==void 0)&&(e[i]=a)}return e}function M(e){return k(e)||Array.isArray(e)}const re=()=>s(()=>O(b.i18n.intl),[t(b.store,e=>e.locale)]),ie=()=>s(()=>b.client.matches,[d()]).filter(e=>!!e.handle?.crumb).map(e=>e.handle.crumb(e.data)),ae=e=>s(()=>{let t=e||b.client.location.pathname;return b.app.permissions?.includes?.(t)},[d().pathname,e]),N=e=>{let{requestId:t,url:n,method:r,params:i,data:a}=e;return t||`${r}:${n}?${JSON.stringify(i)}&${JSON.stringify(a)}`},oe=e=>{let t=N(e),n=b.api.controllers.get(t);n&&(n.abort(),b.api.controllers.delete(t))},P={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)}]},F=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`},I=e=>e.storage.getLocale()||e.config?.locale||navigator.language||`zh-CN`,L=e=>{let t=new Map,n=R(e,t);return{microApps:[...t.values()],menuData:n}},R=(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:b},loader:e=>b.store.getState().setMicroAppLoading(e)};t.set(r,o),n=b.ui.renderComponent(`Microapp`,{name:r,rootId:i})}else n=o===`Microapp`?b.ui.renderComponent(`Microapp`):o===`Outlet`?g(u,{}):b.ui.renderComponent(o);return{...e,key:`${i}_${a}_${r}`,element:n,children:R(c,t),handle:e}}),z=e=>{let t=`/`;return!e||e.length===0?t:(t=e?.[0]?.path,e?.[0]?.children&&e?.[0]?.children.length>0&&(t=z(e?.[0]?.children)),t)},B=({children:e})=>{let t=d(),n=f(),r=p();return b.client.location=t,b.client.matches=n,b.client.navigate=r,e},V=()=>{let e=b.config.loginPath,r=b.config.customRoutes,i=b.config.qiankunMode===`router`,a=b.ui.renderComponent(`Layout`),s=b.ui.renderComponent(`Login`),u=b.ui.renderComponent(`NotFound`),d=[{path:e,element:s},{path:`*`,element:u},...r].map(e=>({...e,element:g(B,{children:e.element})})),[f,p]=c(!1),[m,h]=c(d),[_,v,y]=t(b.store,n(e=>[e.setLocale,e.setTheme,e.setUserInfo])),x=(e,t)=>{v(e||F(b)),_(t||I(b))},S=async()=>{try{p(()=>!0);let[{data:e={}},{data:t=[]}]=await Promise.all([b.api.getUserInfoApi(),b.api.getRoutesApi()]);p(()=>!1),y(e);let{theme:n,locale:r}=e?.settings||{};x(n,r);let{microApps:o=[],menuData:s=[]}=L(t);i&&(ee(o,P),te());let c=z(s),f=[...d,{path:`/`,element:g(l,{to:c,replace:!0})},{path:`/`,element:g(B,{children:a}),children:s,errorElement:u}];h(f),b.app={...b.app,allRoutes:f,microApps:o,menuData:s}}catch(e){console.error(e),p(()=>!1)}};return o(()=>{b.app.initData=S,b.app.allRoutes=d;let t=b.config.customRoutes?.map(e=>e.path),n=window.location.pathname;[e,...t]?.includes(n)?x():S()},[]),{loading:f,routes:m}};var H=class{instance;constructor(e={}){this.instance=v.create(e),this.defaultRequestInterceptor(),this.defaultResponseInterceptor()}defaultRequestInterceptor(){this.instance.interceptors.request.use(function(e){if(e?.isCancelRequest){let t=N(e);oe(e);let n=new AbortController;b.api.controllers.set(t,n),e.requestId=t,e.signal=n.signal}let t=b.storage.getToken();return e.headers.lang=b.config.locale,e.headers.Authorization=t,j(e.headers,b.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&&(console.error(`Sdk: defaultResponseInterceptor - Response error: `,n.url,s),o==20041&&b.app.pageToLogin()),a&&b.api.controllers.delete(n.requestId),r?e:e.data},function(e){let{response:t,config:n}=e,{isShowFailMsg:r}=n;if(v.isCancel(e))return Promise.reject(e);if(t){let{status:e,data:n,statusText:r}=t;e==401&&b.app.pageToLogin()}else console.error(`Sdk: defaultResponseInterceptor - Request error:`,n.url,e);return Promise.reject(e)})}getInstance(){return this.instance}};const U={name:`api`,install(e,t={}){let n={baseURL:`/`,timeout:0,...t.config},r=t?.instance||new H(n).getInstance();e.api=j({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)}},W={name:`app`,install(e,t={}){e.app=j({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)}},G=`client`,K={name:G,install(e,t={}){e[G]=j({location:null,navigate:null,matches:null},t)}},q=`config`,se={name:q,install(e,t={}){e[q]=j({env:{},qiankunMode:`router`,theme:null,locale:null,loginPath:`/login`,defaultPath:``,customRoutes:[]},t)}},J=`i18n`,ce={name:J,install(e,t={}){e[J]=j({intl:{},intlConfig:{}},t)}},Y=`storage`,le={name:Y,install(e,t={}){e[Y]=j({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)}},ue=(e,t)=>({locale:null,setLocale:t=>{e(()=>({locale:t})),b.config.locale=t,b.storage.setLocale(t),document.documentElement.setAttribute(`lang`,t)}}),de=(e,t)=>({microAppLoading:!1,setMicroAppLoading:t=>e(()=>({microAppLoading:t}))}),fe=(e,t)=>({theme:null,setTheme:t=>{e(()=>({theme:t})),b.config.theme=t,b.storage.setTheme(t),document.documentElement.setAttribute(`theme`,t)}}),X={user:{},permissions:[],roles:[],settings:{}},pe=(e,t)=>({userInfo:X,setUserInfo:t=>{e(()=>({userInfo:t})),b.app={...b.app,...t}},resetUserInfo:()=>{e(()=>({userInfo:X}))}}),me=e()(y((...e)=>({...ue(...e),...de(...e),...fe(...e),...pe(...e)}))),Z=`store`,he={name:Z,install(e,t={}){e[Z]=me}},Q=({items:e=[]})=>{let t=d(),n=p();return!e||e.length===0?null:e.map(e=>{let{key:r,name:i,path:a,locale:o,children:s,hideInMenu:c=!1}=e,l=!0;return s&&s.length>0&&s.filter(e=>!e.hideInMenu).length>0&&(l=!1),_(`li`,{className:`sdk-layout-menu-item`,children:[g(`div`,{className:`sdk-layout-menu-item-title`,style:{...t.pathname===a?{background:`#e6f4ff`,color:`#1677ff`}:{},...c?{display:`none`}:{},cursor:l?`pointer`:`not-allowed`},onClick:()=>l?n(a):{},children:b.i18n.intl?.get?.(o)||i}),g(`ul`,{className:`sdk-layout-menu-sub`,children:g(Q,{items:s})})]},r)})},ge=()=>{let e=p(),t=d();return o(()=>{if(!b.app.user||Object.keys(b.app.user).length===0)return b.app.pageToLogin();e(t.pathname)},[t.pathname]),_(`div`,{className:`sdk-layout`,children:[_(`div`,{className:`sdk-layout-header`,children:[g(`div`,{onClick:()=>{e(`/`)},children:`Logo`}),g(`button`,{onClick:()=>{b.app.pageToLogin()},children:`退出登录`})]}),_(`div`,{className:`sdk-layout-content`,children:[g(`ul`,{className:`sdk-layout-menu`,children:g(Q,{items:b.app.menuData||[]})}),g(`div`,{className:`sdk-layout-outlet`,children:g(r,{fallback:b.ui.renderComponent(`Loading`,{isSuspense:!0}),children:g(u,{})})})]})]})},$=({isInitData:e=!1,isSuspense:t=!1,isMicroApp:n=!1})=>g(`div`,{className:`${e?`sdk-loading-init`:``}`,children:`Loading...`}),_e=()=>{let[e,t]=c(!1),[n,r]=c(`admin`),[i,a]=c(`123456`);return g(`div`,{className:`sdk-login`,children:_(`form`,{className:`sdk-login-form`,children:[_(`div`,{className:`sdk-login-form-group`,children:[g(`label`,{children:`用 户 名`}),_(`div`,{className:`sdk-login-form-group-input`,children:[g(`input`,{type:`text`,value:n,placeholder:`your@email.com 或 昵称`,onChange:e=>r(e.target.value)}),g(`div`,{children:n?``:`请输入用户名`})]})]}),_(`div`,{className:`sdk-login-form-group`,children:[g(`label`,{children:`密 码`}),_(`div`,{className:`sdk-login-form-group-input`,children:[g(`input`,{type:`password`,value:i,placeholder:`请输入密码`,onChange:e=>a(e.target.value)}),g(`div`,{children:i?``:`请输入密码`})]})]}),g(`button`,{className:`sdk-login-form-btn`,onClick:async r=>{if(!e){r.preventDefault();try{t(()=>!0);let e=await b.api.loginApi({userName:n,password:i});t(()=>!1);let r=e?.data?.token||``;if(!r)return;b.storage.setToken(r);let a=b.app.getRedirectPath();b.config.qiankunMode===`load`?(b.client.navigate(a,{replace:!0}),b.app.initData?.()):window.location.replace(a)}catch(e){console.log(`Sdk: Login - handleFinish: `,e),t(()=>!1)}}},children:e?`登录中...`:`登录`})]})})};var ve=a(({name:e,rootId:r})=>{let[i,a]=t(b.store,n(e=>[e.microAppLoading,e.setMicroAppLoading]));return o(()=>{if(!e||b.config.qiankunMode!==`load`)return;let t=b.app.microAppsInstance.get(e);if(t)t?.mount();else{let n=b.app.microApps.find(t=>t.name===e);if(!n)return;a(!0),t=m(n,{},P),t?.mountPromise?.finally(()=>{a(!1)}),b.app.microAppsInstance.set(e,t)}return()=>{t?.unmount()}},[e]),_(h,{children:[i&&b.ui.renderComponent(`Loading`,{isMicroApp:!0}),g(`main`,{id:r})]})});const ye=()=>g(`div`,{children:`无权限`}),be=()=>g(`div`,{className:`sdk-notfound`,children:`找不到页面`}),xe={name:`ui`,install(e,t={}){e.ui=j({Layout:ge,Loading:$,Login:_e,Microapp:ve,NotFound:be,NoPermission:ye,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{U as SdkApiPlugin,W as SdkAppPlugin,K as SdkClientPlugin,se as SdkConfigPlugin,ce as SdkI18nPlugin,le as SdkStoragePlugin,he as SdkStorePlugin,xe as SdkUIPlugin,b as sdk,ie as useCrumb,V as useInitData,re as useIntl,ae as usePermission,x 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{createStore as d,useStore as f}from"zustand";import{useShallow as p}from"zustand/shallow";import{subscribeWithSelector as m}from"zustand/middleware";var h=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 g=new h,_={name:`api`,install(e,t={}){e.api=t}};function v(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 y(e){return e===`__proto__`}function b(e,t){let n=Object.keys(t);for(let r=0;r<n.length;r++){let i=n[r];if(y(i))continue;let a=t[i],o=e[i];x(a)&&x(o)?e[i]=b(o,a):Array.isArray(a)?e[i]=b([],a):v(a)?e[i]=b({},a):(o===void 0||a!==void 0)&&(e[i]=a)}return e}function x(e){return v(e)||Array.isArray(e)}const S={name:`app`,install(e,t={}){e.app=b({user:null,menus:[],permissions:[],settings:{},microApps:[],microAppsInstance:new Map,initData:void 0,loadMicroApp:void 0,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)||``)||`/`},pageToLogin(){e.storage.removeItem(e.storage.tokenKey),e.store.getState().resetUserInfo();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)}},C={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`}},w=({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:C.menuItem,children:[l(`div`,{style:{...C.menuTitle,...d?{background:`#e8f3ff`,color:`#1677ff`}:{},...c?{cursor:`not-allowed`}:{}},onMouseEnter:f,onMouseLeave:p,onClick:m,children:i}),c&&l(`div`,{style:C.menuChildren,children:l(w,{items:o})})]},r)}):null},T=()=>{let t=s(),n=o(),i=()=>{t(`/`)},c=()=>{g.app.pageToLogin()};return r(()=>{if(!g.app.user||Object.keys(g.app.user).length===0)return c();t(n.pathname)},[n.pathname]),u(`div`,{style:C.layout,children:[u(`header`,{style:C.header,children:[l(`div`,{style:C.logo,onClick:i,children:`Logo`}),l(`button`,{style:C.logoutBtn,onClick:c,onMouseEnter:e=>{e.currentTarget.style.opacity=`0.75`},onMouseLeave:e=>{e.currentTarget.style.opacity=`1`},children:`退出登录`})]}),u(`div`,{style:C.main,children:[l(`aside`,{style:C.sidebar,children:l(w,{items:g.app.menus||[]})}),l(`main`,{style:C.content,children:l(`div`,{style:C.contentCard,children:l(e,{fallback:g.components.renderComponent(`Loading`,{isSuspense:!0}),children:l(a,{})})})})]})]})},E=({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...`}),D={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`}},O=()=>{let[e,t]=i(!1);return u(`div`,{style:D.page,children:[l(`h2`,{children:`欢迎登录系统`}),l(`button`,{style:D.btn,onClick:async()=>{if(e)return;t(()=>!0),await new Promise(e=>setTimeout(()=>e(!0),500)),t(()=>!1),g.storage.setItem(g.storage.tokenKey,`123456`);let n=g.app.getRedirectPath();g.router.navigate(n,{replace:!0}),await g.app.initData?.()},onMouseEnter:e=>{e.currentTarget.style.opacity=`0.75`},onMouseLeave:e=>{e.currentTarget.style.opacity=`1`},children:e?`登录中...`:`登录`})]})};var k=n(({name:e,rootId:t})=>{let[n,i]=f(g.store,p(e=>[e.microAppLoading,e.setMicroAppLoading]));return r(()=>{if(!e)return;let t=g.app.microAppsInstance.get(e);if(t)t?.mount();else{let n=g.app.microApps.find(t=>t.name===e);if(!n)return;i(!0),t=g.app.loadMicroApp?.(n,{},g.config.lifeCycles),t?.mountPromise?.finally(()=>{i(!1)}),g.app.microAppsInstance.set(e,t)}return()=>{t?.unmount()}},[e]),u(c,{children:[n&&g.components.renderComponent(`Loading`,{isMicroApp:!0}),l(`main`,{id:t})]})});const A=`components`,j={name:A,install(e,n={}){e[A]=b({Layout:T,Loading:E,Login:O,Microapp:k,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)}},M=`config`,N={name:M,install(e,t){e[M]=b({env:{},theme:``,locale:``,loginPath:`/login`,defaultPath:``,redirectField:`redirect`,lifeCycles:{beforeLoad:e=>console.log(`[LifeCycle] before load`,e.name),beforeMount:e=>console.log(`[LifeCycle] before mount`,e.name),afterUnmount:e=>console.log(`[LifeCycle] after unmount`,e.name)}},t)}},P=`i18n`,F={name:P,install(e,t){e[P]=b({},t)}},I=`router`,L={name:I,install(e,t){e[I]=b({location:null,navigate:null,matches:null},t)}},R=`storage`,z={name:R,install(e,t){e[R]=b({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)}},B=(e,t)=>({locale:``,setLocale:t=>{e(()=>({locale:t})),g.config.locale=t,g.storage.setItem(g.storage.localeKey,t),document.documentElement.setAttribute(`lang`,t)}}),V=(e,t)=>({microAppLoading:!1,setMicroAppLoading:t=>e(()=>({microAppLoading:t}))}),H=(e,t)=>({theme:``,setTheme:t=>{e(()=>({theme:t})),g.config.theme=t,g.storage.setItem(g.storage.themeKey,t),document.documentElement.setAttribute(`theme`,t)}}),U={user:null,menus:[],permissions:[],settings:{}},W=(e,t)=>({userInfo:U,setUserInfo:t=>{e(()=>({userInfo:t})),g.app={...g.app,...t}},resetUserInfo:()=>{e(()=>({userInfo:U})),g.app={...g.app,...U}}}),G=e=>d()(m((...t)=>({...B(...t),...V(...t),...H(...t),...W(...t),...Object.values(e||{}).reduce((e,n)=>({...e,...n(...t)}),{})}))),K=`store`,q={name:K,install(e,t={}){e[K]=G(t)}};export{_ as SDKApiPlugin,S as SDKAppPlugin,j as SDKComponentsPlugin,N as SDKConfigPlugin,h as SDKCore,F as SDKI18nPlugin,L as SDKRouterPlugin,z as SDKStoragePlugin,q as SDKStorePlugin,g as sdk};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zxiaosi/sdk",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.3",
4
4
  "description": "A micro frontend kit",
5
5
  "keywords": [
6
6
  "micro frontend",
@@ -21,8 +21,7 @@
21
21
  "types": "dist/index.d.ts",
22
22
  "exports": {
23
23
  ".": "./dist/index.js",
24
- "./package.json": "./package.json",
25
- "./style.css": "./dist/style.css"
24
+ "./package.json": "./package.json"
26
25
  },
27
26
  "publishConfig": {
28
27
  "access": "public",
@@ -32,39 +31,19 @@
32
31
  "build": "tsdown",
33
32
  "dev": "tsdown --watch"
34
33
  },
35
- "dependencies": {
36
- "axios": "^1.13.6"
37
- },
34
+ "dependencies": {},
38
35
  "devDependencies": {
39
- "@tsdown/css": "^0.21.6",
36
+ "@tsdown/css": "^0.22.3",
40
37
  "@types/react": "^18.3.12",
41
38
  "@types/react-dom": "^18.3.1",
42
- "es-toolkit": "^1.45.1",
43
- "tsdown": "^0.21.6",
39
+ "es-toolkit": "^1.47.1",
40
+ "tsdown": "^0.22.3",
44
41
  "typescript": "^5.9.3"
45
42
  },
46
43
  "peerDependencies": {
47
- "qiankun": "^2.10.16",
48
44
  "react": "^18.3.1 || >=19.0.0",
49
45
  "react-dom": "^18.3.1 || >=19.0.0",
50
46
  "react-router-dom": "^6.30.0",
51
47
  "zustand": "^5.0.0"
52
- },
53
- "peerDependenciesMeta": {
54
- "react": {
55
- "optional": false
56
- },
57
- "react-dom": {
58
- "optional": false
59
- },
60
- "react-router-dom": {
61
- "optional": false
62
- },
63
- "qiankun": {
64
- "optional": true
65
- },
66
- "zustand": {
67
- "optional": true
68
- }
69
48
  }
70
49
  }
package/dist/style.css DELETED
@@ -1 +0,0 @@
1
- :root[theme=dark]{--sdk-bg-color:#141414;--sdk-text-color:#ffffffd9;--sdk-border-color:#ffffff26}:root[theme=light]{--sdk-bg-color:#fff;--sdk-text-color:#000000d9;--sdk-border-color:#00000026}.sdk-layout{height:100vh;color:var(--sdk-text-color);background:var(--sdk-bg-color)}.sdk-layout-header{border-bottom:1px solid var(--sdk-border-color);box-sizing:border-box;justify-content:space-between;align-items:center;height:64px;padding:0 24px;line-height:64px;display:flex}.sdk-layout-content{height:calc(100vh - 64px);display:flex}.sdk-layout-menu{box-sizing:border-box;border-right:1px solid var(--sdk-border-color);width:200px;height:100%;margin:0;padding:8px;list-style:none}.sdk-layout-outlet{flex:1;padding:24px;overflow:auto}.sdk-layout-menu-item{margin-bottom:6px}.sdk-layout-menu-item-title{padding:8px 12px}.sdk-layout-menu-item-title:hover{color:#1677ff;cursor:pointer;background:#e6f4ff}.sdk-layout-menu-sub{margin:0;padding:0;list-style:none}.sdk-layout-menu-sub .sdk-layout-menu-item-title{margin-top:8px;padding-left:24px}.sdk-loading-init{width:100%;height:100%;color:var(--sdk-text-color);background-color:var(--sdk-bg-color);justify-content:center;align-items:center;display:flex}.sdk-login{width:100vw;height:100vh;color:var(--sdk-text-color);background:var(--sdk-bg-color);justify-content:center;align-items:center;display:flex}.sdk-login-form{border-radius:8px;flex-direction:column;width:300px;padding:23px;display:flex;box-shadow:0 0 10px #0000004d}.sdk-login-form-group{align-items:flex-start;margin-bottom:24px;display:flex}.sdk-login-form-group>label{width:70px}.sdk-login-form-group-input{flex:1}.sdk-login-form-group-input>input{box-sizing:border-box;width:230px;padding:4px 11px;font-size:16px}.sdk-login-form-btn{padding:4px 11px}.sdk-notfound{width:100%;height:100%;color:var(--sdk-text-color);background-color:var(--sdk-bg-color);justify-content:center;align-items:center;display:flex}