@shwfed/config 2.3.17 → 2.3.18

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 (41) hide show
  1. package/dist/mcp.mjs +727 -534
  2. package/dist/module.json +1 -1
  3. package/dist/preview/assets/{config-lCt8xg5R.js → config-C8Y4mJbo.js} +1 -1
  4. package/dist/preview/assets/{config-DkE2KJox.js → config-CGjY6-4G.js} +1 -1
  5. package/dist/preview/assets/{config-CDcbWHz1.js → config-CR-ypZys.js} +1 -1
  6. package/dist/preview/assets/{config-D9wKVRfv.js → config-Cs3k3BuR.js} +1 -1
  7. package/dist/preview/assets/{config-DReApS3g.js → config-D9mrL0UY.js} +1 -1
  8. package/dist/preview/assets/{config-CAzy2oY1.js → config-DRy0SpMq.js} +1 -1
  9. package/dist/preview/assets/{config-CAUy5OIr.js → config-eopfzGON.js} +1 -1
  10. package/dist/preview/assets/{config-B_D_DLrg.js → config-qmkDiyXK.js} +1 -1
  11. package/dist/preview/assets/{definition.vue_vue_type_script_setup_true_lang-CvzBdDHI.js → definition.vue_vue_type_script_setup_true_lang-BakGkJ_w.js} +1 -1
  12. package/dist/preview/assets/index-Bsbk3Okp.js +1 -0
  13. package/dist/preview/assets/index-Dz_SnGc9.js +639 -0
  14. package/dist/preview/assets/{runtime-eRS_ur88.js → runtime-B1Lqn7iS.js} +1 -1
  15. package/dist/preview/assets/{runtime-C7iG_6_s.js → runtime-BvJjQaAU.js} +1 -1
  16. package/dist/preview/assets/{runtime-CWwZuZQ7.js → runtime-C-3DIGFz.js} +1 -1
  17. package/dist/preview/assets/{runtime-CNI--nfZ.js → runtime-C2q2Mp8_.js} +1 -1
  18. package/dist/preview/assets/{runtime-Dj39BqNd.js → runtime-CdELhC3y.js} +1 -1
  19. package/dist/preview/assets/{runtime-TUjKxXEj.js → runtime-D8OAMic8.js} +1 -1
  20. package/dist/preview/assets/{runtime-Dg8BS7Rt.js → runtime-DQcKN5I_.js} +1 -1
  21. package/dist/preview/assets/{runtime-CEymLc8o.js → runtime-DQvlwOZl.js} +1 -1
  22. package/dist/preview/index.html +1 -1
  23. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/config.d.vue.ts +107 -0
  24. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/config.vue +493 -0
  25. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/config.vue.d.ts +107 -0
  26. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/runtime.d.vue.ts +8 -0
  27. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/runtime.vue +123 -0
  28. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/runtime.vue.d.ts +8 -0
  29. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/schema.d.ts +79 -0
  30. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/schema.js +78 -0
  31. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.d.vue.ts +125 -0
  32. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.vue +645 -0
  33. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.vue.d.ts +125 -0
  34. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/runtime.d.vue.ts +8 -0
  35. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/runtime.vue +155 -0
  36. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/runtime.vue.d.ts +8 -0
  37. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/schema.d.ts +90 -0
  38. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/schema.js +100 -0
  39. package/package.json +1 -1
  40. package/dist/preview/assets/index-DOUzPsqV.js +0 -639
  41. package/dist/preview/assets/index-DUZhCCDY.js +0 -1
