@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.
- package/dist/{ComponentSelect-B1ZyenXP.js → ComponentSelect-Bhl9ciUm.js} +12 -12
- package/dist/ComponentSelect-Bhwuc86o.js +2 -0
- package/dist/ComponentSelect.css +1 -1
- package/dist/DynamicRoutesForm-CE2RPs7y.js +169 -0
- package/dist/DynamicRoutesForm-D35jFnxF.js +4 -0
- package/dist/IconSelect-Ct5FkeHI.js +4 -0
- package/dist/IconSelect-HAhfS6hd.js +661 -0
- package/dist/IconSelect.css +1 -0
- package/dist/{ImportExport-Clbc8xaw.js → ImportExport-ByRTssxP.js} +2 -2
- package/dist/ImportExport-D8uzaOMz.js +4 -0
- package/dist/{MetaForm-BWy4Lpr8.js → MetaForm-BSGuZXgj.js} +2 -2
- package/dist/{MetaForm-DpH9an5A.js → MetaForm-DErrpELP.js} +1 -1
- package/dist/OrgAndPosition-CF7ZJtNB.js +2 -0
- package/dist/{ResourceEdit-0_cYB0iu.js → ResourceEdit-DF-2_zoi.js} +115 -105
- package/dist/ResourceEdit-oAPLPUf0.js +11 -0
- package/dist/{ResourceRelation-DMWa09ae.js → ResourceRelation-BRseTUw2.js} +1 -1
- package/dist/RoleRelation-YaSQQxQ2.js +2 -0
- package/dist/{TenantEdit-D8-Pt7sf.js → TenantEdit-C28473_2.js} +1 -1
- package/dist/{UserAuthorization-DuioGghR.js → UserAuthorization-C4SLMNmh.js} +1 -1
- package/dist/UserEdit-CyCXz7g3.js +4 -0
- package/dist/{UserEdit-C6eNXSCu.js → UserEdit-EOTBOUZu.js} +1 -1
- package/dist/components/ResourceIcones/BoxColorPicker.vue.d.ts +23 -0
- package/dist/components/ResourceIcones/Select.vue.d.ts +48 -2
- package/dist/components/ResourceSelector/hooks/useAppAndResource.d.ts +15 -6
- package/dist/components/RoleSelector/useRole.d.ts +68 -4
- package/dist/context.d.ts +1 -2
- package/dist/{drawerRole-DNsK5a6O.js → drawerRole-CzTW_I2H.js} +1 -1
- package/dist/{drawerRole-BeUNs7cF.js → drawerRole-tan379DU.js} +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +1427 -1431
- package/dist/{resource-CrhX7VRK.js → resource-CyI9ZpmF.js} +9 -8
- package/dist/{role-PjmrOdFd.js → role-B2tKqUvP.js} +2 -2
- package/dist/{role_group-Dlx-6BZF.js → role_group-DqzFHwKx.js} +1 -1
- package/dist/stores/view.d.ts +126 -21
- package/dist/{tenant-BGtOP_wo.js → tenant-C8kVbfgt.js} +2 -2
- package/dist/{toolbar_setting-OemNp6pO.js → toolbar_setting-Dc_h2Mfe.js} +23 -34
- package/dist/toolbar_setting.css +1 -1
- package/dist/{user-JCiGXodF.js → user-Dub4HhCZ.js} +3 -3
- package/dist/utils/routeSystem.d.ts +126 -21
- package/dist/views/blackWhiteList/index.vue.d.ts +40 -40
- package/dist/views/data-permission/components/createAndEditDataPermission.vue.d.ts +4 -4
- package/dist/views/data-permission/index.vue.d.ts +44 -44
- package/dist/views/dictionary/index.vue.d.ts +40 -40
- package/dist/views/dictionary/itemlist.vue.d.ts +40 -40
- package/dist/views/group/index.vue.d.ts +40 -40
- package/dist/views/log_center/components/browserType.vue.d.ts +2 -2
- package/dist/views/log_center/components/loginHistoryDetail.vue.d.ts +2 -2
- package/dist/views/log_center/components/operateHistoryDetail.vue.d.ts +2 -2
- package/dist/views/log_center/login_history.vue.d.ts +40 -40
- package/dist/views/log_center/operate_history.vue.d.ts +40 -40
- package/dist/views/position/index.vue.d.ts +40 -40
- package/dist/views/region/index.vue.d.ts +40 -40
- package/dist/views/resource/components/IconSelect.vue.d.ts +10 -0
- package/dist/views/resource/hooks/useMetaForm.d.ts +8 -1
- package/dist/views/resource/index.vue.d.ts +40 -40
- package/dist/views/role/components/ResourceRelation.vue.d.ts +40 -40
- package/dist/views/role/index.vue.d.ts +80 -80
- package/dist/views/role_group/index.vue.d.ts +40 -40
- package/dist/views/tenant/index.vue.d.ts +40 -40
- package/dist/views/toolbar_setting/animationData.d.ts +1 -3
- package/dist/views/user/index.vue.d.ts +40 -40
- package/package.json +7 -3
- package/src/components/DirectoryIcones/ComponentsIcones.vue +2 -2
- package/src/components/DirectoryIcones/FolderIcones.vue +2 -2
- package/src/components/DirectoryIcones/ModuleIcones.vue +2 -2
- package/src/components/DirectoryIcones/PackagesIcones.vue +2 -2
- package/src/components/DirectoryIcones/SrcIcones.vue +2 -2
- package/src/components/DirectoryIcones/ViewIcones.vue +2 -2
- package/src/components/ResourceIcones/BoxColorPicker.vue +172 -0
- package/src/components/ResourceIcones/Select.vue +227 -15
- package/src/context.ts +1 -17
- package/src/index.ts +3 -10
- package/src/views/resource/components/ComponentSelect.vue +10 -0
- package/src/views/resource/components/DynamicRoutesForm.vue +0 -8
- package/src/views/resource/components/IconSelect.vue +138 -0
- package/src/views/resource/components/ResourceEdit.vue +34 -28
- package/src/views/toolbar_setting/animationData.ts +0 -4
- package/src/views/toolbar_setting/index.vue +0 -13
- package/dist/ComponentSelect-DlVJI5an.js +0 -2
- package/dist/DynamicRoutesForm-Cu3WhRpA.js +0 -4
- package/dist/DynamicRoutesForm-qjbrykW4.js +0 -376
- package/dist/ImportExport-CtEZTU87.js +0 -4
- package/dist/OrgAndPosition-3lCm4Fyb.js +0 -2
- package/dist/ResourceEdit-CYPzEbqn.js +0 -10
- package/dist/RoleRelation-DySyilcs.js +0 -2
- package/dist/UserEdit-6HYbgtOh.js +0 -4
- /package/dist/{OrgAndPosition-DYxNkasL.js → OrgAndPosition-vnBrq-fn.js} +0 -0
- /package/dist/{ResourceRelation-CZ8hGgOm.js → ResourceRelation-B_4woLmP.js} +0 -0
- /package/dist/{ResourceRelation-DkKdZ3TD.js → ResourceRelation-CKolCnHC.js} +0 -0
- /package/dist/{RoleRelation-B1D1NM_L.js → RoleRelation-DXZxscPY.js} +0 -0
- /package/dist/{TenantEdit-DNJzo8SS.js → TenantEdit-B5_v0C8I.js} +0 -0
- /package/dist/{UserAuthorization-DPHUk69M.js → UserAuthorization-DTgmWH5Q.js} +0 -0
- /package/dist/{dayjs.min-DcxwBe2I.js → dayjs.min-CdDcH50N.js} +0 -0
- /package/dist/{drawerRole-DS7fbx1h.js → drawerRole-CwilBmOE.js} +0 -0
- /package/dist/{drawerRole-BdoMYLYW.js → drawerRole-E1jHM3v-.js} +0 -0
- /package/dist/{enum-D0U38jb6.js → enum-BMY6bmPA.js} +0 -0
- /package/dist/{safe_setting-DpWUCv2p.js → safe_setting-uj9p_wW0.js} +0 -0
- /package/dist/{tabbar_setting-9UKDc1Ev.js → tabbar_setting-HFHrLVgt.js} +0 -0
- /package/dist/{theme_setting-BBLaZ4NS.js → theme_setting-QgpSGXxq.js} +0 -0
- /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
|
-
|
|
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
|
-
<
|
|
146
|
-
<
|
|
147
|
-
<
|
|
148
|
-
<
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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="
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
206
|
-
<a-
|
|
207
|
-
<
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
<a-
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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,
|