@shwfed/config 2.10.7 → 2.10.9

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 (84) hide show
  1. package/dist/mcp.mjs +1171 -1049
  2. package/dist/module.json +1 -1
  3. package/dist/preview/assets/{FieldGroup.vue_vue_type_script_setup_true_lang-CV3u1wuG.js → FieldGroup.vue_vue_type_script_setup_true_lang-CysVgnB5.js} +1 -1
  4. package/dist/preview/assets/{badge-De7TI8Ef.js → badge-DdxT1J-1.js} +1 -1
  5. package/dist/preview/assets/{config-A0pwpJhu.js → config-073z__NM.js} +1 -1
  6. package/dist/preview/assets/{config-Cc-RiKc0.js → config-91HQEKhM.js} +1 -1
  7. package/dist/preview/assets/{config-DccYHX13.js → config-B8k8y1J1.js} +1 -1
  8. package/dist/preview/assets/{config-Ckn8NixF.js → config-BK8WZ3pm.js} +1 -1
  9. package/dist/preview/assets/{config-RuqJAgHJ.js → config-BX05eMo3.js} +1 -1
  10. package/dist/preview/assets/{config-BF4I0Pko.js → config-BXP8aJwW.js} +1 -1
  11. package/dist/preview/assets/{config-DqHlKqr-.js → config-BhPAyftm.js} +1 -1
  12. package/dist/preview/assets/{config-DBrYVSus.js → config-BzHBSkht.js} +1 -1
  13. package/dist/preview/assets/{config-BJL5R6TO.js → config-C3eoRHUo.js} +1 -1
  14. package/dist/preview/assets/{config-DGj2Xh1R.js → config-CJ-NzxkH.js} +1 -1
  15. package/dist/preview/assets/{config-C6AzmvwL.js → config-CYRj2ZVL.js} +1 -1
  16. package/dist/preview/assets/{config-CtW_DgDv.js → config-DLNjjMZY.js} +1 -1
  17. package/dist/preview/assets/{config-Bei-4M8U.js → config-DudxzmzF.js} +1 -1
  18. package/dist/preview/assets/{config-CnPPWHxs.js → config-cwlK3BMD.js} +1 -1
  19. package/dist/preview/assets/{definition.vue_vue_type_script_setup_true_lang-Ceh2rttd.js → definition.vue_vue_type_script_setup_true_lang-l59C6Dgs.js} +1 -1
  20. package/dist/preview/assets/index-BL58N7_5.js +763 -0
  21. package/dist/preview/assets/index-CA4hsfRv.js +1 -0
  22. package/dist/preview/assets/index-CyVzfRGQ.css +1 -0
  23. package/dist/preview/assets/{index-DAjdcrqQ.js → index-uAwC9Itb.js} +1 -1
  24. package/dist/preview/assets/{item-BNOUeB-e.js → item-C79mwh5T.js} +1 -1
  25. package/dist/preview/assets/{runtime-DzDI7B5M.js → runtime-1_EaFcPI.js} +1 -1
  26. package/dist/preview/assets/{runtime-BtDC6qh0.js → runtime-2yzxOLnF.js} +1 -1
  27. package/dist/preview/assets/{runtime-CXSBPV7c.js → runtime-8Fe67mkZ.js} +1 -1
  28. package/dist/preview/assets/{runtime-B-m0fZl0.js → runtime-8oME8Env.js} +1 -1
  29. package/dist/preview/assets/{runtime-DDz8U0kg.js → runtime-CCMixJjP.js} +1 -1
  30. package/dist/preview/assets/{runtime-CdrOV1KF.js → runtime-CD1Q4rZ3.js} +1 -1
  31. package/dist/preview/assets/{runtime-DCyGTrMp.js → runtime-Cb-qIo5p.js} +1 -1
  32. package/dist/preview/assets/{runtime-Bb0APo4d.js → runtime-CvgAq3mp.js} +1 -1
  33. package/dist/preview/assets/{runtime-D2Qtb13C.js → runtime-DkWCpjKe.js} +1 -1
  34. package/dist/preview/assets/{runtime-CtG2gR_k.js → runtime-mYNyG7UH.js} +1 -1
  35. package/dist/preview/assets/{schema-meta-DmDYh6aL.js → schema-meta-BlTKRs15.js} +1 -1
  36. package/dist/preview/index.html +2 -2
  37. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.d.vue.ts +4 -4
  38. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.vue.d.ts +4 -4
  39. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.d.vue.ts +6 -6
  40. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.vue.d.ts +6 -6
  41. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.d.vue.ts +2 -2
  42. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.vue.d.ts +2 -2
  43. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/config.d.vue.ts +2 -0
  44. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/config.vue +32 -1
  45. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/config.vue.d.ts +2 -0
  46. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/runtime.vue +2 -0
  47. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/schema.d.ts +2 -0
  48. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/schema.js +4 -0
  49. package/dist/runtime/components/form/fields/2026-05-17/com.shwfed.form.field.checkbox.group/config.vue +202 -112
  50. package/dist/runtime/components/form/fields/2026-05-17/com.shwfed.form.field.radio.group/config.vue +202 -112
  51. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.d.vue.ts +4 -4
  52. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.vue.d.ts +4 -4
  53. package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.multi/config.vue +223 -132
  54. package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.single/config.vue +223 -132
  55. package/dist/runtime/components/form/fields/2026-06-20/com.shwfed.form.field.tabs/config.d.vue.ts +59 -0
  56. package/dist/runtime/components/form/fields/2026-06-20/com.shwfed.form.field.tabs/config.vue +345 -0
  57. package/dist/runtime/components/form/fields/2026-06-20/com.shwfed.form.field.tabs/config.vue.d.ts +59 -0
  58. package/dist/runtime/components/form/fields/2026-06-20/com.shwfed.form.field.tabs/runtime.d.vue.ts +8 -0
  59. package/dist/runtime/components/form/fields/2026-06-20/com.shwfed.form.field.tabs/runtime.vue +113 -0
  60. package/dist/runtime/components/form/fields/2026-06-20/com.shwfed.form.field.tabs/runtime.vue.d.ts +8 -0
  61. package/dist/runtime/components/form/fields/2026-06-20/com.shwfed.form.field.tabs/schema.d.ts +79 -0
  62. package/dist/runtime/components/form/fields/2026-06-20/com.shwfed.form.field.tabs/schema.js +86 -0
  63. package/dist/runtime/components/form/unit-config.d.vue.ts +27 -0
  64. package/dist/runtime/components/form/unit-config.vue +117 -73
  65. package/dist/runtime/components/form/unit-config.vue.d.ts +27 -0
  66. package/dist/runtime/components/table/schema.js +24 -25
  67. package/dist/runtime/components/ui/date-picker/DatePickerInput.d.vue.ts +1 -1
  68. package/dist/runtime/components/ui/date-picker/DatePickerInput.vue.d.ts +1 -1
  69. package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.d.vue.ts +1 -1
  70. package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.vue.d.ts +1 -1
  71. package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.d.vue.ts +1 -1
  72. package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.vue.d.ts +1 -1
  73. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.d.vue.ts +2 -2
  74. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.vue.d.ts +2 -2
  75. package/dist/runtime/components/ui/markdown/Markdown.vue +19 -16
  76. package/dist/runtime/utils/markdown.d.ts +5 -2
  77. package/dist/runtime/utils/markdown.js +22 -1
  78. package/dist/runtime/vendor/cel-js/CLAUDE.md +1 -0
  79. package/dist/runtime/vendor/cel-js/lib/evaluator.d.ts +1 -0
  80. package/dist/runtime/vendor/cel-js/lib/evaluator.js +4 -0
  81. package/package.json +1 -1
  82. package/dist/preview/assets/index-BQIP6UfB.js +0 -763
  83. package/dist/preview/assets/index-Cj7bRG7B.css +0 -1
  84. package/dist/preview/assets/index-DpLMRp28.js +0 -1
