@skyfox2000/webui 1.3.21 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/lib/assets/modules/{baseLayout-Bz3oRE5o.js → baseLayout-DIEq13qJ.js} +6 -6
  2. package/lib/assets/modules/{file-upload-CmnCEJze.js → file-upload-DGuLxYSu.js} +2 -2
  3. package/lib/assets/modules/{index-CAqiqqdd.js → index-BPufvr-r.js} +134 -133
  4. package/lib/assets/modules/{index-CvQ24Mzh.js → index-CAtotNY6.js} +2 -2
  5. package/lib/assets/modules/{index-D0njzOzQ.js → index-DVzBH6zL.js} +1 -1
  6. package/lib/assets/modules/{menuTabs-NVgroqVO.js → menuTabs-DKvoic5x.js} +7 -7
  7. package/lib/assets/modules/{toolIcon-BuqRfX4F.js → toolIcon-Y7Lpx2Hb.js} +1 -1
  8. package/lib/assets/modules/{uploadList-B6MIYOtN.js → uploadList-Bh2SzZzX.js} +705 -700
  9. package/lib/assets/modules/{uploadList-KG6kpOaY.js → uploadList-DG33cpFl.js} +152 -146
  10. package/lib/components/common/icon/index.vue.d.ts +1 -1
  11. package/lib/es/AceEditor/index.js +3 -3
  12. package/lib/es/BasicLayout/index.js +2 -2
  13. package/lib/es/Error403/index.js +1 -1
  14. package/lib/es/Error404/index.js +1 -1
  15. package/lib/es/ExcelForm/index.js +5 -5
  16. package/lib/es/MenuLayout/index.js +2 -2
  17. package/lib/es/UploadForm/index.js +4 -4
  18. package/lib/stores/userInfo.d.ts +3 -1
  19. package/lib/utils/micro-openapis.d.ts +35 -0
  20. package/lib/webui.es.js +358 -353
  21. package/package.json +1 -2
  22. package/src/components/layout/header/headerExits.vue +1 -1
  23. package/src/stores/appInfo.ts +7 -8
  24. package/src/stores/userInfo.ts +21 -63
  25. package/src/utils/data.ts +6 -0
  26. package/src/utils/download.ts +10 -1
  27. package/src/utils/export-table.ts +5 -0
  28. package/src/utils/form-excel.ts +14 -9
  29. package/src/utils/main-openapis.ts +2 -2
  30. package/src/utils/micro-openapis.ts +146 -0
  31. package/src/utils/options.ts +5 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyfox2000/webui",
3
- "version": "1.3.21",
3
+ "version": "1.4.0",
4
4
  "description": "后台前端通用组件定义",
5
5
  "type": "module",
6
6
  "keywords": [],
@@ -26,7 +26,6 @@
26
26
  },
27
27
  "scripts": {
28
28
  "build": "pnpm i && vue-tsc -b && vite build",
29
- "build:dev": "pnpm link ../502428_WebBase/ && vue-tsc -b && vite build",
30
29
  "build:dts": "vue-tsc -b && vite-dts-declaration",
31
30
  "build:types": "vue-tsc --declaration --emitDeclarationOnly"
32
31
  },
@@ -12,7 +12,7 @@ const confirmExit = () => {
12
12
  if (mainAppApis.value) {
13
13
  mainAppApis.value.userLogout!();
14
14
  } else {
15
- userInfoStore.logout();
15
+ userInfoStore.logout(true);
16
16
  }
17
17
  };
18
18
  </script>
@@ -10,12 +10,12 @@ import {
10
10
  RouteRecord,
11
11
  EnvConfig,
12
12
  } from '@skyfox2000/microbase';
13
- import AppRouter, { addAppRoutes, continueNavigation, flattenRoute, LOGIN_PATH } from '@/router';
13
+ import AppRouter, { addAppRoutes, continueNavigation, flattenRoute } from '@/router';
14
14
  import { usePageInfo } from './pageInfo';
