@pubinfo/module-rbac 2.0.0-rc.4 → 2.0.0-rc.5

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 (100) hide show
  1. package/dist/{ComponentSelect-B1ZyenXP.js → ComponentSelect-Bhl9ciUm.js} +12 -12
  2. package/dist/ComponentSelect-Bhwuc86o.js +2 -0
  3. package/dist/ComponentSelect.css +1 -1
  4. package/dist/DynamicRoutesForm-CE2RPs7y.js +169 -0
  5. package/dist/DynamicRoutesForm-D35jFnxF.js +4 -0
  6. package/dist/IconSelect-Ct5FkeHI.js +4 -0
  7. package/dist/IconSelect-HAhfS6hd.js +661 -0
  8. package/dist/IconSelect.css +1 -0
  9. package/dist/{ImportExport-Clbc8xaw.js → ImportExport-ByRTssxP.js} +2 -2
  10. package/dist/ImportExport-D8uzaOMz.js +4 -0
  11. package/dist/{MetaForm-BWy4Lpr8.js → MetaForm-BSGuZXgj.js} +2 -2
  12. package/dist/{MetaForm-DpH9an5A.js → MetaForm-DErrpELP.js} +1 -1
  13. package/dist/OrgAndPosition-CF7ZJtNB.js +2 -0
  14. package/dist/{ResourceEdit-0_cYB0iu.js → ResourceEdit-DF-2_zoi.js} +115 -105
  15. package/dist/ResourceEdit-oAPLPUf0.js +11 -0
  16. package/dist/{ResourceRelation-DMWa09ae.js → ResourceRelation-BRseTUw2.js} +1 -1
  17. package/dist/RoleRelation-YaSQQxQ2.js +2 -0
  18. package/dist/{TenantEdit-D8-Pt7sf.js → TenantEdit-C28473_2.js} +1 -1
  19. package/dist/{UserAuthorization-DuioGghR.js → UserAuthorization-C4SLMNmh.js} +1 -1
  20. package/dist/UserEdit-CyCXz7g3.js +4 -0
  21. package/dist/{UserEdit-C6eNXSCu.js → UserEdit-EOTBOUZu.js} +1 -1
  22. package/dist/components/ResourceIcones/BoxColorPicker.vue.d.ts +23 -0
  23. package/dist/components/ResourceIcones/Select.vue.d.ts +48 -2
  24. package/dist/components/ResourceSelector/hooks/useAppAndResource.d.ts +15 -6
  25. package/dist/components/RoleSelector/useRole.d.ts +68 -4
  26. package/dist/context.d.ts +1 -2
  27. package/dist/{drawerRole-DNsK5a6O.js → drawerRole-CzTW_I2H.js} +1 -1
  28. package/dist/{drawerRole-BeUNs7cF.js → drawerRole-tan379DU.js} +1 -1
  29. package/dist/index.css +1 -1
  30. package/dist/index.js +1427 -1431
  31. package/dist/{resource-CrhX7VRK.js → resource-CyI9ZpmF.js} +9 -8
  32. package/dist/{role-PjmrOdFd.js → role-B2tKqUvP.js} +2 -2
  33. package/dist/{role_group-Dlx-6BZF.js → role_group-DqzFHwKx.js} +1 -1
  34. package/dist/stores/view.d.ts +126 -21
  35. package/dist/{tenant-BGtOP_wo.js → tenant-C8kVbfgt.js} +2 -2
  36. package/dist/{toolbar_setting-OemNp6pO.js → toolbar_setting-Dc_h2Mfe.js} +23 -34
  37. package/dist/toolbar_setting.css +1 -1
  38. package/dist/{user-JCiGXodF.js → user-Dub4HhCZ.js} +3 -3
  39. package/dist/utils/routeSystem.d.ts +126 -21
  40. package/dist/views/blackWhiteList/index.vue.d.ts +40 -40
  41. package/dist/views/data-permission/components/createAndEditDataPermission.vue.d.ts +4 -4
  42. package/dist/views/data-permission/index.vue.d.ts +44 -44
  43. package/dist/views/dictionary/index.vue.d.ts +40 -40
  44. package/dist/views/dictionary/itemlist.vue.d.ts +40 -40
  45. package/dist/views/group/index.vue.d.ts +40 -40
  46. package/dist/views/log_center/components/browserType.vue.d.ts +2 -2
  47. package/dist/views/log_center/components/loginHistoryDetail.vue.d.ts +2 -2
  48. package/dist/views/log_center/components/operateHistoryDetail.vue.d.ts +2 -2
  49. package/dist/views/log_center/login_history.vue.d.ts +40 -40
  50. package/dist/views/log_center/operate_history.vue.d.ts +40 -40
  51. package/dist/views/position/index.vue.d.ts +40 -40
  52. package/dist/views/region/index.vue.d.ts +40 -40
  53. package/dist/views/resource/components/IconSelect.vue.d.ts +10 -0
  54. package/dist/views/resource/hooks/useMetaForm.d.ts +8 -1
  55. package/dist/views/resource/index.vue.d.ts +40 -40
  56. package/dist/views/role/components/ResourceRelation.vue.d.ts +40 -40
  57. package/dist/views/role/index.vue.d.ts +80 -80
  58. package/dist/views/role_group/index.vue.d.ts +40 -40
  59. package/dist/views/tenant/index.vue.d.ts +40 -40
  60. package/dist/views/toolbar_setting/animationData.d.ts +1 -3
  61. package/dist/views/user/index.vue.d.ts +40 -40
  62. package/package.json +7 -3
  63. package/src/components/DirectoryIcones/ComponentsIcones.vue +2 -2
  64. package/src/components/DirectoryIcones/FolderIcones.vue +2 -2
  65. package/src/components/DirectoryIcones/ModuleIcones.vue +2 -2
  66. package/src/components/DirectoryIcones/PackagesIcones.vue +2 -2
  67. package/src/components/DirectoryIcones/SrcIcones.vue +2 -2
  68. package/src/components/DirectoryIcones/ViewIcones.vue +2 -2
  69. package/src/components/ResourceIcones/BoxColorPicker.vue +172 -0
  70. package/src/components/ResourceIcones/Select.vue +227 -15
  71. package/src/context.ts +1 -17
  72. package/src/index.ts +3 -10
  73. package/src/views/resource/components/ComponentSelect.vue +10 -0
  74. package/src/views/resource/components/DynamicRoutesForm.vue +0 -8
  75. package/src/views/resource/components/IconSelect.vue +138 -0
  76. package/src/views/resource/components/ResourceEdit.vue +34 -28
  77. package/src/views/toolbar_setting/animationData.ts +0 -4
  78. package/src/views/toolbar_setting/index.vue +0 -13
  79. package/dist/ComponentSelect-DlVJI5an.js +0 -2
  80. package/dist/DynamicRoutesForm-Cu3WhRpA.js +0 -4
  81. package/dist/DynamicRoutesForm-qjbrykW4.js +0 -376
  82. package/dist/ImportExport-CtEZTU87.js +0 -4
  83. package/dist/OrgAndPosition-3lCm4Fyb.js +0 -2
  84. package/dist/ResourceEdit-CYPzEbqn.js +0 -10
  85. package/dist/RoleRelation-DySyilcs.js +0 -2
  86. package/dist/UserEdit-6HYbgtOh.js +0 -4
  87. /package/dist/{OrgAndPosition-DYxNkasL.js → OrgAndPosition-vnBrq-fn.js} +0 -0
  88. /package/dist/{ResourceRelation-CZ8hGgOm.js → ResourceRelation-B_4woLmP.js} +0 -0
  89. /package/dist/{ResourceRelation-DkKdZ3TD.js → ResourceRelation-CKolCnHC.js} +0 -0
  90. /package/dist/{RoleRelation-B1D1NM_L.js → RoleRelation-DXZxscPY.js} +0 -0
  91. /package/dist/{TenantEdit-DNJzo8SS.js → TenantEdit-B5_v0C8I.js} +0 -0
  92. /package/dist/{UserAuthorization-DPHUk69M.js → UserAuthorization-DTgmWH5Q.js} +0 -0
  93. /package/dist/{dayjs.min-DcxwBe2I.js → dayjs.min-CdDcH50N.js} +0 -0
  94. /package/dist/{drawerRole-DS7fbx1h.js → drawerRole-CwilBmOE.js} +0 -0
  95. /package/dist/{drawerRole-BdoMYLYW.js → drawerRole-E1jHM3v-.js} +0 -0
  96. /package/dist/{enum-D0U38jb6.js → enum-BMY6bmPA.js} +0 -0
  97. /package/dist/{safe_setting-DpWUCv2p.js → safe_setting-uj9p_wW0.js} +0 -0
  98. /package/dist/{tabbar_setting-9UKDc1Ev.js → tabbar_setting-HFHrLVgt.js} +0 -0
  99. /package/dist/{theme_setting-BBLaZ4NS.js → theme_setting-QgpSGXxq.js} +0 -0
  100. /package/dist/{watermark_setting-C1VFRfbW.js → watermark_setting-BcWy6p2_.js} +0 -0