@@ -0,0 +1,345 @@
1
+ <script setup>
2
+ import { Icon } from "@iconify/vue";
3
+ import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from "vue";
4
+ import { useI18n } from "vue-i18n";
5
+ import { reidItems } from "../../../../../share/clipboard";
6
+ import { getLocalizedText } from "../../../../../share/locale";
7
+ import {
8
+ useTreeDnd
9
+ } from "../../../../../composables/useTreeDnd";
10
+ import { Button } from "../../../../ui/button";
11
+ import { Field, FieldLabel } from "../../../../ui/field";
12
+ import { InputGroup, InputGroupInput } from "../../../../ui/input-group";
13
+ import { Locale as LocaleField } from "../../../../ui/locale";
14
+ import { Markdown } from "../../../../ui/markdown";
15
+ import { Textarea } from "../../../../ui/textarea";
16
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../../../../ui/tooltip";
17
+ import { FORM_FIELD_LAYOUT_KEY } from "../../../field-layout";
18
+ import {
19
+ getStructFieldDescription,
20
+ getStructFieldTitle
21
+ } from "../../../schema";
22
+ import ShwfedFormUnitConfig, {
23
+ } from "../../../unit-config.vue";
24
+ import { schema, TabMeta } from "./schema";
25
+ defineOptions({ name: "ShwfedTabsFieldConfig" });
26
+ const value = defineModel({ type: null, ...{ required: true } });
27
+ const { locale } = useI18n();
28
+ const fieldSchema = schema(() => {
29
+ });
30
+ const fieldTitle = (f) => getStructFieldTitle(fieldSchema, f) ?? f;
31
+ const fieldDescription = (f) => getStructFieldDescription(fieldSchema, f);
32
+ const tabFieldTitle = (f) => getStructFieldTitle(TabMeta, f) ?? f;
33
+ const tabFieldDescription = (f) => getStructFieldDescription(TabMeta, f);
34
+ const layout = inject(FORM_FIELD_LAYOUT_KEY, null);
35
+ onMounted(() => {
36
+ if (layout) layout.fullPane.value = true;
37
+ });
38
+ onBeforeUnmount(() => {
39
+ if (layout) layout.fullPane.value = false;
40
+ });
41
+ const tabs = computed(() => value.value.tabs);
42
+ const activeTabId = ref(tabs.value[0]?.id ?? "");
43
+ function tabIndex(id) {
44
+ return value.value.tabs.findIndex((t) => t.id === id);
45
+ }
46
+ const activeTab = computed(
47
+ () => value.value.tabs.find((t) => t.id === activeTabId.value) ?? null
48
+ );
49
+ function tabLabel(t, index) {
50
+ return getLocalizedText(t.label, locale.value) ?? `\u6807\u7B7E ${index + 1}`;
51
+ }
52
+ watch(tabs, (next) => {
53
+ if (next.length === 0) activeTabId.value = "";
54
+ else if (!next.some((t) => t.id === activeTabId.value)) activeTabId.value = next[0].id;
55
+ }, { flush: "post" });
56
+ const activeTabUnitModel = computed({
57
+ get: () => value.value.tabs.find((t) => t.id === activeTabId.value)?.unit ?? { fields: [], layouts: [{ name: "\u9ED8\u8BA4", layout: { columns: 1, placements: {} } }] },
58
+ set: (next) => {
59
+ const arr = value.value.tabs.map(
60
+ (t) => t.id === activeTabId.value ? { ...t, unit: next } : t
61
+ );
62
+ value.value = { ...value.value, tabs: arr };
63
+ }
64
+ });
65
+ const EXTRAS = [
66
+ { id: "field", label: "\u901A\u7528\u914D\u7F6E", icon: "fluent:settings-20-regular" }
67
+ ];
68
+ const selection = ref({ kind: "layout" });
69
+ function selectTab(id) {
70
+ activeTabId.value = id;
71
+ selection.value = { kind: "layout" };
72
+ }
73
+ function updateTabLabel(id, v) {
74
+ const arr = value.value.tabs.map(
75
+ (t) => t.id === id ? { ...t, label: v } : t
76
+ );
77
+ value.value = { ...value.value, tabs: arr };
78
+ }
79
+ function updateDisplayName(v) {
80
+ const s = v.trim();
81
+ value.value = { ...value.value, displayName: s.length > 0 ? s : void 0 };
82
+ }
83
+ function updateStyle(v) {
84
+ const next = { ...value.value };
85
+ if (v.trim() === "") delete next.style;
86
+ else next.style = v;
87
+ value.value = next;
88
+ }
89
+ function makeId() {
90
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
91
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
92
+ const r = Math.random() * 16 | 0;
93
+ return (c === "x" ? r : r & 3 | 8).toString(16);
94
+ });
95
+ }
96
+ function emptyUnit() {
97
+ return {
98
+ fields: [],
99
+ layouts: [{ name: "\u9ED8\u8BA4", layout: { columns: 1, placements: {} } }]
100
+ };
101
+ }
102
+ function addTab() {
103
+ const id = makeId();
104
+ const index = value.value.tabs.length + 1;
105
+ const next = {
106
+ id,
107
+ label: [{ locale: "zh", message: `\u6807\u7B7E ${index}` }],
108
+ unit: emptyUnit()
109
+ };
110
+ value.value = { ...value.value, tabs: [...value.value.tabs, next] };
111
+ selectTab(id);
112
+ }
113
+ function duplicateTab(id) {
114
+ const i = tabIndex(id);
115
+ if (i < 0) return;
116
+ const src = value.value.tabs[i];
117
+ const dupUnit = reidItems([src.unit])[0];
118
+ const dup = { id: makeId(), label: src.label, unit: dupUnit };
119
+ const arr = [...value.value.tabs];
120
+ arr.splice(i + 1, 0, dup);
121
+ value.value = { ...value.value, tabs: arr };
122
+ selectTab(dup.id);
123
+ }
124
+ function deleteTab(id) {
125
+ if (value.value.tabs.length <= 1) return;
126
+ const i = tabIndex(id);
127
+ if (i < 0) return;
128
+ const arr = value.value.tabs.filter((_, idx) => idx !== i);
129
+ value.value = { ...value.value, tabs: arr };
130
+ if (activeTabId.value === id) {
131
+ selectTab(arr[Math.max(0, i - 1)]?.id ?? "");
132
+ }
133
+ }
134
+ function moveTab(from, to) {
135
+ if (from === to) return;
136
+ if (from < 0 || to < 0) return;
137
+ if (from >= value.value.tabs.length || to >= value.value.tabs.length) return;
138
+ const next = [...value.value.tabs];
139
+ const [item] = next.splice(from, 1);
140
+ if (!item) return;
141
+ next.splice(to, 0, item);
142
+ value.value = { ...value.value, tabs: next };
143
+ }
144
+ const ROW_KIND = "tabs-field-row";
145
+ function rowId(i) {
146
+ return `tab-${i}`;
147
+ }
148
+ function onRowDrop(e) {
149
+ if (e.source.kind !== ROW_KIND || e.target.kind !== ROW_KIND) return;
150
+ const from = value.value.tabs.findIndex((_, i) => rowId(i) === e.source.id);
151
+ const to = value.value.tabs.findIndex((_, i) => rowId(i) === e.target.id);
152
+ if (from < 0 || to < 0) return;
153
+ if (e.instruction === "reorder-above") moveTab(from, from < to ? to - 1 : to);
154
+ else if (e.instruction === "reorder-below") moveTab(from, from <= to ? to : to + 1);
155
+ }
156
+ const dnd = useTreeDnd({ onRowDrop });
157
+ const pickDragHandle = (el) => el.querySelector(".drag-handle");
158
+ function rowConfig(i) {
159
+ return {
160
+ kind: ROW_KIND,
161
+ canDrop: (src) => src.kind === ROW_KIND,
162
+ blockInstructions: (src) => {
163
+ const blocked = ["make-child", "reparent"];
164
+ if (src.id === rowId(i)) blocked.push("reorder-above", "reorder-below");
165
+ return blocked;
166
+ },
167
+ dragHandle: pickDragHandle
168
+ };
169
+ }
170
+ </script>
171
+
172
+ <template>
173
+ <ShwfedFormUnitConfig
174
+ v-model="activeTabUnitModel"
175
+ v-model:selection="selection"
176
+ :scope-key="activeTabId"
177
+ :extras="EXTRAS"
178
+ >
179
+ <!-- Tab list — injected as the rail's top half, above the active tab's
180
+ layouts, so the rail reads as two parallel lists (matching the
181
+ block-tabs config). -->
182
+ <template #rail-extra>
183
+ <div class="flex flex-col gap-0.5">
184
+ <div
185
+ v-for="(t, i) in value.tabs"
186
+ :key="t.id"
187
+ :ref="dnd.rowRef(rowId(i), rowConfig(i))"
188
+ class="row group pl-2"
189
+ :class="[
190
+ activeTabId === t.id ? 'bg-[color-mix(in_srgb,var(--primary)_10%,white)] text-(--primary)' : 'text-zinc-700 hover:bg-zinc-50'
191
+ ]"
192
+ :data-instruction="dnd.instructionFor(rowId(i)) ?? void 0"
193
+ @click="selectTab(t.id)"
194
+ >
195
+ <Icon
196
+ icon="fluent:re-order-dots-vertical-20-regular"
197
+ class="drag-handle size-4 shrink-0 cursor-grab text-zinc-400"
198
+ />
199
+ <span class="flex-1 truncate">{{ tabLabel(t, i) }}</span>
200
+ <span
201
+ class="ml-auto flex items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100"
202
+ @click.stop
203
+ >
204
+ <Tooltip :delay-duration="180">
205
+ <TooltipTrigger as-child>
206
+ <button
207
+ type="button"
208
+ class="inline-flex size-6 items-center justify-center rounded text-zinc-400 hover:bg-zinc-100 hover:text-zinc-700"
209
+ @click.stop="duplicateTab(t.id)"
210
+ >
211
+ <Icon
212
+ icon="fluent:copy-20-regular"
213
+ class="size-3.5"
214
+ />
215
+ </button>
216
+ </TooltipTrigger>
217
+ <TooltipContent side="right">
218
+ 复制标签页
219
+ </TooltipContent>
220
+ </Tooltip>
221
+ <Tooltip :delay-duration="180">
222
+ <TooltipTrigger as-child>
223
+ <button
224
+ type="button"
225
+ class="inline-flex size-6 items-center justify-center rounded text-zinc-300 hover:bg-red-50 hover:text-red-500 disabled:cursor-not-allowed disabled:opacity-30 disabled:hover:bg-transparent disabled:hover:text-zinc-300"
226
+ :disabled="value.tabs.length <= 1"
227
+ @click.stop="deleteTab(t.id)"
228
+ >
229
+ <Icon
230
+ icon="fluent:delete-20-regular"
231
+ class="size-3.5"
232
+ />
233
+ </button>
234
+ </TooltipTrigger>
235
+ <TooltipContent side="right">
236
+ {{ value.tabs.length <= 1 ? "\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u6807\u7B7E\u9875" : "\u5220\u9664\u6807\u7B7E\u9875" }}
237
+ </TooltipContent>
238
+ </Tooltip>
239
+ </span>
240
+ </div>
241
+ </div>
242
+ </template>
243
+
244
+ <!-- 新增标签页 — pinned at the foot of the tab-list half. -->
245
+ <template #rail-extra-foot>
246
+ <Button
247
+ variant="ghost"
248
+ size="sm"
249
+ class="w-full justify-start"
250
+ @click="addTab()"
251
+ >
252
+ <Icon
253
+ icon="fluent:add-20-regular"
254
+ class="size-4"
255
+ />
256
+ <span>新增标签页</span>
257
+ </Button>
258
+ </template>
259
+
260
+ <!-- Active tab's label — above the layout canvas, mirroring the block's
261
+ pane header (标签名 → 名称 → canvas). -->
262
+ <template #pane-header>
263
+ <Field
264
+ v-if="activeTab"
265
+ orientation="vertical"
266
+ >
267
+ <FieldLabel class="text-xs text-zinc-500">
268
+ <template
269
+ v-if="tabFieldDescription('label')"
270
+ #tooltip
271
+ >
272
+ <Markdown
273
+ :source="tabFieldDescription('label')"
274
+ block
275
+ class="prose prose-sm prose-zinc"
276
+ />
277
+ </template>
278
+ {{ tabFieldTitle("label") }}
279
+ </FieldLabel>
280
+ <LocaleField
281
+ :model-value="activeTab.label"
282
+ :translate-hint="tabFieldTitle('label')"
283
+ @update:model-value="(v) => updateTabLabel(activeTab.id, v)"
284
+ />
285
+ </Field>
286
+ </template>
287
+
288
+ <!-- 通用配置: field-level props (editor-only name + container style). -->
289
+ <template #extras-pane="{ id }">
290
+ <div
291
+ v-if="id === 'field'"
292
+ class="flex flex-col gap-2"
293
+ >
294
+ <Field orientation="vertical">
295
+ <FieldLabel class="text-xs text-zinc-500">
296
+ <template
297
+ v-if="fieldDescription('displayName')"
298
+ #tooltip
299
+ >
300
+ <Markdown
301
+ :source="fieldDescription('displayName')"
302
+ block
303
+ class="prose prose-sm prose-zinc"
304
+ />
305
+ </template>
306
+ {{ fieldTitle("displayName") }}
307
+ </FieldLabel>
308
+ <InputGroup>
309
+ <InputGroupInput
310
+ :model-value="value.displayName ?? ''"
311
+ placeholder="例:基本信息"
312
+ @update:model-value="(v) => updateDisplayName(String(v ?? ''))"
313
+ />
314
+ </InputGroup>
315
+ </Field>
316
+
317
+ <Field orientation="vertical">
318
+ <FieldLabel class="text-xs text-zinc-500">
319
+ <template
320
+ v-if="fieldDescription('style')"
321
+ #tooltip
322
+ >
323
+ <Markdown
324
+ :source="fieldDescription('style')"
325
+ block
326
+ class="prose prose-sm prose-zinc"
327
+ />
328
+ </template>
329
+ {{ fieldTitle("style") }}
330
+ </FieldLabel>
331
+ <Textarea
332
+ :model-value="value.style ?? ''"
333
+ placeholder="CSS 样式字符串"
334
+ class="min-h-20 font-mono text-xs"
335
+ @update:model-value="(v) => updateStyle(String(v))"
336
+ />
337
+ </Field>
338
+ </div>
339
+ </template>
340
+ </ShwfedFormUnitConfig>
341
+ </template>
342
+
343
+ <style scoped>
344
+ .row{align-items:center;border-radius:.25rem;cursor:pointer;display:flex;font-size:.875rem;gap:.375rem;padding-block:.375rem;padding-right:.5rem;position:relative;transition:background-color .1s ease;-webkit-user-select:none;-moz-user-select:none;user-select:none}[data-instruction=reorder-above]:before,[data-instruction=reorder-below]:after{background:var(--primary,#2563eb);content:"";height:2px;left:.5rem;pointer-events:none;position:absolute;right:.5rem;z-index:1}[data-instruction=reorder-above]:before{top:-1px}[data-instruction=reorder-below]:after{bottom:-1px}
345
+ </style>
@@ -0,0 +1,59 @@
1
+ import { type Value } from './schema.js';
2
+ type __VLS_ModelProps = {
3
+ modelValue: Value;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ "update:modelValue": (value: {
7
+ readonly type: "com.shwfed.form.field.tabs";
8
+ readonly style?: string | undefined;
9
+ readonly id: string;
10
+ readonly displayName?: string | undefined;
11
+ readonly compatibilityDate: "2026-06-20";
12
+ readonly tabs: readonly {
13
+ readonly label: readonly [{
14
+ readonly locale: "zh";
15
+ readonly message: string;
16
+ }, ...{
17
+ readonly locale: "en" | "ja" | "ko";
18
+ readonly message: string;
19
+ }[]];
20
+ readonly id: string;
21
+ readonly unit: Readonly<{
22
+ fields: ReadonlyArray<import("../../../schema.js").FieldValue>;
23
+ layouts: readonly Readonly<{
24
+ name: string;
25
+ media?: string;
26
+ layout: import("../../../schema.js").LayoutValue;
27
+ }>[];
28
+ }>;
29
+ }[];
30
+ }) => any;
31
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
32
+ "onUpdate:modelValue"?: ((value: {
33
+ readonly type: "com.shwfed.form.field.tabs";
34
+ readonly style?: string | undefined;
35
+ readonly id: string;
36
+ readonly displayName?: string | undefined;
37
+ readonly compatibilityDate: "2026-06-20";
38
+ readonly tabs: readonly {
39
+ readonly label: readonly [{
40
+ readonly locale: "zh";
41
+ readonly message: string;
42
+ }, ...{
43
+ readonly locale: "en" | "ja" | "ko";
44
+ readonly message: string;
45
+ }[]];
46
+ readonly id: string;
47
+ readonly unit: Readonly<{
48
+ fields: ReadonlyArray<import("../../../schema.js").FieldValue>;
49
+ layouts: readonly Readonly<{
50
+ name: string;
51
+ media?: string;
52
+ layout: import("../../../schema.js").LayoutValue;
53
+ }>[];
54
+ }>;
55
+ }[];
56
+ }) => any) | undefined;
57
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
58
+ declare const _default: typeof __VLS_export;
59
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import type { Value } from './schema.js';
2
+ type __VLS_Props = {
3
+ fieldId: string;
4
+ config: Value;
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,113 @@
1
+ <script setup>
2
+ import { Effect } from "effect";
3
+ import { computed } from "vue";
4
+ import { useI18n } from "vue-i18n";
5
+ import { cel as _rawCel } from "../../../../../utils/cel";
6
+ import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
7
+ import { getLocalizedText } from "../../../../../share/locale";
8
+ import { useActiveTabId } from "../../../../../share/use-active-tab-id";
9
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../../ui/tabs";
10
+ import FormUnitRenderer from "../../../FormUnitRenderer.vue";
11
+ import { useValidationProvider } from "../../../utils/validation";
12
+ defineOptions({ name: "ShwfedTabsFieldRuntime" });
13
+ const props = defineProps({
14
+ fieldId: { type: String, required: true },
15
+ config: { type: null, required: true }
16
+ });
17
+ const { locale } = useI18n();
18
+ const inherited = injectCELContext();
19
+ const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
20
+ function evaluateMedia(expression) {
21
+ return Effect.runSync($cel(expression));
22
+ }
23
+ function isHidden(field) {
24
+ const hidden = field.hidden;
25
+ if (!hidden) return false;
26
+ try {
27
+ return Effect.runSync($cel(hidden));
28
+ } catch (err) {
29
+ console.error(`[shwfed-form] tabs: failed to evaluate hidden for ${field.id}:`, err);
30
+ return false;
31
+ }
32
+ }
33
+ const tabIds = computed(() => props.config.tabs.map((t) => t.id));
34
+ const active = useActiveTabId(props.fieldId, tabIds);
35
+ function tabLabel(tab, index) {
36
+ return getLocalizedText(tab.label, locale.value) ?? `\u6807\u7B7E ${index + 1}`;
37
+ }
38
+ const validation = useValidationProvider();
39
+ function collectFieldIds(fields) {
40
+ const ids = [];
41
+ for (const field of fields) {
42
+ ids.push(field.id);
43
+ const unit = field.unit;
44
+ if (unit?.fields) ids.push(...collectFieldIds(unit.fields));
45
+ const nestedTabs = field.tabs;
46
+ if (nestedTabs) {
47
+ for (const t of nestedTabs) {
48
+ if (t.unit?.fields) ids.push(...collectFieldIds(t.unit.fields));
49
+ }
50
+ }
51
+ }
52
+ return ids;
53
+ }
54
+ function tabCounts(tab) {
55
+ if (!validation?.revealed.value) return { errors: 0, warnings: 0 };
56
+ const results = validation.resultsFor(collectFieldIds(tab.unit.fields));
57
+ return {
58
+ errors: results.filter((r) => r.severity === "error").length,
59
+ warnings: results.filter((r) => r.severity === "warning").length
60
+ };
61
+ }
62
+ </script>
63
+
64
+ <template>
65
+ <Tabs
66
+ :model-value="active"
67
+ :style="config.style"
68
+ @update:model-value="(v) => {
69
+ if (typeof v === 'string') active = v;
70
+ }"
71
+ >
72
+ <TabsList>
73
+ <TabsTrigger
74
+ v-for="(t, i) in config.tabs"
75
+ :key="t.id"
76
+ :value="t.id"
77
+ class="max-[800px]:flex-1 max-[800px]:justify-center"
78
+ >
79
+ <span>{{ tabLabel(t, i) }}</span>
80
+ <span
81
+ v-if="tabCounts(t).errors > 0"
82
+ data-slot="tabs-error-count"
83
+ class="ml-1 inline-flex min-w-4 items-center justify-center rounded-full bg-red-100 px-1 text-[10px] font-medium text-red-600"
84
+ >{{ tabCounts(t).errors }}</span>
85
+ <span
86
+ v-else-if="tabCounts(t).warnings > 0"
87
+ data-slot="tabs-warning-count"
88
+ class="ml-1 inline-flex min-w-4 items-center justify-center rounded-full bg-amber-100 px-1 text-[10px] font-medium text-amber-600"
89
+ >{{ tabCounts(t).warnings }}</span>
90
+ </TabsTrigger>
91
+ </TabsList>
92
+
93
+ <!-- `force-mount` keeps every tab's fields mounted; reka-ui forces
94
+ `present` true under force-mount and therefore never sets `hidden`, so
95
+ we hide the inactive panes ourselves with `data-[state=inactive]:hidden`
96
+ (display:none, NOT unmounted). Load-bearing: derived / formula fields
97
+ keep computing, commit-bus drafts stay live, and form state for
98
+ inactive tabs is never dropped on switch. -->
99
+ <TabsContent
100
+ v-for="t in config.tabs"
101
+ :key="t.id"
102
+ :value="t.id"
103
+ force-mount
104
+ class="outline-none data-[state=inactive]:hidden"
105
+ >
106
+ <FormUnitRenderer
107
+ :unit="t.unit"
108
+ :evaluate-media="evaluateMedia"
109
+ :is-hidden="isHidden"
110
+ />
111
+ </TabsContent>
112
+ </Tabs>
113
+ </template>
@@ -0,0 +1,8 @@
1
+ import type { Value } from './schema.js';
2
+ type __VLS_Props = {
3
+ fieldId: string;
4
+ config: Value;
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,79 @@
1
+ import { Schema } from 'effect';
2
+ import type { Environment } from '../../../../../vendor/cel-js/lib/index.js';
3
+ export declare const type: "com.shwfed.form.field.tabs";
4
+ export declare const compatibilityDate: "2026-06-20";
5
+ export declare const metadata: {
6
+ readonly name: "标签页";
7
+ readonly icon: "fluent:tabs-20-regular";
8
+ readonly w: {
9
+ readonly initial: 12;
10
+ readonly min: 4;
11
+ readonly max: number;
12
+ };
13
+ readonly h: {
14
+ readonly initial: 6;
15
+ readonly min: 2;
16
+ readonly max: number;
17
+ readonly grow: true;
18
+ };
19
+ readonly inlineConfig: false;
20
+ };
21
+ export declare const TabMeta: Schema.Struct<{
22
+ label: Schema.TupleType<readonly [Schema.Struct<{
23
+ locale: Schema.Literal<["zh"]>;
24
+ message: Schema.SchemaClass<string, string, never>;
25
+ }>], [Schema.Struct<{
26
+ locale: Schema.Literal<["ja", "en", "ko"]>;
27
+ message: Schema.SchemaClass<string, string, never>;
28
+ }>]>;
29
+ }>;
30
+ /**
31
+ * A tabbed grouping. Pure view — children of each tab stay flat in the form
32
+ * state (the tabs introduce no subtree), and the active-tab selection lives
33
+ * only in the runtime component / sessionStorage (not persisted to the value).
34
+ *
35
+ * Deliberately does **not** include `commonFieldFields` — `hidden` /
36
+ * `required` / `binding` are field-flavored concepts that don't fit a
37
+ * structural grouping. We carry only an `id` (so the parent layout can
38
+ * reference the field), an editor-only `displayName`, an optional container
39
+ * `style`, and the `tabs` array — each tab a label + a nested `unit`.
40
+ */
41
+ export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
42
+ type: Schema.Literal<["com.shwfed.form.field.tabs"]>;
43
+ compatibilityDate: Schema.Literal<["2026-06-20"]>;
44
+ id: Schema.refine<string, typeof Schema.String>;
45
+ displayName: Schema.optional<Schema.SchemaClass<string, string, never>>;
46
+ style: Schema.optional<Schema.SchemaClass<string, string, never>>;
47
+ tabs: Schema.refine<readonly {
48
+ readonly label: readonly [{
49
+ readonly locale: "zh";
50
+ readonly message: string;
51
+ }, ...{
52
+ readonly locale: "en" | "ja" | "ko";
53
+ readonly message: string;
54
+ }[]];
55
+ readonly id: string;
56
+ readonly unit: Readonly<{
57
+ fields: ReadonlyArray<import("../../../schema.js").FieldValue>;
58
+ layouts: ReadonlyArray<import("../../../schema.js").LayoutSetValue>;
59
+ }>;
60
+ }[], Schema.filter<Schema.Array$<Schema.Struct<{
61
+ unit: Schema.suspend<Readonly<{
62
+ fields: ReadonlyArray<import("../../../schema.js").FieldValue>;
63
+ layouts: ReadonlyArray<import("../../../schema.js").LayoutSetValue>;
64
+ }>, Readonly<{
65
+ fields: ReadonlyArray<import("../../../schema.js").FieldValue>;
66
+ layouts: ReadonlyArray<import("../../../schema.js").LayoutSetValue>;
67
+ }>, never>;
68
+ label: Schema.TupleType<readonly [Schema.Struct<{
69
+ locale: Schema.Literal<["zh"]>;
70
+ message: Schema.SchemaClass<string, string, never>;
71
+ }>], [Schema.Struct<{
72
+ locale: Schema.Literal<["ja", "en", "ko"]>;
73
+ message: Schema.SchemaClass<string, string, never>;
74
+ }>]>;
75
+ id: Schema.refine<string, typeof Schema.String>;
76
+ }>>>>;
77
+ }>;
78
+ export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
79
+ export declare function defaults(): Partial<Value>;