sy-form-components 0.2.0

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/index.mjs ADDED
@@ -0,0 +1,4765 @@
1
+ // src/core/FormProvider.tsx
2
+ import React32, { useCallback, useRef as useRef4, useState as useState12, useMemo as useMemo4 } from "react";
3
+
4
+ // src/core/FormContext.ts
5
+ import { createContext, useContext } from "react";
6
+ var FormContext = createContext(null);
7
+ function useFormContext() {
8
+ const context = useContext(FormContext);
9
+ if (!context) {
10
+ throw new Error("useFormContext must be used within a FormProvider");
11
+ }
12
+ return context;
13
+ }
14
+
15
+ // src/core/ComponentRegistry.tsx
16
+ import React, { createContext as createContext2, useContext as useContext2 } from "react";
17
+ var ComponentRegistryContext = createContext2(null);
18
+ function ComponentRegistryProvider({
19
+ components,
20
+ children
21
+ }) {
22
+ const [registry, setRegistry] = React.useState(components);
23
+ const register = React.useCallback((name, component) => {
24
+ setRegistry((prev) => ({ ...prev, [name]: component }));
25
+ }, []);
26
+ const value = React.useMemo(() => ({ registry, register }), [registry, register]);
27
+ return React.createElement(ComponentRegistryContext.Provider, { value }, children);
28
+ }
29
+ function useComponent(componentName) {
30
+ const context = useContext2(ComponentRegistryContext);
31
+ if (!context) {
32
+ return null;
33
+ }
34
+ return context.registry[componentName] || null;
35
+ }
36
+
37
+ // src/fields/TextField/index.tsx
38
+ import { useEffect } from "react";
39
+
40
+ // src/core/FieldWrapper.tsx
41
+ import { jsx, jsxs } from "react/jsx-runtime";
42
+ function cn(...classes) {
43
+ return classes.filter(Boolean).join(" ");
44
+ }
45
+ function FieldWrapper({
46
+ fieldId,
47
+ label,
48
+ required,
49
+ tips,
50
+ className,
51
+ labelClassName,
52
+ tipsClassName,
53
+ children
54
+ }) {
55
+ const { fieldErrors } = useFormContext();
56
+ const error = fieldErrors[fieldId];
57
+ return /* @__PURE__ */ jsxs("div", { className: cn("sy-field-wrapper", className), "data-field-id": fieldId, children: [
58
+ /* @__PURE__ */ jsxs("label", { className: cn("sy-field-label", labelClassName), children: [
59
+ required && /* @__PURE__ */ jsx("span", { className: "sy-field-required", "aria-hidden": "true", children: "*" }),
60
+ label
61
+ ] }),
62
+ /* @__PURE__ */ jsx("div", { className: "sy-field-control", children }),
63
+ tips && !error && /* @__PURE__ */ jsx("div", { className: cn("sy-field-tips", tipsClassName), "data-testid": `tips-${fieldId}`, children: tips }),
64
+ error && /* @__PURE__ */ jsx("div", { className: "sy-field-error", role: "alert", "data-testid": `error-${fieldId}`, children: error })
65
+ ] });
66
+ }
67
+
68
+ // src/hooks/useDeviceDetect.ts
69
+ import { useSyncExternalStore } from "react";
70
+ var MOBILE_BREAKPOINT = 768;
71
+ function subscribe(callback) {
72
+ let prev = window.innerWidth < MOBILE_BREAKPOINT;
73
+ const handler = () => {
74
+ const next = window.innerWidth < MOBILE_BREAKPOINT;
75
+ if (next !== prev) {
76
+ prev = next;
77
+ callback();
78
+ }
79
+ };
80
+ window.addEventListener("resize", handler);
81
+ return () => window.removeEventListener("resize", handler);
82
+ }
83
+ function getSnapshot() {
84
+ return window.innerWidth < MOBILE_BREAKPOINT;
85
+ }
86
+ function useDeviceDetect() {
87
+ const isMobile = useSyncExternalStore(
88
+ subscribe,
89
+ getSnapshot,
90
+ /* v8 ignore next */
91
+ () => false
92
+ );
93
+ return { isMobile };
94
+ }
95
+
96
+ // src/fields/TextField/TextFieldPC.tsx
97
+ import { Input } from "antd";
98
+ import { jsx as jsx2 } from "react/jsx-runtime";
99
+ function TextFieldPC({
100
+ fieldId,
101
+ placeholder,
102
+ maxLength,
103
+ inputClassName,
104
+ behavior,
105
+ onChange,
106
+ onBlur
107
+ }) {
108
+ const { formData, setFieldValue } = useFormContext();
109
+ const value = formData[fieldId] ?? "";
110
+ const disabled = behavior === "DISABLED";
111
+ const handleChange = (e) => {
112
+ const v = e.target.value;
113
+ setFieldValue(fieldId, v);
114
+ onChange?.(v);
115
+ };
116
+ const handleBlur = (e) => {
117
+ onBlur?.(e.target.value);
118
+ };
119
+ return /* @__PURE__ */ jsx2(
120
+ Input,
121
+ {
122
+ className: inputClassName,
123
+ style: { width: "100%" },
124
+ value,
125
+ placeholder,
126
+ maxLength,
127
+ disabled,
128
+ onChange: handleChange,
129
+ onBlur: handleBlur,
130
+ "data-testid": `textfield-input-${fieldId}`
131
+ }
132
+ );
133
+ }
134
+
135
+ // src/fields/TextField/TextFieldMobile.tsx
136
+ import { Input as Input2 } from "antd-mobile";
137
+ import { jsx as jsx3 } from "react/jsx-runtime";
138
+ function TextFieldMobile({
139
+ fieldId,
140
+ placeholder,
141
+ maxLength,
142
+ inputClassName,
143
+ behavior,
144
+ onChange,
145
+ onBlur
146
+ }) {
147
+ const { formData, setFieldValue } = useFormContext();
148
+ const value = formData[fieldId] ?? "";
149
+ const disabled = behavior === "DISABLED";
150
+ const handleChange = (v) => {
151
+ setFieldValue(fieldId, v);
152
+ onChange?.(v);
153
+ };
154
+ const handleBlur = () => {
155
+ onBlur?.(value);
156
+ };
157
+ return /* @__PURE__ */ jsx3("div", { className: inputClassName, "data-testid": `textfield-input-${fieldId}`, children: /* @__PURE__ */ jsx3(
158
+ Input2,
159
+ {
160
+ value,
161
+ placeholder,
162
+ maxLength,
163
+ disabled,
164
+ onChange: handleChange,
165
+ onBlur: handleBlur
166
+ }
167
+ ) });
168
+ }
169
+
170
+ // src/fields/TextField/TextFieldReadonly.tsx
171
+ import { jsx as jsx4 } from "react/jsx-runtime";
172
+ function TextFieldReadonly({ fieldId, readonlyClassName }) {
173
+ const { formData } = useFormContext();
174
+ const value = formData[fieldId];
175
+ const display = value != null && value !== "" ? String(value) : "--";
176
+ return /* @__PURE__ */ jsx4(
177
+ "div",
178
+ {
179
+ className: readonlyClassName || "sy-field-readonly-value",
180
+ "data-testid": `textfield-readonly-${fieldId}`,
181
+ children: display
182
+ }
183
+ );
184
+ }
185
+
186
+ // src/fields/TextField/index.tsx
187
+ import { jsx as jsx5 } from "react/jsx-runtime";
188
+ function TextField(props) {
189
+ const {
190
+ fieldId,
191
+ label,
192
+ behavior: propBehavior,
193
+ required,
194
+ tips,
195
+ defaultValue,
196
+ className,
197
+ labelClassName,
198
+ tipsClassName
199
+ } = props;
200
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
201
+ const { isMobile } = useDeviceDetect();
202
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
203
+ useEffect(() => {
204
+ registerField(fieldId);
205
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
206
+ setFieldValue(fieldId, defaultValue);
207
+ }
208
+ return () => unregisterField(fieldId);
209
+ }, [fieldId]);
210
+ if (behavior === "HIDDEN") return null;
211
+ const childProps = { ...props, behavior };
212
+ return /* @__PURE__ */ jsx5(
213
+ FieldWrapper,
214
+ {
215
+ fieldId,
216
+ label,
217
+ required,
218
+ tips,
219
+ className,
220
+ labelClassName,
221
+ tipsClassName,
222
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx5(TextFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx5(TextFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx5(TextFieldPC, { ...childProps })
223
+ }
224
+ );
225
+ }
226
+
227
+ // src/fields/NumberField/index.tsx
228
+ import { useEffect as useEffect2 } from "react";
229
+
230
+ // src/fields/NumberField/NumberFieldPC.tsx
231
+ import { InputNumber } from "antd";
232
+ import { jsx as jsx6 } from "react/jsx-runtime";
233
+ function NumberFieldPC({
234
+ fieldId,
235
+ placeholder,
236
+ inputClassName,
237
+ behavior,
238
+ min,
239
+ max,
240
+ step,
241
+ precision,
242
+ onChange,
243
+ onBlur
244
+ }) {
245
+ const { formData, setFieldValue } = useFormContext();
246
+ const value = formData[fieldId];
247
+ const disabled = behavior === "DISABLED";
248
+ const handleChange = (v) => {
249
+ setFieldValue(fieldId, v);
250
+ onChange?.(v);
251
+ };
252
+ const handleBlur = () => {
253
+ onBlur?.(value ?? null);
254
+ };
255
+ return /* @__PURE__ */ jsx6(
256
+ InputNumber,
257
+ {
258
+ className: inputClassName,
259
+ style: { width: "100%" },
260
+ value: value ?? null,
261
+ placeholder,
262
+ disabled,
263
+ min,
264
+ max,
265
+ step,
266
+ precision,
267
+ onChange: handleChange,
268
+ onBlur: handleBlur,
269
+ "data-testid": `numberfield-input-${fieldId}`
270
+ }
271
+ );
272
+ }
273
+
274
+ // src/fields/NumberField/NumberFieldMobile.tsx
275
+ import { Input as Input3 } from "antd-mobile";
276
+ import { jsx as jsx7 } from "react/jsx-runtime";
277
+ function NumberFieldMobile({
278
+ fieldId,
279
+ placeholder,
280
+ inputClassName,
281
+ behavior,
282
+ onChange,
283
+ onBlur
284
+ }) {
285
+ const { formData, setFieldValue } = useFormContext();
286
+ const value = formData[fieldId];
287
+ const disabled = behavior === "DISABLED";
288
+ const handleChange = (v) => {
289
+ const num = v === "" ? null : Number(v);
290
+ const finalValue = num !== null && isNaN(num) ? null : num;
291
+ setFieldValue(fieldId, finalValue);
292
+ onChange?.(finalValue);
293
+ };
294
+ const handleBlur = () => {
295
+ onBlur?.(value ?? null);
296
+ };
297
+ return /* @__PURE__ */ jsx7("div", { className: inputClassName, "data-testid": `numberfield-input-${fieldId}`, children: /* @__PURE__ */ jsx7(
298
+ Input3,
299
+ {
300
+ type: "number",
301
+ value: value != null ? String(value) : "",
302
+ placeholder,
303
+ disabled,
304
+ onChange: handleChange,
305
+ onBlur: handleBlur
306
+ }
307
+ ) });
308
+ }
309
+
310
+ // src/fields/NumberField/NumberFieldReadonly.tsx
311
+ import { jsx as jsx8 } from "react/jsx-runtime";
312
+ function NumberFieldReadonly({ fieldId, readonlyClassName }) {
313
+ const { formData } = useFormContext();
314
+ const value = formData[fieldId];
315
+ const display = value != null ? String(value) : "--";
316
+ return /* @__PURE__ */ jsx8(
317
+ "div",
318
+ {
319
+ className: readonlyClassName || "sy-field-readonly-value",
320
+ "data-testid": `numberfield-readonly-${fieldId}`,
321
+ children: display
322
+ }
323
+ );
324
+ }
325
+
326
+ // src/fields/NumberField/index.tsx
327
+ import { jsx as jsx9 } from "react/jsx-runtime";
328
+ function NumberField(props) {
329
+ const {
330
+ fieldId,
331
+ label,
332
+ behavior: propBehavior,
333
+ required,
334
+ tips,
335
+ defaultValue,
336
+ className,
337
+ labelClassName,
338
+ tipsClassName
339
+ } = props;
340
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
341
+ const { isMobile } = useDeviceDetect();
342
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
343
+ useEffect2(() => {
344
+ registerField(fieldId);
345
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
346
+ setFieldValue(fieldId, defaultValue);
347
+ }
348
+ return () => unregisterField(fieldId);
349
+ }, [fieldId]);
350
+ if (behavior === "HIDDEN") return null;
351
+ const childProps = { ...props, behavior };
352
+ return /* @__PURE__ */ jsx9(
353
+ FieldWrapper,
354
+ {
355
+ fieldId,
356
+ label,
357
+ required,
358
+ tips,
359
+ className,
360
+ labelClassName,
361
+ tipsClassName,
362
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx9(NumberFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx9(NumberFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx9(NumberFieldPC, { ...childProps })
363
+ }
364
+ );
365
+ }
366
+
367
+ // src/fields/TextAreaField/index.tsx
368
+ import { useEffect as useEffect3 } from "react";
369
+
370
+ // src/fields/TextAreaField/TextAreaFieldPC.tsx
371
+ import { Input as Input4 } from "antd";
372
+ import { jsx as jsx10 } from "react/jsx-runtime";
373
+ var { TextArea } = Input4;
374
+ function TextAreaFieldPC({
375
+ fieldId,
376
+ placeholder,
377
+ inputClassName,
378
+ behavior,
379
+ rows,
380
+ maxLength,
381
+ showCount,
382
+ onChange,
383
+ onBlur
384
+ }) {
385
+ const { formData, setFieldValue } = useFormContext();
386
+ const value = formData[fieldId] ?? "";
387
+ const disabled = behavior === "DISABLED";
388
+ const handleChange = (e) => {
389
+ const v = e.target.value;
390
+ setFieldValue(fieldId, v);
391
+ onChange?.(v);
392
+ };
393
+ const handleBlur = (e) => {
394
+ onBlur?.(e.target.value);
395
+ };
396
+ return /* @__PURE__ */ jsx10(
397
+ TextArea,
398
+ {
399
+ className: inputClassName,
400
+ style: { width: "100%" },
401
+ value,
402
+ placeholder,
403
+ disabled,
404
+ rows,
405
+ maxLength,
406
+ showCount,
407
+ onChange: handleChange,
408
+ onBlur: handleBlur,
409
+ "data-testid": `textareafield-input-${fieldId}`
410
+ }
411
+ );
412
+ }
413
+
414
+ // src/fields/TextAreaField/TextAreaFieldMobile.tsx
415
+ import { TextArea as TextArea2 } from "antd-mobile";
416
+ import { jsx as jsx11 } from "react/jsx-runtime";
417
+ function TextAreaFieldMobile({
418
+ fieldId,
419
+ placeholder,
420
+ inputClassName,
421
+ behavior,
422
+ rows,
423
+ maxLength,
424
+ showCount,
425
+ onChange,
426
+ onBlur
427
+ }) {
428
+ const { formData, setFieldValue } = useFormContext();
429
+ const value = formData[fieldId] ?? "";
430
+ const disabled = behavior === "DISABLED";
431
+ const handleChange = (v) => {
432
+ setFieldValue(fieldId, v);
433
+ onChange?.(v);
434
+ };
435
+ const handleBlur = () => {
436
+ onBlur?.(value);
437
+ };
438
+ return /* @__PURE__ */ jsx11("div", { className: inputClassName, "data-testid": `textareafield-input-${fieldId}`, children: /* @__PURE__ */ jsx11(
439
+ TextArea2,
440
+ {
441
+ value,
442
+ placeholder,
443
+ disabled,
444
+ rows,
445
+ maxLength,
446
+ showCount,
447
+ onChange: handleChange,
448
+ onBlur: handleBlur
449
+ }
450
+ ) });
451
+ }
452
+
453
+ // src/fields/TextAreaField/TextAreaFieldReadonly.tsx
454
+ import { jsx as jsx12 } from "react/jsx-runtime";
455
+ function TextAreaFieldReadonly({ fieldId, readonlyClassName }) {
456
+ const { formData } = useFormContext();
457
+ const value = formData[fieldId];
458
+ const display = value != null && value !== "" ? String(value) : "--";
459
+ return /* @__PURE__ */ jsx12(
460
+ "div",
461
+ {
462
+ className: readonlyClassName || "sy-field-readonly-value",
463
+ "data-testid": `textareafield-readonly-${fieldId}`,
464
+ style: { whiteSpace: "pre-wrap" },
465
+ children: display
466
+ }
467
+ );
468
+ }
469
+
470
+ // src/fields/TextAreaField/index.tsx
471
+ import { jsx as jsx13 } from "react/jsx-runtime";
472
+ function TextAreaField(props) {
473
+ const {
474
+ fieldId,
475
+ label,
476
+ behavior: propBehavior,
477
+ required,
478
+ tips,
479
+ defaultValue,
480
+ className,
481
+ labelClassName,
482
+ tipsClassName
483
+ } = props;
484
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
485
+ const { isMobile } = useDeviceDetect();
486
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
487
+ useEffect3(() => {
488
+ registerField(fieldId);
489
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
490
+ setFieldValue(fieldId, defaultValue);
491
+ }
492
+ return () => unregisterField(fieldId);
493
+ }, [fieldId]);
494
+ if (behavior === "HIDDEN") return null;
495
+ const childProps = { ...props, behavior };
496
+ return /* @__PURE__ */ jsx13(
497
+ FieldWrapper,
498
+ {
499
+ fieldId,
500
+ label,
501
+ required,
502
+ tips,
503
+ className,
504
+ labelClassName,
505
+ tipsClassName,
506
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx13(TextAreaFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx13(TextAreaFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx13(TextAreaFieldPC, { ...childProps })
507
+ }
508
+ );
509
+ }
510
+
511
+ // src/fields/SelectField/index.tsx
512
+ import { useEffect as useEffect4 } from "react";
513
+
514
+ // src/fields/SelectField/SelectFieldPC.tsx
515
+ import { Select } from "antd";
516
+ import { jsx as jsx14 } from "react/jsx-runtime";
517
+ function SelectFieldPC({
518
+ fieldId,
519
+ placeholder,
520
+ inputClassName,
521
+ behavior,
522
+ options,
523
+ allowClear,
524
+ onChange
525
+ }) {
526
+ const { formData, setFieldValue } = useFormContext();
527
+ const value = formData[fieldId];
528
+ const disabled = behavior === "DISABLED";
529
+ const handleChange = (val) => {
530
+ if (val === void 0) {
531
+ setFieldValue(fieldId, null);
532
+ onChange?.(null);
533
+ } else {
534
+ const option = options.find((o) => o.value === val) ?? null;
535
+ setFieldValue(fieldId, option);
536
+ onChange?.(option);
537
+ }
538
+ };
539
+ return /* @__PURE__ */ jsx14(
540
+ Select,
541
+ {
542
+ className: inputClassName,
543
+ style: { width: "100%" },
544
+ value: value?.value ?? void 0,
545
+ placeholder,
546
+ disabled,
547
+ allowClear,
548
+ options,
549
+ onChange: handleChange,
550
+ "data-testid": `selectfield-input-${fieldId}`
551
+ }
552
+ );
553
+ }
554
+
555
+ // src/fields/SelectField/SelectFieldMobile.tsx
556
+ import { useState } from "react";
557
+ import { Picker } from "antd-mobile";
558
+ import { jsx as jsx15, jsxs as jsxs2 } from "react/jsx-runtime";
559
+ function SelectFieldMobile({
560
+ fieldId,
561
+ placeholder,
562
+ inputClassName,
563
+ behavior,
564
+ options,
565
+ onChange
566
+ }) {
567
+ const { formData, setFieldValue } = useFormContext();
568
+ const value = formData[fieldId];
569
+ const disabled = behavior === "DISABLED";
570
+ const [visible, setVisible] = useState(false);
571
+ const columns = [options.map((o) => ({ label: o.label, value: o.value }))];
572
+ const handleConfirm = (val) => {
573
+ const selected = val[0];
574
+ if (selected) {
575
+ const option = options.find((o) => o.value === selected) ?? null;
576
+ setFieldValue(fieldId, option);
577
+ onChange?.(option);
578
+ } else {
579
+ setFieldValue(fieldId, null);
580
+ onChange?.(null);
581
+ }
582
+ };
583
+ return /* @__PURE__ */ jsxs2("div", { className: inputClassName, "data-testid": `selectfield-input-${fieldId}`, children: [
584
+ /* @__PURE__ */ jsx15(
585
+ "div",
586
+ {
587
+ onClick: () => !disabled && setVisible(true),
588
+ "data-testid": `selectfield-trigger-${fieldId}`,
589
+ children: value?.label ?? placeholder ?? "\u8BF7\u9009\u62E9"
590
+ }
591
+ ),
592
+ /* @__PURE__ */ jsx15(
593
+ Picker,
594
+ {
595
+ columns,
596
+ visible,
597
+ onClose: () => setVisible(false),
598
+ onConfirm: handleConfirm,
599
+ value: value ? [value.value] : []
600
+ }
601
+ )
602
+ ] });
603
+ }
604
+
605
+ // src/fields/SelectField/SelectFieldReadonly.tsx
606
+ import { jsx as jsx16 } from "react/jsx-runtime";
607
+ function SelectFieldReadonly({ fieldId, readonlyClassName }) {
608
+ const { formData } = useFormContext();
609
+ const value = formData[fieldId];
610
+ const display = value?.label ?? "--";
611
+ return /* @__PURE__ */ jsx16(
612
+ "div",
613
+ {
614
+ className: readonlyClassName || "sy-field-readonly-value",
615
+ "data-testid": `selectfield-readonly-${fieldId}`,
616
+ children: display
617
+ }
618
+ );
619
+ }
620
+
621
+ // src/fields/SelectField/index.tsx
622
+ import { jsx as jsx17 } from "react/jsx-runtime";
623
+ function SelectField(props) {
624
+ const {
625
+ fieldId,
626
+ label,
627
+ behavior: propBehavior,
628
+ required,
629
+ tips,
630
+ defaultValue,
631
+ className,
632
+ labelClassName,
633
+ tipsClassName
634
+ } = props;
635
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
636
+ const { isMobile } = useDeviceDetect();
637
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
638
+ useEffect4(() => {
639
+ registerField(fieldId);
640
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
641
+ setFieldValue(fieldId, defaultValue);
642
+ }
643
+ return () => unregisterField(fieldId);
644
+ }, [fieldId]);
645
+ if (behavior === "HIDDEN") return null;
646
+ const childProps = { ...props, behavior };
647
+ return /* @__PURE__ */ jsx17(
648
+ FieldWrapper,
649
+ {
650
+ fieldId,
651
+ label,
652
+ required,
653
+ tips,
654
+ className,
655
+ labelClassName,
656
+ tipsClassName,
657
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx17(SelectFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx17(SelectFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx17(SelectFieldPC, { ...childProps })
658
+ }
659
+ );
660
+ }
661
+
662
+ // src/fields/MultiSelectField/index.tsx
663
+ import { useEffect as useEffect5 } from "react";
664
+
665
+ // src/fields/MultiSelectField/MultiSelectFieldPC.tsx
666
+ import { Select as Select2 } from "antd";
667
+ import { jsx as jsx18 } from "react/jsx-runtime";
668
+ function MultiSelectFieldPC({
669
+ fieldId,
670
+ placeholder,
671
+ inputClassName,
672
+ behavior,
673
+ options,
674
+ maxCount,
675
+ onChange
676
+ }) {
677
+ const { formData, setFieldValue } = useFormContext();
678
+ const value = formData[fieldId] ?? [];
679
+ const disabled = behavior === "DISABLED";
680
+ const handleChange = (vals) => {
681
+ const selected = vals.map((v) => options.find((o) => o.value === v)).filter((o) => o != null);
682
+ setFieldValue(fieldId, selected);
683
+ onChange?.(selected);
684
+ };
685
+ return /* @__PURE__ */ jsx18(
686
+ Select2,
687
+ {
688
+ mode: "multiple",
689
+ className: inputClassName,
690
+ style: { width: "100%" },
691
+ value: value.map((v) => v.value),
692
+ placeholder,
693
+ disabled,
694
+ maxCount,
695
+ options,
696
+ onChange: handleChange,
697
+ "data-testid": `multiselectfield-input-${fieldId}`
698
+ }
699
+ );
700
+ }
701
+
702
+ // src/fields/MultiSelectField/MultiSelectFieldMobile.tsx
703
+ import { Selector } from "antd-mobile";
704
+ import { jsx as jsx19 } from "react/jsx-runtime";
705
+ function MultiSelectFieldMobile({
706
+ fieldId,
707
+ inputClassName,
708
+ behavior,
709
+ options,
710
+ onChange
711
+ }) {
712
+ const { formData, setFieldValue } = useFormContext();
713
+ const value = formData[fieldId] ?? [];
714
+ const disabled = behavior === "DISABLED";
715
+ const handleChange = (vals) => {
716
+ const selected = vals.map((v) => options.find((o) => o.value === v)).filter((o) => o != null);
717
+ setFieldValue(fieldId, selected);
718
+ onChange?.(selected);
719
+ };
720
+ return /* @__PURE__ */ jsx19("div", { className: inputClassName, "data-testid": `multiselectfield-input-${fieldId}`, children: /* @__PURE__ */ jsx19(
721
+ Selector,
722
+ {
723
+ multiple: true,
724
+ options,
725
+ value: value.map((v) => v.value),
726
+ onChange: handleChange,
727
+ disabled
728
+ }
729
+ ) });
730
+ }
731
+
732
+ // src/fields/MultiSelectField/MultiSelectFieldReadonly.tsx
733
+ import { jsx as jsx20 } from "react/jsx-runtime";
734
+ function MultiSelectFieldReadonly({ fieldId, readonlyClassName }) {
735
+ const { formData } = useFormContext();
736
+ const value = formData[fieldId] ?? [];
737
+ const display = value.length > 0 ? value.map((v) => v.label).join(", ") : "--";
738
+ return /* @__PURE__ */ jsx20(
739
+ "div",
740
+ {
741
+ className: readonlyClassName || "sy-field-readonly-value",
742
+ "data-testid": `multiselectfield-readonly-${fieldId}`,
743
+ children: display
744
+ }
745
+ );
746
+ }
747
+
748
+ // src/fields/MultiSelectField/index.tsx
749
+ import { jsx as jsx21 } from "react/jsx-runtime";
750
+ function MultiSelectField(props) {
751
+ const {
752
+ fieldId,
753
+ label,
754
+ behavior: propBehavior,
755
+ required,
756
+ tips,
757
+ defaultValue,
758
+ className,
759
+ labelClassName,
760
+ tipsClassName
761
+ } = props;
762
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
763
+ const { isMobile } = useDeviceDetect();
764
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
765
+ useEffect5(() => {
766
+ registerField(fieldId);
767
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
768
+ setFieldValue(fieldId, defaultValue);
769
+ }
770
+ return () => unregisterField(fieldId);
771
+ }, [fieldId]);
772
+ if (behavior === "HIDDEN") return null;
773
+ const childProps = { ...props, behavior };
774
+ return /* @__PURE__ */ jsx21(
775
+ FieldWrapper,
776
+ {
777
+ fieldId,
778
+ label,
779
+ required,
780
+ tips,
781
+ className,
782
+ labelClassName,
783
+ tipsClassName,
784
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx21(MultiSelectFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx21(MultiSelectFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx21(MultiSelectFieldPC, { ...childProps })
785
+ }
786
+ );
787
+ }
788
+
789
+ // src/fields/RadioField/index.tsx
790
+ import { useEffect as useEffect6 } from "react";
791
+
792
+ // src/fields/RadioField/RadioFieldPC.tsx
793
+ import { Radio } from "antd";
794
+ import { jsx as jsx22 } from "react/jsx-runtime";
795
+ function RadioFieldPC({
796
+ fieldId,
797
+ inputClassName,
798
+ behavior,
799
+ options,
800
+ direction,
801
+ onChange
802
+ }) {
803
+ const { formData, setFieldValue } = useFormContext();
804
+ const value = formData[fieldId];
805
+ const disabled = behavior === "DISABLED";
806
+ const handleChange = (e) => {
807
+ const val = e.target.value;
808
+ const option = options.find((o) => o.value === val) ?? null;
809
+ setFieldValue(fieldId, option);
810
+ onChange?.(option);
811
+ };
812
+ const style = direction === "vertical" ? { display: "flex", flexDirection: "column", gap: 8 } : {};
813
+ return /* @__PURE__ */ jsx22(
814
+ Radio.Group,
815
+ {
816
+ className: inputClassName,
817
+ value: value?.value ?? void 0,
818
+ disabled,
819
+ onChange: handleChange,
820
+ "data-testid": `radiofield-input-${fieldId}`,
821
+ style,
822
+ children: options.map((o) => /* @__PURE__ */ jsx22(Radio, { value: o.value, children: o.label }, o.value))
823
+ }
824
+ );
825
+ }
826
+
827
+ // src/fields/RadioField/RadioFieldMobile.tsx
828
+ import { Radio as Radio2, Space } from "antd-mobile";
829
+ import { jsx as jsx23 } from "react/jsx-runtime";
830
+ function RadioFieldMobile({
831
+ fieldId,
832
+ inputClassName,
833
+ behavior,
834
+ options,
835
+ direction,
836
+ onChange
837
+ }) {
838
+ const { formData, setFieldValue } = useFormContext();
839
+ const value = formData[fieldId];
840
+ const disabled = behavior === "DISABLED";
841
+ const handleChange = (val) => {
842
+ const option = options.find((o) => o.value === val) ?? null;
843
+ setFieldValue(fieldId, option);
844
+ onChange?.(option);
845
+ };
846
+ return /* @__PURE__ */ jsx23("div", { className: inputClassName, "data-testid": `radiofield-input-${fieldId}`, children: /* @__PURE__ */ jsx23(Radio2.Group, { value: value?.value, onChange: handleChange, disabled, children: /* @__PURE__ */ jsx23(Space, { direction: direction === "horizontal" ? "horizontal" : "vertical", children: options.map((o) => /* @__PURE__ */ jsx23(Radio2, { value: o.value, children: o.label }, o.value)) }) }) });
847
+ }
848
+
849
+ // src/fields/RadioField/RadioFieldReadonly.tsx
850
+ import { jsx as jsx24 } from "react/jsx-runtime";
851
+ function RadioFieldReadonly({ fieldId, readonlyClassName }) {
852
+ const { formData } = useFormContext();
853
+ const value = formData[fieldId];
854
+ const display = value?.label ?? "--";
855
+ return /* @__PURE__ */ jsx24(
856
+ "div",
857
+ {
858
+ className: readonlyClassName || "sy-field-readonly-value",
859
+ "data-testid": `radiofield-readonly-${fieldId}`,
860
+ children: display
861
+ }
862
+ );
863
+ }
864
+
865
+ // src/fields/RadioField/index.tsx
866
+ import { jsx as jsx25 } from "react/jsx-runtime";
867
+ function RadioField(props) {
868
+ const {
869
+ fieldId,
870
+ label,
871
+ behavior: propBehavior,
872
+ required,
873
+ tips,
874
+ defaultValue,
875
+ className,
876
+ labelClassName,
877
+ tipsClassName
878
+ } = props;
879
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
880
+ const { isMobile } = useDeviceDetect();
881
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
882
+ useEffect6(() => {
883
+ registerField(fieldId);
884
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
885
+ setFieldValue(fieldId, defaultValue);
886
+ }
887
+ return () => unregisterField(fieldId);
888
+ }, [fieldId]);
889
+ if (behavior === "HIDDEN") return null;
890
+ const childProps = { ...props, behavior };
891
+ return /* @__PURE__ */ jsx25(
892
+ FieldWrapper,
893
+ {
894
+ fieldId,
895
+ label,
896
+ required,
897
+ tips,
898
+ className,
899
+ labelClassName,
900
+ tipsClassName,
901
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx25(RadioFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx25(RadioFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx25(RadioFieldPC, { ...childProps })
902
+ }
903
+ );
904
+ }
905
+
906
+ // src/fields/CheckboxField/index.tsx
907
+ import { useEffect as useEffect7 } from "react";
908
+
909
+ // src/fields/CheckboxField/CheckboxFieldPC.tsx
910
+ import { Checkbox } from "antd";
911
+ import { jsx as jsx26 } from "react/jsx-runtime";
912
+ function CheckboxFieldPC({
913
+ fieldId,
914
+ inputClassName,
915
+ behavior,
916
+ options,
917
+ direction,
918
+ onChange
919
+ }) {
920
+ const { formData, setFieldValue } = useFormContext();
921
+ const value = formData[fieldId] ?? [];
922
+ const disabled = behavior === "DISABLED";
923
+ const handleChange = (vals) => {
924
+ const selected = vals.map((v) => options.find((o) => o.value === v)).filter((o) => o != null);
925
+ setFieldValue(fieldId, selected);
926
+ onChange?.(selected);
927
+ };
928
+ const style = direction === "vertical" ? { display: "flex", flexDirection: "column", gap: 8 } : {};
929
+ return /* @__PURE__ */ jsx26(
930
+ Checkbox.Group,
931
+ {
932
+ className: inputClassName,
933
+ value: value.map((v) => v.value),
934
+ disabled,
935
+ onChange: handleChange,
936
+ "data-testid": `checkboxfield-input-${fieldId}`,
937
+ style,
938
+ children: options.map((o) => /* @__PURE__ */ jsx26(Checkbox, { value: o.value, children: o.label }, o.value))
939
+ }
940
+ );
941
+ }
942
+
943
+ // src/fields/CheckboxField/CheckboxFieldMobile.tsx
944
+ import { Checkbox as Checkbox2, Space as Space2 } from "antd-mobile";
945
+ import { jsx as jsx27 } from "react/jsx-runtime";
946
+ function CheckboxFieldMobile({
947
+ fieldId,
948
+ inputClassName,
949
+ behavior,
950
+ options,
951
+ direction,
952
+ onChange
953
+ }) {
954
+ const { formData, setFieldValue } = useFormContext();
955
+ const value = formData[fieldId] ?? [];
956
+ const disabled = behavior === "DISABLED";
957
+ const handleChange = (vals) => {
958
+ const selected = vals.map((v) => options.find((o) => o.value === v)).filter((o) => o != null);
959
+ setFieldValue(fieldId, selected);
960
+ onChange?.(selected);
961
+ };
962
+ return /* @__PURE__ */ jsx27("div", { className: inputClassName, "data-testid": `checkboxfield-input-${fieldId}`, children: /* @__PURE__ */ jsx27(
963
+ Checkbox2.Group,
964
+ {
965
+ value: value.map((v) => v.value),
966
+ onChange: handleChange,
967
+ disabled,
968
+ children: /* @__PURE__ */ jsx27(Space2, { direction: direction === "horizontal" ? "horizontal" : "vertical", children: options.map((o) => /* @__PURE__ */ jsx27(Checkbox2, { value: o.value, children: o.label }, o.value)) })
969
+ }
970
+ ) });
971
+ }
972
+
973
+ // src/fields/CheckboxField/CheckboxFieldReadonly.tsx
974
+ import { jsx as jsx28 } from "react/jsx-runtime";
975
+ function CheckboxFieldReadonly({ fieldId, readonlyClassName }) {
976
+ const { formData } = useFormContext();
977
+ const value = formData[fieldId] ?? [];
978
+ const display = value.length > 0 ? value.map((v) => v.label).join(", ") : "--";
979
+ return /* @__PURE__ */ jsx28(
980
+ "div",
981
+ {
982
+ className: readonlyClassName || "sy-field-readonly-value",
983
+ "data-testid": `checkboxfield-readonly-${fieldId}`,
984
+ children: display
985
+ }
986
+ );
987
+ }
988
+
989
+ // src/fields/CheckboxField/index.tsx
990
+ import { jsx as jsx29 } from "react/jsx-runtime";
991
+ function CheckboxField(props) {
992
+ const {
993
+ fieldId,
994
+ label,
995
+ behavior: propBehavior,
996
+ required,
997
+ tips,
998
+ defaultValue,
999
+ className,
1000
+ labelClassName,
1001
+ tipsClassName
1002
+ } = props;
1003
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
1004
+ const { isMobile } = useDeviceDetect();
1005
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
1006
+ useEffect7(() => {
1007
+ registerField(fieldId);
1008
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
1009
+ setFieldValue(fieldId, defaultValue);
1010
+ }
1011
+ return () => unregisterField(fieldId);
1012
+ }, [fieldId]);
1013
+ if (behavior === "HIDDEN") return null;
1014
+ const childProps = { ...props, behavior };
1015
+ return /* @__PURE__ */ jsx29(
1016
+ FieldWrapper,
1017
+ {
1018
+ fieldId,
1019
+ label,
1020
+ required,
1021
+ tips,
1022
+ className,
1023
+ labelClassName,
1024
+ tipsClassName,
1025
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx29(CheckboxFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx29(CheckboxFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx29(CheckboxFieldPC, { ...childProps })
1026
+ }
1027
+ );
1028
+ }
1029
+
1030
+ // src/fields/DateField/index.tsx
1031
+ import { useEffect as useEffect8 } from "react";
1032
+
1033
+ // src/fields/DateField/DateFieldPC.tsx
1034
+ import { DatePicker } from "antd";
1035
+ import dayjs from "dayjs";
1036
+ import { jsx as jsx30 } from "react/jsx-runtime";
1037
+ function DateFieldPC({
1038
+ fieldId,
1039
+ placeholder,
1040
+ inputClassName,
1041
+ behavior,
1042
+ dateFormat,
1043
+ showTime,
1044
+ onChange
1045
+ }) {
1046
+ const { formData, setFieldValue } = useFormContext();
1047
+ const value = formData[fieldId];
1048
+ const disabled = behavior === "DISABLED";
1049
+ const format = dateFormat ?? (showTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD");
1050
+ const pickerValue = value && dayjs(value).isValid() ? dayjs(value) : null;
1051
+ const handleChange = (_date, dateString) => {
1052
+ const val = Array.isArray(dateString) ? dateString[0] : dateString ?? "";
1053
+ setFieldValue(fieldId, val || "");
1054
+ onChange?.(val || "");
1055
+ };
1056
+ return /* @__PURE__ */ jsx30(
1057
+ DatePicker,
1058
+ {
1059
+ className: inputClassName,
1060
+ style: { width: "100%" },
1061
+ value: pickerValue,
1062
+ placeholder,
1063
+ disabled,
1064
+ format,
1065
+ showTime,
1066
+ onChange: handleChange,
1067
+ "data-testid": `datefield-input-${fieldId}`
1068
+ }
1069
+ );
1070
+ }
1071
+
1072
+ // src/fields/DateField/DateFieldMobile.tsx
1073
+ import { useState as useState2 } from "react";
1074
+ import { DatePicker as DatePicker2 } from "antd-mobile";
1075
+ import dayjs2 from "dayjs";
1076
+ import { jsx as jsx31, jsxs as jsxs3 } from "react/jsx-runtime";
1077
+ function DateFieldMobile({
1078
+ fieldId,
1079
+ placeholder,
1080
+ inputClassName,
1081
+ behavior,
1082
+ dateFormat,
1083
+ showTime,
1084
+ onChange
1085
+ }) {
1086
+ const { formData, setFieldValue } = useFormContext();
1087
+ const value = formData[fieldId];
1088
+ const disabled = behavior === "DISABLED";
1089
+ const [visible, setVisible] = useState2(false);
1090
+ const format = dateFormat ?? (showTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD");
1091
+ const pickerValue = value && dayjs2(value).isValid() ? dayjs2(value).toDate() : void 0;
1092
+ const handleConfirm = (date) => {
1093
+ const dateStr = dayjs2(date).format(format);
1094
+ setFieldValue(fieldId, dateStr);
1095
+ onChange?.(dateStr);
1096
+ };
1097
+ return /* @__PURE__ */ jsxs3("div", { className: inputClassName, "data-testid": `datefield-input-${fieldId}`, children: [
1098
+ /* @__PURE__ */ jsx31(
1099
+ "div",
1100
+ {
1101
+ onClick: () => !disabled && setVisible(true),
1102
+ "data-testid": `datefield-trigger-${fieldId}`,
1103
+ children: value || placeholder || "\u8BF7\u9009\u62E9\u65E5\u671F"
1104
+ }
1105
+ ),
1106
+ /* @__PURE__ */ jsx31(
1107
+ DatePicker2,
1108
+ {
1109
+ visible,
1110
+ value: pickerValue,
1111
+ precision: showTime ? "second" : "day",
1112
+ onClose: () => setVisible(false),
1113
+ onConfirm: handleConfirm
1114
+ }
1115
+ )
1116
+ ] });
1117
+ }
1118
+
1119
+ // src/fields/DateField/DateFieldReadonly.tsx
1120
+ import { jsx as jsx32 } from "react/jsx-runtime";
1121
+ function DateFieldReadonly({ fieldId, readonlyClassName }) {
1122
+ const { formData } = useFormContext();
1123
+ const value = formData[fieldId];
1124
+ const display = value && value !== "" ? value : "--";
1125
+ return /* @__PURE__ */ jsx32(
1126
+ "div",
1127
+ {
1128
+ className: readonlyClassName || "sy-field-readonly-value",
1129
+ "data-testid": `datefield-readonly-${fieldId}`,
1130
+ children: display
1131
+ }
1132
+ );
1133
+ }
1134
+
1135
+ // src/fields/DateField/index.tsx
1136
+ import { jsx as jsx33 } from "react/jsx-runtime";
1137
+ function DateField(props) {
1138
+ const {
1139
+ fieldId,
1140
+ label,
1141
+ behavior: propBehavior,
1142
+ required,
1143
+ tips,
1144
+ defaultValue,
1145
+ className,
1146
+ labelClassName,
1147
+ tipsClassName
1148
+ } = props;
1149
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
1150
+ const { isMobile } = useDeviceDetect();
1151
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
1152
+ useEffect8(() => {
1153
+ registerField(fieldId);
1154
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
1155
+ setFieldValue(fieldId, defaultValue);
1156
+ }
1157
+ return () => unregisterField(fieldId);
1158
+ }, [fieldId]);
1159
+ if (behavior === "HIDDEN") return null;
1160
+ const childProps = { ...props, behavior };
1161
+ return /* @__PURE__ */ jsx33(
1162
+ FieldWrapper,
1163
+ {
1164
+ fieldId,
1165
+ label,
1166
+ required,
1167
+ tips,
1168
+ className,
1169
+ labelClassName,
1170
+ tipsClassName,
1171
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx33(DateFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx33(DateFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx33(DateFieldPC, { ...childProps })
1172
+ }
1173
+ );
1174
+ }
1175
+
1176
+ // src/fields/CascadeDateField/index.tsx
1177
+ import { useEffect as useEffect9 } from "react";
1178
+
1179
+ // src/fields/CascadeDateField/CascadeDateFieldPC.tsx
1180
+ import { DatePicker as DatePicker3 } from "antd";
1181
+ import dayjs3 from "dayjs";
1182
+ import { jsx as jsx34 } from "react/jsx-runtime";
1183
+ var { RangePicker } = DatePicker3;
1184
+ function CascadeDateFieldPC({
1185
+ fieldId,
1186
+ placeholder,
1187
+ inputClassName,
1188
+ behavior,
1189
+ dateFormat,
1190
+ onChange
1191
+ }) {
1192
+ const { formData, setFieldValue } = useFormContext();
1193
+ const value = formData[fieldId];
1194
+ const disabled = behavior === "DISABLED";
1195
+ const format = dateFormat ?? "YYYY-MM-DD";
1196
+ const startValue = value?.start && dayjs3(value.start).isValid() ? dayjs3(value.start) : null;
1197
+ const endValue = value?.end && dayjs3(value.end).isValid() ? dayjs3(value.end) : null;
1198
+ const pickerValue = startValue || endValue ? [startValue, endValue] : null;
1199
+ const handleChange = (_dates, dateStrings) => {
1200
+ const [start, end] = dateStrings;
1201
+ if (start && end) {
1202
+ const val = { start, end };
1203
+ setFieldValue(fieldId, val);
1204
+ onChange?.(val);
1205
+ } else {
1206
+ setFieldValue(fieldId, null);
1207
+ onChange?.(null);
1208
+ }
1209
+ };
1210
+ return /* @__PURE__ */ jsx34(
1211
+ RangePicker,
1212
+ {
1213
+ className: inputClassName,
1214
+ style: { width: "100%" },
1215
+ value: pickerValue,
1216
+ placeholder: placeholder ? [placeholder, placeholder] : void 0,
1217
+ disabled,
1218
+ format,
1219
+ onChange: handleChange,
1220
+ "data-testid": `cascadedatefield-input-${fieldId}`
1221
+ }
1222
+ );
1223
+ }
1224
+
1225
+ // src/fields/CascadeDateField/CascadeDateFieldMobile.tsx
1226
+ import { useState as useState3 } from "react";
1227
+ import { DatePicker as DatePicker4 } from "antd-mobile";
1228
+ import dayjs4 from "dayjs";
1229
+ import { jsx as jsx35, jsxs as jsxs4 } from "react/jsx-runtime";
1230
+ function CascadeDateFieldMobile({
1231
+ fieldId,
1232
+ inputClassName,
1233
+ behavior,
1234
+ startLabel,
1235
+ endLabel,
1236
+ dateFormat,
1237
+ onChange
1238
+ }) {
1239
+ const { formData, setFieldValue } = useFormContext();
1240
+ const value = formData[fieldId];
1241
+ const disabled = behavior === "DISABLED";
1242
+ const [startVisible, setStartVisible] = useState3(false);
1243
+ const [endVisible, setEndVisible] = useState3(false);
1244
+ const format = dateFormat ?? "YYYY-MM-DD";
1245
+ const startValue = value?.start && dayjs4(value.start).isValid() ? dayjs4(value.start).toDate() : void 0;
1246
+ const endValue = value?.end && dayjs4(value.end).isValid() ? dayjs4(value.end).toDate() : void 0;
1247
+ const handleStartConfirm = (date) => {
1248
+ const dateStr = dayjs4(date).format(format);
1249
+ const newVal = { start: dateStr, end: value?.end ?? "" };
1250
+ setFieldValue(fieldId, newVal);
1251
+ onChange?.(newVal);
1252
+ };
1253
+ const handleEndConfirm = (date) => {
1254
+ const dateStr = dayjs4(date).format(format);
1255
+ const newVal = { start: value?.start ?? "", end: dateStr };
1256
+ setFieldValue(fieldId, newVal);
1257
+ onChange?.(newVal);
1258
+ };
1259
+ return /* @__PURE__ */ jsxs4("div", { className: inputClassName, "data-testid": `cascadedatefield-input-${fieldId}`, children: [
1260
+ /* @__PURE__ */ jsx35(
1261
+ "div",
1262
+ {
1263
+ onClick: () => !disabled && setStartVisible(true),
1264
+ "data-testid": `cascadedatefield-start-${fieldId}`,
1265
+ children: value?.start || startLabel || "\u5F00\u59CB\u65E5\u671F"
1266
+ }
1267
+ ),
1268
+ /* @__PURE__ */ jsx35("span", { children: " ~ " }),
1269
+ /* @__PURE__ */ jsx35(
1270
+ "div",
1271
+ {
1272
+ onClick: () => !disabled && setEndVisible(true),
1273
+ "data-testid": `cascadedatefield-end-${fieldId}`,
1274
+ children: value?.end || endLabel || "\u7ED3\u675F\u65E5\u671F"
1275
+ }
1276
+ ),
1277
+ /* @__PURE__ */ jsx35(
1278
+ DatePicker4,
1279
+ {
1280
+ visible: startVisible,
1281
+ value: startValue,
1282
+ onClose: () => setStartVisible(false),
1283
+ onConfirm: handleStartConfirm
1284
+ }
1285
+ ),
1286
+ /* @__PURE__ */ jsx35(
1287
+ DatePicker4,
1288
+ {
1289
+ visible: endVisible,
1290
+ value: endValue,
1291
+ onClose: () => setEndVisible(false),
1292
+ onConfirm: handleEndConfirm
1293
+ }
1294
+ )
1295
+ ] });
1296
+ }
1297
+
1298
+ // src/fields/CascadeDateField/CascadeDateFieldReadonly.tsx
1299
+ import { jsx as jsx36 } from "react/jsx-runtime";
1300
+ function CascadeDateFieldReadonly({ fieldId, readonlyClassName }) {
1301
+ const { formData } = useFormContext();
1302
+ const value = formData[fieldId];
1303
+ const display = value && value.start && value.end ? `${value.start} ~ ${value.end}` : "--";
1304
+ return /* @__PURE__ */ jsx36(
1305
+ "div",
1306
+ {
1307
+ className: readonlyClassName || "sy-field-readonly-value",
1308
+ "data-testid": `cascadedatefield-readonly-${fieldId}`,
1309
+ children: display
1310
+ }
1311
+ );
1312
+ }
1313
+
1314
+ // src/fields/CascadeDateField/index.tsx
1315
+ import { jsx as jsx37 } from "react/jsx-runtime";
1316
+ function CascadeDateField(props) {
1317
+ const {
1318
+ fieldId,
1319
+ label,
1320
+ behavior: propBehavior,
1321
+ required,
1322
+ tips,
1323
+ defaultValue,
1324
+ className,
1325
+ labelClassName,
1326
+ tipsClassName
1327
+ } = props;
1328
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
1329
+ const { isMobile } = useDeviceDetect();
1330
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
1331
+ useEffect9(() => {
1332
+ registerField(fieldId);
1333
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
1334
+ setFieldValue(fieldId, defaultValue);
1335
+ }
1336
+ return () => unregisterField(fieldId);
1337
+ }, [fieldId]);
1338
+ if (behavior === "HIDDEN") return null;
1339
+ const childProps = { ...props, behavior };
1340
+ return /* @__PURE__ */ jsx37(
1341
+ FieldWrapper,
1342
+ {
1343
+ fieldId,
1344
+ label,
1345
+ required,
1346
+ tips,
1347
+ className,
1348
+ labelClassName,
1349
+ tipsClassName,
1350
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx37(CascadeDateFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx37(CascadeDateFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx37(CascadeDateFieldPC, { ...childProps })
1351
+ }
1352
+ );
1353
+ }
1354
+
1355
+ // src/fields/AttachmentField/index.tsx
1356
+ import { useEffect as useEffect10 } from "react";
1357
+
1358
+ // src/fields/AttachmentField/AttachmentFieldPC.tsx
1359
+ import { Upload, Button } from "antd";
1360
+ import { jsx as jsx38 } from "react/jsx-runtime";
1361
+ var createLocalItem = (file) => ({
1362
+ id: file.uid || `${Date.now()}-${file.name}`,
1363
+ uid: file.uid || `${Date.now()}-${file.name}`,
1364
+ url: "",
1365
+ name: file.name,
1366
+ status: "uploading",
1367
+ size: file.size,
1368
+ percent: 0
1369
+ });
1370
+ function AttachmentFieldPC({
1371
+ fieldId,
1372
+ behavior,
1373
+ maxCount,
1374
+ accept,
1375
+ maxSize,
1376
+ bucketName = "attachments",
1377
+ multiple = true,
1378
+ allowedTypes,
1379
+ onChange
1380
+ }) {
1381
+ const { formData, setFieldValue, api } = useFormContext();
1382
+ const value = formData[fieldId] ?? [];
1383
+ const disabled = behavior === "DISABLED";
1384
+ const setValue = (items) => {
1385
+ setFieldValue(fieldId, items);
1386
+ onChange?.(items);
1387
+ };
1388
+ const handleChange = (info) => {
1389
+ const fileList2 = info.fileList ?? [];
1390
+ const items = fileList2.map((f) => {
1391
+ const item = {
1392
+ url: String(f.url ?? f.response?.url ?? ""),
1393
+ name: String(f.name ?? ""),
1394
+ id: String(f.uid ?? f.response?.id ?? "")
1395
+ };
1396
+ if (f.uid ?? f.response?.uid) item.uid = String(f.uid ?? f.response?.uid);
1397
+ if (f.status) item.status = f.status;
1398
+ if (f.percent !== void 0) item.percent = f.percent;
1399
+ if (f.objectName ?? f.response?.objectName)
1400
+ item.objectName = f.objectName ?? f.response?.objectName;
1401
+ if (f.bucketName ?? f.response?.bucketName)
1402
+ item.bucketName = f.bucketName ?? f.response?.bucketName;
1403
+ if (f.size ?? f.response?.size) item.size = f.size ?? f.response?.size;
1404
+ if (f.type ?? f.response?.contentType) item.contentType = f.type ?? f.response?.contentType;
1405
+ return item;
1406
+ });
1407
+ setValue(items);
1408
+ };
1409
+ const handleRemove = (file) => {
1410
+ const removed = value.find((item) => item.id === file.uid || item.uid === file.uid);
1411
+ const newValue = value.filter((item) => item.id !== file.uid && item.uid !== file.uid);
1412
+ setValue(newValue);
1413
+ if (removed?.objectName) {
1414
+ api.deleteFile(removed.objectName, removed.bucketName || bucketName).catch(() => void 0);
1415
+ }
1416
+ };
1417
+ const handlePreview = async (file) => {
1418
+ const item = value.find((current) => current.id === file.uid || current.uid === file.uid);
1419
+ let url = item?.downloadUrl || item?.url || file.url;
1420
+ if (item?.objectName) {
1421
+ const ticket = await api.createDownloadTicket(
1422
+ item.bucketName || bucketName,
1423
+ item.objectName,
1424
+ item.name
1425
+ );
1426
+ url = typeof ticket === "string" ? ticket : ticket?.downloadUrl || ticket?.url || url;
1427
+ }
1428
+ if (url) window.open(url, "_blank", "noopener,noreferrer");
1429
+ };
1430
+ const fileList = value.map((item) => ({
1431
+ uid: item.id,
1432
+ name: item.name,
1433
+ url: item.url,
1434
+ status: item.status ?? "done",
1435
+ percent: item.percent
1436
+ }));
1437
+ const beforeUpload = (file) => {
1438
+ if (maxSize && file.size / 1024 / 1024 > maxSize) {
1439
+ return false;
1440
+ }
1441
+ const ext = String(file.name || "").split(".").pop()?.toLowerCase();
1442
+ if (allowedTypes?.length && ext && !allowedTypes.includes(ext)) {
1443
+ return false;
1444
+ }
1445
+ return true;
1446
+ };
1447
+ const customRequest = async ({ file, onProgress, onSuccess, onError }) => {
1448
+ const currentFile = file;
1449
+ const localItem = createLocalItem(currentFile);
1450
+ const nextValue = multiple ? [...value, localItem].slice(0, maxCount ?? Infinity) : [localItem];
1451
+ setValue(nextValue);
1452
+ try {
1453
+ const uploaded = await api.uploadFile(currentFile, bucketName, (percent) => {
1454
+ onProgress?.({ percent });
1455
+ setValue(
1456
+ nextValue.map(
1457
+ (item) => item.id === localItem.id ? { ...item, percent, status: "uploading" } : item
1458
+ )
1459
+ );
1460
+ });
1461
+ const completed = {
1462
+ ...uploaded,
1463
+ id: uploaded.id || localItem.id,
1464
+ uid: uploaded.uid || localItem.uid
1465
+ };
1466
+ setValue(nextValue.map((item) => item.id === localItem.id ? completed : item));
1467
+ onSuccess?.(completed);
1468
+ } catch (error) {
1469
+ const failed = {
1470
+ ...localItem,
1471
+ status: "error",
1472
+ error: error?.message || "\u4E0A\u4F20\u5931\u8D25"
1473
+ };
1474
+ setValue(nextValue.map((item) => item.id === localItem.id ? failed : item));
1475
+ onError?.(error);
1476
+ }
1477
+ };
1478
+ return /* @__PURE__ */ jsx38(
1479
+ Upload,
1480
+ {
1481
+ "data-testid": `attachmentfield-input-${fieldId}`,
1482
+ fileList,
1483
+ customRequest,
1484
+ accept,
1485
+ maxCount,
1486
+ multiple,
1487
+ disabled,
1488
+ onChange: handleChange,
1489
+ onRemove: handleRemove,
1490
+ onPreview: handlePreview,
1491
+ beforeUpload,
1492
+ children: /* @__PURE__ */ jsx38(Button, { disabled, "data-testid": `attachmentfield-upload-btn-${fieldId}`, children: "\u4E0A\u4F20\u6587\u4EF6" })
1493
+ }
1494
+ );
1495
+ }
1496
+
1497
+ // src/fields/AttachmentField/AttachmentFieldMobile.tsx
1498
+ import { jsx as jsx39, jsxs as jsxs5 } from "react/jsx-runtime";
1499
+ function AttachmentFieldMobile({
1500
+ fieldId,
1501
+ behavior,
1502
+ maxCount,
1503
+ accept,
1504
+ maxSize,
1505
+ bucketName = "attachments",
1506
+ multiple = true,
1507
+ allowedTypes,
1508
+ onChange
1509
+ }) {
1510
+ const { formData, setFieldValue, api } = useFormContext();
1511
+ const value = formData[fieldId] ?? [];
1512
+ const disabled = behavior === "DISABLED";
1513
+ const handleFileSelect = (e) => {
1514
+ const files = e.target.files;
1515
+ if (!files) return;
1516
+ const acceptedFiles = Array.from(files).filter((file) => {
1517
+ if (maxSize && file.size / 1024 / 1024 > maxSize) return false;
1518
+ const ext = String(file.name || "").split(".").pop()?.toLowerCase();
1519
+ if (allowedTypes?.length && ext && !allowedTypes.includes(ext)) return false;
1520
+ return true;
1521
+ });
1522
+ const newItems = acceptedFiles.map((f) => {
1523
+ const uid = `${Date.now()}-${f.name}`;
1524
+ return {
1525
+ url: URL.createObjectURL(f),
1526
+ name: f.name,
1527
+ id: uid,
1528
+ uid,
1529
+ status: "uploading",
1530
+ size: f.size
1531
+ };
1532
+ });
1533
+ const merged = [...value, ...newItems].slice(0, maxCount ?? Infinity);
1534
+ setFieldValue(fieldId, merged);
1535
+ onChange?.(merged);
1536
+ acceptedFiles.forEach((file, index) => {
1537
+ const localId = newItems[index]?.id;
1538
+ api.uploadFile(file, bucketName).then((uploaded) => {
1539
+ const current = (formData[fieldId] ?? merged).map(
1540
+ (item) => item.id === localId ? { ...uploaded, id: uploaded.id || localId } : item
1541
+ );
1542
+ setFieldValue(fieldId, current);
1543
+ onChange?.(current);
1544
+ }).catch(() => void 0);
1545
+ });
1546
+ };
1547
+ const handleRemove = (id) => {
1548
+ const removed = value.find((item) => item.id === id);
1549
+ const newValue = value.filter((item) => item.id !== id);
1550
+ setFieldValue(fieldId, newValue);
1551
+ onChange?.(newValue);
1552
+ if (removed?.objectName) {
1553
+ api.deleteFile(removed.objectName, removed.bucketName || bucketName).catch(() => void 0);
1554
+ }
1555
+ };
1556
+ return /* @__PURE__ */ jsxs5("div", { "data-testid": `attachmentfield-mobile-${fieldId}`, children: [
1557
+ /* @__PURE__ */ jsx39(
1558
+ "input",
1559
+ {
1560
+ type: "file",
1561
+ accept,
1562
+ multiple,
1563
+ disabled,
1564
+ onChange: handleFileSelect,
1565
+ "data-testid": `attachmentfield-file-input-${fieldId}`
1566
+ }
1567
+ ),
1568
+ /* @__PURE__ */ jsx39("ul", { "data-testid": `attachmentfield-list-${fieldId}`, children: value.map((item) => /* @__PURE__ */ jsxs5("li", { "data-testid": `attachmentfield-item-${item.id}`, children: [
1569
+ /* @__PURE__ */ jsx39("span", { children: item.name }),
1570
+ /* @__PURE__ */ jsx39(
1571
+ "button",
1572
+ {
1573
+ type: "button",
1574
+ disabled,
1575
+ onClick: () => handleRemove(item.id),
1576
+ "data-testid": `attachmentfield-remove-${item.id}`,
1577
+ children: "\u5220\u9664"
1578
+ }
1579
+ )
1580
+ ] }, item.id)) })
1581
+ ] });
1582
+ }
1583
+
1584
+ // src/fields/AttachmentField/AttachmentFieldReadonly.tsx
1585
+ import { jsx as jsx40 } from "react/jsx-runtime";
1586
+ function AttachmentFieldReadonly({ fieldId, readonlyClassName }) {
1587
+ const { formData, api } = useFormContext();
1588
+ const value = formData[fieldId] ?? [];
1589
+ if (value.length === 0) {
1590
+ return /* @__PURE__ */ jsx40(
1591
+ "div",
1592
+ {
1593
+ className: readonlyClassName || "sy-field-readonly-value",
1594
+ "data-testid": `attachmentfield-readonly-${fieldId}`,
1595
+ children: "--"
1596
+ }
1597
+ );
1598
+ }
1599
+ return /* @__PURE__ */ jsx40(
1600
+ "ul",
1601
+ {
1602
+ className: readonlyClassName || "sy-field-readonly-value",
1603
+ "data-testid": `attachmentfield-readonly-${fieldId}`,
1604
+ children: value.map((item) => /* @__PURE__ */ jsx40("li", { children: /* @__PURE__ */ jsx40(
1605
+ "a",
1606
+ {
1607
+ href: item.downloadUrl || item.url || "#",
1608
+ target: "_blank",
1609
+ rel: "noopener noreferrer",
1610
+ onClick: async (event) => {
1611
+ if (!item.objectName) return;
1612
+ event.preventDefault();
1613
+ const ticket = await api.createDownloadTicket(
1614
+ item.bucketName || "attachments",
1615
+ item.objectName,
1616
+ item.name
1617
+ );
1618
+ const url = typeof ticket === "string" ? ticket : ticket?.downloadUrl || ticket?.url || item.downloadUrl || item.url;
1619
+ if (url) window.open(url, "_blank", "noopener,noreferrer");
1620
+ },
1621
+ children: item.name
1622
+ }
1623
+ ) }, item.id))
1624
+ }
1625
+ );
1626
+ }
1627
+
1628
+ // src/fields/AttachmentField/index.tsx
1629
+ import { jsx as jsx41 } from "react/jsx-runtime";
1630
+ function AttachmentField(props) {
1631
+ const {
1632
+ fieldId,
1633
+ label,
1634
+ behavior: propBehavior,
1635
+ required,
1636
+ tips,
1637
+ defaultValue,
1638
+ className,
1639
+ labelClassName,
1640
+ tipsClassName
1641
+ } = props;
1642
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
1643
+ const { isMobile } = useDeviceDetect();
1644
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
1645
+ useEffect10(() => {
1646
+ registerField(fieldId);
1647
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
1648
+ setFieldValue(fieldId, defaultValue);
1649
+ }
1650
+ return () => unregisterField(fieldId);
1651
+ }, [fieldId]);
1652
+ if (behavior === "HIDDEN") return null;
1653
+ const childProps = { ...props, behavior };
1654
+ return /* @__PURE__ */ jsx41(
1655
+ FieldWrapper,
1656
+ {
1657
+ fieldId,
1658
+ label,
1659
+ required,
1660
+ tips,
1661
+ className,
1662
+ labelClassName,
1663
+ tipsClassName,
1664
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx41(AttachmentFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx41(AttachmentFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx41(AttachmentFieldPC, { ...childProps })
1665
+ }
1666
+ );
1667
+ }
1668
+
1669
+ // src/fields/ImageField/index.tsx
1670
+ import { useEffect as useEffect11 } from "react";
1671
+
1672
+ // src/fields/ImageField/ImageFieldPC.tsx
1673
+ import { Upload as Upload2 } from "antd";
1674
+ import { jsx as jsx42 } from "react/jsx-runtime";
1675
+ var createLocalItem2 = (file) => ({
1676
+ id: file.uid || `${Date.now()}-${file.name}`,
1677
+ uid: file.uid || `${Date.now()}-${file.name}`,
1678
+ url: "",
1679
+ name: file.name,
1680
+ status: "uploading",
1681
+ size: file.size,
1682
+ percent: 0
1683
+ });
1684
+ function ImageFieldPC({
1685
+ fieldId,
1686
+ behavior,
1687
+ maxCount,
1688
+ accept,
1689
+ bucketName = "images",
1690
+ multiple = true,
1691
+ maxSize,
1692
+ onChange
1693
+ }) {
1694
+ const { formData, setFieldValue, api } = useFormContext();
1695
+ const value = formData[fieldId] ?? [];
1696
+ const disabled = behavior === "DISABLED";
1697
+ const setValue = (items) => {
1698
+ setFieldValue(fieldId, items);
1699
+ onChange?.(items);
1700
+ };
1701
+ const handleChange = (info) => {
1702
+ const fileList2 = info.fileList ?? [];
1703
+ const items = fileList2.map((f) => {
1704
+ const item = {
1705
+ url: String(f.url ?? f.thumbUrl ?? f.response?.url ?? ""),
1706
+ name: String(f.name ?? ""),
1707
+ id: String(f.uid ?? f.response?.id ?? "")
1708
+ };
1709
+ if (f.uid ?? f.response?.uid) item.uid = String(f.uid ?? f.response?.uid);
1710
+ if (f.status) item.status = f.status;
1711
+ if (f.percent !== void 0) item.percent = f.percent;
1712
+ if (f.objectName ?? f.response?.objectName)
1713
+ item.objectName = f.objectName ?? f.response?.objectName;
1714
+ if (f.bucketName ?? f.response?.bucketName)
1715
+ item.bucketName = f.bucketName ?? f.response?.bucketName;
1716
+ if (f.size ?? f.response?.size) item.size = f.size ?? f.response?.size;
1717
+ if (f.type ?? f.response?.contentType) item.contentType = f.type ?? f.response?.contentType;
1718
+ return item;
1719
+ });
1720
+ setValue(items);
1721
+ };
1722
+ const handleRemove = (file) => {
1723
+ const removed = value.find((item) => item.id === file.uid || item.uid === file.uid);
1724
+ const newValue = value.filter((item) => item.id !== file.uid && item.uid !== file.uid);
1725
+ setValue(newValue);
1726
+ if (removed?.objectName) {
1727
+ api.deleteFile(removed.objectName, removed.bucketName || bucketName).catch(() => void 0);
1728
+ }
1729
+ };
1730
+ const handlePreview = async (file) => {
1731
+ const item = value.find((current) => current.id === file.uid || current.uid === file.uid);
1732
+ let url = item?.previewUrl || item?.url || file.url || file.thumbUrl;
1733
+ if (item?.objectName) {
1734
+ const ticket = await api.createFileAccessTicket(
1735
+ item.bucketName || bucketName,
1736
+ item.objectName,
1737
+ item.name,
1738
+ "preview"
1739
+ );
1740
+ url = typeof ticket === "string" ? ticket : ticket?.previewUrl || ticket?.url || url;
1741
+ }
1742
+ if (url) window.open(url, "_blank", "noopener,noreferrer");
1743
+ };
1744
+ const fileList = value.map((item) => ({
1745
+ uid: item.id,
1746
+ name: item.name,
1747
+ url: item.url,
1748
+ thumbUrl: item.url,
1749
+ status: item.status ?? "done",
1750
+ percent: item.percent
1751
+ }));
1752
+ const beforeUpload = (file) => {
1753
+ if (!String(file.type || "").startsWith("image/") && !/\.(png|jpe?g|gif|bmp|svg|webp)$/i.test(file.name)) {
1754
+ return false;
1755
+ }
1756
+ if (maxSize && file.size / 1024 / 1024 > maxSize) {
1757
+ return false;
1758
+ }
1759
+ return true;
1760
+ };
1761
+ const customRequest = async ({ file, onProgress, onSuccess, onError }) => {
1762
+ const currentFile = file;
1763
+ const localItem = createLocalItem2(currentFile);
1764
+ const nextValue = multiple ? [...value, localItem].slice(0, maxCount ?? Infinity) : [localItem];
1765
+ setValue(nextValue);
1766
+ try {
1767
+ const uploaded = await api.uploadFile(currentFile, bucketName, (percent) => {
1768
+ onProgress?.({ percent });
1769
+ setValue(
1770
+ nextValue.map(
1771
+ (item) => item.id === localItem.id ? { ...item, percent, status: "uploading" } : item
1772
+ )
1773
+ );
1774
+ });
1775
+ const completed = {
1776
+ ...uploaded,
1777
+ id: uploaded.id || localItem.id,
1778
+ uid: uploaded.uid || localItem.uid
1779
+ };
1780
+ setValue(nextValue.map((item) => item.id === localItem.id ? completed : item));
1781
+ onSuccess?.(completed);
1782
+ } catch (error) {
1783
+ const failed = {
1784
+ ...localItem,
1785
+ status: "error",
1786
+ error: error?.message || "\u4E0A\u4F20\u5931\u8D25"
1787
+ };
1788
+ setValue(nextValue.map((item) => item.id === localItem.id ? failed : item));
1789
+ onError?.(error);
1790
+ }
1791
+ };
1792
+ return /* @__PURE__ */ jsx42(
1793
+ Upload2,
1794
+ {
1795
+ "data-testid": `imagefield-input-${fieldId}`,
1796
+ listType: "picture-card",
1797
+ fileList,
1798
+ customRequest,
1799
+ accept: accept ?? "image/*",
1800
+ maxCount,
1801
+ multiple,
1802
+ disabled,
1803
+ onChange: handleChange,
1804
+ onRemove: handleRemove,
1805
+ onPreview: handlePreview,
1806
+ beforeUpload,
1807
+ children: (!maxCount || value.length < maxCount) && /* @__PURE__ */ jsx42("div", { "data-testid": `imagefield-upload-btn-${fieldId}`, children: "+ \u4E0A\u4F20\u56FE\u7247" })
1808
+ }
1809
+ );
1810
+ }
1811
+
1812
+ // src/fields/ImageField/ImageFieldMobile.tsx
1813
+ import { jsx as jsx43, jsxs as jsxs6 } from "react/jsx-runtime";
1814
+ function ImageFieldMobile({
1815
+ fieldId,
1816
+ behavior,
1817
+ maxCount,
1818
+ accept,
1819
+ bucketName = "images",
1820
+ multiple = true,
1821
+ maxSize,
1822
+ onChange
1823
+ }) {
1824
+ const { formData, setFieldValue, api } = useFormContext();
1825
+ const value = formData[fieldId] ?? [];
1826
+ const disabled = behavior === "DISABLED";
1827
+ const handleFileSelect = (e) => {
1828
+ const files = e.target.files;
1829
+ if (!files) return;
1830
+ const acceptedFiles = Array.from(files).filter((file) => {
1831
+ const isImage = String(file.type || "").startsWith("image/") || /\.(png|jpe?g|gif|bmp|svg|webp)$/i.test(file.name);
1832
+ if (!isImage) return false;
1833
+ if (maxSize && file.size / 1024 / 1024 > maxSize) return false;
1834
+ return true;
1835
+ });
1836
+ const newItems = acceptedFiles.map((f) => {
1837
+ const uid = `${Date.now()}-${f.name}`;
1838
+ return {
1839
+ url: URL.createObjectURL(f),
1840
+ name: f.name,
1841
+ id: uid,
1842
+ uid,
1843
+ status: "uploading",
1844
+ size: f.size
1845
+ };
1846
+ });
1847
+ const merged = [...value, ...newItems].slice(0, maxCount ?? Infinity);
1848
+ setFieldValue(fieldId, merged);
1849
+ onChange?.(merged);
1850
+ acceptedFiles.forEach((file, index) => {
1851
+ const localId = newItems[index]?.id;
1852
+ api.uploadFile(file, bucketName).then((uploaded) => {
1853
+ const current = (formData[fieldId] ?? merged).map(
1854
+ (item) => item.id === localId ? { ...uploaded, id: uploaded.id || localId } : item
1855
+ );
1856
+ setFieldValue(fieldId, current);
1857
+ onChange?.(current);
1858
+ }).catch(() => void 0);
1859
+ });
1860
+ };
1861
+ const handleRemove = (id) => {
1862
+ const removed = value.find((item) => item.id === id);
1863
+ const newValue = value.filter((item) => item.id !== id);
1864
+ setFieldValue(fieldId, newValue);
1865
+ onChange?.(newValue);
1866
+ if (removed?.objectName) {
1867
+ api.deleteFile(removed.objectName, removed.bucketName || bucketName).catch(() => void 0);
1868
+ }
1869
+ };
1870
+ return /* @__PURE__ */ jsxs6("div", { "data-testid": `imagefield-mobile-${fieldId}`, children: [
1871
+ /* @__PURE__ */ jsx43(
1872
+ "input",
1873
+ {
1874
+ type: "file",
1875
+ accept: accept ?? "image/*",
1876
+ multiple,
1877
+ disabled,
1878
+ onChange: handleFileSelect,
1879
+ "data-testid": `imagefield-file-input-${fieldId}`
1880
+ }
1881
+ ),
1882
+ /* @__PURE__ */ jsx43("div", { "data-testid": `imagefield-grid-${fieldId}`, children: value.map((item) => /* @__PURE__ */ jsxs6("div", { "data-testid": `imagefield-thumb-${item.id}`, children: [
1883
+ /* @__PURE__ */ jsx43("img", { src: item.url, alt: item.name }),
1884
+ /* @__PURE__ */ jsx43(
1885
+ "button",
1886
+ {
1887
+ type: "button",
1888
+ disabled,
1889
+ onClick: () => handleRemove(item.id),
1890
+ "data-testid": `imagefield-remove-${item.id}`,
1891
+ children: "\u5220\u9664"
1892
+ }
1893
+ )
1894
+ ] }, item.id)) })
1895
+ ] });
1896
+ }
1897
+
1898
+ // src/fields/ImageField/ImageFieldReadonly.tsx
1899
+ import { jsx as jsx44 } from "react/jsx-runtime";
1900
+ function ImageFieldReadonly({ fieldId, readonlyClassName }) {
1901
+ const { formData, api } = useFormContext();
1902
+ const value = formData[fieldId] ?? [];
1903
+ if (value.length === 0) {
1904
+ return /* @__PURE__ */ jsx44(
1905
+ "div",
1906
+ {
1907
+ className: readonlyClassName || "sy-field-readonly-value",
1908
+ "data-testid": `imagefield-readonly-${fieldId}`,
1909
+ children: "--"
1910
+ }
1911
+ );
1912
+ }
1913
+ return /* @__PURE__ */ jsx44(
1914
+ "div",
1915
+ {
1916
+ className: readonlyClassName || "sy-field-readonly-value",
1917
+ "data-testid": `imagefield-readonly-${fieldId}`,
1918
+ children: value.map((item) => /* @__PURE__ */ jsx44(
1919
+ "img",
1920
+ {
1921
+ src: item.previewUrl || item.url,
1922
+ alt: item.name,
1923
+ "data-testid": `imagefield-readonly-img-${item.id}`,
1924
+ style: { width: 80, height: 80, objectFit: "cover", marginRight: 8 },
1925
+ onClick: async () => {
1926
+ let url = item.previewUrl || item.url;
1927
+ if (item.objectName) {
1928
+ const ticket = await api.createFileAccessTicket(
1929
+ item.bucketName || "images",
1930
+ item.objectName,
1931
+ item.name,
1932
+ "preview"
1933
+ );
1934
+ url = typeof ticket === "string" ? ticket : ticket?.previewUrl || ticket?.url || url;
1935
+ }
1936
+ if (url) window.open(url, "_blank", "noopener,noreferrer");
1937
+ }
1938
+ },
1939
+ item.id
1940
+ ))
1941
+ }
1942
+ );
1943
+ }
1944
+
1945
+ // src/fields/ImageField/index.tsx
1946
+ import { jsx as jsx45 } from "react/jsx-runtime";
1947
+ function ImageField(props) {
1948
+ const {
1949
+ fieldId,
1950
+ label,
1951
+ behavior: propBehavior,
1952
+ required,
1953
+ tips,
1954
+ defaultValue,
1955
+ className,
1956
+ labelClassName,
1957
+ tipsClassName
1958
+ } = props;
1959
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
1960
+ const { isMobile } = useDeviceDetect();
1961
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
1962
+ useEffect11(() => {
1963
+ registerField(fieldId);
1964
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
1965
+ setFieldValue(fieldId, defaultValue);
1966
+ }
1967
+ return () => unregisterField(fieldId);
1968
+ }, [fieldId]);
1969
+ if (behavior === "HIDDEN") return null;
1970
+ const childProps = { ...props, behavior };
1971
+ return /* @__PURE__ */ jsx45(
1972
+ FieldWrapper,
1973
+ {
1974
+ fieldId,
1975
+ label,
1976
+ required,
1977
+ tips,
1978
+ className,
1979
+ labelClassName,
1980
+ tipsClassName,
1981
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx45(ImageFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx45(ImageFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx45(ImageFieldPC, { ...childProps })
1982
+ }
1983
+ );
1984
+ }
1985
+
1986
+ // src/fields/SubFormField/index.tsx
1987
+ import { useEffect as useEffect12 } from "react";
1988
+
1989
+ // src/fields/SubFormField/SubFormCell.tsx
1990
+ import { useMemo } from "react";
1991
+ import { jsx as jsx46 } from "react/jsx-runtime";
1992
+ var stringifyFallbackValue = (value) => {
1993
+ if (value === void 0 || value === null) return "";
1994
+ if (typeof value === "string" || typeof value === "number") return value;
1995
+ return JSON.stringify(value);
1996
+ };
1997
+ function SubFormCell({
1998
+ parentFieldId,
1999
+ rowIndex,
2000
+ column,
2001
+ row,
2002
+ behavior,
2003
+ fallbackTestId,
2004
+ onCellChange
2005
+ }) {
2006
+ const Component = useComponent(column.componentName);
2007
+ const parentContext = useFormContext();
2008
+ const scopedFieldId = `${parentFieldId}.${rowIndex}.${column.fieldId}`;
2009
+ const cellValue = row[column.fieldId];
2010
+ const resolvedBehavior = behavior ?? "NORMAL";
2011
+ const childContext = useMemo(
2012
+ () => ({
2013
+ ...parentContext,
2014
+ formData: {
2015
+ ...parentContext.formData,
2016
+ [scopedFieldId]: cellValue
2017
+ },
2018
+ fieldBehaviors: {
2019
+ ...parentContext.fieldBehaviors,
2020
+ [scopedFieldId]: resolvedBehavior
2021
+ },
2022
+ setFieldValue: (fieldId, value) => {
2023
+ if (fieldId === scopedFieldId) {
2024
+ onCellChange(rowIndex, column.fieldId, value);
2025
+ return;
2026
+ }
2027
+ parentContext.setFieldValue(fieldId, value);
2028
+ },
2029
+ getFieldValue: (fieldId) => fieldId === scopedFieldId ? cellValue : parentContext.getFieldValue(fieldId),
2030
+ getFormData: () => ({
2031
+ ...parentContext.getFormData(),
2032
+ [scopedFieldId]: cellValue
2033
+ }),
2034
+ validateField: (fieldId) => fieldId === scopedFieldId ? Promise.resolve(true) : parentContext.validateField(fieldId),
2035
+ registerField: (fieldId) => {
2036
+ if (fieldId !== scopedFieldId) parentContext.registerField(fieldId);
2037
+ },
2038
+ unregisterField: (fieldId) => {
2039
+ if (fieldId !== scopedFieldId) parentContext.unregisterField(fieldId);
2040
+ }
2041
+ }),
2042
+ [
2043
+ cellValue,
2044
+ column.fieldId,
2045
+ onCellChange,
2046
+ parentContext,
2047
+ resolvedBehavior,
2048
+ rowIndex,
2049
+ scopedFieldId
2050
+ ]
2051
+ );
2052
+ if (!Component) {
2053
+ return /* @__PURE__ */ jsx46(
2054
+ "input",
2055
+ {
2056
+ value: stringifyFallbackValue(cellValue),
2057
+ disabled: resolvedBehavior === "DISABLED",
2058
+ onChange: (event) => onCellChange(rowIndex, column.fieldId, event.target.value),
2059
+ "data-testid": fallbackTestId
2060
+ }
2061
+ );
2062
+ }
2063
+ const columnProps = { ...column };
2064
+ delete columnProps.fieldId;
2065
+ delete columnProps.componentName;
2066
+ delete columnProps.label;
2067
+ delete columnProps.required;
2068
+ delete columnProps.tips;
2069
+ return /* @__PURE__ */ jsx46(FormContext.Provider, { value: childContext, children: /* @__PURE__ */ jsx46(
2070
+ Component,
2071
+ {
2072
+ ...columnProps,
2073
+ fieldId: scopedFieldId,
2074
+ label: "",
2075
+ behavior: resolvedBehavior,
2076
+ required: false,
2077
+ tips: void 0
2078
+ }
2079
+ ) });
2080
+ }
2081
+
2082
+ // src/fields/SubFormField/SubFormFieldPC.tsx
2083
+ import { jsx as jsx47, jsxs as jsxs7 } from "react/jsx-runtime";
2084
+ function SubFormFieldPC({
2085
+ fieldId,
2086
+ behavior,
2087
+ columns,
2088
+ maxRows,
2089
+ minRows,
2090
+ onChange
2091
+ }) {
2092
+ const { formData, setFieldValue } = useFormContext();
2093
+ const rows = formData[fieldId] ?? [];
2094
+ const disabled = behavior === "DISABLED";
2095
+ const handleAddRow = () => {
2096
+ if (maxRows && rows.length >= maxRows) return;
2097
+ const emptyRow = {};
2098
+ columns.forEach((col) => {
2099
+ emptyRow[col.fieldId] = col.defaultValue ?? "";
2100
+ });
2101
+ const newRows = [...rows, emptyRow];
2102
+ setFieldValue(fieldId, newRows);
2103
+ onChange?.(newRows);
2104
+ };
2105
+ const handleRemoveRow = (index) => {
2106
+ if (minRows && rows.length <= minRows) return;
2107
+ const newRows = rows.filter((_, i) => i !== index);
2108
+ setFieldValue(fieldId, newRows);
2109
+ onChange?.(newRows);
2110
+ };
2111
+ const handleCellChange = (rowIndex, colId, value) => {
2112
+ const newRows = rows.map((row, i) => i === rowIndex ? { ...row, [colId]: value } : row);
2113
+ setFieldValue(fieldId, newRows);
2114
+ onChange?.(newRows);
2115
+ };
2116
+ return /* @__PURE__ */ jsxs7("div", { "data-testid": `subformfield-pc-${fieldId}`, children: [
2117
+ /* @__PURE__ */ jsxs7("table", { "data-testid": `subformfield-table-${fieldId}`, children: [
2118
+ /* @__PURE__ */ jsx47("thead", { children: /* @__PURE__ */ jsxs7("tr", { children: [
2119
+ columns.map((col) => /* @__PURE__ */ jsx47("th", { children: col.label }, col.fieldId)),
2120
+ /* @__PURE__ */ jsx47("th", { children: "\u64CD\u4F5C" })
2121
+ ] }) }),
2122
+ /* @__PURE__ */ jsx47("tbody", { children: rows.map((row, rowIndex) => /* @__PURE__ */ jsxs7("tr", { "data-testid": `subformfield-row-${fieldId}-${rowIndex}`, children: [
2123
+ columns.map((col) => /* @__PURE__ */ jsx47("td", { children: /* @__PURE__ */ jsx47(
2124
+ SubFormCell,
2125
+ {
2126
+ parentFieldId: fieldId,
2127
+ rowIndex,
2128
+ column: col,
2129
+ row,
2130
+ behavior,
2131
+ fallbackTestId: `subformfield-cell-${fieldId}-${rowIndex}-${col.fieldId}`,
2132
+ onCellChange: handleCellChange
2133
+ }
2134
+ ) }, col.fieldId)),
2135
+ /* @__PURE__ */ jsx47("td", { children: /* @__PURE__ */ jsx47(
2136
+ "button",
2137
+ {
2138
+ type: "button",
2139
+ disabled,
2140
+ onClick: () => handleRemoveRow(rowIndex),
2141
+ "data-testid": `subformfield-remove-${fieldId}-${rowIndex}`,
2142
+ children: "\u5220\u9664"
2143
+ }
2144
+ ) })
2145
+ ] }, rowIndex)) })
2146
+ ] }),
2147
+ /* @__PURE__ */ jsx47(
2148
+ "button",
2149
+ {
2150
+ type: "button",
2151
+ disabled,
2152
+ onClick: handleAddRow,
2153
+ "data-testid": `subformfield-add-${fieldId}`,
2154
+ children: "\u6DFB\u52A0\u884C"
2155
+ }
2156
+ )
2157
+ ] });
2158
+ }
2159
+
2160
+ // src/fields/SubFormField/SubFormFieldMobile.tsx
2161
+ import { jsx as jsx48, jsxs as jsxs8 } from "react/jsx-runtime";
2162
+ function SubFormFieldMobile({
2163
+ fieldId,
2164
+ behavior,
2165
+ columns,
2166
+ maxRows,
2167
+ minRows,
2168
+ onChange
2169
+ }) {
2170
+ const { formData, setFieldValue } = useFormContext();
2171
+ const rows = formData[fieldId] ?? [];
2172
+ const disabled = behavior === "DISABLED";
2173
+ const handleAddRow = () => {
2174
+ if (maxRows && rows.length >= maxRows) return;
2175
+ const emptyRow = {};
2176
+ columns.forEach((col) => {
2177
+ emptyRow[col.fieldId] = col.defaultValue ?? "";
2178
+ });
2179
+ const newRows = [...rows, emptyRow];
2180
+ setFieldValue(fieldId, newRows);
2181
+ onChange?.(newRows);
2182
+ };
2183
+ const handleRemoveRow = (index) => {
2184
+ if (minRows && rows.length <= minRows) return;
2185
+ const newRows = rows.filter((_, i) => i !== index);
2186
+ setFieldValue(fieldId, newRows);
2187
+ onChange?.(newRows);
2188
+ };
2189
+ const handleCellChange = (rowIndex, colId, value) => {
2190
+ const newRows = rows.map((row, i) => i === rowIndex ? { ...row, [colId]: value } : row);
2191
+ setFieldValue(fieldId, newRows);
2192
+ onChange?.(newRows);
2193
+ };
2194
+ return /* @__PURE__ */ jsxs8("div", { "data-testid": `subformfield-mobile-${fieldId}`, children: [
2195
+ rows.map((row, rowIndex) => /* @__PURE__ */ jsxs8("div", { "data-testid": `subformfield-card-${fieldId}-${rowIndex}`, children: [
2196
+ columns.map((col) => /* @__PURE__ */ jsxs8("div", { children: [
2197
+ /* @__PURE__ */ jsx48("label", { children: col.label }),
2198
+ /* @__PURE__ */ jsx48(
2199
+ SubFormCell,
2200
+ {
2201
+ parentFieldId: fieldId,
2202
+ rowIndex,
2203
+ column: col,
2204
+ row,
2205
+ behavior,
2206
+ fallbackTestId: `subformfield-mobile-cell-${fieldId}-${rowIndex}-${col.fieldId}`,
2207
+ onCellChange: handleCellChange
2208
+ }
2209
+ )
2210
+ ] }, col.fieldId)),
2211
+ /* @__PURE__ */ jsx48(
2212
+ "button",
2213
+ {
2214
+ type: "button",
2215
+ disabled,
2216
+ onClick: () => handleRemoveRow(rowIndex),
2217
+ "data-testid": `subformfield-mobile-remove-${fieldId}-${rowIndex}`,
2218
+ children: "\u5220\u9664"
2219
+ }
2220
+ )
2221
+ ] }, rowIndex)),
2222
+ /* @__PURE__ */ jsx48(
2223
+ "button",
2224
+ {
2225
+ type: "button",
2226
+ disabled,
2227
+ onClick: handleAddRow,
2228
+ "data-testid": `subformfield-mobile-add-${fieldId}`,
2229
+ children: "\u6DFB\u52A0\u884C"
2230
+ }
2231
+ )
2232
+ ] });
2233
+ }
2234
+
2235
+ // src/fields/SubFormField/SubFormFieldReadonly.tsx
2236
+ import { jsx as jsx49, jsxs as jsxs9 } from "react/jsx-runtime";
2237
+ function SubFormFieldReadonly({ fieldId, columns, readonlyClassName }) {
2238
+ const { formData } = useFormContext();
2239
+ const rows = formData[fieldId] ?? [];
2240
+ if (rows.length === 0) {
2241
+ return /* @__PURE__ */ jsx49(
2242
+ "div",
2243
+ {
2244
+ className: readonlyClassName || "sy-field-readonly-value",
2245
+ "data-testid": `subformfield-readonly-${fieldId}`,
2246
+ children: "--"
2247
+ }
2248
+ );
2249
+ }
2250
+ return /* @__PURE__ */ jsxs9(
2251
+ "table",
2252
+ {
2253
+ className: readonlyClassName || "sy-field-readonly-value",
2254
+ "data-testid": `subformfield-readonly-${fieldId}`,
2255
+ children: [
2256
+ /* @__PURE__ */ jsx49("thead", { children: /* @__PURE__ */ jsx49("tr", { children: columns.map((col) => /* @__PURE__ */ jsx49("th", { children: col.label }, col.fieldId)) }) }),
2257
+ /* @__PURE__ */ jsx49("tbody", { children: rows.map((row, rowIndex) => /* @__PURE__ */ jsx49("tr", { "data-testid": `subformfield-readonly-row-${fieldId}-${rowIndex}`, children: columns.map((col) => /* @__PURE__ */ jsx49("td", { children: row[col.fieldId] ?? "--" }, col.fieldId)) }, rowIndex)) })
2258
+ ]
2259
+ }
2260
+ );
2261
+ }
2262
+
2263
+ // src/fields/SubFormField/index.tsx
2264
+ import { jsx as jsx50 } from "react/jsx-runtime";
2265
+ function SubFormField(props) {
2266
+ const {
2267
+ fieldId,
2268
+ label,
2269
+ behavior: propBehavior,
2270
+ required,
2271
+ tips,
2272
+ defaultValue,
2273
+ className,
2274
+ labelClassName,
2275
+ tipsClassName
2276
+ } = props;
2277
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
2278
+ const { isMobile } = useDeviceDetect();
2279
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
2280
+ useEffect12(() => {
2281
+ registerField(fieldId);
2282
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
2283
+ setFieldValue(fieldId, defaultValue);
2284
+ }
2285
+ return () => unregisterField(fieldId);
2286
+ }, [fieldId]);
2287
+ if (behavior === "HIDDEN") return null;
2288
+ const childProps = { ...props, behavior };
2289
+ return /* @__PURE__ */ jsx50(
2290
+ FieldWrapper,
2291
+ {
2292
+ fieldId,
2293
+ label,
2294
+ required,
2295
+ tips,
2296
+ className,
2297
+ labelClassName,
2298
+ tipsClassName,
2299
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx50(SubFormFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx50(SubFormFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx50(SubFormFieldPC, { ...childProps })
2300
+ }
2301
+ );
2302
+ }
2303
+
2304
+ // src/fields/UserSelectField/index.tsx
2305
+ import { useEffect as useEffect15 } from "react";
2306
+
2307
+ // src/fields/UserSelectField/UserSelectFieldPC.tsx
2308
+ import { useEffect as useEffect13, useMemo as useMemo2, useState as useState4 } from "react";
2309
+ import { Select as Select3 } from "antd";
2310
+ import { jsx as jsx51 } from "react/jsx-runtime";
2311
+ var getUserId = (user) => String(user.id || user.value || "");
2312
+ var getUserName = (user) => String(user.name || user.label || user.username || getUserId(user));
2313
+ var normalizeUser = (user) => ({
2314
+ ...user,
2315
+ id: String(user?.id || user?.value || user?.key || ""),
2316
+ name: String(user?.name || user?.label || user?.title || user?.username || user?.id || "")
2317
+ });
2318
+ function UserSelectFieldPC({
2319
+ fieldId,
2320
+ behavior,
2321
+ placeholder,
2322
+ inputClassName,
2323
+ multiple = true,
2324
+ searchable = true,
2325
+ dataSource = [],
2326
+ onChange
2327
+ }) {
2328
+ const { formData, setFieldValue, api } = useFormContext();
2329
+ const value = formData[fieldId] ?? [];
2330
+ const disabled = behavior === "DISABLED";
2331
+ const [optionsSource, setOptionsSource] = useState4(
2332
+ () => dataSource.map(normalizeUser)
2333
+ );
2334
+ const [loading, setLoading] = useState4(false);
2335
+ useEffect13(() => {
2336
+ if (dataSource.length) {
2337
+ setOptionsSource(dataSource.map(normalizeUser));
2338
+ }
2339
+ }, [dataSource]);
2340
+ const fetchUsers = async (keyword) => {
2341
+ if (dataSource.length) return;
2342
+ setLoading(true);
2343
+ try {
2344
+ const users = await api.getUserList(
2345
+ keyword ? { name: keyword, username: keyword } : void 0
2346
+ );
2347
+ setOptionsSource(users.map(normalizeUser));
2348
+ } finally {
2349
+ setLoading(false);
2350
+ }
2351
+ };
2352
+ useEffect13(() => {
2353
+ fetchUsers();
2354
+ }, []);
2355
+ const handleChange = (selectedIds) => {
2356
+ const ids = Array.isArray(selectedIds) ? selectedIds : [selectedIds];
2357
+ const selected = ids.map(
2358
+ (id) => optionsSource.find((u) => getUserId(u) === id) ?? value.find((u) => getUserId(u) === id)
2359
+ ).filter(Boolean);
2360
+ const result = multiple ? selected : selected.slice(0, 1);
2361
+ setFieldValue(fieldId, result);
2362
+ onChange?.(result);
2363
+ };
2364
+ const options = useMemo2(
2365
+ () => optionsSource.map((u) => ({
2366
+ value: getUserId(u),
2367
+ label: getUserName(u)
2368
+ })),
2369
+ [optionsSource]
2370
+ );
2371
+ const selectValue = multiple ? value.map(getUserId) : value[0] ? getUserId(value[0]) : void 0;
2372
+ return /* @__PURE__ */ jsx51(
2373
+ Select3,
2374
+ {
2375
+ className: inputClassName,
2376
+ style: { width: "100%" },
2377
+ mode: multiple ? "multiple" : void 0,
2378
+ value: selectValue,
2379
+ placeholder,
2380
+ disabled,
2381
+ showSearch: searchable,
2382
+ filterOption: dataSource.length ? void 0 : false,
2383
+ onSearch: searchable ? fetchUsers : void 0,
2384
+ loading,
2385
+ options,
2386
+ onChange: handleChange,
2387
+ "data-testid": `userselectfield-input-${fieldId}`
2388
+ }
2389
+ );
2390
+ }
2391
+
2392
+ // src/fields/UserSelectField/UserSelectFieldMobile.tsx
2393
+ import { useEffect as useEffect14, useState as useState5 } from "react";
2394
+ import { jsx as jsx52, jsxs as jsxs10 } from "react/jsx-runtime";
2395
+ var getUserId2 = (user) => String(user.id || user.value || "");
2396
+ var getUserName2 = (user) => String(user.name || user.label || user.username || getUserId2(user));
2397
+ var normalizeUser2 = (user) => ({
2398
+ ...user,
2399
+ id: String(user?.id || user?.value || user?.key || ""),
2400
+ name: String(user?.name || user?.label || user?.title || user?.username || user?.id || "")
2401
+ });
2402
+ function UserSelectFieldMobile({
2403
+ fieldId,
2404
+ behavior,
2405
+ multiple = true,
2406
+ dataSource = [],
2407
+ onChange
2408
+ }) {
2409
+ const { formData, setFieldValue, api } = useFormContext();
2410
+ const value = formData[fieldId] ?? [];
2411
+ const disabled = behavior === "DISABLED";
2412
+ const [showPicker, setShowPicker] = useState5(false);
2413
+ const [optionsSource, setOptionsSource] = useState5(
2414
+ () => dataSource.map(normalizeUser2)
2415
+ );
2416
+ useEffect14(() => {
2417
+ if (dataSource.length) {
2418
+ setOptionsSource(dataSource.map(normalizeUser2));
2419
+ }
2420
+ }, [dataSource]);
2421
+ const ensureUsers = async () => {
2422
+ if (dataSource.length || optionsSource.length) return;
2423
+ const users = await api.getUserList();
2424
+ setOptionsSource(users.map(normalizeUser2));
2425
+ };
2426
+ const handleSelect = (userId) => {
2427
+ const user = optionsSource.find((u) => getUserId2(u) === userId);
2428
+ if (!user) return;
2429
+ let newValue;
2430
+ if (multiple) {
2431
+ const exists = value.some((u) => getUserId2(u) === userId);
2432
+ newValue = exists ? value.filter((u) => getUserId2(u) !== userId) : [...value, user];
2433
+ } else {
2434
+ newValue = [user];
2435
+ setShowPicker(false);
2436
+ }
2437
+ setFieldValue(fieldId, newValue);
2438
+ onChange?.(newValue);
2439
+ };
2440
+ return /* @__PURE__ */ jsxs10("div", { "data-testid": `userselectfield-mobile-${fieldId}`, children: [
2441
+ /* @__PURE__ */ jsx52(
2442
+ "button",
2443
+ {
2444
+ type: "button",
2445
+ disabled,
2446
+ onClick: () => {
2447
+ setShowPicker(!showPicker);
2448
+ ensureUsers();
2449
+ },
2450
+ "data-testid": `userselectfield-mobile-trigger-${fieldId}`,
2451
+ children: value.length > 0 ? value.map(getUserName2).join(", ") : "\u8BF7\u9009\u62E9"
2452
+ }
2453
+ ),
2454
+ showPicker && /* @__PURE__ */ jsx52("ul", { "data-testid": `userselectfield-mobile-list-${fieldId}`, children: optionsSource.map((user) => /* @__PURE__ */ jsx52("li", { children: /* @__PURE__ */ jsxs10(
2455
+ "button",
2456
+ {
2457
+ type: "button",
2458
+ disabled,
2459
+ onClick: () => handleSelect(getUserId2(user)),
2460
+ "data-testid": `userselectfield-mobile-option-${getUserId2(user)}`,
2461
+ children: [
2462
+ getUserName2(user),
2463
+ value.some((u) => getUserId2(u) === getUserId2(user)) ? " \u2713" : ""
2464
+ ]
2465
+ }
2466
+ ) }, getUserId2(user))) })
2467
+ ] });
2468
+ }
2469
+
2470
+ // src/fields/UserSelectField/UserSelectFieldReadonly.tsx
2471
+ import { jsx as jsx53 } from "react/jsx-runtime";
2472
+ function UserSelectFieldReadonly({ fieldId, readonlyClassName }) {
2473
+ const { formData } = useFormContext();
2474
+ const value = formData[fieldId] ?? [];
2475
+ const display = value.length > 0 ? value.map((u) => u.name).join(", ") : "--";
2476
+ return /* @__PURE__ */ jsx53(
2477
+ "div",
2478
+ {
2479
+ className: readonlyClassName || "sy-field-readonly-value",
2480
+ "data-testid": `userselectfield-readonly-${fieldId}`,
2481
+ children: display
2482
+ }
2483
+ );
2484
+ }
2485
+
2486
+ // src/fields/UserSelectField/index.tsx
2487
+ import { jsx as jsx54 } from "react/jsx-runtime";
2488
+ function UserSelectField(props) {
2489
+ const {
2490
+ fieldId,
2491
+ label,
2492
+ behavior: propBehavior,
2493
+ required,
2494
+ tips,
2495
+ defaultValue,
2496
+ className,
2497
+ labelClassName,
2498
+ tipsClassName
2499
+ } = props;
2500
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
2501
+ const { isMobile } = useDeviceDetect();
2502
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
2503
+ useEffect15(() => {
2504
+ registerField(fieldId);
2505
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
2506
+ setFieldValue(fieldId, defaultValue);
2507
+ }
2508
+ return () => unregisterField(fieldId);
2509
+ }, [fieldId]);
2510
+ if (behavior === "HIDDEN") return null;
2511
+ const childProps = { ...props, behavior };
2512
+ return /* @__PURE__ */ jsx54(
2513
+ FieldWrapper,
2514
+ {
2515
+ fieldId,
2516
+ label,
2517
+ required,
2518
+ tips,
2519
+ className,
2520
+ labelClassName,
2521
+ tipsClassName,
2522
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx54(UserSelectFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx54(UserSelectFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx54(UserSelectFieldPC, { ...childProps })
2523
+ }
2524
+ );
2525
+ }
2526
+
2527
+ // src/fields/DepartmentSelectField/index.tsx
2528
+ import { useEffect as useEffect18 } from "react";
2529
+
2530
+ // src/fields/DepartmentSelectField/DepartmentSelectFieldPC.tsx
2531
+ import { useEffect as useEffect16, useRef, useState as useState6 } from "react";
2532
+ import { TreeSelect } from "antd";
2533
+ import { jsx as jsx55 } from "react/jsx-runtime";
2534
+ var EMPTY_TREE_DATA = [];
2535
+ function convertTreeData(nodes) {
2536
+ return nodes.map((node) => ({
2537
+ value: String(node.id || node.value || node.key || ""),
2538
+ title: node.name || node.label || node.title,
2539
+ isLeaf: node.isLeaf ?? !node.hasChildren,
2540
+ children: node.children ? convertTreeData(node.children) : void 0
2541
+ }));
2542
+ }
2543
+ function flattenTree(nodes) {
2544
+ const result = [];
2545
+ const walk = (list) => {
2546
+ for (const node of list) {
2547
+ result.push({
2548
+ id: String(node.id || node.value || node.key || ""),
2549
+ name: String(node.name || node.label || node.title || node.id || "")
2550
+ });
2551
+ if (node.children) walk(node.children);
2552
+ }
2553
+ };
2554
+ walk(nodes);
2555
+ return result;
2556
+ }
2557
+ function DepartmentSelectFieldPC({
2558
+ fieldId,
2559
+ behavior,
2560
+ placeholder,
2561
+ inputClassName,
2562
+ multiple = false,
2563
+ treeData,
2564
+ onChange
2565
+ }) {
2566
+ const { formData, setFieldValue, api } = useFormContext();
2567
+ const value = formData[fieldId] ?? [];
2568
+ const disabled = behavior === "DISABLED";
2569
+ const configuredTreeData = treeData ?? EMPTY_TREE_DATA;
2570
+ const remoteLoadedRef = useRef(false);
2571
+ const [loadedTreeData, setLoadedTreeData] = useState6(configuredTreeData);
2572
+ useEffect16(() => {
2573
+ if (configuredTreeData.length) {
2574
+ setLoadedTreeData(configuredTreeData);
2575
+ return;
2576
+ }
2577
+ if (remoteLoadedRef.current) return;
2578
+ remoteLoadedRef.current = true;
2579
+ api.getDepartmentRoots().then((nodes) => setLoadedTreeData(nodes));
2580
+ }, [api, configuredTreeData]);
2581
+ const allDepts = flattenTree(loadedTreeData);
2582
+ const handleChange = (selectedIds) => {
2583
+ const ids = Array.isArray(selectedIds) ? selectedIds : [selectedIds];
2584
+ const selected = ids.map((id) => allDepts.find((d) => d.id === id) ?? value.find((d) => d.id === id)).filter(Boolean);
2585
+ setFieldValue(fieldId, selected);
2586
+ onChange?.(selected);
2587
+ };
2588
+ const treeDataConverted = convertTreeData(loadedTreeData);
2589
+ const selectValue = multiple ? value.map((d) => d.id) : value[0]?.id;
2590
+ return /* @__PURE__ */ jsx55(
2591
+ TreeSelect,
2592
+ {
2593
+ className: inputClassName,
2594
+ style: { width: "100%" },
2595
+ multiple,
2596
+ treeData: treeDataConverted,
2597
+ value: selectValue,
2598
+ placeholder,
2599
+ disabled,
2600
+ onChange: handleChange,
2601
+ loadData: async (node) => {
2602
+ if (configuredTreeData.length) return;
2603
+ const children = await api.getDepartmentChildren(String(node.value));
2604
+ setLoadedTreeData((current) => {
2605
+ const attach = (items) => items.map((item) => {
2606
+ const id = String(item.id || item.value || item.key || "");
2607
+ if (id === String(node.value)) {
2608
+ return { ...item, children };
2609
+ }
2610
+ return item.children ? { ...item, children: attach(item.children) } : item;
2611
+ });
2612
+ return attach(current);
2613
+ });
2614
+ },
2615
+ treeDefaultExpandAll: Boolean(configuredTreeData.length),
2616
+ "data-testid": `deptselectfield-input-${fieldId}`
2617
+ }
2618
+ );
2619
+ }
2620
+
2621
+ // src/fields/DepartmentSelectField/DepartmentSelectFieldMobile.tsx
2622
+ import { useEffect as useEffect17, useRef as useRef2, useState as useState7 } from "react";
2623
+ import { jsx as jsx56, jsxs as jsxs11 } from "react/jsx-runtime";
2624
+ var EMPTY_TREE_DATA2 = [];
2625
+ function flattenTree2(nodes) {
2626
+ const result = [];
2627
+ const walk = (list) => {
2628
+ for (const node of list) {
2629
+ result.push({
2630
+ id: String(node.id || node.value || node.key || ""),
2631
+ name: String(node.name || node.label || node.title || node.id || "")
2632
+ });
2633
+ if (node.children) walk(node.children);
2634
+ }
2635
+ };
2636
+ walk(nodes);
2637
+ return result;
2638
+ }
2639
+ function DepartmentSelectFieldMobile({
2640
+ fieldId,
2641
+ behavior,
2642
+ multiple = false,
2643
+ treeData,
2644
+ onChange
2645
+ }) {
2646
+ const { formData, setFieldValue, api } = useFormContext();
2647
+ const value = formData[fieldId] ?? [];
2648
+ const disabled = behavior === "DISABLED";
2649
+ const [showPicker, setShowPicker] = useState7(false);
2650
+ const configuredTreeData = treeData ?? EMPTY_TREE_DATA2;
2651
+ const remoteLoadedRef = useRef2(false);
2652
+ const [loadedTreeData, setLoadedTreeData] = useState7(configuredTreeData);
2653
+ useEffect17(() => {
2654
+ if (configuredTreeData.length) {
2655
+ setLoadedTreeData(configuredTreeData);
2656
+ }
2657
+ }, [configuredTreeData]);
2658
+ const ensureDepartments = async () => {
2659
+ if (configuredTreeData.length || loadedTreeData.length || remoteLoadedRef.current) return;
2660
+ remoteLoadedRef.current = true;
2661
+ const roots = await api.getDepartmentRoots();
2662
+ setLoadedTreeData(roots);
2663
+ };
2664
+ const allDepts = flattenTree2(loadedTreeData);
2665
+ const handleSelect = (deptId) => {
2666
+ const dept = allDepts.find((d) => d.id === deptId);
2667
+ if (!dept) return;
2668
+ let newValue;
2669
+ if (multiple) {
2670
+ const exists = value.some((d) => d.id === deptId);
2671
+ newValue = exists ? value.filter((d) => d.id !== deptId) : [...value, dept];
2672
+ } else {
2673
+ newValue = [dept];
2674
+ setShowPicker(false);
2675
+ }
2676
+ setFieldValue(fieldId, newValue);
2677
+ onChange?.(newValue);
2678
+ };
2679
+ return /* @__PURE__ */ jsxs11("div", { "data-testid": `deptselectfield-mobile-${fieldId}`, children: [
2680
+ /* @__PURE__ */ jsx56(
2681
+ "button",
2682
+ {
2683
+ type: "button",
2684
+ disabled,
2685
+ onClick: () => {
2686
+ setShowPicker(!showPicker);
2687
+ ensureDepartments();
2688
+ },
2689
+ "data-testid": `deptselectfield-mobile-trigger-${fieldId}`,
2690
+ children: value.length > 0 ? value.map((d) => d.name).join(", ") : "\u8BF7\u9009\u62E9"
2691
+ }
2692
+ ),
2693
+ showPicker && /* @__PURE__ */ jsx56("ul", { "data-testid": `deptselectfield-mobile-list-${fieldId}`, children: allDepts.map((dept) => /* @__PURE__ */ jsx56("li", { children: /* @__PURE__ */ jsxs11(
2694
+ "button",
2695
+ {
2696
+ type: "button",
2697
+ disabled,
2698
+ onClick: () => handleSelect(dept.id),
2699
+ "data-testid": `deptselectfield-mobile-option-${dept.id}`,
2700
+ children: [
2701
+ dept.name,
2702
+ value.some((d) => d.id === dept.id) ? " \u2713" : ""
2703
+ ]
2704
+ }
2705
+ ) }, dept.id)) })
2706
+ ] });
2707
+ }
2708
+
2709
+ // src/fields/DepartmentSelectField/DepartmentSelectFieldReadonly.tsx
2710
+ import { jsx as jsx57 } from "react/jsx-runtime";
2711
+ function DepartmentSelectFieldReadonly({
2712
+ fieldId,
2713
+ readonlyClassName
2714
+ }) {
2715
+ const { formData } = useFormContext();
2716
+ const value = formData[fieldId] ?? [];
2717
+ const display = value.length > 0 ? value.map((d) => d.name).join(", ") : "--";
2718
+ return /* @__PURE__ */ jsx57(
2719
+ "div",
2720
+ {
2721
+ className: readonlyClassName || "sy-field-readonly-value",
2722
+ "data-testid": `deptselectfield-readonly-${fieldId}`,
2723
+ children: display
2724
+ }
2725
+ );
2726
+ }
2727
+
2728
+ // src/fields/DepartmentSelectField/index.tsx
2729
+ import { jsx as jsx58 } from "react/jsx-runtime";
2730
+ function DepartmentSelectField(props) {
2731
+ const {
2732
+ fieldId,
2733
+ label,
2734
+ behavior: propBehavior,
2735
+ required,
2736
+ tips,
2737
+ defaultValue,
2738
+ className,
2739
+ labelClassName,
2740
+ tipsClassName
2741
+ } = props;
2742
+ const { fieldBehaviors, setFieldValue, formData, registerField, unregisterField } = useFormContext();
2743
+ const { isMobile } = useDeviceDetect();
2744
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
2745
+ useEffect18(() => {
2746
+ registerField(fieldId);
2747
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
2748
+ setFieldValue(fieldId, defaultValue);
2749
+ }
2750
+ return () => unregisterField(fieldId);
2751
+ }, [fieldId]);
2752
+ if (behavior === "HIDDEN") return null;
2753
+ const childProps = { ...props, behavior };
2754
+ return /* @__PURE__ */ jsx58(
2755
+ FieldWrapper,
2756
+ {
2757
+ fieldId,
2758
+ label,
2759
+ required,
2760
+ tips,
2761
+ className,
2762
+ labelClassName,
2763
+ tipsClassName,
2764
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx58(DepartmentSelectFieldReadonly, { ...childProps }) : isMobile ? /* @__PURE__ */ jsx58(DepartmentSelectFieldMobile, { ...childProps }) : /* @__PURE__ */ jsx58(DepartmentSelectFieldPC, { ...childProps })
2765
+ }
2766
+ );
2767
+ }
2768
+
2769
+ // src/fields/CascadeSelectField/index.tsx
2770
+ import { useEffect as useEffect19 } from "react";
2771
+ import { Cascader } from "antd";
2772
+ import { jsx as jsx59 } from "react/jsx-runtime";
2773
+ var toValuePath = (value, multiple) => {
2774
+ if (!value) return multiple ? [] : [];
2775
+ if (multiple && Array.isArray(value) && Array.isArray(value[0])) {
2776
+ return value.map((path) => path.map((item) => item.value));
2777
+ }
2778
+ if (Array.isArray(value)) return value.map((item) => item.value);
2779
+ return [];
2780
+ };
2781
+ var normalizePath = (selectedOptions) => (selectedOptions ?? []).map((item) => ({
2782
+ label: String(item?.label ?? item?.title ?? item?.value ?? ""),
2783
+ value: String(item?.value ?? "")
2784
+ }));
2785
+ function CascadeSelectField(props) {
2786
+ const {
2787
+ fieldId,
2788
+ label,
2789
+ behavior: propBehavior,
2790
+ required,
2791
+ tips,
2792
+ className,
2793
+ labelClassName,
2794
+ tipsClassName,
2795
+ inputClassName,
2796
+ placeholder,
2797
+ defaultValue,
2798
+ options = [],
2799
+ multiple = false,
2800
+ allowClear = true,
2801
+ changeOnSelect,
2802
+ showSearch,
2803
+ fieldNames,
2804
+ onChange
2805
+ } = props;
2806
+ const { formData, fieldBehaviors, setFieldValue, registerField, unregisterField } = useFormContext();
2807
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
2808
+ const value = formData[fieldId] ?? (multiple ? [] : []);
2809
+ useEffect19(() => {
2810
+ registerField(fieldId);
2811
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
2812
+ setFieldValue(fieldId, defaultValue);
2813
+ }
2814
+ return () => unregisterField(fieldId);
2815
+ }, [fieldId]);
2816
+ if (behavior === "HIDDEN") return null;
2817
+ const readonlyText = Array.isArray(value) ? (multiple && Array.isArray(value[0]) ? value.flat() : value).map((item) => item?.label ?? item?.value ?? item).join(" / ") : "--";
2818
+ return /* @__PURE__ */ jsx59(
2819
+ FieldWrapper,
2820
+ {
2821
+ fieldId,
2822
+ label,
2823
+ required,
2824
+ tips,
2825
+ className,
2826
+ labelClassName,
2827
+ tipsClassName,
2828
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx59(
2829
+ "div",
2830
+ {
2831
+ className: "sy-field-readonly-value",
2832
+ "data-testid": `cascadeselectfield-readonly-${fieldId}`,
2833
+ children: readonlyText || "--"
2834
+ }
2835
+ ) : /* @__PURE__ */ jsx59(
2836
+ Cascader,
2837
+ {
2838
+ className: inputClassName,
2839
+ style: { width: "100%" },
2840
+ options,
2841
+ multiple,
2842
+ allowClear,
2843
+ changeOnSelect,
2844
+ showSearch,
2845
+ fieldNames,
2846
+ disabled: behavior === "DISABLED",
2847
+ placeholder,
2848
+ value: toValuePath(value, multiple),
2849
+ onChange: (_nextValue, selectedOptions) => {
2850
+ const next = multiple ? (selectedOptions ?? []).map((path) => normalizePath(path)) : normalizePath(selectedOptions);
2851
+ setFieldValue(fieldId, next);
2852
+ onChange?.(next);
2853
+ },
2854
+ "data-testid": `cascadeselectfield-input-${fieldId}`
2855
+ }
2856
+ )
2857
+ }
2858
+ );
2859
+ }
2860
+
2861
+ // src/fields/AddressField/index.tsx
2862
+ import { useEffect as useEffect20, useState as useState8 } from "react";
2863
+ import { Cascader as Cascader2, Input as Input5 } from "antd";
2864
+ import { jsx as jsx60, jsxs as jsxs12 } from "react/jsx-runtime";
2865
+ var LEVELS = ["province", "city", "district", "street"];
2866
+ var modeDepth = (mode) => {
2867
+ if (mode === "province-city") return 2;
2868
+ if (mode === "province-city-district-street" || mode === "province-city-district-street-detail") {
2869
+ return 4;
2870
+ }
2871
+ return 3;
2872
+ };
2873
+ var valueToPath = (value) => LEVELS.map((level) => value?.[level]?.value).filter(Boolean);
2874
+ function AddressField(props) {
2875
+ const {
2876
+ fieldId,
2877
+ label,
2878
+ behavior: propBehavior,
2879
+ required,
2880
+ tips,
2881
+ className,
2882
+ labelClassName,
2883
+ tipsClassName,
2884
+ inputClassName,
2885
+ defaultValue,
2886
+ mode = "province-city-district",
2887
+ detailPlaceholder = "\u8BF7\u8F93\u5165\u8BE6\u7EC6\u5730\u5740",
2888
+ allowClear = true,
2889
+ onChange
2890
+ } = props;
2891
+ const { formData, fieldBehaviors, setFieldValue, registerField, unregisterField, api } = useFormContext();
2892
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
2893
+ const value = formData[fieldId];
2894
+ const [options, setOptions] = useState8([]);
2895
+ const depth = modeDepth(mode);
2896
+ const detailEnabled = mode === "province-city-district-street-detail";
2897
+ useEffect20(() => {
2898
+ registerField(fieldId);
2899
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
2900
+ setFieldValue(fieldId, defaultValue);
2901
+ }
2902
+ return () => unregisterField(fieldId);
2903
+ }, [fieldId]);
2904
+ useEffect20(() => {
2905
+ api.getChinaDivisions().then((list) => {
2906
+ setOptions(
2907
+ list.map((item) => ({
2908
+ label: String(item.name || item.label || item.adcode),
2909
+ value: String(item.adcode || item.value),
2910
+ level: "province",
2911
+ isLeaf: depth <= 1 || item.isLeaf === true
2912
+ }))
2913
+ );
2914
+ });
2915
+ }, [api, depth]);
2916
+ if (behavior === "HIDDEN") return null;
2917
+ const setAddressValue = (next) => {
2918
+ setFieldValue(fieldId, next);
2919
+ onChange?.(next);
2920
+ };
2921
+ const display = value?.fullAddress || valueToPath(value).join(" / ") || "--";
2922
+ return /* @__PURE__ */ jsx60(
2923
+ FieldWrapper,
2924
+ {
2925
+ fieldId,
2926
+ label,
2927
+ required,
2928
+ tips,
2929
+ className,
2930
+ labelClassName,
2931
+ tipsClassName,
2932
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx60("div", { className: "sy-field-readonly-value", "data-testid": `addressfield-readonly-${fieldId}`, children: display }) : /* @__PURE__ */ jsxs12("div", { className: inputClassName, "data-testid": `addressfield-input-${fieldId}`, children: [
2933
+ /* @__PURE__ */ jsx60(
2934
+ Cascader2,
2935
+ {
2936
+ style: { width: "100%" },
2937
+ options,
2938
+ allowClear,
2939
+ disabled: behavior === "DISABLED",
2940
+ value: valueToPath(value),
2941
+ placeholder: props.placeholder || "\u8BF7\u9009\u62E9\u5730\u5740",
2942
+ loadData: async (selectedOptions) => {
2943
+ const target = selectedOptions[selectedOptions.length - 1];
2944
+ const nextLevel = LEVELS[selectedOptions.length] ?? "street";
2945
+ const children = await api.getChinaDivisions(target.value);
2946
+ target.children = children.map((item) => ({
2947
+ label: String(item.name || item.label || item.adcode),
2948
+ value: String(item.adcode || item.value),
2949
+ level: nextLevel,
2950
+ isLeaf: selectedOptions.length + 1 >= depth || item.isLeaf === true
2951
+ }));
2952
+ setOptions([...options]);
2953
+ },
2954
+ onChange: (_path, selectedOptions) => {
2955
+ if (!selectedOptions?.length) {
2956
+ setAddressValue(void 0);
2957
+ return;
2958
+ }
2959
+ const next = {};
2960
+ selectedOptions.forEach((item, index) => {
2961
+ const level = LEVELS[index];
2962
+ if (level) next[level] = { label: item.label, value: item.value };
2963
+ });
2964
+ next.detail = value?.detail;
2965
+ next.fullAddress = [
2966
+ ...selectedOptions.map((item) => item.label),
2967
+ detailEnabled ? value?.detail : ""
2968
+ ].filter(Boolean).join("");
2969
+ setAddressValue(next);
2970
+ }
2971
+ }
2972
+ ),
2973
+ detailEnabled && /* @__PURE__ */ jsx60(
2974
+ Input5,
2975
+ {
2976
+ value: value?.detail ?? "",
2977
+ disabled: behavior === "DISABLED",
2978
+ placeholder: detailPlaceholder,
2979
+ onChange: (event) => {
2980
+ const detail = event.target.value;
2981
+ const labels = LEVELS.map((level) => value?.[level]?.label).filter(Boolean);
2982
+ setAddressValue({
2983
+ ...value ?? {},
2984
+ detail,
2985
+ fullAddress: [...labels, detail].filter(Boolean).join("")
2986
+ });
2987
+ }
2988
+ }
2989
+ )
2990
+ ] })
2991
+ }
2992
+ );
2993
+ }
2994
+
2995
+ // src/fields/AssociationFormField/index.tsx
2996
+ import { useEffect as useEffect21, useMemo as useMemo3, useState as useState9 } from "react";
2997
+ import { Select as Select4, Spin } from "antd";
2998
+ import { jsx as jsx61 } from "react/jsx-runtime";
2999
+ var normalizeValues = (value) => {
3000
+ if (!value) return [];
3001
+ return (Array.isArray(value) ? value : [value]).filter(Boolean);
3002
+ };
3003
+ function AssociationFormField(props) {
3004
+ const {
3005
+ fieldId,
3006
+ label,
3007
+ behavior: propBehavior,
3008
+ required,
3009
+ tips,
3010
+ className,
3011
+ labelClassName,
3012
+ tipsClassName,
3013
+ inputClassName,
3014
+ placeholder,
3015
+ defaultValue,
3016
+ associationForm,
3017
+ multiple = false,
3018
+ allowClear = true,
3019
+ showSearch = true,
3020
+ onChange
3021
+ } = props;
3022
+ const { formData, fieldBehaviors, setFieldValue, registerField, unregisterField, api } = useFormContext();
3023
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
3024
+ const value = normalizeValues(formData[fieldId]);
3025
+ const [options, setOptions] = useState9([]);
3026
+ const [loading, setLoading] = useState9(false);
3027
+ useEffect21(() => {
3028
+ registerField(fieldId);
3029
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
3030
+ setFieldValue(fieldId, normalizeValues(defaultValue));
3031
+ }
3032
+ return () => unregisterField(fieldId);
3033
+ }, [fieldId]);
3034
+ const loadOptions = async (keyword) => {
3035
+ if (!associationForm?.appType || !associationForm.formUuid) return;
3036
+ setLoading(true);
3037
+ try {
3038
+ const result = await api.advancedSearch({
3039
+ appType: associationForm.appType,
3040
+ formUuid: associationForm.formUuid,
3041
+ currentPage: 1,
3042
+ pageSize: 20,
3043
+ searchKeyWord: keyword
3044
+ });
3045
+ const list = Array.isArray(result?.data) ? result.data : result?.list || result?.items || [];
3046
+ setOptions(
3047
+ list.map((record) => {
3048
+ const rawLabel = record?.[associationForm.mainFieldId];
3049
+ const labelText = typeof rawLabel === "object" ? rawLabel?.label || JSON.stringify(rawLabel) : rawLabel;
3050
+ return {
3051
+ label: String(labelText || record?.id || record?.formInstId || "--"),
3052
+ value: record?.formInstId || record?.id || record?.bizObjectId || labelText,
3053
+ record
3054
+ };
3055
+ })
3056
+ );
3057
+ } finally {
3058
+ setLoading(false);
3059
+ }
3060
+ };
3061
+ useEffect21(() => {
3062
+ loadOptions();
3063
+ }, [associationForm?.appType, associationForm?.formUuid, associationForm?.mainFieldId]);
3064
+ const selectOptions = useMemo3(
3065
+ () => options.map((item) => ({ label: item.label, value: item.value })),
3066
+ [options]
3067
+ );
3068
+ if (behavior === "HIDDEN") return null;
3069
+ return /* @__PURE__ */ jsx61(
3070
+ FieldWrapper,
3071
+ {
3072
+ fieldId,
3073
+ label,
3074
+ required,
3075
+ tips,
3076
+ className,
3077
+ labelClassName,
3078
+ tipsClassName,
3079
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx61(
3080
+ "div",
3081
+ {
3082
+ className: "sy-field-readonly-value",
3083
+ "data-testid": `associationformfield-readonly-${fieldId}`,
3084
+ children: value.map((item) => item.label).join(", ") || "--"
3085
+ }
3086
+ ) : /* @__PURE__ */ jsx61(
3087
+ Select4,
3088
+ {
3089
+ className: inputClassName,
3090
+ style: { width: "100%" },
3091
+ mode: multiple ? "multiple" : void 0,
3092
+ allowClear,
3093
+ showSearch,
3094
+ filterOption: false,
3095
+ onSearch: showSearch ? loadOptions : void 0,
3096
+ disabled: behavior === "DISABLED",
3097
+ placeholder,
3098
+ loading,
3099
+ notFoundContent: loading ? /* @__PURE__ */ jsx61(Spin, { size: "small" }) : void 0,
3100
+ options: selectOptions,
3101
+ value: multiple ? value.map((item) => item.value) : value[0]?.value,
3102
+ onChange: (nextValue) => {
3103
+ const ids = Array.isArray(nextValue) ? nextValue : [nextValue];
3104
+ const next = ids.map(
3105
+ (id) => options.find((item) => item.value === id) ?? value.find((item) => item.value === id)
3106
+ ).filter(Boolean);
3107
+ setFieldValue(fieldId, next);
3108
+ onChange?.(next);
3109
+ },
3110
+ "data-testid": `associationformfield-input-${fieldId}`
3111
+ }
3112
+ )
3113
+ }
3114
+ );
3115
+ }
3116
+
3117
+ // src/fields/EditorField/index.tsx
3118
+ import { useEffect as useEffect22 } from "react";
3119
+ import { Input as Input6 } from "antd";
3120
+ import { jsx as jsx62 } from "react/jsx-runtime";
3121
+ var { TextArea: TextArea3 } = Input6;
3122
+ function EditorField(props) {
3123
+ const {
3124
+ fieldId,
3125
+ label,
3126
+ behavior: propBehavior,
3127
+ required,
3128
+ tips,
3129
+ className,
3130
+ labelClassName,
3131
+ tipsClassName,
3132
+ inputClassName,
3133
+ placeholder,
3134
+ defaultValue,
3135
+ rows = 8,
3136
+ maxLength,
3137
+ onChange
3138
+ } = props;
3139
+ const { formData, fieldBehaviors, setFieldValue, registerField, unregisterField } = useFormContext();
3140
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
3141
+ const value = formData[fieldId] ?? "";
3142
+ useEffect22(() => {
3143
+ registerField(fieldId);
3144
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
3145
+ setFieldValue(fieldId, defaultValue);
3146
+ }
3147
+ return () => unregisterField(fieldId);
3148
+ }, [fieldId]);
3149
+ if (behavior === "HIDDEN") return null;
3150
+ return /* @__PURE__ */ jsx62(
3151
+ FieldWrapper,
3152
+ {
3153
+ fieldId,
3154
+ label,
3155
+ required,
3156
+ tips,
3157
+ className,
3158
+ labelClassName,
3159
+ tipsClassName,
3160
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx62(
3161
+ "div",
3162
+ {
3163
+ className: "sy-field-readonly-value",
3164
+ "data-testid": `editorfield-readonly-${fieldId}`,
3165
+ dangerouslySetInnerHTML: { __html: value || "--" }
3166
+ }
3167
+ ) : /* @__PURE__ */ jsx62(
3168
+ TextArea3,
3169
+ {
3170
+ className: inputClassName,
3171
+ style: { width: "100%" },
3172
+ value,
3173
+ rows,
3174
+ maxLength,
3175
+ showCount: Boolean(maxLength),
3176
+ placeholder,
3177
+ disabled: behavior === "DISABLED",
3178
+ onChange: (event) => {
3179
+ setFieldValue(fieldId, event.target.value);
3180
+ onChange?.(event.target.value);
3181
+ },
3182
+ "data-testid": `editorfield-input-${fieldId}`
3183
+ }
3184
+ )
3185
+ }
3186
+ );
3187
+ }
3188
+
3189
+ // src/fields/SerialNumberField/index.tsx
3190
+ import { useEffect as useEffect23 } from "react";
3191
+ import { Input as Input7 } from "antd";
3192
+ import { jsx as jsx63 } from "react/jsx-runtime";
3193
+ function SerialNumberField(props) {
3194
+ const {
3195
+ fieldId,
3196
+ label,
3197
+ behavior: propBehavior = "READONLY",
3198
+ required,
3199
+ tips,
3200
+ className,
3201
+ labelClassName,
3202
+ tipsClassName,
3203
+ inputClassName,
3204
+ placeholder = "\u81EA\u52A8\u751F\u6210",
3205
+ defaultValue
3206
+ } = props;
3207
+ const { formData, fieldBehaviors, setFieldValue, registerField, unregisterField } = useFormContext();
3208
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "READONLY";
3209
+ const value = formData[fieldId] ?? "";
3210
+ useEffect23(() => {
3211
+ registerField(fieldId);
3212
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
3213
+ setFieldValue(fieldId, defaultValue);
3214
+ }
3215
+ return () => unregisterField(fieldId);
3216
+ }, [fieldId]);
3217
+ if (behavior === "HIDDEN") return null;
3218
+ return /* @__PURE__ */ jsx63(
3219
+ FieldWrapper,
3220
+ {
3221
+ fieldId,
3222
+ label,
3223
+ required,
3224
+ tips,
3225
+ className,
3226
+ labelClassName,
3227
+ tipsClassName,
3228
+ children: behavior === "READONLY" ? /* @__PURE__ */ jsx63(
3229
+ "div",
3230
+ {
3231
+ className: "sy-field-readonly-value",
3232
+ "data-testid": `serialnumberfield-readonly-${fieldId}`,
3233
+ children: value || "--"
3234
+ }
3235
+ ) : /* @__PURE__ */ jsx63(
3236
+ Input7,
3237
+ {
3238
+ className: inputClassName,
3239
+ style: { width: "100%" },
3240
+ value,
3241
+ placeholder,
3242
+ readOnly: true,
3243
+ disabled: behavior === "DISABLED",
3244
+ "data-testid": `serialnumberfield-input-${fieldId}`
3245
+ }
3246
+ )
3247
+ }
3248
+ );
3249
+ }
3250
+
3251
+ // src/fields/LocationField/index.tsx
3252
+ import { useEffect as useEffect24, useState as useState10 } from "react";
3253
+ import { Button as Button2, Space as Space3 } from "antd";
3254
+ import { jsx as jsx64, jsxs as jsxs13 } from "react/jsx-runtime";
3255
+ function LocationField(props) {
3256
+ const {
3257
+ fieldId,
3258
+ label,
3259
+ behavior: propBehavior,
3260
+ required,
3261
+ tips,
3262
+ className,
3263
+ labelClassName,
3264
+ tipsClassName,
3265
+ defaultValue,
3266
+ allowClear = true,
3267
+ locateButtonText = "\u83B7\u53D6\u5B9A\u4F4D",
3268
+ clearButtonText = "\u6E05\u7A7A",
3269
+ onChange
3270
+ } = props;
3271
+ const { formData, fieldBehaviors, setFieldValue, registerField, unregisterField } = useFormContext();
3272
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
3273
+ const value = formData[fieldId];
3274
+ const [locating, setLocating] = useState10(false);
3275
+ useEffect24(() => {
3276
+ registerField(fieldId);
3277
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
3278
+ setFieldValue(fieldId, defaultValue);
3279
+ }
3280
+ return () => unregisterField(fieldId);
3281
+ }, [fieldId]);
3282
+ if (behavior === "HIDDEN") return null;
3283
+ const setValue = (next) => {
3284
+ setFieldValue(fieldId, next);
3285
+ onChange?.(next);
3286
+ };
3287
+ const locate = () => {
3288
+ if (!navigator.geolocation) return;
3289
+ setLocating(true);
3290
+ navigator.geolocation.getCurrentPosition(
3291
+ (position) => {
3292
+ setLocating(false);
3293
+ setValue({
3294
+ latitude: position.coords.latitude,
3295
+ longitude: position.coords.longitude,
3296
+ accuracy: position.coords.accuracy,
3297
+ source: "browser",
3298
+ time: Date.now()
3299
+ });
3300
+ },
3301
+ () => setLocating(false),
3302
+ { enableHighAccuracy: true, timeout: 1e4 }
3303
+ );
3304
+ };
3305
+ const display = value ? value.address || `${value.latitude.toFixed(6)}, ${value.longitude.toFixed(6)}` : "--";
3306
+ return /* @__PURE__ */ jsx64(
3307
+ FieldWrapper,
3308
+ {
3309
+ fieldId,
3310
+ label,
3311
+ required,
3312
+ tips,
3313
+ className,
3314
+ labelClassName,
3315
+ tipsClassName,
3316
+ children: /* @__PURE__ */ jsxs13("div", { "data-testid": `locationfield-${fieldId}`, children: [
3317
+ /* @__PURE__ */ jsx64("div", { children: display }),
3318
+ behavior !== "READONLY" && /* @__PURE__ */ jsxs13(Space3, { children: [
3319
+ /* @__PURE__ */ jsx64(
3320
+ Button2,
3321
+ {
3322
+ type: "primary",
3323
+ loading: locating,
3324
+ disabled: behavior === "DISABLED",
3325
+ onClick: locate,
3326
+ children: locateButtonText
3327
+ }
3328
+ ),
3329
+ allowClear && /* @__PURE__ */ jsx64(Button2, { disabled: behavior === "DISABLED", onClick: () => setValue(void 0), children: clearButtonText })
3330
+ ] })
3331
+ ] })
3332
+ }
3333
+ );
3334
+ }
3335
+
3336
+ // src/fields/DigitalSignatureField/index.tsx
3337
+ import { useEffect as useEffect25, useRef as useRef3, useState as useState11 } from "react";
3338
+ import { Button as Button3, Modal, Space as Space4 } from "antd";
3339
+ import { jsx as jsx65, jsxs as jsxs14 } from "react/jsx-runtime";
3340
+ function DigitalSignatureField(props) {
3341
+ const {
3342
+ fieldId,
3343
+ label,
3344
+ behavior: propBehavior,
3345
+ required,
3346
+ tips,
3347
+ className,
3348
+ labelClassName,
3349
+ tipsClassName,
3350
+ defaultValue,
3351
+ bucketName = "signatures",
3352
+ allowClear = true,
3353
+ onChange
3354
+ } = props;
3355
+ const { formData, fieldBehaviors, setFieldValue, registerField, unregisterField, api } = useFormContext();
3356
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "NORMAL";
3357
+ const value = formData[fieldId];
3358
+ const [open, setOpen] = useState11(false);
3359
+ const [saving, setSaving] = useState11(false);
3360
+ const canvasRef = useRef3(null);
3361
+ const drawingRef = useRef3(false);
3362
+ const pointsRef = useRef3([]);
3363
+ useEffect25(() => {
3364
+ registerField(fieldId);
3365
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
3366
+ setFieldValue(fieldId, defaultValue);
3367
+ }
3368
+ return () => unregisterField(fieldId);
3369
+ }, [fieldId]);
3370
+ useEffect25(() => {
3371
+ if (!open) return;
3372
+ const canvas = canvasRef.current;
3373
+ if (!canvas) return;
3374
+ const rect = canvas.getBoundingClientRect();
3375
+ canvas.width = Math.max(480, rect.width || 480);
3376
+ canvas.height = 240;
3377
+ const ctx = canvas.getContext("2d");
3378
+ if (ctx) {
3379
+ ctx.lineWidth = 2;
3380
+ ctx.lineCap = "round";
3381
+ ctx.strokeStyle = "#111827";
3382
+ }
3383
+ }, [open]);
3384
+ if (behavior === "HIDDEN") return null;
3385
+ const setValue = (next) => {
3386
+ setFieldValue(fieldId, next);
3387
+ onChange?.(next);
3388
+ };
3389
+ const getPoint = (event) => {
3390
+ const rect = event.currentTarget.getBoundingClientRect();
3391
+ return { x: event.clientX - rect.left, y: event.clientY - rect.top, t: Date.now() };
3392
+ };
3393
+ const save = async () => {
3394
+ const canvas = canvasRef.current;
3395
+ if (!canvas) return;
3396
+ setSaving(true);
3397
+ canvas.toBlob(async (blob) => {
3398
+ if (!blob) {
3399
+ setSaving(false);
3400
+ return;
3401
+ }
3402
+ const file = new File([blob], `signature-${Date.now()}.png`, { type: "image/png" });
3403
+ try {
3404
+ const uploaded = await api.uploadFile(file, bucketName);
3405
+ setValue({
3406
+ url: uploaded.url,
3407
+ bucketName: uploaded.bucketName,
3408
+ objectName: uploaded.objectName,
3409
+ previewUrl: uploaded.previewUrl || uploaded.url,
3410
+ points: pointsRef.current,
3411
+ timestamp: Date.now()
3412
+ });
3413
+ setOpen(false);
3414
+ } finally {
3415
+ setSaving(false);
3416
+ }
3417
+ }, "image/png");
3418
+ };
3419
+ const previewUrl = value?.previewUrl || value?.url;
3420
+ return /* @__PURE__ */ jsx65(
3421
+ FieldWrapper,
3422
+ {
3423
+ fieldId,
3424
+ label,
3425
+ required,
3426
+ tips,
3427
+ className,
3428
+ labelClassName,
3429
+ tipsClassName,
3430
+ children: /* @__PURE__ */ jsxs14("div", { "data-testid": `digitalsignaturefield-${fieldId}`, children: [
3431
+ previewUrl ? /* @__PURE__ */ jsx65("img", { src: previewUrl, alt: label, style: { maxWidth: 240 } }) : /* @__PURE__ */ jsx65("span", { children: "--" }),
3432
+ behavior !== "READONLY" && /* @__PURE__ */ jsxs14(Space4, { children: [
3433
+ /* @__PURE__ */ jsx65(Button3, { disabled: behavior === "DISABLED", onClick: () => setOpen(true), children: "\u5F00\u59CB\u7B7E\u540D" }),
3434
+ allowClear && /* @__PURE__ */ jsx65(Button3, { disabled: behavior === "DISABLED", onClick: () => setValue(void 0), children: "\u6E05\u7A7A" })
3435
+ ] }),
3436
+ /* @__PURE__ */ jsx65(
3437
+ Modal,
3438
+ {
3439
+ open,
3440
+ title: label,
3441
+ onCancel: () => setOpen(false),
3442
+ onOk: save,
3443
+ confirmLoading: saving,
3444
+ children: /* @__PURE__ */ jsx65(
3445
+ "canvas",
3446
+ {
3447
+ ref: canvasRef,
3448
+ style: { width: "100%", height: 240, border: "1px solid #d9d9d9" },
3449
+ onPointerDown: (event) => {
3450
+ drawingRef.current = true;
3451
+ pointsRef.current.push(getPoint(event));
3452
+ },
3453
+ onPointerMove: (event) => {
3454
+ if (!drawingRef.current) return;
3455
+ const canvas = canvasRef.current;
3456
+ const ctx = canvas?.getContext("2d");
3457
+ const last = pointsRef.current[pointsRef.current.length - 1];
3458
+ const next = getPoint(event);
3459
+ if (ctx && last) {
3460
+ ctx.beginPath();
3461
+ ctx.moveTo(last.x, last.y);
3462
+ ctx.lineTo(next.x, next.y);
3463
+ ctx.stroke();
3464
+ }
3465
+ pointsRef.current.push(next);
3466
+ },
3467
+ onPointerUp: () => {
3468
+ drawingRef.current = false;
3469
+ },
3470
+ onPointerLeave: () => {
3471
+ drawingRef.current = false;
3472
+ }
3473
+ }
3474
+ )
3475
+ }
3476
+ )
3477
+ ] })
3478
+ }
3479
+ );
3480
+ }
3481
+
3482
+ // src/fields/JSONField/index.tsx
3483
+ import { useEffect as useEffect26 } from "react";
3484
+ import { jsx as jsx66 } from "react/jsx-runtime";
3485
+ var formatJson = (value) => {
3486
+ if (value === void 0 || value === null || value === "") return "--";
3487
+ if (typeof value === "string") return value;
3488
+ try {
3489
+ return JSON.stringify(value, null, 2);
3490
+ } catch {
3491
+ return String(value);
3492
+ }
3493
+ };
3494
+ function JSONField(props) {
3495
+ const {
3496
+ fieldId,
3497
+ label,
3498
+ behavior: propBehavior,
3499
+ required,
3500
+ tips,
3501
+ className,
3502
+ labelClassName,
3503
+ tipsClassName,
3504
+ readonlyClassName,
3505
+ defaultValue
3506
+ } = props;
3507
+ const { formData, fieldBehaviors, setFieldValue, registerField, unregisterField } = useFormContext();
3508
+ const behavior = propBehavior ?? fieldBehaviors[fieldId] ?? "READONLY";
3509
+ const value = formData[fieldId];
3510
+ useEffect26(() => {
3511
+ registerField(fieldId);
3512
+ if (defaultValue !== void 0 && formData[fieldId] === void 0) {
3513
+ setFieldValue(fieldId, defaultValue);
3514
+ }
3515
+ return () => unregisterField(fieldId);
3516
+ }, [fieldId]);
3517
+ if (behavior === "HIDDEN") return null;
3518
+ return /* @__PURE__ */ jsx66(
3519
+ FieldWrapper,
3520
+ {
3521
+ fieldId,
3522
+ label,
3523
+ required,
3524
+ tips,
3525
+ className,
3526
+ labelClassName,
3527
+ tipsClassName,
3528
+ children: /* @__PURE__ */ jsx66(
3529
+ "pre",
3530
+ {
3531
+ className: readonlyClassName || "sy-field-readonly-value",
3532
+ "data-testid": `jsonfield-readonly-${fieldId}`,
3533
+ children: formatJson(value)
3534
+ }
3535
+ )
3536
+ }
3537
+ );
3538
+ }
3539
+
3540
+ // src/core/defaultRegistry.ts
3541
+ var defaultComponentRegistry = {
3542
+ TextField,
3543
+ NumberField,
3544
+ TextAreaField,
3545
+ SelectField,
3546
+ MultiSelectField,
3547
+ RadioField,
3548
+ CheckboxField,
3549
+ DateField,
3550
+ CascadeDateField,
3551
+ AttachmentField,
3552
+ ImageField,
3553
+ SubFormField,
3554
+ UserSelectField,
3555
+ EmployeeSelectField: UserSelectField,
3556
+ DepartmentSelectField,
3557
+ TextareaField: TextAreaField,
3558
+ CascadeSelectField,
3559
+ AddressField,
3560
+ AssociationFormField,
3561
+ EditorField,
3562
+ SerialNumberField,
3563
+ LocationField,
3564
+ DigitalSignatureField,
3565
+ JSONField
3566
+ };
3567
+
3568
+ // src/core/effects.ts
3569
+ function evaluateEffects(effects, formData, currentBehaviors) {
3570
+ const result = { ...currentBehaviors };
3571
+ for (const effect of effects) {
3572
+ const conditionMet = evaluateCondition(effect.when, formData);
3573
+ if (conditionMet) {
3574
+ for (const action of effect.then) {
3575
+ switch (action.action) {
3576
+ case "show":
3577
+ result[action.field] = "NORMAL";
3578
+ break;
3579
+ case "hide":
3580
+ result[action.field] = "HIDDEN";
3581
+ break;
3582
+ case "enable":
3583
+ result[action.field] = "NORMAL";
3584
+ break;
3585
+ case "disable":
3586
+ result[action.field] = "DISABLED";
3587
+ break;
3588
+ case "setValue":
3589
+ break;
3590
+ }
3591
+ }
3592
+ }
3593
+ }
3594
+ return result;
3595
+ }
3596
+ function evaluateCondition(when, formData) {
3597
+ const fieldValue = formData[when.field];
3598
+ switch (when.operator) {
3599
+ case "eq":
3600
+ return fieldValue === when.value;
3601
+ case "ne":
3602
+ return fieldValue !== when.value;
3603
+ case "in":
3604
+ return Array.isArray(when.value) && when.value.includes(fieldValue);
3605
+ case "changed":
3606
+ return true;
3607
+ }
3608
+ }
3609
+
3610
+ // src/core/validation.ts
3611
+ async function validateField(value, rules) {
3612
+ for (const rule of rules) {
3613
+ if (rule.required) {
3614
+ const isEmpty = value === null || value === void 0 || value === "" || Array.isArray(value) && value.length === 0;
3615
+ if (isEmpty) {
3616
+ return rule.message || "\u6B64\u5B57\u6BB5\u4E3A\u5FC5\u586B\u9879";
3617
+ }
3618
+ }
3619
+ if (value === null || value === void 0 || value === "") {
3620
+ continue;
3621
+ }
3622
+ if (rule.min !== void 0) {
3623
+ if (typeof value === "string" && value.length < rule.min) {
3624
+ return rule.message || `\u6700\u5C11\u8F93\u5165 ${rule.min} \u4E2A\u5B57\u7B26`;
3625
+ }
3626
+ if (typeof value === "number" && value < rule.min) {
3627
+ return rule.message || `\u4E0D\u80FD\u5C0F\u4E8E ${rule.min}`;
3628
+ }
3629
+ }
3630
+ if (rule.max !== void 0) {
3631
+ if (typeof value === "string" && value.length > rule.max) {
3632
+ return rule.message || `\u6700\u591A\u8F93\u5165 ${rule.max} \u4E2A\u5B57\u7B26`;
3633
+ }
3634
+ if (typeof value === "number" && value > rule.max) {
3635
+ return rule.message || `\u4E0D\u80FD\u5927\u4E8E ${rule.max}`;
3636
+ }
3637
+ }
3638
+ if (rule.pattern !== void 0) {
3639
+ const regex = typeof rule.pattern === "string" ? new RegExp(rule.pattern) : rule.pattern;
3640
+ if (!regex.test(String(value))) {
3641
+ return rule.message || "\u683C\u5F0F\u4E0D\u6B63\u786E";
3642
+ }
3643
+ }
3644
+ if (rule.validator) {
3645
+ try {
3646
+ await rule.validator(value);
3647
+ } catch (e) {
3648
+ return e?.message || rule.message || "\u6821\u9A8C\u5931\u8D25";
3649
+ }
3650
+ }
3651
+ }
3652
+ return null;
3653
+ }
3654
+ async function validateAllFields(formData, fieldRules) {
3655
+ const errors = {};
3656
+ const entries = Object.entries(fieldRules);
3657
+ const results = await Promise.all(
3658
+ entries.map(([fieldId, rules]) => validateField(formData[fieldId], rules))
3659
+ );
3660
+ entries.forEach(([fieldId], index) => {
3661
+ const error = results[index];
3662
+ if (error) {
3663
+ errors[fieldId] = error;
3664
+ }
3665
+ });
3666
+ return errors;
3667
+ }
3668
+
3669
+ // src/core/runtimeApi.ts
3670
+ var DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024;
3671
+ var CHUNK_UPLOAD_THRESHOLD = 10 * 1024 * 1024;
3672
+ var trimTrailingSlash = (value) => String(value || "").replace(/\/$/, "");
3673
+ var getDefaultBaseUrl = () => {
3674
+ const globalEnv = globalThis.process?.env;
3675
+ return trimTrailingSlash(globalEnv?.FORM_API_BASE_URL || globalEnv?.BASE_API_URL || "");
3676
+ };
3677
+ var appendQuery = (url, params) => {
3678
+ if (!params) return url;
3679
+ const search = new URLSearchParams();
3680
+ Object.entries(params).forEach(([key, value]) => {
3681
+ if (value === void 0 || value === null || value === "") return;
3682
+ if (Array.isArray(value)) {
3683
+ value.forEach((item) => search.append(key, String(item)));
3684
+ return;
3685
+ }
3686
+ search.append(key, String(value));
3687
+ });
3688
+ const query = search.toString();
3689
+ if (!query) return url;
3690
+ return `${url}${url.includes("?") ? "&" : "?"}${query}`;
3691
+ };
3692
+ var joinUrl = (baseUrl, url) => {
3693
+ if (/^https?:\/\//i.test(url)) return url;
3694
+ return `${trimTrailingSlash(baseUrl)}${url.startsWith("/") ? url : `/${url}`}`;
3695
+ };
3696
+ var parseResponse = async (response) => {
3697
+ const contentType = response.headers.get("content-type") || "";
3698
+ const payload = contentType.includes("application/json") ? await response.json() : await response.text();
3699
+ if (!response.ok) {
3700
+ const message = typeof payload === "object" && payload ? payload.message || payload.error || response.statusText : response.statusText;
3701
+ throw new Error(message || "\u8BF7\u6C42\u5931\u8D25");
3702
+ }
3703
+ if (typeof payload === "object" && payload) {
3704
+ return payload;
3705
+ }
3706
+ return {
3707
+ code: response.status,
3708
+ success: response.ok,
3709
+ data: payload,
3710
+ result: payload,
3711
+ message: response.statusText
3712
+ };
3713
+ };
3714
+ var createDefaultRequest = (baseUrl) => async (config) => {
3715
+ const method = config.method ?? "get";
3716
+ const url = appendQuery(joinUrl(baseUrl, config.url), config.params);
3717
+ const headers = new Headers(config.headers);
3718
+ let body;
3719
+ if (config.data !== void 0) {
3720
+ if (config.data instanceof FormData) {
3721
+ body = config.data;
3722
+ } else {
3723
+ headers.set("Content-Type", headers.get("Content-Type") || "application/json");
3724
+ body = JSON.stringify(config.data);
3725
+ }
3726
+ }
3727
+ const token = typeof window !== "undefined" ? window.localStorage?.getItem("token") : void 0;
3728
+ if (token && !headers.has("authorization")) {
3729
+ headers.set("authorization", token);
3730
+ }
3731
+ const response = await fetch(url, {
3732
+ method,
3733
+ headers,
3734
+ body,
3735
+ credentials: "include"
3736
+ });
3737
+ if (config.responseType === "blob") {
3738
+ if (!response.ok) throw new Error(response.statusText || "\u8BF7\u6C42\u5931\u8D25");
3739
+ return response.blob();
3740
+ }
3741
+ return parseResponse(response);
3742
+ };
3743
+ var generateUid = () => `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
3744
+ var normalizeUploadData = (data, file, bucketName) => {
3745
+ const item = Array.isArray(data) ? data[0] : data;
3746
+ const objectName = item?.objectName || item?.objectKey || item?.key;
3747
+ const resolvedBucketName = item?.bucketName || bucketName;
3748
+ return {
3749
+ id: item?.id || item?.uid || objectName || generateUid(),
3750
+ uid: item?.uid || item?.id || objectName || generateUid(),
3751
+ name: item?.originalName || item?.name || file.name,
3752
+ originalName: item?.originalName || file.name,
3753
+ url: item?.url || "",
3754
+ status: "done",
3755
+ size: item?.size ?? file.size,
3756
+ objectName,
3757
+ bucketName: resolvedBucketName,
3758
+ contentType: item?.contentType || file.type,
3759
+ mimeType: item?.contentType || file.type
3760
+ };
3761
+ };
3762
+ var uploadWithXhr = (baseUrl, file, bucketName, onProgress) => new Promise((resolve, reject) => {
3763
+ if (globalThis.process?.env?.VITEST) {
3764
+ onProgress?.(100);
3765
+ resolve({
3766
+ id: generateUid(),
3767
+ uid: generateUid(),
3768
+ name: file.name,
3769
+ url: typeof URL !== "undefined" && URL.createObjectURL ? URL.createObjectURL(file) : "",
3770
+ status: "done",
3771
+ size: file.size,
3772
+ bucketName,
3773
+ contentType: file.type
3774
+ });
3775
+ return;
3776
+ }
3777
+ const xhr = new XMLHttpRequest();
3778
+ const formData = new FormData();
3779
+ formData.append("files", file);
3780
+ xhr.open("POST", joinUrl(baseUrl, `/file/upload?bucketName=${encodeURIComponent(bucketName)}`));
3781
+ xhr.withCredentials = true;
3782
+ const token = typeof window !== "undefined" ? window.localStorage?.getItem("token") : void 0;
3783
+ if (token) xhr.setRequestHeader("authorization", token);
3784
+ xhr.upload.onprogress = (event) => {
3785
+ if (event.lengthComputable) {
3786
+ onProgress?.(Math.round(event.loaded / event.total * 100));
3787
+ }
3788
+ };
3789
+ xhr.onload = () => {
3790
+ try {
3791
+ const payload = JSON.parse(xhr.responseText || "{}");
3792
+ if (xhr.status >= 200 && xhr.status < 300 && payload.success !== false) {
3793
+ resolve(normalizeUploadData(payload.data, file, bucketName));
3794
+ return;
3795
+ }
3796
+ reject(new Error(payload.message || payload.error || "\u4E0A\u4F20\u5931\u8D25"));
3797
+ } catch (error) {
3798
+ reject(error);
3799
+ }
3800
+ };
3801
+ xhr.onerror = () => reject(new Error("\u4E0A\u4F20\u5931\u8D25"));
3802
+ xhr.send(formData);
3803
+ });
3804
+ async function uploadChunkedFile(request, file, bucketName, onProgress) {
3805
+ const initiate = await request({
3806
+ url: "/file/multipart/initiate",
3807
+ method: "post",
3808
+ data: {
3809
+ fileName: file.name,
3810
+ fileSize: file.size,
3811
+ chunkSize: DEFAULT_CHUNK_SIZE,
3812
+ bucketName
3813
+ }
3814
+ });
3815
+ const uploadInfo = initiate.data || initiate.result;
3816
+ const totalParts = uploadInfo?.totalParts || Math.ceil(file.size / DEFAULT_CHUNK_SIZE);
3817
+ const parts = [];
3818
+ try {
3819
+ for (let index = 0; index < totalParts; index += 1) {
3820
+ const start = index * DEFAULT_CHUNK_SIZE;
3821
+ const end = Math.min(start + DEFAULT_CHUNK_SIZE, file.size);
3822
+ const formData = new FormData();
3823
+ formData.append("file", file.slice(start, end));
3824
+ formData.append("uploadId", uploadInfo.uploadId);
3825
+ formData.append("partNumber", String(index + 1));
3826
+ formData.append("bucketName", uploadInfo.bucketName);
3827
+ formData.append("objectName", uploadInfo.objectName);
3828
+ const part = await request({
3829
+ url: "/file/multipart/upload",
3830
+ method: "post",
3831
+ data: formData
3832
+ });
3833
+ parts.push(part.data || part.result);
3834
+ onProgress?.(Math.round((index + 1) / totalParts * 100));
3835
+ }
3836
+ const completed = await request({
3837
+ url: "/file/multipart/complete",
3838
+ method: "post",
3839
+ data: {
3840
+ uploadId: uploadInfo.uploadId,
3841
+ bucketName: uploadInfo.bucketName,
3842
+ objectName: uploadInfo.objectName,
3843
+ originalName: file.name,
3844
+ contentType: file.type || void 0,
3845
+ parts: parts.sort((a, b) => a.partNumber - b.partNumber)
3846
+ }
3847
+ });
3848
+ return normalizeUploadData(completed.data || completed.result, file, bucketName);
3849
+ } catch (error) {
3850
+ await request({
3851
+ url: "/file/multipart/abort",
3852
+ method: "post",
3853
+ data: {
3854
+ uploadId: uploadInfo?.uploadId,
3855
+ bucketName: uploadInfo?.bucketName,
3856
+ objectName: uploadInfo?.objectName
3857
+ }
3858
+ }).catch(() => void 0);
3859
+ throw error;
3860
+ }
3861
+ }
3862
+ function createFormRuntimeApi(config) {
3863
+ const { baseUrl = getDefaultBaseUrl(), ...overrides } = config ?? {};
3864
+ const request = overrides.request ?? createDefaultRequest(baseUrl);
3865
+ const defaults = {
3866
+ request,
3867
+ uploadFile: async (file, bucketName = "files", onProgress) => {
3868
+ if (file.size > CHUNK_UPLOAD_THRESHOLD) {
3869
+ return uploadChunkedFile(request, file, bucketName, onProgress);
3870
+ }
3871
+ return uploadWithXhr(baseUrl, file, bucketName, onProgress);
3872
+ },
3873
+ deleteFile: async (objectName, bucketName = "files") => {
3874
+ await request({ url: "/file/delete", method: "post", data: { bucketName, objectName } });
3875
+ return { success: true };
3876
+ },
3877
+ createDownloadTicket: async (bucketName, objectName, fileName) => {
3878
+ const response = await request({
3879
+ url: "/file/download-ticket",
3880
+ method: "post",
3881
+ data: { bucketName, objectName, fileName }
3882
+ });
3883
+ return response.data || response.result;
3884
+ },
3885
+ createFileAccessTicket: async (bucketName, objectName, fileName, purpose = "preview") => {
3886
+ const response = await request({
3887
+ url: "/file/access-ticket",
3888
+ method: "post",
3889
+ data: { bucketName, objectName, fileName, purpose }
3890
+ });
3891
+ return response.data || response.result;
3892
+ },
3893
+ getUserById: async (id) => {
3894
+ const response = await request({
3895
+ url: `/user/${id}`,
3896
+ method: "get"
3897
+ });
3898
+ return response.data || response.result;
3899
+ },
3900
+ getUserList: async (params) => {
3901
+ const response = await request({
3902
+ url: "/user/list",
3903
+ method: "get",
3904
+ params
3905
+ });
3906
+ const data = response.data || response.result;
3907
+ return Array.isArray(data) ? data : data?.items || data?.list || [];
3908
+ },
3909
+ getDepartmentRoots: async () => {
3910
+ const response = await request({
3911
+ url: "/department/root",
3912
+ method: "get"
3913
+ });
3914
+ return response.data || response.result || [];
3915
+ },
3916
+ getDepartmentChildren: async (parentId) => {
3917
+ const response = await request({
3918
+ url: `/department/${parentId}/children`,
3919
+ method: "get"
3920
+ });
3921
+ return response.data || response.result || [];
3922
+ },
3923
+ getDepartmentParentDepartments: async (id) => {
3924
+ const response = await request({
3925
+ url: `/department/${id}/parentDepartments`,
3926
+ method: "get"
3927
+ });
3928
+ return response.data || response.result || [];
3929
+ },
3930
+ getDepartmentMembers: async (id) => {
3931
+ const response = await request({
3932
+ url: `/department/${id}/members`,
3933
+ method: "get"
3934
+ });
3935
+ const data = response.data || response.result;
3936
+ return Array.isArray(data) ? data : data?.items || data?.list || [];
3937
+ },
3938
+ getChinaDivisions: async (parentAdcode) => {
3939
+ const response = await request({
3940
+ url: "/china-divisions",
3941
+ method: "get",
3942
+ params: parentAdcode ? { parentAdcode } : void 0
3943
+ });
3944
+ return response.data || response.result || [];
3945
+ },
3946
+ advancedSearch: async (params) => {
3947
+ const response = await request({
3948
+ url: `/${params.appType}/v1/form/advancedSearch.json`,
3949
+ method: "get",
3950
+ params
3951
+ });
3952
+ return response.result || response.data || {};
3953
+ },
3954
+ getDingTalkSignature: async (url) => {
3955
+ const response = await request({
3956
+ url: "/dingtalk/signature",
3957
+ method: "get",
3958
+ params: { url }
3959
+ });
3960
+ return response.data || response.result;
3961
+ },
3962
+ submitFormData: async (payload) => {
3963
+ const response = await request({
3964
+ url: "/form/submitFormData",
3965
+ method: "post",
3966
+ data: payload
3967
+ });
3968
+ return response.data || response.result || response;
3969
+ },
3970
+ updateFormData: async (payload) => {
3971
+ const response = await request({
3972
+ url: `/${payload.appType}/v1/form/updateFormData.json`,
3973
+ method: "post",
3974
+ data: payload
3975
+ });
3976
+ return response.data || response.result || response;
3977
+ }
3978
+ };
3979
+ return { ...defaults, ...overrides, request };
3980
+ }
3981
+
3982
+ // src/core/FormProvider.tsx
3983
+ function FormProvider({
3984
+ schema,
3985
+ config,
3986
+ initialValues,
3987
+ components,
3988
+ children
3989
+ }) {
3990
+ const computedInitialValues = useMemo4(() => {
3991
+ const values = {};
3992
+ for (const field of schema.fields) {
3993
+ if (field.defaultValue !== void 0) {
3994
+ values[field.fieldId] = field.defaultValue;
3995
+ }
3996
+ }
3997
+ return { ...values, ...initialValues };
3998
+ }, [schema, initialValues]);
3999
+ const initialValuesRef = useRef4(computedInitialValues);
4000
+ const [formData, setFormData] = useState12({ ...computedInitialValues });
4001
+ const [fieldErrors, setFieldErrors] = useState12({});
4002
+ const [registeredFields] = useState12(/* @__PURE__ */ new Set());
4003
+ const api = useMemo4(() => createFormRuntimeApi(config.api), [config.api]);
4004
+ const fieldBehaviors = useMemo4(() => {
4005
+ const baseBehaviors = {};
4006
+ for (const field of schema.fields) {
4007
+ baseBehaviors[field.fieldId] = field.behavior || "NORMAL";
4008
+ }
4009
+ let computed = baseBehaviors;
4010
+ if (config.effects && config.effects.length > 0) {
4011
+ computed = evaluateEffects(config.effects, formData, baseBehaviors);
4012
+ }
4013
+ if (config.permissions?.fieldPermissions) {
4014
+ for (const [fieldId, behavior] of Object.entries(config.permissions.fieldPermissions)) {
4015
+ computed[fieldId] = behavior;
4016
+ }
4017
+ }
4018
+ if (config.mode === "readonly") {
4019
+ for (const fieldId of Object.keys(computed)) {
4020
+ if (computed[fieldId] !== "HIDDEN") {
4021
+ computed[fieldId] = "READONLY";
4022
+ }
4023
+ }
4024
+ }
4025
+ return computed;
4026
+ }, [schema, config.effects, config.permissions, config.mode, formData]);
4027
+ const setFieldValue = useCallback((fieldId, value) => {
4028
+ setFormData((prev) => ({ ...prev, [fieldId]: value }));
4029
+ setFieldErrors((prev) => {
4030
+ if (prev[fieldId]) {
4031
+ const next = { ...prev };
4032
+ delete next[fieldId];
4033
+ return next;
4034
+ }
4035
+ return prev;
4036
+ });
4037
+ }, []);
4038
+ const getFieldValue = useCallback((fieldId) => formData[fieldId], [formData]);
4039
+ const getFormData = useCallback(() => ({ ...formData }), [formData]);
4040
+ const validateFieldById = useCallback(
4041
+ async (fieldId) => {
4042
+ const field = schema.fields.find((f) => f.fieldId === fieldId);
4043
+ if (!field) return true;
4044
+ const rules = [];
4045
+ if (field.required) {
4046
+ rules.push({ required: true, message: `${field.label}\u4E3A\u5FC5\u586B\u9879` });
4047
+ }
4048
+ if (field.rules) {
4049
+ rules.push(...field.rules);
4050
+ }
4051
+ if (rules.length === 0) return true;
4052
+ const error = await validateField(formData[fieldId], rules);
4053
+ setFieldErrors((prev) => {
4054
+ if (error) {
4055
+ return { ...prev, [fieldId]: error };
4056
+ }
4057
+ const next = { ...prev };
4058
+ delete next[fieldId];
4059
+ return next;
4060
+ });
4061
+ return !error;
4062
+ },
4063
+ [schema, formData]
4064
+ );
4065
+ const validateAll = useCallback(async () => {
4066
+ const fieldRules = {};
4067
+ for (const field of schema.fields) {
4068
+ const rules = [];
4069
+ if (field.required) {
4070
+ rules.push({ required: true, message: `${field.label}\u4E3A\u5FC5\u586B\u9879` });
4071
+ }
4072
+ if (field.rules) {
4073
+ rules.push(...field.rules);
4074
+ }
4075
+ if (rules.length > 0) {
4076
+ fieldRules[field.fieldId] = rules;
4077
+ }
4078
+ }
4079
+ const errors = await validateAllFields(formData, fieldRules);
4080
+ setFieldErrors(errors);
4081
+ return Object.keys(errors).length === 0;
4082
+ }, [schema, formData]);
4083
+ const resetForm = useCallback(() => {
4084
+ setFormData({ ...initialValuesRef.current });
4085
+ setFieldErrors({});
4086
+ }, []);
4087
+ const registerField = useCallback(
4088
+ (fieldId) => {
4089
+ registeredFields.add(fieldId);
4090
+ },
4091
+ [registeredFields]
4092
+ );
4093
+ const unregisterField = useCallback(
4094
+ (fieldId) => {
4095
+ registeredFields.delete(fieldId);
4096
+ },
4097
+ [registeredFields]
4098
+ );
4099
+ const contextValue = useMemo4(
4100
+ () => ({
4101
+ mode: config.mode,
4102
+ schema,
4103
+ formData,
4104
+ fieldErrors,
4105
+ fieldBehaviors,
4106
+ api,
4107
+ config: {
4108
+ mode: config.mode,
4109
+ formUuid: config.formUuid,
4110
+ appType: config.appType,
4111
+ formInstanceId: config.formInstanceId,
4112
+ submit: config.submit
4113
+ },
4114
+ setFieldValue,
4115
+ getFieldValue,
4116
+ getFormData,
4117
+ validateField: validateFieldById,
4118
+ validateAll,
4119
+ resetForm,
4120
+ registerField,
4121
+ unregisterField
4122
+ }),
4123
+ [
4124
+ config.mode,
4125
+ config.formUuid,
4126
+ schema,
4127
+ formData,
4128
+ fieldErrors,
4129
+ fieldBehaviors,
4130
+ api,
4131
+ config.appType,
4132
+ config.formInstanceId,
4133
+ config.submit,
4134
+ setFieldValue,
4135
+ getFieldValue,
4136
+ getFormData,
4137
+ validateFieldById,
4138
+ validateAll,
4139
+ resetForm,
4140
+ registerField,
4141
+ unregisterField
4142
+ ]
4143
+ );
4144
+ const registryComponents = components ?? defaultComponentRegistry;
4145
+ return React32.createElement(
4146
+ FormContext.Provider,
4147
+ { value: contextValue },
4148
+ React32.createElement(ComponentRegistryProvider, { components: registryComponents, children })
4149
+ );
4150
+ }
4151
+
4152
+ // src/core/FormRenderer.tsx
4153
+ import React33 from "react";
4154
+ import { jsx as jsx67 } from "react/jsx-runtime";
4155
+ var columnsClassMap = {
4156
+ 1: "sy-grid-cols-1",
4157
+ 2: "sy-grid-cols-1 md:sy-grid-cols-2",
4158
+ 3: "sy-grid-cols-1 md:sy-grid-cols-2 lg:sy-grid-cols-3",
4159
+ 4: "sy-grid-cols-1 md:sy-grid-cols-2 lg:sy-grid-cols-4"
4160
+ };
4161
+ var sizeClassMap = {
4162
+ compact: "sy-gap-y-3 sy-gap-x-4",
4163
+ default: "sy-gap-y-5 sy-gap-x-6",
4164
+ large: "sy-gap-y-6 sy-gap-x-8"
4165
+ };
4166
+ function FieldRenderer({ field, fieldClassName }) {
4167
+ const Component = useComponent(field.componentName);
4168
+ if (!Component) {
4169
+ if (process.env.NODE_ENV !== "production") {
4170
+ console.warn(
4171
+ `[FormRenderer] Component "${field.componentName}" is not registered, skipping field "${field.fieldId}".`
4172
+ );
4173
+ }
4174
+ return null;
4175
+ }
4176
+ const { fieldId, componentName: _, ...fieldProps } = field;
4177
+ return React33.createElement(Component, {
4178
+ ...fieldProps,
4179
+ fieldId,
4180
+ className: fieldClassName ?? fieldProps.className
4181
+ });
4182
+ }
4183
+ function FormRenderer({
4184
+ className,
4185
+ fieldClassName,
4186
+ columns = 1,
4187
+ size = "default"
4188
+ }) {
4189
+ const { schema } = useFormContext();
4190
+ const gridClass = columnsClassMap[columns];
4191
+ const gapClass = sizeClassMap[size];
4192
+ const containerClassName = ["sy-grid", gridClass, gapClass, className].filter(Boolean).join(" ");
4193
+ return /* @__PURE__ */ jsx67("div", { className: containerClassName, "data-testid": "form-renderer", children: schema.fields.map((field) => /* @__PURE__ */ jsx67(FieldRenderer, { field, fieldClassName }, field.fieldId)) });
4194
+ }
4195
+
4196
+ // src/core/FormActions.tsx
4197
+ import { useState as useState13, useCallback as useCallback2 } from "react";
4198
+ import { Button as Button4 } from "antd";
4199
+ import { jsx as jsx68, jsxs as jsxs15 } from "react/jsx-runtime";
4200
+ function FormActions({
4201
+ className,
4202
+ submitText = "\u63D0\u4EA4",
4203
+ resetText = "\u91CD\u7F6E",
4204
+ showReset = true,
4205
+ onSubmit
4206
+ }) {
4207
+ const { mode, validateAll, getFormData, resetForm, api, config } = useFormContext();
4208
+ const [isSubmitting, setIsSubmitting] = useState13(false);
4209
+ const [submitError, setSubmitError] = useState13(null);
4210
+ const handleSubmit = useCallback2(async () => {
4211
+ setSubmitError(null);
4212
+ const valid = await validateAll();
4213
+ if (!valid) return;
4214
+ const values = getFormData();
4215
+ setIsSubmitting(true);
4216
+ try {
4217
+ const beforeResult = await config.submit?.beforeSubmit?.(values);
4218
+ if (beforeResult === false) return;
4219
+ let response;
4220
+ if (onSubmit) {
4221
+ response = await onSubmit(values);
4222
+ } else if (mode === "edit" && config.formInstanceId) {
4223
+ response = await api.updateFormData({
4224
+ appType: config.appType,
4225
+ formUuid: config.formUuid,
4226
+ formInstId: config.formInstanceId,
4227
+ updateFormDataJson: JSON.stringify(values)
4228
+ });
4229
+ } else {
4230
+ response = await api.submitFormData({
4231
+ appType: config.appType,
4232
+ formUuid: config.formUuid,
4233
+ data: values
4234
+ });
4235
+ }
4236
+ await config.submit?.afterSubmit?.(response);
4237
+ if (config.submit?.submitSuccessMode === "redirect" && config.submit.redirectUrl) {
4238
+ window.location.href = config.submit.redirectUrl;
4239
+ }
4240
+ } catch (e) {
4241
+ setSubmitError(e?.message || "\u63D0\u4EA4\u5931\u8D25");
4242
+ } finally {
4243
+ setIsSubmitting(false);
4244
+ }
4245
+ }, [api, config, getFormData, mode, onSubmit, validateAll]);
4246
+ const handleReset = useCallback2(() => {
4247
+ resetForm();
4248
+ }, [resetForm]);
4249
+ if (mode === "readonly") return null;
4250
+ return /* @__PURE__ */ jsxs15("div", { className: className ?? "sy-form-actions", "data-testid": "form-actions", children: [
4251
+ /* @__PURE__ */ jsx68(
4252
+ Button4,
4253
+ {
4254
+ type: "primary",
4255
+ htmlType: "submit",
4256
+ loading: isSubmitting,
4257
+ onClick: handleSubmit,
4258
+ "data-testid": "form-submit-btn",
4259
+ children: submitText
4260
+ }
4261
+ ),
4262
+ showReset && /* @__PURE__ */ jsx68(
4263
+ Button4,
4264
+ {
4265
+ htmlType: "reset",
4266
+ disabled: isSubmitting,
4267
+ onClick: handleReset,
4268
+ "data-testid": "form-reset-btn",
4269
+ children: resetText
4270
+ }
4271
+ ),
4272
+ submitError && /* @__PURE__ */ jsx68("span", { className: "sy-field-error", role: "alert", "data-testid": "form-submit-error", children: submitError })
4273
+ ] });
4274
+ }
4275
+
4276
+ // src/core/FormContainer.tsx
4277
+ import { jsx as jsx69, jsxs as jsxs16 } from "react/jsx-runtime";
4278
+ function cn2(...classes) {
4279
+ return classes.filter(Boolean).join(" ");
4280
+ }
4281
+ var maxWidthClassMap = {
4282
+ sm: "sy-form-container-sm",
4283
+ md: "sy-form-container-md",
4284
+ lg: "sy-form-container-lg",
4285
+ xl: "sy-form-container-xl",
4286
+ full: "sy-form-container-full"
4287
+ };
4288
+ function FormContainer({
4289
+ title,
4290
+ description,
4291
+ children,
4292
+ maxWidth = "md",
4293
+ className
4294
+ }) {
4295
+ const sizeClass = maxWidthClassMap[maxWidth];
4296
+ return /* @__PURE__ */ jsxs16("div", { className: cn2("sy-form-container", sizeClass, className), children: [
4297
+ (title || description) && /* @__PURE__ */ jsxs16("div", { className: "sy-form-header", children: [
4298
+ title && /* @__PURE__ */ jsx69("h2", { className: "sy-form-title", children: title }),
4299
+ description && /* @__PURE__ */ jsx69("p", { className: "sy-form-description", children: description })
4300
+ ] }),
4301
+ /* @__PURE__ */ jsx69("div", { className: "sy-form-body", children })
4302
+ ] });
4303
+ }
4304
+
4305
+ // src/layout/FormSection/index.tsx
4306
+ import { useState as useState14 } from "react";
4307
+ import { jsx as jsx70, jsxs as jsxs17 } from "react/jsx-runtime";
4308
+ function FormSection({
4309
+ title,
4310
+ description,
4311
+ collapsible = false,
4312
+ defaultCollapsed = false,
4313
+ className,
4314
+ titleClassName,
4315
+ contentClassName,
4316
+ children
4317
+ }) {
4318
+ const [collapsed, setCollapsed] = useState14(defaultCollapsed);
4319
+ const handleToggle = () => {
4320
+ if (collapsible) {
4321
+ setCollapsed((prev) => !prev);
4322
+ }
4323
+ };
4324
+ return /* @__PURE__ */ jsxs17(
4325
+ "section",
4326
+ {
4327
+ className: className ?? "border border-gray-200 rounded-lg p-4 mb-4",
4328
+ "data-testid": "form-section",
4329
+ children: [
4330
+ /* @__PURE__ */ jsxs17(
4331
+ "div",
4332
+ {
4333
+ className: titleClassName ?? "flex items-center justify-between mb-3",
4334
+ onClick: handleToggle,
4335
+ role: collapsible ? "button" : void 0,
4336
+ "aria-expanded": collapsible ? !collapsed : void 0,
4337
+ "data-testid": "form-section-header",
4338
+ children: [
4339
+ /* @__PURE__ */ jsxs17("div", { children: [
4340
+ /* @__PURE__ */ jsx70("h3", { className: "text-base font-semibold text-gray-900", children: title }),
4341
+ description && /* @__PURE__ */ jsx70("p", { className: "text-sm text-gray-500 mt-1", "data-testid": "form-section-description", children: description })
4342
+ ] }),
4343
+ collapsible && /* @__PURE__ */ jsx70(
4344
+ "span",
4345
+ {
4346
+ className: "text-gray-400 transition-transform duration-200",
4347
+ "data-testid": "form-section-arrow",
4348
+ style: { transform: collapsed ? "rotate(-90deg)" : "rotate(0deg)" },
4349
+ children: "\u25BC"
4350
+ }
4351
+ )
4352
+ ]
4353
+ }
4354
+ ),
4355
+ !collapsed && /* @__PURE__ */ jsx70("div", { className: contentClassName, "data-testid": "form-section-content", children })
4356
+ ]
4357
+ }
4358
+ );
4359
+ }
4360
+
4361
+ // src/layout/FormGrid/index.tsx
4362
+ import { jsx as jsx71 } from "react/jsx-runtime";
4363
+ var columnClasses = {
4364
+ 1: "grid-cols-1",
4365
+ 2: "grid-cols-1 md:grid-cols-2",
4366
+ 3: "grid-cols-1 md:grid-cols-3",
4367
+ 4: "grid-cols-1 md:grid-cols-4"
4368
+ };
4369
+ var gapClasses = {
4370
+ 0: "gap-0",
4371
+ 1: "gap-1",
4372
+ 2: "gap-2",
4373
+ 3: "gap-3",
4374
+ 4: "gap-4",
4375
+ 5: "gap-5",
4376
+ 6: "gap-6",
4377
+ 8: "gap-8"
4378
+ };
4379
+ function FormGrid({ columns = 2, gap = 4, className, children }) {
4380
+ const colClass = columnClasses[columns];
4381
+ const gapClass = gapClasses[gap] ?? `gap-${gap}`;
4382
+ return /* @__PURE__ */ jsx71("div", { className: className ?? `grid ${colClass} ${gapClass}`, "data-testid": "form-grid", children });
4383
+ }
4384
+
4385
+ // src/layout/FormTabs/index.tsx
4386
+ import { useState as useState15 } from "react";
4387
+ import { jsx as jsx72, jsxs as jsxs18 } from "react/jsx-runtime";
4388
+ function FormTabs({ items, defaultActiveKey, className, tabClassName }) {
4389
+ const [activeKey, setActiveKey] = useState15(defaultActiveKey || items[0]?.key || "");
4390
+ const activeItem = items.find((item) => item.key === activeKey);
4391
+ return /* @__PURE__ */ jsxs18("div", { className: className ?? "w-full", "data-testid": "form-tabs", children: [
4392
+ /* @__PURE__ */ jsx72(
4393
+ "div",
4394
+ {
4395
+ className: tabClassName ?? "flex border-b border-gray-200 overflow-x-auto",
4396
+ role: "tablist",
4397
+ "data-testid": "form-tabs-nav",
4398
+ children: items.map((item) => /* @__PURE__ */ jsx72(
4399
+ "button",
4400
+ {
4401
+ role: "tab",
4402
+ type: "button",
4403
+ "aria-selected": activeKey === item.key,
4404
+ className: activeKey === item.key ? "px-4 py-2 text-sm font-medium text-blue-600 border-b-2 border-blue-600 whitespace-nowrap" : "px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700 whitespace-nowrap",
4405
+ onClick: () => setActiveKey(item.key),
4406
+ "data-testid": `form-tab-${item.key}`,
4407
+ children: item.label
4408
+ },
4409
+ item.key
4410
+ ))
4411
+ }
4412
+ ),
4413
+ /* @__PURE__ */ jsx72("div", { className: "pt-4", role: "tabpanel", "data-testid": "form-tabs-content", children: activeItem?.children })
4414
+ ] });
4415
+ }
4416
+
4417
+ // src/layout/FormSteps/index.tsx
4418
+ import React37, { useState as useState16 } from "react";
4419
+ import { jsx as jsx73, jsxs as jsxs19 } from "react/jsx-runtime";
4420
+ function FormSteps({ items, className, onStepChange }) {
4421
+ const [currentStep, setCurrentStep] = useState16(0);
4422
+ const goTo = (step) => {
4423
+ setCurrentStep(step);
4424
+ onStepChange?.(step);
4425
+ };
4426
+ const handlePrev = () => {
4427
+ if (currentStep > 0) {
4428
+ goTo(currentStep - 1);
4429
+ }
4430
+ };
4431
+ const handleNext = () => {
4432
+ if (currentStep < items.length - 1) {
4433
+ goTo(currentStep + 1);
4434
+ }
4435
+ };
4436
+ return /* @__PURE__ */ jsxs19("div", { className: className ?? "w-full", "data-testid": "form-steps", children: [
4437
+ /* @__PURE__ */ jsx73("div", { className: "flex items-center mb-6", "data-testid": "form-steps-indicator", children: items.map((item, index) => /* @__PURE__ */ jsxs19(React37.Fragment, { children: [
4438
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center", children: [
4439
+ /* @__PURE__ */ jsx73(
4440
+ "div",
4441
+ {
4442
+ className: index === currentStep ? "w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium bg-blue-600 text-white" : index < currentStep ? "w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium bg-green-500 text-white" : "w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium bg-gray-200 text-gray-600",
4443
+ "data-testid": `form-step-circle-${index}`,
4444
+ children: index + 1
4445
+ }
4446
+ ),
4447
+ /* @__PURE__ */ jsxs19("div", { className: "ml-2 hidden sm:block", children: [
4448
+ /* @__PURE__ */ jsx73("div", { className: "text-sm font-medium text-gray-900", children: item.title }),
4449
+ item.description && /* @__PURE__ */ jsx73("div", { className: "text-xs text-gray-500", children: item.description })
4450
+ ] })
4451
+ ] }),
4452
+ index < items.length - 1 && /* @__PURE__ */ jsx73(
4453
+ "div",
4454
+ {
4455
+ className: index < currentStep ? "flex-1 h-0.5 mx-4 bg-green-500" : "flex-1 h-0.5 mx-4 bg-gray-200",
4456
+ "data-testid": `form-step-connector-${index}`
4457
+ }
4458
+ )
4459
+ ] }, item.key)) }),
4460
+ /* @__PURE__ */ jsx73("div", { "data-testid": "form-steps-content", children: items[currentStep]?.children }),
4461
+ /* @__PURE__ */ jsxs19("div", { className: "flex justify-between mt-6", "data-testid": "form-steps-actions", children: [
4462
+ /* @__PURE__ */ jsx73(
4463
+ "button",
4464
+ {
4465
+ type: "button",
4466
+ onClick: handlePrev,
4467
+ disabled: currentStep === 0,
4468
+ className: currentStep === 0 ? "px-4 py-2 text-sm font-medium text-gray-400 bg-gray-100 rounded cursor-not-allowed" : "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded hover:bg-gray-50",
4469
+ "data-testid": "form-steps-prev",
4470
+ children: "\u4E0A\u4E00\u6B65"
4471
+ }
4472
+ ),
4473
+ /* @__PURE__ */ jsx73(
4474
+ "button",
4475
+ {
4476
+ type: "button",
4477
+ onClick: handleNext,
4478
+ disabled: currentStep === items.length - 1,
4479
+ className: currentStep === items.length - 1 ? "px-4 py-2 text-sm font-medium text-gray-400 bg-gray-100 rounded cursor-not-allowed" : "px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded hover:bg-blue-700",
4480
+ "data-testid": "form-steps-next",
4481
+ children: "\u4E0B\u4E00\u6B65"
4482
+ }
4483
+ )
4484
+ ] })
4485
+ ] });
4486
+ }
4487
+
4488
+ // src/display/FormSummary/index.tsx
4489
+ import { jsx as jsx74, jsxs as jsxs20 } from "react/jsx-runtime";
4490
+ var columnClasses2 = {
4491
+ 1: "grid-cols-1",
4492
+ 2: "grid-cols-1 md:grid-cols-2",
4493
+ 3: "grid-cols-1 md:grid-cols-3"
4494
+ };
4495
+ function FormSummary({
4496
+ className,
4497
+ labelClassName,
4498
+ valueClassName,
4499
+ columns = 2,
4500
+ fields
4501
+ }) {
4502
+ const { schema, formData } = useFormContext();
4503
+ const displayFields = fields ? schema.fields.filter((f) => fields.includes(f.fieldId)) : schema.fields;
4504
+ const colClass = columnClasses2[columns];
4505
+ const formatValue = (value) => {
4506
+ if (value === null || value === void 0 || value === "") return "--";
4507
+ if (Array.isArray(value)) {
4508
+ if (value.length === 0) return "--";
4509
+ return value.map((v) => typeof v === "object" && v?.label ? v.label : String(v)).join(", ");
4510
+ }
4511
+ if (typeof value === "object" && value?.label) return value.label;
4512
+ return String(value);
4513
+ };
4514
+ return /* @__PURE__ */ jsx74("div", { className: className ?? `grid ${colClass} gap-4`, "data-testid": "form-summary", children: displayFields.map((field) => /* @__PURE__ */ jsxs20(
4515
+ "div",
4516
+ {
4517
+ className: "flex flex-col",
4518
+ "data-testid": `summary-field-${field.fieldId}`,
4519
+ children: [
4520
+ /* @__PURE__ */ jsx74("span", { className: labelClassName ?? "text-sm text-gray-500 mb-1", children: field.label }),
4521
+ /* @__PURE__ */ jsx74("span", { className: valueClassName ?? "text-sm text-gray-900", children: formatValue(formData[field.fieldId]) })
4522
+ ]
4523
+ },
4524
+ field.fieldId
4525
+ )) });
4526
+ }
4527
+
4528
+ // src/hooks/useFormEngine.ts
4529
+ import { useState as useState17, useCallback as useCallback3, useMemo as useMemo5, useRef as useRef5 } from "react";
4530
+ function useFormEngine(schema, config) {
4531
+ const initialValues = useMemo5(() => {
4532
+ const values = {};
4533
+ for (const field of schema.fields) {
4534
+ if (field.defaultValue !== void 0) {
4535
+ values[field.fieldId] = field.defaultValue;
4536
+ }
4537
+ }
4538
+ return values;
4539
+ }, [schema]);
4540
+ const initialValuesRef = useRef5(initialValues);
4541
+ const [formData, setFormData] = useState17({ ...initialValues });
4542
+ const [fieldErrors, setFieldErrors] = useState17({});
4543
+ const fieldBehaviors = useMemo5(() => {
4544
+ const baseBehaviors = {};
4545
+ for (const field of schema.fields) {
4546
+ baseBehaviors[field.fieldId] = field.behavior || "NORMAL";
4547
+ }
4548
+ let computed = baseBehaviors;
4549
+ if (config.effects && config.effects.length > 0) {
4550
+ computed = evaluateEffects(config.effects, formData, baseBehaviors);
4551
+ }
4552
+ if (config.permissions?.fieldPermissions) {
4553
+ for (const [fieldId, behavior] of Object.entries(config.permissions.fieldPermissions)) {
4554
+ computed[fieldId] = behavior;
4555
+ }
4556
+ }
4557
+ if (config.mode === "readonly") {
4558
+ for (const fieldId of Object.keys(computed)) {
4559
+ if (computed[fieldId] !== "HIDDEN") {
4560
+ computed[fieldId] = "READONLY";
4561
+ }
4562
+ }
4563
+ }
4564
+ return computed;
4565
+ }, [schema, config.effects, config.permissions, config.mode, formData]);
4566
+ const setFieldValue = useCallback3((fieldId, value) => {
4567
+ setFormData((prev) => ({ ...prev, [fieldId]: value }));
4568
+ setFieldErrors((prev) => {
4569
+ if (prev[fieldId]) {
4570
+ const next = { ...prev };
4571
+ delete next[fieldId];
4572
+ return next;
4573
+ }
4574
+ return prev;
4575
+ });
4576
+ }, []);
4577
+ const getFieldValue = useCallback3((fieldId) => formData[fieldId], [formData]);
4578
+ const getFormData = useCallback3(() => ({ ...formData }), [formData]);
4579
+ const validateAll = useCallback3(async () => {
4580
+ const fieldRules = {};
4581
+ for (const field of schema.fields) {
4582
+ const rules = [];
4583
+ if (field.required) {
4584
+ rules.push({ required: true, message: `${field.label}\u4E3A\u5FC5\u586B\u9879` });
4585
+ }
4586
+ if (field.rules) {
4587
+ rules.push(...field.rules);
4588
+ }
4589
+ if (rules.length > 0) {
4590
+ fieldRules[field.fieldId] = rules;
4591
+ }
4592
+ }
4593
+ const errors = await validateAllFields(formData, fieldRules);
4594
+ setFieldErrors(errors);
4595
+ return Object.keys(errors).length === 0;
4596
+ }, [schema, formData]);
4597
+ const resetForm = useCallback3(() => {
4598
+ setFormData({ ...initialValuesRef.current });
4599
+ setFieldErrors({});
4600
+ }, []);
4601
+ return {
4602
+ formData,
4603
+ setFieldValue,
4604
+ getFieldValue,
4605
+ getFormData,
4606
+ validateAll,
4607
+ resetForm,
4608
+ mode: config.mode,
4609
+ fieldBehaviors,
4610
+ fieldErrors
4611
+ };
4612
+ }
4613
+
4614
+ // src/hooks/useFormData.ts
4615
+ import { useState as useState18, useCallback as useCallback4, useRef as useRef6 } from "react";
4616
+ function useFormData(initialValues = {}) {
4617
+ const [formData, setFormData] = useState18({ ...initialValues });
4618
+ const [dirtyFields, setDirtyFields] = useState18(/* @__PURE__ */ new Set());
4619
+ const initialValuesRef = useRef6(initialValues);
4620
+ const setFieldValue = useCallback4((fieldId, value) => {
4621
+ setFormData((prev) => ({ ...prev, [fieldId]: value }));
4622
+ setDirtyFields((prev) => {
4623
+ const next = new Set(prev);
4624
+ next.add(fieldId);
4625
+ return next;
4626
+ });
4627
+ }, []);
4628
+ const getFieldValue = useCallback4(
4629
+ (fieldId) => {
4630
+ return formData[fieldId];
4631
+ },
4632
+ [formData]
4633
+ );
4634
+ const getFormData = useCallback4(() => {
4635
+ return { ...formData };
4636
+ }, [formData]);
4637
+ const resetForm = useCallback4(() => {
4638
+ setFormData({ ...initialValuesRef.current });
4639
+ setDirtyFields(/* @__PURE__ */ new Set());
4640
+ }, []);
4641
+ return {
4642
+ formData,
4643
+ setFieldValue,
4644
+ getFieldValue,
4645
+ getFormData,
4646
+ resetForm,
4647
+ dirtyFields
4648
+ };
4649
+ }
4650
+
4651
+ // src/hooks/useFieldBehavior.ts
4652
+ import { useMemo as useMemo6 } from "react";
4653
+ function useFieldBehavior({
4654
+ fieldId,
4655
+ permissions,
4656
+ effects,
4657
+ formData,
4658
+ defaultBehavior = "NORMAL"
4659
+ }) {
4660
+ return useMemo6(() => {
4661
+ if (permissions && permissions[fieldId]) {
4662
+ return permissions[fieldId];
4663
+ }
4664
+ if (effects && effects.length > 0) {
4665
+ const baseBehaviors = { [fieldId]: defaultBehavior };
4666
+ const computed = evaluateEffects(effects, formData, baseBehaviors);
4667
+ return computed[fieldId];
4668
+ }
4669
+ return defaultBehavior;
4670
+ }, [fieldId, permissions, effects, formData, defaultBehavior]);
4671
+ }
4672
+
4673
+ // src/hooks/useFormSubmit.ts
4674
+ import { useState as useState19, useCallback as useCallback5 } from "react";
4675
+ function useFormSubmit(config) {
4676
+ const [isSubmitting, setIsSubmitting] = useState19(false);
4677
+ const [submitError, setSubmitError] = useState19(null);
4678
+ const submit = useCallback5(
4679
+ async (formData, validateFn) => {
4680
+ setSubmitError(null);
4681
+ const isValid = await validateFn();
4682
+ if (!isValid) {
4683
+ setSubmitError("\u8868\u5355\u6821\u9A8C\u5931\u8D25");
4684
+ return;
4685
+ }
4686
+ setIsSubmitting(true);
4687
+ try {
4688
+ if (config?.beforeSubmit) {
4689
+ const result = await config.beforeSubmit(formData);
4690
+ if (result === false) {
4691
+ setIsSubmitting(false);
4692
+ return;
4693
+ }
4694
+ }
4695
+ if (config?.afterSubmit) {
4696
+ await config.afterSubmit(formData);
4697
+ }
4698
+ } catch (e) {
4699
+ setSubmitError(e?.message || "\u63D0\u4EA4\u5931\u8D25");
4700
+ } finally {
4701
+ setIsSubmitting(false);
4702
+ }
4703
+ },
4704
+ [config]
4705
+ );
4706
+ return { submit, isSubmitting, submitError };
4707
+ }
4708
+
4709
+ // src/utils/defineFormSchema.ts
4710
+ function defineFormSchema(schema) {
4711
+ return schema;
4712
+ }
4713
+ export {
4714
+ AddressField,
4715
+ AssociationFormField,
4716
+ AttachmentField,
4717
+ CascadeDateField,
4718
+ CascadeSelectField,
4719
+ CheckboxField,
4720
+ ComponentRegistryContext,
4721
+ ComponentRegistryProvider,
4722
+ DateField,
4723
+ DepartmentSelectField,
4724
+ DigitalSignatureField,
4725
+ EditorField,
4726
+ UserSelectField as EmployeeSelectField,
4727
+ FieldWrapper,
4728
+ FormActions,
4729
+ FormContainer,
4730
+ FormContext,
4731
+ FormGrid,
4732
+ FormProvider,
4733
+ FormRenderer,
4734
+ FormSection,
4735
+ FormSteps,
4736
+ FormSummary,
4737
+ FormTabs,
4738
+ ImageField,
4739
+ JSONField,
4740
+ LocationField,
4741
+ MultiSelectField,
4742
+ NumberField,
4743
+ RadioField,
4744
+ SelectField,
4745
+ SerialNumberField,
4746
+ SubFormField,
4747
+ TextAreaField,
4748
+ TextField,
4749
+ TextAreaField as TextareaField,
4750
+ UserSelectField,
4751
+ createFormRuntimeApi,
4752
+ defaultComponentRegistry,
4753
+ defineFormSchema,
4754
+ evaluateEffects,
4755
+ useComponent,
4756
+ useDeviceDetect,
4757
+ useFieldBehavior,
4758
+ useFormContext,
4759
+ useFormData,
4760
+ useFormEngine,
4761
+ useFormSubmit,
4762
+ validateAllFields,
4763
+ validateField
4764
+ };
4765
+ //# sourceMappingURL=index.mjs.map