@pubinfo/module-rbac 2.0.12 → 2.0.14

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 (97) hide show
  1. package/dist/{DynamicRoutesForm-DzOGoyf3.js → DynamicRoutesForm-BU4EAISs.js} +1 -1
  2. package/dist/{DynamicRoutesForm.vue_vue_type_script_setup_true_lang-lpCaLpPL.js → DynamicRoutesForm.vue_vue_type_script_setup_true_lang-bEqJ03T_.js} +6 -6
  3. package/dist/{IconSelect-CkqLR5Dg.js → IconSelect-DtNpk3Rj.js} +1 -1
  4. package/dist/IconSelect.css +1 -1
  5. package/dist/IconSelect.vue_vue_type_script_setup_true_lang-D450SiKg.js +757 -0
  6. package/dist/{ImportExport-BD8o7IvB.js → ImportExport-DAe_Sl13.js} +1 -1
  7. package/dist/{ImportExport.vue_vue_type_script_setup_true_lang-BzdP0ADd.js → ImportExport.vue_vue_type_script_setup_true_lang-CV0DrMvw.js} +1 -1
  8. package/dist/LayoutForm-C-vNwp3q.js +173 -0
  9. package/dist/LayoutForm.css +1 -0
  10. package/dist/{MetaForm-DtlyvzoM.js → MetaForm-13JASu7v.js} +142 -105
  11. package/dist/MetaForm.css +1 -1
  12. package/dist/{ResourceEdit-WpEdbzaw.js → ResourceEdit-D2YFu-Gk.js} +1 -1
  13. package/dist/ResourceEdit.vue_vue_type_script_setup_true_lang-Cb0mKIlJ.js +429 -0
  14. package/dist/{ResourceRelation-DGiGYy6V.js → ResourceRelation-CeM9Eq39.js} +10 -11
  15. package/dist/{ResourceRelation-26FnHEfU.js → ResourceRelation-qhIdXLAy.js} +12 -13
  16. package/dist/{TenantEdit-B24X5nND.js → TenantEdit-C61O1pSu.js} +1 -1
  17. package/dist/{TenantEdit.vue_vue_type_script_setup_true_lang-Bu5HcJJv.js → TenantEdit.vue_vue_type_script_setup_true_lang-BwsA9hth.js} +39 -40
  18. package/dist/{UserAuthorization-Co6JV0py.js → UserAuthorization-rqttVub5.js} +9 -10
  19. package/dist/components/OrgUserSelector/OrgSelector.vue.d.ts +1 -1
  20. package/dist/components/OrgUserSelector/UserSelector.vue.d.ts +1 -1
  21. package/dist/components/ResourceIcones/BoxColorPicker.vue.d.ts +3 -0
  22. package/dist/components/ResourceIcones/Select.vue.d.ts +3 -0
  23. package/dist/{createAndEditDataPermission-BhOLGGna.js → createAndEditDataPermission-DPeFulie.js} +1 -1
  24. package/dist/{createAndEditDataPermission.vue_vue_type_script_setup_true_lang-D5wInKwO.js → createAndEditDataPermission.vue_vue_type_script_setup_true_lang-DlUIYtif.js} +98 -101
  25. package/dist/{drawerRole-DHLv47_z.js → drawerRole-CXGVqxPX.js} +1 -1
  26. package/dist/{drawerRole-DUWRM43j.js → drawerRole-CfJjf3v_.js} +1 -1
  27. package/dist/{drawerRole.vue_vue_type_script_setup_true_lang-BET5ukoo.js → drawerRole.vue_vue_type_script_setup_true_lang-B-VZS3gq.js} +42 -43
  28. package/dist/{drawerRole.vue_vue_type_script_setup_true_lang-BK3zjhop.js → drawerRole.vue_vue_type_script_setup_true_lang-B1eT0e_Q.js} +1 -1
  29. package/dist/{enum-CjyX3J0o.js → enum-D5iPiZiT.js} +0 -1
  30. package/dist/{index-Csfu3kAr.js → index-B5aP0re3.js} +6 -7
  31. package/dist/{index-DwpOyfRe.js → index-BEB4liGe.js} +8 -9
  32. package/dist/{index-DfuGXjYh.js → index-BNwGLzXe.js} +21 -22
  33. package/dist/{index-DdoOaWs-.js → index-BPkJOFXE.js} +3 -3
  34. package/dist/{index-ioZh8Qom.js → index-BXvJhqJD.js} +6 -7
  35. package/dist/{index-BFx8F5lO.js → index-C2G4xRrJ.js} +8 -9
  36. package/dist/{index-x9Iim2eD.js → index-C5WD-YUu.js} +7 -8
  37. package/dist/{index-rNYtJ7Om.js → index-CbVJdR5M.js} +6 -7
  38. package/dist/{index-DECno0gv.js → index-ChWnVs6j.js} +2 -2
  39. package/dist/{index-C9hHp4Iw.js → index-CoZM8pyH.js} +1 -1
  40. package/dist/{index-B2tTTlBE.js → index-Copy5kiW.js} +276 -273
  41. package/dist/index-D3Xw_aOi.js +144 -0
  42. package/dist/{index-CCw3ZJgQ.js → index-DHVEXch0.js} +8 -9
  43. package/dist/{index-COWUocfL.js → index-Dl0hWfIE.js} +7 -8
  44. package/dist/{index-BIpmC999.js → index-En8oOf9e.js} +8 -9
  45. package/dist/{index-ZQuRR9fG.js → index-VZuizuKs.js} +1 -1
  46. package/dist/{index-BwWrE3dY.js → index-XhYtVVT9.js} +6 -7
  47. package/dist/{index-BUmMaC21.js → index-nAPHtTM7.js} +5 -6
  48. package/dist/{index-y9CQTo95.js → index-qn7U9T51.js} +9 -10
  49. package/dist/index.d.ts +1 -0
  50. package/dist/index.js +2578 -2303
  51. package/dist/index10.css +1 -1
  52. package/dist/index11.css +1 -1
  53. package/dist/index12.css +1 -1
  54. package/dist/index13.css +1 -1
  55. package/dist/index14.css +1 -1
  56. package/dist/index15.css +1 -1
  57. package/dist/index16.css +1 -1
  58. package/dist/index17.css +1 -1
  59. package/dist/index4.css +1 -1
  60. package/dist/index5.css +1 -1
  61. package/dist/index6.css +1 -1
  62. package/dist/index7.css +1 -1
  63. package/dist/index8.css +1 -1
  64. package/dist/index9.css +1 -1
  65. package/dist/{loginHistoryDetail-B6eI-TmJ.js → loginHistoryDetail-BZVwG8m2.js} +1 -1
  66. package/dist/{login_history-B4Q1NCJn.js → login_history-CMZGS2PN.js} +5 -6
  67. package/dist/{operateHistoryDetail-CLZ9lyua.js → operateHistoryDetail-Badx3zl-.js} +1 -1
  68. package/dist/{operate_history-KASZ7_JO.js → operate_history-B42WNY1u.js} +5 -6
  69. package/dist/{setItem-BwMHXOUm.js → setItem-Blb3dox4.js} +1 -1
  70. package/dist/{useAppAndResource-38HCqYKa.js → useAppAndResource-DDywX2iz.js} +11 -12
  71. package/dist/views/data-permission/components/createAndEditDataPermission.vue.d.ts +3 -3
  72. package/dist/views/data-permission/index.vue.d.ts +3 -3
  73. package/dist/views/resource/components/LayoutForm.vue.d.ts +10 -0
  74. package/dist/views/resource/hooks/useLayoutForm.d.ts +12 -0
  75. package/dist/views/resource/model.d.ts +13 -0
  76. package/package.json +7 -7
  77. package/src/components/ResourceIcones/BoxColorPicker.vue +64 -8
  78. package/src/components/ResourceIcones/Select.vue +13 -5
  79. package/src/index.ts +1 -0
  80. package/src/views/organization/index.vue +17 -10
  81. package/src/views/resource/components/DynamicRoutesForm.vue +1 -1
  82. package/src/views/resource/components/IconSelect.vue +7 -3
  83. package/src/views/resource/components/LayoutForm.vue +92 -0
  84. package/src/views/resource/components/MetaForm.vue +11 -0
  85. package/src/views/resource/components/ResourceEdit.vue +26 -8
  86. package/src/views/resource/enum.ts +0 -1
  87. package/src/views/resource/hooks/useLayoutForm.ts +57 -0
  88. package/src/views/resource/hooks/useMetaForm.ts +4 -0
  89. package/src/views/resource/index.vue +0 -3
  90. package/src/views/resource/model.ts +23 -1
  91. package/dist/IconSelect.vue_vue_type_script_setup_true_lang-BoGxAoNd.js +0 -703
  92. package/dist/ResourceEdit.vue_vue_type_script_setup_true_lang-id6jZAko.js +0 -392
  93. package/dist/_plugin-vue_export-helper-CHgC5LLL.js +0 -9
  94. package/dist/enum-CTUzwCYG.js +0 -4
  95. package/dist/index-53oBz1jp.js +0 -299
  96. package/dist/index-DVt5k9xh.js +0 -145
  97. package/dist/index18.css +0 -1
