zy-react-library 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +5 -1
  2. package/components/Cascader/Area/index.js +11 -11
  3. package/components/Cascader/Basic/index.js +23 -30
  4. package/components/Cascader/Dictionary/index.js +42 -42
  5. package/components/Cascader/Industry/index.js +12 -11
  6. package/components/Editor/index.js +43 -63
  7. package/components/FormBuilder/FormBuilder.js +97 -87
  8. package/components/FormBuilder/FormItemsRenderer.js +579 -581
  9. package/components/FormBuilder/index.js +5 -3
  10. package/components/HeaderBack/index.js +39 -32
  11. package/components/HiddenInfo/gwj/index.js +507 -439
  12. package/components/Icon/AddIcon/index.js +6 -6
  13. package/components/Icon/BackIcon/index.js +6 -6
  14. package/components/Icon/DeleteIcon/index.js +6 -6
  15. package/components/Icon/DownloadIcon/index.js +6 -6
  16. package/components/Icon/EditIcon/index.js +6 -6
  17. package/components/Icon/ExportIcon/index.js +6 -6
  18. package/components/Icon/ImportIcon/index.js +6 -6
  19. package/components/Icon/LocationIcon/index.js +6 -6
  20. package/components/Icon/PrintIcon/index.js +6 -6
  21. package/components/Icon/ResetIcon/index.js +6 -6
  22. package/components/Icon/SearchIcon/index.js +6 -6
  23. package/components/Icon/VideoIcon/index.js +6 -6
  24. package/components/Icon/ViewIcon/index.js +6 -6
  25. package/components/ImportFile/index.js +94 -91
  26. package/components/LeftTree/Area/index.js +15 -15
  27. package/components/LeftTree/Basic/index.js +54 -65
  28. package/components/LeftTree/Department/Gwj/index.js +29 -32
  29. package/components/LeftTree/Dictionary/index.js +42 -42
  30. package/components/Map/MapSelector.js +280 -254
  31. package/components/Map/index.js +90 -77
  32. package/components/Page/index.d.ts +2 -0
  33. package/components/Page/index.js +44 -34
  34. package/components/Pdf/index.js +92 -90
  35. package/components/PreviewImg/index.js +26 -32
  36. package/components/PreviewPdf/index.js +78 -86
  37. package/components/Search/index.js +147 -141
  38. package/components/Select/Basic/index.js +70 -76
  39. package/components/Select/Dictionary/index.js +42 -42
  40. package/components/Select/Personnel/Gwj/index.js +45 -49
  41. package/components/SelectCreate/index.js +33 -40
  42. package/components/SelectTree/Area/index.js +11 -17
  43. package/components/SelectTree/Basic/index.js +105 -102
  44. package/components/SelectTree/Department/Gwj/index.js +40 -46
  45. package/components/SelectTree/Dictionary/index.js +42 -42
  46. package/components/SelectTree/HiddenLevel/Gwj/index.js +33 -35
  47. package/components/SelectTree/HiddenPart/Gwj/index.js +16 -19
  48. package/components/SelectTree/Industry/index.js +12 -18
  49. package/components/Signature/index.js +68 -62
  50. package/components/Table/index.js +77 -73
  51. package/components/Table/index.less +7 -1
  52. package/components/TooltipPreviewImg/index.js +28 -27
  53. package/components/Upload/index.js +229 -275
  54. package/components/Video/AliPlayer.js +182 -160
  55. package/components/Video/index.js +71 -90
  56. package/css/common.less +4 -0
  57. package/enum/dictionary/index.js +5 -3
  58. package/enum/formItemRender/index.js +37 -35
  59. package/enum/hidden/gwj/index.js +65 -26
  60. package/enum/uploadFile/gwj/index.js +166 -84
  61. package/hooks/useDeleteFile/index.js +24 -30
  62. package/hooks/useDictionary/index.js +28 -30
  63. package/hooks/useDownloadBlob/index.js +78 -77
  64. package/hooks/useDownloadFile/index.js +76 -79
  65. package/hooks/useGetFile/index.js +32 -32
  66. package/hooks/useGetUrlQuery/index.js +1 -2
  67. package/hooks/useGetUserInfo/index.js +19 -26
  68. package/hooks/useIdle/index.js +9 -11
  69. package/hooks/useImportFile/index.js +30 -28
  70. package/hooks/useIsExistenceDuplicateSelection/index.js +25 -18
  71. package/hooks/useTable/index.js +49 -38
  72. package/hooks/useUploadFile/index.js +142 -147
  73. package/hooks/useUrlQueryCriteria/index.js +20 -13
  74. package/package.json +14 -1
  75. package/regular/index.js +34 -39
  76. package/utils/index.js +515 -511
