@dckj-npm/dc-material 0.1.377 → 0.1.379

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 (43) hide show
  1. package/build/docs/colorful-button.html +3 -3
  2. package/build/docs/colorful-input.html +3 -3
  3. package/build/docs/custom-form/requirements.html +48 -0
  4. package/build/docs/custom-form.html +48 -0
  5. package/build/docs/index.html +3 -3
  6. package/build/docs/teletext-list.html +3 -3
  7. package/build/docs/umi.80bcbda5.js +1 -0
  8. package/build/docs/{umi.9770df27.css → umi.b31e14a3.css} +1 -1
  9. package/build/docs/~demos/colorful-button-demo.html +3 -3
  10. package/build/docs/~demos/colorful-input-demo.html +3 -3
  11. package/build/docs/~demos/teletext-list-demo-1.html +3 -3
  12. package/build/docs/~demos/teletext-list-demo.html +3 -3
  13. package/build/lowcode/assets-daily.json +13 -13
  14. package/build/lowcode/assets-dev.json +2 -2
  15. package/build/lowcode/assets-prod.json +13 -13
  16. package/build/lowcode/meta.design.js +1 -1
  17. package/build/lowcode/meta.js +1 -1
  18. package/build/lowcode/render/default/view.css +1 -1
  19. package/build/lowcode/render/default/view.js +1 -1
  20. package/build/lowcode/view.css +1 -1
  21. package/build/lowcode/view.js +1 -1
  22. package/dist/BizComps.css +1 -1
  23. package/dist/BizComps.js +2 -2
  24. package/dist/BizComps.js.map +1 -1
  25. package/es/components/custom-form/CUSTOM_FORM_OPERATION_MANUAL.md +284 -0
  26. package/es/components/custom-form/custom-form.d.ts +188 -2
  27. package/es/components/custom-form/custom-form.js +506 -26
  28. package/es/components/custom-form/index.d.ts +1 -1
  29. package/es/components/custom-form/index.scss +1 -1
  30. package/es/components/custom-form/schema.json +1374 -0
  31. package/lib/components/custom-form/CUSTOM_FORM_OPERATION_MANUAL.md +284 -0
  32. package/lib/components/custom-form/custom-form.d.ts +188 -2
  33. package/lib/components/custom-form/custom-form.js +511 -31
  34. package/lib/components/custom-form/index.d.ts +1 -1
  35. package/lib/components/custom-form/index.scss +1 -1
  36. package/lib/components/custom-form/schema.json +1374 -0
  37. package/lowcode/custom-form/meta.ts +1675 -116
  38. package/lowcode_es/custom-form/meta.js +2116 -141
  39. package/lowcode_es/meta.js +1 -1
  40. package/lowcode_lib/custom-form/meta.js +2116 -141
  41. package/lowcode_lib/meta.js +1 -1
  42. package/package.json +3 -3
  43. package/build/docs/umi.6f6bf535.js +0 -1
@@ -5,13 +5,53 @@ import _NumberPicker from "@alifd/next/es/number-picker";
5
5
  import _DatePicker from "@alifd/next/es/date-picker";
6
6
  import _Checkbox from "@alifd/next/es/checkbox";
7
7
  import _Radio from "@alifd/next/es/radio";
8
+ import _Button from "@alifd/next/es/button";
8
9
  import _Select from "@alifd/next/es/select";
9
10
  import _Input from "@alifd/next/es/input";
10
11
  import _ResponsiveGrid from "@alifd/next/es/responsive-grid";
11
12
  import _Form from "@alifd/next/es/form";
12
- var _excluded = ["columns", "spacing", "emptyContent", "formItems", "showSubmit", "submitText", "submitValidate", "submitDataSource", "submitButtonProps", "showReset", "resetText", "resetButtonProps", "onSubmit", "onSubmitFailed", "children"];
13
- import React, { useCallback } from 'react';
13
+ var _excluded = ["format", "showTime"],
14
+ _excluded2 = ["columns", "spacing", "emptyContent", "formItems", "initialValues", "computedFields", "fieldLinkage", "submitMapping", "fullWidth", "showSubmit", "submitText", "submitValidate", "submitDataSource", "submitButtonProps", "showReset", "resetText", "resetButtonProps", "operations", "operationAlign", "onSubmit", "onSubmitFailed", "onChange", "onReset", "children", "labelAlign", "labelCol", "size"];
15
+ /**
16
+ * CustomForm v0.5
17
+ * 阶段一新增能力:
18
+ * - onChange(field, value, allValues) 统一字段变化事件,消灭所有 onXxxChange 样板函数
19
+ * - initialValues 属性面板直接配置字段初始值,无需在页面 state 手写
20
+ * - computedFields 声明式模板字符串计算衍生字段,提交时自动合并
21
+ * 阶段二新增能力:
22
+ * - DateTimePicker 日期时间组件,格式化为 YYYY-MM-DD HH:mm
23
+ * - optionsBind 下拉/单选/复选选项支持变量绑定(动态数据源)
24
+ * - fieldLinkage 字段联动:选 A → 自动填 B/C(从数据源查找匹配项)
25
+ * - submitMapping 提交映射:自动组装 itemList 格式,消灭手写 onSubmitClick
26
+ * 阶段三新增能力:
27
+ * - labelAlign 全局标签位置(top/left/inset),对齐 Fusion Form 标准
28
+ * - fullWidth(全局) 全局控制所有表单项组件是否 100% 宽,可被单项覆盖
29
+ * - isPreview 全局预览/只读态(依赖 Fusion Form isPreview 广播)
30
+ * - labelCol / wrapperCol via otherProps 透传,labelAlign=left 时配合使用
31
+ * - onReset 点击重置按钮后触发的回调事件
32
+ */
33
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
14
34
  import "./index.scss";
