@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,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { Checkbox, Input, InputNumber, Select } from "ant-design-vue";
|
|
3
4
|
import type { PanelElement, PanelLayer } from "../../types";
|
|
4
|
-
import {
|
|
5
|
+
import { getPanelMessages } from "../../constants/messages";
|
|
5
6
|
import ConfigSection from "./ConfigSection.vue";
|
|
6
7
|
|
|
8
|
+
const { t, locale } = useI18n();
|
|
9
|
+
const msgs = () => getPanelMessages(t);
|
|
7
10
|
const props = defineProps<{
|
|
8
11
|
element: PanelElement;
|
|
9
12
|
layers: PanelLayer[];
|
|
@@ -34,18 +37,18 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
34
37
|
|
|
35
38
|
<template>
|
|
36
39
|
<ConfigSection
|
|
37
|
-
title="
|
|
40
|
+
:title="t('panel.config.sectionNodeInfo')"
|
|
38
41
|
:open="open"
|
|
39
42
|
:force-open="forceOpen"
|
|
40
43
|
@update:open="emit('update:open', $event)"
|
|
41
44
|
>
|
|
42
45
|
<label class="block space-y-1">
|
|
43
|
-
<div
|
|
46
|
+
<div>{{ t("panel.config.nodeName") }}</div>
|
|
44
47
|
<Input
|
|
45
48
|
size="small"
|
|
46
49
|
:value="element.name ?? ''"
|
|
47
50
|
:disabled="!isEditable"
|
|
48
|
-
placeholder="
|
|
51
|
+
:placeholder="t('panel.config.nodeNamePlaceholder')"
|
|
49
52
|
@update:value="(v: string) => patch({ name: v || undefined })"
|
|
50
53
|
/>
|
|
51
54
|
</label>
|
|
@@ -73,7 +76,7 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
73
76
|
</div>
|
|
74
77
|
<div class="grid grid-cols-3 gap-2">
|
|
75
78
|
<label class="block space-y-1">
|
|
76
|
-
<div
|
|
79
|
+
<div>{{ t("panel.config.rotate") }}</div>
|
|
77
80
|
<InputNumber
|
|
78
81
|
size="small"
|
|
79
82
|
class="w-full"
|
|
@@ -83,7 +86,7 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
83
86
|
/>
|
|
84
87
|
</label>
|
|
85
88
|
<label class="block space-y-1">
|
|
86
|
-
<div
|
|
89
|
+
<div>{{ t("panel.config.width") }}</div>
|
|
87
90
|
<InputNumber
|
|
88
91
|
size="small"
|
|
89
92
|
class="w-full"
|
|
@@ -94,7 +97,7 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
94
97
|
/>
|
|
95
98
|
</label>
|
|
96
99
|
<label class="block space-y-1">
|
|
97
|
-
<div
|
|
100
|
+
<div>{{ t("panel.config.height") }}</div>
|
|
98
101
|
<InputNumber
|
|
99
102
|
size="small"
|
|
100
103
|
class="w-full"
|
|
@@ -106,8 +109,8 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
106
109
|
</label>
|
|
107
110
|
</div>
|
|
108
111
|
<div class="space-y-1.5">
|
|
109
|
-
<div class="text-[11px] text-gray-500"
|
|
110
|
-
<div class="text-[11px] text-gray-400"
|
|
112
|
+
<div class="text-[11px] text-gray-500">{{ t("panel.config.nodeZOrder") }}</div>
|
|
113
|
+
<div class="text-[11px] text-gray-400">{{ t("panel.config.currentZIndex", { value: nodeZOrderLabel ?? "-" }) }}</div>
|
|
111
114
|
<div v-if="onAdjustNodeZOrder" class="grid grid-cols-2 gap-2">
|
|
112
115
|
<button
|
|
113
116
|
type="button"
|
|
@@ -115,7 +118,7 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
115
118
|
:disabled="!isEditable"
|
|
116
119
|
@click="onAdjustNodeZOrder(element.id, 'bringForward')"
|
|
117
120
|
>
|
|
118
|
-
|
|
121
|
+
{{ t("panel.menubar.bringForward") }}
|
|
119
122
|
</button>
|
|
120
123
|
<button
|
|
121
124
|
type="button"
|
|
@@ -123,7 +126,7 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
123
126
|
:disabled="!isEditable"
|
|
124
127
|
@click="onAdjustNodeZOrder(element.id, 'sendBackward')"
|
|
125
128
|
>
|
|
126
|
-
|
|
129
|
+
{{ t("panel.menubar.sendBackward") }}
|
|
127
130
|
</button>
|
|
128
131
|
<button
|
|
129
132
|
type="button"
|
|
@@ -131,7 +134,7 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
131
134
|
:disabled="!isEditable"
|
|
132
135
|
@click="onAdjustNodeZOrder(element.id, 'bringToFront')"
|
|
133
136
|
>
|
|
134
|
-
|
|
137
|
+
{{ t("panel.menubar.bringToFront") }}
|
|
135
138
|
</button>
|
|
136
139
|
<button
|
|
137
140
|
type="button"
|
|
@@ -139,12 +142,12 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
139
142
|
:disabled="!isEditable"
|
|
140
143
|
@click="onAdjustNodeZOrder(element.id, 'sendToBack')"
|
|
141
144
|
>
|
|
142
|
-
|
|
145
|
+
{{ t("panel.menubar.sendToBack") }}
|
|
143
146
|
</button>
|
|
144
147
|
</div>
|
|
145
148
|
</div>
|
|
146
149
|
<div class="truncate text-gray-400">ID: {{ element.id }}</div>
|
|
147
|
-
<div class="text-gray-400"
|
|
150
|
+
<div class="text-gray-400">{{ t("panel.config.type") }}: {{ element.materialType ?? element.id }}</div>
|
|
148
151
|
<div class="rounded-lg border border-gray-200/60 bg-white/80 px-2.5 py-2">
|
|
149
152
|
<label class="flex items-center gap-2">
|
|
150
153
|
<Checkbox
|
|
@@ -152,14 +155,14 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
152
155
|
:disabled="!canToggleNodeLock"
|
|
153
156
|
@update:checked="(v) => patch({ locked: v === true })"
|
|
154
157
|
/>
|
|
155
|
-
<span
|
|
158
|
+
<span>{{ t("panel.config.lockNode") }}</span>
|
|
156
159
|
</label>
|
|
157
160
|
</div>
|
|
158
161
|
<div
|
|
159
162
|
v-if="!isEditable"
|
|
160
163
|
class="rounded border border-amber-500/40 bg-amber-500/10 px-2 py-1.5 text-[11px] text-amber-700"
|
|
161
164
|
>
|
|
162
|
-
{{ element.locked ?
|
|
165
|
+
{{ element.locked ? msgs().nodeConfigLocked : msgs().nodeConfigLayerLocked }}
|
|
163
166
|
</div>
|
|
164
167
|
</ConfigSection>
|
|
165
168
|
</template>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { Select } from "ant-design-vue";
|
|
3
4
|
import type { PanelElement, PanelLayer, ReferenceCopyMode } from "../../types";
|
|
4
5
|
import ConfigFieldGroup from "./ConfigFieldGroup.vue";
|
|
5
6
|
import ConfigSection from "./ConfigSection.vue";
|
|
6
7
|
|
|
8
|
+
const { t, locale } = useI18n();
|
|
7
9
|
const props = defineProps<{
|
|
8
10
|
element: PanelElement;
|
|
9
11
|
layers: PanelLayer[];
|
|
@@ -29,14 +31,14 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
29
31
|
|
|
30
32
|
<template>
|
|
31
33
|
<ConfigSection
|
|
32
|
-
title="
|
|
34
|
+
:title="t('panel.config.sectionReference')"
|
|
33
35
|
:open="open"
|
|
34
36
|
:force-open="forceOpen"
|
|
35
37
|
@update:open="emit('update:open', $event)"
|
|
36
38
|
>
|
|
37
|
-
<ConfigFieldGroup title="
|
|
39
|
+
<ConfigFieldGroup :title="t('panel.config.groupRefSource')">
|
|
38
40
|
<label class="block space-y-1">
|
|
39
|
-
<div
|
|
41
|
+
<div>{{ t("panel.config.refLayer") }}</div>
|
|
40
42
|
<Select
|
|
41
43
|
size="small"
|
|
42
44
|
class="w-full"
|
|
@@ -44,7 +46,7 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
44
46
|
:disabled="!isEditable"
|
|
45
47
|
@update:value="(v) => patch({ refLayerId: v === '__none__' ? undefined : String(v) })"
|
|
46
48
|
>
|
|
47
|
-
<Select.Option value="__none__"
|
|
49
|
+
<Select.Option value="__none__">{{ t("panel.config.noneNoRef") }}</Select.Option>
|
|
48
50
|
<Select.Option
|
|
49
51
|
v-for="layer in layers.filter((l) => l.id !== element.layerId)"
|
|
50
52
|
:key="layer.id"
|
|
@@ -55,12 +57,12 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
55
57
|
</Select>
|
|
56
58
|
</label>
|
|
57
59
|
</ConfigFieldGroup>
|
|
58
|
-
<ConfigFieldGroup title="
|
|
60
|
+
<ConfigFieldGroup :title="t('panel.config.groupCopyStrategy')">
|
|
59
61
|
<template #hint>
|
|
60
|
-
|
|
62
|
+
{{ t("panel.config.copyStrategyHint") }}
|
|
61
63
|
</template>
|
|
62
64
|
<label class="block space-y-1">
|
|
63
|
-
<div
|
|
65
|
+
<div>{{ t("panel.config.copyMode") }}</div>
|
|
64
66
|
<Select
|
|
65
67
|
size="small"
|
|
66
68
|
class="w-full"
|
|
@@ -68,8 +70,8 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
68
70
|
:disabled="!isEditable"
|
|
69
71
|
@update:value="(v) => setReferenceCopyMode?.(element.id, v as ReferenceCopyMode)"
|
|
70
72
|
>
|
|
71
|
-
<Select.Option value="shallow"
|
|
72
|
-
<Select.Option value="deep"
|
|
73
|
+
<Select.Option value="shallow">{{ t("panel.config.shallowFollow") }}</Select.Option>
|
|
74
|
+
<Select.Option value="deep">{{ t("panel.config.deepFreeze") }}</Select.Option>
|
|
73
75
|
</Select>
|
|
74
76
|
</label>
|
|
75
77
|
</ConfigFieldGroup>
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { ref } from "vue";
|
|
3
4
|
import { Input, Select } from "ant-design-vue";
|
|
4
5
|
import type { PanelElement, PanelElementStyle } 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 ConfigColorField from "./ConfigColorField.vue";
|
|
11
9
|
import ConfigFieldGroup from "./ConfigFieldGroup.vue";
|
|
12
10
|
import ConfigSection from "./ConfigSection.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;
|
|
@@ -40,15 +41,15 @@ function patchStyle(patch: Partial<PanelElementStyle>) {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
async function handleUploadBackgroundImage(file: File) {
|
|
43
|
-
const base64 = await readFileAsDataUrl(file,
|
|
44
|
+
const base64 = await readFileAsDataUrl(file, msgs().readImageFailed);
|
|
44
45
|
patchStyle({ backgroundImage: `url("${base64}")` });
|
|
45
|
-
uploadStatus.value = "
|
|
46
|
+
uploadStatus.value = t("panel.config.uploadWrittenBase64");
|
|
46
47
|
const url = await uploadFileToRemote(file);
|
|
47
48
|
if (url) {
|
|
48
49
|
patchStyle({ backgroundImageRemoteUrl: url });
|
|
49
|
-
uploadStatus.value = "
|
|
50
|
+
uploadStatus.value = t("panel.config.uploadServerAndBase64");
|
|
50
51
|
} else {
|
|
51
|
-
uploadStatus.value = "
|
|
52
|
+
uploadStatus.value = t("panel.config.uploadServerFailedKeepBase64");
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -63,20 +64,20 @@ function onFileChange(e: Event) {
|
|
|
63
64
|
<template>
|
|
64
65
|
<ConfigSection
|
|
65
66
|
v-if="showBackground !== false"
|
|
66
|
-
title="
|
|
67
|
+
:title="t('panel.config.sectionStyleBackground')"
|
|
67
68
|
:open="backgroundOpen"
|
|
68
69
|
:force-open="forceOpen"
|
|
69
70
|
@update:open="emit('update:backgroundOpen', $event)"
|
|
70
71
|
>
|
|
71
|
-
<ConfigFieldGroup title="
|
|
72
|
+
<ConfigFieldGroup :title="t('panel.config.groupBgFill')">
|
|
72
73
|
<ConfigColorField
|
|
73
|
-
label="
|
|
74
|
+
:label="t('panel.scope.fieldStyleBackgroundColor')"
|
|
74
75
|
:value="element.style?.backgroundColor ?? ''"
|
|
75
76
|
:disabled="!isEditable"
|
|
76
77
|
@update:value="(v) => patchStyle({ backgroundColor: v || undefined })"
|
|
77
78
|
/>
|
|
78
79
|
<label class="block space-y-1">
|
|
79
|
-
<div
|
|
80
|
+
<div>{{ t("panel.scope.fieldStyleBackgroundImage") }}</div>
|
|
80
81
|
<Input
|
|
81
82
|
size="small"
|
|
82
83
|
:value="element.style?.backgroundImage ?? ''"
|
|
@@ -90,7 +91,7 @@ function onFileChange(e: Event) {
|
|
|
90
91
|
class="inline-flex cursor-pointer items-center rounded border border-gray-200 px-2 py-1 text-[11px] hover:bg-gray-50"
|
|
91
92
|
:class="{ 'pointer-events-none opacity-50': !isEditable }"
|
|
92
93
|
>
|
|
93
|
-
|
|
94
|
+
{{ t("panel.config.uploadImage") }}
|
|
94
95
|
<input
|
|
95
96
|
type="file"
|
|
96
97
|
accept="image/*"
|
|
@@ -102,10 +103,10 @@ function onFileChange(e: Event) {
|
|
|
102
103
|
<span v-if="uploadStatus" class="text-[11px] text-gray-500">{{ uploadStatus }}</span>
|
|
103
104
|
</div>
|
|
104
105
|
</ConfigFieldGroup>
|
|
105
|
-
<ConfigFieldGroup title="
|
|
106
|
+
<ConfigFieldGroup :title="t('panel.config.groupBgLayout')">
|
|
106
107
|
<div class="grid grid-cols-2 gap-2">
|
|
107
108
|
<label class="block space-y-1">
|
|
108
|
-
<div
|
|
109
|
+
<div>{{ t("panel.scope.fieldStyleBackgroundSize") }}</div>
|
|
109
110
|
<Select
|
|
110
111
|
size="small"
|
|
111
112
|
class="w-full"
|
|
@@ -113,7 +114,7 @@ function onFileChange(e: Event) {
|
|
|
113
114
|
:disabled="!isEditable"
|
|
114
115
|
@update:value="(v) => patchStyle({ backgroundSize: v === '__none__' ? undefined : String(v) })"
|
|
115
116
|
>
|
|
116
|
-
<Select.Option value="__none__"
|
|
117
|
+
<Select.Option value="__none__">{{ t("common.default") }}</Select.Option>
|
|
117
118
|
<Select.Option value="cover">cover</Select.Option>
|
|
118
119
|
<Select.Option value="contain">contain</Select.Option>
|
|
119
120
|
<Select.Option value="100% 100%">100% 100%</Select.Option>
|
|
@@ -121,7 +122,7 @@ function onFileChange(e: Event) {
|
|
|
121
122
|
</Select>
|
|
122
123
|
</label>
|
|
123
124
|
<label class="block space-y-1">
|
|
124
|
-
<div
|
|
125
|
+
<div>{{ t("panel.scope.fieldStyleBackgroundPosition") }}</div>
|
|
125
126
|
<Select
|
|
126
127
|
size="small"
|
|
127
128
|
class="w-full"
|
|
@@ -129,7 +130,7 @@ function onFileChange(e: Event) {
|
|
|
129
130
|
:disabled="!isEditable"
|
|
130
131
|
@update:value="(v) => patchStyle({ backgroundPosition: v === '__none__' ? undefined : String(v) })"
|
|
131
132
|
>
|
|
132
|
-
<Select.Option value="__none__"
|
|
133
|
+
<Select.Option value="__none__">{{ t("common.default") }}</Select.Option>
|
|
133
134
|
<Select.Option value="center">center</Select.Option>
|
|
134
135
|
<Select.Option value="top">top</Select.Option>
|
|
135
136
|
<Select.Option value="bottom">bottom</Select.Option>
|
|
@@ -147,15 +148,15 @@ function onFileChange(e: Event) {
|
|
|
147
148
|
|
|
148
149
|
<ConfigSection
|
|
149
150
|
v-if="showBorder !== false"
|
|
150
|
-
title="
|
|
151
|
+
:title="t('panel.config.sectionStyleBorder')"
|
|
151
152
|
:open="borderOpen"
|
|
152
153
|
:force-open="forceOpen"
|
|
153
154
|
@update:open="emit('update:borderOpen', $event)"
|
|
154
155
|
>
|
|
155
|
-
<ConfigFieldGroup title="
|
|
156
|
+
<ConfigFieldGroup :title="t('panel.config.groupBorderGeometry')">
|
|
156
157
|
<div class="grid grid-cols-2 gap-2">
|
|
157
158
|
<label class="block space-y-1">
|
|
158
|
-
<div
|
|
159
|
+
<div>{{ t("panel.config.borderWidth") }}</div>
|
|
159
160
|
<Input
|
|
160
161
|
size="small"
|
|
161
162
|
type="number"
|
|
@@ -166,7 +167,7 @@ function onFileChange(e: Event) {
|
|
|
166
167
|
/>
|
|
167
168
|
</label>
|
|
168
169
|
<label class="block space-y-1">
|
|
169
|
-
<div
|
|
170
|
+
<div>{{ t("panel.config.borderRadius") }}</div>
|
|
170
171
|
<Input
|
|
171
172
|
size="small"
|
|
172
173
|
type="number"
|
|
@@ -178,10 +179,10 @@ function onFileChange(e: Event) {
|
|
|
178
179
|
</label>
|
|
179
180
|
</div>
|
|
180
181
|
</ConfigFieldGroup>
|
|
181
|
-
<ConfigFieldGroup title="
|
|
182
|
+
<ConfigFieldGroup :title="t('panel.config.groupBorderVisual')">
|
|
182
183
|
<div class="grid grid-cols-2 gap-2">
|
|
183
184
|
<label class="block space-y-1">
|
|
184
|
-
<div
|
|
185
|
+
<div>{{ t("panel.config.borderStyle") }}</div>
|
|
185
186
|
<Select
|
|
186
187
|
size="small"
|
|
187
188
|
class="w-full"
|
|
@@ -197,7 +198,7 @@ function onFileChange(e: Event) {
|
|
|
197
198
|
</Select>
|
|
198
199
|
</label>
|
|
199
200
|
<ConfigColorField
|
|
200
|
-
label="
|
|
201
|
+
:label="t('panel.scope.fieldStyleBorderColor')"
|
|
201
202
|
:value="element.style?.borderColor ?? ''"
|
|
202
203
|
:disabled="!isEditable"
|
|
203
204
|
@update:value="(v) => patchStyle({ borderColor: v || undefined })"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { onMounted, ref, watch } from "vue";
|
|
3
4
|
import { Checkbox, Input, InputNumber, Select } from "ant-design-vue";
|
|
4
5
|
import type { PanelElement } from "../../types";
|
|
@@ -6,6 +7,7 @@ import ConfigColorField from "./ConfigColorField.vue";
|
|
|
6
7
|
import ConfigFieldGroup from "./ConfigFieldGroup.vue";
|
|
7
8
|
import ConfigSection from "./ConfigSection.vue";
|
|
8
9
|
|
|
10
|
+
const { t, locale } = useI18n();
|
|
9
11
|
const props = defineProps<{
|
|
10
12
|
element: PanelElement;
|
|
11
13
|
isEditable: boolean;
|
|
@@ -38,7 +40,8 @@ function execTextCommand(cmd: "bold" | "italic" | "underline") {
|
|
|
38
40
|
|
|
39
41
|
function syncEditorHtml() {
|
|
40
42
|
if (props.element.materialType !== "text") return;
|
|
41
|
-
const nextHtml =
|
|
43
|
+
const nextHtml =
|
|
44
|
+
props.element.textHtml ?? `<p>${t("panel.defaults.doubleClickTextHtml")}</p>`;
|
|
42
45
|
if (textEditorRef.value && textEditorRef.value.innerHTML !== nextHtml) {
|
|
43
46
|
textEditorRef.value.innerHTML = nextHtml;
|
|
44
47
|
}
|
|
@@ -50,12 +53,12 @@ watch(() => [props.element.id, props.element.textHtml], syncEditorHtml);
|
|
|
50
53
|
|
|
51
54
|
<template>
|
|
52
55
|
<ConfigSection
|
|
53
|
-
title="
|
|
56
|
+
:title="t('panel.config.sectionText')"
|
|
54
57
|
:open="open"
|
|
55
58
|
:force-open="forceOpen"
|
|
56
59
|
@update:open="emit('update:open', $event)"
|
|
57
60
|
>
|
|
58
|
-
<ConfigFieldGroup title="
|
|
61
|
+
<ConfigFieldGroup :title="t('panel.config.groupTextContent')">
|
|
59
62
|
<div class="flex items-center gap-1">
|
|
60
63
|
<button
|
|
61
64
|
type="button"
|
|
@@ -100,20 +103,20 @@ watch(() => [props.element.id, props.element.textHtml], syncEditorHtml);
|
|
|
100
103
|
}"
|
|
101
104
|
/>
|
|
102
105
|
</ConfigFieldGroup>
|
|
103
|
-
<ConfigFieldGroup title="
|
|
106
|
+
<ConfigFieldGroup :title="t('panel.config.groupTextStyle')">
|
|
104
107
|
<label class="block space-y-1">
|
|
105
|
-
<div
|
|
108
|
+
<div>{{ t("panel.config.fontFamily") }}</div>
|
|
106
109
|
<Input
|
|
107
110
|
size="small"
|
|
108
111
|
:value="element.textFontFamily ?? ''"
|
|
109
112
|
:disabled="!isEditable"
|
|
110
|
-
placeholder="
|
|
113
|
+
:placeholder="t('panel.config.fontFamilyPlaceholder')"
|
|
111
114
|
@update:value="(v: string) => patch({ textFontFamily: v || undefined })"
|
|
112
115
|
/>
|
|
113
116
|
</label>
|
|
114
117
|
<div class="grid grid-cols-2 gap-2">
|
|
115
118
|
<label class="block space-y-1">
|
|
116
|
-
<div
|
|
119
|
+
<div>{{ t("panel.config.fontSizePx") }}</div>
|
|
117
120
|
<InputNumber
|
|
118
121
|
size="small"
|
|
119
122
|
class="w-full"
|
|
@@ -125,7 +128,7 @@ watch(() => [props.element.id, props.element.textHtml], syncEditorHtml);
|
|
|
125
128
|
/>
|
|
126
129
|
</label>
|
|
127
130
|
<label class="block space-y-1">
|
|
128
|
-
<div
|
|
131
|
+
<div>{{ t("panel.config.fontWeight") }}</div>
|
|
129
132
|
<Select
|
|
130
133
|
size="small"
|
|
131
134
|
class="w-full"
|
|
@@ -143,7 +146,7 @@ watch(() => [props.element.id, props.element.textHtml], syncEditorHtml);
|
|
|
143
146
|
</div>
|
|
144
147
|
<div class="grid grid-cols-2 gap-2">
|
|
145
148
|
<label class="block space-y-1">
|
|
146
|
-
<div
|
|
149
|
+
<div>{{ t("panel.config.textAlign") }}</div>
|
|
147
150
|
<Select
|
|
148
151
|
size="small"
|
|
149
152
|
class="w-full"
|
|
@@ -151,14 +154,14 @@ watch(() => [props.element.id, props.element.textHtml], syncEditorHtml);
|
|
|
151
154
|
:disabled="!isEditable"
|
|
152
155
|
@update:value="(v) => patch({ textAlign: v as PanelElement['textAlign'] })"
|
|
153
156
|
>
|
|
154
|
-
<Select.Option value="left"
|
|
155
|
-
<Select.Option value="center"
|
|
156
|
-
<Select.Option value="right"
|
|
157
|
-
<Select.Option value="justify"
|
|
157
|
+
<Select.Option value="left">{{ t("panel.config.alignLeft") }}</Select.Option>
|
|
158
|
+
<Select.Option value="center">{{ t("panel.config.alignCenter") }}</Select.Option>
|
|
159
|
+
<Select.Option value="right">{{ t("panel.config.alignRight") }}</Select.Option>
|
|
160
|
+
<Select.Option value="justify">{{ t("panel.config.alignJustify") }}</Select.Option>
|
|
158
161
|
</Select>
|
|
159
162
|
</label>
|
|
160
163
|
<label class="block space-y-1">
|
|
161
|
-
<div
|
|
164
|
+
<div>{{ t("panel.config.lineHeight") }}</div>
|
|
162
165
|
<InputNumber
|
|
163
166
|
size="small"
|
|
164
167
|
class="w-full"
|
|
@@ -175,20 +178,20 @@ watch(() => [props.element.id, props.element.textHtml], syncEditorHtml);
|
|
|
175
178
|
</label>
|
|
176
179
|
</div>
|
|
177
180
|
<ConfigColorField
|
|
178
|
-
label="
|
|
181
|
+
:label="t('panel.config.textColor')"
|
|
179
182
|
:value="element.textColor ?? ''"
|
|
180
183
|
:disabled="!isEditable"
|
|
181
184
|
@update:value="(v) => patch({ textColor: v || undefined })"
|
|
182
185
|
/>
|
|
183
186
|
</ConfigFieldGroup>
|
|
184
|
-
<ConfigFieldGroup title="
|
|
187
|
+
<ConfigFieldGroup :title="t('panel.config.groupInputAbility')">
|
|
185
188
|
<label class="flex items-center gap-2">
|
|
186
189
|
<Checkbox
|
|
187
190
|
:checked="element.textAllowInput ?? true"
|
|
188
191
|
:disabled="!isEditable"
|
|
189
192
|
@update:checked="(v) => patch({ textAllowInput: v !== false })"
|
|
190
193
|
/>
|
|
191
|
-
<span
|
|
194
|
+
<span>{{ t("panel.config.allowCanvasInput") }}</span>
|
|
192
195
|
</label>
|
|
193
196
|
</ConfigFieldGroup>
|
|
194
197
|
</ConfigSection>
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/vue";
|
|
2
3
|
import { ref } from "vue";
|
|
3
4
|
import { Checkbox, Input } 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
|
|
|
11
|
+
const { t, locale } = useI18n();
|
|
12
|
+
const msgs = () => getPanelMessages(t);
|
|
13
|
+
|
|
13
14
|
const props = defineProps<{
|
|
14
15
|
element: PanelElement;
|
|
15
16
|
isEditable: boolean;
|
|
@@ -34,15 +35,15 @@ function patch(patch: Partial<PanelElement>) {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
async function handleUploadVideoFile(file: File) {
|
|
37
|
-
const base64 = await readFileAsDataUrl(file,
|
|
38
|
+
const base64 = await readFileAsDataUrl(file, msgs().readVideoFailed);
|
|
38
39
|
patch({ videoSrc: base64 });
|
|
39
|
-
videoStatus.value =
|
|
40
|
+
videoStatus.value = msgs().videoLocalSaved;
|
|
40
41
|
const url = await uploadFileToRemote(file);
|
|
41
42
|
if (url) {
|
|
42
43
|
patch({ videoRemoteUrl: url });
|
|
43
|
-
videoStatus.value =
|
|
44
|
+
videoStatus.value = msgs().videoRemoteUploaded;
|
|
44
45
|
} else {
|
|
45
|
-
videoStatus.value =
|
|
46
|
+
videoStatus.value = msgs().videoServerUploadFailed;
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
|
|
@@ -55,14 +56,14 @@ function onVideoFileChange(e: Event) {
|
|
|
55
56
|
|
|
56
57
|
<template>
|
|
57
58
|
<ConfigSection
|
|
58
|
-
title="
|
|
59
|
+
:title="t('panel.config.sectionVideo')"
|
|
59
60
|
:open="open"
|
|
60
61
|
:force-open="forceOpen"
|
|
61
62
|
@update:open="emit('update:open', $event)"
|
|
62
63
|
>
|
|
63
|
-
<ConfigFieldGroup title="
|
|
64
|
+
<ConfigFieldGroup :title="t('panel.config.groupVideoSource')">
|
|
64
65
|
<label class="block space-y-1">
|
|
65
|
-
<div
|
|
66
|
+
<div>{{ t("panel.config.videoUrl") }}</div>
|
|
66
67
|
<Input
|
|
67
68
|
size="small"
|
|
68
69
|
:value="element.videoRemoteUrl ?? ''"
|
|
@@ -79,7 +80,7 @@ function onVideoFileChange(e: Event) {
|
|
|
79
80
|
class="inline-flex cursor-pointer items-center rounded border border-gray-200 px-2 py-1 text-[11px] hover:bg-gray-50"
|
|
80
81
|
:class="{ 'pointer-events-none opacity-50': !isEditable }"
|
|
81
82
|
>
|
|
82
|
-
|
|
83
|
+
{{ t("panel.config.uploadVideo") }}
|
|
83
84
|
<input type="file" accept="video/*" class="hidden" :disabled="!isEditable" @change="onVideoFileChange" />
|
|
84
85
|
</label>
|
|
85
86
|
</div>
|
|
@@ -100,7 +101,7 @@ function onVideoFileChange(e: Event) {
|
|
|
100
101
|
:disabled="!isEditable"
|
|
101
102
|
@update:checked="(v) => patch({ mediaAutoPauseOnEdit: v !== false })"
|
|
102
103
|
/>
|
|
103
|
-
<span
|
|
104
|
+
<span>{{ t("panel.config.autoPauseMedia") }}</span>
|
|
104
105
|
</label>
|
|
105
106
|
</ConfigFieldGroup>
|
|
106
107
|
</ConfigSection>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { PanelChartConfig } from "../../types";
|
|
2
|
-
import { PANEL_MESSAGES } from "../../constants/messages";
|
|
3
2
|
|
|
4
3
|
export function mergeOptionPatch(
|
|
5
4
|
base: Record<string, unknown> | undefined,
|
|
@@ -70,5 +69,3 @@ export function patchChart(
|
|
|
70
69
|
): PanelChartConfig {
|
|
71
70
|
return { ...(chart ?? {}), ...patch };
|
|
72
71
|
}
|
|
73
|
-
|
|
74
|
-
export { PANEL_MESSAGES };
|