@@ -2,20 +2,63 @@
2
2
  import * as AntdIcons from '@ant-design/icons-vue';
3
3
  import { Empty } from 'ant-design-vue';
4
4
  import { getAllIconModules, PubinfoIcon } from 'pubinfo';
5
+ import { computed, ref, watch } from 'vue';
6
+ import ResourceIconesBoxColorPicker from './BoxColorPicker.vue';
5
7
 
6
8
  defineOptions({ name: 'ResourceIconesSelect' });
7
9
 
8
- // v-model
10
+ // v-model(对外的值)
9
11
  const modelValueRef = defineModel<string>();
12
+ // 额外样式参数的多路 v-model:边框(boxType)、角度(angle)、背景(background)、圆角(radius)、图标色(iconColor)
13
+ const modelBoxType = defineModel<'null' | 'square' | 'prism'>('boxType', { default: 'null' });
14
+ const modelAngle = defineModel<number | string>('angle', { default: 65 });
15
+ const modelBackground = defineModel<string | { from: string, to: string }>('background', { default: { from: '#65E54A', to: '#35C724' } });
16
+ const modelRadius = defineModel<number | string>('radius', { default: 6 });
17
+ const modelIconColor = defineModel<string>('iconColor', { default: '' });
18
+ // 对话框内部临时选择值(仅在点击确定时写回到 v-model)
19
+ const tempValueRef = ref<string | undefined>(modelValueRef.value);
20
+ // 对话框内部样式状态
21
+ const boxType = ref<'null' | 'square' | 'prism'>(modelBoxType.value || 'null');
22
+ const boxAngle = ref<number | string>(modelAngle.value ?? 65);
23
+ const boxBackground = ref<string | { from: string, to: string }>(modelBackground.value ?? { from: '#65E54A', to: '#35C724' });
24
+ const boxRadius = ref<number | string>(modelRadius.value ?? 6);
25
+ // 图标颜色:无背景默认不着色(空字符串表示未设置);有背景默认白色
26
+ const iconColor = ref<string>(modelIconColor.value ?? '');
27
+ const iconColorPickerRef = ref<HTMLInputElement | null>(null);
10
28
 
