@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,16 +1,17 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { onUnmounted, ref } from "vue";
|
|
3
4
|
import { Checkbox, Input, Select } from "ant-design-vue";
|
|
4
5
|
import type { PanelElement } from "../../types";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
readFileAsDataUrl,
|
|
8
|
-
uploadFileToRemote,
|
|
9
|
-
} from "./shared";
|
|
6
|
+
import { readFileAsDataUrl, uploadFileToRemote } from "./shared";
|
|
7
|
+
import { getPanelMessages } from "../../constants/messages";
|
|
10
8
|
import ConfigFieldGroup from "./ConfigFieldGroup.vue";
|
|
11
9
|
import ConfigSection from "./ConfigSection.vue";
|
|
12
10
|
import ConfigHintIcon from "../ConfigHintIcon.vue";
|
|
13
11
|
|
|
12
|
+
const { t, locale } = useI18n();
|
|
13
|
+
const msgs = () => getPanelMessages(t);
|
|
14
|
+
|
|
14
15
|
const props = defineProps<{
|
|
15
16
|
element: PanelElement;
|
|
16
17
|
isEditable: boolean;
|
|
@@ -43,22 +44,22 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
async function handleUploadAudioFile(file: File) {
|
|
46
|
-
const base64 = await readFileAsDataUrl(file,
|
|
47
|
+
const base64 = await readFileAsDataUrl(file, msgs().readAudioFailed);
|
|
47
48
|
patch({ audioSrc: base64 });
|
|
48
|
-
audioStatus.value =
|
|
49
|
+
audioStatus.value = msgs().audioLocalSaved;
|
|
49
50
|
const url = await uploadFileToRemote(file);
|
|
50
51
|
if (url) {
|
|
51
52
|
patch({ audioRemoteUrl: url });
|
|
52
|
-
audioStatus.value =
|
|
53
|
+
audioStatus.value = msgs().audioRemoteUploaded;
|
|
53
54
|
} else {
|
|
54
|
-
audioStatus.value =
|
|
55
|
+
audioStatus.value = msgs().audioServerUploadFailed;
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
async function handleUploadAudioPoster(file: File) {
|
|
59
|
-
const base64 = await readFileAsDataUrl(file,
|
|
60
|
+
const base64 = await readFileAsDataUrl(file, msgs().readImageFailed);
|
|
60
61
|
patch({ audioPosterImage: base64 });
|
|
61
|
-
audioStatus.value =
|
|
62
|
+
audioStatus.value = msgs().audioPosterSet;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
function stopRecordingAudio() {
|
|
@@ -68,7 +69,7 @@ function stopRecordingAudio() {
|
|
|
68
69
|
async function startRecordingAudio() {
|
|
69
70
|
if (props.element.materialType !== "audio") return;
|
|
70
71
|
if (!navigator.mediaDevices?.getUserMedia) {
|
|
71
|
-
audioStatus.value =
|
|
72
|
+
audioStatus.value = msgs().audioRecordUnsupported;
|
|
72
73
|
return;
|
|
73
74
|
}
|
|
74
75
|
try {
|
|
@@ -86,10 +87,10 @@ async function startRecordingAudio() {
|
|
|
86
87
|
});
|
|
87
88
|
const dataUrl = await readFileAsDataUrl(
|
|
88
89
|
new File([blob], "recording.webm", { type: blob.type }),
|
|
89
|
-
|
|
90
|
+
msgs().readRecordAudioFailed
|
|
90
91
|
);
|
|
91
92
|
patch({ audioSrc: dataUrl });
|
|
92
|
-
audioStatus.value =
|
|
93
|
+
audioStatus.value = msgs().audioRecordSaved;
|
|
93
94
|
recordStreamRef.value?.getTracks().forEach((track) => track.stop());
|
|
94
95
|
recordStreamRef.value = null;
|
|
95
96
|
recorderRef.value = null;
|
|
@@ -97,9 +98,9 @@ async function startRecordingAudio() {
|
|
|
97
98
|
};
|
|
98
99
|
recorder.start();
|
|
99
100
|
isRecordingAudio.value = true;
|
|
100
|
-
audioStatus.value =
|
|
101
|
+
audioStatus.value = msgs().audioRecording;
|
|
101
102
|
} catch {
|
|
102
|
-
audioStatus.value =
|
|
103
|
+
audioStatus.value = msgs().audioRecordStartFailed;
|
|
103
104
|
isRecordingAudio.value = false;
|
|
104
105
|
}
|
|
105
106
|
}
|
|
@@ -119,14 +120,14 @@ function onPosterFileChange(e: Event) {
|
|
|
119
120
|
|
|
120
121
|
<template>
|
|
121
122
|
<ConfigSection
|
|
122
|
-
title="
|
|
123
|
+
:title="t('panel.config.sectionAudio')"
|
|
123
124
|
:open="open"
|
|
124
125
|
:force-open="forceOpen"
|
|
125
126
|
@update:open="emit('update:open', $event)"
|
|
126
127
|
>
|
|
127
|
-
<ConfigFieldGroup title="
|
|
128
|
+
<ConfigFieldGroup :title="t('panel.config.groupAudioSource')">
|
|
128
129
|
<label class="block space-y-1">
|
|
129
|
-
<div
|
|
130
|
+
<div>{{ t("panel.config.audioUrl") }}</div>
|
|
130
131
|
<Input
|
|
131
132
|
size="small"
|
|
132
133
|
:value="element.audioRemoteUrl ?? ''"
|
|
@@ -143,7 +144,7 @@ function onPosterFileChange(e: Event) {
|
|
|
143
144
|
class="inline-flex cursor-pointer items-center rounded border border-gray-200 px-2 py-1 text-[11px] hover:bg-gray-50"
|
|
144
145
|
:class="{ 'pointer-events-none opacity-50': !isEditable }"
|
|
145
146
|
>
|
|
146
|
-
|
|
147
|
+
{{ t("panel.config.uploadAudio") }}
|
|
147
148
|
<input type="file" accept="audio/*" class="hidden" :disabled="!isEditable" @change="onAudioFileChange" />
|
|
148
149
|
</label>
|
|
149
150
|
<button
|
|
@@ -152,7 +153,7 @@ function onPosterFileChange(e: Event) {
|
|
|
152
153
|
:disabled="!isEditable"
|
|
153
154
|
@click="isRecordingAudio ? stopRecordingAudio() : startRecordingAudio()"
|
|
154
155
|
>
|
|
155
|
-
{{ isRecordingAudio ? "
|
|
156
|
+
{{ isRecordingAudio ? t("panel.config.stopRecord") : t("panel.config.startRecord") }}
|
|
156
157
|
</button>
|
|
157
158
|
</div>
|
|
158
159
|
<div
|
|
@@ -167,17 +168,17 @@ function onPosterFileChange(e: Event) {
|
|
|
167
168
|
:src="element.audioSrc || element.audioRemoteUrl || ''"
|
|
168
169
|
/>
|
|
169
170
|
</ConfigFieldGroup>
|
|
170
|
-
<ConfigFieldGroup title="
|
|
171
|
+
<ConfigFieldGroup :title="t('panel.config.groupDisplayStyle')">
|
|
171
172
|
<label class="flex items-center gap-2">
|
|
172
173
|
<Checkbox
|
|
173
174
|
:checked="element.mediaAutoPauseOnEdit !== false"
|
|
174
175
|
:disabled="!isEditable"
|
|
175
176
|
@update:checked="(v) => patch({ mediaAutoPauseOnEdit: v !== false })"
|
|
176
177
|
/>
|
|
177
|
-
<span
|
|
178
|
+
<span>{{ t("panel.config.autoPauseMedia") }}</span>
|
|
178
179
|
</label>
|
|
179
180
|
<label class="block space-y-1">
|
|
180
|
-
<div
|
|
181
|
+
<div>{{ t("panel.config.audioIconPreset") }}</div>
|
|
181
182
|
<Select
|
|
182
183
|
size="small"
|
|
183
184
|
class="w-full"
|
|
@@ -187,15 +188,15 @@ function onPosterFileChange(e: Event) {
|
|
|
187
188
|
audioIconPreset: v === '__none__' ? undefined : v as PanelElement['audioIconPreset'],
|
|
188
189
|
})"
|
|
189
190
|
>
|
|
190
|
-
<Select.Option value="__none__"
|
|
191
|
-
<Select.Option value="speaker"
|
|
192
|
-
<Select.Option value="music"
|
|
193
|
-
<Select.Option value="headphone"
|
|
194
|
-
<Select.Option value="wave"
|
|
191
|
+
<Select.Option value="__none__">{{ t("panel.config.iconDefaultProgress") }}</Select.Option>
|
|
192
|
+
<Select.Option value="speaker">{{ t("panel.config.iconSpeaker") }}</Select.Option>
|
|
193
|
+
<Select.Option value="music">{{ t("panel.config.iconMusic") }}</Select.Option>
|
|
194
|
+
<Select.Option value="headphone">{{ t("panel.config.iconHeadphone") }}</Select.Option>
|
|
195
|
+
<Select.Option value="wave">{{ t("panel.config.iconWave") }}</Select.Option>
|
|
195
196
|
</Select>
|
|
196
197
|
</label>
|
|
197
198
|
<label class="block space-y-1">
|
|
198
|
-
<div
|
|
199
|
+
<div>{{ t("panel.config.visualEffect") }}</div>
|
|
199
200
|
<Select
|
|
200
201
|
size="small"
|
|
201
202
|
class="w-full"
|
|
@@ -203,13 +204,13 @@ function onPosterFileChange(e: Event) {
|
|
|
203
204
|
:disabled="!isEditable"
|
|
204
205
|
@update:value="(v) => patch({ audioVisualEffect: v as PanelElement['audioVisualEffect'] })"
|
|
205
206
|
>
|
|
206
|
-
<Select.Option value="none"
|
|
207
|
-
<Select.Option value="pulse"
|
|
208
|
-
<Select.Option value="ripple"
|
|
207
|
+
<Select.Option value="none">{{ t("panel.config.effectNone") }}</Select.Option>
|
|
208
|
+
<Select.Option value="pulse">{{ t("panel.config.effectPulse") }}</Select.Option>
|
|
209
|
+
<Select.Option value="ripple">{{ t("panel.config.effectRipple") }}</Select.Option>
|
|
209
210
|
</Select>
|
|
210
211
|
</label>
|
|
211
212
|
<label class="block space-y-1">
|
|
212
|
-
<div
|
|
213
|
+
<div>{{ t("panel.config.effectSpeed") }}</div>
|
|
213
214
|
<Select
|
|
214
215
|
size="small"
|
|
215
216
|
class="w-full"
|
|
@@ -217,9 +218,9 @@ function onPosterFileChange(e: Event) {
|
|
|
217
218
|
:disabled="!isEditable"
|
|
218
219
|
@update:value="(v) => patch({ audioVisualSpeed: v as PanelElement['audioVisualSpeed'] })"
|
|
219
220
|
>
|
|
220
|
-
<Select.Option value="slow"
|
|
221
|
-
<Select.Option value="normal"
|
|
222
|
-
<Select.Option value="fast"
|
|
221
|
+
<Select.Option value="slow">{{ t("panel.config.speedSlow") }}</Select.Option>
|
|
222
|
+
<Select.Option value="normal">{{ t("panel.config.speedNormal") }}</Select.Option>
|
|
223
|
+
<Select.Option value="fast">{{ t("panel.config.speedFast") }}</Select.Option>
|
|
223
224
|
</Select>
|
|
224
225
|
</label>
|
|
225
226
|
<div class="flex items-center gap-2">
|
|
@@ -227,7 +228,7 @@ function onPosterFileChange(e: Event) {
|
|
|
227
228
|
class="inline-flex cursor-pointer items-center rounded border border-gray-200 px-2 py-1 text-[11px] hover:bg-gray-50"
|
|
228
229
|
:class="{ 'pointer-events-none opacity-50': !isEditable }"
|
|
229
230
|
>
|
|
230
|
-
|
|
231
|
+
{{ t("panel.config.uploadPoster") }}
|
|
231
232
|
<input type="file" accept="image/*" class="hidden" :disabled="!isEditable" @change="onPosterFileChange" />
|
|
232
233
|
</label>
|
|
233
234
|
<button
|
|
@@ -236,19 +237,19 @@ function onPosterFileChange(e: Event) {
|
|
|
236
237
|
:disabled="!isEditable"
|
|
237
238
|
@click="patch({ audioPosterImage: undefined })"
|
|
238
239
|
>
|
|
239
|
-
|
|
240
|
+
{{ t("panel.config.clearPoster") }}
|
|
240
241
|
</button>
|
|
241
242
|
</div>
|
|
242
243
|
<img
|
|
243
244
|
v-if="element.audioPosterImage"
|
|
244
245
|
:src="element.audioPosterImage"
|
|
245
|
-
alt="
|
|
246
|
+
:alt="t('panel.config.audioPosterAlt')"
|
|
246
247
|
class="h-20 w-full rounded border border-gray-200/60 object-cover"
|
|
247
248
|
/>
|
|
248
249
|
<div class="flex items-center gap-1">
|
|
249
|
-
<div class="text-[11px] text-gray-500"
|
|
250
|
-
<ConfigHintIcon label="
|
|
251
|
-
|
|
250
|
+
<div class="text-[11px] text-gray-500">{{ t("panel.config.audioPoster") }}</div>
|
|
251
|
+
<ConfigHintIcon :label="t('panel.config.audioPoster')">
|
|
252
|
+
{{ t("panel.config.audioPosterHint") }}
|
|
252
253
|
</ConfigHintIcon>
|
|
253
254
|
</div>
|
|
254
255
|
</ConfigFieldGroup>
|