@@ -3,12 +3,13 @@ import { ref, watch } from 'vue';
3
3
 
4
4
  defineOptions({ name: 'ResourceIconesBoxColorPicker' });
5
5
 
6
- const props = defineProps<{ angle?: number | string, background?: Background, radius?: number | string, disabled?: boolean }>();
6
+ const props = defineProps<{ angle?: number | string, background?: Background, radius?: number | string, iconSize?: number | string, disabled?: boolean }>();
7
7
 
8
8
  const emit = defineEmits<{
9
9
  (e: 'update:angle', v: number | string): void
10
10
  (e: 'update:background', v: Background): void
11
11
  (e: 'update:radius', v: number | string): void
12
+ (e: 'update:iconSize', v: number | string): void
12
13
  }>();
13
14
 
14
15
  type Background = string | { from: string, to: string };
@@ -34,17 +35,53 @@ function normalizeBg(b?: Background): { from: string, to: string } {
34
35
  }
35
36
  return { from: b.from, to: b.to };
36
37
  }
38
+ // 圆角改为百分比:内部使用纯数字 0-50 表示 0%-50%,对外发射 'xx%'
37
39
  function normalizeRadius(r?: number | string): number {
40
+ if (typeof r === 'string' && r.trim().endsWith('%')) {
41
+ const n = Number.parseFloat(r);
42
+ if (Number.isFinite(n)) {
43
+ return clampPercent(n);
44
+ }
45
+ }
38
46
  if (typeof r === 'number' && Number.isFinite(r)) {
39
- return Math.max(0, r);
47
+ return clampPercent(r);
40
48
  }
41
49
  if (typeof r === 'string') {
42
50
  const n = Number.parseFloat(r);
43
51
  if (Number.isFinite(n)) {
44
- return Math.max(0, n);
52
+ return clampPercent(n);
53
+ }
54
+ }
55
+ return 50;
56
+ }
57
+
58
+ // 图标大小(百分比):内部使用 0-100 数字,外部发射 'xx%'
59
+ function normalizeIconSize(v?: number | string): number {
60
+ if (typeof v === 'string' && v.trim().endsWith('%')) {
61
+ const n = Number.parseFloat(v);
62
+ if (Number.isFinite(n)) {
63
+ return clamp100(n);
64
+ }
65
+ }
66
+ if (typeof v === 'number' && Number.isFinite(v)) {
67
+ return clamp100(v);
68
+ }
69
+ if (typeof v === 'string') {
70
+ const n = Number.parseFloat(v);
71
+ if (Number.isFinite(n)) {
72
+ return clamp100(n);
45
73
  }
46
74
  }
47
- return 6; // default px
75
+ return 50;
76
+ }
77
+
78
+ function clampPercent(n: number) {
79
+ // 一般正方形圆角超过 50% 没有意义,这里限制 0-50
80
+ return Math.min(50, Math.max(0, n));
81
+ }
82
+
83
+ function clamp100(n: number) {
84
+ return Math.min(100, Math.max(0, n));
48
85
  }