@@ -0,0 +1,645 @@
1
+ <script setup>
2
+ import { Icon } from "@iconify/vue";
3
+ import { format as formatDate } from "date-fns";
4
+ import { computed } from "vue";
5
+ import { Button } from "../../../../ui/button";
6
+ import { ExpressionEditor } from "../../../../ui/expression-editor";
7
+ import DerivedValueEditor from "../../../DerivedValueEditor.vue";
8
+ import { Field, FieldLabel } from "../../../../ui/field";
9
+ import { IconPicker } from "../../../../ui/icon-picker";
10
+ import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText } from "../../../../ui/input-group";
11
+ import { Locale as LocaleField } from "../../../../ui/locale";
12
+ import { Markdown } from "../../../../ui/markdown";
13
+ import {
14
+ Select,
15
+ SelectContent,
16
+ SelectItem,
17
+ SelectTrigger,
18
+ SelectValue
19
+ } from "../../../../ui/select";
20
+ import { getStructFieldDescription, getStructFieldTitle } from "../../../schema";
21
+ import { DEFAULT_FIELD_ORIENTATION, FIELD_ORIENTATION_OPTIONS } from "../../../utils/common";
22
+ import { presetSchema, schema } from "./schema";
23
+ defineOptions({ name: "ShwfedMonthRangeFieldConfig" });
24
+ const value = defineModel({ type: null, ...{ required: true } });
25
+ const fieldSchema = schema(() => {
26
+ });
27
+ const fieldTitle = (f) => getStructFieldTitle(fieldSchema, f) ?? f;
28
+ const fieldDescription = (f) => getStructFieldDescription(fieldSchema, f);
29
+ const presetFieldSchema = presetSchema(() => {
30
+ });
31
+ const presetFieldTitle = (f) => getStructFieldTitle(presetFieldSchema, f) ?? f;
32
+ const presetFieldDescription = (f) => getStructFieldDescription(presetFieldSchema, f);
33
+ const presets = computed(() => value.value.presets ?? []);
34
+ function newId() {
35
+ return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
36
+ const r = Math.random() * 16 | 0;
37
+ const v = c === "x" ? r : r & 3 | 8;
38
+ return v.toString(16);
39
+ });
40
+ }
41
+ function newPreset() {
42
+ return {
43
+ id: newId(),
44
+ label: [{ locale: "zh", message: "" }],
45
+ start: "",
46
+ end: ""
47
+ };
48
+ }
49
+ function patchPreset(index, patch) {
50
+ const next = [...value.value.presets ?? []];
51
+ const cur = next[index];
52
+ if (!cur) return;
53
+ next[index] = { ...cur, ...patch };
54
+ value.value = { ...value.value, presets: next };
55
+ }
56
+ function addPreset() {
57
+ value.value = { ...value.value, presets: [...value.value.presets ?? [], newPreset()] };
58
+ }
59
+ function removePreset(index) {
60
+ const next = [...value.value.presets ?? []];
61
+ next.splice(index, 1);
62
+ value.value = { ...value.value, presets: next };
63
+ }
64
+ function setPresetLabel(index, label) {
65
+ patchPreset(index, { label });
66
+ }
67
+ function setPresetStart(index, expr) {
68
+ patchPreset(index, { start: expr });
69
+ }
70
+ function setPresetEnd(index, expr) {
71
+ patchPreset(index, { end: expr });
72
+ }
73
+ const bindingMode = computed(
74
+ () => Array.isArray(value.value.binding) ? "split" : "single"
75
+ );
76
+ const singlePathText = computed({
77
+ get: () => typeof value.value.binding === "string" ? value.value.binding : "",
78
+ set: (next) => {
79
+ const trimmed = next.trim();
80
+ if (trimmed.length === 0) {
81
+ const { binding: _omit, ...rest } = value.value;
82
+ value.value = rest;
83
+ } else {
84
+ value.value = { ...value.value, binding: trimmed };
85
+ }
86
+ }
87
+ });
88
+ const splitStartText = computed(
89
+ () => Array.isArray(value.value.binding) ? value.value.binding[0] : ""
90
+ );
91
+ const splitEndText = computed(
92
+ () => Array.isArray(value.value.binding) ? value.value.binding[1] : ""
93
+ );
94
+ function setSplitStart(next) {
95
+ const cur = Array.isArray(value.value.binding) ? value.value.binding : ["", ""];
96
+ value.value = { ...value.value, binding: [next.trim(), cur[1]] };
97
+ }
98
+ function setSplitEnd(next) {
99
+ const cur = Array.isArray(value.value.binding) ? value.value.binding : ["", ""];
100
+ value.value = { ...value.value, binding: [cur[0], next.trim()] };
101
+ }
102
+ function toggleBindingMode() {
103
+ if (bindingMode.value === "single") {
104
+ value.value = { ...value.value, binding: ["", ""] };
105
+ } else {
106
+ const { binding: _omit, ...rest } = value.value;
107
+ value.value = rest;
108
+ }
109
+ }
110
+ const formatText = computed({
111
+ get: () => value.value.format ?? "",
112
+ set: (next) => {
113
+ const trimmed = next.trim();
114
+ if (trimmed.length === 0) {
115
+ const { format: _omit, ...rest } = value.value;
116
+ value.value = rest;
117
+ } else {
118
+ value.value = { ...value.value, format: trimmed };
119
+ }
120
+ }
121
+ });
122
+ const valueFormatText = computed({
123
+ get: () => value.value.valueFormat ?? "",
124
+ set: (next) => {
125
+ const trimmed = next.trim();
126
+ if (trimmed.length === 0) {
127
+ const { valueFormat: _omit, ...rest } = value.value;
128
+ value.value = rest;
129
+ } else {
130
+ value.value = { ...value.value, valueFormat: trimmed };
131
+ }
132
+ }
133
+ });
134
+ function setSeparatorIcon(next) {
135
+ const trimmed = next.trim();
136
+ if (trimmed.length === 0) {
137
+ const { rangeSeparatorIcon: _omit, ...rest } = value.value;
138
+ value.value = rest;
139
+ } else {
140
+ value.value = { ...value.value, rangeSeparatorIcon: trimmed };
141
+ }
142
+ }
143
+ function previewFormat(fmt) {
144
+ if (!fmt) return "";
145
+ try {
146
+ return formatDate(/* @__PURE__ */ new Date(), fmt);
147
+ } catch {
148
+ return "(\u65E0\u6548\u683C\u5F0F)";
149
+ }
150
+ }
151
+ const formatExample = computed(() => previewFormat(value.value.format ?? ""));
152
+ const valueFormatExample = computed(() => previewFormat(value.value.valueFormat ?? ""));
153
+ </script>
154
+
155
+ <template>
156
+ <div class="flex flex-col gap-3">
157
+ <div class="grid grid-cols-2 gap-3">
158
+ <Field orientation="vertical">
159
+ <FieldLabel class="text-xs text-zinc-500">
160
+ <template
161
+ v-if="fieldDescription('displayName')"
162
+ #tooltip
163
+ >
164
+ <Markdown
165
+ :source="fieldDescription('displayName')"
166
+ block
167
+ class="prose prose-sm prose-zinc"
168
+ />
169
+ </template>
170
+ {{ fieldTitle("displayName") }}
171
+ </FieldLabel>
172
+ <InputGroup>
173
+ <InputGroupInput
174
+ :model-value="value.displayName ?? ''"
175
+ placeholder="例:月份范围"
176
+ @update:model-value="(v) => {
177
+ const s = String(v ?? '');
178
+ value = { ...value, displayName: s.length > 0 ? s : void 0 };
179
+ }"
180
+ />
181
+ </InputGroup>
182
+ </Field>
183
+
184
+ <Field orientation="vertical">
185
+ <FieldLabel class="text-xs text-zinc-500">
186
+ <template #tooltip>
187
+ <Markdown
188
+ source="写入表单状态的嵌套键路径,使用 `.` 分隔。单路径模式写入 `[start, end]` 字符串数组;拆分模式分别写入起始与结束两端"
189
+ block
190
+ class="prose prose-sm prose-zinc"
191
+ />
192
+ </template>
193
+ {{ fieldTitle("binding") }}
194
+ </FieldLabel>
195
+ <template v-if="bindingMode === 'single'">
196
+ <InputGroup>
197
+ <InputGroupInput
198
+ v-model="singlePathText"
199
+ placeholder="例:event.months"
200
+ class="font-mono"
201
+ />
202
+ <InputGroupAddon align="inline-end">
203
+ <InputGroupButton
204
+ size="icon-xs"
205
+ aria-label="拆分起止绑定路径"
206
+ @click="toggleBindingMode"
207
+ >
208
+ <Icon icon="fluent:split-horizontal-20-regular" />
209
+ </InputGroupButton>
210
+ </InputGroupAddon>
211
+ </InputGroup>
212
+ </template>
213
+ <template v-else>
214
+ <div class="grid grid-cols-2 gap-2">
215
+ <InputGroup>
216
+ <InputGroupAddon align="inline-start">
217
+ <InputGroupText class="text-xs text-zinc-400">
218
+
219
+ </InputGroupText>
220
+ </InputGroupAddon>
221
+ <InputGroupInput
222
+ :model-value="splitStartText"
223
+ placeholder="例:event.startMonth"
224
+ class="font-mono"
225
+ @update:model-value="(v) => setSplitStart(String(v ?? ''))"
226
+ />
227
+ </InputGroup>
228
+ <InputGroup>
229
+ <InputGroupAddon align="inline-start">
230
+ <InputGroupText class="text-xs text-zinc-400">
231
+
232
+ </InputGroupText>
233
+ </InputGroupAddon>
234
+ <InputGroupInput
235
+ :model-value="splitEndText"
236
+ placeholder="例:event.endMonth"
237
+ class="font-mono"
238
+ @update:model-value="(v) => setSplitEnd(String(v ?? ''))"
239
+ />
240
+ <InputGroupAddon align="inline-end">
241
+ <InputGroupButton
242
+ size="icon-xs"
243
+ aria-label="合并起止绑定路径"
244
+ @click="toggleBindingMode"
245
+ >
246
+ <Icon icon="fluent:merge-vertical-20-regular" />
247
+ </InputGroupButton>
248
+ </InputGroupAddon>
249
+ </InputGroup>
250
+ </div>
251
+ </template>
252
+ </Field>
253
+ </div>
254
+
255
+ <div class="grid grid-cols-2 gap-3">
256
+ <Field orientation="vertical">
257
+ <FieldLabel class="text-xs text-zinc-500">
258
+ <template
259
+ v-if="fieldDescription('label')"
260
+ #tooltip
261
+ >
262
+ <Markdown
263
+ :source="fieldDescription('label')"
264
+ block
265
+ class="prose prose-sm prose-zinc"
266
+ />
267
+ </template>
268
+ {{ fieldTitle("label") }}
269
+ </FieldLabel>
270
+ <LocaleField
271
+ :model-value="value.label"
272
+ @update:model-value="(v) => value = { ...value, label: v }"
273
+ />
274
+ </Field>
275
+
276
+ <Field orientation="vertical">
277
+ <FieldLabel class="text-xs text-zinc-500">
278
+ <template
279
+ v-if="fieldDescription('tooltip')"
280
+ #tooltip
281
+ >
282
+ <Markdown
283
+ :source="fieldDescription('tooltip')"
284
+ block
285
+ class="prose prose-sm prose-zinc"
286
+ />
287
+ </template>
288
+ {{ fieldTitle("tooltip") }}
289
+ </FieldLabel>
290
+ <LocaleField
291
+ markdown
292
+ :model-value="value.tooltip"
293
+ @update:model-value="(v) => value = { ...value, tooltip: v }"
294
+ />
295
+ </Field>
296
+ </div>
297
+
298
+ <div class="grid grid-cols-2 gap-3">
299
+ <Field orientation="vertical">
300
+ <FieldLabel class="text-xs text-zinc-500">
301
+ <template
302
+ v-if="fieldDescription('startPlaceholder')"
303
+ #tooltip
304
+ >
305
+ <Markdown
306
+ :source="fieldDescription('startPlaceholder')"
307
+ block
308
+ class="prose prose-sm prose-zinc"
309
+ />
310
+ </template>
311
+ {{ fieldTitle("startPlaceholder") }}
312
+ </FieldLabel>
313
+ <LocaleField
314
+ :model-value="value.startPlaceholder"
315
+ @update:model-value="(v) => value = { ...value, startPlaceholder: v }"
316
+ />
317
+ </Field>
318
+
319
+ <Field orientation="vertical">
320
+ <FieldLabel class="text-xs text-zinc-500">
321
+ <template
322
+ v-if="fieldDescription('endPlaceholder')"
323
+ #tooltip
324
+ >
325
+ <Markdown
326
+ :source="fieldDescription('endPlaceholder')"
327
+ block
328
+ class="prose prose-sm prose-zinc"
329
+ />
330
+ </template>
331
+ {{ fieldTitle("endPlaceholder") }}
332
+ </FieldLabel>
333
+ <LocaleField
334
+ :model-value="value.endPlaceholder"
335
+ @update:model-value="(v) => value = { ...value, endPlaceholder: v }"
336
+ />
337
+ </Field>
338
+ </div>
339
+
340
+ <div class="grid grid-cols-3 gap-3">
341
+ <Field orientation="vertical">
342
+ <FieldLabel class="text-xs text-zinc-500">
343
+ <template #tooltip>
344
+ <Markdown
345
+ :source="fieldDescription('format') ?? '\u8F93\u5165\u6846\u4E2D\u5C55\u793A\u6708\u4EFD\u7684 `date-fns` \u683C\u5F0F\u4E32'"
346
+ block
347
+ class="prose prose-sm prose-zinc"
348
+ />
349
+ </template>
350
+ {{ fieldTitle("format") }}
351
+ </FieldLabel>
352
+ <InputGroup>
353
+ <InputGroupInput
354
+ v-model="formatText"
355
+ placeholder="例:yyyy-MM"
356
+ class="font-mono"
357
+ />
358
+ <InputGroupAddon
359
+ v-if="formatExample"
360
+ align="inline-end"
361
+ >
362
+ <InputGroupText class="text-xs text-zinc-400">
363
+ {{ formatExample }}
364
+ </InputGroupText>
365
+ </InputGroupAddon>
366
+ </InputGroup>
367
+ </Field>
368
+
369
+ <Field orientation="vertical">
370
+ <FieldLabel class="text-xs text-zinc-500">
371
+ <template #tooltip>
372
+ <Markdown
373
+ :source="fieldDescription('valueFormat') ?? '\u5199\u5165\u8868\u5355\u72B6\u6001\u7684 `date-fns` \u683C\u5F0F\u4E32'"
374
+ block
375
+ class="prose prose-sm prose-zinc"
376
+ />
377
+ </template>
378
+ {{ fieldTitle("valueFormat") }}
379
+ </FieldLabel>
380
+ <InputGroup>
381
+ <InputGroupInput
382
+ v-model="valueFormatText"
383
+ placeholder="例:yyyy-MM"
384
+ class="font-mono"
385
+ />
386
+ <InputGroupAddon
387
+ v-if="valueFormatExample"
388
+ align="inline-end"
389
+ >
390
+ <InputGroupText class="text-xs text-zinc-400">
391
+ {{ valueFormatExample }}
392
+ </InputGroupText>
393
+ </InputGroupAddon>
394
+ </InputGroup>
395
+ </Field>
396
+ </div>
397
+
398
+ <div class="grid grid-cols-3 gap-3">
399
+ <Field orientation="vertical">
400
+ <FieldLabel class="text-xs text-zinc-500">
401
+ <template
402
+ v-if="fieldDescription('orientation')"
403
+ #tooltip
404
+ >
405
+ <Markdown
406
+ :source="fieldDescription('orientation')"
407
+ block
408
+ class="prose prose-sm prose-zinc"
409
+ />
410
+ </template>
411
+ {{ fieldTitle("orientation") }}
412
+ </FieldLabel>
413
+ <Select
414
+ :model-value="value.orientation ?? DEFAULT_FIELD_ORIENTATION"
415
+ @update:model-value="(v) => value = { ...value, orientation: v }"
416
+ >
417
+ <SelectTrigger class="w-full">
418
+ <SelectValue />
419
+ </SelectTrigger>
420
+ <SelectContent>
421
+ <SelectItem
422
+ v-for="opt in FIELD_ORIENTATION_OPTIONS"
423
+ :key="opt.value"
424
+ :value="opt.value"
425
+ >
426
+ {{ opt.label }}
427
+ </SelectItem>
428
+ </SelectContent>
429
+ </Select>
430
+ </Field>
431
+
432
+ <Field orientation="vertical">
433
+ <FieldLabel class="text-xs text-zinc-500">
434
+ <template #tooltip>
435
+ <Markdown
436
+ :source="fieldDescription('rangeSeparatorIcon') ?? '\u8303\u56F4\u4E24\u7AEF\u4E4B\u95F4\u5206\u9694\u56FE\u6807\u7684 Iconify \u540D\u79F0'"
437
+ block
438
+ class="prose prose-sm prose-zinc"
439
+ />
440
+ </template>
441
+ {{ fieldTitle("rangeSeparatorIcon") }}
442
+ </FieldLabel>
443
+ <IconPicker
444
+ :model-value="value.rangeSeparatorIcon ?? ''"
445
+ placeholder="例:lucide:arrow-right"
446
+ @update:model-value="setSeparatorIcon"
447
+ />
448
+ </Field>
449
+ </div>
450
+
451
+ <Field orientation="vertical">
452
+ <FieldLabel class="text-xs text-zinc-500">
453
+ <template
454
+ v-if="fieldDescription('presets')"
455
+ #tooltip
456
+ >
457
+ <Markdown
458
+ :source="fieldDescription('presets')"
459
+ block
460
+ class="prose prose-sm prose-zinc"
461
+ />
462
+ </template>
463
+ {{ fieldTitle("presets") }}
464
+ </FieldLabel>
465
+
466
+ <div class="flex flex-col gap-3">
467
+ <div
468
+ v-for="(preset, index) in presets"
469
+ :key="preset.id"
470
+ data-slot="monthrange-field-preset"
471
+ class="relative pt-8 grid grid-cols-3 gap-3 rounded border border-zinc-200 bg-zinc-50/40 p-3"
472
+ >
473
+ <InputGroupButton
474
+ variant="destructive"
475
+ size="icon-xs"
476
+ data-slot="monthrange-field-preset-delete"
477
+ aria-label="删除预设"
478
+ class="absolute right-2 top-2 z-10"
479
+ @click="removePreset(index)"
480
+ >
481
+ <Icon icon="fluent:delete-20-regular" />
482
+ </InputGroupButton>
483
+
484
+ <Field orientation="vertical">
485
+ <FieldLabel class="text-xs text-zinc-500">
486
+ <template
487
+ v-if="presetFieldDescription('label')"
488
+ #tooltip
489
+ >
490
+ <Markdown
491
+ :source="presetFieldDescription('label')"
492
+ block
493
+ class="prose prose-sm prose-zinc"
494
+ />
495
+ </template>
496
+ {{ presetFieldTitle("label") }}
497
+ </FieldLabel>
498
+ <LocaleField
499
+ translate-hint="month range field preset label"
500
+ :model-value="preset.label"
501
+ @update:model-value="(v) => setPresetLabel(index, v)"
502
+ />
503
+ </Field>
504
+
505
+ <Field orientation="vertical">
506
+ <FieldLabel class="text-xs text-zinc-500">
507
+ <template
508
+ v-if="presetFieldDescription('start')"
509
+ #tooltip
510
+ >
511
+ <Markdown
512
+ :source="presetFieldDescription('start')"
513
+ block
514
+ class="prose prose-sm prose-zinc"
515
+ />
516
+ </template>
517
+ {{ presetFieldTitle("start") }}
518
+ </FieldLabel>
519
+ <ExpressionEditor
520
+ :model-value="preset.start"
521
+ placeholder="例:now.offset(-3, &quot;months&quot;)"
522
+ result-type="Date"
523
+ class="min-h-10"
524
+ @update:model-value="(v) => setPresetStart(index, v)"
525
+ />
526
+ </Field>
527
+
528
+ <Field orientation="vertical">
529
+ <FieldLabel class="text-xs text-zinc-500">
530
+ <template
531
+ v-if="presetFieldDescription('end')"
532
+ #tooltip
533
+ >
534
+ <Markdown
535
+ :source="presetFieldDescription('end')"
536
+ block
537
+ class="prose prose-sm prose-zinc"
538
+ />
539
+ </template>
540
+ {{ presetFieldTitle("end") }}
541
+ </FieldLabel>
542
+ <ExpressionEditor
543
+ :model-value="preset.end"
544
+ placeholder="例:now"
545
+ result-type="Date"
546
+ class="min-h-10"
547
+ @update:model-value="(v) => setPresetEnd(index, v)"
548
+ />
549
+ </Field>
550
+ </div>
551
+
552
+ <Button
553
+ type="button"
554
+ data-slot="monthrange-field-add-preset"
555
+ class="w-full justify-center"
556
+ @click="addPreset"
557
+ >
558
+ <Icon icon="fluent:add-20-regular" />
559
+ <span>增加快捷选项</span>
560
+ </Button>
561
+ </div>
562
+ </Field>
563
+
564
+ <div class="grid grid-cols-3 gap-3">
565
+ <Field orientation="vertical">
566
+ <FieldLabel class="text-xs text-zinc-500">
567
+ <template #tooltip>
568
+ <Markdown
569
+ :source="fieldDescription('hidden') ?? '\u8FD4\u56DE `true` \u65F6\u5B57\u6BB5\u5728\u6240\u6709\u5E03\u5C40\u4E2D\u90FD\u4E0D\u6E32\u67D3'"
570
+ block
571
+ class="prose prose-sm prose-zinc"
572
+ />
573
+ </template>
574
+ {{ fieldTitle("hidden") }}
575
+ </FieldLabel>
576
+ <ExpressionEditor
577
+ :model-value="value.hidden ?? ''"
578
+ placeholder="例:form.role == 'guest'"
579
+ result-type="bool"
580
+ class="min-h-10"
581
+ @update:model-value="(v) => value = { ...value, hidden: v.length > 0 ? v : void 0 }"
582
+ />
583
+ </Field>
584
+
585
+ <Field orientation="vertical">
586
+ <FieldLabel class="text-xs text-zinc-500">
587
+ <template #tooltip>
588
+ <Markdown
589
+ :source="fieldDescription('disabled') ?? '\u8FD4\u56DE `true` \u65F6\u8F93\u5165\u6846\u4ECD\u7136\u6E32\u67D3\u4F46\u4E0D\u53EF\u7F16\u8F91'"
590
+ block
591
+ class="prose prose-sm prose-zinc"
592
+ />
593
+ </template>
594
+ {{ fieldTitle("disabled") }}
595
+ </FieldLabel>
596
+ <ExpressionEditor
597
+ :model-value="value.disabled ?? ''"
598
+ placeholder="例:form.status == 'locked'"
599
+ result-type="bool"
600
+ class="min-h-10"
601
+ @update:model-value="(v) => value = { ...value, disabled: v.length > 0 ? v : void 0 }"
602
+ />
603
+ </Field>
604
+
605
+ <Field orientation="vertical">
606
+ <FieldLabel class="text-xs text-zinc-500">
607
+ <template #tooltip>
608
+ <Markdown
609
+ :source="fieldDescription('readonly') ?? '\u8FD4\u56DE `true` \u65F6\u4EC5\u4EE5\u7EAF\u6587\u672C\u5C55\u793A\u5F53\u524D\u503C'"
610
+ block
611
+ class="prose prose-sm prose-zinc"
612
+ />
613
+ </template>
614
+ {{ fieldTitle("readonly") }}
615
+ </FieldLabel>
616
+ <ExpressionEditor
617
+ :model-value="value.readonly ?? ''"
618
+ placeholder="例:form.id != null"
619
+ result-type="bool"
620
+ class="min-h-10"
621
+ @update:model-value="(v) => value = { ...value, readonly: v.length > 0 ? v : void 0 }"
622
+ />
623
+ </Field>
624
+ </div>
625
+
626
+ <Field orientation="vertical">
627
+ <FieldLabel class="text-xs text-zinc-500">
628
+ <template #tooltip>
629
+ <Markdown
630
+ :source="fieldDescription('derived')"
631
+ block
632
+ class="prose prose-sm prose-zinc"
633
+ />
634
+ </template>
635
+ {{ fieldTitle("derived") }}
636
+ </FieldLabel>
637
+ <DerivedValueEditor
638
+ :model-value="value.derived"
639
+ result-type="dyn"
640
+ placeholder="例:[form.from, form.to]"
641
+ @update:model-value="(v) => value = { ...value, derived: v }"
642
+ />
643
+ </Field>
644
+ </div>
645
+ </template>