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