@arronqzy/vue-view 0.1.24 → 0.1.26
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/README.md +1 -11
- package/package.json +3 -3
- package/src/index.ts +25 -1
- package/src/panel/VueViewOnlinePreview.vue +25 -0
- package/src/panel/VueViewPanel.vue +188 -63
- package/src/panel/components/ElementsLayer.vue +1 -1
- package/src/panel/components/MaterialPreview.vue +28 -27
- package/src/panel/components/MaterialSidebar.vue +288 -225
- package/src/panel/components/MaterialSidebarTreeNode.vue +15 -10
- package/src/panel/components/MoveableLayer.vue +8 -0
- package/src/panel/components/PanelCanvas.vue +1 -0
- package/src/panel/components/PanelConfigSidebar.vue +23 -41
- package/src/panel/components/PanelRulers.vue +158 -40
- package/src/panel/components/SelectLayer.vue +2 -1
- package/src/panel/components/config/ConfigFieldGroup.vue +3 -3
- package/src/panel/components/config/ConfigSection.vue +5 -5
- package/src/panel/hooks/useWorkspaceProjects.ts +209 -67
- package/src/panel/lib/panel-ruler-canvas.ts +8 -4
- package/src/panel/library/abuilder-storage.ts +101 -3
- package/src/panel/library/abuilder-view-handle.ts +100 -0
- package/src/panel/library/capture-preview-snapshot.ts +800 -0
- package/src/panel/library/event-subscription.ts +70 -5
- package/src/panel/library/preview-snapshot.ts +252 -0
- package/src/panel/library/workspace-project-sync.ts +7 -1
- package/src/panel/library/workspace-storage.ts +58 -4
- package/src/tailwind.css +66 -5
package/README.md
CHANGED
|
@@ -49,17 +49,7 @@ import {
|
|
|
49
49
|
} from "@arronqzy/vue-view";
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
创建 / 同步发出 `workspace:add` / `workspace:sync
|
|
53
|
-
|
|
54
|
-
浏览器里的本地数据可以读取和清理,最后一个参数都是 `nameSpace`:
|
|
55
|
-
|
|
56
|
-
- 工作区:`listWorkspaceProjects` / `getWorkspaceProject` / `deleteWorkspaceProject` / `clearWorkspaceProjects`
|
|
57
|
-
- 蓝图库:`listBlueprintLibrary` / `getBlueprintLibraryRecord` / `deleteBlueprintLibraryRecord` / `clearBlueprintLibrary`
|
|
58
|
-
- 蓝图执行日志:`listExecutionRunRecords` / `getExecutionRunRecord` / `deleteExecutionRunRecord` / `clearAllExecutionRunRecords` / `purgeExecutionRunsOlderThan` / `trimExecutionRunRecordsToMax`,保留策略用 `readExecutionLogSettings` / `writeExecutionLogSettings`
|
|
59
|
-
- 接口集合:`listApiCollectionRecords` / `deleteApiCollection` / `clearApiCollections`
|
|
60
|
-
- `clearAbuilderStorage`:一次清空以上四类,返回各自删除数量
|
|
61
|
-
|
|
62
|
-
工作区、蓝图库的写入接口不导出(由编辑器自己维护)。`getPreviewSnapshot` 只在 React 包实现。
|
|
52
|
+
创建 / 同步发出 `workspace:add` / `workspace:sync`(载荷含 `nameSpace`),删除发出 `workspace:delete`。另有 `blueprint-library:changed` / `api-collection:changed`。`parseWorkspaceData` 的 `value` 可作 `initialWorkspace`;带 `id` 时默认同步进 IndexedDB。也支持 `workspaceId` prop、`ref`(`AbuilderViewHandle`)以及 `put*` / `openWorkspace` / `getCurrentWorkspace` / `getPreviewSnapshot` 等与 React 对齐的公开 API。
|
|
63
53
|
|
|
64
54
|
## 和 React 版的差别
|
|
65
55
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arronqzy/vue-view",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"description": "Vue 3 visual view editor for Abuilder (Ant Design Vue)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
"selecto": "^1.26.3",
|
|
24
24
|
"uuid": "^13.0.0",
|
|
25
25
|
"vue": "^3.5.13",
|
|
26
|
-
"@arronqzy/i18n": "0.1.17",
|
|
27
26
|
"@arronqzy/rx-store": "1.0.8",
|
|
28
27
|
"@arronqzy/blueprint-dsl": "1.0.17",
|
|
29
28
|
"@arronqzy/view-scene3d": "0.1.5",
|
|
29
|
+
"@arronqzy/vue-blueprint": "0.1.20",
|
|
30
30
|
"@arronqzy/view-table": "0.1.8",
|
|
31
31
|
"@arronqzy/vue-rx-store": "0.1.6",
|
|
32
|
-
"@arronqzy/
|
|
32
|
+
"@arronqzy/i18n": "0.1.17"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"vue": "^3.4.0"
|
package/src/index.ts
CHANGED
|
@@ -16,17 +16,24 @@ export { createWorkspaceProjectId } from "./panel/library/workspace-project-db";
|
|
|
16
16
|
export {
|
|
17
17
|
listWorkspaceProjects,
|
|
18
18
|
getWorkspaceProject,
|
|
19
|
+
putWorkspaceProject,
|
|
20
|
+
upsertWorkspaceProject,
|
|
19
21
|
deleteWorkspaceProject,
|
|
20
22
|
clearWorkspaceProjects,
|
|
21
23
|
} from "./panel/library/workspace-storage";
|
|
22
|
-
export type {
|
|
24
|
+
export type {
|
|
25
|
+
WorkspaceProjectListItem,
|
|
26
|
+
PutWorkspaceProjectOptions,
|
|
27
|
+
} from "./panel/library/workspace-storage";
|
|
23
28
|
export {
|
|
24
29
|
listBlueprintLibrary,
|
|
25
30
|
getBlueprintLibraryRecord,
|
|
31
|
+
putBlueprintLibraryRecord,
|
|
26
32
|
deleteBlueprintLibraryRecord,
|
|
27
33
|
clearBlueprintLibrary,
|
|
28
34
|
listExecutionRunRecords,
|
|
29
35
|
getExecutionRunRecord,
|
|
36
|
+
putExecutionRunRecord,
|
|
30
37
|
deleteExecutionRunRecord,
|
|
31
38
|
clearAllExecutionRunRecords,
|
|
32
39
|
purgeExecutionRunsOlderThan,
|
|
@@ -34,6 +41,8 @@ export {
|
|
|
34
41
|
readExecutionLogSettings,
|
|
35
42
|
writeExecutionLogSettings,
|
|
36
43
|
listApiCollectionRecords,
|
|
44
|
+
putApiCollection,
|
|
45
|
+
upsertApiCollectionFromJson,
|
|
37
46
|
deleteApiCollection,
|
|
38
47
|
clearApiCollections,
|
|
39
48
|
clearAbuilderStorage,
|
|
@@ -45,6 +54,8 @@ export type {
|
|
|
45
54
|
ExecutionRunRecord,
|
|
46
55
|
ExecutionLogSettings,
|
|
47
56
|
ApiCollectionRecord,
|
|
57
|
+
UpsertApiCollectionInput,
|
|
58
|
+
UpsertApiCollectionResult,
|
|
48
59
|
} from "./panel/library/abuilder-storage";
|
|
49
60
|
export {
|
|
50
61
|
addEventSubscription,
|
|
@@ -53,14 +64,27 @@ export {
|
|
|
53
64
|
export type {
|
|
54
65
|
AbuilderEventName,
|
|
55
66
|
AbuilderEventPayloadMap,
|
|
67
|
+
AddEventSubscriptionOptions,
|
|
56
68
|
WorkspaceAddEventPayload,
|
|
57
69
|
WorkspaceSyncEventPayload,
|
|
58
70
|
WorkspaceDeleteEventPayload,
|
|
71
|
+
BlueprintLibraryChangedEventPayload,
|
|
59
72
|
ApiCollectionUploadStartEventPayload,
|
|
60
73
|
ApiCollectionUploadSuccessEventPayload,
|
|
74
|
+
ApiCollectionChangedEventPayload,
|
|
61
75
|
WorkspaceData,
|
|
62
76
|
} from "./panel/library/event-subscription";
|
|
77
|
+
export {
|
|
78
|
+
getCurrentWorkspace,
|
|
79
|
+
openWorkspace,
|
|
80
|
+
createWorkspace,
|
|
81
|
+
saveWorkspace,
|
|
82
|
+
syncWorkspace,
|
|
83
|
+
} from "./panel/library/abuilder-view-handle";
|
|
84
|
+
export type { AbuilderViewHandle } from "./panel/library/abuilder-view-handle";
|
|
63
85
|
export type {
|
|
64
86
|
ParseCheckResult,
|
|
65
87
|
WorkspaceParseCheckResult,
|
|
66
88
|
} from "./panel/library/parse-workspace-data";
|
|
89
|
+
export { getPreviewSnapshot } from "./panel/library/preview-snapshot";
|
|
90
|
+
export type { GetPreviewSnapshotOptions } from "./panel/library/preview-snapshot";
|
|
@@ -40,6 +40,8 @@ import {
|
|
|
40
40
|
resolvePreviewLayerElements,
|
|
41
41
|
} from "./utils/panelStateIO";
|
|
42
42
|
import { applyPreviewSceneFill, readOutputScale } from "./utils/outputScale";
|
|
43
|
+
import { registerPreviewSnapshotProvider } from "./library/preview-snapshot";
|
|
44
|
+
import { capturePreviewSceneElement } from "./library/capture-preview-snapshot";
|
|
43
45
|
|
|
44
46
|
const { t, locale } = useI18n();
|
|
45
47
|
useBlueprintDslLocaleSync();
|
|
@@ -292,6 +294,29 @@ onMounted(() => {
|
|
|
292
294
|
onUnmounted(() => window.removeEventListener("resize", onResize));
|
|
293
295
|
});
|
|
294
296
|
|
|
297
|
+
watch(
|
|
298
|
+
[layoutReady, sceneRef, sceneBounds, fillScale, outputScale],
|
|
299
|
+
() => {
|
|
300
|
+
if (!layoutReady.value || !sceneRef.value) {
|
|
301
|
+
registerPreviewSnapshotProvider(null);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
registerPreviewSnapshotProvider(async () => {
|
|
305
|
+
const scene = sceneRef.value;
|
|
306
|
+
if (!scene) return null;
|
|
307
|
+
const width = outputScale.value
|
|
308
|
+
? sceneBounds.value.width
|
|
309
|
+
: sceneBounds.value.width * fillScale.value.scaleX;
|
|
310
|
+
const height = outputScale.value
|
|
311
|
+
? sceneBounds.value.height
|
|
312
|
+
: sceneBounds.value.height * fillScale.value.scaleY;
|
|
313
|
+
return capturePreviewSceneElement(scene, width, height);
|
|
314
|
+
});
|
|
315
|
+
},
|
|
316
|
+
{ immediate: true }
|
|
317
|
+
);
|
|
318
|
+
onUnmounted(() => registerPreviewSnapshotProvider(null));
|
|
319
|
+
|
|
295
320
|
function noopUpdate() {}
|
|
296
321
|
function noopSelect() {}
|
|
297
322
|
</script>
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
|
|
3
|
+
import {
|
|
4
|
+
registerAbuilderViewHandle,
|
|
5
|
+
type AbuilderViewHandle,
|
|
6
|
+
} from "./library/abuilder-view-handle";
|
|
7
|
+
import {
|
|
8
|
+
captureEditorPreviewSnapshot,
|
|
9
|
+
registerPreviewSnapshotProvider,
|
|
10
|
+
} from "./library/preview-snapshot";
|
|
11
|
+
import { readOutputScale } from "./utils/outputScale";
|
|
3
12
|
import type { State } from "@arronqzy/rx-store";
|
|
4
13
|
import {
|
|
5
14
|
useBlueprintDslLocaleSync,
|
|
@@ -41,9 +50,9 @@ import {
|
|
|
41
50
|
Modal,
|
|
42
51
|
Slider,
|
|
43
52
|
Space,
|
|
44
|
-
Switch,
|
|
45
53
|
Tabs,
|
|
46
54
|
Tag,
|
|
55
|
+
Tooltip,
|
|
47
56
|
message,
|
|
48
57
|
} from "ant-design-vue";
|
|
49
58
|
import { usePanelElements } from "./hooks/usePanelElements";
|
|
@@ -90,6 +99,11 @@ const props = withDefaults(
|
|
|
90
99
|
initialZoom?: number;
|
|
91
100
|
nameSpace?: string | null;
|
|
92
101
|
initialWorkspace?: WorkspaceProjectRecord | null;
|
|
102
|
+
/**
|
|
103
|
+
* 按 IndexedDB id 打开工作区(不 remount)。
|
|
104
|
+
* 与 initialWorkspace 同时存在时:首次优先完整记录;之后 id 变化从 IndexedDB 打开。
|
|
105
|
+
*/
|
|
106
|
+
workspaceId?: string | null;
|
|
93
107
|
/** 顶栏「设置 → 关于」展示的 npm 包名 */
|
|
94
108
|
packageName?: string | null;
|
|
95
109
|
/** 顶栏「设置 → 关于」展示的包版本号 */
|
|
@@ -99,6 +113,7 @@ const props = withDefaults(
|
|
|
99
113
|
initialZoom: 1,
|
|
100
114
|
nameSpace: null,
|
|
101
115
|
initialWorkspace: null,
|
|
116
|
+
workspaceId: null,
|
|
102
117
|
packageName: null,
|
|
103
118
|
packageVersion: null,
|
|
104
119
|
}
|
|
@@ -173,8 +188,8 @@ const editingLayerId = ref<string | null>(null);
|
|
|
173
188
|
const editingLayerName = ref("");
|
|
174
189
|
const isMergingLayers = ref(false);
|
|
175
190
|
const mergeLayerName = ref("");
|
|
176
|
-
const leftWidth = ref(
|
|
177
|
-
const rightWidth = ref(
|
|
191
|
+
const leftWidth = ref(220);
|
|
192
|
+
const rightWidth = ref(280);
|
|
178
193
|
const importInputRef = ref<HTMLInputElement | null>(null);
|
|
179
194
|
const blueprintImportInputRef = ref<HTMLInputElement | null>(null);
|
|
180
195
|
const apiCollectionManagerOpen = ref(false);
|
|
@@ -470,9 +485,39 @@ const workspaceProjects = useWorkspaceProjects({
|
|
|
470
485
|
panelRevision: computed(() => `${historyCursor.value}|${allElements.value.length}`),
|
|
471
486
|
onProjectApplied: handleWorkspaceProjectApplied,
|
|
472
487
|
initialWorkspace: () => props.initialWorkspace,
|
|
488
|
+
workspaceId: () => props.workspaceId,
|
|
473
489
|
nameSpace: () => props.nameSpace,
|
|
474
490
|
});
|
|
475
491
|
|
|
492
|
+
const viewHandle: AbuilderViewHandle = {
|
|
493
|
+
getNameSpace: () => props.nameSpace ?? null,
|
|
494
|
+
getCurrentWorkspace: () => workspaceProjects.getCurrentWorkspaceRecord(),
|
|
495
|
+
openWorkspace: (idOrRecord) => workspaceProjects.handleOpenProject(idOrRecord),
|
|
496
|
+
createWorkspace: () => workspaceProjects.handleCreateProject(),
|
|
497
|
+
saveWorkspace: () => workspaceProjects.handleSaveProject(),
|
|
498
|
+
syncWorkspace: () => workspaceProjects.handleSyncProject(),
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
defineExpose(viewHandle);
|
|
502
|
+
onUnmounted(registerAbuilderViewHandle(viewHandle));
|
|
503
|
+
|
|
504
|
+
watch(
|
|
505
|
+
[canvasEl, allElements, layers, activeLayerId, () => props.nameSpace],
|
|
506
|
+
() => {
|
|
507
|
+
registerPreviewSnapshotProvider(async () =>
|
|
508
|
+
captureEditorPreviewSnapshot({
|
|
509
|
+
canvasRoot: canvasEl.value,
|
|
510
|
+
allElements: allElements.value,
|
|
511
|
+
layers: layers.value,
|
|
512
|
+
activeLayerId: activeLayerId.value,
|
|
513
|
+
outputScale: readOutputScale(props.nameSpace),
|
|
514
|
+
})
|
|
515
|
+
);
|
|
516
|
+
},
|
|
517
|
+
{ immediate: true }
|
|
518
|
+
);
|
|
519
|
+
onUnmounted(() => registerPreviewSnapshotProvider(null));
|
|
520
|
+
|
|
476
521
|
const blueprintLifecycleToken = computed(() => {
|
|
477
522
|
const doc = blueprintGraph.value.document;
|
|
478
523
|
const nodes = doc.nodes;
|
|
@@ -646,7 +691,10 @@ function handleApiCollectionUploadStart(info: {
|
|
|
646
691
|
fileNames: string[];
|
|
647
692
|
count: number;
|
|
648
693
|
}) {
|
|
649
|
-
emitAbuilderEvent(AbuilderEvents.apiCollectionUploadStart,
|
|
694
|
+
emitAbuilderEvent(AbuilderEvents.apiCollectionUploadStart, {
|
|
695
|
+
...info,
|
|
696
|
+
nameSpace: props.nameSpace ?? null,
|
|
697
|
+
});
|
|
650
698
|
}
|
|
651
699
|
|
|
652
700
|
function handleApiCollectionUploadSuccess(info: {
|
|
@@ -657,7 +705,10 @@ function handleApiCollectionUploadSuccess(info: {
|
|
|
657
705
|
persistLocal: boolean;
|
|
658
706
|
}>;
|
|
659
707
|
}) {
|
|
660
|
-
emitAbuilderEvent(AbuilderEvents.apiCollectionUploadSuccess,
|
|
708
|
+
emitAbuilderEvent(AbuilderEvents.apiCollectionUploadSuccess, {
|
|
709
|
+
...info,
|
|
710
|
+
nameSpace: props.nameSpace ?? null,
|
|
711
|
+
});
|
|
661
712
|
}
|
|
662
713
|
|
|
663
714
|
function handleApiCollectionSync(record: {
|
|
@@ -807,9 +858,9 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
|
|
|
807
858
|
|
|
808
859
|
<template>
|
|
809
860
|
<Layout class="vue-view-panel h-screen overflow-hidden" :class="props.class" :key="locale">
|
|
810
|
-
<Layout.Header class="flex h-
|
|
861
|
+
<Layout.Header class="flex h-10 flex-wrap items-center gap-2 border-b border-border/70 !bg-background/80 px-3 py-0 leading-none backdrop-blur-md">
|
|
811
862
|
<Dropdown :trigger="['click']">
|
|
812
|
-
<Button type="text" class="!text-
|
|
863
|
+
<Button type="text" class="!text-foreground">{{ t("panel.menubar.file") }}</Button>
|
|
813
864
|
<template #overlay>
|
|
814
865
|
<Menu>
|
|
815
866
|
<Menu.Item @click="handleExport">{{ t("panel.menubar.exportPanelJson") }}</Menu.Item>
|
|
@@ -818,7 +869,7 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
|
|
|
818
869
|
</template>
|
|
819
870
|
</Dropdown>
|
|
820
871
|
<Dropdown :trigger="['click']">
|
|
821
|
-
<Button type="text" class="!text-
|
|
872
|
+
<Button type="text" class="!text-foreground">{{ t("panel.menubar.edit") }}</Button>
|
|
822
873
|
<template #overlay>
|
|
823
874
|
<Menu>
|
|
824
875
|
<Menu.Item :disabled="!canUndo" @click="undo">{{ t("panel.menubar.undo") }}</Menu.Item>
|
|
@@ -834,7 +885,7 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
|
|
|
834
885
|
</template>
|
|
835
886
|
</Dropdown>
|
|
836
887
|
<Dropdown :trigger="['click']">
|
|
837
|
-
<Button type="text" class="!text-
|
|
888
|
+
<Button type="text" class="!text-foreground">{{ t("panel.menubar.blueprint") }}</Button>
|
|
838
889
|
<template #overlay>
|
|
839
890
|
<Menu>
|
|
840
891
|
<Menu.Item @click="openBlueprintExport">{{ t("panel.menubar.exportBlueprint") }}</Menu.Item>
|
|
@@ -857,7 +908,7 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
|
|
|
857
908
|
@preview-project="openOnlinePreviewForProject"
|
|
858
909
|
/>
|
|
859
910
|
<Dropdown :trigger="['click']">
|
|
860
|
-
<Button type="text" class="!text-
|
|
911
|
+
<Button type="text" class="!text-foreground">{{ t("panel.menubar.settings") }}</Button>
|
|
861
912
|
<template #overlay>
|
|
862
913
|
<Menu>
|
|
863
914
|
<Menu.ItemGroup :title="t('panel.theme.language')">
|
|
@@ -879,7 +930,7 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
|
|
|
879
930
|
</template>
|
|
880
931
|
</Dropdown>
|
|
881
932
|
<Dropdown :trigger="['click']">
|
|
882
|
-
<Button type="text" class="!text-
|
|
933
|
+
<Button type="text" class="!text-foreground">{{ t("panel.theme.language") }}</Button>
|
|
883
934
|
<template #overlay>
|
|
884
935
|
<Menu>
|
|
885
936
|
<Menu.Item :class="{ 'ant-menu-item-selected': locale === 'zh-CN' }" @click="setLocale('zh-CN')">{{ t("panel.theme.zhCN") }}</Menu.Item>
|
|
@@ -914,12 +965,12 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
|
|
|
914
965
|
|
|
915
966
|
<Layout.Content class="relative min-h-0 min-w-0">
|
|
916
967
|
<div class="flex h-full min-h-0 flex-col">
|
|
917
|
-
<div class="flex flex-wrap items-center gap-2 border-b bg-
|
|
968
|
+
<div class="flex flex-wrap items-center gap-2 border-b border-border/60 bg-muted/15 px-3 py-1.5">
|
|
918
969
|
<Input
|
|
919
970
|
:value="displayProductName"
|
|
920
971
|
size="small"
|
|
921
972
|
@update:value="(value: string) => (productName = value)"
|
|
922
|
-
class="w-[
|
|
973
|
+
class="w-[180px]"
|
|
923
974
|
:placeholder="t('panel.menubar.productName')"
|
|
924
975
|
/>
|
|
925
976
|
<span class="text-xs text-gray-500">{{ t("panel.menubar.zoom") }}</span>
|
|
@@ -933,10 +984,35 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
|
|
|
933
984
|
/>
|
|
934
985
|
<Tag v-if="activeLayer">{{ activeLayer.name }}</Tag>
|
|
935
986
|
<Tag color="blue">{{ t("panel.menubar.selectedCount", { count: selectedIds.length }) }}</Tag>
|
|
936
|
-
<
|
|
937
|
-
<
|
|
938
|
-
|
|
939
|
-
|
|
987
|
+
<Tooltip :title="t('panel.menubar.showBlueprintPanel')">
|
|
988
|
+
<button
|
|
989
|
+
type="button"
|
|
990
|
+
data-blueprint-toggle
|
|
991
|
+
class="ml-auto inline-flex h-7 w-7 items-center justify-center rounded-md border transition-colors"
|
|
992
|
+
:class="
|
|
993
|
+
blueprintOpen
|
|
994
|
+
? 'border-primary/50 bg-primary/15 text-primary'
|
|
995
|
+
: 'border-border/70 bg-background/80 text-muted-foreground hover:bg-accent hover:text-accent-foreground'
|
|
996
|
+
"
|
|
997
|
+
:aria-pressed="blueprintOpen"
|
|
998
|
+
:aria-label="t('panel.menubar.showBlueprintPanel')"
|
|
999
|
+
@click="blueprintOpen = !blueprintOpen"
|
|
1000
|
+
>
|
|
1001
|
+
<svg
|
|
1002
|
+
viewBox="0 0 24 24"
|
|
1003
|
+
class="h-3.5 w-3.5"
|
|
1004
|
+
fill="none"
|
|
1005
|
+
stroke="currentColor"
|
|
1006
|
+
:stroke-width="blueprintOpen ? 2.2 : 2"
|
|
1007
|
+
>
|
|
1008
|
+
<rect x="3" y="4" width="18" height="16" rx="2" />
|
|
1009
|
+
<circle cx="8" cy="10" r="1.5" :fill="blueprintOpen ? 'currentColor' : 'none'" />
|
|
1010
|
+
<circle cx="16" cy="10" r="1.5" :fill="blueprintOpen ? 'currentColor' : 'none'" />
|
|
1011
|
+
<circle cx="12" cy="16" r="1.5" :fill="blueprintOpen ? 'currentColor' : 'none'" />
|
|
1012
|
+
<path d="M9.2 10.8 10.8 14.2M14.8 10.8 13.2 14.2" />
|
|
1013
|
+
</svg>
|
|
1014
|
+
</button>
|
|
1015
|
+
</Tooltip>
|
|
940
1016
|
</div>
|
|
941
1017
|
|
|
942
1018
|
<WorkspaceStageSplit
|
|
@@ -1005,63 +1081,112 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
|
|
|
1005
1081
|
</PanelCanvas>
|
|
1006
1082
|
</WorkspaceStageSplit>
|
|
1007
1083
|
|
|
1008
|
-
<div class="border-t bg-
|
|
1009
|
-
<div class="
|
|
1010
|
-
<
|
|
1011
|
-
|
|
1012
|
-
|
|
1084
|
+
<div class="border-t border-border/50 bg-muted/5 px-1.5 py-0.5">
|
|
1085
|
+
<div class="flex min-w-0 flex-wrap items-center gap-1">
|
|
1086
|
+
<Tooltip :title="t('panel.layers.title')">
|
|
1087
|
+
<span class="inline-flex h-6 items-center px-1 text-[11px] font-semibold text-muted-foreground">
|
|
1088
|
+
{{ t("panel.layers.title") }}
|
|
1089
|
+
</span>
|
|
1090
|
+
</Tooltip>
|
|
1091
|
+
<Tooltip :title="t('panel.layers.add')">
|
|
1092
|
+
<Button size="small" class="!h-6 !px-2 !text-[11px]" @click="addLayer()">
|
|
1093
|
+
{{ t("panel.layers.addShort") }}
|
|
1094
|
+
</Button>
|
|
1095
|
+
</Tooltip>
|
|
1096
|
+
<Tooltip :title="canMergeLayers ? t('panel.layers.merge') : t('panel.layers.mergeNeedTwo')">
|
|
1097
|
+
<Button
|
|
1098
|
+
size="small"
|
|
1099
|
+
class="!h-6 !px-2 !text-[11px]"
|
|
1100
|
+
:disabled="!canMergeLayers"
|
|
1101
|
+
@click="isMergingLayers = true"
|
|
1102
|
+
>
|
|
1103
|
+
{{ t("panel.layers.mergeShort") }}
|
|
1104
|
+
</Button>
|
|
1105
|
+
</Tooltip>
|
|
1106
|
+
<Tabs
|
|
1107
|
+
size="small"
|
|
1108
|
+
class="min-w-0 flex-1"
|
|
1109
|
+
:active-key="activeLayerId"
|
|
1110
|
+
type="card"
|
|
1111
|
+
@update:active-key="(k: string | number) => setActiveLayer(String(k))"
|
|
1112
|
+
>
|
|
1113
|
+
<Tabs.TabPane v-for="layer in layers" :key="layer.id">
|
|
1114
|
+
<template #tab>
|
|
1115
|
+
<span class="inline-flex max-w-[110px] items-center gap-1 text-[11px] font-medium">
|
|
1116
|
+
<span class="truncate">{{ layer.name }}</span>
|
|
1117
|
+
<Tag v-if="layer.isPrimary" color="green" class="!m-0 !px-1 !text-[10px]">
|
|
1118
|
+
{{ t("panel.layers.primary") }}
|
|
1119
|
+
</Tag>
|
|
1120
|
+
</span>
|
|
1121
|
+
</template>
|
|
1122
|
+
</Tabs.TabPane>
|
|
1123
|
+
</Tabs>
|
|
1013
1124
|
</div>
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
@update:active-key="(k: string | number) => setActiveLayer(String(k))"
|
|
1125
|
+
|
|
1126
|
+
<div
|
|
1127
|
+
v-if="activeLayer"
|
|
1128
|
+
class="mt-1 flex flex-wrap items-center gap-1 rounded-md border border-border/50 bg-card/80 px-1.5 py-0.5 text-[11px]"
|
|
1019
1129
|
>
|
|
1020
|
-
<Tabs.TabPane v-for="layer in layers" :key="layer.id">
|
|
1021
|
-
<template #tab>
|
|
1022
|
-
<span class="inline-flex items-center gap-1">
|
|
1023
|
-
{{ layer.name }}
|
|
1024
|
-
<Tag v-if="layer.isMapping" color="blue" class="!m-0">{{ t("panel.layers.mapping") }}</Tag>
|
|
1025
|
-
<Tag v-if="layer.isPrimary" color="green" class="!m-0">{{ t("panel.layers.primary") }}</Tag>
|
|
1026
|
-
</span>
|
|
1027
|
-
</template>
|
|
1028
|
-
</Tabs.TabPane>
|
|
1029
|
-
</Tabs>
|
|
1030
|
-
|
|
1031
|
-
<div v-if="activeLayer" class="mt-2 flex flex-wrap items-center gap-2 rounded border px-2 py-1.5 text-xs">
|
|
1032
1130
|
<template v-if="editingLayerId === activeLayer.id">
|
|
1033
1131
|
<Input
|
|
1034
1132
|
v-model:value="editingLayerName"
|
|
1035
1133
|
size="small"
|
|
1036
|
-
class="w-
|
|
1134
|
+
class="!h-6 w-28"
|
|
1037
1135
|
@press-enter="commitRenameLayer"
|
|
1038
1136
|
/>
|
|
1039
|
-
<
|
|
1137
|
+
<Tooltip :title="t('common.ok')">
|
|
1138
|
+
<Button size="small" type="primary" class="!h-6 !px-2" @click="commitRenameLayer">
|
|
1139
|
+
{{ t("common.ok") }}
|
|
1140
|
+
</Button>
|
|
1141
|
+
</Tooltip>
|
|
1040
1142
|
</template>
|
|
1041
1143
|
<template v-else>
|
|
1042
|
-
<span class="font-
|
|
1043
|
-
<
|
|
1144
|
+
<span class="max-w-[100px] truncate font-semibold">{{ activeLayer.name }}</span>
|
|
1145
|
+
<Tooltip :title="t('panel.layers.rename')">
|
|
1146
|
+
<Button
|
|
1147
|
+
size="small"
|
|
1148
|
+
type="link"
|
|
1149
|
+
class="!h-6 !px-1 !text-[11px]"
|
|
1150
|
+
@click="startRenameLayer(activeLayer.id, activeLayer.name)"
|
|
1151
|
+
>
|
|
1152
|
+
{{ t("panel.layers.renameShort") }}
|
|
1153
|
+
</Button>
|
|
1154
|
+
</Tooltip>
|
|
1044
1155
|
</template>
|
|
1045
|
-
<
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1156
|
+
<Tooltip :title="activeLayer.locked ? t('panel.layers.unlock') : t('panel.layers.lock')">
|
|
1157
|
+
<Button size="small" class="!h-6 !px-2 !text-[11px]" @click="toggleLayerLock(activeLayer.id)">
|
|
1158
|
+
{{ activeLayer.locked ? t("panel.layers.unlockShort") : t("panel.layers.lockShort") }}
|
|
1159
|
+
</Button>
|
|
1160
|
+
</Tooltip>
|
|
1161
|
+
<Tooltip :title="t('panel.layers.joinMerge')">
|
|
1162
|
+
<Checkbox
|
|
1163
|
+
:checked="Boolean(activeLayer.mergeSelected)"
|
|
1164
|
+
@change="() => toggleLayerMergeSelected(activeLayer!.id)"
|
|
1165
|
+
>
|
|
1166
|
+
<span class="text-[11px]">{{ t("panel.layers.joinMerge") }}</span>
|
|
1167
|
+
</Checkbox>
|
|
1168
|
+
</Tooltip>
|
|
1169
|
+
<Tooltip :title="t('panel.layers.setPrimaryHint')">
|
|
1170
|
+
<Button
|
|
1171
|
+
size="small"
|
|
1172
|
+
class="!h-6 !px-2 !text-[11px]"
|
|
1173
|
+
:disabled="activeLayer.isPrimary"
|
|
1174
|
+
@click="setPrimaryLayer(activeLayer.id)"
|
|
1175
|
+
>
|
|
1176
|
+
{{ t("panel.layers.setPrimary") }}
|
|
1177
|
+
</Button>
|
|
1178
|
+
</Tooltip>
|
|
1179
|
+
<Tooltip :title="t('panel.layers.delete')">
|
|
1180
|
+
<Button
|
|
1181
|
+
size="small"
|
|
1182
|
+
danger
|
|
1183
|
+
class="!h-6 !px-2 !text-[11px]"
|
|
1184
|
+
:disabled="!activeLayer.editable"
|
|
1185
|
+
@click="deleteLayer(activeLayer.id)"
|
|
1186
|
+
>
|
|
1187
|
+
{{ t("panel.layers.delete") }}
|
|
1188
|
+
</Button>
|
|
1189
|
+
</Tooltip>
|
|
1065
1190
|
</div>
|
|
1066
1191
|
</div>
|
|
1067
1192
|
</div>
|
|
@@ -189,7 +189,7 @@ function hasViewEvent(el: PanelElement) {
|
|
|
189
189
|
:class="[
|
|
190
190
|
'absolute select-none',
|
|
191
191
|
previewMode ? '' : 'rv-selectable',
|
|
192
|
-
!previewMode && selectedIds.includes(el.id) ? '
|
|
192
|
+
!previewMode && selectedIds.includes(el.id) ? 'rv-node-selected' : '',
|
|
193
193
|
hasViewEvent(el) ? 'cursor-pointer' : '',
|
|
194
194
|
]"
|
|
195
195
|
:data-element-id="el.id"
|