@@ -1,24 +1,16 @@
1
- import { InfoCircleOutlined } from "@ant-design/icons";
2
- import {
3
- Button,
4
- Checkbox,
5
- Col,
6
- DatePicker,
7
- Divider,
8
- Form,
9
- Input,
10
- InputNumber,
11
- Radio,
12
- Row,
13
- Select,
14
- Tooltip,
15
- } from "antd";
16
- import dayjs from "dayjs";
17
- import { FORM_ITEM_RENDER_ENUM } from "../../enum/formItemRender";
18
-
19
- const { TextArea } = Input;
20
- const { RangePicker } = DatePicker;
21
-
1
+ import { InfoCircleOutlined } from '@ant-design/icons';
2
+ import { Input, DatePicker, Form, Col, Divider, Row, Button, Tooltip, Checkbox, Radio, Select, InputNumber } from 'antd';
3
+ import dayjs from 'dayjs';
4
+ import { FORM_ITEM_RENDER_ENUM } from '../../enum/formItemRender/index.js';
5
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
6
+
7
+ const {
8
+ TextArea
9
+ } = Input;
10
+ const {
11
+ RangePicker
12
+ } = DatePicker;
13
+
22
14
  /**
23
15
  * 表单项渲染器组件
24
16
  * @param {object} props - 组件属性
@@ -29,563 +21,569 @@ const { RangePicker } = DatePicker;
29
21
  * @param {boolean} props.collapse - 是否折叠(仅显示前3项)
30
22
  * @param {boolean} props.useAutoGenerateRequired - 是否自动生成必填规则
31
23
  * @param {object} props.initialValues - 初始值
32
- */
33
- const FormItemsRenderer = ({
34
- options,
35
- labelCol,
36
- gutter = 24,
37
- span = 12,
38
- collapse = false,
39
- useAutoGenerateRequired = true,
40
- initialValues,
41
- }) => {
42
- const form = Form.useFormInstance();
43
-
44
- // 获取表单值,优先使用 initialValues
45
- const getFormValues = () => {
46
- const formValues = form.getFieldsValue();
47
- // 如果表单值为空但有 initialValues,则使用 initialValues
48
- if (Object.keys(formValues).length === 0 && initialValues) {
49
- return initialValues;
50
- }
51
- return formValues;
52
- };
53
-
54
- // 获取传给组件的属性
55
- const getComponentProps = (option) => {
56
- return typeof option.componentProps === "function"
57
- ? option.componentProps(getFormValues())
58
- : (option.componentProps || {});
59
- };
60
-
61
- // 获取 Form.List 独有的属性
62
- const getFormListUniqueProps = (option) => {
63
- const defaultProps = {
64
- showAddButton: true,
65
- showRemoveButton: true,
66
- addButtonText: "添加",
67
- removeButtonText: "删除",
68
- options: [],
69
- addDefaultValue: {},
70
- addInsertIndex: undefined,
71
- };
72
-
73
- if (typeof option.formListUniqueProps === "function") {
74
- return {
75
- ...defaultProps,
76
- ...option.formListUniqueProps(getFormValues()),
77
- };
78
- }
79
-
80
- return {
81
- ...defaultProps,
82
- ...(option.formListUniqueProps || {}),
83
- };
84
- };
85
-
86
- // 获取传给formItem的属性
87
- const getFormItemProps = (option) => {
88
- const formItemProps = typeof option.formItemProps === "function"
89
- ? option.formItemProps(getFormValues())
90
- : (option.formItemProps || {});
91
-
92
- // 为日期组件添加特殊处理
93
- if ([
94
- FORM_ITEM_RENDER_ENUM.DATE,
95
- FORM_ITEM_RENDER_ENUM.DATE_MONTH,
96
- FORM_ITEM_RENDER_ENUM.DATE_YEAR,
97
- FORM_ITEM_RENDER_ENUM.DATETIME,
98
- ].includes(option.render)) {
99
- formItemProps.getValueFromEvent = (_, dateString) => dateString;
100
- formItemProps.getValueProps = value => ({ value: value ? dayjs(value) : undefined });
101
- }
102
-
103
- // 为日期周组件添加特殊处理
104
- if ([
105
- FORM_ITEM_RENDER_ENUM.DATE_WEEK,
106
- ].includes(option.render)) {
107
- formItemProps.getValueFromEvent = (_, dateString) => dateString;
108
- formItemProps.getValueProps = value => ({ value: value ? dayjs(value, "YYYY-wo") : undefined });
109
- }
110
-
111
- // 为日期范围组件添加特殊处理
112
- if ([
113
- FORM_ITEM_RENDER_ENUM.DATE_RANGE,
114
- FORM_ITEM_RENDER_ENUM.DATETIME_RANGE,
115
- ].includes(option.render)) {
116
- formItemProps.getValueFromEvent = (_, dateString) => dateString;
117
- formItemProps.getValueProps = value => ({ value: Array.isArray(value) ? value.map(v => v ? dayjs(v) : undefined) : undefined });
118
- }
119
-
120
- return formItemProps;
121
- };
122
-
123
- // 获取items里的value和label字段key
124
- const getItemsFieldKey = (option) => {
125
- return {
126
- valueKey: option?.itemsField?.valueKey || "bianma",
127
- labelKey: option?.itemsField?.labelKey || "name",
128
- };
129
- };
130
-
131
- // 获取 required
132
- const getRequired = (required) => {
133
- // 支持动态计算 required
134
- return typeof required === "function"
135
- ? required(getFormValues())
136
- : (required ?? true);
137
- };
138
-
139
- // 获取验证规则
140
- const getRules = (option) => {
141
- if (option.render === FORM_ITEM_RENDER_ENUM.DIVIDER)
142
- return [];
143
-
144
- const rules = [];
145
-
146
- /** @type {string | Function} */
147
- const render = option.render || FORM_ITEM_RENDER_ENUM.INPUT;
148
- switch (render) {
149
- case FORM_ITEM_RENDER_ENUM.INPUT:
150
- option.useConstraints !== false && rules.push({ max: 50, message: "最多输入50字符" });
151
- break;
152
-
153
- case FORM_ITEM_RENDER_ENUM.TEXTAREA:
154
- option.useConstraints !== false && rules.push({ max: 500, message: "最多输入500字符" });
155
- break;
156
-
157
- case FORM_ITEM_RENDER_ENUM.INPUT_NUMBER:
158
- case FORM_ITEM_RENDER_ENUM.NUMBER:
159
- option.useConstraints !== false && rules.push({ pattern: /^(\d+)(\.\d{1,2})?$/, message: "请输入正确的数字,最多保留两位小数" });
160
- option.useConstraints !== false && rules.push({
161
- validator: (_, value) => {
162
- if (value && Math.abs(Number.parseFloat(value)) > 999999999) {
163
- return Promise.reject("输入数值超出安全范围");
164
- }
165
- return Promise.resolve();
166
- },
167
- });
168
- break;
169
- }
170
-
171
- if (!useAutoGenerateRequired)
172
- return option.rules ? (Array.isArray(option.rules) ? [...option.rules, ...rules] : [option.rules, ...rules]) : rules;
173
-
174
- if (getRequired(option.required)) {
175
- const isBlurTrigger = !option.render || [
176
- FORM_ITEM_RENDER_ENUM.INPUT,
177
- FORM_ITEM_RENDER_ENUM.TEXTAREA,
178
- FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
179
- FORM_ITEM_RENDER_ENUM.NUMBER,
180
- ].includes(option.render);
181
-
182
- rules.push({ required: true, message: `${isBlurTrigger ? "请输入" : "请选择"}${option.label}` });
183
-
184
- if (option.rules) {
185
- if (Array.isArray(option.rules)) {
186
- rules.push(...option.rules);
187
- }
188
- else {
189
- rules.push(option.rules);
190
- }
191
- }
192
-
193
- return rules;
194
- }
195
-
196
- return option.rules ? (Array.isArray(option.rules) ? [...option.rules, ...rules] : [option.rules, ...rules]) : rules;
197
- };
198
-
199
- // 获取key
200
- const getKey = (option) => {
201
- return option.key || option.name;
202
- };
203
-
204
- // 使用 style 控制显示/隐藏
205
- const getStyle = (index) => {
206
- return collapse && index >= 3 ? { display: "none" } : undefined;
207
- };
208
-
209
- // 列数
210
- const getCol = (option) => {
211
- const itemSpan = option.render === FORM_ITEM_RENDER_ENUM.DIVIDER ? 24 : (option.span ?? span);
212
- const itemLabelCol = option.labelCol ?? (itemSpan === 24 ? { span: labelCol.span / 2 } : labelCol);
213
- const itemWrapperCol = option.wrapperCol ?? { span: 24 - itemLabelCol.span };
214
- return { span: itemSpan, labelCol: itemLabelCol, wrapperCol: itemWrapperCol };
215
- };
216
-
217
- // 获取 hidden
218
- const getHidden = (hidden) => {
219
- // 支持动态计算 hidden
220
- return typeof hidden === "function"
221
- ? hidden(getFormValues())
222
- : (hidden ?? false);
223
- };
224
-
225
- // 获取 listOptions
226
- const getListOptions = (listOptions, field, fieldIndex, add, remove, move) => {
227
- return typeof listOptions === "function"
228
- ? listOptions(field, fieldIndex, { field, fieldIndex, add, remove, move })
229
- : (listOptions ?? []);
230
- };
231
-
232
- // 渲染表单控件
233
- const renderFormControl = (option) => {
234
- const componentProps = getComponentProps(option);
235
- const itemsFieldKey = getItemsFieldKey(option);
236
- /** @type {string | Function} */
237
- const render = option.render || FORM_ITEM_RENDER_ENUM.INPUT;
238
- const placeholder = option.placeholder || `请${[FORM_ITEM_RENDER_ENUM.INPUT, FORM_ITEM_RENDER_ENUM.TEXTAREA, FORM_ITEM_RENDER_ENUM.INPUT_NUMBER, FORM_ITEM_RENDER_ENUM.NUMBER].includes(render) ? "输入" : "选择"}${option.label}`;
239
-
240
- switch (render) {
241
- case FORM_ITEM_RENDER_ENUM.INPUT:
242
- return <Input placeholder={placeholder} maxLength={50} {...componentProps} />;
243
-
244
- case FORM_ITEM_RENDER_ENUM.TEXTAREA:
245
- return <TextArea placeholder={placeholder} maxLength={500} showCount={true} rows={3} {...componentProps} />;
246
-
247
- case FORM_ITEM_RENDER_ENUM.INPUT_NUMBER:
248
- case FORM_ITEM_RENDER_ENUM.NUMBER:
249
- return <InputNumber placeholder={placeholder} style={{ width: "100%" }} {...componentProps} />;
250
-
251
- case FORM_ITEM_RENDER_ENUM.SELECT:
252
- return (
253
- <Select placeholder={placeholder} showSearch allowClear optionFilterProp="children" {...componentProps}>
254
- {(option.items || []).map((item) => {
255
- const value = item[itemsFieldKey.valueKey];
256
- const label = item[typeof itemsFieldKey.labelKey === "function" ? itemsFieldKey.labelKey(item) : itemsFieldKey.labelKey];
257
- return (
258
- <Select.Option key={value} value={value}>
259
- {label}
260
- </Select.Option>
261
- );
262
- })}
263
- </Select>
264
- );
265
-
266
- case FORM_ITEM_RENDER_ENUM.RADIO:
267
- return (
268
- <Radio.Group {...componentProps}>
269
- {(option.items || []).map((item) => {
270
- const value = item[itemsFieldKey.valueKey];
271
- const label = item[typeof itemsFieldKey.labelKey === "function" ? itemsFieldKey.labelKey(item) : itemsFieldKey.labelKey];
272
- return (
273
- <Radio key={value} value={value}>
274
- {label}
275
- </Radio>
276
- );
277
- })}
278
- </Radio.Group>
279
- );
280
-
281
- case FORM_ITEM_RENDER_ENUM.CHECKBOX:
282
- return (
283
- <Checkbox.Group {...componentProps}>
284
- {(option.items || []).map((item) => {
285
- const value = item[itemsFieldKey.valueKey];
286
- const label = item[typeof itemsFieldKey.labelKey === "function" ? itemsFieldKey.labelKey(item) : itemsFieldKey.labelKey];
287
- return (
288
- <Checkbox key={value} value={value}>
289
- {label}
290
- </Checkbox>
291
- );
292
- })}
293
- </Checkbox.Group>
294
- );
295
-
296
- case FORM_ITEM_RENDER_ENUM.DATE:
297
- return <DatePicker placeholder={placeholder} format="YYYY-MM-DD" style={{ width: "100%" }} {...componentProps} />;
298
-
299
- case FORM_ITEM_RENDER_ENUM.DATE_MONTH:
300
- return (
301
- <DatePicker
302
- picker="month"
303
- placeholder={placeholder}
304
- format="YYYY-MM"
305
- style={{ width: "100%" }}
306
- {...componentProps}
307
- />
308
- );
309
-
310
- case FORM_ITEM_RENDER_ENUM.DATE_YEAR:
311
- return (
312
- <DatePicker
313
- picker="year"
314
- placeholder={placeholder}
315
- format="YYYY"
316
- style={{ width: "100%" }}
317
- {...componentProps}
318
- />
319
- );
320
-
321
- case FORM_ITEM_RENDER_ENUM.DATE_WEEK:
322
- return (
323
- <DatePicker
324
- picker="week"
325
- placeholder={placeholder}
326
- format="YYYY-wo"
327
- style={{ width: "100%" }}
328
- {...componentProps}
329
- />
330
- );
331
-
332
- case FORM_ITEM_RENDER_ENUM.DATE_RANGE:
333
- return (
334
- <RangePicker
335
- placeholder={[`请选择开始${option.label}`, `请选择结束${option.label}`]}
336
- format="YYYY-MM-DD"
337
- style={{ width: "100%" }}
338
- {...componentProps}
339
- />
340
- );
341
-
342
- case FORM_ITEM_RENDER_ENUM.DATETIME:
343
- return (
344
- <DatePicker
345
- showTime
346
- placeholder={placeholder}
347
- format="YYYY-MM-DD HH:mm:ss"
348
- style={{ width: "100%" }}
349
- {...componentProps}
350
- />
351
- );
352
-
353
- case FORM_ITEM_RENDER_ENUM.DATETIME_RANGE:
354
- return (
355
- <RangePicker
356
- showTime
357
- placeholder={[`请选择开始${option.label}`, `请选择结束${option.label}`]}
358
- format="YYYY-MM-DD HH:mm:ss"
359
- style={{ width: "100%" }}
360
- {...componentProps}
361
- />
362
- );
363
-
364
- case FORM_ITEM_RENDER_ENUM.DIVIDER:
365
- return null;
366
-
367
- default:
368
- return render;
369
- }
370
- };
371
-
372
- // 渲染 label(带提示)
373
- const renderLabel = (option) => {
374
- if (!option.tip)
375
- return option.label;
376
-
377
- return (
378
- <>
379
- {option.label}
380
- <Tooltip title={option.tip}>
381
- <InfoCircleOutlined style={{ marginLeft: 4, fontSize: 12 }} />
382
- </Tooltip>
383
- </>
384
- );
385
- };
386
-
387
- // 渲染普通表单项
388
- const renderFormItem = ({ option, style, col, index }) => {
389
- if (getHidden(option.hidden))
390
- return null;
391
-
392
- return (
393
- <Col key={getKey(option) || index} span={col.span} style={style}>
394
- <Form.Item
395
- name={option.name}
396
- label={renderLabel(option)}
397
- rules={getRules(option)}
398
- labelCol={col.labelCol}
399
- wrapperCol={col.wrapperCol}
400
- preserve={false}
401
- {...getFormItemProps(option)}
402
- >
403
- {renderFormControl(option)}
404
- </Form.Item>
405
- </Col>
406
- );
407
- };
408
-
409
- // 渲染特殊类型的表单项
410
- const renderOtherTypeItem = ({ option, style, col, index }) => {
411
- const componentProps = getComponentProps(option);
412
-
413
- // 如果是 customizeRender 类型,完全交给外部控制渲染
414
- if (option.customizeRender) {
415
- return (
416
- <Col key={getKey(option) || index} span={col.span} style={style}>
417
- {option.render}
418
- </Col>
419
- );
420
- }
421
-
422
- // 如果是 onlyForLabel 类型,不渲染任何UI,只在表单中保存数据
423
- if (option.onlyForLabel) {
424
- return (
425
- <Form.Item
426
- key={getKey(option) || index}
427
- name={option.name}
428
- noStyle
429
- preserve={false}
430
- >
431
- <input type="hidden" />
432
- </Form.Item>
433
- );
434
- }
435
-
436
- // 如果是分割线
437
- if (option.render === FORM_ITEM_RENDER_ENUM.DIVIDER) {
438
- return (
439
- <Col key={getKey(option) || index} span={col.span} style={style}>
440
- <Divider orientation="left" {...componentProps}>{option.label}</Divider>
441
- </Col>
442
- );
443
- }
444
-
445
- return null;
446
- };
447
-
448
- // 渲染 Form.List
449
- const renderFormList = (option, index, col, style) => {
450
- const formListUniqueProps = getFormListUniqueProps(option);
451
- const componentProps = getComponentProps(option);
452
-
453
- return (
454
- <Col key={getKey(option) || index} span={col.span} style={style}>
455
- <Form.List name={option.name} {...componentProps}>
456
- {(fields, { add, remove, move }) => (
457
- <>
458
- {fields.map((field, fieldIndex) => {
459
- const listOptions = getListOptions(option.formListUniqueProps.options, field, fieldIndex, add, remove, move);
460
- return (
461
- <Row gutter={gutter} key={field.key}>
462
- {listOptions.map((listOption, listIndex) => {
463
- const col = getCol(listOption);
464
-
465
- const params = {
466
- option: listOption,
467
- style,
468
- col,
469
- index: `${fieldIndex}_${listIndex}`,
470
- };
471
- const otherTypeItem = renderOtherTypeItem(params);
472
- if (otherTypeItem)
473
- return otherTypeItem;
474
-
475
- // 如果是最后一个表单项,则在其后添加操作按钮
476
- // 这样可以确保每个表单项组都有添加/删除按钮
477
- if (listIndex === listOptions.length - 1) {
478
- return (
479
- <Col key={getKey(listOption) || listIndex} span={col.span} style={style}>
480
- <Form.Item
481
- label={renderLabel(listOption)}
482
- labelCol={col.labelCol}
483
- wrapperCol={col.wrapperCol}
484
- preserve={false}
485
- required={getRequired(listOption.required)}
486
- {...getFormItemProps(listOption)}
487
- >
488
- <div style={{ display: "flex", gap: 10, alignItems: "center", justifyContent: "space-between" }}>
489
- <div style={{ flex: 1 }}>
490
- <Form.Item
491
- noStyle
492
- rules={getRules(listOption)}
493
- name={listOption.name}
494
- >
495
- {renderFormControl(listOption)}
496
- </Form.Item>
497
- </div>
498
- {
499
- // 只有当不是第一行时才显示删除按钮
500
- fieldIndex >= 1
501
- ? (
502
- formListUniqueProps.showRemoveButton
503
- && (
504
- <Button
505
- type="primary"
506
- danger
507
- onClick={() => remove(field.name)}
508
- >
509
- {formListUniqueProps.removeButtonText}
510
- </Button>
511
- )
512
- )
513
- : (
514
- // 第一行显示添加按钮
515
- formListUniqueProps.showAddButton
516
- && (
517
- <Button
518
- type="primary"
519
- onClick={() => add(formListUniqueProps.addDefaultValue, formListUniqueProps.addInsertIndex)}
520
- >
521
- {formListUniqueProps.addButtonText}
522
- </Button>
523
- )
524
- )
525
- }
526
- </div>
527
- </Form.Item>
528
- </Col>
529
- );
530
- }
531
-
532
- return renderFormItem(params);
533
- })}
534
- </Row>
535
- );
536
- })}
537
- </>
538
- )}
539
- </Form.List>
540
- </Col>
541
- );
542
- };
543
-
544
- // 渲染需要动态更新的表单项
545
- const renderDynamicFormItem = (option, index, style, col) => {
546
- return (
547
- <Form.Item
548
- key={getKey(option) || index}
549
- noStyle
550
- preserve={false}
551
- shouldUpdate={option.shouldUpdate ?? option?.componentProps?.shouldUpdate}
552
- dependencies={option.dependencies ?? option?.componentProps?.dependencies}
553
- >
554
- {() => {
555
- return renderFormItem({ option, style, col, index });
556
- }}
557
- </Form.Item>
558
- );
559
- };
560
-
561
- return (
562
- <>
563
- {options.map((option, index) => {
564
- const col = getCol(option);
565
- const style = getStyle(index);
566
-
567
- // 处理特殊类型的表单项
568
- const otherTypeItem = renderOtherTypeItem({ option, style, col, index });
569
- if (otherTypeItem)
570
- return otherTypeItem;
571
-
572
- // 如果是 Form.List
573
- if (option.render === FORM_ITEM_RENDER_ENUM.FORM_LIST) {
574
- return renderFormList(option, index, col, style);
575
- }
576
-
577
- // 如果配置了 shouldUpdate dependencies,使用 Form.Item 的联动机制
578
- if ((option.shouldUpdate ?? option.dependencies) || (option?.componentProps?.shouldUpdate ?? option?.componentProps?.dependencies)) {
579
- return renderDynamicFormItem(option, index, style, col);
580
- }
581
-
582
- // 普通表单项(静态配置)
583
- return renderFormItem({ option, style, col, index });
584
- })}
585
- </>
586
- );
587
- };
588
-
589
- FormItemsRenderer.displayName = "FormItemsRenderer";
590
-
591
- export default FormItemsRenderer;
24
+ */
25
+ const FormItemsRenderer = ({
26
+ options,
27
+ labelCol,
28
+ gutter = 24,
29
+ span = 12,
30
+ collapse = false,
31
+ useAutoGenerateRequired = true,
32
+ initialValues
33
+ }) => {
34
+ const form = Form.useFormInstance();
35
+
36
+ // 获取表单值,优先使用 initialValues
37
+ const getFormValues = () => {
38
+ const formValues = form.getFieldsValue();
39
+ // 如果表单值为空但有 initialValues,则使用 initialValues
40
+ if (Object.keys(formValues).length === 0 && initialValues) {
41
+ return initialValues;
42
+ }
43
+ return formValues;
44
+ };
45
+
46
+ // 获取传给组件的属性
47
+ const getComponentProps = option => {
48
+ return typeof option.componentProps === "function" ? option.componentProps(getFormValues()) : option.componentProps || {};
49
+ };
50
+
51
+ // 获取 Form.List 独有的属性
52
+ const getFormListUniqueProps = option => {
53
+ const defaultProps = {
54
+ showAddButton: true,
55
+ showRemoveButton: true,
56
+ addButtonText: "添加",
57
+ removeButtonText: "删除",
58
+ options: [],
59
+ addDefaultValue: {},
60
+ addInsertIndex: undefined
61
+ };
62
+ if (typeof option.formListUniqueProps === "function") {
63
+ return {
64
+ ...defaultProps,
65
+ ...option.formListUniqueProps(getFormValues())
66
+ };
67
+ }
68
+ return {
69
+ ...defaultProps,
70
+ ...(option.formListUniqueProps || {})
71
+ };
72
+ };
73
+
74
+ // 获取传给formItem的属性
75
+ const getFormItemProps = option => {
76
+ const formItemProps = typeof option.formItemProps === "function" ? option.formItemProps(getFormValues()) : option.formItemProps || {};
77
+
78
+ // 为日期组件添加特殊处理
79
+ if ([FORM_ITEM_RENDER_ENUM.DATE, FORM_ITEM_RENDER_ENUM.DATE_MONTH, FORM_ITEM_RENDER_ENUM.DATE_YEAR, FORM_ITEM_RENDER_ENUM.DATETIME].includes(option.render)) {
80
+ formItemProps.getValueFromEvent = (_, dateString) => dateString;
81
+ formItemProps.getValueProps = value => ({
82
+ value: value ? dayjs(value) : undefined
83
+ });
84
+ }
85
+
86
+ // 为日期周组件添加特殊处理
87
+ if ([FORM_ITEM_RENDER_ENUM.DATE_WEEK].includes(option.render)) {
88
+ formItemProps.getValueFromEvent = (_, dateString) => dateString;
89
+ formItemProps.getValueProps = value => ({
90
+ value: value ? dayjs(value, "YYYY-wo") : undefined
91
+ });
92
+ }
93
+
94
+ // 为日期范围组件添加特殊处理
95
+ if ([FORM_ITEM_RENDER_ENUM.DATE_RANGE, FORM_ITEM_RENDER_ENUM.DATETIME_RANGE].includes(option.render)) {
96
+ formItemProps.getValueFromEvent = (_, dateString) => dateString;
97
+ formItemProps.getValueProps = value => ({
98
+ value: Array.isArray(value) ? value.map(v => v ? dayjs(v) : undefined) : undefined
99
+ });
100
+ }
101
+ return formItemProps;
102
+ };
103
+
104
+ // 获取items里的value和label字段key
105
+ const getItemsFieldKey = option => {
106
+ return {
107
+ valueKey: option?.itemsField?.valueKey || "bianma",
108
+ labelKey: option?.itemsField?.labelKey || "name"
109
+ };
110
+ };
111
+
112
+ // 获取 required
113
+ const getRequired = required => {
114
+ // 支持动态计算 required
115
+ return typeof required === "function" ? required(getFormValues()) : required ?? true;
116
+ };
117
+
118
+ // 获取验证规则
119
+ const getRules = option => {
120
+ if (option.render === FORM_ITEM_RENDER_ENUM.DIVIDER) return [];
121
+ const rules = [];
122
+
123
+ /** @type {string | Function} */
124
+ const render = option.render || FORM_ITEM_RENDER_ENUM.INPUT;
125
+ switch (render) {
126
+ case FORM_ITEM_RENDER_ENUM.INPUT:
127
+ option.useConstraints !== false && rules.push({
128
+ max: 50,
129
+ message: "最多输入50字符"
130
+ });
131
+ break;
132
+ case FORM_ITEM_RENDER_ENUM.TEXTAREA:
133
+ option.useConstraints !== false && rules.push({
134
+ max: 500,
135
+ message: "最多输入500字符"
136
+ });
137
+ break;
138
+ case FORM_ITEM_RENDER_ENUM.INPUT_NUMBER:
139
+ case FORM_ITEM_RENDER_ENUM.NUMBER:
140
+ option.useConstraints !== false && rules.push({
141
+ pattern: /^(\d+)(\.\d{1,2})?$/,
142
+ message: "请输入正确的数字,最多保留两位小数"
143
+ });
144
+ option.useConstraints !== false && rules.push({
145
+ validator: (_, value) => {
146
+ if (value && Math.abs(Number.parseFloat(value)) > 999999999) {
147
+ return Promise.reject("输入数值超出安全范围");
148
+ }
149
+ return Promise.resolve();
150
+ }
151
+ });
152
+ break;
153
+ }
154
+ if (!useAutoGenerateRequired) return option.rules ? Array.isArray(option.rules) ? [...option.rules, ...rules] : [option.rules, ...rules] : rules;
155
+ if (getRequired(option.required)) {
156
+ const isBlurTrigger = !option.render || [FORM_ITEM_RENDER_ENUM.INPUT, FORM_ITEM_RENDER_ENUM.TEXTAREA, FORM_ITEM_RENDER_ENUM.INPUT_NUMBER, FORM_ITEM_RENDER_ENUM.NUMBER].includes(option.render);
157
+ rules.push({
158
+ required: true,
159
+ message: `${isBlurTrigger ? "请输入" : "请选择"}${option.label}`
160
+ });
161
+ if (option.rules) {
162
+ if (Array.isArray(option.rules)) {
163
+ rules.push(...option.rules);
164
+ } else {
165
+ rules.push(option.rules);
166
+ }
167
+ }
168
+ return rules;
169
+ }
170
+ return option.rules ? Array.isArray(option.rules) ? [...option.rules, ...rules] : [option.rules, ...rules] : rules;
171
+ };
172
+
173
+ // 获取key
174
+ const getKey = option => {
175
+ return option.key || option.name;
176
+ };
177
+
178
+ // 使用 style 控制显示/隐藏
179
+ const getStyle = index => {
180
+ return collapse && index >= 3 ? {
181
+ display: "none"
182
+ } : undefined;
183
+ };
184
+
185
+ // 列数
186
+ const getCol = option => {
187
+ const itemSpan = option.render === FORM_ITEM_RENDER_ENUM.DIVIDER ? 24 : option.span ?? span;
188
+ const itemLabelCol = option.labelCol ?? (itemSpan === 24 ? {
189
+ span: labelCol.span / 2
190
+ } : labelCol);
191
+ const itemWrapperCol = option.wrapperCol ?? {
192
+ span: 24 - itemLabelCol.span
193
+ };
194
+ return {
195
+ span: itemSpan,
196
+ labelCol: itemLabelCol,
197
+ wrapperCol: itemWrapperCol
198
+ };
199
+ };
200
+
201
+ // 获取 hidden
202
+ const getHidden = hidden => {
203
+ // 支持动态计算 hidden
204
+ return typeof hidden === "function" ? hidden(getFormValues()) : hidden ?? false;
205
+ };
206
+
207
+ // 获取 listOptions
208
+ const getListOptions = (listOptions, field, fieldIndex, add, remove, move) => {
209
+ return typeof listOptions === "function" ? listOptions(field, fieldIndex, {
210
+ field,
211
+ fieldIndex,
212
+ add,
213
+ remove,
214
+ move
215
+ }) : listOptions ?? [];
216
+ };
217
+
218
+ // 渲染表单控件
219
+ const renderFormControl = option => {
220
+ const componentProps = getComponentProps(option);
221
+ const itemsFieldKey = getItemsFieldKey(option);
222
+ /** @type {string | Function} */
223
+ const render = option.render || FORM_ITEM_RENDER_ENUM.INPUT;
224
+ const placeholder = option.placeholder || `请${[FORM_ITEM_RENDER_ENUM.INPUT, FORM_ITEM_RENDER_ENUM.TEXTAREA, FORM_ITEM_RENDER_ENUM.INPUT_NUMBER, FORM_ITEM_RENDER_ENUM.NUMBER].includes(render) ? "输入" : "选择"}${option.label}`;
225
+ switch (render) {
226
+ case FORM_ITEM_RENDER_ENUM.INPUT:
227
+ return /*#__PURE__*/jsx(Input, {
228
+ placeholder: placeholder,
229
+ maxLength: 50,
230
+ ...componentProps
231
+ });
232
+ case FORM_ITEM_RENDER_ENUM.TEXTAREA:
233
+ return /*#__PURE__*/jsx(TextArea, {
234
+ placeholder: placeholder,
235
+ maxLength: 500,
236
+ showCount: true,
237
+ rows: 3,
238
+ ...componentProps
239
+ });
240
+ case FORM_ITEM_RENDER_ENUM.INPUT_NUMBER:
241
+ case FORM_ITEM_RENDER_ENUM.NUMBER:
242
+ return /*#__PURE__*/jsx(InputNumber, {
243
+ placeholder: placeholder,
244
+ style: {
245
+ width: "100%"
246
+ },
247
+ ...componentProps
248
+ });
249
+ case FORM_ITEM_RENDER_ENUM.SELECT:
250
+ return /*#__PURE__*/jsx(Select, {
251
+ placeholder: placeholder,
252
+ showSearch: true,
253
+ allowClear: true,
254
+ optionFilterProp: "children",
255
+ ...componentProps,
256
+ children: (option.items || []).map(item => {
257
+ const value = item[itemsFieldKey.valueKey];
258
+ const label = item[typeof itemsFieldKey.labelKey === "function" ? itemsFieldKey.labelKey(item) : itemsFieldKey.labelKey];
259
+ return /*#__PURE__*/jsx(Select.Option, {
260
+ value: value,
261
+ children: label
262
+ }, value);
263
+ })
264
+ });
265
+ case FORM_ITEM_RENDER_ENUM.RADIO:
266
+ return /*#__PURE__*/jsx(Radio.Group, {
267
+ ...componentProps,
268
+ children: (option.items || []).map(item => {
269
+ const value = item[itemsFieldKey.valueKey];
270
+ const label = item[typeof itemsFieldKey.labelKey === "function" ? itemsFieldKey.labelKey(item) : itemsFieldKey.labelKey];
271
+ return /*#__PURE__*/jsx(Radio, {
272
+ value: value,
273
+ children: label
274
+ }, value);
275
+ })
276
+ });
277
+ case FORM_ITEM_RENDER_ENUM.CHECKBOX:
278
+ return /*#__PURE__*/jsx(Checkbox.Group, {
279
+ ...componentProps,
280
+ children: (option.items || []).map(item => {
281
+ const value = item[itemsFieldKey.valueKey];
282
+ const label = item[typeof itemsFieldKey.labelKey === "function" ? itemsFieldKey.labelKey(item) : itemsFieldKey.labelKey];
283
+ return /*#__PURE__*/jsx(Checkbox, {
284
+ value: value,
285
+ children: label
286
+ }, value);
287
+ })
288
+ });
289
+ case FORM_ITEM_RENDER_ENUM.DATE:
290
+ return /*#__PURE__*/jsx(DatePicker, {
291
+ placeholder: placeholder,
292
+ format: "YYYY-MM-DD",
293
+ style: {
294
+ width: "100%"
295
+ },
296
+ ...componentProps
297
+ });
298
+ case FORM_ITEM_RENDER_ENUM.DATE_MONTH:
299
+ return /*#__PURE__*/jsx(DatePicker, {
300
+ picker: "month",
301
+ placeholder: placeholder,
302
+ format: "YYYY-MM",
303
+ style: {
304
+ width: "100%"
305
+ },
306
+ ...componentProps
307
+ });
308
+ case FORM_ITEM_RENDER_ENUM.DATE_YEAR:
309
+ return /*#__PURE__*/jsx(DatePicker, {
310
+ picker: "year",
311
+ placeholder: placeholder,
312
+ format: "YYYY",
313
+ style: {
314
+ width: "100%"
315
+ },
316
+ ...componentProps
317
+ });
318
+ case FORM_ITEM_RENDER_ENUM.DATE_WEEK:
319
+ return /*#__PURE__*/jsx(DatePicker, {
320
+ picker: "week",
321
+ placeholder: placeholder,
322
+ format: "YYYY-wo",
323
+ style: {
324
+ width: "100%"
325
+ },
326
+ ...componentProps
327
+ });
328
+ case FORM_ITEM_RENDER_ENUM.DATE_RANGE:
329
+ return /*#__PURE__*/jsx(RangePicker, {
330
+ placeholder: [`请选择开始${option.label}`, `请选择结束${option.label}`],
331
+ format: "YYYY-MM-DD",
332
+ style: {
333
+ width: "100%"
334
+ },
335
+ ...componentProps
336
+ });
337
+ case FORM_ITEM_RENDER_ENUM.DATETIME:
338
+ return /*#__PURE__*/jsx(DatePicker, {
339
+ showTime: true,
340
+ placeholder: placeholder,
341
+ format: "YYYY-MM-DD HH:mm:ss",
342
+ style: {
343
+ width: "100%"
344
+ },
345
+ ...componentProps
346
+ });
347
+ case FORM_ITEM_RENDER_ENUM.DATETIME_RANGE:
348
+ return /*#__PURE__*/jsx(RangePicker, {
349
+ showTime: true,
350
+ placeholder: [`请选择开始${option.label}`, `请选择结束${option.label}`],
351
+ format: "YYYY-MM-DD HH:mm:ss",
352
+ style: {
353
+ width: "100%"
354
+ },
355
+ ...componentProps
356
+ });
357
+ case FORM_ITEM_RENDER_ENUM.DIVIDER:
358
+ return null;
359
+ default:
360
+ return render;
361
+ }
362
+ };
363
+
364
+ // 渲染 label(带提示)
365
+ const renderLabel = option => {
366
+ if (!option.tip) return option.label;
367
+ return /*#__PURE__*/jsxs(Fragment, {
368
+ children: [option.label, /*#__PURE__*/jsx(Tooltip, {
369
+ title: option.tip,
370
+ children: /*#__PURE__*/jsx(InfoCircleOutlined, {
371
+ style: {
372
+ marginLeft: 4,
373
+ fontSize: 12
374
+ }
375
+ })
376
+ })]
377
+ });
378
+ };
379
+
380
+ // 渲染普通表单项
381
+ const renderFormItem = ({
382
+ option,
383
+ style,
384
+ col,
385
+ index
386
+ }) => {
387
+ if (getHidden(option.hidden)) return null;
388
+ return /*#__PURE__*/jsx(Col, {
389
+ span: col.span,
390
+ style: style,
391
+ children: /*#__PURE__*/jsx(Form.Item, {
392
+ name: option.name,
393
+ label: renderLabel(option),
394
+ rules: getRules(option),
395
+ labelCol: col.labelCol,
396
+ wrapperCol: col.wrapperCol,
397
+ preserve: false,
398
+ ...getFormItemProps(option),
399
+ children: renderFormControl(option)
400
+ })
401
+ }, getKey(option) || index);
402
+ };
403
+
404
+ // 渲染特殊类型的表单项
405
+ const renderOtherTypeItem = ({
406
+ option,
407
+ style,
408
+ col,
409
+ index
410
+ }) => {
411
+ const componentProps = getComponentProps(option);
412
+
413
+ // 如果是 customizeRender 类型,完全交给外部控制渲染
414
+ if (option.customizeRender) {
415
+ return /*#__PURE__*/jsx(Col, {
416
+ span: col.span,
417
+ style: style,
418
+ children: option.render
419
+ }, getKey(option) || index);
420
+ }
421
+
422
+ // 如果是 onlyForLabel 类型,不渲染任何UI,只在表单中保存数据
423
+ if (option.onlyForLabel) {
424
+ return /*#__PURE__*/jsx(Form.Item, {
425
+ name: option.name,
426
+ noStyle: true,
427
+ preserve: false,
428
+ children: /*#__PURE__*/jsx("input", {
429
+ type: "hidden"
430
+ })
431
+ }, getKey(option) || index);
432
+ }
433
+
434
+ // 如果是分割线
435
+ if (option.render === FORM_ITEM_RENDER_ENUM.DIVIDER) {
436
+ return /*#__PURE__*/jsx(Col, {
437
+ span: col.span,
438
+ style: style,
439
+ children: /*#__PURE__*/jsx(Divider, {
440
+ orientation: "left",
441
+ ...componentProps,
442
+ children: option.label
443
+ })
444
+ }, getKey(option) || index);
445
+ }
446
+ return null;
447
+ };
448
+
449
+ // 渲染 Form.List
450
+ const renderFormList = (option, index, col, style) => {
451
+ const formListUniqueProps = getFormListUniqueProps(option);
452
+ const componentProps = getComponentProps(option);
453
+ return /*#__PURE__*/jsx(Col, {
454
+ span: col.span,
455
+ style: style,
456
+ children: /*#__PURE__*/jsx(Form.List, {
457
+ name: option.name,
458
+ ...componentProps,
459
+ children: (fields, {
460
+ add,
461
+ remove,
462
+ move
463
+ }) => /*#__PURE__*/jsx(Fragment, {
464
+ children: fields.map((field, fieldIndex) => {
465
+ const listOptions = getListOptions(option.formListUniqueProps.options, field, fieldIndex, add, remove, move);
466
+ return /*#__PURE__*/jsx(Row, {
467
+ gutter: gutter,
468
+ children: listOptions.map((listOption, listIndex) => {
469
+ const col = getCol(listOption);
470
+ const params = {
471
+ option: listOption,
472
+ style,
473
+ col,
474
+ index: `${fieldIndex}_${listIndex}`
475
+ };
476
+ const otherTypeItem = renderOtherTypeItem(params);
477
+ if (otherTypeItem) return otherTypeItem;
478
+
479
+ // 如果是最后一个表单项,则在其后添加操作按钮
480
+ // 这样可以确保每个表单项组都有添加/删除按钮
481
+ if (listIndex === listOptions.length - 1) {
482
+ return /*#__PURE__*/jsx(Col, {
483
+ span: col.span,
484
+ style: style,
485
+ children: /*#__PURE__*/jsx(Form.Item, {
486
+ label: renderLabel(listOption),
487
+ labelCol: col.labelCol,
488
+ wrapperCol: col.wrapperCol,
489
+ preserve: false,
490
+ required: getRequired(listOption.required),
491
+ ...getFormItemProps(listOption),
492
+ children: /*#__PURE__*/jsxs("div", {
493
+ style: {
494
+ display: "flex",
495
+ gap: 10,
496
+ alignItems: "center",
497
+ justifyContent: "space-between"
498
+ },
499
+ children: [/*#__PURE__*/jsx("div", {
500
+ style: {
501
+ flex: 1
502
+ },
503
+ children: /*#__PURE__*/jsx(Form.Item, {
504
+ noStyle: true,
505
+ rules: getRules(listOption),
506
+ name: listOption.name,
507
+ children: renderFormControl(listOption)
508
+ })
509
+ }),
510
+ // 只有当不是第一行时才显示删除按钮
511
+ fieldIndex >= 1 ? formListUniqueProps.showRemoveButton && /*#__PURE__*/jsx(Button, {
512
+ type: "primary",
513
+ danger: true,
514
+ onClick: () => remove(field.name),
515
+ children: formListUniqueProps.removeButtonText
516
+ }) :
517
+ // 第一行显示添加按钮
518
+ formListUniqueProps.showAddButton && /*#__PURE__*/jsx(Button, {
519
+ type: "primary",
520
+ onClick: () => add(formListUniqueProps.addDefaultValue, formListUniqueProps.addInsertIndex),
521
+ children: formListUniqueProps.addButtonText
522
+ })]
523
+ })
524
+ })
525
+ }, getKey(listOption) || listIndex);
526
+ }
527
+ return renderFormItem(params);
528
+ })
529
+ }, field.key);
530
+ })
531
+ })
532
+ })
533
+ }, getKey(option) || index);
534
+ };
535
+
536
+ // 渲染需要动态更新的表单项
537
+ const renderDynamicFormItem = (option, index, style, col) => {
538
+ return /*#__PURE__*/jsx(Form.Item, {
539
+ noStyle: true,
540
+ preserve: false,
541
+ shouldUpdate: option.shouldUpdate ?? option?.componentProps?.shouldUpdate,
542
+ dependencies: option.dependencies ?? option?.componentProps?.dependencies,
543
+ children: () => {
544
+ return renderFormItem({
545
+ option,
546
+ style,
547
+ col,
548
+ index
549
+ });
550
+ }
551
+ }, getKey(option) || index);
552
+ };
553
+ return /*#__PURE__*/jsx(Fragment, {
554
+ children: options.map((option, index) => {
555
+ const col = getCol(option);
556
+ const style = getStyle(index);
557
+
558
+ // 处理特殊类型的表单项
559
+ const otherTypeItem = renderOtherTypeItem({
560
+ option,
561
+ style,
562
+ col,
563
+ index
564
+ });
565
+ if (otherTypeItem) return otherTypeItem;
566
+
567
+ // 如果是 Form.List
568
+ if (option.render === FORM_ITEM_RENDER_ENUM.FORM_LIST) {
569
+ return renderFormList(option, index, col, style);
570
+ }
571
+
572
+ // 如果配置了 shouldUpdate 或 dependencies,使用 Form.Item 的联动机制
573
+ if ((option.shouldUpdate ?? option.dependencies) || (option?.componentProps?.shouldUpdate ?? option?.componentProps?.dependencies)) {
574
+ return renderDynamicFormItem(option, index, style, col);
575
+ }
576
+
577
+ // 普通表单项(静态配置)
578
+ return renderFormItem({
579
+ option,
580
+ style,
581
+ col,
582
+ index
583
+ });
584
+ })
585
+ });
586
+ };
587
+ FormItemsRenderer.displayName = "FormItemsRenderer";
588
+
589
+ export { FormItemsRenderer as default };