49
86
 
50
87
  const init = normalizeBg(props.background);
@@ -52,6 +89,7 @@ const fromColor = ref<string>(init.from);
52
89
  const toColor = ref<string>(init.to);
53
90
  const angleRef = ref<number>(normalizeAngle(props.angle));
54
91
  const radiusRef = ref<number>(normalizeRadius(props.radius));
92
+ const iconSizeRef = ref<number>(normalizeIconSize(props.iconSize));
55
93
 
56
94
  // 原生 color 输入:放在前缀里,用透明绝对定位来锚定弹层位置
57
95
  const fromColorPickerRef = ref<HTMLInputElement | null>(null);
@@ -83,7 +121,11 @@ function openToPicker() {
83
121
  watch([fromColor, toColor, angleRef, radiusRef], () => {
84
122
  emit('update:background', { from: fromColor.value, to: toColor.value });
85
123
  emit('update:angle', angleRef.value);
86
- emit('update:radius', radiusRef.value);
124
+ emit('update:radius', `${radiusRef.value}%`);
125
+ }, { immediate: true });
126
+
127
+ watch(iconSizeRef, () => {
128
+ emit('update:iconSize', `${iconSizeRef.value}%`);
87
129
  }, { immediate: true });
88
130
 
89
131
  watch(() => props.background, (b) => {
@@ -107,6 +149,13 @@ watch(() => props.radius, (r) => {
107
149
  radiusRef.value = normalizeRadius(r);
108
150
  });
109
151
 
152
+ watch(() => props.iconSize, (v) => {
153
+ if (v == null) {
154
+ return;
155
+ }
156
+ iconSizeRef.value = normalizeIconSize(v);
157
+ });
158
+
110
159
  // 提示:预览区已移除以节省侧栏空间
111
160
  </script>
112
161
 
@@ -162,11 +211,18 @@ watch(() => props.radius, (r) => {
162
211
  <span class="text-12px opacity-70">deg</span>
163
212
  </div>
164
213
 
165
- <!-- 圆角:仅使用数字输入,节省横向空间 -->
214
+ <!-- 圆角(百分比):仅使用数字输入,节省横向空间 -->
166
215
  <div class="flex items-center gap-3">
167
216
  <label class="text-12px opacity-70 w-14 shrink-0">圆角</label>
168
- <a-input-number v-model:value="radiusRef" :disabled="props.disabled" :min="0" :max="48" :step="1" size="small" class="w-28" />
169
- <span class="text-12px opacity-70">px</span>
217
+ <a-input-number v-model:value="radiusRef" :disabled="props.disabled" :min="0" :max="50" :step="1" size="small" class="w-28" />
218
+ <span class="text-12px opacity-70">%</span>
219
+ </div>
220
+
221
+ <!-- 图标大小(百分比):仅在盒子内生效,控制内层图标比例 -->
222
+ <div class="flex items-center gap-3">
223
+ <label class="text-12px opacity-70 w-14 shrink-0">图标大小</label>
224
+ <a-input-number v-model:value="iconSizeRef" :disabled="props.disabled" :min="0" :max="100" :step="1" size="small" class="w-28" />
225
+ <span class="text-12px opacity-70">%</span>
170
226
  </div>
171
227
  </div>
172
228
  </template>
@@ -13,15 +13,19 @@ const modelValueRef = defineModel<string>();
13
13
  const modelBoxType = defineModel<'null' | 'square' | 'prism'>('boxType', { default: 'null' });
14
14
  const modelAngle = defineModel<number | string>('angle', { default: 65 });
15
15
  const modelBackground = defineModel<string | { from: string, to: string }>('background', { default: { from: '#65E54A', to: '#35C724' } });
16
- const modelRadius = defineModel<number | string>('radius', { default: 6 });
16
+ // 圆角使用百分比,默认 50%
17
+ const modelRadius = defineModel<number | string>('radius', { default: '50%' });
17
18
  const modelIconColor = defineModel<string>('iconColor', { default: '' });
19
+ // 图标大小(百分比或 CSS 长度),默认 50%
20
+ const modelIconSize = defineModel<number | string>('iconSize', { default: '50%' });
18
21
  // 对话框内部临时选择值(仅在点击确定时写回到 v-model)
19
22
  const tempValueRef = ref<string | undefined>(modelValueRef.value);
20
23
  // 对话框内部样式状态
21
24
  const boxType = ref<'null' | 'square' | 'prism'>(modelBoxType.value || 'null');
22
25
  const boxAngle = ref<number | string>(modelAngle.value ?? 65);
23
26
  const boxBackground = ref<string | { from: string, to: string }>(modelBackground.value ?? { from: '#65E54A', to: '#35C724' });
24
- const boxRadius = ref<number | string>(modelRadius.value ?? 6);
27
+ const boxRadius = ref<number | string>(modelRadius.value ?? '50%');
28
+ const boxIconSize = ref<number | string>(modelIconSize.value ?? '50%');
25
29
  // 图标颜色:无背景默认不着色(空字符串表示未设置);有背景默认白色
26
30
  const iconColor = ref<string>(modelIconColor.value ?? '');
27
31
  const iconColorPickerRef = ref<HTMLInputElement | null>(null);
@@ -39,7 +43,8 @@ function handleOpenModal() {
39
43
  boxBackground.value = typeof bg === 'string'
40
44
  ? bg
41
45
  : (bg ? { ...(bg as any) } : { from: '#65E54A', to: '#35C724' });
42
- boxRadius.value = modelRadius.value ?? 6;
46
+ boxRadius.value = modelRadius.value ?? '50%';
47
+ boxIconSize.value = modelIconSize.value ?? '50%';
43
48
  iconColor.value = modelIconColor.value ?? '';
44
49
  visibleRef.value = true;
45
50
  }
@@ -56,6 +61,7 @@ function handleConfirm() {
56
61
  const bg2 = boxBackground.value as any;
57
62
  modelBackground.value = typeof bg2 === 'string' ? bg2 : (bg2 ? { ...bg2 } : bg2);
58
63
  modelRadius.value = boxRadius.value;
64
+ modelIconSize.value = boxIconSize.value;
59
65
  modelIconColor.value = iconColor.value;
60
66
  visibleRef.value = false;
61
67
  }
@@ -323,7 +329,7 @@ const previewBoxPadding = 4; // 与 shapeBoxDefaults 中的 padding 对齐
323
329
  :name="tempValueRef"
324
330
  box="square"
325
331
  :size="previewBoxOuter"
326
- radius="6px"
332
+ radius="25%"
327
333
  color="#ffffff"
328
334
  />
329
335
  </div>
@@ -335,7 +341,7 @@ const previewBoxPadding = 4; // 与 shapeBoxDefaults 中的 padding 对齐
335
341
  box="prism"
336
342
  :size="previewBoxOuter"
337
343
  color="#ffffff"
338
- radius="2px"
344
+ radius="2%"
339
345
  />
340
346
  </div>
341
347
  </a-radio-button>
@@ -352,6 +358,7 @@ const previewBoxPadding = 4; // 与 shapeBoxDefaults 中的 padding 对齐
352
358
  v-model:angle="boxAngle"
353
359
  v-model:background="boxBackground"
354
360
  v-model:radius="boxRadius"
361
+ v-model:icon-size="boxIconSize"
355
362
  :disabled="boxType === 'null'"
356
363
  />
357
364
  </div>
@@ -420,6 +427,7 @@ const previewBoxPadding = 4; // 与 shapeBoxDefaults 中的 padding 对齐
420
427
  :angle="boxAngle"
421
428
  :background="boxBackground"
422
429
  :radius="boxRadius"
430
+ :icon-size="boxIconSize"
423
431
  :color="isAntdIcon ? (iconColor || '#ffffff') : undefined"
424
432
  />
425
433
  </div>
package/src/index.ts CHANGED
@@ -43,6 +43,7 @@ export function rbac(options: RbacOptions): ModuleOptions {
43
43
  }
44
44
 
45
45
  export * from './components/OrgUserSelector';
46
+ export { default as ResourceSelector } from './components/ResourceSelector/index.vue';
46
47
  export * from './composables/useWebSocketManager';
47
48
  export * from './interface';
48
49
  export * from './stores/view';
@@ -42,7 +42,7 @@ const expandedRowKeys = ref<string[]>([]);
42
42
 
43
43
  const query = ref<API.getRbacOrgOrgListByParentIdParams>({ parentId: '0' });
44
44
 
45
- const tableData: Ref<ChilData[]> = ref([]);
45
+ const tableData = ref<ChilData[]>([]);
46
46
 
47
47
  const tableRef = ref<VxeTableInstance<ChilData>>();
48
48
  const DrawerOrganizationRef = ref();
@@ -150,12 +150,11 @@ function reset() {
150
150
  generateData();
151
151
  }
152
152
 
153
- function generateData(): void {
153
+ async function generateData() {
154
154
  expandedRowKeys.value = [];
155
155
  setLoading(true);
156
- getRbacOrgOrgListByParentId({
157
- parentId: '0',
158
- }).then((res: any) => {
156
+ try {
157
+ const res = await getRbacOrgOrgListByParentId({ parentId: '0' });
159
158
  if (res?.success) {
160
159
  tableData.value = res.data?.map((e: API.PubOrg) => {
161
160
  return {
@@ -163,11 +162,13 @@ function generateData(): void {
163
162
  children: [],
164
163
  hasChild: !e.leaf,
165
164
  };
166
- });
165
+ }) ?? [];
167
166
  tableRef.value?.reloadData(tableData.value);
168
167
  }
168
+ }
169
+ finally {
169
170
  setLoading(false);
170
- });
171
+ }
171
172
  }
172
173
 
173
174
  generateData();
@@ -175,13 +176,14 @@ generateData();
175
176
  const operateRow = ref<ChilData>({});
176
177
  const operateType = ref<string>('');
177
178
 
178
- function updateData() {
179
+ async function updateData() {
179
180
  if (operateType.value === ACTION.ADD) {
180
181
  searchId.value = '';
181
182
  isSearch.value = false;
182
183
  generateData();
183
184
  }
184
- else if (operateType.value === ACTION.ADDNEXT) {
185
+
186
+ if (operateType.value === ACTION.ADDNEXT) {
185
187
  if (operateRow.value.hasChild) {
186
188
  tableRef.value?.setTreeExpand(operateRow.value.children, false);
187
189
  tableRef.value?.reloadTreeExpand(operateRow.value);
@@ -198,9 +200,14 @@ function updateData() {
198
200
  }
199
201
  }
200
202
  }
203
+
201
204
  // 编辑某一行数据后,把父节点关闭然后重新打开,页面数据才更新
202
- else if (operateType.value === ACTION.EDIT) {
205
+ if (operateType.value === ACTION.EDIT) {
203
206
  const parentNode = getNodeById(tableData.value, operateRow.value.parentId!);
207
+ if (!parentNode) {
208
+ await generateData();
209
+ return;
210
+ }
204
211
  tableRef.value?.setTreeExpand(parentNode.children, false);
205
212
  tableRef.value?.reloadTreeExpand(parentNode);
206
213
  }
@@ -24,7 +24,7 @@ const show = computed(() => {
24
24
  const type = form.value.type as RESOURCE_TYPE;
25
25
 
26
26
  return {
27
- path: [RESOURCE_TYPE.INDEX, RESOURCE_TYPE.MENU, RESOURCE_TYPE.MICRO_APP, RESOURCE_TYPE.NON_MENU, RESOURCE_TYPE.IFRAME, RESOURCE_TYPE.LINK].includes(type),
27
+ path: [RESOURCE_TYPE.INDEX, RESOURCE_TYPE.MENU, RESOURCE_TYPE.NON_MENU, RESOURCE_TYPE.IFRAME, RESOURCE_TYPE.LINK].includes(type),
28
28
  component: [RESOURCE_TYPE.MENU, RESOURCE_TYPE.NON_MENU].includes(type),
29
29
  iframe: [RESOURCE_TYPE.IFRAME].includes(type),
30
30
  link: [RESOURCE_TYPE.LINK].includes(type),
@@ -23,8 +23,9 @@ interface LocalIconOptions {
23
23
  boxType: 'null' | 'square' | 'prism'
24
24
  angle: number
25
25
  background: { from: string, to: string } | string
26
- radius: number
26
+ radius: number | string
27
27
  iconColor: string
28
+ iconSize?: number | string
28
29
  }
29
30
 
30
31
  function createDefaultIconOptions(): LocalIconOptions {
@@ -32,8 +33,9 @@ function createDefaultIconOptions(): LocalIconOptions {
32
33
  boxType: 'null',
33
34
  angle: 65,
34
35
  background: { from: '#65E54A', to: '#35C724' },
35
- radius: 6,
36
+ radius: '50%',
36
37
  iconColor: '',
38
+ iconSize: '50%',
37
39
  };
38
40
  }
39
41
 
@@ -93,7 +95,7 @@ const show = computed(() => {
93
95
  const type = modelValue.value.type as RESOURCE_TYPE;
94
96
 
95
97
  return {
96
- icon: [RESOURCE_TYPE.DYNAMIC_APP, RESOURCE_TYPE.MICRO_APP, RESOURCE_TYPE.INDEX, RESOURCE_TYPE.MENU, RESOURCE_TYPE.IFRAME, RESOURCE_TYPE.LINK].includes(type),
98
+ icon: [RESOURCE_TYPE.DYNAMIC_APP, RESOURCE_TYPE.INDEX, RESOURCE_TYPE.MENU, RESOURCE_TYPE.IFRAME, RESOURCE_TYPE.LINK].includes(type),
97
99
  };
98
100
  });
99
101
 
@@ -122,6 +124,7 @@ function clearIcon(e: MouseEvent) {
122
124
  v-model:background="iconOptions.background"
123
125
  v-model:radius="iconOptions.radius"
124
126
  v-model:icon-color="iconOptions.iconColor"
127
+ v-model:icon-size="iconOptions.iconSize"
125
128
  >
126
129
  <!-- select box -->
127
130
  <template v-if="modelValue.icon">
@@ -141,6 +144,7 @@ function clearIcon(e: MouseEvent) {
141
144
  :radius="iconOptions.radius"
142
145
  :background="iconOptions.background"
143
146
  :angle="iconOptions.angle"
147
+ :icon-size="iconOptions.iconSize"
144
148
  :color="(modelValue.icon || '').startsWith('antd:') ? (iconOptions.iconColor || '#ffffff') : undefined"
145
149
  />
146
150
  <PubinfoIcon
@@ -0,0 +1,92 @@
1
+ <script setup lang="ts">
2
+ import type { Form } from '../interface';
3
+ import { useLayoutForm } from '../hooks/useLayoutForm';
4
+ import HoverCard from './HoverCard.vue';
5
+
6
+ /**
7
+ * 修改系统层的布局
8
+ *
9
+ * ## Methods
10
+ * - 控制 侧边栏 是否显示
11
+ * - 控制 顶栏 是否显示
12
+ * - 控制 Tabs 是否显示
13
+ * - 控制 面包屑 是否显示
14
+ */
15
+
16
+ defineOptions({
17
+ name: 'LayoutForm',
18
+ });
19
+
20
+ const form = defineModel<Form>({
21
+ default: {
22
+ meta: {
23
+ layout: {
24
+ header: true,
25
+ sidebar: true,
26
+ tabs: true,
27
+ toolbar: true,
28
+ },
29
+ },
30
+ },
31
+ });
32
+
33
+ const { layoutForm, show } = useLayoutForm(form);
34
+ </script>
35
+
36
+ <template>
37
+ <div v-if="show">
38
+ <a-divider>布局配置</a-divider>
39
+
40
+ <a-row :gutter="[8, 8]">
41
+ <a-col :span="12">
42
+ <a-form-item name="enable" :label-col="{ span: 0 }">
43
+ <HoverCard class="flex items-center justify-between">
44
+ <div class="text-sm font-medium">
45
+ 显示 Header
46
+ </div>
47
+ <a-switch v-model:checked="layoutForm.header" />
48
+ </HoverCard>
49
+ </a-form-item>
50
+ </a-col>
51
+
52
+ <a-col :span="12">
53
+ <a-form-item name="enable" :label-col="{ span: 0 }">
54
+ <HoverCard class="flex items-center justify-between">
55
+ <div class="text-sm font-medium">
56
+ 显示 Sidebar
57
+ </div>
58
+ <a-switch v-model:checked="layoutForm.sidebar" />
59
+ </HoverCard>
60
+ </a-form-item>
61
+ </a-col>
62
+
63
+ <a-col :span="12">
64
+ <a-form-item name="enable" :label-col="{ span: 0 }">
65
+ <HoverCard class="flex items-center justify-between">
66
+ <div class="text-sm font-medium">
67
+ 显示 Tabs
68
+ </div>
69
+ <a-switch v-model:checked="layoutForm.tabs" />
70
+ </HoverCard>
71
+ </a-form-item>
72
+ </a-col>
73
+
74
+ <a-col :span="12">
75
+ <a-form-item name="enable" :label-col="{ span: 0 }">
76
+ <HoverCard class="flex items-center justify-between">
77
+ <div class="text-sm font-medium">
78
+ 显示 Toolbar
79
+ </div>
80
+ <a-switch v-model:checked="layoutForm.toolbar" />
81
+ </HoverCard>
82
+ </a-form-item>
83
+ </a-col>
84
+ </a-row>
85
+ </div>
86
+ </template>
87
+
88
+ <style lang="css" scoped>
89
+ .form-item-bottom-0, .ant-form-item {
90
+ margin-bottom: 0;
91
+ }
92
+ </style>
@@ -80,6 +80,17 @@ const { show } = useMetaForm(form);
80
80
  </HoverCard>
81
81
  </a-col>
82
82
 
83
+ <a-col v-if="show.iframe" :span="12">
84
+ <a-form-item name="enable" :label-col="{ span: 0 }">
85
+ <HoverCard class="flex items-center justify-between">
86
+ <div class="text-sm font-medium">
87
+ 启用缓存
88
+ </div>
89
+ <a-switch v-model:checked="form.meta!.iframeCache" />
90
+ </HoverCard>
91
+ </a-form-item>
92
+ </a-col>
93
+
83
94
  <!-- 缓存标签页:仅在菜单中显示 -->
84
95
  <a-col v-if="show.cacheAndPin" :span="12">
85
96
  <HoverCard :more="form.meta?.cache" class="flex flex-wrap items-center">
@@ -5,6 +5,7 @@ import type { AddForm, EditForm, Form, TreeNode } from '../interface';
5
5
  import { useToggle } from '@vueuse/core';
6
6
  import { message } from 'ant-design-vue';
7
7
  import { cloneDeep } from 'lodash-es';
8
+ import { PartyLoginModal } from 'pubinfo';
8
9
  import { getRbacResourceInfo, postRbacResourceAddResource, postRbacResourceResourceUpdate } from '@/api/modules/rbac';
9
10
  import { DATA_PERMISSION_TYPE, dataPermissionOptions } from '@/views/data-permission/enum';
10
11
  import { ACTION, RESOURCE_COLOR, RESOURCE_TYPE, resourceOptions } from '../enum';
@@ -12,6 +13,7 @@ import { useMetaForm } from '../hooks/useMetaForm';
12
13
  import { defaultMeta } from '../model';
13
14
  import DynamicRoutesForm from './DynamicRoutesForm.vue';
14
15
  import ResourceIconSelect from './IconSelect.vue';
16
+ import LayoutForm from './LayoutForm.vue';
15
17
  import MetaForm from './MetaForm.vue';
16
18
 
17
19
  defineOptions({
@@ -71,7 +73,6 @@ function recursiveTreeFind<T extends { children?: T[] }>(tree: T[], fn: (node: T
71
73
  const resourceComputedOptions = computed(() => {
72
74
  switch (parentNode.value?.type) {
73
75
  case RESOURCE_TYPE.APP:
74
- case RESOURCE_TYPE.MICRO_APP:
75
76
  case RESOURCE_TYPE.DYNAMIC_APP:
76
77
  return resourceOptions.filter(node => [RESOURCE_TYPE.INDEX, RESOURCE_TYPE.MENU, RESOURCE_TYPE.IFRAME, RESOURCE_TYPE.LINK].includes(node.value));
77
78
 
@@ -88,12 +89,17 @@ const resourceComputedOptions = computed(() => {
88
89
  return [];
89
90
 
90
91
  default:
91
- return resourceOptions.filter(node => [RESOURCE_TYPE.APP, /* RESOURCE_TYPE.MICRO_APP, */ RESOURCE_TYPE.DYNAMIC_APP].includes(node.value));
92
+ return resourceOptions.filter(node => [RESOURCE_TYPE.APP, RESOURCE_TYPE.DYNAMIC_APP].includes(node.value));
92
93
  }
93
94
  });
94
95
 
95
96
  const { show, resetIncompatibleOptions } = useMetaForm(form);
96
97
 
98
+ // 计算第三方登录的目标URL (iframe或link地址)
99
+ const partyLoginTargetUrl = computed(() => {
100
+ return form.value.meta?.iframe || form.value.meta?.link || '';
101
+ });
102
+
97
103
  const [formLoading, setFormLoading] = useToggle(false);
98
104
  async function onOpen(key: ACTION, record?: Form) {
99
105
  state.title = key;
@@ -213,16 +219,11 @@ defineExpose({
213
219
  </a-form-item>
214
220
  </a-col>
215
221
  <a-col :span="24">
216
- <a-form-item label="资源类型" name="type">
222
+ <a-form-item label="资源类型" name="type" :tooltip="!parentNode?.type ? '动态应用为后端路由,应用为前端路由' : ''">
217
223
  <div
218
224
  v-if="state.title === ACTION.EDIT"
219
225
  class="flex items-center space-x-1"
220
226
  >
221
- <!-- <PubinfoIcon v-if="form.type" :name="`resource-${RESOURCE_ICON[Number(form.type)]}`" class="text-xl" />
222
- <span>
223
- {{ resourceComputedOptions.find(e => e.value === form.type)?.label }}
224
- </span> -->
225
-
226
227
  <a-tag v-if="form.type" :color="RESOURCE_COLOR[Number(form.type)]" :bordered="false">
227
228
  {{ resourceComputedOptions.find(e => e.value === form.type)?.label }}
228
229
  </a-tag>
@@ -268,6 +269,22 @@ defineExpose({
268
269
  </a-col>
269
270
  </a-row>
270
271
 
272
+ <a-row v-if="show.dynamic" :gutter="8">
273
+ <a-col :span="24">
274
+ <a-form-item label="对接第三方" name="description" :label-col="{ span: 3 }">
275
+ <PartyLoginModal v-model="form.meta!.partyLogin" :target-url="partyLoginTargetUrl">
276
+ <template #trigger>
277
+ <a-input
278
+ :value="form.meta!.partyLogin && form.meta!.partyLogin.length > 0 ? '已配置第三方登录流程' : ''"
279
+ placeholder="配置第三方登录流程"
280
+ readonly
281
+ />
282
+ </template>
283
+ </PartyLoginModal>
284
+ </a-form-item>
285
+ </a-col>
286
+ </a-row>
287
+
271
288
  <a-row :gutter="8">
272
289
  <a-col :span="24">
273
290
  <a-form-item label="描述" name="description" :label-col="{ span: 3 }">
@@ -296,6 +313,7 @@ defineExpose({
296
313
  </a-row>
297
314
 
298
315
  <MetaForm v-model="form" />
316
+ <LayoutForm v-model="form" />
299
317
  </a-form>
300
318
  </a-spin>
301
319
 
@@ -24,7 +24,6 @@ export const RESOURCE_COLOR = ['', 'geekblue', 'blue', 'default', 'cyan', 'defau
24
24
  export const resourceOptions = [
25
25
  { label: '动态应用', value: RESOURCE_TYPE.DYNAMIC_APP },
26
26
  { label: '应用', value: RESOURCE_TYPE.APP },
27
- { label: '微应用', value: RESOURCE_TYPE.MICRO_APP },
28
27
  { label: '目录', value: RESOURCE_TYPE.INDEX },
29
28
  { label: '菜单', value: RESOURCE_TYPE.MENU },
30
29
  { label: '按钮', value: RESOURCE_TYPE.BUTTON },
@@ -0,0 +1,57 @@
1
+ import type { Ref } from 'vue';
2
+ import type { Form } from '../interface';
3
+ import type { CustomMetaLayout } from '../model';
4
+ import { RESOURCE_TYPE } from '../enum';
5
+
6
+ /**
7
+ * 需要判断是否显示布局配置项
8
+ * 因为这是后续增加的一个功能,所以在之前是没有的。
9
+ * 这就意味着,对于已有的数据,如果没有 layout 字段,则默认都显示
10
+ * @param form 资源表单对象
11
+ */
12
+ export function useLayoutForm(form: Ref<Form>) {
13
+ // 仅在 资源类型 为 内嵌 的时候显示布局配置项
14
+ const show = computed(() => {
15
+ const type = form.value.type as RESOURCE_TYPE;
16
+ return [RESOURCE_TYPE.APP, RESOURCE_TYPE.DYNAMIC_APP].includes(type);
17
+ });
18
+
19
+ /**
20
+ * 在这里是为了让 LayoutForm 的 v-model 绑定有一个稳定的对象引用
21
+ * 避免直接绑定 form.value.meta.layout 导致的引用变化问题
22
+ *
23
+ * @step-1 在这里会判断 layout 是否存在,不存在则创建默认值
24
+ *
25
+ * @step-2 有可能 layout 是有部分字段缺失的,所以需要补全,
26
+ * 虽然这可能不会存在,但是这需要在我们的考虑范围之内
27
+ *
28
+ */
29
+ const layoutForm = computed<CustomMetaLayout>(() => {
30
+ // 如果没有 layout 字段,则默认都显示
31
+ if (!form.value.meta?.layout) {
32
+ return {
33
+ header: true,
34
+ sidebar: true,
35
+ tabs: true,
36
+ toolbar: true,
37
+ };
38
+ }
39
+ // 如果 layout 存在,但有部分字段缺失,则补全
40
+ const defaultLayout: CustomMetaLayout = {
41
+ header: true,
42
+ sidebar: true,
43
+ tabs: true,
44
+ toolbar: true,
45
+ };
46
+ form.value.meta.layout = {
47
+ ...defaultLayout,
48
+ ...form.value.meta.layout,
49
+ };
50
+ return form.value.meta.layout;
51
+ });
52
+
53
+ return {
54
+ show,
55
+ layoutForm,
56
+ };
57
+ }
@@ -23,6 +23,10 @@ export function useMetaForm(form: Ref<Form>) {
23
23
  tabbar: ![RESOURCE_TYPE.APP, RESOURCE_TYPE.DYNAMIC_APP, RESOURCE_TYPE.INDEX, RESOURCE_TYPE.BUTTON].includes(type),
24
24
 
25
25
  breadcrumb: ![RESOURCE_TYPE.BUTTON].includes(type),
26
+
27
+ dynamic: [RESOURCE_TYPE.IFRAME, RESOURCE_TYPE.LINK].includes(type),
28
+
29
+ iframe: [RESOURCE_TYPE.IFRAME].includes(type),
26
30
  };
27
31
  });
28
32
 
@@ -42,9 +42,6 @@ const columns: ProColumns<TreeNode> = [
42
42
  customRender({ text }) {
43
43
  return (
44
44
  <div class="flex items-center space-x-1">
45
- {/* <PubinfoIcon name={`resource-${RESOURCE_ICON[text]}`} class="text-xl" />
46
- <span>{resourceOptions.find(item => item.value === text)?.label}</span> */}
47
-
48
45
  <Tag color={RESOURCE_COLOR[text]} bordered={false}>{resourceOptions.find(item => item.value === text)?.label}</Tag>
49
46
  </div>
50
47
  );