@arronqzy/vue-view 0.1.0 → 0.1.2

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.
Files changed (48) hide show
  1. package/package.json +9 -6
  2. package/src/panel/VueViewOnlinePreview.vue +15 -7
  3. package/src/panel/VueViewPanel.vue +81 -55
  4. package/src/panel/components/ConfigHintIcon.vue +4 -2
  5. package/src/panel/components/ElementsLayer.vue +15 -0
  6. package/src/panel/components/MaterialPreview.vue +19 -1
  7. package/src/panel/components/MaterialSidebar.vue +35 -31
  8. package/src/panel/components/MaterialSidebarTreeNode.vue +27 -26
  9. package/src/panel/components/MoveableLayer.vue +7 -4
  10. package/src/panel/components/PanelConfigSidebar.vue +57 -39
  11. package/src/panel/components/ViewElementScopePanel.vue +6 -4
  12. package/src/panel/components/WorkspaceConfigSidebar.vue +7 -1
  13. package/src/panel/components/WorkspaceProjectNav.vue +23 -16
  14. package/src/panel/components/WorkspaceStageSplit.vue +10 -7
  15. package/src/panel/components/config/ConfigColorField.vue +3 -1
  16. package/src/panel/components/config/PanelConfigAudioSection.vue +44 -43
  17. package/src/panel/components/config/PanelConfigChartSection.vue +79 -76
  18. package/src/panel/components/config/PanelConfigGeometrySection.vue +21 -19
  19. package/src/panel/components/config/PanelConfigGridChildSpan.vue +7 -5
  20. package/src/panel/components/config/PanelConfigGridSection.vue +10 -8
  21. package/src/panel/components/config/PanelConfigImageSection.vue +22 -21
  22. package/src/panel/components/config/PanelConfigMultiSelect.vue +42 -40
  23. package/src/panel/components/config/PanelConfigNodeInfo.vue +19 -16
  24. package/src/panel/components/config/PanelConfigReferenceSection.vue +11 -9
  25. package/src/panel/components/config/PanelConfigStyleSections.vue +27 -26
  26. package/src/panel/components/config/PanelConfigTextSection.vue +20 -17
  27. package/src/panel/components/config/PanelConfigVideoSection.vue +15 -14
  28. package/src/panel/components/config/shared.ts +0 -3
  29. package/src/panel/components/elementsLayerNodes.ts +37 -18
  30. package/src/panel/components/materialSidebarData.ts +73 -54
  31. package/src/panel/components/scope-config/ScopeTemplatePreviewHost.vue +7 -2
  32. package/src/panel/components/scope-config/ScopeTemplatePreviewPanel.vue +6 -3
  33. package/src/panel/components/scope-config/ScopeTemplateUsageHint.vue +7 -16
  34. package/src/panel/components/scope-config/ScopeTemplateWarningsPanel.vue +4 -2
  35. package/src/panel/components/table/ConditionEditor.vue +181 -0
  36. package/src/panel/components/table/JsonCodeEditor.vue +148 -0
  37. package/src/panel/components/table/PanelConfigTableSection.vue +2033 -0
  38. package/src/panel/components/table/TableNodeContent.vue +557 -0
  39. package/src/panel/constants/messages.ts +76 -34
  40. package/src/panel/hooks/usePanelElements.ts +54 -41
  41. package/src/panel/hooks/useWorkspaceProjects.ts +8 -5
  42. package/src/panel/types.ts +4 -0
  43. package/src/panel/utils/panelElementDefaults.ts +73 -39
  44. package/src/panel/utils/scope-field-labels.ts +53 -39
  45. package/src/panel/utils/scope-template-spread.ts +57 -24
  46. package/src/panel/utils/scope-template-warnings.ts +23 -22
  47. package/src/panel/utils/scope-template.ts +80 -0
  48. 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
- PANEL_MESSAGES,
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, PANEL_MESSAGES.readAudioFailed);
47
+ const base64 = await readFileAsDataUrl(file, msgs().readAudioFailed);
47
48
  patch({ audioSrc: base64 });
48
- audioStatus.value = PANEL_MESSAGES.audioLocalSaved;
49
+ audioStatus.value = msgs().audioLocalSaved;
49
50
  const url = await uploadFileToRemote(file);
50
51
  if (url) {
51
52
  patch({ audioRemoteUrl: url });
52
- audioStatus.value = PANEL_MESSAGES.audioRemoteUploaded;
53
+ audioStatus.value = msgs().audioRemoteUploaded;
53
54
  } else {
54
- audioStatus.value = PANEL_MESSAGES.audioServerUploadFailed;
55
+ audioStatus.value = msgs().audioServerUploadFailed;
55
56
  }
56
57
  }
57
58
 
58
59
  async function handleUploadAudioPoster(file: File) {
59
- const base64 = await readFileAsDataUrl(file, PANEL_MESSAGES.readImageFailed);
60
+ const base64 = await readFileAsDataUrl(file, msgs().readImageFailed);
60
61
  patch({ audioPosterImage: base64 });
61
- audioStatus.value = PANEL_MESSAGES.audioPosterSet;
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 = PANEL_MESSAGES.audioRecordUnsupported;
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
- PANEL_MESSAGES.readRecordAudioFailed
90
+ msgs().readRecordAudioFailed
90
91
  );
91
92
  patch({ audioSrc: dataUrl });
92
- audioStatus.value = PANEL_MESSAGES.audioRecordSaved;
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 = PANEL_MESSAGES.audioRecording;
101
+ audioStatus.value = msgs().audioRecording;
101
102
  } catch {
102
- audioStatus.value = PANEL_MESSAGES.audioRecordStartFailed;
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>音频 URL</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>编辑时自动暂停媒体</span>
178
+ <span>{{ t("panel.config.autoPauseMedia") }}</span>
178
179
  </label>
179
180
  <label class="block space-y-1">
180
- <div>预设喇叭图标</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__">默认(显示进度条)</Select.Option>
191
- <Select.Option value="speaker">喇叭</Select.Option>
192
- <Select.Option value="music">音符</Select.Option>
193
- <Select.Option value="headphone">耳机</Select.Option>
194
- <Select.Option value="wave">声波</Select.Option>
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>播放动效</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">无动效</Select.Option>
207
- <Select.Option value="pulse">呼吸高亮</Select.Option>
208
- <Select.Option value="ripple">波纹扩散</Select.Option>
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>动效速度</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">慢</Select.Option>
221
- <Select.Option value="normal">中</Select.Option>
222
- <Select.Option value="fast">快</Select.Option>
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">音频占位图</div>
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>