15
15
  import { isEmpty } from '@/utils/isEmpty';
16
16
 
17
17
  import { Component, h, nextTick } from 'vue';
18
- import { useUserInfo } from './userInfo';
18
+ import { LoginExpiredError, useUserInfo } from './userInfo';
19
19
  import { ApiResponse, httpPost, IUrlInfo, ResStatus } from '@skyfox2000/fapi';
20
20
  import message from 'vue-m-message';
21
21
 
@@ -240,6 +240,10 @@ export const useAppInfo = defineStore('appInfo', {
240
240
  if (result?.status === ResStatus.SUCCESS && result.data) {
241
241
  this.appList.length = 0;
242
242
  this.appList.push(...(result.data as AppInfo[]));
243
+ } else if (result?.errno == LoginExpiredError) {
244
+ const userInfoStore = useUserInfo();
245
+ userInfoStore.logout(false);
246
+ return;
243
247
  }
244
248
  });
245
249
  },
@@ -491,12 +495,7 @@ export const useAppInfo = defineStore('appInfo', {
491
495
  logout() {
492
496
  // 清理应用信息
493
497
  this.clean();
494
- AppRouter.push({ path: LOGIN_PATH + '#/' });
495
- if (isMicroApp()) {
496
- if (mainAppApis.value && mainAppApis.value.mainAppPush) {
497
- mainAppApis.value.mainAppPush(LOGIN_PATH + '#/');
498
- }
499
- }
498
+ window.location.assign('/');
500
499
  },
501
500
  },
502
501
  persist: false,
@@ -6,7 +6,8 @@ import { ref } from 'vue';
6
6
  import { useAppInfo } from './appInfo';
7
7
 
8
8
  const TokenError = 'Token解析失败';
9
- const LoginExpired = '登录过期,请重新登录';
9
+ export const LoginExpired = '登录过期,请重新登录';
10
+ export const LoginExpiredError = 1401;
10
11
 
11
12
  /** 登录相关接口 */
12
13
  const LoginUrlList: {
@@ -72,6 +73,10 @@ const getRolePermitsApi = <T>(appId: string): Promise<T | null | undefined> => {
72
73
  return httpPost<T>(LoginUrlList.auth, authParams).then((result) => {
73
74
  if (result?.status === ResStatus.SUCCESS) {
74
75
  return result.data as T;
76
+ } else if (result?.errno == LoginExpiredError) {
77
+ const userInfoStore = useUserInfo();
78
+ userInfoStore.logout(false);
79
+ return;
75
80
  }
76
81
  message.error('获取授权信息失败,' + result?.msg!);
77
82
  return null;
@@ -96,48 +101,6 @@ const logoutApi = <T>(): Promise<T | null | undefined> => {
96
101
  });
97
102
  };
98
103
 
99
- /**
100
- * 解析JWT token并检查是否过期
101
- * @param token JWT token字符串
102
- * @returns 检查结果
103
- */
104
- const isTokenExpired = (token: string): boolean => {
105
- try {
106
- const payload = JSON.parse(atob(token.split('.')[1])); // 解析payload部分
107
- const exp = payload.exp; // 获取过期时间
108
-
109
- const now = Date.now() / 1000; // 当前时间戳转换为秒
110
- return now > exp; // 检查是否过期
111
- } catch (error) {
112
- console.error(TokenError, error);
113
- return true; // 如果解析失败,认为token已过期
114
- }
115
- };
116
-
117
- /**
118
- * 检查token是否过期
119
- * @returns 检查结果
120
- */
121
- const checkToken = (token: string, onClose?: () => void): boolean => {
122
- if (token == '') {
123
- message.error(LoginExpired, {
124
- duration: 3000,
125
- onClose,
126
- });
127
- return false;
128
- }
129
-
130
- if (isTokenExpired(token)) {
131
- message.error(LoginExpired, {
132
- duration: 3000,
133
- onClose,
134
- });
135
- return false;
136
- }
137
-
138
- return true;
139
- };
140
-
141
104
  const expandUser = (loginInfo: LoginInfo): boolean => {
142
105
  try {
143
106
  const payload = JSON.parse(atob(loginInfo.token!.split('.')[1])); // 解析payload部分
@@ -210,7 +173,6 @@ export const useUserInfo = defineStore('userInfo', {
210
173
  // 非子应用,则需要检查token
211
174
  if (!isMicroApp()) {
212
175
  if (this.token) {
213
- checkToken(this.token, () => this.clean());
214
176
  setToken(this.token);
215
177
  userLevel.value = this.userInfo.UserLevel;
216
178
  this.isLogin = true;
@@ -336,11 +298,6 @@ export const useUserInfo = defineStore('userInfo', {
336
298
  * @param token 用户Token
337
299
  */
338
300
  setUserInfo(userInfo: UserInfo, token: string) {
339
- if (!checkToken(token)) {
340
- this.clean();
341
- return;
342
- }
343
-
344
301
  if (!userLevel.value) {
345
302
  // 仅允许设置一次,启动时设置,避免通过其它方式设置
346
303
  userLevel.value = userInfo.UserLevel;
@@ -359,13 +316,9 @@ export const useUserInfo = defineStore('userInfo', {
359
316
  * @returns 用户Token
360
317
  */
361
318
  getToken(): string {
362
- if (
363
- checkToken(this.token!, () => {
364
- this.clean();
365
- this.logout();
366
- })
367
- ) {
368
- return this.token!;
319
+ if (!this.token) {
320
+ this.clean();
321
+ this.logout(false);
369
322
  }
370
323
  return '';
371
324
  },
@@ -419,22 +372,27 @@ export const useUserInfo = defineStore('userInfo', {
419
372
 
420
373
  /**
421
374
  * 登出操作
375
+ * @param exit 是否主动退出应用
422
376
  * @returns
423
377
  */
424
- async logout(): Promise<void> {
378
+ async logout(exit: boolean): Promise<void> {
425
379
  try {
426
- // 调用后端登出接口
427
- await logoutApi<any>();
380
+ if (exit) {
381
+ // 调用后端登出接口
382
+ await logoutApi<any>();
383
+ }
428
384
  } catch (error) {
429
385
  console.error('调用登出接口失败', error);
430
386
  } finally {
431
387
  // 无论登出接口是否成功,都执行本地登出操作
432
388
  await this.clean();
433
389
 
434
- // 跳转到登录页
435
- setTimeout(async () => {
436
- message.success('已退出登录');
437
- }, 1000);
390
+ if (exit) {
391
+ // 跳转到登录页
392
+ setTimeout(async () => {
393
+ message.success('已退出登录');
394
+ }, 1000);
395
+ }
438
396
  setTimeout(async () => {
439
397
  const appInfoStore = useAppInfo();
440
398
  appInfoStore.logout();
package/src/utils/data.ts CHANGED
@@ -3,6 +3,7 @@ import { ApiResponse, httpPost, IUrlInfo, ResStatus } from '@skyfox2000/fapi';
3
3
  import message from 'vue-m-message';
4
4
  import { isEmpty } from './isEmpty';
5
5
  import { combineParams } from '@skyfox2000/microbase';
6
+ import { LoginExpiredError, useUserInfo } from '@/stores/userInfo';
6
7
 
7
8
  /**
8
9
  * 合并URL信息
@@ -68,6 +69,11 @@ const doPost = <T>(control: AnyControl, options: PostOptions<T>): Promise<ApiRes
68
69
  if (options.loadingState) {
69
70
  options.loadingState.value = false;
70
71
  }
72
+ if (result?.errno == LoginExpiredError) {
73
+ const userInfoStore = useUserInfo();
74
+ userInfoStore.logout(false);
75
+ return null;
76
+ }
71
77
 
72
78
  return result;
73
79
  })
@@ -3,6 +3,7 @@ import { PageControl } from '@/typings/page.d';
3
3
  import { ApiResponse, httpPost, IUrlInfo, ReqParams, ResStatus } from '@skyfox2000/fapi';
4
4
  import message from 'vue-m-message';
5
5
  import { combineParams } from '@skyfox2000/microbase';
6
+ import { LoginExpiredError, useUserInfo } from '@/stores/userInfo';
6
7
 
7
8
  /** 通用 Blob 下载方法
8
9
  *
@@ -67,6 +68,10 @@ export const donwloadFromMinio = <T>(url: IUrlInfo, params?: ReqParams, pageCtrl
67
68
 
68
69
  // 调用下载方法
69
70
  downloadBlob(blob, fileName);
71
+ } else if (result?.errno == LoginExpiredError) {
72
+ const userInfoStore = useUserInfo();
73
+ userInfoStore.logout(false);
74
+ return;
70
75
  } else {
71
76
  message.error('下载文件失败!');
72
77
  }
@@ -103,9 +108,13 @@ export const previewFromMinio = (url: IUrlInfo, fileName: string, params?: ReqPa
103
108
  for (let b = 0; b < byteCharacters.length; b++) {
104
109
  byteArrays.push(byteCharacters.charCodeAt(b));
105
110
  }
111
+ } else if (result?.errno == LoginExpiredError) {
112
+ const userInfoStore = useUserInfo();
113
+ userInfoStore.logout(false);
114
+ return;
106
115
  } else {
107
116
  message.error('文件预览失败!');
108
117
  }
109
118
  return undefined;
110
119
  });
111
- };
120
+ };
@@ -3,6 +3,7 @@ import { ApiResponse, httpPost, IUrlInfo, ResStatus } from '@skyfox2000/fapi';
3
3
  import dayjs from 'dayjs';
4
4
  import message from 'vue-m-message';
5
5
  import { downloadBlob } from './download';
6
+ import { LoginExpiredError, useUserInfo } from '@/stores/userInfo';
6
7
 
7
8
  // 下载当前选择行
8
9
  // 表头相同
@@ -141,6 +142,10 @@ export const exportResults = async <T extends Record<string, any>>(
141
142
  const blob = new Blob([`\uFEFF${csvContent}`], { type: 'text/csv' });
142
143
  downloadBlob(blob, processedFileName);
143
144
  }
145
+ } else if (result?.errno == LoginExpiredError) {
146
+ const userInfoStore = useUserInfo();
147
+ userInfoStore.logout(false);
148
+ return;
144
149
  }
145
150
  return undefined;
146
151
  });
@@ -8,6 +8,7 @@ import { ValidateRule } from '@/typings/form';
8
8
  import { validMessages } from './form-validate';
9
9
  import { UploadFile } from '@/typings/upload';
10
10
  import { toExcel, type ExcelMarkInfo, excelToNormalized } from './excel-view';
11
+ import { LoginExpiredError, useUserInfo } from '@/stores/userInfo';
11
12
 
12
13
  // ExcelMarkCell 和 ExcelMarkInfo 类型已移至 excel-view.ts 统一管理
13
14
 
@@ -260,16 +261,20 @@ export const checkExcelDuplicates = async (
260
261
  });
261
262
 
262
263
  if (url) {
263
- const duplicateRows = await httpPost(url, {
264
+ const result = await httpPost(url, {
264
265
  Data: allKeys,
265
266
  });
266
- if (duplicateRows?.data) {
267
- (duplicateRows.data as number[]).forEach((index) => {
267
+ if (result?.status == ResStatus.SUCCESS) {
268
+ const duplicateRows = result?.data;
269
+ (duplicateRows as number[]).forEach((index) => {
268
270
  duplicateIndices.add(index);
269
271
  });
270
- }
271
- if (duplicateRows?.status === ResStatus.ERROR) {
272
- throw new Error(duplicateRows.msg);
272
+ } else if (result?.errno == LoginExpiredError) {
273
+ const userInfoStore = useUserInfo();
274
+ userInfoStore.logout(false);
275
+ throw new Error('登录已过期');
276
+ } else {
277
+ throw new Error(result?.msg);
273
278
  }
274
279
  }
275
280
 
@@ -392,14 +397,14 @@ export const validateExcelUnified = async (
392
397
  validationMsg,
393
398
  duplicateMsg,
394
399
  hasFormatError,
395
- hasDuplicateError
400
+ hasDuplicateError,
396
401
  };
397
402
  } else {
398
403
  return {
399
404
  validationMsg,
400
405
  duplicateMsg,
401
406
  hasFormatError,
402
- hasDuplicateError
407
+ hasDuplicateError,
403
408
  };
404
409
  }
405
410
  }
@@ -408,7 +413,7 @@ export const validateExcelUnified = async (
408
413
  validationMsg,
409
414
  duplicateMsg,
410
415
  hasFormatError: false,
411
- hasDuplicateError: false
416
+ hasDuplicateError: false,
412
417
  };
413
418
  };
414
419
 
@@ -42,7 +42,7 @@ export const userLogin = (loginInfo: LoginInfo): Promise<ApiResponse<LoginInfo>
42
42
  export const userLogout = (): Promise<ApiResponse<void> | void> => {
43
43
  if (mainAppApis.value && mainAppApis.value.userLogout) return mainAppApis.value.userLogout();
44
44
  const userInfoStore = useUserInfo();
45
- return userInfoStore.logout();
45
+ return userInfoStore.logout(true);
46
46
  };
47
47
  /**
48
48
  * 获取授权码
@@ -61,4 +61,4 @@ export const getUserInfo = (): UserInfo => {
61
61
  if (mainAppApis.value && mainAppApis.value.getUserInfo) return mainAppApis.value.getUserInfo();
62
62
  const userInfoStore = useUserInfo();
63
63
  return userInfoStore.getUserInfo();
64
- };
64
+ };
@@ -0,0 +1,146 @@
1
+ /**
2
+ * 子应用SDK - 提供统一的主应用API调用接口
3
+ */
4
+
5
+ // 定义API方法类型
6
+ interface MainApis {
7
+ getAppInfo: () => Promise<any>;
8
+ getHostInfo: () => Promise<any>;
9
+ getUserInfo: () => Promise<any>;
10
+ getToken: () => Promise<any>;
11
+ userLogin: (params: any) => Promise<any>;
12
+ userLogout: () => Promise<any>;
13
+ mainAppPush: (params: any) => Promise<any>;
14
+ }
15
+
16
+ // 定义可用的API方法名称
17
+ type MainApiMethod = keyof MainApis;
18
+
19
+ /**
20
+ * MicroAppSDK类 - 管理与主应用的通信
21
+ */
22
+ class MicroAppSDK {
23
+ constructor() {
24
+ this.ensureMicroAppReady();
25
+ }
26
+
27
+ /**
28
+ * 确保micro-app环境就绪
29
+ */
30
+ ensureMicroAppReady() {
31
+ if (typeof window === 'undefined') {
32
+ throw new Error('MicroAppSDK can only be used in browser environment');
33
+ }
34
+ }
35
+
36
+ /**
37
+ * 获取基座API
38
+ */
39
+ getMainApis(): Partial<MainApis> {
40
+ if (this.isInMicroApp() && (window as any).microApp && (window as any).microApp.getData) {
41
+ const data = (window as any).microApp.getData();
42
+ return data?.MainApis || {};
43
+ }
44
+ return {};
45
+ }
46
+
47
+ /**
48
+ * 检查是否在微前端环境中
49
+ */
50
+ isInMicroApp(): boolean {
51
+ return !!(window as any).microApp;
52
+ }
53
+
54
+ /**
55
+ * API调用包装器
56
+ */
57
+ async callMainApi<T extends MainApiMethod>(methodName: T, params?: any): Promise<ReturnType<MainApis[T]>> {
58
+ const mainApis = this.getMainApis();
59
+
60
+ // 使用类型断言解决TypeScript类型检查问题
61
+ const method = mainApis[methodName] as Function | undefined;
62
+
63
+ if (!method) {
64
+ throw new Error(`Main API method '${methodName}' is not available`);
65
+ }
66
+
67
+ try {
68
+ if (params !== undefined) {
69
+ return await method(params);
70
+ } else {
71
+ return await method();
72
+ }
73
+ } catch (error) {
74
+ console.error(`Failed to call main API '${methodName}':`, error);
75
+ throw error;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * 获取应用信息
81
+ */
82
+ async getAppInfo(): Promise<any> {
83
+ return this.callMainApi('getAppInfo');
84
+ }
85
+
86
+ /**
87
+ * 获取主机信息
88
+ */
89
+ async getHostInfo(): Promise<any> {
90
+ return this.callMainApi('getHostInfo');
91
+ }
92
+
93
+ /**
94
+ * 获取用户信息
95
+ */
96
+ async getUserInfo(): Promise<any> {
97
+ return this.callMainApi('getUserInfo');
98
+ }
99
+
100
+ /**
101
+ * 获取授权令牌
102
+ */
103
+ async getToken(): Promise<any> {
104
+ return this.callMainApi('getToken');
105
+ }
106
+
107
+ /**
108
+ * 用户登录
109
+ */
110
+ async userLogin(params: any): Promise<any> {
111
+ return this.callMainApi('userLogin', params);
112
+ }
113
+
114
+ /**
115
+ * 用户登出
116
+ */
117
+ async userLogout(): Promise<any> {
118
+ return this.callMainApi('userLogout');
119
+ }
120
+
121
+ /**
122
+ * 主应用推送
123
+ */
124
+ async mainAppPush(params: any): Promise<any> {
125
+ return this.callMainApi('mainAppPush', params);
126
+ }
127
+ }
128
+
129
+ // 创建单例实例
130
+ const microAppSDKInstance = new MicroAppSDK();
131
+
132
+ // 导出SDK实例和具体方法
133
+ export default microAppSDKInstance;
134
+
135
+ // 导出类和实例
136
+ export { MicroAppSDK, microAppSDKInstance };
137
+
138
+ // 导出所有方法
139
+ export const getAppInfo = microAppSDKInstance.getAppInfo.bind(microAppSDKInstance);
140
+ export const getHostInfo = microAppSDKInstance.getHostInfo.bind(microAppSDKInstance);
141
+ export const getUserInfo = microAppSDKInstance.getUserInfo.bind(microAppSDKInstance);
142
+ export const getToken = microAppSDKInstance.getToken.bind(microAppSDKInstance);
143
+ export const userLogin = microAppSDKInstance.userLogin.bind(microAppSDKInstance);
144
+ export const userLogout = microAppSDKInstance.userLogout.bind(microAppSDKInstance);
145
+ export const mainAppPush = microAppSDKInstance.mainAppPush.bind(microAppSDKInstance);
146
+ export const isInMicroApp = microAppSDKInstance.isInMicroApp.bind(microAppSDKInstance);
@@ -15,6 +15,7 @@ import message from 'vue-m-message';
15
15
  import eventBus from './eventbus';
16
16
  import { formValidate } from './form-validate';
17
17
  import { combineParams } from '@skyfox2000/microbase';
18
+ import { LoginExpiredError, useUserInfo } from '@/stores/userInfo';
18
19
 
19
20
  /**
20
21
  * 自动初始化选择项
@@ -169,6 +170,10 @@ const queryOptions = <T>(
169
170
  if (result.data) {
170
171
  return result.data as T[];
171
172
  }
173
+ } else if (result?.errno == LoginExpiredError) {
174
+ const userInfoStore = useUserInfo();
175
+ userInfoStore.logout(false);
176
+ return;
172
177
  }
173
178
  return [];
174
179
  })