@shwfed/nuxt 0.9.2 → 0.10.1
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/dist/module.json +1 -1
- package/dist/runtime/components/fields.d.vue.ts +75 -3
- package/dist/runtime/components/fields.vue +2 -0
- package/dist/runtime/components/fields.vue.d.ts +75 -3
- package/dist/runtime/components/ui/field/FieldError.vue +1 -1
- package/dist/runtime/components/ui/field/index.js +1 -1
- package/dist/runtime/components/ui/fields/Fields.d.vue.ts +147 -2
- package/dist/runtime/components/ui/fields/Fields.vue +107 -9
- package/dist/runtime/components/ui/fields/Fields.vue.d.ts +147 -2
- package/dist/runtime/components/ui/fields/schema.d.ts +127 -2
- package/dist/runtime/components/ui/fields/schema.js +37 -1
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +70 -0
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +692 -151
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +70 -0
- package/dist/runtime/components/ui/input-group/index.js +2 -2
- package/package.json +1 -1
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { useNuxtApp } from "#app";
|
|
3
|
+
import z, {} from "zod";
|
|
2
4
|
import { useSortable } from "@vueuse/integrations/useSortable";
|
|
3
5
|
import { Icon } from "@iconify/vue";
|
|
4
6
|
import { computed, nextTick, ref, toRaw, watch } from "vue";
|
|
5
7
|
import { useI18n } from "vue-i18n";
|
|
6
8
|
import {
|
|
7
9
|
CalendarFieldC,
|
|
10
|
+
EmptyFieldC,
|
|
11
|
+
FieldsConfigC,
|
|
12
|
+
FieldsStyleC,
|
|
8
13
|
NumberFieldC,
|
|
9
14
|
SelectFieldC,
|
|
15
|
+
SlotFieldC,
|
|
10
16
|
StringFieldC,
|
|
11
|
-
|
|
17
|
+
TextareaFieldC
|
|
12
18
|
} from "../fields/schema";
|
|
13
19
|
import { cn } from "../../../utils/cn";
|
|
14
20
|
import { Button } from "../button";
|
|
@@ -26,7 +32,6 @@ import {
|
|
|
26
32
|
DropdownMenuItem,
|
|
27
33
|
DropdownMenuTrigger
|
|
28
34
|
} from "../dropdown-menu";
|
|
29
|
-
import { IconPicker } from "../icon-picker";
|
|
30
35
|
import { Input } from "../input";
|
|
31
36
|
import Locale from "../locale/Locale.vue";
|
|
32
37
|
import { NativeSelect, NativeSelectOption } from "../native-select";
|
|
@@ -39,9 +44,11 @@ const emit = defineEmits(["confirm"]);
|
|
|
39
44
|
const open = defineModel("open", { type: Boolean, ...{
|
|
40
45
|
default: false
|
|
41
46
|
} });
|
|
47
|
+
const { $toast } = useNuxtApp();
|
|
42
48
|
const { t } = useI18n();
|
|
43
49
|
const draftOrientation = ref("horizontal");
|
|
44
50
|
const draftStyle = ref();
|
|
51
|
+
const search = ref("");
|
|
45
52
|
const selectedItemId = ref("general");
|
|
46
53
|
const draftFields = ref([]);
|
|
47
54
|
const sortableListRef = ref(null);
|
|
@@ -49,19 +56,32 @@ const sortableItemIds = ref([]);
|
|
|
49
56
|
const validationErrors = ref({});
|
|
50
57
|
const fieldTypeOptions = computed(() => [
|
|
51
58
|
{ type: "string", label: t("field-type-string") },
|
|
59
|
+
{ type: "textarea", label: t("field-type-textarea") },
|
|
52
60
|
{ type: "number", label: t("field-type-number") },
|
|
53
61
|
{ type: "select", label: t("field-type-select") },
|
|
54
|
-
{ type: "calendar", label: t("field-type-calendar") }
|
|
62
|
+
{ type: "calendar", label: t("field-type-calendar") },
|
|
63
|
+
{ type: "empty", label: t("field-type-empty") },
|
|
64
|
+
{ type: "slot", label: t("field-type-slot") }
|
|
55
65
|
]);
|
|
56
66
|
const generalItem = computed(() => ({
|
|
57
67
|
id: "general",
|
|
58
68
|
label: t("general")
|
|
59
69
|
}));
|
|
70
|
+
const normalizedSearch = computed(() => search.value.trim().toLocaleLowerCase());
|
|
60
71
|
const selectedField = computed(() => draftFields.value.find((field) => field.draftId === selectedItemId.value));
|
|
61
|
-
const selectedFieldValidationRules = computed(() =>
|
|
72
|
+
const selectedFieldValidationRules = computed(() => {
|
|
73
|
+
const field = selectedField.value?.field;
|
|
74
|
+
if (!field || field.type === "slot" || field.type === "empty") {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
return field.validation ?? [];
|
|
78
|
+
});
|
|
62
79
|
function createDraftId() {
|
|
63
80
|
return crypto.randomUUID();
|
|
64
81
|
}
|
|
82
|
+
function createFieldId() {
|
|
83
|
+
return crypto.randomUUID();
|
|
84
|
+
}
|
|
65
85
|
function createDefaultLocaleValue() {
|
|
66
86
|
return [{ locale: "zh", message: "" }];
|
|
67
87
|
}
|
|
@@ -83,12 +103,24 @@ function normalizeOrientation(value) {
|
|
|
83
103
|
function getFieldTypeLabel(type) {
|
|
84
104
|
return t(`field-type-${type}`);
|
|
85
105
|
}
|
|
106
|
+
function isPassiveField(field) {
|
|
107
|
+
return field.type === "slot" || field.type === "empty";
|
|
108
|
+
}
|
|
109
|
+
function getSlotFieldLabel(_) {
|
|
110
|
+
return getFieldTypeLabel("slot");
|
|
111
|
+
}
|
|
112
|
+
function getEmptyFieldLabel(_) {
|
|
113
|
+
return getFieldTypeLabel("empty");
|
|
114
|
+
}
|
|
86
115
|
function getUnnamedFieldLabel(field) {
|
|
87
116
|
return t("unnamed-field", {
|
|
88
117
|
type: getFieldTypeLabel(field.type)
|
|
89
118
|
});
|
|
90
119
|
}
|
|
91
120
|
function getFieldChineseTitle(field) {
|
|
121
|
+
if (isPassiveField(field)) {
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
92
124
|
const zhTitle = field.title.find((item) => item.locale === "zh");
|
|
93
125
|
if (!zhTitle) {
|
|
94
126
|
return void 0;
|
|
@@ -97,14 +129,31 @@ function getFieldChineseTitle(field) {
|
|
|
97
129
|
return message.length > 0 ? message : void 0;
|
|
98
130
|
}
|
|
99
131
|
function getFieldListLabel(field) {
|
|
132
|
+
if (field.type === "slot") {
|
|
133
|
+
return getSlotFieldLabel(field);
|
|
134
|
+
}
|
|
135
|
+
if (field.type === "empty") {
|
|
136
|
+
return getEmptyFieldLabel(field);
|
|
137
|
+
}
|
|
100
138
|
return getFieldChineseTitle(field) ?? getUnnamedFieldLabel(field);
|
|
101
139
|
}
|
|
102
140
|
const fieldItems = computed(() => draftFields.value.map((item) => ({
|
|
103
141
|
itemId: item.draftId,
|
|
142
|
+
fieldId: item.field.id,
|
|
104
143
|
label: getFieldListLabel(item.field),
|
|
105
|
-
path: item.field.path,
|
|
144
|
+
path: isPassiveField(item.field) ? void 0 : item.field.path,
|
|
145
|
+
searchMeta: isPassiveField(item.field) ? item.field.id : [item.field.path, item.field.id].filter(Boolean).join(" "),
|
|
106
146
|
type: item.field.type
|
|
107
147
|
})));
|
|
148
|
+
const filteredFieldItems = computed(() => {
|
|
149
|
+
if (!normalizedSearch.value) {
|
|
150
|
+
return fieldItems.value;
|
|
151
|
+
}
|
|
152
|
+
return fieldItems.value.filter((item) => {
|
|
153
|
+
const haystack = [item.label, item.searchMeta].filter(Boolean).join(" ").toLocaleLowerCase();
|
|
154
|
+
return haystack.includes(normalizedSearch.value);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
108
157
|
const selectedItemLabel = computed(() => selectedField.value ? getFieldListLabel(selectedField.value.field) : generalItem.value.label);
|
|
109
158
|
const sortable = useSortable(sortableListRef, sortableItemIds);
|
|
110
159
|
function getFieldErrorKey(draftId, fieldKey) {
|
|
@@ -163,9 +212,11 @@ function normalizeValidationRules(validation) {
|
|
|
163
212
|
function normalizeField(field) {
|
|
164
213
|
switch (field.type) {
|
|
165
214
|
case "string":
|
|
215
|
+
case "textarea":
|
|
166
216
|
return {
|
|
167
217
|
...field,
|
|
168
218
|
path: field.path.trim(),
|
|
219
|
+
required: field.required ? true : void 0,
|
|
169
220
|
icon: normalizeOptionalString(field.icon ?? ""),
|
|
170
221
|
style: normalizeOptionalString(field.style ?? ""),
|
|
171
222
|
maxLength: normalizeOptionalString(field.maxLength ?? ""),
|
|
@@ -178,6 +229,7 @@ function normalizeField(field) {
|
|
|
178
229
|
return {
|
|
179
230
|
...field,
|
|
180
231
|
path: field.path.trim(),
|
|
232
|
+
required: field.required ? true : void 0,
|
|
181
233
|
icon: normalizeOptionalString(field.icon ?? ""),
|
|
182
234
|
style: normalizeOptionalString(field.style ?? ""),
|
|
183
235
|
min: normalizeOptionalString(field.min ?? ""),
|
|
@@ -191,6 +243,7 @@ function normalizeField(field) {
|
|
|
191
243
|
return {
|
|
192
244
|
...field,
|
|
193
245
|
path: field.path.trim(),
|
|
246
|
+
required: field.required ? true : void 0,
|
|
194
247
|
icon: normalizeOptionalString(field.icon ?? ""),
|
|
195
248
|
style: normalizeOptionalString(field.style ?? ""),
|
|
196
249
|
options: field.options.trim(),
|
|
@@ -205,6 +258,7 @@ function normalizeField(field) {
|
|
|
205
258
|
return {
|
|
206
259
|
...field,
|
|
207
260
|
path: field.path.trim(),
|
|
261
|
+
required: field.required ? true : void 0,
|
|
208
262
|
icon: normalizeOptionalString(field.icon ?? ""),
|
|
209
263
|
style: normalizeOptionalString(field.style ?? ""),
|
|
210
264
|
display: normalizeOptionalString(field.display ?? ""),
|
|
@@ -214,25 +268,40 @@ function normalizeField(field) {
|
|
|
214
268
|
disabled: normalizeOptionalString(field.disabled ?? ""),
|
|
215
269
|
validation: normalizeValidationRules(field.validation)
|
|
216
270
|
};
|
|
271
|
+
case "slot":
|
|
272
|
+
return {
|
|
273
|
+
...field,
|
|
274
|
+
style: normalizeOptionalString(field.style ?? "")
|
|
275
|
+
};
|
|
276
|
+
case "empty":
|
|
277
|
+
return {
|
|
278
|
+
...field,
|
|
279
|
+
style: normalizeOptionalString(field.style ?? "")
|
|
280
|
+
};
|
|
217
281
|
}
|
|
218
282
|
}
|
|
219
283
|
function createField(type) {
|
|
220
284
|
const title = createDefaultLocaleValue();
|
|
285
|
+
const id = createFieldId();
|
|
221
286
|
switch (type) {
|
|
222
287
|
case "string":
|
|
288
|
+
case "textarea":
|
|
223
289
|
return {
|
|
290
|
+
id,
|
|
224
291
|
type,
|
|
225
292
|
path: "",
|
|
226
293
|
title
|
|
227
294
|
};
|
|
228
295
|
case "number":
|
|
229
296
|
return {
|
|
297
|
+
id,
|
|
230
298
|
type,
|
|
231
299
|
path: "",
|
|
232
300
|
title
|
|
233
301
|
};
|
|
234
302
|
case "select":
|
|
235
303
|
return {
|
|
304
|
+
id,
|
|
236
305
|
type,
|
|
237
306
|
path: "",
|
|
238
307
|
title,
|
|
@@ -243,18 +312,33 @@ function createField(type) {
|
|
|
243
312
|
};
|
|
244
313
|
case "calendar":
|
|
245
314
|
return {
|
|
315
|
+
id,
|
|
246
316
|
type,
|
|
247
317
|
path: "",
|
|
248
318
|
title,
|
|
249
319
|
mode: "date",
|
|
250
320
|
value: "yyyy-MM-dd"
|
|
251
321
|
};
|
|
322
|
+
case "empty":
|
|
323
|
+
return {
|
|
324
|
+
id,
|
|
325
|
+
type
|
|
326
|
+
};
|
|
327
|
+
case "slot":
|
|
328
|
+
return {
|
|
329
|
+
id,
|
|
330
|
+
type
|
|
331
|
+
};
|
|
252
332
|
}
|
|
253
333
|
}
|
|
254
334
|
function resetDraftConfig() {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
335
|
+
applyDraftConfig(props.config);
|
|
336
|
+
}
|
|
337
|
+
function applyDraftConfig(config) {
|
|
338
|
+
draftOrientation.value = config.orientation ?? "horizontal";
|
|
339
|
+
draftStyle.value = normalizeOptionalString(config.style ?? "");
|
|
340
|
+
search.value = "";
|
|
341
|
+
draftFields.value = cloneFields(config.fields);
|
|
258
342
|
selectedItemId.value = "general";
|
|
259
343
|
validationErrors.value = {};
|
|
260
344
|
}
|
|
@@ -289,7 +373,7 @@ function configureSortable() {
|
|
|
289
373
|
}
|
|
290
374
|
async function refreshSortable() {
|
|
291
375
|
sortable.stop();
|
|
292
|
-
if (!open.value || draftFields.value.length === 0) {
|
|
376
|
+
if (!open.value || draftFields.value.length === 0 || normalizedSearch.value) {
|
|
293
377
|
return;
|
|
294
378
|
}
|
|
295
379
|
await nextTick();
|
|
@@ -324,6 +408,21 @@ watch(fieldItems, async (items) => {
|
|
|
324
408
|
await refreshSortable();
|
|
325
409
|
}
|
|
326
410
|
}, { immediate: true });
|
|
411
|
+
watch(filteredFieldItems, (items) => {
|
|
412
|
+
if (!normalizedSearch.value || selectedItemId.value === "general") {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (items.some((item) => item.itemId === selectedItemId.value)) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
selectedItemId.value = items[0]?.itemId ?? "general";
|
|
419
|
+
}, { immediate: true });
|
|
420
|
+
watch(normalizedSearch, async () => {
|
|
421
|
+
if (!open.value) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
await refreshSortable();
|
|
425
|
+
});
|
|
327
426
|
function discardChanges() {
|
|
328
427
|
resetDraftConfig();
|
|
329
428
|
open.value = false;
|
|
@@ -385,26 +484,25 @@ function updateSelectedFieldTitle(value) {
|
|
|
385
484
|
title: value
|
|
386
485
|
}));
|
|
387
486
|
}
|
|
388
|
-
function
|
|
487
|
+
async function copySelectedFieldId() {
|
|
389
488
|
const selected = selectedField.value;
|
|
390
489
|
if (!selected) {
|
|
391
490
|
return;
|
|
392
491
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}));
|
|
492
|
+
await copyFieldId(selected.field.id);
|
|
493
|
+
}
|
|
494
|
+
async function copyFieldId(fieldId) {
|
|
495
|
+
await writeClipboardText(fieldId, t("copy-field-id-failed"));
|
|
398
496
|
}
|
|
399
|
-
function
|
|
497
|
+
function updateSelectedFieldPath(value) {
|
|
400
498
|
const selected = selectedField.value;
|
|
401
|
-
if (!selected) {
|
|
499
|
+
if (!selected || isPassiveField(selected.field)) {
|
|
402
500
|
return;
|
|
403
501
|
}
|
|
404
|
-
clearFieldError(selected.draftId, "
|
|
502
|
+
clearFieldError(selected.draftId, "path");
|
|
405
503
|
updateDraftField(selected.draftId, (field) => ({
|
|
406
504
|
...field,
|
|
407
|
-
|
|
505
|
+
path: String(value).trim()
|
|
408
506
|
}));
|
|
409
507
|
}
|
|
410
508
|
function updateSelectedFieldStyle(value) {
|
|
@@ -440,13 +538,28 @@ function updateSelectedFieldDisabled(value) {
|
|
|
440
538
|
disabled: normalizeOptionalString(String(value))
|
|
441
539
|
}));
|
|
442
540
|
}
|
|
541
|
+
function updateSelectedFieldRequired(value) {
|
|
542
|
+
const selected = selectedField.value;
|
|
543
|
+
if (!selected || isPassiveField(selected.field)) {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
updateDraftField(selected.draftId, (field) => {
|
|
547
|
+
if (isPassiveField(field)) {
|
|
548
|
+
return field;
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
...field,
|
|
552
|
+
required: value ? true : void 0
|
|
553
|
+
};
|
|
554
|
+
});
|
|
555
|
+
}
|
|
443
556
|
function updateSelectedStringDiscardEmpty(value) {
|
|
444
557
|
const selected = selectedField.value;
|
|
445
|
-
if (!selected || selected.field.type !== "string") {
|
|
558
|
+
if (!selected || selected.field.type !== "string" && selected.field.type !== "textarea") {
|
|
446
559
|
return;
|
|
447
560
|
}
|
|
448
561
|
updateDraftField(selected.draftId, (field) => {
|
|
449
|
-
if (field.type !== "string") {
|
|
562
|
+
if (field.type !== "string" && field.type !== "textarea") {
|
|
450
563
|
return field;
|
|
451
564
|
}
|
|
452
565
|
return {
|
|
@@ -457,12 +570,12 @@ function updateSelectedStringDiscardEmpty(value) {
|
|
|
457
570
|
}
|
|
458
571
|
function updateSelectedStringMaxLength(value) {
|
|
459
572
|
const selected = selectedField.value;
|
|
460
|
-
if (!selected || selected.field.type !== "string") {
|
|
573
|
+
if (!selected || selected.field.type !== "string" && selected.field.type !== "textarea") {
|
|
461
574
|
return;
|
|
462
575
|
}
|
|
463
576
|
clearFieldError(selected.draftId, "maxLength");
|
|
464
577
|
updateDraftField(selected.draftId, (field) => {
|
|
465
|
-
if (field.type !== "string") {
|
|
578
|
+
if (field.type !== "string" && field.type !== "textarea") {
|
|
466
579
|
return field;
|
|
467
580
|
}
|
|
468
581
|
return {
|
|
@@ -653,23 +766,28 @@ function updateSelectedCalendarDisableDate(value) {
|
|
|
653
766
|
}
|
|
654
767
|
function addValidationRule() {
|
|
655
768
|
const selected = selectedField.value;
|
|
656
|
-
if (!selected) {
|
|
769
|
+
if (!selected || isPassiveField(selected.field)) {
|
|
657
770
|
return;
|
|
658
771
|
}
|
|
659
|
-
updateDraftField(selected.draftId, (field) =>
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
772
|
+
updateDraftField(selected.draftId, (field) => {
|
|
773
|
+
if (isPassiveField(field)) {
|
|
774
|
+
return field;
|
|
775
|
+
}
|
|
776
|
+
return {
|
|
777
|
+
...field,
|
|
778
|
+
validation: [
|
|
779
|
+
...field.validation ?? [],
|
|
780
|
+
{
|
|
781
|
+
expression: "",
|
|
782
|
+
message: ""
|
|
783
|
+
}
|
|
784
|
+
]
|
|
785
|
+
};
|
|
786
|
+
});
|
|
669
787
|
}
|
|
670
788
|
function updateValidationRule(index, updater) {
|
|
671
789
|
const selected = selectedField.value;
|
|
672
|
-
if (!selected) {
|
|
790
|
+
if (!selected || isPassiveField(selected.field)) {
|
|
673
791
|
return;
|
|
674
792
|
}
|
|
675
793
|
const validation = selected.field.validation ?? [];
|
|
@@ -678,10 +796,15 @@ function updateValidationRule(index, updater) {
|
|
|
678
796
|
return;
|
|
679
797
|
}
|
|
680
798
|
const nextValidation = validation.map((rule, ruleIndex) => ruleIndex === index ? updater(rule) : rule);
|
|
681
|
-
updateDraftField(selected.draftId, (field) =>
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
799
|
+
updateDraftField(selected.draftId, (field) => {
|
|
800
|
+
if (isPassiveField(field)) {
|
|
801
|
+
return field;
|
|
802
|
+
}
|
|
803
|
+
return {
|
|
804
|
+
...field,
|
|
805
|
+
validation: nextValidation
|
|
806
|
+
};
|
|
807
|
+
});
|
|
685
808
|
}
|
|
686
809
|
function updateSelectedValidationExpression(index, value) {
|
|
687
810
|
const selected = selectedField.value;
|
|
@@ -707,7 +830,7 @@ function updateSelectedValidationMessage(index, value) {
|
|
|
707
830
|
}
|
|
708
831
|
function moveValidationRule(index, offset) {
|
|
709
832
|
const selected = selectedField.value;
|
|
710
|
-
if (!selected) {
|
|
833
|
+
if (!selected || isPassiveField(selected.field)) {
|
|
711
834
|
return;
|
|
712
835
|
}
|
|
713
836
|
const validation = selected.field.validation ?? [];
|
|
@@ -723,15 +846,20 @@ function moveValidationRule(index, offset) {
|
|
|
723
846
|
}
|
|
724
847
|
nextValidation[index] = targetRule;
|
|
725
848
|
nextValidation[nextIndex] = currentRule;
|
|
726
|
-
updateDraftField(selected.draftId, (field) =>
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
849
|
+
updateDraftField(selected.draftId, (field) => {
|
|
850
|
+
if (isPassiveField(field)) {
|
|
851
|
+
return field;
|
|
852
|
+
}
|
|
853
|
+
return {
|
|
854
|
+
...field,
|
|
855
|
+
validation: nextValidation
|
|
856
|
+
};
|
|
857
|
+
});
|
|
730
858
|
clearValidationRuleErrors(selected.draftId);
|
|
731
859
|
}
|
|
732
860
|
function deleteValidationRule(index) {
|
|
733
861
|
const selected = selectedField.value;
|
|
734
|
-
if (!selected) {
|
|
862
|
+
if (!selected || isPassiveField(selected.field)) {
|
|
735
863
|
return;
|
|
736
864
|
}
|
|
737
865
|
const validation = selected.field.validation ?? [];
|
|
@@ -741,7 +869,14 @@ function deleteValidationRule(index) {
|
|
|
741
869
|
clearValidationRuleError(selected.draftId, index, "expression");
|
|
742
870
|
clearValidationRuleError(selected.draftId, index, "message");
|
|
743
871
|
updateDraftField(selected.draftId, (field) => {
|
|
744
|
-
|
|
872
|
+
if (isPassiveField(field)) {
|
|
873
|
+
return field;
|
|
874
|
+
}
|
|
875
|
+
const validation2 = field.validation ?? [];
|
|
876
|
+
const nextValidation = [
|
|
877
|
+
...validation2.slice(0, index),
|
|
878
|
+
...validation2.slice(index + 1)
|
|
879
|
+
];
|
|
745
880
|
return {
|
|
746
881
|
...field,
|
|
747
882
|
validation: nextValidation.length > 0 ? nextValidation : void 0
|
|
@@ -755,6 +890,10 @@ function getSchemaIssues(field) {
|
|
|
755
890
|
const result = StringFieldC.safeParse(field);
|
|
756
891
|
return result.success ? [] : result.error.issues;
|
|
757
892
|
}
|
|
893
|
+
case "textarea": {
|
|
894
|
+
const result = TextareaFieldC.safeParse(field);
|
|
895
|
+
return result.success ? [] : result.error.issues;
|
|
896
|
+
}
|
|
758
897
|
case "number": {
|
|
759
898
|
const result = NumberFieldC.safeParse(field);
|
|
760
899
|
return result.success ? [] : result.error.issues;
|
|
@@ -767,6 +906,14 @@ function getSchemaIssues(field) {
|
|
|
767
906
|
const result = CalendarFieldC.safeParse(field);
|
|
768
907
|
return result.success ? [] : result.error.issues;
|
|
769
908
|
}
|
|
909
|
+
case "slot": {
|
|
910
|
+
const result = SlotFieldC.safeParse(field);
|
|
911
|
+
return result.success ? [] : result.error.issues;
|
|
912
|
+
}
|
|
913
|
+
case "empty": {
|
|
914
|
+
const result = EmptyFieldC.safeParse(field);
|
|
915
|
+
return result.success ? [] : result.error.issues;
|
|
916
|
+
}
|
|
770
917
|
}
|
|
771
918
|
}
|
|
772
919
|
function normalizeIssuePath(path) {
|
|
@@ -794,19 +941,30 @@ function validateDraftFields() {
|
|
|
794
941
|
draftId: item.draftId,
|
|
795
942
|
field: normalizeField(item.field)
|
|
796
943
|
}));
|
|
944
|
+
const idOwners = {};
|
|
797
945
|
const pathOwners = {};
|
|
798
946
|
let firstInvalidItemId;
|
|
799
947
|
for (const item of normalizedFields) {
|
|
800
|
-
const
|
|
801
|
-
if (
|
|
802
|
-
setError(errors, getFieldErrorKey(item.draftId, "
|
|
803
|
-
|
|
804
|
-
} else if (existingOwner !== void 0) {
|
|
805
|
-
setError(errors, getFieldErrorKey(item.draftId, "path"), t("field-path-duplicate"));
|
|
806
|
-
setError(errors, getFieldErrorKey(existingOwner, "path"), t("field-path-duplicate"));
|
|
948
|
+
const existingIdOwner = idOwners[item.field.id];
|
|
949
|
+
if (existingIdOwner !== void 0) {
|
|
950
|
+
setError(errors, getFieldErrorKey(item.draftId, "id"), t("field-id-duplicate"));
|
|
951
|
+
setError(errors, getFieldErrorKey(existingIdOwner, "id"), t("field-id-duplicate"));
|
|
807
952
|
firstInvalidItemId = firstInvalidItemId ?? item.draftId;
|
|
808
953
|
} else {
|
|
809
|
-
|
|
954
|
+
idOwners[item.field.id] = item.draftId;
|
|
955
|
+
}
|
|
956
|
+
if (!isPassiveField(item.field)) {
|
|
957
|
+
const existingPathOwner = pathOwners[item.field.path];
|
|
958
|
+
if (item.field.path.length === 0) {
|
|
959
|
+
setError(errors, getFieldErrorKey(item.draftId, "path"), t("field-path-required"));
|
|
960
|
+
firstInvalidItemId = firstInvalidItemId ?? item.draftId;
|
|
961
|
+
} else if (existingPathOwner !== void 0) {
|
|
962
|
+
setError(errors, getFieldErrorKey(item.draftId, "path"), t("field-path-duplicate"));
|
|
963
|
+
setError(errors, getFieldErrorKey(existingPathOwner, "path"), t("field-path-duplicate"));
|
|
964
|
+
firstInvalidItemId = firstInvalidItemId ?? item.draftId;
|
|
965
|
+
} else {
|
|
966
|
+
pathOwners[item.field.path] = item.draftId;
|
|
967
|
+
}
|
|
810
968
|
}
|
|
811
969
|
if (item.field.type === "calendar" && item.field.value.length === 0) {
|
|
812
970
|
setError(errors, getFieldErrorKey(item.draftId, "value"), t("calendar-value-required"));
|
|
@@ -828,10 +986,10 @@ function validateDraftFields() {
|
|
|
828
986
|
}
|
|
829
987
|
return normalizedFields;
|
|
830
988
|
}
|
|
831
|
-
function
|
|
989
|
+
function buildDraftConfig() {
|
|
832
990
|
const normalizedFields = validateDraftFields();
|
|
833
991
|
if (!normalizedFields) {
|
|
834
|
-
return;
|
|
992
|
+
return void 0;
|
|
835
993
|
}
|
|
836
994
|
const generalStyleResult = FieldsStyleC.safeParse(draftStyle.value);
|
|
837
995
|
if (!generalStyleResult.success) {
|
|
@@ -840,9 +998,8 @@ function confirmChanges() {
|
|
|
840
998
|
[getGeneralErrorKey("style")]: generalStyleResult.error.issues[0]?.message ?? t("general-style-invalid")
|
|
841
999
|
};
|
|
842
1000
|
selectedItemId.value = "general";
|
|
843
|
-
return;
|
|
1001
|
+
return void 0;
|
|
844
1002
|
}
|
|
845
|
-
draftFields.value = normalizedFields.map((item) => createDraftField(item.field));
|
|
846
1003
|
const nextConfig = {
|
|
847
1004
|
fields: normalizedFields.map((item) => item.field)
|
|
848
1005
|
};
|
|
@@ -852,7 +1009,235 @@ function confirmChanges() {
|
|
|
852
1009
|
if (generalStyleResult.data) {
|
|
853
1010
|
nextConfig.style = generalStyleResult.data;
|
|
854
1011
|
}
|
|
855
|
-
|
|
1012
|
+
return {
|
|
1013
|
+
config: nextConfig,
|
|
1014
|
+
normalizedFields
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
function showImportError(message) {
|
|
1018
|
+
$toast.error(message);
|
|
1019
|
+
}
|
|
1020
|
+
function showCopyError(message) {
|
|
1021
|
+
$toast.error(message);
|
|
1022
|
+
}
|
|
1023
|
+
function showImportErrorWithCopyAction(message, onClick) {
|
|
1024
|
+
$toast.error(message, {
|
|
1025
|
+
action: {
|
|
1026
|
+
label: t("copy-paste-error"),
|
|
1027
|
+
onClick
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
function getValidDraftConfig(errorMessage) {
|
|
1032
|
+
const result = buildDraftConfig();
|
|
1033
|
+
if (!result) {
|
|
1034
|
+
showCopyError(errorMessage);
|
|
1035
|
+
return void 0;
|
|
1036
|
+
}
|
|
1037
|
+
return result.config;
|
|
1038
|
+
}
|
|
1039
|
+
function buildFieldsConfigJsonSchema() {
|
|
1040
|
+
return z.toJSONSchema(FieldsConfigC, {
|
|
1041
|
+
io: "input",
|
|
1042
|
+
unrepresentable: "any"
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
function buildAiPromptHeaderMarkdown() {
|
|
1046
|
+
return [
|
|
1047
|
+
"# \u5B57\u6BB5\u914D\u7F6E AI \u4E0A\u4E0B\u6587",
|
|
1048
|
+
"\u4F60\u662F\u4E00\u4E2A\u5E2E\u52A9\u7528\u6237\u914D\u7F6E\u5B57\u6BB5\u7EC4\u4EF6\u7684 AI \u52A9\u624B\uFF0C\u8D1F\u8D23\u89E3\u91CA\u3001\u4FEE\u6539\u5E76\u751F\u6210\u53EF\u7C98\u8D34\u7684\u5B57\u6BB5\u914D\u7F6E\u3002",
|
|
1049
|
+
"\u5982\u679C\u4F60\u4E0D\u786E\u5B9A\u67D0\u4E2A\u5B57\u6BB5\u3001\u8868\u8FBE\u5F0F\u53D8\u91CF\u3001\u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587\u6216\u4E1A\u52A1\u542B\u4E49\uFF0C\u5FC5\u987B\u660E\u786E\u8BF4\u660E\u4E0D\u786E\u5B9A\u70B9\uFF0C\u4E0D\u8981\u731C\u6D4B\u3002",
|
|
1050
|
+
"\u53EA\u6709\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u751F\u6210\u914D\u7F6E\u65F6\uFF0C\u624D\u8FD4\u56DE\u5B8C\u6574\u914D\u7F6E\u6216\u914D\u7F6E\u7247\u6BB5\uFF1B\u5982\u679C\u8FD4\u56DE\u914D\u7F6E\uFF0C\u5FC5\u987B\u653E\u5728 Markdown code block \u4E2D\u3002"
|
|
1051
|
+
].join("\n");
|
|
1052
|
+
}
|
|
1053
|
+
function buildDslGuideMarkdown() {
|
|
1054
|
+
return [
|
|
1055
|
+
"## DSL / CEL \u7F16\u5199\u8BF4\u660E",
|
|
1056
|
+
"\u672C\u914D\u7F6E\u4E2D\u7684\u8868\u8FBE\u5F0F\u5B57\u6BB5\u5FC5\u987B\u586B\u5199 CEL \u5B57\u7B26\u4E32\uFF0C\u4E0D\u8981\u751F\u6210 JavaScript\u3001TypeScript\u3001\u7BAD\u5934\u51FD\u6570\u6216\u4F2A\u4EE3\u7801\u3002",
|
|
1057
|
+
"",
|
|
1058
|
+
"### 1. \u57FA\u7840\u8BED\u6CD5",
|
|
1059
|
+
'- \u5B57\u9762\u91CF\uFF1A`"text"`\u3001`123`\u3001`true`\u3001`false`\u3001`null`\u3002',
|
|
1060
|
+
'- \u5217\u8868 / \u5BF9\u8C61\uFF1A`[1, 2]`\u3001`{"display": "grid"}`\u3002',
|
|
1061
|
+
"- \u8BBF\u95EE\uFF1A`.`\u3001`[]`\u3001`.?`\u3001`[?]`\uFF0C\u4F8B\u5982 `form.name`\u3001`ctx.?user.orValue(null)`\u3002",
|
|
1062
|
+
"- \u6761\u4EF6\u4E0E\u903B\u8F91\uFF1A`&&`\u3001`||`\u3001`!`\u3001`condition ? a : b`\u3002",
|
|
1063
|
+
'- \u65B9\u6CD5\u8C03\u7528\uFF1A`value.method(args)`\uFF0C\u4F8B\u5982 `now.format("yyyy-MM-dd")`\u3002',
|
|
1064
|
+
"",
|
|
1065
|
+
"### 2. \u5E38\u89C1\u5B57\u6BB5\u4E0E\u7528\u9014",
|
|
1066
|
+
'- `style` \u76F8\u5173\u5B57\u6BB5\u9700\u8981\u8FD4\u56DE style map\uFF0C\u4F8B\u5982 `{"display": "grid"}`\u3002',
|
|
1067
|
+
"- `hidden` / `disabled` / `disableDate` \u9700\u8981\u8FD4\u56DE `bool`\u3002",
|
|
1068
|
+
"- `validation[].expression` \u9700\u8981\u8FD4\u56DE `bool`\uFF1B\u8FD4\u56DE `false` \u65F6\u5C55\u793A\u5BF9\u5E94\u6D88\u606F\u3002",
|
|
1069
|
+
"- `select.options` \u901A\u5E38\u8FD4\u56DE\u5217\u8868\uFF1B`label` / `value` / `key` \u7528\u6765\u63CF\u8FF0\u5355\u4E2A\u9009\u9879\u5982\u4F55\u6620\u5C04\u3002",
|
|
1070
|
+
"",
|
|
1071
|
+
"### 3. \u5B57\u6BB5\u7C7B\u578B\u7EA6\u675F",
|
|
1072
|
+
"- \u6240\u6709\u5B57\u6BB5\u90FD\u5FC5\u987B\u5305\u542B\u7A33\u5B9A\u7684 UUID `id`\u3002",
|
|
1073
|
+
"- \u53EA\u6709\u975E `slot` / `empty` \u5B57\u6BB5\u53EF\u4EE5\u914D\u7F6E `path`\uFF0C\u5E76\u53C2\u4E0E\u8868\u5355\u503C\u8BFB\u5199\u3002",
|
|
1074
|
+
"- `slot` \u548C `empty` \u5B57\u6BB5\u90FD\u4E0D\u4F1A\u7ED1\u5B9A\u6A21\u578B\u503C\uFF0C\u53EA\u5141\u8BB8 `id`\u3001`type` \u548C\u53EF\u9009\u7684 `style`\u3002"
|
|
1075
|
+
].join("\n");
|
|
1076
|
+
}
|
|
1077
|
+
function buildMarkdownNotes() {
|
|
1078
|
+
return [
|
|
1079
|
+
"## \u6CE8\u610F\u4E8B\u9879",
|
|
1080
|
+
"- \u6240\u6709\u5B57\u6BB5\u90FD\u5FC5\u987B\u4FDD\u7559\u73B0\u6709 `id`\uFF0C\u4E0D\u8981\u751F\u6210\u65B0\u7684 `id` \u66FF\u6362\u5DF2\u6709\u5B57\u6BB5\u3002",
|
|
1081
|
+
"- `slot` \u4E0E `empty` \u5B57\u6BB5\u53EA\u80FD\u4F7F\u7528 `id`\u3001`type` \u548C\u53EF\u9009\u7684 `style`\u3002",
|
|
1082
|
+
"- \u975E `slot` / `empty` \u5B57\u6BB5\u7684 `path` \u5FC5\u987B\u552F\u4E00\u4E14\u4E0D\u80FD\u4E3A\u7A7A\u3002",
|
|
1083
|
+
"- \u8868\u8FBE\u5F0F\u5B57\u6BB5\u5FC5\u987B\u4E25\u683C\u9075\u5B88 schema \u7EA6\u675F\uFF1B\u5982\u679C schema \u4E0D\u652F\u6301\uFF0C\u5C31\u76F4\u63A5\u8BF4\u660E\u9650\u5236\u3002"
|
|
1084
|
+
].join("\n");
|
|
1085
|
+
}
|
|
1086
|
+
function buildMarkdownCopyContent(config) {
|
|
1087
|
+
return [
|
|
1088
|
+
buildAiPromptHeaderMarkdown(),
|
|
1089
|
+
"",
|
|
1090
|
+
"## \u5F53\u524D\u914D\u7F6E",
|
|
1091
|
+
"```json",
|
|
1092
|
+
JSON.stringify(config, null, 2),
|
|
1093
|
+
"```",
|
|
1094
|
+
"",
|
|
1095
|
+
buildDslGuideMarkdown(),
|
|
1096
|
+
"",
|
|
1097
|
+
buildMarkdownNotes(),
|
|
1098
|
+
"",
|
|
1099
|
+
"## FieldsConfig JSON Schema",
|
|
1100
|
+
"```json",
|
|
1101
|
+
JSON.stringify(buildFieldsConfigJsonSchema(), null, 2),
|
|
1102
|
+
"```"
|
|
1103
|
+
].join("\n");
|
|
1104
|
+
}
|
|
1105
|
+
function formatIssuePath(path) {
|
|
1106
|
+
if (path.length === 0) {
|
|
1107
|
+
return "(root)";
|
|
1108
|
+
}
|
|
1109
|
+
return path.map((segment) => {
|
|
1110
|
+
if (typeof segment === "number") {
|
|
1111
|
+
return `${segment}`;
|
|
1112
|
+
}
|
|
1113
|
+
if (typeof segment === "string") {
|
|
1114
|
+
return segment;
|
|
1115
|
+
}
|
|
1116
|
+
return String(segment);
|
|
1117
|
+
}).join(".");
|
|
1118
|
+
}
|
|
1119
|
+
function formatIssueExtraFields(issue) {
|
|
1120
|
+
const lines = [];
|
|
1121
|
+
for (const [key, value] of Object.entries(issue)) {
|
|
1122
|
+
if (key === "code" || key === "message" || key === "path") {
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
lines.push(` - ${key}: ${JSON.stringify(value)}`);
|
|
1126
|
+
}
|
|
1127
|
+
return lines;
|
|
1128
|
+
}
|
|
1129
|
+
function buildPasteConfigErrorDetails(source, error) {
|
|
1130
|
+
if (error instanceof SyntaxError) {
|
|
1131
|
+
return [
|
|
1132
|
+
"## \u7C98\u8D34\u5931\u8D25\u539F\u56E0",
|
|
1133
|
+
"- \u7C7B\u578B\uFF1AJSON \u89E3\u6790\u5931\u8D25",
|
|
1134
|
+
`- message: ${error.message}`,
|
|
1135
|
+
"",
|
|
1136
|
+
"## \u539F\u59CB\u7C98\u8D34\u5185\u5BB9",
|
|
1137
|
+
"```text",
|
|
1138
|
+
source,
|
|
1139
|
+
"```"
|
|
1140
|
+
].join("\n");
|
|
1141
|
+
}
|
|
1142
|
+
const issueLines = error.issues.flatMap((issue, index) => [
|
|
1143
|
+
`### Issue ${index + 1}`,
|
|
1144
|
+
`- path: ${formatIssuePath(issue.path)}`,
|
|
1145
|
+
`- code: ${issue.code}`,
|
|
1146
|
+
`- message: ${issue.message}`,
|
|
1147
|
+
...formatIssueExtraFields(issue)
|
|
1148
|
+
]);
|
|
1149
|
+
return [
|
|
1150
|
+
"## \u7C98\u8D34\u5931\u8D25\u539F\u56E0",
|
|
1151
|
+
"- \u7C7B\u578B\uFF1ASchema \u6821\u9A8C\u5931\u8D25",
|
|
1152
|
+
"",
|
|
1153
|
+
"## \u539F\u59CB\u7C98\u8D34\u5185\u5BB9",
|
|
1154
|
+
"```json",
|
|
1155
|
+
source,
|
|
1156
|
+
"```",
|
|
1157
|
+
"",
|
|
1158
|
+
"## Schema \u62A5\u9519",
|
|
1159
|
+
...issueLines
|
|
1160
|
+
].join("\n");
|
|
1161
|
+
}
|
|
1162
|
+
function buildPasteConfigErrorMarkdown(source, error) {
|
|
1163
|
+
return [
|
|
1164
|
+
buildAiPromptHeaderMarkdown(),
|
|
1165
|
+
"",
|
|
1166
|
+
"## \u5F53\u524D\u4EFB\u52A1",
|
|
1167
|
+
"\u7528\u6237\u628A\u4E00\u6BB5\u914D\u7F6E\u7C98\u8D34\u56DE\u5B57\u6BB5\u914D\u7F6E\u5668\u65F6\u5931\u8D25\u4E86\u3002\u8BF7\u57FA\u4E8E\u4E0B\u9762\u7684\u539F\u59CB\u5185\u5BB9\u548C\u62A5\u9519\u4FEE\u590D\u5F53\u524D\u914D\u7F6E\u3002",
|
|
1168
|
+
"\u8BF7\u4F18\u5148\u4FEE\u590D\u6700\u5C0F\u5FC5\u8981\u8303\u56F4\uFF0C\u4E0D\u8981\u53D1\u660E schema \u4E2D\u4E0D\u5B58\u5728\u7684\u5B57\u6BB5\uFF0C\u4E5F\u4E0D\u8981\u66FF\u73B0\u6709\u5B57\u6BB5\u751F\u6210\u65B0\u7684 ID\u3002",
|
|
1169
|
+
"\u53EA\u6709\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u8F93\u51FA\u914D\u7F6E\u65F6\uFF0C\u624D\u8FD4\u56DE\u5B8C\u6574\u914D\u7F6E\uFF1B\u5982\u679C\u8FD4\u56DE\u914D\u7F6E\uFF0C\u5FC5\u987B\u653E\u5728 Markdown code block \u4E2D\u3002",
|
|
1170
|
+
"",
|
|
1171
|
+
buildPasteConfigErrorDetails(source, error),
|
|
1172
|
+
"",
|
|
1173
|
+
buildMarkdownNotes()
|
|
1174
|
+
].join("\n");
|
|
1175
|
+
}
|
|
1176
|
+
async function writeClipboardText(value, errorMessage) {
|
|
1177
|
+
try {
|
|
1178
|
+
await navigator.clipboard.writeText(value);
|
|
1179
|
+
} catch {
|
|
1180
|
+
showCopyError(errorMessage);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
async function copyPasteConfigError(source, error) {
|
|
1184
|
+
await writeClipboardText(buildPasteConfigErrorMarkdown(source, error), t("copy-paste-error-failed"));
|
|
1185
|
+
}
|
|
1186
|
+
async function pasteConfigFromClipboard() {
|
|
1187
|
+
let source = "";
|
|
1188
|
+
try {
|
|
1189
|
+
source = await navigator.clipboard.readText();
|
|
1190
|
+
} catch {
|
|
1191
|
+
showImportError(t("paste-config-read-failed"));
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
let parsedValue;
|
|
1195
|
+
try {
|
|
1196
|
+
parsedValue = JSON.parse(source);
|
|
1197
|
+
} catch (error) {
|
|
1198
|
+
if (error instanceof SyntaxError) {
|
|
1199
|
+
showImportErrorWithCopyAction(
|
|
1200
|
+
t("paste-config-invalid-json"),
|
|
1201
|
+
async () => copyPasteConfigError(source, error)
|
|
1202
|
+
);
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
showImportError(t("paste-config-invalid-json"));
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
const result = FieldsConfigC.safeParse(parsedValue);
|
|
1209
|
+
if (!result.success) {
|
|
1210
|
+
showImportErrorWithCopyAction(
|
|
1211
|
+
t("paste-config-invalid-schema"),
|
|
1212
|
+
async () => copyPasteConfigError(source, result.error)
|
|
1213
|
+
);
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
applyDraftConfig(result.data);
|
|
1217
|
+
await nextTick();
|
|
1218
|
+
await refreshSortable();
|
|
1219
|
+
}
|
|
1220
|
+
async function copyConfig() {
|
|
1221
|
+
const config = getValidDraftConfig(t("copy-config-failed"));
|
|
1222
|
+
if (!config) {
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
await writeClipboardText(JSON.stringify(config, null, 2), t("copy-config-failed"));
|
|
1226
|
+
}
|
|
1227
|
+
async function copyMarkdown() {
|
|
1228
|
+
const config = getValidDraftConfig(t("copy-markdown-failed"));
|
|
1229
|
+
if (!config) {
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
await writeClipboardText(buildMarkdownCopyContent(config), t("copy-markdown-failed"));
|
|
1233
|
+
}
|
|
1234
|
+
function confirmChanges() {
|
|
1235
|
+
const result = buildDraftConfig();
|
|
1236
|
+
if (!result) {
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
draftFields.value = result.normalizedFields.map((item) => createDraftField(item.field));
|
|
1240
|
+
emit("confirm", result.config);
|
|
856
1241
|
open.value = false;
|
|
857
1242
|
}
|
|
858
1243
|
</script>
|
|
@@ -867,9 +1252,22 @@ function confirmChanges() {
|
|
|
867
1252
|
:show-close-button="true"
|
|
868
1253
|
>
|
|
869
1254
|
<DialogHeader class="gap-1 border-b border-zinc-200 px-6 py-5">
|
|
870
|
-
<
|
|
871
|
-
|
|
872
|
-
|
|
1255
|
+
<div class="flex items-center gap-3">
|
|
1256
|
+
<DialogTitle class="text-xl font-semibold text-zinc-800">
|
|
1257
|
+
{{ t("configure-fields") }}
|
|
1258
|
+
</DialogTitle>
|
|
1259
|
+
<Button
|
|
1260
|
+
type="button"
|
|
1261
|
+
variant="ghost"
|
|
1262
|
+
size="sm"
|
|
1263
|
+
data-slot="fields-configurator-paste"
|
|
1264
|
+
class="shrink-0"
|
|
1265
|
+
@click="pasteConfigFromClipboard"
|
|
1266
|
+
>
|
|
1267
|
+
<Icon icon="fluent:clipboard-paste-20-regular" />
|
|
1268
|
+
{{ t("paste-config") }}
|
|
1269
|
+
</Button>
|
|
1270
|
+
</div>
|
|
873
1271
|
<DialogDescription class="text-sm text-zinc-500">
|
|
874
1272
|
{{ t("configure-fields-description") }}
|
|
875
1273
|
</DialogDescription>
|
|
@@ -877,7 +1275,42 @@ function confirmChanges() {
|
|
|
877
1275
|
|
|
878
1276
|
<div class="grid min-h-0 flex-1 grid-cols-[19rem_minmax(0,1fr)]">
|
|
879
1277
|
<section class="flex min-h-0 flex-col border-r border-zinc-200 px-4 py-4">
|
|
880
|
-
<
|
|
1278
|
+
<Input
|
|
1279
|
+
v-model="search"
|
|
1280
|
+
data-slot="fields-configurator-search"
|
|
1281
|
+
:placeholder="t('search-fields')"
|
|
1282
|
+
/>
|
|
1283
|
+
|
|
1284
|
+
<DropdownMenu>
|
|
1285
|
+
<DropdownMenuTrigger as-child>
|
|
1286
|
+
<Button
|
|
1287
|
+
type="button"
|
|
1288
|
+
data-slot="fields-configurator-add"
|
|
1289
|
+
class="mt-3 w-full justify-center"
|
|
1290
|
+
>
|
|
1291
|
+
<Icon icon="fluent:add-20-regular" />
|
|
1292
|
+
{{ t("add-field") }}
|
|
1293
|
+
</Button>
|
|
1294
|
+
</DropdownMenuTrigger>
|
|
1295
|
+
|
|
1296
|
+
<DropdownMenuContent
|
|
1297
|
+
align="start"
|
|
1298
|
+
:style="{
|
|
1299
|
+
width: 'var(--reka-dropdown-menu-trigger-width)'
|
|
1300
|
+
}"
|
|
1301
|
+
>
|
|
1302
|
+
<DropdownMenuItem
|
|
1303
|
+
v-for="option in fieldTypeOptions"
|
|
1304
|
+
:key="option.type"
|
|
1305
|
+
:data-slot="`fields-configurator-add-item-${option.type}`"
|
|
1306
|
+
@select="addField(option.type)"
|
|
1307
|
+
>
|
|
1308
|
+
{{ option.label }}
|
|
1309
|
+
</DropdownMenuItem>
|
|
1310
|
+
</DropdownMenuContent>
|
|
1311
|
+
</DropdownMenu>
|
|
1312
|
+
|
|
1313
|
+
<div class="mt-4 flex min-h-0 flex-1 flex-col overflow-hidden">
|
|
881
1314
|
<div class="flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto pr-1">
|
|
882
1315
|
<button
|
|
883
1316
|
type="button"
|
|
@@ -899,16 +1332,17 @@ function confirmChanges() {
|
|
|
899
1332
|
</button>
|
|
900
1333
|
|
|
901
1334
|
<div
|
|
902
|
-
v-if="
|
|
1335
|
+
v-if="filteredFieldItems.length > 0"
|
|
903
1336
|
ref="sortableListRef"
|
|
904
1337
|
data-slot="fields-configurator-list"
|
|
905
1338
|
class="flex flex-col gap-1"
|
|
906
1339
|
>
|
|
907
1340
|
<div
|
|
908
|
-
v-for="item in
|
|
1341
|
+
v-for="item in filteredFieldItems"
|
|
909
1342
|
:key="item.itemId"
|
|
910
1343
|
data-slot="fields-configurator-field-item"
|
|
911
1344
|
:data-item-id="item.itemId"
|
|
1345
|
+
:data-field-id="item.fieldId"
|
|
912
1346
|
:data-field-path="item.path"
|
|
913
1347
|
:data-selected="selectedItemId === item.itemId ? 'true' : 'false'"
|
|
914
1348
|
:class="cn(
|
|
@@ -952,6 +1386,14 @@ function confirmChanges() {
|
|
|
952
1386
|
</div>
|
|
953
1387
|
</div>
|
|
954
1388
|
|
|
1389
|
+
<p
|
|
1390
|
+
v-else-if="normalizedSearch"
|
|
1391
|
+
data-slot="fields-configurator-empty"
|
|
1392
|
+
class="px-1 pt-2 text-xs text-zinc-400"
|
|
1393
|
+
>
|
|
1394
|
+
{{ t("no-matches") }}
|
|
1395
|
+
</p>
|
|
1396
|
+
|
|
955
1397
|
<p
|
|
956
1398
|
v-else
|
|
957
1399
|
data-slot="fields-configurator-empty"
|
|
@@ -961,46 +1403,30 @@ function confirmChanges() {
|
|
|
961
1403
|
</p>
|
|
962
1404
|
</div>
|
|
963
1405
|
</div>
|
|
964
|
-
|
|
965
|
-
<div
|
|
966
|
-
data-slot="fields-configurator-add-container"
|
|
967
|
-
class="mt-4"
|
|
968
|
-
>
|
|
969
|
-
<DropdownMenu>
|
|
970
|
-
<DropdownMenuTrigger as-child>
|
|
971
|
-
<Button
|
|
972
|
-
type="button"
|
|
973
|
-
data-slot="fields-configurator-add"
|
|
974
|
-
size="sm"
|
|
975
|
-
variant="default"
|
|
976
|
-
class="w-full justify-center"
|
|
977
|
-
>
|
|
978
|
-
<Icon icon="fluent:add-20-regular" />
|
|
979
|
-
{{ t("add-field") }}
|
|
980
|
-
</Button>
|
|
981
|
-
</DropdownMenuTrigger>
|
|
982
|
-
|
|
983
|
-
<DropdownMenuContent align="start">
|
|
984
|
-
<DropdownMenuItem
|
|
985
|
-
v-for="option in fieldTypeOptions"
|
|
986
|
-
:key="option.type"
|
|
987
|
-
:data-slot="`fields-configurator-add-item-${option.type}`"
|
|
988
|
-
@select="addField(option.type)"
|
|
989
|
-
>
|
|
990
|
-
{{ option.label }}
|
|
991
|
-
</DropdownMenuItem>
|
|
992
|
-
</DropdownMenuContent>
|
|
993
|
-
</DropdownMenu>
|
|
994
|
-
</div>
|
|
995
1406
|
</section>
|
|
996
1407
|
|
|
997
1408
|
<section class="flex min-h-0 flex-col overflow-y-auto px-6 py-6">
|
|
998
|
-
<
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1409
|
+
<div class="flex items-center gap-2">
|
|
1410
|
+
<h3
|
|
1411
|
+
data-slot="fields-configurator-detail-title"
|
|
1412
|
+
class="text-lg font-semibold text-zinc-800"
|
|
1413
|
+
>
|
|
1414
|
+
{{ selectedItemLabel }}
|
|
1415
|
+
</h3>
|
|
1416
|
+
<Button
|
|
1417
|
+
v-if="selectedField"
|
|
1418
|
+
type="button"
|
|
1419
|
+
variant="ghost"
|
|
1420
|
+
size="sm"
|
|
1421
|
+
data-slot="fields-configurator-copy-id"
|
|
1422
|
+
class="size-7 p-0 text-zinc-400 hover:text-zinc-700"
|
|
1423
|
+
:aria-label="t('copy-field-id', { field: selectedItemLabel })"
|
|
1424
|
+
:title="selectedField.field.id"
|
|
1425
|
+
@click="copySelectedFieldId"
|
|
1426
|
+
>
|
|
1427
|
+
<Icon icon="fluent:copy-20-regular" />
|
|
1428
|
+
</Button>
|
|
1429
|
+
</div>
|
|
1004
1430
|
|
|
1005
1431
|
<p
|
|
1006
1432
|
v-if="selectedItemId === 'general'"
|
|
@@ -1069,24 +1495,20 @@ function confirmChanges() {
|
|
|
1069
1495
|
data-slot="fields-configurator-field-main"
|
|
1070
1496
|
class="mt-6 flex flex-col gap-6"
|
|
1071
1497
|
>
|
|
1072
|
-
<
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
</span>
|
|
1080
|
-
<div
|
|
1081
|
-
data-slot="fields-configurator-field-type"
|
|
1082
|
-
class="flex h-9 items-center rounded-md border border-zinc-200 bg-zinc-50 px-3 text-sm text-zinc-600"
|
|
1083
|
-
>
|
|
1084
|
-
{{ getFieldTypeLabel(selectedField.field.type) }}
|
|
1085
|
-
</div>
|
|
1086
|
-
</label>
|
|
1498
|
+
<p
|
|
1499
|
+
v-if="validationErrors[getFieldErrorKey(selectedField.draftId, 'id')]"
|
|
1500
|
+
data-slot="fields-configurator-field-id-error"
|
|
1501
|
+
class="-mt-4 text-xs text-red-500"
|
|
1502
|
+
>
|
|
1503
|
+
{{ validationErrors[getFieldErrorKey(selectedField.draftId, "id")] }}
|
|
1504
|
+
</p>
|
|
1087
1505
|
|
|
1506
|
+
<section
|
|
1507
|
+
v-if="!isPassiveField(selectedField.field)"
|
|
1508
|
+
data-slot="fields-configurator-field-path-section"
|
|
1509
|
+
class="flex flex-col gap-2"
|
|
1510
|
+
>
|
|
1088
1511
|
<label
|
|
1089
|
-
data-slot="fields-configurator-field-path-section"
|
|
1090
1512
|
class="flex flex-col gap-2"
|
|
1091
1513
|
>
|
|
1092
1514
|
<span class="text-xs font-medium text-zinc-500">
|
|
@@ -1111,6 +1533,7 @@ function confirmChanges() {
|
|
|
1111
1533
|
</section>
|
|
1112
1534
|
|
|
1113
1535
|
<section
|
|
1536
|
+
v-if="!isPassiveField(selectedField.field)"
|
|
1114
1537
|
data-slot="fields-configurator-field-label-section"
|
|
1115
1538
|
class="flex flex-col gap-2"
|
|
1116
1539
|
>
|
|
@@ -1134,27 +1557,47 @@ function confirmChanges() {
|
|
|
1134
1557
|
</section>
|
|
1135
1558
|
|
|
1136
1559
|
<section
|
|
1137
|
-
|
|
1560
|
+
v-if="isPassiveField(selectedField.field)"
|
|
1561
|
+
data-slot="fields-configurator-passive-options"
|
|
1138
1562
|
class="grid gap-4 md:grid-cols-2"
|
|
1139
1563
|
>
|
|
1140
|
-
<label class="flex flex-col gap-2">
|
|
1564
|
+
<label class="flex flex-col gap-2 md:col-span-2">
|
|
1141
1565
|
<span class="text-xs font-medium text-zinc-500">
|
|
1142
|
-
{{ t("field-
|
|
1566
|
+
{{ t("field-style") }}
|
|
1143
1567
|
</span>
|
|
1144
|
-
<
|
|
1145
|
-
data-slot="fields-configurator-field-
|
|
1146
|
-
:model-value="selectedField.field.
|
|
1147
|
-
:invalid="validationErrors[getFieldErrorKey(selectedField.draftId, '
|
|
1148
|
-
:placeholder="t('field-
|
|
1149
|
-
|
|
1568
|
+
<Textarea
|
|
1569
|
+
data-slot="fields-configurator-field-style-input"
|
|
1570
|
+
:model-value="selectedField.field.style ?? ''"
|
|
1571
|
+
:aria-invalid="validationErrors[getFieldErrorKey(selectedField.draftId, 'style')] ? 'true' : void 0"
|
|
1572
|
+
:placeholder="t('field-style-placeholder')"
|
|
1573
|
+
class="min-h-20 font-mono text-sm"
|
|
1574
|
+
@update:model-value="updateSelectedFieldStyle"
|
|
1150
1575
|
/>
|
|
1151
1576
|
<p
|
|
1152
|
-
v-if="validationErrors[getFieldErrorKey(selectedField.draftId, '
|
|
1577
|
+
v-if="validationErrors[getFieldErrorKey(selectedField.draftId, 'style')]"
|
|
1153
1578
|
class="text-xs text-red-500"
|
|
1154
1579
|
>
|
|
1155
|
-
{{ validationErrors[getFieldErrorKey(selectedField.draftId, "
|
|
1580
|
+
{{ validationErrors[getFieldErrorKey(selectedField.draftId, "style")] }}
|
|
1156
1581
|
</p>
|
|
1157
1582
|
</label>
|
|
1583
|
+
</section>
|
|
1584
|
+
|
|
1585
|
+
<section
|
|
1586
|
+
v-else
|
|
1587
|
+
data-slot="fields-configurator-field-general-options"
|
|
1588
|
+
class="grid gap-4 md:grid-cols-2"
|
|
1589
|
+
>
|
|
1590
|
+
<label class="flex items-center justify-between gap-3 rounded-md border border-zinc-200 px-3 py-2 md:col-span-2">
|
|
1591
|
+
<div class="flex flex-col gap-1">
|
|
1592
|
+
<span class="text-sm font-medium text-zinc-800">{{ t("field-required") }}</span>
|
|
1593
|
+
<span class="text-xs text-zinc-500">{{ t("field-required-description") }}</span>
|
|
1594
|
+
</div>
|
|
1595
|
+
<Switch
|
|
1596
|
+
data-slot="fields-configurator-field-required-switch"
|
|
1597
|
+
:model-value="selectedField.field.required ?? false"
|
|
1598
|
+
@update:model-value="updateSelectedFieldRequired"
|
|
1599
|
+
/>
|
|
1600
|
+
</label>
|
|
1158
1601
|
|
|
1159
1602
|
<label class="flex flex-col gap-2">
|
|
1160
1603
|
<span class="text-xs font-medium text-zinc-500">
|
|
@@ -1218,7 +1661,7 @@ function confirmChanges() {
|
|
|
1218
1661
|
</section>
|
|
1219
1662
|
|
|
1220
1663
|
<section
|
|
1221
|
-
v-if="selectedField.field.type === 'string'"
|
|
1664
|
+
v-if="selectedField.field.type === 'string' || selectedField.field.type === 'textarea'"
|
|
1222
1665
|
data-slot="fields-configurator-string-options"
|
|
1223
1666
|
class="grid gap-4 md:grid-cols-2"
|
|
1224
1667
|
>
|
|
@@ -1486,6 +1929,7 @@ function confirmChanges() {
|
|
|
1486
1929
|
</section>
|
|
1487
1930
|
|
|
1488
1931
|
<section
|
|
1932
|
+
v-if="!isPassiveField(selectedField.field)"
|
|
1489
1933
|
data-slot="fields-configurator-validation"
|
|
1490
1934
|
class="flex flex-col gap-4"
|
|
1491
1935
|
>
|
|
@@ -1617,25 +2061,50 @@ function confirmChanges() {
|
|
|
1617
2061
|
</section>
|
|
1618
2062
|
</div>
|
|
1619
2063
|
|
|
1620
|
-
<DialogFooter class="border-t border-zinc-200 px-6 py-4">
|
|
1621
|
-
<
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
variant="default"
|
|
1625
|
-
@click="discardChanges"
|
|
1626
|
-
>
|
|
1627
|
-
<Icon icon="fluent:dismiss-20-regular" />
|
|
1628
|
-
{{ t("cancel") }}
|
|
1629
|
-
</Button>
|
|
1630
|
-
<Button
|
|
1631
|
-
type="button"
|
|
1632
|
-
data-slot="fields-configurator-confirm"
|
|
1633
|
-
variant="primary"
|
|
1634
|
-
@click="confirmChanges"
|
|
2064
|
+
<DialogFooter class="border-t border-zinc-200 px-6 py-4 sm:justify-between">
|
|
2065
|
+
<div
|
|
2066
|
+
data-slot="fields-configurator-copy-actions"
|
|
2067
|
+
class="flex items-center gap-2"
|
|
1635
2068
|
>
|
|
1636
|
-
<
|
|
1637
|
-
|
|
1638
|
-
|
|
2069
|
+
<Button
|
|
2070
|
+
type="button"
|
|
2071
|
+
data-slot="fields-configurator-copy-markdown"
|
|
2072
|
+
variant="ghost"
|
|
2073
|
+
@click="copyMarkdown"
|
|
2074
|
+
>
|
|
2075
|
+
<Icon icon="simple-icons:markdown" />
|
|
2076
|
+
{{ t("copy-markdown") }}
|
|
2077
|
+
</Button>
|
|
2078
|
+
<Button
|
|
2079
|
+
type="button"
|
|
2080
|
+
data-slot="fields-configurator-copy-config"
|
|
2081
|
+
variant="ghost"
|
|
2082
|
+
@click="copyConfig"
|
|
2083
|
+
>
|
|
2084
|
+
<Icon icon="fluent:copy-20-regular" />
|
|
2085
|
+
{{ t("copy-config") }}
|
|
2086
|
+
</Button>
|
|
2087
|
+
</div>
|
|
2088
|
+
<div class="flex items-center gap-2">
|
|
2089
|
+
<Button
|
|
2090
|
+
type="button"
|
|
2091
|
+
data-slot="fields-configurator-cancel"
|
|
2092
|
+
variant="default"
|
|
2093
|
+
@click="discardChanges"
|
|
2094
|
+
>
|
|
2095
|
+
<Icon icon="fluent:dismiss-20-regular" />
|
|
2096
|
+
{{ t("cancel") }}
|
|
2097
|
+
</Button>
|
|
2098
|
+
<Button
|
|
2099
|
+
type="button"
|
|
2100
|
+
data-slot="fields-configurator-confirm"
|
|
2101
|
+
variant="primary"
|
|
2102
|
+
@click="confirmChanges"
|
|
2103
|
+
>
|
|
2104
|
+
<Icon icon="fluent:checkmark-20-regular" />
|
|
2105
|
+
{{ t("confirm") }}
|
|
2106
|
+
</Button>
|
|
2107
|
+
</div>
|
|
1639
2108
|
</DialogFooter>
|
|
1640
2109
|
</DialogContent>
|
|
1641
2110
|
</Dialog>
|
|
@@ -1645,7 +2114,7 @@ function confirmChanges() {
|
|
|
1645
2114
|
{
|
|
1646
2115
|
"zh": {
|
|
1647
2116
|
"configure-fields": "配置字段",
|
|
1648
|
-
"configure-fields-description": "
|
|
2117
|
+
"configure-fields-description": "在这里管理字段列表,并编辑当前选中的通用项或字段配置。",
|
|
1649
2118
|
"field-list": "字段列表",
|
|
1650
2119
|
"general": "通用",
|
|
1651
2120
|
"general-description": "在这里配置字段集合级别的公共选项。",
|
|
@@ -1656,17 +2125,38 @@ function confirmChanges() {
|
|
|
1656
2125
|
"fields-orientation-horizontal": "水平",
|
|
1657
2126
|
"fields-orientation-vertical": "垂直",
|
|
1658
2127
|
"fields-orientation-floating": "浮动标签",
|
|
2128
|
+
"paste-config": "粘贴配置",
|
|
2129
|
+
"paste-config-invalid-json": "粘贴失败,剪贴板不是有效的 JSON。",
|
|
2130
|
+
"paste-config-invalid-schema": "粘贴失败,配置格式无效。",
|
|
2131
|
+
"paste-config-read-failed": "读取剪贴板失败,请检查剪贴板权限。",
|
|
2132
|
+
"copy-paste-error": "复制错误",
|
|
2133
|
+
"copy-paste-error-failed": "复制错误详情失败,请检查剪贴板权限。",
|
|
2134
|
+
"copy-markdown": "复制为 Markdown",
|
|
2135
|
+
"copy-markdown-failed": "复制 Markdown 失败,请先修正当前配置中的错误。",
|
|
2136
|
+
"copy-config": "仅复制配置",
|
|
2137
|
+
"copy-config-failed": "复制配置失败,请先修正当前配置中的错误。",
|
|
2138
|
+
"search-fields": "搜索字段名称……",
|
|
1659
2139
|
"add-field": "新增字段",
|
|
1660
2140
|
"field-type": "字段类型",
|
|
1661
2141
|
"field-type-string": "文本",
|
|
2142
|
+
"field-type-textarea": "多行文本",
|
|
1662
2143
|
"field-type-number": "数字",
|
|
1663
2144
|
"field-type-select": "选择",
|
|
1664
2145
|
"field-type-calendar": "日期",
|
|
2146
|
+
"field-type-empty": "空白",
|
|
2147
|
+
"field-type-slot": "插槽",
|
|
2148
|
+
"field-id": "字段 ID",
|
|
2149
|
+
"field-id-duplicate": "字段 ID 不能重复",
|
|
1665
2150
|
"field-path": "字段路径",
|
|
1666
2151
|
"field-path-placeholder": "例如 profile.name",
|
|
1667
2152
|
"field-path-required": "字段路径不能为空",
|
|
1668
2153
|
"field-path-duplicate": "字段路径不能重复",
|
|
2154
|
+
"copy-field-id": "复制字段 ID:{field}",
|
|
2155
|
+
"copy-field-id-short": "复制 ID",
|
|
2156
|
+
"copy-field-id-failed": "复制字段 ID 失败,请检查剪贴板权限。",
|
|
1669
2157
|
"field-label": "字段标题",
|
|
2158
|
+
"field-required": "显示必填提示",
|
|
2159
|
+
"field-required-description": "开启后仅在标签后显示红色星号,不会自动添加校验规则。",
|
|
1670
2160
|
"field-icon": "图标",
|
|
1671
2161
|
"field-icon-placeholder": "例如 fluent:person-20-regular",
|
|
1672
2162
|
"field-style": "样式表达式",
|
|
@@ -1712,8 +2202,11 @@ function confirmChanges() {
|
|
|
1712
2202
|
"validation-expression-placeholder": "返回 false 时展示下面的消息",
|
|
1713
2203
|
"validation-message": "失败消息",
|
|
1714
2204
|
"validation-message-placeholder": "支持 Markdown 与双花括号表达式",
|
|
2205
|
+
"no-matches": "没有匹配的字段。",
|
|
1715
2206
|
"no-validation-rules": "暂未配置校验规则。",
|
|
1716
2207
|
"unnamed-field": "未命名{type}字段",
|
|
2208
|
+
"empty-field": "空白 {id}",
|
|
2209
|
+
"slot-field": "插槽 {id}",
|
|
1717
2210
|
"no-fields": "还没有字段。",
|
|
1718
2211
|
"drag-field": "拖拽调整字段顺序:{field}",
|
|
1719
2212
|
"delete-field": "删除字段:{field}",
|
|
@@ -1722,7 +2215,7 @@ function confirmChanges() {
|
|
|
1722
2215
|
},
|
|
1723
2216
|
"ja": {
|
|
1724
2217
|
"configure-fields": "フィールドを設定",
|
|
1725
|
-
"configure-fields-description": "
|
|
2218
|
+
"configure-fields-description": "ここではフィールド一覧を管理し、選択中の共通設定またはフィールド設定を編集できます。",
|
|
1726
2219
|
"field-list": "フィールド一覧",
|
|
1727
2220
|
"general": "共通",
|
|
1728
2221
|
"general-description": "ここではフィールド群全体に適用される共通設定を編集できます。",
|
|
@@ -1733,17 +2226,38 @@ function confirmChanges() {
|
|
|
1733
2226
|
"fields-orientation-horizontal": "横並び",
|
|
1734
2227
|
"fields-orientation-vertical": "縦並び",
|
|
1735
2228
|
"fields-orientation-floating": "フローティングラベル",
|
|
2229
|
+
"paste-config": "設定を貼り付け",
|
|
2230
|
+
"paste-config-invalid-json": "貼り付けに失敗しました。クリップボードの内容が有効な JSON ではありません。",
|
|
2231
|
+
"paste-config-invalid-schema": "貼り付けに失敗しました。設定形式が無効です。",
|
|
2232
|
+
"paste-config-read-failed": "クリップボードの読み取りに失敗しました。権限を確認してください。",
|
|
2233
|
+
"copy-paste-error": "エラーをコピー",
|
|
2234
|
+
"copy-paste-error-failed": "エラー詳細のコピーに失敗しました。クリップボード権限を確認してください。",
|
|
2235
|
+
"copy-markdown": "Markdown としてコピー",
|
|
2236
|
+
"copy-markdown-failed": "Markdown のコピーに失敗しました。現在の設定エラーを先に修正してください。",
|
|
2237
|
+
"copy-config": "設定のみコピー",
|
|
2238
|
+
"copy-config-failed": "設定のコピーに失敗しました。現在の設定エラーを先に修正してください。",
|
|
2239
|
+
"search-fields": "フィールド名を検索…",
|
|
1736
2240
|
"add-field": "フィールドを追加",
|
|
1737
2241
|
"field-type": "フィールド種別",
|
|
1738
2242
|
"field-type-string": "テキスト",
|
|
2243
|
+
"field-type-textarea": "複数行テキスト",
|
|
1739
2244
|
"field-type-number": "数値",
|
|
1740
2245
|
"field-type-select": "選択",
|
|
1741
2246
|
"field-type-calendar": "日付",
|
|
2247
|
+
"field-type-empty": "空白",
|
|
2248
|
+
"field-type-slot": "スロット",
|
|
2249
|
+
"field-id": "フィールド ID",
|
|
2250
|
+
"field-id-duplicate": "フィールド ID は重複できません",
|
|
1742
2251
|
"field-path": "フィールドパス",
|
|
1743
2252
|
"field-path-placeholder": "例: profile.name",
|
|
1744
2253
|
"field-path-required": "フィールドパスは必須です",
|
|
1745
2254
|
"field-path-duplicate": "フィールドパスは重複できません",
|
|
2255
|
+
"copy-field-id": "{field} のフィールド ID をコピー",
|
|
2256
|
+
"copy-field-id-short": "ID をコピー",
|
|
2257
|
+
"copy-field-id-failed": "フィールド ID のコピーに失敗しました。クリップボード権限を確認してください。",
|
|
1746
2258
|
"field-label": "フィールドラベル",
|
|
2259
|
+
"field-required": "必須ヒントを表示",
|
|
2260
|
+
"field-required-description": "有効にするとラベルの後ろに赤い星印だけを表示し、検証ルールは自動追加しません。",
|
|
1747
2261
|
"field-icon": "アイコン",
|
|
1748
2262
|
"field-icon-placeholder": "例: fluent:person-20-regular",
|
|
1749
2263
|
"field-style": "スタイル式",
|
|
@@ -1789,8 +2303,11 @@ function confirmChanges() {
|
|
|
1789
2303
|
"validation-expression-placeholder": "false を返すと下のメッセージを表示します",
|
|
1790
2304
|
"validation-message": "失敗メッセージ",
|
|
1791
2305
|
"validation-message-placeholder": "Markdown と二重波括弧式を利用できます",
|
|
2306
|
+
"no-matches": "一致するフィールドがありません。",
|
|
1792
2307
|
"no-validation-rules": "検証ルールはまだありません。",
|
|
1793
2308
|
"unnamed-field": "未命名の{type}フィールド",
|
|
2309
|
+
"empty-field": "空白 {id}",
|
|
2310
|
+
"slot-field": "スロット {id}",
|
|
1794
2311
|
"no-fields": "フィールドがありません。",
|
|
1795
2312
|
"drag-field": "{field} の順序をドラッグで変更",
|
|
1796
2313
|
"delete-field": "{field} を削除",
|
|
@@ -1799,7 +2316,7 @@ function confirmChanges() {
|
|
|
1799
2316
|
},
|
|
1800
2317
|
"en": {
|
|
1801
2318
|
"configure-fields": "Configure Fields",
|
|
1802
|
-
"configure-fields-description": "
|
|
2319
|
+
"configure-fields-description": "Manage the field list and edit the selected general or field settings here.",
|
|
1803
2320
|
"field-list": "Field list",
|
|
1804
2321
|
"general": "General",
|
|
1805
2322
|
"general-description": "Edit the shared settings that apply to the whole field group here.",
|
|
@@ -1810,17 +2327,38 @@ function confirmChanges() {
|
|
|
1810
2327
|
"fields-orientation-horizontal": "Horizontal",
|
|
1811
2328
|
"fields-orientation-vertical": "Vertical",
|
|
1812
2329
|
"fields-orientation-floating": "Floating label",
|
|
2330
|
+
"paste-config": "Paste Config",
|
|
2331
|
+
"paste-config-invalid-json": "Paste failed because the clipboard does not contain valid JSON.",
|
|
2332
|
+
"paste-config-invalid-schema": "Paste failed because the config shape is invalid.",
|
|
2333
|
+
"paste-config-read-failed": "Failed to read from the clipboard. Check clipboard permissions.",
|
|
2334
|
+
"copy-paste-error": "Copy Error",
|
|
2335
|
+
"copy-paste-error-failed": "Failed to copy the error details. Check clipboard permissions.",
|
|
2336
|
+
"copy-markdown": "Copy as Markdown",
|
|
2337
|
+
"copy-markdown-failed": "Failed to copy Markdown. Fix the current config errors first.",
|
|
2338
|
+
"copy-config": "Copy Config Only",
|
|
2339
|
+
"copy-config-failed": "Failed to copy the config. Fix the current config errors first.",
|
|
2340
|
+
"search-fields": "Search fields…",
|
|
1813
2341
|
"add-field": "Add field",
|
|
1814
2342
|
"field-type": "Field type",
|
|
1815
2343
|
"field-type-string": "Text",
|
|
2344
|
+
"field-type-textarea": "Textarea",
|
|
1816
2345
|
"field-type-number": "Number",
|
|
1817
2346
|
"field-type-select": "Select",
|
|
1818
2347
|
"field-type-calendar": "Date",
|
|
2348
|
+
"field-type-empty": "Empty",
|
|
2349
|
+
"field-type-slot": "Slot",
|
|
2350
|
+
"field-id": "Field ID",
|
|
2351
|
+
"field-id-duplicate": "Field ID must be unique",
|
|
1819
2352
|
"field-path": "Field path",
|
|
1820
2353
|
"field-path-placeholder": "For example profile.name",
|
|
1821
2354
|
"field-path-required": "Field path is required",
|
|
1822
2355
|
"field-path-duplicate": "Field path must be unique",
|
|
2356
|
+
"copy-field-id": "Copy field ID: {field}",
|
|
2357
|
+
"copy-field-id-short": "Copy ID",
|
|
2358
|
+
"copy-field-id-failed": "Failed to copy the field ID. Check clipboard permissions.",
|
|
1823
2359
|
"field-label": "Field label",
|
|
2360
|
+
"field-required": "Show required hint",
|
|
2361
|
+
"field-required-description": "When enabled, only a red asterisk is shown after the label. No validation rule is added automatically.",
|
|
1824
2362
|
"field-icon": "Icon",
|
|
1825
2363
|
"field-icon-placeholder": "For example fluent:person-20-regular",
|
|
1826
2364
|
"field-style": "Style expression",
|
|
@@ -1866,8 +2404,11 @@ function confirmChanges() {
|
|
|
1866
2404
|
"validation-expression-placeholder": "Return false to show the message below",
|
|
1867
2405
|
"validation-message": "Failure message",
|
|
1868
2406
|
"validation-message-placeholder": "Supports Markdown and double-curly expressions",
|
|
2407
|
+
"no-matches": "No matching fields.",
|
|
1869
2408
|
"no-validation-rules": "No validation rules yet.",
|
|
1870
2409
|
"unnamed-field": "Untitled {type} field",
|
|
2410
|
+
"empty-field": "Empty {id}",
|
|
2411
|
+
"slot-field": "Slot {id}",
|
|
1871
2412
|
"no-fields": "No fields yet.",
|
|
1872
2413
|
"drag-field": "Drag to reorder field {field}",
|
|
1873
2414
|
"delete-field": "Delete field {field}",
|