11
29
  // 弹窗
12
30
  const visibleRef = ref(false);
13
31
  function handleOpenModal() {
32
+ // 打开时同步一次外部值到临时值,避免未确认的变更影响外部
33
+ tempValueRef.value = modelValueRef.value;
34
+ // 同步样式参数到对话框内部状态
35
+ boxType.value = modelBoxType.value || 'null';
36
+ boxAngle.value = modelAngle.value ?? 65;
37
+ // 深拷贝背景,避免未确认时影响外部,同时确保确认时能产生新引用
38
+ const bg = modelBackground.value;
39
+ boxBackground.value = typeof bg === 'string'
40
+ ? bg
41
+ : (bg ? { ...(bg as any) } : { from: '#65E54A', to: '#35C724' });
42
+ boxRadius.value = modelRadius.value ?? 6;
43
+ iconColor.value = modelIconColor.value ?? '';
14
44
  visibleRef.value = true;
15
45
  }
16
46
  function handleClose() {
17
47
  visibleRef.value = false;
18
48
  }
49
+ function handleConfirm() {
50
+ // 将临时值写回 v-model,再关闭弹窗
51
+ modelValueRef.value = tempValueRef.value as any;
52
+ // 回写样式参数
53
+ modelBoxType.value = boxType.value;
54
+ modelAngle.value = boxAngle.value;
55
+ // 使用新对象引用写回,确保仅背景变化也会触发更新
56
+ const bg2 = boxBackground.value as any;
57
+ modelBackground.value = typeof bg2 === 'string' ? bg2 : (bg2 ? { ...bg2 } : bg2);
58
+ modelRadius.value = boxRadius.value;
59
+ modelIconColor.value = iconColor.value;
60
+ visibleRef.value = false;
61
+ }
19
62
 
20
63
  // 基础数据
21
64
  interface IconItem { name: string, module?: string }
