@skyfox2000/webui 1.0.13 → 1.2.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 (162) hide show
  1. package/lib/assets/modules/file-upload-CBUcsUnR.js +170 -0
  2. package/lib/assets/modules/form-validate-CgX7aR7T.js +297 -0
  3. package/lib/assets/modules/index-Civhd8xG.js +112 -0
  4. package/lib/assets/modules/index-DQMdt51R.js +726 -0
  5. package/lib/assets/modules/{index-BEWJ_qAH.js → index-DmWrkTXX.js} +1 -1
  6. package/lib/assets/modules/{menuTabs-BXdbFZor.js → menuTabs-BRYvFWA-.js} +131 -121
  7. package/lib/assets/modules/settingInfo-BZakNKIN.js +999 -0
  8. package/lib/assets/modules/uploadList-B7XoxGOh.js +278 -0
  9. package/lib/components/common/icon/index.vue.d.ts +1 -1
  10. package/lib/components/content/dialog/index.vue.d.ts +1 -1
  11. package/lib/components/content/drawer/index.vue.d.ts +1 -1
  12. package/lib/components/content/form/index.vue.d.ts +1 -1
  13. package/lib/components/content/search/index.vue.d.ts +1 -1
  14. package/lib/components/content/table/index.vue.d.ts +1 -1
  15. package/lib/components/content/table/tableOperate.vue.d.ts +1 -1
  16. package/lib/components/content/toolbar/icontool.vue.d.ts +1 -1
  17. package/lib/components/content/toolbar/index.vue.d.ts +1 -1
  18. package/lib/components/content/tree/index.vue.d.ts +1 -1
  19. package/lib/components/form/transfer/transferTable.vue.d.ts +1 -1
  20. package/lib/components/form/treeSelect/index.vue.d.ts +1 -1
  21. package/lib/components/form/upload/uploadList.vue.d.ts +1 -1
  22. package/lib/const/options.d.ts +32 -0
  23. package/lib/directives/enter-submit.d.ts +4 -0
  24. package/lib/directives/index.d.ts +2 -0
  25. package/lib/directives/permission.d.ts +5 -0
  26. package/lib/es/AceEditor/index.js +9 -8
  27. package/lib/es/BasicLayout/index.js +28 -24
  28. package/lib/es/Error403/index.js +15 -10
  29. package/lib/es/Error404/index.js +15 -10
  30. package/lib/es/ExcelForm/index.js +380 -175
  31. package/lib/es/UploadForm/index.js +23 -20
  32. package/lib/index.d.ts +42 -2
  33. package/lib/router/index.d.ts +16 -0
  34. package/lib/stores/appInfo.d.ts +34 -0
  35. package/lib/stores/hostInfo.d.ts +9 -0
  36. package/lib/stores/pageInfo.d.ts +18 -0
  37. package/lib/stores/pinia.d.ts +3 -0
  38. package/lib/stores/settingInfo.d.ts +8 -0
  39. package/lib/stores/userInfo.d.ts +21 -0
  40. package/lib/typings/data.d.ts +80 -0
  41. package/lib/typings/form.d.ts +171 -0
  42. package/lib/typings/menu.d.ts +7 -0
  43. package/lib/typings/option.d.ts +175 -0
  44. package/lib/typings/page.d.ts +69 -0
  45. package/lib/typings/table.d.ts +181 -0
  46. package/lib/typings/tools.d.ts +130 -0
  47. package/lib/typings/tree.d.ts +72 -0
  48. package/lib/typings/upload.d.ts +161 -0
  49. package/lib/typings/urls.d.ts +69 -0
  50. package/lib/utils/cache.d.ts +23 -0
  51. package/lib/utils/data.d.ts +6 -0
  52. package/lib/utils/download.d.ts +4 -0
  53. package/lib/utils/eventbus.d.ts +16 -0
  54. package/lib/utils/export-table.d.ts +12 -0
  55. package/lib/utils/file-upload.d.ts +15 -0
  56. package/lib/utils/form-excel.d.ts +30 -0
  57. package/lib/utils/form-validate.d.ts +29 -0
  58. package/lib/utils/form.d.ts +9 -0
  59. package/lib/utils/icon-loader.d.ts +125 -0
  60. package/lib/utils/isEmpty.d.ts +1 -0
  61. package/lib/utils/main-openapis.d.ts +9 -0
  62. package/lib/utils/menu.d.ts +6 -0
  63. package/lib/utils/options.d.ts +10 -0
  64. package/lib/utils/page.d.ts +25 -0
  65. package/lib/utils/table.d.ts +21 -0
  66. package/lib/utils/tools.d.ts +18 -0
  67. package/lib/utils/tree.d.ts +3 -0
  68. package/lib/vite-env.d.ts +8 -0
  69. package/lib/webui.css +1 -1
  70. package/lib/webui.es.js +1020 -854
  71. package/package.json +7 -6
  72. package/src/components/common/icon/appicon.vue +1 -1
  73. package/src/components/common/icon/fullscreen.vue +2 -1
  74. package/src/components/common/icon/index.vue +1 -1
  75. package/src/components/common/icon/layoutIcon.vue +1 -1
  76. package/src/components/common/icon/projectIcon.vue +1 -1
  77. package/src/components/common/icon/toolIcon.vue +1 -1
  78. package/src/components/content/dialog/excelForm.vue +2 -2
  79. package/src/components/content/dialog/index.vue +1 -1
  80. package/src/components/content/dialog/uploadForm.vue +7 -6
  81. package/src/components/content/drawer/index.vue +43 -18
  82. package/src/components/content/form/formItem.vue +1 -1
  83. package/src/components/content/form/index.vue +1 -1
  84. package/src/components/content/search/index.vue +1 -1
  85. package/src/components/content/search/searchItem.vue +1 -1
  86. package/src/components/content/table/index.vue +8 -5
  87. package/src/components/content/table/tableOperate.vue +8 -4
  88. package/src/components/content/toolbar/icontool.vue +2 -2
  89. package/src/components/content/toolbar/index.vue +9 -5
  90. package/src/components/content/tree/index.vue +1 -1
  91. package/src/components/error/error403.vue +2 -2
  92. package/src/components/error/error404.vue +2 -2
  93. package/src/components/form/autoComplete/index.vue +1 -1
  94. package/src/components/form/cascader/index.vue +1 -2
  95. package/src/components/form/checkbox/index.vue +11 -5
  96. package/src/components/form/datePicker/index.vue +1 -1
  97. package/src/components/form/input/index.vue +1 -1
  98. package/src/components/form/input/inputNumber.vue +1 -1
  99. package/src/components/form/input/inputPassword.vue +1 -1
  100. package/src/components/form/radio/index.vue +1 -1
  101. package/src/components/form/radio/radioStatus.vue +1 -1
  102. package/src/components/form/rangePicker/index.vue +1 -1
  103. package/src/components/form/select/index.vue +1 -1
  104. package/src/components/form/switch/index.vue +7 -3
  105. package/src/components/form/textarea/index.vue +1 -1
  106. package/src/components/form/transfer/index.vue +1 -1
  107. package/src/components/form/transfer/transferTable.vue +42 -22
  108. package/src/components/form/treeSelect/index.vue +2 -3
  109. package/src/components/form/upload/uploadList.vue +1 -1
  110. package/src/components/layout/breadcrumb/index.vue +1 -1
  111. package/src/components/layout/header/headerExits.vue +1 -1
  112. package/src/components/layout/header/index.vue +1 -1
  113. package/src/components/layout/header/user.vue +2 -1
  114. package/src/components/layout/menu/index.vue +9 -3
  115. package/src/components/layout/menu/menuTabs.vue +10 -12
  116. package/src/components/layout/page/basicLayout.vue +1 -1
  117. package/src/const/options.ts +114 -0
  118. package/src/directives/enter-submit.ts +13 -0
  119. package/src/directives/index.ts +26 -0
  120. package/src/directives/permission.ts +144 -0
  121. package/src/index.ts +201 -0
  122. package/src/router/index.ts +196 -0
  123. package/src/stores/appInfo.ts +471 -0
  124. package/src/stores/hostInfo.ts +117 -0
  125. package/src/stores/pageInfo.ts +131 -0
  126. package/src/stores/pinia.ts +10 -0
  127. package/src/stores/settingInfo.ts +53 -0
  128. package/src/stores/userInfo.ts +392 -0
  129. package/src/typings/data.d.ts +81 -0
  130. package/src/typings/form.d.ts +172 -0
  131. package/src/typings/menu.d.ts +7 -0
  132. package/src/typings/option.d.ts +177 -0
  133. package/src/typings/page.d.ts +70 -0
  134. package/src/typings/table.d.ts +182 -0
  135. package/src/typings/tools.d.ts +131 -0
  136. package/src/typings/tree.d.ts +73 -0
  137. package/src/typings/upload.d.ts +162 -0
  138. package/src/typings/urls.d.ts +70 -0
  139. package/src/utils/cache.ts +175 -0
  140. package/src/utils/data.ts +189 -0
  141. package/src/utils/download.ts +80 -0
  142. package/src/utils/eventbus.ts +78 -0
  143. package/src/utils/export-table.ts +155 -0
  144. package/src/utils/file-upload.ts +304 -0
  145. package/src/utils/form-excel.ts +523 -0
  146. package/src/utils/form-validate.ts +368 -0
  147. package/src/utils/form.ts +188 -0
  148. package/src/utils/icon-loader.ts +412 -0
  149. package/src/utils/isEmpty.ts +18 -0
  150. package/src/utils/main-openapis.ts +72 -0
  151. package/src/utils/menu.ts +89 -0
  152. package/src/utils/options.ts +324 -0
  153. package/src/utils/page.ts +262 -0
  154. package/src/utils/table.ts +274 -0
  155. package/src/utils/tools.ts +362 -0
  156. package/src/utils/tree.ts +28 -0
  157. package/tsconfig.json +1 -8
  158. package/vite.config.ts +7 -4
  159. package/lib/assets/modules/index-BahGnrAq.js +0 -415
  160. package/lib/assets/modules/index-BoKIa2sr.js +0 -109
  161. package/lib/assets/modules/index-D47Ci-T3.js +0 -107
  162. package/lib/assets/modules/uploadList-Dzlg47V0.js +0 -182