35
+ // ─── 调试日志工具(阶段一)────────────────────────────────────────────────────
36
+ var DEBUG_PREFIX = '[CustomForm v0.4]';
37
+ var cfLog = function cfLog(tag) {
38
+ var _console;
39
+ // 生产环境可通过 window.__CUSTOM_FORM_DEBUG__ = false 关闭
40
+ if (typeof window !== 'undefined' && window.__CUSTOM_FORM_DEBUG__ === false) return;
41
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
42
+ args[_key - 1] = arguments[_key];
43
+ }
44
+ (_console = console).log.apply(_console, [DEBUG_PREFIX + " [" + tag + "]"].concat(args));
45
+ };
46
+ var cfWarn = function cfWarn(tag) {
47
+ var _console2;
48
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
49
+ args[_key2 - 1] = arguments[_key2];
50
+ }
51
+ (_console2 = console).warn.apply(_console2, [DEBUG_PREFIX + " [" + tag + "]"].concat(args));
52
+ };
53
+ // ─────────────────────────────────────────────────────────────────────────────
54
+
15
55
  var NextFormAny = _Form;
16
56
  var ResponsiveGridAny = _ResponsiveGrid;
17
57
  var ResponsiveGridCell = _ResponsiveGrid.Cell;
@@ -19,17 +59,130 @@ var FormItemAny = _Form.Item;
19
59
  var InputAny = _Input;
20
60
  var TextAreaAny = _Input.TextArea;
21
61
  var SelectAny = _Select;
62
+ var ButtonAny = _Button;
22
63
  var RadioGroupAny = _Radio.Group;
23
64
  var CheckboxGroupAny = _Checkbox.Group;
24
65
  var DatePickerAny = _DatePicker;
25
66
  var NumberPickerAny = _NumberPicker;
26
67
  var UploadAny = _Upload;
