@arronqzy/vue-view 0.1.0 → 0.1.3
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/package.json +10 -7
- package/src/panel/VueViewOnlinePreview.vue +15 -7
- package/src/panel/VueViewPanel.vue +81 -55
- package/src/panel/components/ConfigHintIcon.vue +4 -2
- package/src/panel/components/ElementsLayer.vue +15 -0
- package/src/panel/components/MaterialPreview.vue +19 -1
- package/src/panel/components/MaterialSidebar.vue +35 -31
- package/src/panel/components/MaterialSidebarTreeNode.vue +27 -26
- package/src/panel/components/MoveableLayer.vue +7 -4
- package/src/panel/components/PanelConfigSidebar.vue +57 -39
- package/src/panel/components/ViewElementScopePanel.vue +6 -4
- package/src/panel/components/WorkspaceConfigSidebar.vue +7 -1
- package/src/panel/components/WorkspaceProjectNav.vue +23 -16
- package/src/panel/components/WorkspaceStageSplit.vue +10 -7
- package/src/panel/components/config/ConfigColorField.vue +3 -1
- package/src/panel/components/config/PanelConfigAudioSection.vue +44 -43
- package/src/panel/components/config/PanelConfigChartSection.vue +79 -76
- package/src/panel/components/config/PanelConfigGeometrySection.vue +21 -19
- package/src/panel/components/config/PanelConfigGridChildSpan.vue +7 -5
- package/src/panel/components/config/PanelConfigGridSection.vue +10 -8
- package/src/panel/components/config/PanelConfigImageSection.vue +22 -21
- package/src/panel/components/config/PanelConfigMultiSelect.vue +42 -40
- package/src/panel/components/config/PanelConfigNodeInfo.vue +19 -16
- package/src/panel/components/config/PanelConfigReferenceSection.vue +11 -9
- package/src/panel/components/config/PanelConfigStyleSections.vue +27 -26
- package/src/panel/components/config/PanelConfigTextSection.vue +20 -17
- package/src/panel/components/config/PanelConfigVideoSection.vue +15 -14
- package/src/panel/components/config/shared.ts +0 -3
- package/src/panel/components/elementsLayerNodes.ts +37 -18
- package/src/panel/components/materialSidebarData.ts +73 -54
- package/src/panel/components/scope-config/ScopeTemplatePreviewHost.vue +7 -2
- package/src/panel/components/scope-config/ScopeTemplatePreviewPanel.vue +6 -3
- package/src/panel/components/scope-config/ScopeTemplateUsageHint.vue +7 -16
- package/src/panel/components/scope-config/ScopeTemplateWarningsPanel.vue +4 -2
- package/src/panel/components/table/ConditionEditor.vue +181 -0
- package/src/panel/components/table/JsonCodeEditor.vue +148 -0
- package/src/panel/components/table/PanelConfigTableSection.vue +2033 -0
- package/src/panel/components/table/TableNodeContent.vue +557 -0
- package/src/panel/constants/messages.ts +76 -34
- package/src/panel/hooks/usePanelElements.ts +54 -41
- package/src/panel/hooks/useWorkspaceProjects.ts +8 -5
- package/src/panel/types.ts +4 -0
- package/src/panel/utils/panelElementDefaults.ts +73 -39
- package/src/panel/utils/scope-field-labels.ts +53 -39
- package/src/panel/utils/scope-template-spread.ts +57 -24
- package/src/panel/utils/scope-template-warnings.ts +23 -22
- package/src/panel/utils/scope-template.ts +80 -0
- package/src/tailwind.css +18 -10
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { computed, onMounted, ref, watch } from "vue";
|
|
3
4
|
import { Empty, Input } from "ant-design-vue";
|
|
4
5
|
import type { PanelElement, PanelLayer, ReferenceCopyMode } from "../types";
|
|
5
6
|
import { CHART_TYPES } from "../utils/chartOptionBuilder";
|
|
6
|
-
import {
|
|
7
|
+
import { getPanelMessages } from "../constants/messages";
|
|
7
8
|
import { hasViewElementScope } from "../scope/view-scope-store";
|
|
8
9
|
import { collectElementScopeWarnings } from "../utils/scope-template-warnings";
|
|
9
10
|
import { sectionMatchesSearch } from "./config/shared";
|
|
@@ -22,7 +23,10 @@ import PanelConfigGeometrySection from "./config/PanelConfigGeometrySection.vue"
|
|
|
22
23
|
import PanelConfigGridSection from "./config/PanelConfigGridSection.vue";
|
|
23
24
|
import PanelConfigGridChildSpan from "./config/PanelConfigGridChildSpan.vue";
|
|
24
25
|
import PanelConfigReferenceSection from "./config/PanelConfigReferenceSection.vue";
|
|
26
|
+
import PanelConfigTableSection from "./table/PanelConfigTableSection.vue";
|
|
25
27
|
|
|
28
|
+
const { t, locale } = useI18n();
|
|
29
|
+
const msgs = () => getPanelMessages(t);
|
|
26
30
|
const SEARCH_COLLAPSE_STORAGE_KEY = "panel:config-search-collapsed";
|
|
27
31
|
|
|
28
32
|
const props = withDefaults(
|
|
@@ -43,9 +47,11 @@ const props = withDefaults(
|
|
|
43
47
|
action: "bringForward" | "sendBackward" | "bringToFront" | "sendToBack"
|
|
44
48
|
) => void;
|
|
45
49
|
viewElementScope?: unknown;
|
|
50
|
+
blueprintNodeOptions?: { id: string; label: string }[];
|
|
46
51
|
}>(),
|
|
47
52
|
{
|
|
48
53
|
selectedElements: () => [],
|
|
54
|
+
blueprintNodeOptions: () => [],
|
|
49
55
|
}
|
|
50
56
|
);
|
|
51
57
|
|
|
@@ -67,6 +73,7 @@ const expandedSections = ref<Record<string, boolean>>({
|
|
|
67
73
|
gridChildSpan: true,
|
|
68
74
|
reference: true,
|
|
69
75
|
imageConfig: true,
|
|
76
|
+
tableConfig: true,
|
|
70
77
|
});
|
|
71
78
|
|
|
72
79
|
const normalizedSearch = computed(() => configSearch.value.trim().toLowerCase());
|
|
@@ -110,7 +117,7 @@ const showScopePanel = computed(
|
|
|
110
117
|
|
|
111
118
|
const scopeWarnings = computed(() => {
|
|
112
119
|
if (!props.selectedElement || props.viewElementScope === undefined) return [];
|
|
113
|
-
return collectElementScopeWarnings(props.selectedElement, props.viewElementScope);
|
|
120
|
+
return collectElementScopeWarnings(props.selectedElement, props.viewElementScope, t);
|
|
114
121
|
});
|
|
115
122
|
|
|
116
123
|
const materialType = computed(() => props.selectedElement?.materialType ?? "");
|
|
@@ -133,24 +140,24 @@ const visibleSectionCount = computed(() => {
|
|
|
133
140
|
if (!props.selectedElement || isMultiSelectMode.value) return 0;
|
|
134
141
|
let count = 0;
|
|
135
142
|
const checks: Array<[string, string, string[]]> = [
|
|
136
|
-
["nodeInfo", "
|
|
137
|
-
["styleBackground", "
|
|
138
|
-
["styleBorder", "
|
|
143
|
+
["nodeInfo", t("panel.config.sectionNodeInfo"), [t("panel.config.name"), "id", t("panel.config.type")]],
|
|
144
|
+
["styleBackground", t("panel.config.sectionStyleBackground"), [t("panel.config.backgroundColor"), "background"]],
|
|
145
|
+
["styleBorder", t("panel.config.sectionStyleBorder"), [t("panel.config.searchKwBorder")]],
|
|
139
146
|
];
|
|
140
147
|
if (isChartElement.value) {
|
|
141
|
-
checks.push(["chartBasic", "
|
|
142
|
-
checks.push(["chartAdvanced", "
|
|
148
|
+
checks.push(["chartBasic", t("panel.config.sectionChartBasic"), [t("panel.material.charts")]]);
|
|
149
|
+
checks.push(["chartAdvanced", t("panel.config.sectionChartAdvanced"), ["json", t("panel.config.searchKwAdvanced")]]);
|
|
143
150
|
}
|
|
144
|
-
if (materialType.value === "text") checks.push(["textConfig", "
|
|
145
|
-
if (materialType.value === "audio") checks.push(["audioConfig", "
|
|
146
|
-
if (materialType.value === "video") checks.push(["videoConfig", "
|
|
147
|
-
if (materialType.value === "image") checks.push(["imageConfig", "
|
|
148
|
-
if (materialType.value === "geometry") checks.push(["geometryConfig", "
|
|
149
|
-
if (materialType.value === "grid") checks.push(["gridConfig", "
|
|
151
|
+
if (materialType.value === "text") checks.push(["textConfig", t("panel.config.sectionText"), [t("panel.defaults.text")]]);
|
|
152
|
+
if (materialType.value === "audio") checks.push(["audioConfig", t("panel.config.sectionAudio"), [t("panel.defaults.audio")]]);
|
|
153
|
+
if (materialType.value === "video") checks.push(["videoConfig", t("panel.config.sectionVideo"), [t("panel.defaults.video")]]);
|
|
154
|
+
if (materialType.value === "image") checks.push(["imageConfig", t("panel.config.sectionImage"), [t("panel.defaults.image")]]);
|
|
155
|
+
if (materialType.value === "geometry") checks.push(["geometryConfig", t("panel.config.sectionGeometry"), [t("panel.defaults.geometry")]]);
|
|
156
|
+
if (materialType.value === "grid") checks.push(["gridConfig", t("panel.config.sectionGrid"), [t("panel.config.searchKwGrid")]]);
|
|
150
157
|
if (props.selectedElement?.parentGridId) {
|
|
151
|
-
checks.push(["gridChildSpan", "
|
|
158
|
+
checks.push(["gridChildSpan", t("panel.config.sectionGridChildSpan"), [t("panel.config.searchKwCrossCol")]]);
|
|
152
159
|
}
|
|
153
|
-
if (materialType.value === "reference") checks.push(["reference", "
|
|
160
|
+
if (materialType.value === "reference") checks.push(["reference", t("panel.config.sectionReference"), [t("panel.material.reference")]]);
|
|
154
161
|
for (const [, title, terms] of checks) {
|
|
155
162
|
if (shouldShowSection("", title, terms)) count += 1;
|
|
156
163
|
}
|
|
@@ -188,7 +195,7 @@ watch(
|
|
|
188
195
|
class="scope-config-sidebar h-full overflow-auto border-l border-gray-200 bg-gray-50/30 px-3 py-3 text-gray-900 [&_.scope-field--highlight]:rounded-md [&_.scope-field--highlight]:ring-2 [&_.scope-field--highlight]:ring-amber-400/80"
|
|
189
196
|
>
|
|
190
197
|
<div class="sticky top-0 z-20 mb-3 rounded-lg border border-gray-200/80 bg-white px-2.5 py-2 shadow-sm">
|
|
191
|
-
<div class="text-xs font-semibold tracking-wide"
|
|
198
|
+
<div class="text-xs font-semibold tracking-wide">{{ t("panel.config.panelTitle") }}</div>
|
|
192
199
|
<ViewElementScopePanel v-if="showScopePanel" :scope="viewElementScope!" />
|
|
193
200
|
<ScopeTemplateWarningsPanel />
|
|
194
201
|
<div
|
|
@@ -200,18 +207,18 @@ watch(
|
|
|
200
207
|
class="rounded border border-gray-200 px-1.5 py-0.5 text-[11px] text-gray-500 hover:bg-gray-50"
|
|
201
208
|
@click="isSearchCollapsed = !isSearchCollapsed"
|
|
202
209
|
>
|
|
203
|
-
{{ isSearchCollapsed ? "
|
|
210
|
+
{{ isSearchCollapsed ? t("panel.config.expandSearch") : t("panel.config.collapseSearch") }}
|
|
204
211
|
</button>
|
|
205
212
|
</div>
|
|
206
213
|
<div v-if="!isSearchCollapsed" class="mt-2">
|
|
207
214
|
<Input
|
|
208
215
|
v-model:value="configSearch"
|
|
209
216
|
size="small"
|
|
210
|
-
placeholder="
|
|
217
|
+
:placeholder="t('panel.config.searchPlaceholder')"
|
|
211
218
|
data-scope-autocomplete="off"
|
|
212
219
|
/>
|
|
213
220
|
<div v-if="hasSearch" class="mt-1 text-[11px] text-gray-500">
|
|
214
|
-
|
|
221
|
+
{{ t("panel.config.searching", { query: configSearch }) }}
|
|
215
222
|
</div>
|
|
216
223
|
</div>
|
|
217
224
|
</div>
|
|
@@ -232,14 +239,14 @@ watch(
|
|
|
232
239
|
<Empty
|
|
233
240
|
v-else-if="!selectedElement"
|
|
234
241
|
class="py-7"
|
|
235
|
-
description="
|
|
242
|
+
:description="t('panel.config.emptyNoNodeDesc')"
|
|
236
243
|
/>
|
|
237
244
|
|
|
238
245
|
<div v-else class="space-y-3">
|
|
239
246
|
<fieldset :disabled="!isNodeEditable" :class="!isNodeEditable ? 'opacity-60' : ''">
|
|
240
247
|
<div class="space-y-3.5 text-xs">
|
|
241
248
|
<PanelConfigNodeInfo
|
|
242
|
-
v-if="shouldShowSection('nodeInfo', '
|
|
249
|
+
v-if="shouldShowSection('nodeInfo', t('panel.config.sectionNodeInfo'), [t('panel.config.name'), 'id', t('panel.config.type'), t('panel.layers.lockShort')])"
|
|
243
250
|
:element="selectedElement"
|
|
244
251
|
:layers="layers"
|
|
245
252
|
:is-editable="isNodeEditable"
|
|
@@ -254,13 +261,13 @@ watch(
|
|
|
254
261
|
|
|
255
262
|
<PanelConfigStyleSections
|
|
256
263
|
v-if="
|
|
257
|
-
shouldShowSection('styleBackground', '
|
|
258
|
-
shouldShowSection('styleBorder', '
|
|
264
|
+
shouldShowSection('styleBackground', t('panel.config.sectionStyleBackground'), [t('panel.config.backgroundColor'), 'background']) ||
|
|
265
|
+
shouldShowSection('styleBorder', t('panel.config.sectionStyleBorder'), [t('panel.config.searchKwBorder'), 'border'])
|
|
259
266
|
"
|
|
260
267
|
:element="selectedElement"
|
|
261
268
|
:is-editable="isNodeEditable"
|
|
262
|
-
:show-background="shouldShowSection('styleBackground', '
|
|
263
|
-
:show-border="shouldShowSection('styleBorder', '
|
|
269
|
+
:show-background="shouldShowSection('styleBackground', t('panel.config.sectionStyleBackground'), [t('panel.config.backgroundColor'), 'background'])"
|
|
270
|
+
:show-border="shouldShowSection('styleBorder', t('panel.config.sectionStyleBorder'), [t('panel.config.searchKwBorder'), 'border'])"
|
|
264
271
|
:background-open="isSectionExpanded('styleBackground')"
|
|
265
272
|
:border-open="isSectionExpanded('styleBorder')"
|
|
266
273
|
:force-open="forceOpenSections"
|
|
@@ -270,7 +277,7 @@ watch(
|
|
|
270
277
|
/>
|
|
271
278
|
|
|
272
279
|
<PanelConfigChartSection
|
|
273
|
-
v-if="isChartElement && shouldShowSection('chartBasic', '
|
|
280
|
+
v-if="isChartElement && shouldShowSection('chartBasic', t('panel.config.sectionChartBasic'), [t('panel.material.charts'), 'tooltip'])"
|
|
274
281
|
:element="selectedElement"
|
|
275
282
|
:is-editable="isNodeEditable"
|
|
276
283
|
:basic-open="isSectionExpanded('chartBasic')"
|
|
@@ -282,7 +289,7 @@ watch(
|
|
|
282
289
|
/>
|
|
283
290
|
|
|
284
291
|
<PanelConfigTextSection
|
|
285
|
-
v-if="materialType === 'text' && shouldShowSection('textConfig', '
|
|
292
|
+
v-if="materialType === 'text' && shouldShowSection('textConfig', t('panel.config.sectionText'), [t('panel.defaults.text'), t('panel.config.fontFamily'), t('panel.config.color')])"
|
|
286
293
|
:element="selectedElement"
|
|
287
294
|
:is-editable="isNodeEditable"
|
|
288
295
|
:open="isSectionExpanded('textConfig')"
|
|
@@ -291,8 +298,19 @@ watch(
|
|
|
291
298
|
@update:open="(v) => setSectionExpanded('textConfig', v)"
|
|
292
299
|
/>
|
|
293
300
|
|
|
301
|
+
<PanelConfigTableSection
|
|
302
|
+
v-if="materialType === 'table' && shouldShowSection('tableConfig', t('panel.config.sectionTable'), [t('panel.material.table'), 'table', t('panel.config.tableSource'), t('panel.config.tableColumns'), t('panel.config.tableRowsText')])"
|
|
303
|
+
:element="selectedElement"
|
|
304
|
+
:is-editable="isNodeEditable"
|
|
305
|
+
:open="isSectionExpanded('tableConfig')"
|
|
306
|
+
:force-open="forceOpenSections"
|
|
307
|
+
:update-element="updateElement"
|
|
308
|
+
:blueprint-node-options="blueprintNodeOptions"
|
|
309
|
+
@update:open="(v) => setSectionExpanded('tableConfig', v)"
|
|
310
|
+
/>
|
|
311
|
+
|
|
294
312
|
<PanelConfigAudioSection
|
|
295
|
-
v-if="materialType === 'audio' && shouldShowSection('audioConfig', '
|
|
313
|
+
v-if="materialType === 'audio' && shouldShowSection('audioConfig', t('panel.config.sectionAudio'), [t('panel.defaults.audio'), 'url', t('panel.config.searchKwRecord')])"
|
|
296
314
|
:element="selectedElement"
|
|
297
315
|
:is-editable="isNodeEditable"
|
|
298
316
|
:open="isSectionExpanded('audioConfig')"
|
|
@@ -302,7 +320,7 @@ watch(
|
|
|
302
320
|
/>
|
|
303
321
|
|
|
304
322
|
<PanelConfigVideoSection
|
|
305
|
-
v-if="materialType === 'video' && shouldShowSection('videoConfig', '
|
|
323
|
+
v-if="materialType === 'video' && shouldShowSection('videoConfig', t('panel.config.sectionVideo'), [t('panel.defaults.video'), 'url'])"
|
|
306
324
|
:element="selectedElement"
|
|
307
325
|
:is-editable="isNodeEditable"
|
|
308
326
|
:open="isSectionExpanded('videoConfig')"
|
|
@@ -312,7 +330,7 @@ watch(
|
|
|
312
330
|
/>
|
|
313
331
|
|
|
314
332
|
<PanelConfigImageSection
|
|
315
|
-
v-if="materialType === 'image' && shouldShowSection('imageConfig', '
|
|
333
|
+
v-if="materialType === 'image' && shouldShowSection('imageConfig', t('panel.config.sectionImage'), [t('panel.defaults.image'), 'src', 'fit'])"
|
|
316
334
|
:element="selectedElement"
|
|
317
335
|
:is-editable="isNodeEditable"
|
|
318
336
|
:open="isSectionExpanded('imageConfig')"
|
|
@@ -322,7 +340,7 @@ watch(
|
|
|
322
340
|
/>
|
|
323
341
|
|
|
324
342
|
<PanelConfigGeometrySection
|
|
325
|
-
v-if="materialType === 'geometry' && shouldShowSection('geometryConfig', '
|
|
343
|
+
v-if="materialType === 'geometry' && shouldShowSection('geometryConfig', t('panel.config.sectionGeometry'), [t('panel.defaults.geometry'), t('panel.config.shape')])"
|
|
326
344
|
:element="selectedElement"
|
|
327
345
|
:is-editable="isNodeEditable"
|
|
328
346
|
:open="isSectionExpanded('geometryConfig')"
|
|
@@ -332,7 +350,7 @@ watch(
|
|
|
332
350
|
/>
|
|
333
351
|
|
|
334
352
|
<PanelConfigGridSection
|
|
335
|
-
v-if="materialType === 'grid' && shouldShowSection('gridConfig', '
|
|
353
|
+
v-if="materialType === 'grid' && shouldShowSection('gridConfig', t('panel.config.sectionGrid'), [t('panel.config.searchKwGrid'), t('panel.config.rowsShort'), t('panel.config.colsShort'), t('panel.config.searchKwGap')])"
|
|
336
354
|
:element="selectedElement"
|
|
337
355
|
:is-editable="isNodeEditable"
|
|
338
356
|
:open="isSectionExpanded('gridConfig')"
|
|
@@ -342,7 +360,7 @@ watch(
|
|
|
342
360
|
/>
|
|
343
361
|
|
|
344
362
|
<PanelConfigGridChildSpan
|
|
345
|
-
v-if="selectedElement.parentGridId && shouldShowSection('gridChildSpan', '
|
|
363
|
+
v-if="selectedElement.parentGridId && shouldShowSection('gridChildSpan', t('panel.config.sectionGridChildSpan'), [t('panel.config.searchKwCrossCol'), t('panel.config.searchKwCrossRow')])"
|
|
346
364
|
:element="selectedElement"
|
|
347
365
|
:is-editable="isNodeEditable"
|
|
348
366
|
:open="isSectionExpanded('gridChildSpan')"
|
|
@@ -352,7 +370,7 @@ watch(
|
|
|
352
370
|
/>
|
|
353
371
|
|
|
354
372
|
<PanelConfigReferenceSection
|
|
355
|
-
v-if="materialType === 'reference' && shouldShowSection('reference', '
|
|
373
|
+
v-if="materialType === 'reference' && shouldShowSection('reference', t('panel.config.sectionReference'), [t('panel.material.reference'), t('panel.material.shallowCopy'), t('panel.material.deepCopy')])"
|
|
356
374
|
:element="selectedElement"
|
|
357
375
|
:layers="layers"
|
|
358
376
|
:is-editable="isNodeEditable"
|
|
@@ -364,17 +382,17 @@ watch(
|
|
|
364
382
|
/>
|
|
365
383
|
|
|
366
384
|
<div
|
|
367
|
-
v-if="!isChartElement && materialType && !['text', 'audio', 'video', 'image', 'geometry', 'grid', 'reference'].includes(materialType)"
|
|
385
|
+
v-if="!isChartElement && materialType && !['text', 'table', 'audio', 'video', 'image', 'geometry', 'grid', 'reference'].includes(materialType)"
|
|
368
386
|
class="text-xs leading-6 text-gray-500"
|
|
369
387
|
>
|
|
370
|
-
|
|
388
|
+
{{ t("panel.config.notChartType") }}
|
|
371
389
|
</div>
|
|
372
390
|
|
|
373
391
|
<div
|
|
374
392
|
v-if="hasSearch && visibleSectionCount === 0"
|
|
375
393
|
class="rounded border border-gray-200/60 bg-white px-2 py-1.5 text-[11px] text-gray-500"
|
|
376
394
|
>
|
|
377
|
-
|
|
395
|
+
{{ t("panel.config.noMatch") }}
|
|
378
396
|
</div>
|
|
379
397
|
</div>
|
|
380
398
|
</fieldset>
|
|
@@ -383,13 +401,13 @@ watch(
|
|
|
383
401
|
v-if="selectedElement.locked"
|
|
384
402
|
class="rounded border border-amber-500/40 bg-amber-500/10 px-2 py-1.5 text-[11px] text-amber-700"
|
|
385
403
|
>
|
|
386
|
-
{{
|
|
404
|
+
{{ msgs().nodeConfigLocked }}
|
|
387
405
|
</div>
|
|
388
406
|
<div
|
|
389
407
|
v-else-if="selectedLayer?.locked"
|
|
390
408
|
class="rounded border border-amber-500/40 bg-amber-500/10 px-2 py-1.5 text-[11px] text-amber-700"
|
|
391
409
|
>
|
|
392
|
-
{{
|
|
410
|
+
{{ msgs().nodeConfigLayerLocked }}
|
|
393
411
|
</div>
|
|
394
412
|
</div>
|
|
395
413
|
</aside>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { formatViewElementScope } from "../utils/scope-template";
|
|
3
4
|
import ConfigHintIcon from "./ConfigHintIcon.vue";
|
|
4
5
|
import ScopeTemplateUsageHint from "./scope-config/ScopeTemplateUsageHint.vue";
|
|
5
6
|
import { onMounted, ref, watch } from "vue";
|
|
6
7
|
|
|
8
|
+
const { t, locale } = useI18n();
|
|
7
9
|
const SCOPE_COLLAPSE_STORAGE_KEY = "panel:config-scope-collapsed";
|
|
8
10
|
|
|
9
11
|
const props = defineProps<{
|
|
@@ -49,15 +51,15 @@ watch(
|
|
|
49
51
|
<div class="mt-2 border-t border-border/50 pt-2">
|
|
50
52
|
<div class="flex items-center justify-between gap-2">
|
|
51
53
|
<div class="flex min-w-0 items-center gap-1.5">
|
|
52
|
-
<div class="text-[11px] font-medium text-foreground">
|
|
53
|
-
<ConfigHintIcon label="
|
|
54
|
+
<div class="text-[11px] font-medium text-foreground">{{ t("panel.scope.dataTitle") }}</div>
|
|
55
|
+
<ConfigHintIcon :label="t('panel.scope.templateHintLabel')" content-class="max-w-[380px]">
|
|
54
56
|
<ScopeTemplateUsageHint />
|
|
55
57
|
</ConfigHintIcon>
|
|
56
58
|
<span
|
|
57
59
|
v-if="scopeUpdated"
|
|
58
60
|
class="shrink-0 rounded bg-sky-500/15 px-1.5 py-0.5 text-[10px] font-medium text-sky-700 dark:text-sky-300"
|
|
59
61
|
>
|
|
60
|
-
|
|
62
|
+
{{ t("panel.scope.updated") }}
|
|
61
63
|
</span>
|
|
62
64
|
</div>
|
|
63
65
|
<button
|
|
@@ -65,7 +67,7 @@ watch(
|
|
|
65
67
|
class="rounded border border-border px-1.5 py-0.5 text-[11px] text-muted-foreground hover:bg-accent"
|
|
66
68
|
@click="isCollapsed = !isCollapsed"
|
|
67
69
|
>
|
|
68
|
-
{{ isCollapsed ? "
|
|
70
|
+
{{ isCollapsed ? t("panel.scope.expand") : t("panel.scope.collapse") }}
|
|
69
71
|
</button>
|
|
70
72
|
</div>
|
|
71
73
|
<pre
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { computed } from "vue";
|
|
3
4
|
import { Empty } from "ant-design-vue";
|
|
4
5
|
import {
|
|
@@ -11,6 +12,8 @@ import {
|
|
|
11
12
|
import type { PanelElement, PanelLayer, ReferenceCopyMode } from "../types";
|
|
12
13
|
import PanelConfigSidebar from "./PanelConfigSidebar.vue";
|
|
13
14
|
|
|
15
|
+
const { t, locale } = useI18n();
|
|
16
|
+
|
|
14
17
|
export type WorkspaceConfigFocus = "view" | "blueprint" | "blueprint-log";
|
|
15
18
|
|
|
16
19
|
export type PanelConfigSidebarProps = {
|
|
@@ -30,6 +33,7 @@ export type PanelConfigSidebarProps = {
|
|
|
30
33
|
action: "bringForward" | "sendBackward" | "bringToFront" | "sendToBack"
|
|
31
34
|
) => void;
|
|
32
35
|
viewElementScope?: unknown;
|
|
36
|
+
blueprintNodeOptions?: { id: string; label: string }[];
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
export type BlueprintExecutionLogViewProps = {
|
|
@@ -86,6 +90,7 @@ const props = withDefaults(
|
|
|
86
90
|
{
|
|
87
91
|
allowFalseSignalPropagation: false,
|
|
88
92
|
blueprintLibraryOptions: () => [],
|
|
93
|
+
blueprintNodeOptions: () => [],
|
|
89
94
|
selectedElements: () => [],
|
|
90
95
|
}
|
|
91
96
|
);
|
|
@@ -107,6 +112,7 @@ const viewElementOptions = computed(() =>
|
|
|
107
112
|
:layers="layers"
|
|
108
113
|
:update-element="updateElement"
|
|
109
114
|
:view-element-scope="viewElementScope"
|
|
115
|
+
:blueprint-node-options="blueprintNodeOptions"
|
|
110
116
|
:set-reference-copy-mode="setReferenceCopyMode"
|
|
111
117
|
:node-z-order-label="nodeZOrderLabel"
|
|
112
118
|
:on-exclude-selected-node="onExcludeSelectedNode"
|
|
@@ -131,7 +137,7 @@ const viewElementOptions = computed(() =>
|
|
|
131
137
|
v-else-if="!selectedBlueprintNode"
|
|
132
138
|
class="flex h-full flex-col overflow-hidden bg-background text-foreground"
|
|
133
139
|
>
|
|
134
|
-
<Empty class="py-10" description="
|
|
140
|
+
<Empty class="py-10" :description="t('panel.workspace.configEmptyDesc')" />
|
|
135
141
|
</div>
|
|
136
142
|
|
|
137
143
|
<BlueprintNodeConfigSidebar
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { Button, Dropdown, Menu, Modal, Tooltip } from "ant-design-vue";
|
|
3
4
|
import { computed, h, ref } from "vue";
|
|
4
5
|
import type { WorkspaceProjectListItem } from "../library/workspace-project-db";
|
|
5
6
|
|
|
7
|
+
const { t, locale } = useI18n();
|
|
8
|
+
|
|
6
9
|
function formatUpdatedAt(ts: number): string {
|
|
7
10
|
try {
|
|
8
11
|
return new Date(ts).toLocaleString();
|
|
@@ -26,6 +29,14 @@ const emit = defineEmits<{
|
|
|
26
29
|
previewProject: [id: string, options?: { syncFirst?: boolean }];
|
|
27
30
|
}>();
|
|
28
31
|
|
|
32
|
+
const syncTooltip = computed(() => {
|
|
33
|
+
void locale.value;
|
|
34
|
+
if (!props.dirty) return t("panel.workspace.syncUpToDate");
|
|
35
|
+
return t("panel.workspace.syncUpdate", {
|
|
36
|
+
name: props.activeProjectName ?? t("panel.workspace.currentWorkspaceFallback"),
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
29
40
|
const pendingDeleteId = ref<string | null>(null);
|
|
30
41
|
const busy = ref(false);
|
|
31
42
|
|
|
@@ -64,7 +75,7 @@ const projectMenu = computed(() =>
|
|
|
64
75
|
{
|
|
65
76
|
default: () =>
|
|
66
77
|
props.projects.length === 0
|
|
67
|
-
? [h(Menu.Item, { key: "empty", disabled: true }, () => "
|
|
78
|
+
? [h(Menu.Item, { key: "empty", disabled: true }, () => t("panel.workspace.noSavedWorkspaces"))]
|
|
68
79
|
: props.projects.map((project) =>
|
|
69
80
|
h(
|
|
70
81
|
"div",
|
|
@@ -105,7 +116,7 @@ const projectMenu = computed(() =>
|
|
|
105
116
|
})
|
|
106
117
|
),
|
|
107
118
|
},
|
|
108
|
-
() => "
|
|
119
|
+
() => t("panel.workspace.previewDocTitle")
|
|
109
120
|
),
|
|
110
121
|
h(
|
|
111
122
|
Button,
|
|
@@ -118,7 +129,7 @@ const projectMenu = computed(() =>
|
|
|
118
129
|
pendingDeleteId.value = project.id;
|
|
119
130
|
},
|
|
120
131
|
},
|
|
121
|
-
() => "
|
|
132
|
+
() => t("common.delete")
|
|
122
133
|
),
|
|
123
134
|
])
|
|
124
135
|
)
|
|
@@ -131,7 +142,7 @@ const projectMenu = computed(() =>
|
|
|
131
142
|
<template>
|
|
132
143
|
<div class="flex items-center gap-1.5 border-l border-border pl-2">
|
|
133
144
|
<Tooltip
|
|
134
|
-
title="
|
|
145
|
+
:title="t('panel.workspace.createHint')"
|
|
135
146
|
:overlay-style="{ zIndex: 10100 }"
|
|
136
147
|
:mouse-enter-delay="0.15"
|
|
137
148
|
>
|
|
@@ -141,17 +152,13 @@ const projectMenu = computed(() =>
|
|
|
141
152
|
:disabled="busy"
|
|
142
153
|
@click="runAction(() => emit('createProject'))"
|
|
143
154
|
>
|
|
144
|
-
|
|
155
|
+
{{ t("panel.workspace.create") }}
|
|
145
156
|
</Button>
|
|
146
157
|
</Tooltip>
|
|
147
158
|
|
|
148
159
|
<Tooltip
|
|
149
160
|
v-if="activeProjectId"
|
|
150
|
-
:title="
|
|
151
|
-
dirty
|
|
152
|
-
? `同步更新「${activeProjectName ?? '当前工作区'}」到 IndexedDB`
|
|
153
|
-
: '当前工作区已与 IndexedDB 同步'
|
|
154
|
-
"
|
|
161
|
+
:title="syncTooltip"
|
|
155
162
|
:overlay-style="{ zIndex: 10100 }"
|
|
156
163
|
:mouse-enter-delay="0.15"
|
|
157
164
|
>
|
|
@@ -162,13 +169,13 @@ const projectMenu = computed(() =>
|
|
|
162
169
|
:disabled="busy || !dirty"
|
|
163
170
|
@click="runAction(() => emit('syncProject'))"
|
|
164
171
|
>
|
|
165
|
-
|
|
172
|
+
{{ dirty ? t("panel.workspace.syncDirty") : t("common.sync") }}
|
|
166
173
|
</Button>
|
|
167
174
|
</Tooltip>
|
|
168
175
|
|
|
169
176
|
<Dropdown :trigger="['click']" :disabled="busy" :overlay-style="{ zIndex: 10100 }">
|
|
170
177
|
<Button size="small" class="h-7 max-w-[200px] truncate px-2 text-xs">
|
|
171
|
-
{{ activeProjectName ?
|
|
178
|
+
{{ activeProjectName ? t("panel.workspace.workspaceNamed", { name: activeProjectName }) : t("panel.workspace.savedWorkspaces") }}
|
|
172
179
|
</Button>
|
|
173
180
|
<template #overlay>
|
|
174
181
|
<component :is="projectMenu" />
|
|
@@ -178,15 +185,15 @@ const projectMenu = computed(() =>
|
|
|
178
185
|
|
|
179
186
|
<Modal
|
|
180
187
|
v-model:open="deleteModalOpen"
|
|
181
|
-
title="
|
|
182
|
-
ok-text="
|
|
183
|
-
cancel-text="
|
|
188
|
+
:title="t('panel.workspace.deleteConfirmTitle')"
|
|
189
|
+
:ok-text="t('common.delete')"
|
|
190
|
+
:cancel-text="t('common.cancel')"
|
|
184
191
|
ok-type="danger"
|
|
185
192
|
:z-index="10150"
|
|
186
193
|
@ok="confirmDelete"
|
|
187
194
|
>
|
|
188
195
|
<p class="text-sm text-muted-foreground">
|
|
189
|
-
|
|
196
|
+
{{ t("panel.workspace.deleteConfirmBody", { name: pendingDeleteProject?.name ?? "" }) }}
|
|
190
197
|
</p>
|
|
191
198
|
</Modal>
|
|
192
199
|
</template>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import {
|
|
3
4
|
computed,
|
|
4
5
|
onMounted,
|
|
@@ -13,6 +14,8 @@ import {
|
|
|
13
14
|
type BlueprintExecutionOverlay,
|
|
14
15
|
} from "@arronqzy/vue-blueprint";
|
|
15
16
|
|
|
17
|
+
const { t, locale } = useI18n();
|
|
18
|
+
|
|
16
19
|
export type BlueprintLibraryListItem = {
|
|
17
20
|
id: string;
|
|
18
21
|
name: string;
|
|
@@ -200,7 +203,7 @@ watch(blueprintWrapRef, (el) => {
|
|
|
200
203
|
class="min-w-[140px]"
|
|
201
204
|
:value="activeBlueprintLibraryId ?? undefined"
|
|
202
205
|
:options="libraryOptions"
|
|
203
|
-
placeholder="
|
|
206
|
+
:placeholder="t('panel.workspace.blueprintLibrary')"
|
|
204
207
|
@change="(id) => id && onSelectBlueprintLibraryItem?.(String(id))"
|
|
205
208
|
/>
|
|
206
209
|
<span
|
|
@@ -209,17 +212,17 @@ watch(blueprintWrapRef, (el) => {
|
|
|
209
212
|
>
|
|
210
213
|
{{ currentBlueprintLabel }}
|
|
211
214
|
</span>
|
|
212
|
-
<Tooltip title="
|
|
213
|
-
<Button size="small" @click="onSaveBlueprint?.()"
|
|
215
|
+
<Tooltip :title="t('panel.workspace.saveBlueprintToLibrary')">
|
|
216
|
+
<Button size="small" @click="onSaveBlueprint?.()">{{ t("common.save") }}</Button>
|
|
214
217
|
</Tooltip>
|
|
215
|
-
<Tooltip :title="canSyncBlueprint ? '
|
|
218
|
+
<Tooltip :title="canSyncBlueprint ? t('panel.workspace.syncBlueprint') : t('panel.workspace.noSyncChanges')">
|
|
216
219
|
<Button
|
|
217
220
|
size="small"
|
|
218
221
|
type="primary"
|
|
219
222
|
:disabled="!canSyncBlueprint"
|
|
220
223
|
@click="onSyncBlueprint?.()"
|
|
221
224
|
>
|
|
222
|
-
|
|
225
|
+
{{ t("common.sync") }}
|
|
223
226
|
</Button>
|
|
224
227
|
</Tooltip>
|
|
225
228
|
<Select
|
|
@@ -228,7 +231,7 @@ watch(blueprintWrapRef, (el) => {
|
|
|
228
231
|
class="min-w-[120px]"
|
|
229
232
|
:value="blueprintDebug.lifecyclePhase"
|
|
230
233
|
:options="blueprintDebug.lifecycleOptions"
|
|
231
|
-
placeholder="
|
|
234
|
+
:placeholder="t('panel.workspace.lifecycle')"
|
|
232
235
|
@change="(v) => v && blueprintDebug?.onLifecyclePhaseChange?.(String(v))"
|
|
233
236
|
/>
|
|
234
237
|
</Space>
|
|
@@ -250,7 +253,7 @@ watch(blueprintWrapRef, (el) => {
|
|
|
250
253
|
v-else
|
|
251
254
|
class="flex h-full items-center justify-center text-[11px] text-muted-foreground"
|
|
252
255
|
>
|
|
253
|
-
{{ blueprintOpen ? "
|
|
256
|
+
{{ blueprintOpen ? t("panel.workspace.canvasLoading") : "" }}
|
|
254
257
|
</div>
|
|
255
258
|
</div>
|
|
256
259
|
</div>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { computed } from "vue";
|
|
3
4
|
import { Input } from "ant-design-vue";
|
|
4
5
|
|
|
6
|
+
const { t, locale } = useI18n();
|
|
5
7
|
const props = defineProps<{
|
|
6
8
|
label: string;
|
|
7
9
|
value: string;
|
|
@@ -44,7 +46,7 @@ function onColorInput(e: Event) {
|
|
|
44
46
|
class="h-7 w-10 shrink-0 cursor-pointer rounded border border-gray-200 p-0.5"
|
|
45
47
|
:value="pickerValue"
|
|
46
48
|
:disabled="disabled"
|
|
47
|
-
:aria-label="
|
|
49
|
+
:aria-label="t('common.colorPickerAria', { label })"
|
|
48
50
|
@input="onColorInput"
|
|
49
51
|
/>
|
|
50
52
|
</div>
|