@@ -134,24 +177,51 @@ function onPageChange(page: number) {
134
177
 
135
178
  // 选择
136
179
  function handleSelect(icon: IconItem) {
137
- modelValueRef.value = icon.name;
138
- handleClose();
180
+ tempValueRef.value = icon.name;
139
181
  }
140
182
 
141
183
  const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
184
+ function openIconColorPicker() {
185
+ if (!((tempValueRef.value || '').startsWith('antd:'))) {
186
+ return;
187
+ }
188
+ const el = iconColorPickerRef.value;
189
+ if (el) {
190
+ el.style.width = '16px';
191
+ el.style.height = '16px';
192
+ el.click();
193
+ }
194
+ }
195
+ // 当前选中是否为 AntD 图标
196
+ const isAntdIcon = computed(() => (tempValueRef.value || '').startsWith('antd:'));
197
+
198
+ // 当切换背景时,控制图标颜色的默认值:
199
+ // - 选择背景(从无到有)时,默认为白色
200
+ // - 取消背景(从有到无)时,恢复为未设置(使用图标默认颜色)
201
+ watch(boxType, (val, oldVal) => {
202
+ if (oldVal === 'null' && val !== 'null' && !iconColor.value) {
203
+ iconColor.value = '#ffffff';
204
+ }
205
+ });
206
+
207
+ // 预览统一尺寸(外层 box 尺寸)与 padding,确保三个选项视觉对齐
208
+ const previewBoxOuter = 24; // 与 Square/Prism 默认保持一致
209
+ const previewBoxPadding = 4; // 与 shapeBoxDefaults 中的 padding 对齐
142
210
  </script>
143
211
 
144
212
  <template>
145
- <a-input v-model:value="modelValueRef" @click="handleOpenModal">
146
- <template #addonAfter>
147
- <a-tooltip>
148
- <template #title>
149
- <PubinfoIcon :name="modelValueRef || ''" size="40px" class="cursor-default" />
150
- </template>
151
- <PubinfoIcon :name="modelValueRef || ''" size="20px" class="cursor-default" />
152
- </a-tooltip>
153
- </template>
154
- </a-input>
213
+ <slot :open="handleOpenModal">
214
+ <a-input :value="modelValueRef" readonly @click="handleOpenModal">
215
+ <template #addonAfter>
216
+ <a-tooltip>
217
+ <template #title>
218
+ <PubinfoIcon :name="modelValueRef || ''" size="40px" class="cursor-default" />
219
+ </template>
220
+ <PubinfoIcon :name="modelValueRef || ''" size="20px" class="cursor-default" />
221
+ </a-tooltip>
222
+ </template>
223
+ </a-input>
224
+ </slot>
155
225
  <a-form-item-rest>
156
226
  <a-modal
157
227
  v-model:open="visibleRef"
@@ -210,8 +280,10 @@ const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
210
280
  </div>
211
281
  </template>
212
282
  <div
213
- class="group relative flex flex-col items-center justify-center cursor-pointer rounded-md"
214
- :class="modelValueRef === icon.name ? 'border-[#1677ff] ring-2 ring-[#1677ff80]' : ''"
283
+ class="group relative flex flex-col items-center justify-center cursor-pointer rounded-md border"
284
+ :class="tempValueRef === icon.name
285
+ ? 'border-solid border-[#1677ff]'
286
+ : 'border-dashed border-[#e5e7eb] dark:border-[#30363d]'"
215
287
  @click="handleSelect(icon)"
216
288
  >
217
289
  <PubinfoIcon :name="icon.name" size="30px" />
@@ -234,7 +306,147 @@ const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
234
306
  <a-empty :image="simpleImage" description="没有匹配的图标" />
235
307
  </div>
236
308
  </div>
309
+ <!-- 右侧预留区域 -->
310
+ <div v-if="tempValueRef" class="relative w-60 flex flex-col border-l border-[#e5e7eb] dark:border-[#30363d] pl-4 min-h-0">
311
+ <div class="flex flex-col flex-1 w-full rounded-md border border-dashed border-[#d0d7de] dark:border-[#30363d] text-12px text-[#6b7280] dark:text-[#94a3b8] p-3 overflow-hidden">
312
+ <!-- 选项:固定顶部 -->
313
+ <div class="shrink-0 flex justify-center items-center pb-4 border-b border-b-dashed border-b-[#d0d7de] dark:border-b-[#30363d]">
314
+ <a-radio-group v-model:value="boxType" class="flex justify-center items-center">
315
+ <a-radio-button value="null">
316
+ <div class="w-8 h-8 flex justify-center items-center">
317
+ <PubinfoIcon :name="tempValueRef" :size="previewBoxOuter - previewBoxPadding * 2" />
318
+ </div>
319
+ </a-radio-button>
320
+ <a-radio-button value="square">
321
+ <div class="w-8 h-8 flex justify-center items-center">
322
+ <PubinfoIcon
323
+ :name="tempValueRef"
324
+ box="square"
325
+ :size="previewBoxOuter"
326
+ radius="6px"
327
+ color="#ffffff"
328
+ />
329
+ </div>
330
+ </a-radio-button>
331
+ <a-radio-button value="prism">
332
+ <div class="w-8 h-8 flex justify-center items-center">
333
+ <PubinfoIcon
334
+ :name="tempValueRef"
335
+ box="prism"
336
+ :size="previewBoxOuter"
337
+ color="#ffffff"
338
+ radius="2px"
339
+ />
340
+ </div>
341
+ </a-radio-button>
342
+ </a-radio-group>
343
+ </div>
344
+ <!-- 中间配置:保持在中部,可滚动,占据剩余空间 -->
345
+ <div class="flex-1 w-full mt-3 px-2 overflow-auto space-y-3">
346
+ <!-- 背景设置:始终显示;当 boxType 为 null 时禁用 -->
347
+ <div class="flex flex-col justify-start w-full opacity-100">
348
+ <div class="text-12px mb-2 opacity-70">
349
+ 背景设置
350
+ </div>
351
+ <ResourceIconesBoxColorPicker
352
+ v-model:angle="boxAngle"
353
+ v-model:background="boxBackground"
354
+ v-model:radius="boxRadius"
355
+ :disabled="boxType === 'null'"
356
+ />
357
+ </div>
358
+ <!-- 图标颜色设置:始终显示;非 AntD 图标时禁用 -->
359
+ <div class="flex items-center gap-3 w-full">
360
+ <label class="text-12px opacity-70 w-14 shrink-0">图标色</label>
361
+ <a-input
362
+ v-model:value="iconColor"
363
+ allow-clear
364
+ :placeholder="boxType === 'null' ? '默认' : '#ffffff'"
365
+ size="small"
366
+ class="flex-1 min-w-0"
367
+ :disabled="!isAntdIcon"
368
+ >
369
+ <template #prefix>
370
+ <div class="relative inline-flex items-center">
371
+ <button
372
+ type="button"
373
+ :disabled="!isAntdIcon"
374
+ class="w-4 h-4 rounded border border-gray-300 dark:border-gray-700"
375
+ :class="!isAntdIcon ? 'opacity-50 cursor-not-allowed' : ''"
376
+ :style="{ background: iconColor || '#ffffff' }"
377
+ @click="openIconColorPicker"
378
+ />
379
+ <input
380
+ ref="iconColorPickerRef"
381
+ type="color"
382
+ :value="iconColor || '#ffffff'"
383
+ :disabled="!isAntdIcon"
384
+ class="absolute left-0 top-0 opacity-0 pointer-events-none"
385
+ style="width:16px;height:16px;"
386
+ @input="iconColor = ($event.target as HTMLInputElement).value"
387
+ >
388
+ </div>
389
+ </template>
390
+ </a-input>
391
+ </div>
392
+ </div>
393
+ <!-- 预览:固定底部 -->
394
+ <div class="w-full mt-auto">
395
+ <div class="py-4 rounded-md border-t border-t-dashed border-t-[#d0d7de] dark:border-t-[#30363d] flex flex-col items-center gap-2">
396
+ <template v-if="tempValueRef">
397
+ <PubinfoIcon
398
+ v-if="boxType === 'null'"
399
+ :name="tempValueRef"
400
+ :size="80"
401
+ :color="(isAntdIcon && iconColor) ? iconColor : undefined"
402
+ />
403
+ <PubinfoIcon
404
+ v-else
405
+ :name="tempValueRef"
406
+ :box="boxType"
407
+ :size="80"
408
+ :angle="boxAngle"
409
+ :background="boxBackground"
410
+ :radius="boxRadius"
411
+ :color="isAntdIcon ? (iconColor || '#ffffff') : undefined"
412
+ />
413
+ </template>
414
+ </div>
415
+ </div>
416
+ </div>
417
+ <!-- 侧栏底部操作按钮 -->
418
+ <div class="mt-4 flex justify-end gap-2">
419
+ <a-button @click="handleClose">
420
+ 取消
421
+ </a-button>
422
+ <a-button type="primary" @click="handleConfirm">
423
+ 确定
424
+ </a-button>
425
+ </div>
426
+ </div>
237
427
  </div>
238
428
  </a-modal>
239
429
  </a-form-item-rest>
240
430
  </template>
431
+
432
+ <style scoped>
433
+ /* Center radio group and prevent wrapper stretching */
434
+ :deep(.ant-radio-group) {
435
+ display: inline-flex;
436
+ align-items: center;
437
+ }
438
+ :deep(.ant-radio-button-wrapper) {
439
+ display: inline-flex;
440
+ align-items: center;
441
+ justify-content: center;
442
+ padding: 4px 10px;
443
+ height: auto !important;
444
+ line-height: 1 !important;
445
+ flex: 0 0 auto;
446
+ }
447
+ :deep(.ant-radio-button-wrapper > span) {
448
+ display: inline-flex;
449
+ align-items: center;
450
+ justify-content: center;
451
+ }
452
+ </style>
package/src/context.ts CHANGED
@@ -1,20 +1,4 @@
1
1
  import type { InternalContext } from './interface';
2
2
  import { createContext } from 'pubinfo';
3
3
 
4
- // 持久化 rbac 模块上下文,避免 HMR 后丢失 request
5
- const g = globalThis as any;
6
- g.__PUBINFO__ = g.__PUBINFO__ || {};
7
- if (!g.__PUBINFO__.rbacCtx) {
8
- g.__PUBINFO__.rbacCtx = createContext<InternalContext>();
9
- }
10
- export const ctx = g.__PUBINFO__.rbacCtx as ReturnType<typeof createContext<InternalContext>>;
11
-
12
- // HMR 恢复
13
- if (g.__PUBINFO__.rbacState) {
14
- try {
15
- ctx.set(g.__PUBINFO__.rbacState);
16
- }
17
- catch {
18
- // ignore
19
- }
20
- }
4
+ export const ctx = createContext<InternalContext>('rbac');
package/src/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import type { ModuleOptions } from 'pubinfo';
2
2
  import type { RbacOptions } from './interface';
3
- import { defineAsyncRoutes, defineIconModule, defineRouteModule } from 'pubinfo';
4
- import modules from 'virtual:pubinfo-resolver';
3
+ import { defineAsyncRoutes } from 'pubinfo';
5
4
  import { useWebSocketManager } from '@/composables/useWebSocketManager';
6
5
  import { useViewStore } from '@/stores/view';
7
6
  import { ctx } from './context';
@@ -12,16 +11,10 @@ import 'uno.css';
12
11
  export function rbac(options: RbacOptions): ModuleOptions {
13
12
  const { pages, sort, id = 'RBAC', request } = options;
14
13
 
14
+ // ctx.set 会自动缓存以便 HMR 恢复
15
15
  ctx.set({ request });
16
- // 缓存以便 HMR 恢复
17
- (globalThis as any).__PUBINFO__.rbacState = { request };
18
16
 
19
- defineIconModule(id, modules.icons);
20
-
21
- if (pages === 'dynamic') {
22
- defineRouteModule(id, modules.pages);
23
- }
24
- else {
17
+ if (pages !== 'dynamic') {
25
18
  // 兼容 1.x 版本的本地路由
26
19
  const asyncRoutes = filterPages({ pages, id });
27
20
  defineAsyncRoutes(asyncRoutes, { sort });
@@ -279,6 +279,12 @@ function onSelect(keys: (string | number)[], e: any) {
279
279
  border-radius: 6px !important;
280
280
  }
281
281
 
282
+ .dark .directory-tree-root .ant-tree-treenode-selected::before {
283
+ /* Dark theme: use a deeper primary‑tinted background for selected row (previously #e6f4ff) */
284
+ background: #3166D4 !important; /* fallback solid */
285
+ border-radius: 6px !important;
286
+ }
287
+
282
288
  .directory-tree-root .ant-tree-treenode:hover:before {
283
289
  border-radius: 6px !important;
284
290
  }
@@ -287,6 +293,10 @@ function onSelect(keys: (string | number)[], e: any) {
287
293
  color: #000 !important;
288
294
  }
289
295
 
296
+ .dark .directory-tree-root .ant-tree-title{
297
+ color: #FFF !important;
298
+ }
299
+
290
300
  .directory-tree-root .anticon-file{
291
301
  opacity: 0 !important;
292
302
  }
@@ -1,6 +1,5 @@
1
1
  <script setup lang="ts">
2
2
  import type { Form } from '../interface';
3
- import ResourceIconesSelect from '@/components/ResourceIcones/Select.vue';
4
3
  import { RESOURCE_TYPE } from '../enum';
5
4
  import ComponentSelect from './ComponentSelect.vue';
6
5
 
@@ -29,7 +28,6 @@ const show = computed(() => {
29
28
  component: [RESOURCE_TYPE.MENU, RESOURCE_TYPE.NON_MENU].includes(type),
30
29
  iframe: [RESOURCE_TYPE.IFRAME].includes(type),
31
30
  link: [RESOURCE_TYPE.LINK].includes(type),
32
- icon: [RESOURCE_TYPE.DYNAMIC_APP, RESOURCE_TYPE.MICRO_APP, RESOURCE_TYPE.INDEX, RESOURCE_TYPE.MENU, RESOURCE_TYPE.IFRAME, RESOURCE_TYPE.LINK].includes(type),
33
31
  routeName: [RESOURCE_TYPE.MENU, RESOURCE_TYPE.NON_MENU, RESOURCE_TYPE.IFRAME, RESOURCE_TYPE.LINK].includes(type),
34
32
  activeMenu: [RESOURCE_TYPE.MENU, RESOURCE_TYPE.NON_MENU, RESOURCE_TYPE.IFRAME, RESOURCE_TYPE.LINK].includes(type),
35
33
  };
@@ -71,12 +69,6 @@ const isDynamicRoutesForm = computed(() => {
71
69
  </a-form-item>
72
70
  </a-col>
73
71
 
74
- <a-col v-if="show.icon" :span="12">
75
- <a-form-item label="资源图标" name="icon">
76
- <ResourceIconesSelect v-model="form.icon" />
77
- </a-form-item>
78
- </a-col>
79
-
80
72
  <a-col v-if="show.routeName" :span="12">
81
73
  <a-form-item label="路由名称" :name="['meta', 'routeName']">
82
74
  <a-input v-model:value="form.meta!.routeName" placeholder="请输入路由名称(name)" :maxlength="256" />
@@ -0,0 +1,138 @@
1
+ <script setup lang="ts">
2
+ import type { Form } from '../interface';
3
+ import { PubinfoIcon } from 'pubinfo';
4
+ import ResourceIconesSelect from '../../../components/ResourceIcones/Select.vue';
5
+ import { RESOURCE_TYPE } from '../enum';
6
+
7
+ defineOptions({
8
+ name: 'ResourceIconSelect',
9
+ });
10
+
11
+ const modelValue = defineModel<Form>({
12
+ default: {
13
+ meta: {
14
+ url: '',
15
+ component: '',
16
+ },
17
+ },
18
+ });
19
+
20
+ // 将外部存储扩展到 meta.iconOptions,避免破坏后端接口的 `icon: string` 结构
21
+ const iconOptions = computed({
22
+ get() {
23
+ const meta = (modelValue.value.meta as any) || ((modelValue.value.meta as any) = {});
24
+ if (!meta.iconOptions) {
25
+ meta.iconOptions = {
26
+ boxType: 'null',
27
+ angle: 65,
28
+ background: { from: '#65E54A', to: '#35C724' },
29
+ radius: 6,
30
+ iconColor: '',
31
+ };
32
+ }
33
+ return meta.iconOptions;
34
+ },
35
+ set(val) {
36
+ const meta = (modelValue.value.meta as any) || ((modelValue.value.meta as any) = {});
37
+ meta.iconOptions = val || {
38
+ boxType: 'null',
39
+ angle: 65,
40
+ background: { from: '#65E54A', to: '#35C724' },
41
+ radius: 6,
42
+ iconColor: '',
43
+ };
44
+ },
45
+ });
46
+
47
+ const show = computed(() => {
48
+ const type = modelValue.value.type as RESOURCE_TYPE;
49
+
50
+ return {
51
+ icon: [RESOURCE_TYPE.DYNAMIC_APP, RESOURCE_TYPE.MICRO_APP, RESOURCE_TYPE.INDEX, RESOURCE_TYPE.MENU, RESOURCE_TYPE.IFRAME, RESOURCE_TYPE.LINK].includes(type),
52
+ };
53
+ });
54
+
55
+ /** 是否展示微应用/动态应用的额外表单项 */
56
+ const isDynamicRoutesForm = computed(() => {
57
+ return true;
58
+ });
59
+
60
+ /** 清除已选择的图标 */
61
+ function clearIcon(e: MouseEvent) {
62
+ e.stopPropagation();
63
+ if (modelValue.value.icon) {
64
+ modelValue.value.icon = undefined; // 设为空串以触发 v-if 切换
65
+ }
66
+ }
67
+ </script>
68
+
69
+ <template>
70
+ <div v-if="isDynamicRoutesForm && show.icon" class="w-full h-full flex justify-center mb-4">
71
+ <ResourceIconesSelect
72
+ v-slot="{ open }"
73
+ v-model="modelValue.icon"
74
+ v-model:box-type="iconOptions.boxType"
75
+ v-model:angle="iconOptions.angle"
76
+ v-model:background="iconOptions.background"
77
+ v-model:radius="iconOptions.radius"
78
+ v-model:icon-color="iconOptions.iconColor"
79
+ >
80
+ <!-- select box -->
81
+ <template v-if="modelValue.icon">
82
+ <!-- 已选择图标展示盒子 -->
83
+ <div
84
+ class="size-20 flex justify-center items-center cursor-pointer
85
+ rounded-md border-px border-dashed relative group
86
+ border-[#d9d9d9] dark:border-[#424242]
87
+ hover:border-[#5794f7] dark:hover:border-[#5187e1]"
88
+ @click="open"
89
+ >
90
+ <PubinfoIcon
91
+ v-if="iconOptions.boxType && iconOptions.boxType !== 'null'"
92
+ :name="modelValue.icon"
93
+ :box="iconOptions.boxType"
94
+ :size="76"
95
+ :radius="iconOptions.radius"
96
+ :background="iconOptions.background"
97
+ :angle="iconOptions.angle"
98
+ :color="(modelValue.icon || '').startsWith('antd:') ? (iconOptions.iconColor || '#ffffff') : undefined"
99
+ />
100
+ <PubinfoIcon
101
+ v-else
102
+ :name="modelValue.icon"
103
+ size="76px"
104
+ :color="(modelValue.icon || '').startsWith('antd:') ? (iconOptions.iconColor || undefined) : undefined"
105
+ />
106
+ <!-- 清除按钮 -->
107
+ <button
108
+ v-if="modelValue.icon"
109
+ class="absolute -top-1.5 -right-1.5 size-5 flex items-center justify-center rounded-full
110
+ cursor-pointer border-none op-65 dark:op-55 hover:op-100"
111
+ type="button"
112
+ @click="clearIcon"
113
+ >
114
+ <PubinfoIcon name="antd:CloseOutlined" size="10px" />
115
+ </button>
116
+ </div>
117
+ </template>
118
+ <!-- null icon select box -->
119
+ <template v-else>
120
+ <div
121
+ class="size-20 flex justify-center items-center
122
+ rounded-md border-px border-dashed cursor-pointer
123
+ border-[#d9d9d9] dark:border-[#424242]
124
+ hover:border-[#5794f7] dark:hover:border-[#5187e1]
125
+ text-[#d9d9d9] dark:text-[#424242]
126
+ hover:text-[#5794f7] dark:hover:text-[#5187e1]
127
+ transition"
128
+ @click="open"
129
+ >
130
+ <div class="flex flex-col items-center justify-center">
131
+ <PubinfoIcon name="antd:PlusOutlined" />
132
+ <span class="text-10px mt-1">选择图标</span>
133
+ </div>
134
+ </div>
135
+ </template>
136
+ </ResourceIconesSelect>
137
+ </div>
138
+ </template>
@@ -11,6 +11,7 @@ import { ACTION, RESOURCE_COLOR, RESOURCE_TYPE, resourceOptions } from '../enum'
11
11
  import { useMetaForm } from '../hooks/useMetaForm';
12
12
  import { defaultMeta } from '../model';
13
13
  import DynamicRoutesForm from './DynamicRoutesForm.vue';
14
+ import ResourceIconSelect from './IconSelect.vue';
14
15
  import MetaForm from './MetaForm.vue';
15
16
 
16
17
  defineOptions({
@@ -202,38 +203,43 @@ defineExpose({
202
203
  :label-col="{ style: { width: '100px' } }"
203
204
  >
204
205
  <a-row :gutter="8">
205
- <a-col v-if="parentNode?.name" :span="24">
206
- <a-form-item name="parentId">
207
- <template #label>
208
- <DatabaseOutlined class="mr-1" />
209
- 父资源
210
- </template>
211
- {{ parentNode?.name }}
212
- </a-form-item>
213
- </a-col>
214
- <a-col :span="24">
215
- <a-form-item label="资源类型" name="type">
216
- <div
217
- v-if="state.title === ACTION.EDIT"
218
- class="flex items-center space-x-1"
219
- >
220
- <!-- <PubinfoIcon v-if="form.type" :name="`resource-${RESOURCE_ICON[Number(form.type)]}`" class="text-xl" />
206
+ <a-col :span="12">
207
+ <a-col v-if="parentNode?.name" :span="24">
208
+ <a-form-item name="parentId">
209
+ <template #label>
210
+ <DatabaseOutlined class="mr-1" />
211
+ 父资源
212
+ </template>
213
+ {{ parentNode?.name }}
214
+ </a-form-item>
215
+ </a-col>
216
+ <a-col :span="24">
217
+ <a-form-item label="资源类型" name="type">
218
+ <div
219
+ v-if="state.title === ACTION.EDIT"
220
+ class="flex items-center space-x-1"
221
+ >
222
+ <!-- <PubinfoIcon v-if="form.type" :name="`resource-${RESOURCE_ICON[Number(form.type)]}`" class="text-xl" />
221
223
  <span>
222
224
  {{ resourceComputedOptions.find(e => e.value === form.type)?.label }}
223
225
  </span> -->
224
226
 
225
- <a-tag v-if="form.type" :color="RESOURCE_COLOR[Number(form.type)]" :bordered="false">
226
- {{ resourceComputedOptions.find(e => e.value === form.type)?.label }}
227
- </a-tag>
228
- </div>
229
- <a-radio-group
230
- v-else
231
- v-model:value="form.type"
232
- :options="resourceComputedOptions"
233
- option-type="button"
234
- button-style="solid"
235
- />
236
- </a-form-item>
227
+ <a-tag v-if="form.type" :color="RESOURCE_COLOR[Number(form.type)]" :bordered="false">
228
+ {{ resourceComputedOptions.find(e => e.value === form.type)?.label }}
229
+ </a-tag>
230
+ </div>
231
+ <a-radio-group
232
+ v-else
233
+ v-model:value="form.type"
234
+ :options="resourceComputedOptions"
235
+ option-type="button"
236
+ button-style="solid"
237
+ />
238
+ </a-form-item>
239
+ </a-col>
240
+ </a-col>
241
+ <a-col :span="12">
242
+ <ResourceIconSelect v-model="form" />
237
243
  </a-col>
238
244
  </a-row>
239
245
  <a-row :gutter="8">
@@ -1,6 +1,5 @@
1
1
  import darkEnableColorSchemeData from '@/assets/lottie/toolbar/dark/enableColorScheme.json?url';
2
2
  import darkEnableFullscreenData from '@/assets/lottie/toolbar/dark/enableFullscreen.json?url';
3
- import darkEnableI18nData from '@/assets/lottie/toolbar/dark/enableI18n.json?url';
4
3
  import darkEnableNotificationData from '@/assets/lottie/toolbar/dark/enableNotification.json?url';
5
4
  import darkEnablePageReloadData from '@/assets/lottie/toolbar/dark/enablePageReload.json?url';
6
5
  import darkEnableUserPreferencesData from '@/assets/lottie/toolbar/dark/enableUserPreferences.json?url';
@@ -11,7 +10,6 @@ import darkNavSearchEnableHotkeysData from '@/assets/lottie/toolbar/dark/navSear
11
10
  import lightEnableColorSchemeData from '@/assets/lottie/toolbar/light/enableColorScheme.json?url';
12
11
  import lightEnableFullscreenData from '@/assets/lottie/toolbar/light/enableFullscreen.json?url';
13
12
 
14
- import lightEnableI18nData from '@/assets/lottie/toolbar/light/enableI18n.json?url';
15
13
  import lightEnableNotificationData from '@/assets/lottie/toolbar/light/enableNotification.json?url';
16
14
 
17
15
  import lightEnablePageReloadData from '@/assets/lottie/toolbar/light/enablePageReload.json?url';
@@ -23,7 +21,6 @@ import lightNavSearchEnableHotkeysData from '@/assets/lottie/toolbar/light/navSe
23
21
  export {
24
22
  darkEnableColorSchemeData,
25
23
  darkEnableFullscreenData,
26
- darkEnableI18nData,
27
24
  darkEnableNotificationData,
28
25
  darkEnablePageReloadData,
29
26
  darkEnableUserPreferencesData,
@@ -32,7 +29,6 @@ export {
32
29
  darkNavSearchEnableHotkeysData,
33
30
  lightEnableColorSchemeData,
34
31
  lightEnableFullscreenData,
35
- lightEnableI18nData,
36
32
  lightEnableNotificationData,
37
33
 
38
34
  lightEnablePageReloadData,