68
+
69
+ /**
70
+ * computedFields:计算字段映射表
71
+ * key = 要写入 values 的字段名
72
+ * value = 模板字符串,用 {fieldName} 引用其他字段值
73
+ * 示例:{ "order_desc": "{package_name},{package_price},日期:{banquet_date}" }
74
+ */
75
+
76
+ /**
77
+ * 字段联动规则(阶段二 + 阶段四 UX 简化)
78
+ *
79
+ * 【简单格式(推荐)】— 属性面板 ArraySetter 可视化编辑,每条规则对应一行:
80
+ * { watchField, matchValue?, fillField, fillValue }
81
+ * watchField = 监听哪个字段变化
82
+ * matchValue = 匹配值(留空 = 任何变化都触发)
83
+ * fillField = 要回填的目标字段名
84
+ * fillValue = 回填的具体值
85
+ *
86
+ * 【高级格式(兼容旧配置)】— 支持动态数据源查找 + staticRules 嵌套格式:
87
+ * { watchField, dataSource?, matchKey?, fillFields?, staticRules? }
88
+ */
89
+
90
+ /** 提交映射 item(阶段二) */
91
+
92
+ /** rename 模式:单条字段重命名规则 */
93
+
94
+ /**
95
+ * 操作按钮配置项(阶段四新增)
96
+ * 表单底部可配置多个按钮,每个按钮均可独立设置操作类型与数据源。
97
+ */
98
+
99
+ /**
100
+ * 提交参数配置(阶段五扩展)
101
+ *
102
+ * 三种工作模式(mode):
103
+ * - 'passthrough':直接将 finalValues 作为请求参数,无需任何配置(字段名与 API 参数名相同时使用)。
104
+ * - 'rename':按 fieldRenameMap 规则重命名指定字段,未列出的字段保持原名透传;
105
+ * 适合字段名与 API 参数名不一致的标准 REST 接口。
106
+ * - 'itemList':将字段转换为动态表单格式 [{columnName, columnValue}],
107
+ * 适合 /source/api/dynamicFormTableRecord/ 等动态表单接口。
108
+ *
109
+ * 向后兼容:未设置 mode 时,若 items 不为空则自动识别为 'itemList' 模式,否则等同于 'passthrough'。
110
+ */
111
+
112
+ // ─── 工具:模板字符串渲染 ─────────────────────────────────────────────────────
113
+ /**
114
+ * 将 "{fieldA},{fieldB}" 中的占位符替换为 values 对应的真实值
115
+ */
116
+ var renderTemplate = function renderTemplate(template, values) {
117
+ return template.replace(/\{(\w+)\}/g, function (_, key) {
118
+ var val = values[key];
119
+ return val !== undefined && val !== null ? String(val) : '';
120
+ });
121
+ };
122
+
123
+ /**
124
+ * 将 initialValues 从数组格式标准化为对象格式(向后兼容)。
125
+ * 数组格式由低代码设计器属性面板存储:[{field, valueType, value}, ...]
126
+ * 对象格式为运行时使用:{ fieldName: value, ... }
127
+ */
128
+ var normalizeInitialValues = function normalizeInitialValues(raw) {
129
+ if (!raw) return {};
130
+ if (Array.isArray(raw)) {
131
+ return raw.reduce(function (acc, item) {
132
+ if (item && item.field) {
133
+ acc[item.field] = item.value !== undefined ? item.value : '';
134
+ }
135
+ return acc;
136
+ }, {});
137
+ }
138
+ return raw;
139
+ };
140
+
141
+ /**
142
+ * 将 computedFields 从数组格式标准化为对象格式(向后兼容)。
143
+ * 数组格式:[{targetField, template}, ...]
144
+ * 对象格式:{ targetField: template, ... }
145
+ */
146
+ var normalizeComputedFields = function normalizeComputedFields(raw) {
147
+ if (!raw) return {};
148
+ if (Array.isArray(raw)) {
149
+ return raw.reduce(function (acc, item) {
150
+ if (item && item.targetField) {
151
+ acc[item.targetField] = item.template || '';
152
+ }
153
+ return acc;
154
+ }, {});
155
+ }
156
+ return raw;
157
+ };
158
+
159
+ /**
160
+ * 将 computedFields 的所有模板计算后合并进 values,返回新对象(不修改原值)
161
+ */
162
+ var applyComputedFields = function applyComputedFields(values, computedFields) {
163
+ if (!computedFields || Object.keys(computedFields).length === 0) return values;
164
+ var result = _extends({}, values);
165
+ Object.keys(computedFields).forEach(function (key) {
166
+ result[key] = renderTemplate(computedFields[key], result);
167
+ });
168
+ cfLog('computedFields', '计算字段结果', result);
169
+ return result;
170
+ };
171
+ // ─────────────────────────────────────────────────────────────────────────────
172
+
27
173
  var runSubmitDataSource = function runSubmitDataSource(submitDataSource, values, errors, field) {
28
174
  if (!submitDataSource) return;
29
175
  if (typeof submitDataSource === 'function') {
30
176
  submitDataSource(values, errors, field);
31
177
  return;
32
178
  }
179
+ // lowcode-engine 数据源对象(this.dataSourceMap.xxx)使用 .load(params) 触发请求。
180
+ // params 会与数据源静态 options.params 合并覆盖;
181
+ // 若 submitMapping 已配置,values 中包含 itemList,可直接覆盖接口默认参数。
182
+ if (typeof (submitDataSource === null || submitDataSource === void 0 ? void 0 : submitDataSource.load) === 'function') {
183
+ submitDataSource.load(values);
184
+ return;
185
+ }
33
186
  if (typeof (submitDataSource === null || submitDataSource === void 0 ? void 0 : submitDataSource.run) === 'function') {
34
187
  submitDataSource.run(values, errors, field);
35
188
  return;
@@ -47,8 +200,10 @@ var runSubmitDataSource = function runSubmitDataSource(submitDataSource, values,
47
200
  }
48
201
  };
49
202
  var renderFormItemComponent = function renderFormItemComponent(item) {
203
+ var _item$optionsBind;
50
204
  var componentProps = item.componentProps || {};
51
- var dataSource = componentProps.dataSource || item.options || [];
205
+ // optionsBind(动态绑定)优先级 > componentProps.dataSource > options(静态)
206
+ var dataSource = (_item$optionsBind = item.optionsBind) !== null && _item$optionsBind !== void 0 && _item$optionsBind.length ? item.optionsBind : componentProps.dataSource || item.options || [];
52
207
  switch (item.componentType) {
53
208
  case 'TextArea':
54
209
  return /*#__PURE__*/React.createElement(TextAreaAny, componentProps);
@@ -67,7 +222,29 @@ var renderFormItemComponent = function renderFormItemComponent(item) {
67
222
  case 'NumberPicker':
68
223
  return /*#__PURE__*/React.createElement(NumberPickerAny, componentProps);
69
224
  case 'DatePicker':
70
- return /*#__PURE__*/React.createElement(DatePickerAny, componentProps);
225
+ return /*#__PURE__*/React.createElement(DatePickerAny, _extends({
226
+ format: "YYYY-MM-DD"
227
+ }, componentProps));
228
+ case 'DateTimePicker':
229
+ {
230
+ // 将 format / showTime 从 componentProps 中单独析构,防止 spread 覆盖我们的计算值
231
+ var cpFormat = componentProps.format,
232
+ _ignoredShowTime = componentProps.showTime,
233
+ restCp = _objectWithoutPropertiesLoose(componentProps, _excluded);
234
+ var dtFmt = cpFormat || 'YYYY-MM-DD HH:mm:ss';
235
+ // 提取日期部分(空格前)和时间部分(空格后)
236
+ // @alifd/next DatePicker 会将 format(日期)与 showTime.format(时间)拼接显示
237
+ // 若把含时间的完整格式传给 format,再传 showTime.format,会导致时间重复显示
238
+ var spaceIdx = dtFmt.indexOf(' ');
239
+ var dateFmt = spaceIdx !== -1 ? dtFmt.slice(0, spaceIdx) : dtFmt;
240
+ var timeFmt = spaceIdx !== -1 ? dtFmt.slice(spaceIdx + 1) : 'HH:mm:ss';
241
+ return /*#__PURE__*/React.createElement(DatePickerAny, _extends({}, restCp, {
242
+ format: dateFmt,
243
+ showTime: {
244
+ format: timeFmt
245
+ }
246
+ }));
247
+ }
71
248
  case 'Upload':
72
249
  return /*#__PURE__*/React.createElement(UploadAny, componentProps);
73
250
  case 'Input':
@@ -79,11 +256,16 @@ var CustomForm = function CustomForm(_ref) {
79
256
  var _ref$columns = _ref.columns,
80
257
  columns = _ref$columns === void 0 ? 1 : _ref$columns,
81
258
  _ref$spacing = _ref.spacing,
82
- spacing = _ref$spacing === void 0 ? [0, 16, 16, 0] : _ref$spacing,
259
+ spacing = _ref$spacing === void 0 ? [0, 16] : _ref$spacing,
83
260
  _ref$emptyContent = _ref.emptyContent,
84
261
  emptyContent = _ref$emptyContent === void 0 ? '添加表单项' : _ref$emptyContent,
85
262
  _ref$formItems = _ref.formItems,
86
263
  formItems = _ref$formItems === void 0 ? [] : _ref$formItems,
264
+ initialValues = _ref.initialValues,
265
+ computedFields = _ref.computedFields,
266
+ fieldLinkage = _ref.fieldLinkage,
267
+ submitMapping = _ref.submitMapping,
268
+ globalFullWidth = _ref.fullWidth,
87
269
  _ref$showSubmit = _ref.showSubmit,
88
270
  showSubmit = _ref$showSubmit === void 0 ? true : _ref$showSubmit,
89
271
  _ref$submitText = _ref.submitText,
@@ -97,25 +279,283 @@ var CustomForm = function CustomForm(_ref) {
97
279
  _ref$resetText = _ref.resetText,
98
280
  resetText = _ref$resetText === void 0 ? '重置' : _ref$resetText,
99
281
  resetButtonProps = _ref.resetButtonProps,
282
+ operations = _ref.operations,
283
+ _ref$operationAlign = _ref.operationAlign,
284
+ operationAlign = _ref$operationAlign === void 0 ? 'center' : _ref$operationAlign,
100
285
  onSubmit = _ref.onSubmit,
101
286
  onSubmitFailed = _ref.onSubmitFailed,
287
+ onChange = _ref.onChange,
288
+ onReset = _ref.onReset,
102
289
  children = _ref.children,
103
- otherProps = _objectWithoutPropertiesLoose(_ref, _excluded);
104
- var handleSubmit = useCallback(function (values, errors, field) {
290
+ labelAlign = _ref.labelAlign,
291
+ labelCol = _ref.labelCol,
292
+ globalSize = _ref.size,
293
+ otherProps = _objectWithoutPropertiesLoose(_ref, _excluded2);
294
+ // ─── 直接传递列数,通过 device="desktop" 锁定成桌面模式,防止 ResponsiveGrid 根据视口自动缩列
295
+ // ResponsiveGrid columns 只接受 number/string,不接受 breakpoint 对象
296
+ var fixedColumns = columns;
297
+
298
+ // ─── 兼容旧版 4-value spacing → 标准化为 [rowGap, colGap] ─────────────────
299
+ var normalizedGap = useMemo(function () {
300
+ var _spacing$, _spacing$2;
301
+ if (!Array.isArray(spacing)) return spacing;
302
+ if (spacing.length >= 2) return [spacing[0], spacing[1]];
303
+ return [(_spacing$ = spacing[0]) !== null && _spacing$ !== void 0 ? _spacing$ : 0, (_spacing$2 = spacing[0]) !== null && _spacing$2 !== void 0 ? _spacing$2 : 16];
304
+ }, [spacing]);
305
+
306
+ // ─── 标准化 initialValues / computedFields(支持设计器生成的数组格式与运行时对象格式)
307
+ var resolvedInitialValues = useMemo(function () {
308
+ return normalizeInitialValues(initialValues);
309
+ }, [initialValues]);
310
+ var resolvedComputedFields = useMemo(function () {
311
+ return normalizeComputedFields(computedFields);
312
+ }, [computedFields]);
313
+
314
+ // ─── 用 ref 缓存所有字段的当前值,避免 onChange 闭包过期 ──────────────────
315
+ // 初始化时直接合并 resolvedInitialValues,避免 useEffect 延迟问题
316
+ var valuesRef = useRef(resolvedInitialValues ? _extends({}, resolvedInitialValues) : {});
317
+
318
+ // ─── 联动回填触发器:用 state 驱动组件重渲染以更新受控字段 ───────────────
319
+ // 注意:不预填 resolvedInitialValues,初始值通过 defaultValue(非受控)处理
320
+ // 预填会导致所有有初始值的字段变为受控组件,用户改值后因 linkageValues 未更新而回显旧值
321
+ var _useState = useState({}),
322
+ linkageValues = _useState[0],
323
+ setLinkageValues = _useState[1];
324
+
325
+ // ─── 统一 onChange 处理器(阶段一 + 阶段二核心)─────────────────────────────
326
+ var handleFieldChange = useCallback(function (field, value) {
327
+ var _extends2;
328
+ // 1. 更新本地 values 缓存
329
+ valuesRef.current = _extends({}, valuesRef.current, (_extends2 = {}, _extends2[field] = value, _extends2));
330
+ cfLog('onChange', "\u5B57\u6BB5 \"" + field + "\" \u53D8\u5316", {
331
+ value: value,
332
+ allValues: valuesRef.current
333
+ });
334
+
335
+ // 2. 字段联动(阶段二):检查是否有对应的联动规则
336
+ var linkagePatched = null;
337
+ if (fieldLinkage && fieldLinkage.length > 0) {
338
+ fieldLinkage.forEach(function (rule) {
339
+ if (rule.watchField !== field) return;
340
+
341
+ // 2a. 静态联动规则(高级格式)
342
+ if (rule.staticRules) {
343
+ var staticFill = rule.staticRules[String(value)];
344
+ if (staticFill) {
345
+ linkagePatched = _extends({}, linkagePatched || {}, staticFill);
346
+ Object.assign(valuesRef.current, staticFill);
347
+ cfLog('linkage', "\u9759\u6001\u8054\u52A8 \"" + field + "\" = " + value + "\uFF0C\u56DE\u586B", staticFill);
348
+ }
349
+ }
350
+
351
+ // 2b. 数据源动态联动(高级格式)
352
+ if (rule.dataSource && rule.fillFields) {
353
+ var matchKey = rule.matchKey || 'value';
354
+ var matched = rule.dataSource.find(function (item) {
355
+ return item[matchKey] === value;
356
+ });
357
+ if (matched) {
358
+ var fill = {};
359
+ Object.entries(rule.fillFields).forEach(function (_ref2) {
360
+ var formField = _ref2[0],
361
+ dsKey = _ref2[1];
362
+ fill[formField] = matched[dsKey];
363
+ });
364
+ linkagePatched = _extends({}, linkagePatched || {}, fill);
365
+ Object.assign(valuesRef.current, fill);
366
+ cfLog('linkage', "\u52A8\u6001\u8054\u52A8 \"" + field + "\" = " + value + "\uFF0C\u56DE\u586B", fill);
367
+ }
368
+ }
369
+
370
+ // 2c. 简单联动格式(新格式,属性面板可视化编辑生成)
371
+ if (rule.fillField !== undefined && rule.fillValue !== undefined) {
372
+ var noConstraint = rule.matchValue === undefined || rule.matchValue === '';
373
+ var _matched = noConstraint || String(value) === String(rule.matchValue);
374
+ if (_matched) {
375
+ var _fill2;
376
+ var _fill = (_fill2 = {}, _fill2[rule.fillField] = rule.fillValue, _fill2);
377
+ linkagePatched = _extends({}, linkagePatched || {}, _fill);
378
+ Object.assign(valuesRef.current, _fill);
379
+ cfLog('linkage', "\u7B80\u5355\u8054\u52A8 \"" + field + "\" = " + value + "\uFF0C\u56DE\u586B", _fill);
380
+ }
381
+ }
382
+ });
383
+ }
384
+ // 有联动回填时,更新 linkageValues 触发重渲染
385
+ if (linkagePatched) {
386
+ setLinkageValues(function (prev) {
387
+ return _extends({}, prev, linkagePatched);
388
+ });
389
+ }
390
+
391
+ // 3. 计算 computedFields,得到包含衍生字段的完整值
392
+ var allValues = applyComputedFields(valuesRef.current, resolvedComputedFields);
393
+
394
+ // 4. 触发外部 onChange 事件(低代码侧可绑定 updateVariable)
395
+ if (onChange) {
396
+ onChange(field, value, allValues);
397
+ }
398
+ }, [resolvedComputedFields, fieldLinkage, onChange]);
399
+
400
+ // ─── 提交处理 ───────────────────────────────────────────────────────────────
401
+ // ─── 对齐方式映射 ───────────────────────────────────────────────────────────
402
+ var alignJustifyMap = {
403
+ left: 'flex-start',
404
+ center: 'center',
405
+ right: 'flex-end'
406
+ };
407
+
408
+ // ─── 有效操作按钮列表 ────────────────────────────────────────────────────────
409
+ // operations prop 存在(哪怕是空数组)时:以 operations 为准,完全忽略旧版 showSubmit/showReset
410
+ // operations prop 为 undefined 时:回退到旧版双按钮模式(向后兼容)
411
+ var effectiveOperations = useMemo(function () {
412
+ if (operations !== undefined) return operations;
413
+ var ops = [];
414
+ if (showReset) ops.push({
415
+ text: resetText || '重置',
416
+ action: 'reset',
417
+ type: 'normal'
418
+ });
419
+ if (showSubmit) ops.push({
420
+ text: submitText || '提交',
421
+ action: 'submit',
422
+ type: 'primary',
423
+ submitValidate: submitValidate
424
+ });
425
+ return ops;
426
+ }, [operations, showSubmit, submitText, submitValidate, showReset, resetText]);
427
+ var handleSubmit = useCallback(function (values, errors, field, buttonDataSource) {
428
+ // 将 Form 收集的 values 与 ref 缓存合并(兼容 initialValues 未经 onChange 的字段)
429
+ var mergedValues = _extends({}, valuesRef.current, values);
430
+ // 追加 computedFields 衍生字段
431
+ var finalValues = applyComputedFields(mergedValues, resolvedComputedFields);
432
+
433
+ // submitMapping:按 mode 构建请求参数(阶段五)
434
+ if (submitMapping) {
435
+ var _submitMapping$mode, _submitMapping$items, _submitMapping$fieldR, _submitMapping$items2;
436
+ // 向后兼容:未设置 mode 时,有 items 数组则默认 itemList,否则 passthrough
437
+ var mode = (_submitMapping$mode = submitMapping.mode) !== null && _submitMapping$mode !== void 0 ? _submitMapping$mode : (_submitMapping$items = submitMapping.items) !== null && _submitMapping$items !== void 0 && _submitMapping$items.length ? 'itemList' : 'passthrough';
438
+ if (mode === 'rename' && (_submitMapping$fieldR = submitMapping.fieldRenameMap) !== null && _submitMapping$fieldR !== void 0 && _submitMapping$fieldR.length) {
439
+ // ── rename:重命名指定字段,其余字段按原名透传
440
+ var renamed = _extends({}, finalValues);
441
+ submitMapping.fieldRenameMap.forEach(function (_ref3) {
442
+ var fromField = _ref3.fromField,
443
+ toField = _ref3.toField;
444
+ if (fromField && toField && fromField in renamed) {
445
+ renamed[toField] = renamed[fromField];
446
+ if (fromField !== toField) delete renamed[fromField];
447
+ }
448
+ });
449
+ finalValues = renamed;
450
+ cfLog('submitMapping', 'rename 模式字段重命名完成', finalValues);
451
+ } else if (mode === 'itemList' && (_submitMapping$items2 = submitMapping.items) !== null && _submitMapping$items2 !== void 0 && _submitMapping$items2.length) {
452
+ var _extends3;
453
+ // ── itemList:转换为动态表单格式 [{columnName, columnValue}]
454
+ var targetField = submitMapping.targetField || 'itemList';
455
+ var itemList = submitMapping.items.map(function (mappingItem) {
456
+ var columnValue;
457
+ if (mappingItem.field) {
458
+ var _finalValues$mappingI;
459
+ columnValue = (_finalValues$mappingI = finalValues[mappingItem.field]) !== null && _finalValues$mappingI !== void 0 ? _finalValues$mappingI : '';
460
+ } else if (mappingItem.template) {
461
+ columnValue = renderTemplate(mappingItem.template, finalValues);
462
+ } else {
463
+ columnValue = '';
464
+ }
465
+ return {
466
+ columnName: mappingItem.columnName,
467
+ columnValue: columnValue
468
+ };
469
+ });
470
+ finalValues = _extends({}, finalValues, (_extends3 = {}, _extends3[targetField] = itemList, _extends3));
471
+ cfLog('submitMapping', "itemList \u6A21\u5F0F\u5DF2\u751F\u6210 " + targetField, itemList);
472
+ }
473
+ // passthrough: finalValues 直接作为请求参数,无需处理
474
+ }
475
+ cfLog('submit', '提交触发', {
476
+ rawValues: values,
477
+ errors: errors,
478
+ finalValues: finalValues
479
+ });
105
480
  if (errors && Object.keys(errors).length) {
481
+ cfWarn('submit', '校验失败', errors);
106
482
  if (onSubmitFailed) {
107
- onSubmitFailed(values, errors, field);
483
+ onSubmitFailed(finalValues, errors, field);
108
484
  }
109
485
  return;
110
486
  }
487
+ cfLog('submit', '校验通过,准备提交', finalValues);
111
488
  if (onSubmit) {
112
- onSubmit(values, errors, field);
489
+ onSubmit(finalValues, errors, field);
113
490
  }
114
- runSubmitDataSource(submitDataSource, values, errors, field);
115
- }, [onSubmit, onSubmitFailed, submitDataSource]);
491
+ // buttonDataSource:单个按钮绑定的数据源(优先);未绑定时回退到组件级 submitDataSource
492
+ var effectiveDS = buttonDataSource !== null && buttonDataSource !== void 0 ? buttonDataSource : submitDataSource;
493
+ runSubmitDataSource(effectiveDS, finalValues, errors, field);
494
+ }, [computedFields, submitMapping, onSubmit, onSubmitFailed, submitDataSource]);
495
+
496
+ // ─── 渲染表单项,绑定统一 onChange ──────────────────────────────────────────
116
497
  var formItemNodes = formItems.map(function (item, index) {
498
+ var _linkageValues$item$f, _item$componentProps;
117
499
  var colSpan = Math.min(columns, Math.max(1, item.columnSpan || 1));
118
- var formItemProps = item.formItemProps || {};
500
+
501
+ // 初始值优先级:item.initialValue > linkageValues[field](含联动回填)> initialValues[field]
502
+ var fieldInitialValue = item.initialValue !== undefined ? item.initialValue : (_linkageValues$item$f = linkageValues[item.field]) !== null && _linkageValues$item$f !== void 0 ? _linkageValues$item$f : resolvedInitialValues === null || resolvedInitialValues === void 0 ? void 0 : resolvedInitialValues[item.field];
503
+
504
+ // 包装组件的 onChange,注入 field 信息
505
+ // fullWidth 优先级:item.fullWidth(单项)> globalFullWidth(全局)> undefined(不限制)
506
+ var effectiveFullWidth = item.fullWidth !== undefined ? item.fullWidth : globalFullWidth;
507
+ // 有效尺寸:单项 > 全局 > undefined(继承 Form 默认)
508
+ var effectiveSize = item.size || globalSize;
509
+ var componentPropsWithChange = _extends({}, item.placeholder ? {
510
+ placeholder: item.placeholder
511
+ } : {}, effectiveSize ? {
512
+ size: effectiveSize
513
+ } : {}, effectiveFullWidth !== undefined ? {
514
+ style: _extends({
515
+ width: effectiveFullWidth ? '100%' : undefined
516
+ }, ((_item$componentProps = item.componentProps) === null || _item$componentProps === void 0 ? void 0 : _item$componentProps.style) || {})
517
+ } : {}, item.componentProps || {}, {
518
+ onChange: function onChange(value) {
519
+ var _item$componentProps2;
520
+ if (typeof ((_item$componentProps2 = item.componentProps) === null || _item$componentProps2 === void 0 ? void 0 : _item$componentProps2.onChange) === 'function') {
521
+ item.componentProps.onChange(value);
522
+ }
523
+ // DatePicker / DateTimePicker 的 onChange 返回 Moment 对象,需格式化为字符串后再存储
524
+ var storedValue = value;
525
+ if (['DatePicker', 'DateTimePicker'].includes(item.componentType) && value && typeof value.format === 'function') {
526
+ var _item$componentProps3;
527
+ var fmt = ((_item$componentProps3 = item.componentProps) === null || _item$componentProps3 === void 0 ? void 0 : _item$componentProps3.format) || (item.componentType === 'DateTimePicker' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD');
528
+ storedValue = value.format(fmt);
529
+ }
530
+ handleFieldChange(item.field, storedValue);
531
+ }
532
+ });
533
+
534
+ // 联动回填时,通过 value(受控)覆盖字段值;否则只用 defaultValue
535
+ var isLinkageControlled = linkageValues[item.field] !== undefined && item.initialValue === undefined;
536
+ if (isLinkageControlled) {
537
+ componentPropsWithChange.value = linkageValues[item.field];
538
+ } else if (fieldInitialValue !== undefined && componentPropsWithChange.value === undefined) {
539
+ componentPropsWithChange.defaultValue = fieldInitialValue;
540
+ cfLog('renderItem', "\u5B57\u6BB5 \"" + item.field + "\" \u6CE8\u5165\u521D\u59CB\u503C", fieldInitialValue);
541
+ }
542
+ if (!item.field) {
543
+ cfWarn('renderItem', "\u7B2C " + (index + 1) + " \u4E2A\u8868\u5355\u9879\u7F3A\u5C11 field \u5B57\u6BB5\uFF0ConChange \u5C06\u65E0\u6CD5\u7ED1\u5B9A");
544
+ }
545
+
546
+ // 合并 Form.Item 属性:通用属性 < 快捷字段 < formItemProps(最高优先级)
547
+ // labelAlign / labelCol 显式传入,确保设计器中不依赖 Form context 也能生效
548
+ var mergedFormItemProps = _extends({}, labelAlign ? {
549
+ labelAlign: labelAlign
550
+ } : {}, labelCol ? {
551
+ labelCol: labelCol
552
+ } : {}, effectiveSize ? {
553
+ size: effectiveSize
554
+ } : {}, item.help ? {
555
+ help: item.help
556
+ } : {}, item.extra ? {
557
+ extra: item.extra
558
+ } : {}, item.formItemProps || {});
119
559
  return /*#__PURE__*/React.createElement(ResponsiveGridCell, {
120
560
  colSpan: colSpan,
121
561
  key: (item.field || index) + "-form-item"
@@ -123,7 +563,9 @@ var CustomForm = function CustomForm(_ref) {
123
563
  label: item.label,
124
564
  name: item.field,
125
565
  required: item.required
126
- }, formItemProps), renderFormItemComponent(item)));
566
+ }, mergedFormItemProps), renderFormItemComponent(_extends({}, item, {
567
+ componentProps: componentPropsWithChange
568
+ }))));
127
569
  });
128
570
  var childrenNodes = React.Children.toArray(children).map(function (child, index) {
129
571
  if (/*#__PURE__*/React.isValidElement(child) && child.type === ResponsiveGridCell) {
@@ -135,24 +577,62 @@ var CustomForm = function CustomForm(_ref) {
135
577
  }, child);
136
578
  });
137
579
  var hasContent = formItemNodes.length > 0 || childrenNodes.length > 0;
580
+
581
+ // initialValues 通过 defaultValue 传入 Form(非受控初始化),避免覆盖联动回填的受控值
582
+ var formInitialProps = {};
583
+ if (initialValues && Object.keys(initialValues).length > 0) {
584
+ formInitialProps.defaultValue = initialValues;
585
+ cfLog('render', '表单携带 initialValues 渲染', initialValues);
586
+ }
138
587
  return /*#__PURE__*/React.createElement(NextFormAny, _extends({
139
588
  className: "custom-form"
140
- }, otherProps), hasContent ? /*#__PURE__*/React.createElement(ResponsiveGridAny, {
589
+ }, labelAlign ? {
590
+ labelAlign: labelAlign
591
+ } : {}, labelCol ? {
592
+ labelCol: labelCol
593
+ } : {}, globalSize ? {
594
+ size: globalSize
595
+ } : {}, formInitialProps, otherProps), hasContent ? /*#__PURE__*/React.createElement(ResponsiveGridAny, {
141
596
  className: "custom-form__grid",
142
- gap: spacing,
143
- columns: columns
597
+ device: "desktop",
598
+ gap: normalizedGap,
599
+ columns: fixedColumns
144
600
  }, formItemNodes, childrenNodes) : /*#__PURE__*/React.createElement("div", {
145
601
  className: "custom-form__empty"
146
- }, emptyContent), showSubmit || showReset ? /*#__PURE__*/React.createElement("div", {
147
- className: "custom-form__actions"
148
- }, showReset ? /*#__PURE__*/React.createElement(NextFormAny.Reset, _extends({
149
- className: "custom-form__reset"
150
- }, resetButtonProps), resetText) : null, showSubmit ? /*#__PURE__*/React.createElement(NextFormAny.Submit, _extends({
151
- className: "custom-form__submit",
152
- type: "primary",
153
- validate: submitValidate,
154
- onClick: handleSubmit
155
- }, submitButtonProps), submitText) : null) : null);
602
+ }, emptyContent), effectiveOperations.length > 0 ? /*#__PURE__*/React.createElement("div", {
603
+ className: "custom-form__actions",
604
+ style: {
605
+ justifyContent: alignJustifyMap[operationAlign] || 'center'
606
+ }
607
+ }, effectiveOperations.map(function (op, opIndex) {
608
+ // ── 重置按钮 ─────────────────────────────────────────────────────
609
+ if (op.action === 'reset') {
610
+ return /*#__PURE__*/React.createElement(NextFormAny.Reset, _extends({
611
+ key: opIndex,
612
+ type: op.type || 'normal',
613
+ onClick: onReset
614
+ }, op.buttonProps || {}), op.text);
615
+ }
616
+ // ── 提交按钮 ─────────────────────────────────────────────────────
617
+ if (op.action === 'submit') {
618
+ // 每个提交按钮独立决定是否校验:per-button > 全局 submitValidate
619
+ var btnValidate = op.submitValidate !== undefined ? op.submitValidate : submitValidate;
620
+ return /*#__PURE__*/React.createElement(NextFormAny.Submit, _extends({
621
+ key: opIndex,
622
+ type: op.type || 'primary',
623
+ validate: btnValidate,
624
+ onClick: function onClick(values, errors, field) {
625
+ return handleSubmit(values, errors, field, op.dataSource);
626
+ }
627
+ }, op.buttonProps || {}), op.text);
628
+ }
629
+ // ── 自定义按钮 ───────────────────────────────────────────────────
630
+ return /*#__PURE__*/React.createElement(ButtonAny, _extends({
631
+ key: opIndex,
632
+ type: op.type || 'normal',
633
+ onClick: op.onClick
634
+ }, op.buttonProps || {}), op.text);
635
+ })) : null);
156
636
  };
157
637
  CustomForm.displayName = 'CustomForm';
158
638
  export default CustomForm;
@@ -2,5 +2,5 @@
2
2
  * 自定义表单
3
3
  */
4
4
  import CustomForm from './custom-form';
5
- export type { CustomFormProps, CustomFormItemSchema, CustomFormItemType } from './custom-form';
5
+ export type { CustomFormProps, CustomFormItemSchema, CustomFormItemType, ComputedFields } from './custom-form';
6
6
  export { CustomForm };
@@ -14,9 +14,9 @@
14
14
 
15
15
  .custom-form__actions {
16
16
  display: flex;
17
- justify-content: center;
18
17
  gap: 12px;
19
18
  padding: 12px 0 0;
19
+ /* 对齐方式通过 style.justifyContent 动态注入,此处不再硬编码 center */
20
20
  }
21
21
 
22
22
  .custom-form__submit,