@pubinfo/module-rbac 2.0.11 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{IconSelect-CkqLR5Dg.js → IconSelect-Cco4lJFZ.js} +1 -1
- package/dist/IconSelect.css +1 -1
- package/dist/IconSelect.vue_vue_type_script_setup_true_lang-C9GJFjrV.js +757 -0
- package/dist/LayoutForm-DqDnsQcr.js +173 -0
- package/dist/LayoutForm.css +1 -0
- package/dist/{MetaForm-DtlyvzoM.js → MetaForm-s74FbAc9.js} +51 -43
- package/dist/{ResourceEdit-WpEdbzaw.js → ResourceEdit-CNYAteRE.js} +1 -1
- package/dist/ResourceEdit.vue_vue_type_script_setup_true_lang-BLqs77A8.js +433 -0
- package/dist/{ResourceRelation-DGiGYy6V.js → ResourceRelation-CeM9Eq39.js} +10 -11
- package/dist/{ResourceRelation-26FnHEfU.js → ResourceRelation-qhIdXLAy.js} +12 -13
- package/dist/{TenantEdit-B24X5nND.js → TenantEdit-C61O1pSu.js} +1 -1
- package/dist/{TenantEdit.vue_vue_type_script_setup_true_lang-Bu5HcJJv.js → TenantEdit.vue_vue_type_script_setup_true_lang-BwsA9hth.js} +39 -40
- package/dist/{UserAuthorization-Co6JV0py.js → UserAuthorization-rqttVub5.js} +9 -10
- package/dist/components/OrgUserSelector/OrgSelector.vue.d.ts +1 -1
- package/dist/components/OrgUserSelector/UserSelector.vue.d.ts +1 -1
- package/dist/components/ResourceIcones/BoxColorPicker.vue.d.ts +3 -0
- package/dist/components/ResourceIcones/Select.vue.d.ts +3 -0
- package/dist/{createAndEditDataPermission-BhOLGGna.js → createAndEditDataPermission-DPeFulie.js} +1 -1
- package/dist/{createAndEditDataPermission.vue_vue_type_script_setup_true_lang-D5wInKwO.js → createAndEditDataPermission.vue_vue_type_script_setup_true_lang-DlUIYtif.js} +98 -101
- package/dist/{drawerRole-DHLv47_z.js → drawerRole-CXGVqxPX.js} +1 -1
- package/dist/{drawerRole-DUWRM43j.js → drawerRole-CfJjf3v_.js} +1 -1
- 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
- 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
- package/dist/{index-Csfu3kAr.js → index-B5aP0re3.js} +6 -7
- package/dist/{index-DwpOyfRe.js → index-BEB4liGe.js} +8 -9
- package/dist/{index-DfuGXjYh.js → index-BNwGLzXe.js} +21 -22
- package/dist/{index-ioZh8Qom.js → index-BXvJhqJD.js} +6 -7
- package/dist/{index-DdoOaWs-.js → index-BsZjgoZQ.js} +1 -1
- package/dist/{index-BFx8F5lO.js → index-C2G4xRrJ.js} +8 -9
- package/dist/{index-x9Iim2eD.js → index-C5WD-YUu.js} +7 -8
- package/dist/{index-rNYtJ7Om.js → index-CbVJdR5M.js} +6 -7
- package/dist/{index-DECno0gv.js → index-ChWnVs6j.js} +2 -2
- package/dist/{index-C9hHp4Iw.js → index-CoZM8pyH.js} +1 -1
- package/dist/{index-B2tTTlBE.js → index-Copy5kiW.js} +276 -273
- package/dist/index-D3Xw_aOi.js +144 -0
- package/dist/{index-CCw3ZJgQ.js → index-DHVEXch0.js} +8 -9
- package/dist/{index-COWUocfL.js → index-Dl0hWfIE.js} +7 -8
- package/dist/{index-BIpmC999.js → index-En8oOf9e.js} +8 -9
- package/dist/{index-ZQuRR9fG.js → index-VZuizuKs.js} +1 -1
- package/dist/{index-BwWrE3dY.js → index-XhYtVVT9.js} +6 -7
- package/dist/{index-BUmMaC21.js → index-nAPHtTM7.js} +5 -6
- package/dist/{index-y9CQTo95.js → index-qn7U9T51.js} +9 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2588 -2289
- package/dist/index10.css +1 -1
- package/dist/index11.css +1 -1
- package/dist/index12.css +1 -1
- package/dist/index13.css +1 -1
- package/dist/index14.css +1 -1
- package/dist/index15.css +1 -1
- package/dist/index16.css +1 -1
- package/dist/index17.css +1 -1
- package/dist/index4.css +1 -1
- package/dist/index5.css +1 -1
- package/dist/index6.css +1 -1
- package/dist/index7.css +1 -1
- package/dist/index8.css +1 -1
- package/dist/index9.css +1 -1
- package/dist/{loginHistoryDetail-B6eI-TmJ.js → loginHistoryDetail-BZVwG8m2.js} +1 -1
- package/dist/{login_history-B4Q1NCJn.js → login_history-CMZGS2PN.js} +5 -6
- package/dist/{operateHistoryDetail-CLZ9lyua.js → operateHistoryDetail-Badx3zl-.js} +1 -1
- package/dist/{operate_history-KASZ7_JO.js → operate_history-B42WNY1u.js} +5 -6
- package/dist/{setItem-BwMHXOUm.js → setItem-Blb3dox4.js} +1 -1
- package/dist/{useAppAndResource-38HCqYKa.js → useAppAndResource-DDywX2iz.js} +11 -12
- package/dist/views/data-permission/components/createAndEditDataPermission.vue.d.ts +3 -3
- package/dist/views/data-permission/index.vue.d.ts +3 -3
- package/dist/views/resource/components/LayoutForm.vue.d.ts +10 -0
- package/dist/views/resource/hooks/useLayoutForm.d.ts +12 -0
- package/dist/views/resource/model.d.ts +12 -0
- package/package.json +3 -3
- package/src/components/ResourceIcones/BoxColorPicker.vue +64 -8
- package/src/components/ResourceIcones/Select.vue +13 -5
- package/src/index.ts +1 -0
- package/src/views/organization/index.vue +17 -10
- package/src/views/resource/components/IconSelect.vue +6 -2
- package/src/views/resource/components/LayoutForm.vue +92 -0
- package/src/views/resource/components/ResourceEdit.vue +24 -0
- package/src/views/resource/hooks/useLayoutForm.ts +57 -0
- package/src/views/resource/hooks/useMetaForm.ts +2 -0
- package/src/views/resource/model.ts +21 -1
- package/dist/IconSelect.vue_vue_type_script_setup_true_lang-BoGxAoNd.js +0 -703
- package/dist/ResourceEdit.vue_vue_type_script_setup_true_lang-id6jZAko.js +0 -392
- package/dist/_plugin-vue_export-helper-CHgC5LLL.js +0 -9
- package/dist/enum-CTUzwCYG.js +0 -4
- package/dist/index-53oBz1jp.js +0 -299
- package/dist/index-DVt5k9xh.js +0 -145
- 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
|
|
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
|
|
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
|
|
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="
|
|
169
|
-
<span class="text-12px opacity-70"
|
|
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
|
-
|
|
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 ??
|
|
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 ??
|
|
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="
|
|
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="
|
|
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
|
|
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()
|
|
153
|
+
async function generateData() {
|
|
154
154
|
expandedRowKeys.value = [];
|
|
155
155
|
setLoading(true);
|
|
156
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -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:
|
|
36
|
+
radius: '50%',
|
|
36
37
|
iconColor: '',
|
|
38
|
+
iconSize: '50%',
|
|
37
39
|
};
|
|
38
40
|
}
|
|
39
41
|
|
|
@@ -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>
|
|
@@ -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({
|
|
@@ -94,6 +96,11 @@ const resourceComputedOptions = computed(() => {
|
|
|
94
96
|
|
|
95
97
|
const { show, resetIncompatibleOptions } = useMetaForm(form);
|
|
96
98
|
|
|
99
|
+
// 计算第三方登录的目标URL (iframe或link地址)
|
|
100
|
+
const partyLoginTargetUrl = computed(() => {
|
|
101
|
+
return form.value.meta?.iframe || form.value.meta?.link || '';
|
|
102
|
+
});
|
|
103
|
+
|
|
97
104
|
const [formLoading, setFormLoading] = useToggle(false);
|
|
98
105
|
async function onOpen(key: ACTION, record?: Form) {
|
|
99
106
|
state.title = key;
|
|
@@ -268,6 +275,22 @@ defineExpose({
|
|
|
268
275
|
</a-col>
|
|
269
276
|
</a-row>
|
|
270
277
|
|
|
278
|
+
<a-row v-if="show.dynamic" :gutter="8">
|
|
279
|
+
<a-col :span="24">
|
|
280
|
+
<a-form-item label="对接第三方" name="description" :label-col="{ span: 3 }">
|
|
281
|
+
<PartyLoginModal v-model="form.meta!.partyLogin" :target-url="partyLoginTargetUrl">
|
|
282
|
+
<template #trigger>
|
|
283
|
+
<a-input
|
|
284
|
+
:value="form.meta!.partyLogin && form.meta!.partyLogin.length > 0 ? '已配置第三方登录流程' : ''"
|
|
285
|
+
placeholder="配置第三方登录流程"
|
|
286
|
+
readonly
|
|
287
|
+
/>
|
|
288
|
+
</template>
|
|
289
|
+
</PartyLoginModal>
|
|
290
|
+
</a-form-item>
|
|
291
|
+
</a-col>
|
|
292
|
+
</a-row>
|
|
293
|
+
|
|
271
294
|
<a-row :gutter="8">
|
|
272
295
|
<a-col :span="24">
|
|
273
296
|
<a-form-item label="描述" name="description" :label-col="{ span: 3 }">
|
|
@@ -296,6 +319,7 @@ defineExpose({
|
|
|
296
319
|
</a-row>
|
|
297
320
|
|
|
298
321
|
<MetaForm v-model="form" />
|
|
322
|
+
<LayoutForm v-model="form" />
|
|
299
323
|
</a-form>
|
|
300
324
|
</a-spin>
|
|
301
325
|
|
|
@@ -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,8 @@ 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),
|
|
26
28
|
};
|
|
27
29
|
});
|
|
28
30
|
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 默认的 meta.layout 对象
|
|
3
|
+
*/
|
|
4
|
+
export interface CustomMetaLayout {
|
|
5
|
+
header: boolean
|
|
6
|
+
sidebar: boolean
|
|
7
|
+
tabs: boolean
|
|
8
|
+
toolbar: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
1
11
|
/**
|
|
2
12
|
* 默认的 meta 对象
|
|
3
13
|
*
|
|
@@ -17,6 +27,7 @@
|
|
|
17
27
|
* @param breadcrumb 是否在面包屑显示
|
|
18
28
|
* @param link 外链地址
|
|
19
29
|
* @param iframe 内嵌地址
|
|
30
|
+
* @param partyLogin 第三方登录配置
|
|
20
31
|
*/
|
|
21
32
|
export interface CustomMeta {
|
|
22
33
|
url: string
|
|
@@ -35,6 +46,8 @@ export interface CustomMeta {
|
|
|
35
46
|
breadcrumb: boolean
|
|
36
47
|
link?: string
|
|
37
48
|
iframe?: string
|
|
49
|
+
layout: CustomMetaLayout
|
|
50
|
+
partyLogin?: any[] // 第三方登录配置数据
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
export const defaultMeta: CustomMeta = {
|
|
@@ -44,7 +57,7 @@ export const defaultMeta: CustomMeta = {
|
|
|
44
57
|
scope: '',
|
|
45
58
|
activeMenu: undefined,
|
|
46
59
|
isDev: false,
|
|
47
|
-
devText: '
|
|
60
|
+
devText: '该功能暂未上线,敬请期待!',
|
|
48
61
|
cache: false,
|
|
49
62
|
cacheName: undefined,
|
|
50
63
|
noCache: undefined,
|
|
@@ -54,4 +67,11 @@ export const defaultMeta: CustomMeta = {
|
|
|
54
67
|
breadcrumb: true,
|
|
55
68
|
link: undefined,
|
|
56
69
|
iframe: undefined,
|
|
70
|
+
layout: {
|
|
71
|
+
header: true,
|
|
72
|
+
sidebar: true,
|
|
73
|
+
tabs: true,
|
|
74
|
+
toolbar: true,
|
|
75
|
+
},
|
|
76
|
+
partyLogin: undefined,
|
|
57
77
|
};
|