@@ -0,0 +1,412 @@
1
+ import { defineComponent, h, Ref, ref, toRaw, watch } from 'vue';
2
+ import message from 'vue-m-message';
3
+
4
+ type IconCacheData = {
5
+ /// 存储到window里面的key
6
+ windowKey: string;
7
+ /// 是否单色
8
+ monoColor: boolean;
9
+ /// 实际的Icon脚本字符串
10
+ scriptContent: string;
11
+ /// 超时时间
12
+ expireTime: number;
13
+ };
14
+
15
+ export type IconFontOptions = {
16
+ /**
17
+ * 加载的图标集地址
18
+ */
19
+ iconUrl: string;
20
+ /**
21
+ * 是否单色图标集,会替换清空所有fill参数
22
+ */
23
+ monoColor?: boolean;
24
+ /**
25
+ * 图标名称
26
+ */
27
+ icon?: string;
28
+ /**
29
+ * 图标集
30
+ */
31
+ icons?: string[];
32
+ };
33
+
34
+ const ICON_CACHE_TIME = 7 * 24 * 60 * 60 * 1000; /// 7天
35
+ const loadingPromises: Map<string, Promise<void>> = new Map(); // 用于存储加载中的 Promise
36
+ const loadedIcons: Set<string> = new Set(); // 用于缓存已加载的图标
37
+ const ToolIcons: Ref<Record<string, any>> = ref({}); // 图标库
38
+
39
+ export const ICONS_LIB = ToolIcons;
40
+
41
+ /**
42
+ *
43
+ * @param options 参数配置
44
+ * - iconUrl 加载的图标集地址
45
+ * - monoColor: 是否单色图标
46
+ * @returns
47
+ */
48
+ export const createFromIconfont = (options: IconFontOptions) => {
49
+ if (!options.iconUrl) return circleLoading;
50
+ const isLoaded = ref(true); // 添加状态管理,表示图标库是否加载完成
51
+ const Icon = ref('');
52
+ let existIcon = true;
53
+ if (options.icons?.length) {
54
+ // 检查图标库是否存在
55
+ existIcon = options.icons.every((icon: string) => ToolIcons.value[icon] !== undefined);
56
+ }
57
+
58
+ // 如果图标不存在,则加载图标库
59
+ if ((options.icon && !ToolIcons.value[options.icon]) || !existIcon) {
60
+ loadIconfont(options, isLoaded).then(() => {
61
+ parseCacheIcons(options.iconUrl, options.monoColor || false);
62
+ isLoaded.value = true; // 设置加载完成状态
63
+ });
64
+ }
65
+
66
+ return defineComponent({
67
+ props: {
68
+ icon: {
69
+ type: String,
70
+ },
71
+ icons: {
72
+ type: Array<string>,
73
+ },
74
+ iconIndex: {
75
+ type: Number,
76
+ },
77
+ clickable: {
78
+ type: Boolean,
79
+ },
80
+ angle: {
81
+ type: Number,
82
+ },
83
+ flip: {
84
+ type: Boolean,
85
+ },
86
+ class: {
87
+ type: [String, Array<string>],
88
+ default: '',
89
+ },
90
+ },
91
+ emits: ['update:iconIndex'],
92
+ setup(props, { emit }) {
93
+ const curIcon = ref<string | undefined>(props.icon);
94
+ const curIndex = ref(0);
95
+ const handleClick = () => {
96
+ if (props.icons) curIndex.value++; // 自增 curIndex
97
+ };
98
+
99
+ watch(
100
+ () => props.iconIndex,
101
+ (newIndex) => {
102
+ if (newIndex !== undefined) {
103
+ curIndex.value = newIndex;
104
+ }
105
+ },
106
+ { immediate: true },
107
+ );
108
+
109
+ watch(
110
+ () => curIndex.value,
111
+ (newIndex) => {
112
+ if (props.icons) {
113
+ if (newIndex >= props.icons.length) {
114
+ curIndex.value = 0; // 超过长度则重置为 0
115
+ }
116
+ curIcon.value = props.icons[curIndex.value];
117
+ updateIcon(Icon, curIcon, curIcon.value);
118
+ emit('update:iconIndex', curIndex.value);
119
+ }
120
+ },
121
+ { immediate: true },
122
+ );
123
+
124
+ watch(
125
+ () => ToolIcons.value,
126
+ () => {
127
+ updateIcon(Icon, curIcon, curIcon.value);
128
+ },
129
+ { deep: true },
130
+ );
131
+
132
+ watch(
133
+ () => props.icon,
134
+ (newIcon) => {
135
+ updateIcon(Icon, curIcon, newIcon);
136
+ },
137
+ );
138
+ updateIcon(Icon, curIcon, curIcon.value);
139
+
140
+ return { isLoaded, Icon, handleClick }; // 返回状态以供模板使用
141
+ },
142
+ render() {
143
+ if (!isLoaded.value) {
144
+ return circleLoading;
145
+ }
146
+
147
+ let existingClass = this.class;
148
+ let classString = '';
149
+
150
+ if (Array.isArray(existingClass)) {
151
+ classString = existingClass.join(' ');
152
+ } else if (typeof existingClass === 'string') {
153
+ classString = existingClass;
154
+ }
155
+
156
+ let finalClasses = [classString];
157
+
158
+ if (!/\bw-/.test(classString) && !/$w-/.test(classString)) {
159
+ finalClasses.push('w-4');
160
+ }
161
+
162
+ if (!/\bh-/.test(classString) && !/$h-/.test(classString)) {
163
+ finalClasses.push('h-4');
164
+ }
165
+
166
+ // 根据传入的 icon 渲染对应的组件
167
+ return Icon.value
168
+ ? h(toRaw(Icon.value), {
169
+ onClick: this.$props.clickable ? this.handleClick : undefined,
170
+ class: `${finalClasses.join(' ')}`,
171
+ style: {
172
+ fill: 'currentColor',
173
+ cursor: this.$props.clickable ? 'pointer' : 'default',
174
+ transform: getIconTransform(this.angle, this.flip),
175
+ },
176
+ })
177
+ : h('div', { class: 'w-[14px] h-[14px]' }); // 占位符
178
+ },
179
+ });
180
+ };
181
+
182
+ /**
183
+ * 远程加载图标集
184
+ * @param url 远程图标库地址
185
+ */
186
+ const loadIconfont = (options: IconFontOptions, isLoaded: Ref<Boolean>): Promise<void> => {
187
+ let url = options.iconUrl;
188
+ if (!url) {
189
+ console.error('图标库地址不能为空');
190
+ return Promise.resolve();
191
+ }
192
+ if (loadingPromises.has(url)) {
193
+ return Promise.resolve(); // 如果正在加载中,返回该 URL 对应的 Promise
194
+ }
195
+
196
+ const cacheData = getIconCache(url);
197
+
198
+ // 如果有未过期且内容相同的缓存
199
+ if (cacheData) {
200
+ return new Promise<void>(async (resolve) => {
201
+ loadScript(cacheData.scriptContent);
202
+ return resolve();
203
+ });
204
+ }
205
+
206
+ /// 需要远程加载
207
+ isLoaded.value = false;
208
+
209
+ // 创建一个新的 Promise 并存储在 Map 中
210
+ const loadingPromise = new Promise<void>(async (resolve) => {
211
+ await loadAndCacheScript(url, options.monoColor || false);
212
+ resolve();
213
+ loadingPromises.delete(url);
214
+ });
215
+
216
+ loadingPromises.set(url, loadingPromise);
217
+
218
+ return loadingPromise;
219
+ };
220
+
221
+ const loadAndCacheScript = async (url: string, monoColor: boolean) => {
222
+ try {
223
+ const response = await fetch(url);
224
+ if (!response.ok) {
225
+ message.error('网络异常,无法加载图标库!');
226
+ return;
227
+ }
228
+ let scriptContent = await response.text();
229
+
230
+ /// 是否单色
231
+ scriptContent = monoColor ? scriptContent.replace(/fill=\"[^\"]*\"/gi, '') : scriptContent;
232
+
233
+ let reg = /^window\.(\w+)\s*=/;
234
+ const matches = scriptContent.match(reg);
235
+ if (matches && matches.length > 1) {
236
+ let windowKey = matches[1];
237
+ storeIconCache(url, windowKey, monoColor, scriptContent);
238
+ }
239
+
240
+ loadScript(scriptContent);
241
+ return;
242
+ } catch (error) {
243
+ console.error('网络异常,无法加载图标库:', error);
244
+ return null;
245
+ }
246
+ };
247
+
248
+ const loadScript = (scriptContent: string) => {
249
+ const script = document.createElement('script');
250
+ script.textContent = scriptContent;
251
+ document.head.appendChild(script);
252
+ };
253
+
254
+ /**
255
+ * 存储图标缓存到 localStorage
256
+ * @param key 缓存键
257
+ * @param svgString SVG 字符串
258
+ */
259
+ const storeIconCache = (key: string, windowKey: string, monoColor: boolean, scriptContent: string): void => {
260
+ let cacheKey = `icon_cache:${key}`;
261
+ const cache = {
262
+ windowKey,
263
+ monoColor,
264
+ scriptContent,
265
+ expireTime: Date.now() + ICON_CACHE_TIME, // 缓存时间:7天
266
+ };
267
+ localStorage.setItem(cacheKey, JSON.stringify(cache));
268
+ };
269
+
270
+ /**
271
+ * 从 localStorage 读取图标缓存
272
+ * @param key 缓存键
273
+ * @returns 缓存内容
274
+ */
275
+ const getIconCache = (key: string): IconCacheData | null => {
276
+ let cacheKey = `icon_cache:${key}`;
277
+ const cache = localStorage.getItem(cacheKey);
278
+ if (!cache) return null;
279
+
280
+ let parsedCache: IconCacheData;
281
+ try {
282
+ parsedCache = JSON.parse(cache);
283
+ } catch (error) {
284
+ console.error('解析缓存失败:', error);
285
+ return null;
286
+ }
287
+
288
+ // 检查缓存是否过期
289
+ if (parsedCache.expireTime < Date.now()) {
290
+ localStorage.removeItem(key);
291
+ return null;
292
+ }
293
+
294
+ return parsedCache;
295
+ };
296
+
297
+ /**
298
+ * 检查图标库是否过期
299
+ */
300
+ export const checkIconsExpired = () => {
301
+ const currentTime = Date.now();
302
+ for (const key in localStorage) {
303
+ if (key.startsWith('icon_cache:')) {
304
+ const cache = localStorage.getItem(key);
305
+ if (cache) {
306
+ const parsedCache: IconCacheData = JSON.parse(cache);
307
+ if (parsedCache.expireTime < currentTime) {
308
+ localStorage.removeItem(key);
309
+ }
310
+ }
311
+ }
312
+ }
313
+ };
314
+
315
+ const updateIcon = (Icon: Ref<string>, curIcon: Ref<string | undefined>, newIcon?: string) => {
316
+ setTimeout(() => {
317
+ curIcon.value = newIcon;
318
+ if (curIcon.value) {
319
+ Icon.value = ToolIcons.value[curIcon.value];
320
+ }
321
+ }, 1);
322
+ };
323
+
324
+ export const circleLoading = h(
325
+ 'svg',
326
+ { class: 'w-4 h-4', viewBox: '0 0 50 50', style: { animation: 'rotate 2s linear infinite' } },
327
+ [
328
+ h('circle', {
329
+ cx: '25',
330
+ cy: '25',
331
+ r: '20',
332
+ stroke: 'currentColor',
333
+ fill: 'none',
334
+ style: { strokeWidth: '3 !important' }, // 强制应用线宽
335
+ }),
336
+ h('circle', {
337
+ cx: '25',
338
+ cy: '6',
339
+ r: '6',
340
+ fill: 'currentColor',
341
+ }),
342
+ ],
343
+ );
344
+
345
+ /**
346
+ * 匹配缓存中解析已加载的图标库
347
+ * @param url 远程图标库地址
348
+ * @param monoColor 是否单色
349
+ */
350
+ const parseCacheIcons = (url: string, monoColor: boolean) => {
351
+ const cacheData = getIconCache(url);
352
+ Object.keys(window).forEach((key) => {
353
+ if (key.startsWith('_iconfont_svg_string_') && cacheData?.windowKey === key && !loadedIcons.has(key)) {
354
+ loadedIcons.add(key);
355
+ parseIcons(key, monoColor);
356
+ }
357
+ });
358
+ };
359
+
360
+ /**
361
+ * 从window中解析已加载的图标库
362
+ * @param key window中的key
363
+ * @param monoColor 是否单色
364
+ */
365
+ export const parseIcons = (key: string, monoColor: boolean) => {
366
+ const svgString = (window as Record<string, any>)[key];
367
+
368
+ const parser = new DOMParser();
369
+ const svgDoc = parser.parseFromString(svgString, 'image/svg+xml');
370
+ const newSymbols = svgDoc.getElementsByTagName('symbol');
371
+
372
+ // 获取全局的 SVG symbol 定义
373
+ const globalSymbols = document.querySelectorAll('symbol');
374
+
375
+ for (const newSymbol of newSymbols) {
376
+ const id = newSymbol.id;
377
+ const globalSymbol = Array.from(globalSymbols).find((s) => s.id === id);
378
+ if (globalSymbol && monoColor) {
379
+ globalSymbol.innerHTML = newSymbol.innerHTML;
380
+ // 如果全局中存在相同id的symbol,则删除其子元素的fill属性
381
+ globalSymbol.querySelectorAll('*').forEach((element) => {
382
+ if (element.hasAttribute('fill')) {
383
+ element.removeAttribute('fill');
384
+ }
385
+ });
386
+ }
387
+
388
+ if (!ToolIcons.value[id]) {
389
+ ToolIcons.value[id] = h('svg', { fill: 'currentColor' }, [h('use', { 'xlink:href': `#${id}` })]);
390
+ } else {
391
+ /// 优先使用平台图标
392
+ // console.warn('已存在图标名:', id);
393
+ }
394
+ }
395
+ };
396
+
397
+ /**
398
+ * 图标显示效果转换
399
+ * @param angle 旋转角度
400
+ * @param flip 翻转
401
+ * @returns
402
+ */
403
+ export const getIconTransform = (angle?: number, flip?: boolean): string => {
404
+ let transform = '';
405
+ if (angle) {
406
+ transform += `rotate(${angle}deg)`;
407
+ }
408
+ if (flip) {
409
+ transform += ' scaleX(-1)';
410
+ }
411
+ return transform;
412
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 判断数据是否为空
3
+ * @param data 待检查的数据
4
+ * @returns 如果数据为空返回 true,否则返回 false
5
+ */
6
+ export const isEmpty = <T>(data: T | null | undefined): boolean => {
7
+ // 处理 null、undefined 和 空字符串
8
+ if (data === null || data === undefined || data === '') return true;
9
+
10
+ // 处理数组
11
+ if (Array.isArray(data)) return data.length === 0;
12
+
13
+ // 处理对象
14
+ if (typeof data === 'object') return Object.keys(data).length === 0;
15
+
16
+ // 其他类型不为空
17
+ return false;
18
+ };
@@ -0,0 +1,72 @@
1
+ import { useAppInfo } from '@/stores/appInfo';
2
+ import { useHostInfo } from '@/stores/hostInfo';
3
+ import { useUserInfo } from '@/stores/userInfo';
4
+ import { ApiResponse } from '@skyfox2000/fapi';
5
+ import { AppInfo, HostInfo, LoginInfo, UserInfo, mainAppApis } from '@skyfox2000/microbase';
6
+
7
+ /**
8
+ * 获取HostInfo
9
+ */
10
+ export const getHostInfo = (): HostInfo => {
11
+ if (mainAppApis.value) return mainAppApis.value.getHostInfo();
12
+ const hostInfoStore = useHostInfo();
13
+ return hostInfoStore.hostInfo;
14
+ };
15
+
16
+ /**
17
+ * 获取当前应用信息
18
+ */
19
+ export const getAppInfo = (): AppInfo => {
20
+ if (mainAppApis.value) return mainAppApis.value.getAppInfo();
21
+ const appInfoStore = useAppInfo();
22
+ return appInfoStore.appInfo;
23
+ };
24
+
25
+ // export const userRegister = () => {
26
+ // if (mainAppApis.value && mainAppApis.value.userRegister) return mainAppApis.value.userRegister();
27
+ // const userInfoStore = useUserInfo()
28
+ // return userInfoStore.userRegister();
29
+ // }
30
+
31
+ /**
32
+ * 用户登录接口
33
+ */
34
+ export const userLogin = (loginInfo: LoginInfo): Promise<ApiResponse<LoginInfo> | void> => {
35
+ if (mainAppApis.value && mainAppApis.value.userLogin) return mainAppApis.value.userLogin(loginInfo);
36
+ const userInfoStore = useUserInfo();
37
+ return userInfoStore.login(loginInfo, true);
38
+ };
39
+ /**
40
+ * 用户注销接口
41
+ */
42
+ export const userLogout = (): Promise<ApiResponse<void> | void> => {
43
+ if (mainAppApis.value && mainAppApis.value.userLogout) return mainAppApis.value.userLogout();
44
+ const userInfoStore = useUserInfo();
45
+ return userInfoStore.logout();
46
+ };
47
+ /**
48
+ * 获取授权码
49
+ * @returns 授权码
50
+ */
51
+ export const getToken = (): string => {
52
+ if (mainAppApis.value && mainAppApis.value.getToken) return mainAppApis.value.getToken();
53
+ const userInfoStore = useUserInfo();
54
+ return userInfoStore.getToken();
55
+ };
56
+
57
+ /**
58
+ * 获取当前用户信息
59
+ */
60
+ export const getUserInfo = (): UserInfo => {
61
+ if (mainAppApis.value && mainAppApis.value.getUserInfo) return mainAppApis.value.getUserInfo();
62
+ const userInfoStore = useUserInfo();
63
+ return userInfoStore.getUserInfo();
64
+ };
65
+
66
+ /**
67
+ * 子应用推送路由变更
68
+ */
69
+ export const mainAppPush = (subPath: string) => {
70
+ const appInfoStore = useAppInfo();
71
+ appInfoStore.push(subPath);
72
+ };
@@ -0,0 +1,89 @@
1
+ import { useAppInfo } from '@/stores/appInfo';
2
+ import { usePageInfo } from '@/stores/pageInfo';
3
+ import { useUserInfo } from '@/stores/userInfo';
4
+ import { BreadcrumbRoute } from '@/typings/menu';
5
+ import { RouteRecord } from '@skyfox2000/microbase';
6
+ import { Component, h, reactive } from 'vue';
7
+
8
+ /**
9
+ * 初始化菜单信息
10
+ * @param routes 路由信息
11
+ * @param menuData 菜单信息
12
+ */
13
+ export const initMenu = <T>(
14
+ routes: RouteRecord[],
15
+ menuData: T[],
16
+ iconCom: Component,
17
+ iconProps?: Record<string, any>,
18
+ ) => {
19
+ const result = routeToMenu<T>(routes, [], iconCom, iconProps);
20
+ result.forEach((item) => menuData.push(item));
21
+ };
22
+
23
+ /**
24
+ * 转换路由为菜单树结构
25
+ * @param routes 路由
26
+ * @param parentPath 上级路径
27
+ * @returns
28
+ */
29
+ const routeToMenu = <T>(
30
+ routes: RouteRecord[],
31
+ parentPath: string[],
32
+ iconCom: Component,
33
+ iconProps?: Record<string, any>,
34
+ ): T[] => {
35
+ const result: T[] = [];
36
+ const userInfoStore = useUserInfo();
37
+ for (const route of routes) {
38
+ if (
39
+ route.redirect ||
40
+ route.path.includes('/login') ||
41
+ route.path.includes('/error') ||
42
+ route.path.includes('/:page(.*)')
43
+ ) {
44
+ // 非业务菜单
45
+ continue;
46
+ }
47
+ /// 角色匹配,继续处理
48
+ if (route.meta?.roles?.length && !userInfoStore.hasRole(route.meta.roles)) {
49
+ // 角色不匹配,跳过
50
+ if (route.meta?.permission && !userInfoStore.hasPermit(route.meta.permission, ':page')) {
51
+ // 权限不匹配,跳过
52
+ continue;
53
+ }
54
+ }
55
+ /// 角色匹配,继续处理
56
+ const paths = route.path.split('/');
57
+ paths.unshift(...parentPath);
58
+ const children = routeToMenu(route.children || [], paths, iconCom, iconProps);
59
+ const newMenu: T = {
60
+ key: paths.join('/'),
61
+ label: route.name?.toString() ?? '',
62
+ title: route.name?.toString() ?? '',
63
+ icon: route.icon ? h(iconCom, { ...iconProps, icon: route.icon }) : undefined,
64
+ children: children.length > 0 ? children : undefined,
65
+ } as T;
66
+ if (children.length > 0 || !route.children) result.push(newMenu);
67
+ }
68
+ return result;
69
+ };
70
+
71
+ export const crumbs: BreadcrumbRoute[] = reactive([]);
72
+ /**
73
+ * 显示面包屑数据
74
+ */
75
+ export const showBreadcrumb = () => {
76
+ crumbs.length = 0;
77
+ const appInfoStore = useAppInfo();
78
+ const pageInfoStore = usePageInfo();
79
+ let fullPath = pageInfoStore.TabActive;
80
+ const matched = appInfoStore.matchedRoutes(fullPath);
81
+ matched.forEach((item: RouteRecord, index: number) => {
82
+ const crumb: BreadcrumbRoute = {
83
+ index: index,
84
+ path: item.path,
85
+ breadcrumbName: item.name?.toString() ?? '',
86
+ };
87
+ crumbs.push(crumb);
88
+ });
89
+ };