cloud-web-corejs 1.0.54-dev.688 → 1.0.54-dev.689

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 (24) hide show
  1. package/package.json +1 -1
  2. package/src/components/fileLibrary/index.vue +113 -33
  3. package/src/components/xform/docs//345/220/216/345/217/260/345/255/227/346/256/265/357/274/210/345/212/250/346/200/201/347/224/237/346/210/220/357/274/211JSON /350/257/264/346/230/216.md" +210 -305
  4. package/src/components/xform/docs//346/225/260/346/215/256/350/241/250/346/240/274/345/212/250/346/200/201/345/210/227 JSON /350/257/264/346/230/216.md" +657 -0
  5. package/src/components/xform/form-designer/form-widget/field-widget/fieldMixin.js +80 -14
  6. package/src/components/xform/form-designer/form-widget/field-widget/form-item-wrapper.vue +18 -13
  7. package/src/components/xform/form-designer/form-widget/field-widget/number-widget.vue +11 -8
  8. package/src/components/xform/form-designer/form-widget/field-widget/script-input-widget.vue +143 -0
  9. package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/data-table-editor.vue +87 -25
  10. package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/table-column-dialog.vue +5 -0
  11. package/src/components/xform/form-designer/setting-panel/property-editor/container-table/table-dynamicField-editor.vue +80 -34
  12. package/src/components/xform/form-designer/setting-panel/property-editor/field-script-input/script-input-editor.vue +54 -0
  13. package/src/components/xform/form-designer/setting-panel/propertyRegister.js +1 -0
  14. package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +56 -1
  15. package/src/components/xform/form-render/container-item/data-table-mixin.js +296 -118
  16. package/src/components/xform/form-render/container-item/dynamicFieldMixin.js +25 -37
  17. package/src/components/xform/form-render/container-item/list-h5-item.vue +5 -1
  18. package/src/components/xform/form-render/indexMixin.js +175 -6
  19. package/src/components/xform/lang/en-US.js +1 -0
  20. package/src/components/xform/lang/zh-CN.js +1 -0
  21. package/src/components/xform/utils/dynamicSchemaUtil.js +312 -0
  22. package/src/components/xform/utils/tableCellValidateUtil.js +148 -0
  23. package/src/components/xform/utils/tableColumnHelper.js +21 -1
  24. package/src/components/xform/utils/util.js +126 -0
@@ -0,0 +1,657 @@
1
+ # 数据表格动态列 JSON 说明
2
+
3
+ > **本文只描述数据表格与表头后台字段的差异。** 控件类型、options 配置、选项脚本等**统一维护**在 [`后台字段(动态生成)JSON 说明.md`](./后台字段(动态生成)JSON%20说明.md)。
4
+ >
5
+ > **完整 JSON / Java 综合示例见 §0。** 协议代码:`utils/dynamicSchemaUtil.js` · 运行时:`form-render/container-item/data-table-mixin.js`
6
+
7
+ ---
8
+
9
+ ## 0. 完整综合示例
10
+
11
+ 以下示例覆盖**控件列、展示列、多级表头、操作列**。控件 `options` 字段说明见主文档 **§3**;与表头的差异仅为 **`width` / `formatS` / `children`**,**不写 `colspan`**。列对齐(`align`)由前端默认,**后台无需设置**。
12
+
13
+ ### 0.1 响应 JSON
14
+
15
+ `objx` 为数组,或 `objx.columns` 为数组,二者等价。配置了 `dynamicSchemaConvert` 时,转换脚本返回值须为数组或 `{ columns: [...] }`。
16
+
17
+ ```json
18
+ {
19
+ "type": "success",
20
+ "message": "ok",
21
+ "objx": {
22
+ "columns": [
23
+ {
24
+ "type": "input",
25
+ "keyName": "lineNo",
26
+ "label": "行号",
27
+ "width": 80,
28
+ "required": true
29
+ },
30
+ {
31
+ "type": "number",
32
+ "keyName": "qty",
33
+ "label": "数量",
34
+ "width": 100,
35
+ "options": { "min": 0, "precision": 2 }
36
+ },
37
+ {
38
+ "type": "select",
39
+ "keyName": "unitCode",
40
+ "label": "单位",
41
+ "width": 90,
42
+ "optionItems": [
43
+ { "label": "件", "value": "PCS" },
44
+ { "label": "箱", "value": "BOX" }
45
+ ]
46
+ },
47
+ {
48
+ "type": "date",
49
+ "keyName": "deliveryDate",
50
+ "label": "交货日期",
51
+ "width": 120
52
+ },
53
+ {
54
+ "type": "vabsearch",
55
+ "keyName": "materialCode",
56
+ "label": "物料",
57
+ "width": 160,
58
+ "options": {
59
+ "vabSearchName": "materialName",
60
+ "searchDialogConfig": {
61
+ "formCode": "MATERIAL_LIST",
62
+ "valueSourceField": "materialCode",
63
+ "labelSourceField": "materialName"
64
+ }
65
+ }
66
+ },
67
+ {
68
+ "keyName": "lineAmount",
69
+ "formatS": "n5",
70
+ "label": "行金额",
71
+ "width": 120
72
+ },
73
+ {
74
+ "keyName": "qtyWarn",
75
+ "formatS": "render",
76
+ "label": "库存预警",
77
+ "width": 100,
78
+ "render": "let q = params.row.qty || 0;\nreturn q < (params.row.safeQty || 0) ? '不足' : '正常';"
79
+ },
80
+ {
81
+ "keyName": "_createBy",
82
+ "label": "创建人",
83
+ "width": 100
84
+ },
85
+ {
86
+ "label": "价格信息",
87
+ "children": [
88
+ {
89
+ "type": "number",
90
+ "keyName": "unitPrice",
91
+ "label": "单价",
92
+ "width": 100,
93
+ "options": { "precision": 4 }
94
+ },
95
+ {
96
+ "type": "number",
97
+ "keyName": "taxRate",
98
+ "label": "税率(%)",
99
+ "width": 90,
100
+ "options": { "min": 0, "max": 100, "precision": 2 }
101
+ }
102
+ ]
103
+ },
104
+ {
105
+ "type": "a-link",
106
+ "label": "查看",
107
+ "width": 80,
108
+ "options": {
109
+ "onClick": "let p = this.tableParam;\nthis.getParentTarget().openDetailDialog(p.row);"
110
+ }
111
+ },
112
+ {
113
+ "type": "button",
114
+ "label": "同步",
115
+ "width": 80,
116
+ "options": {
117
+ "type": "primary",
118
+ "onClick": "let p = this.tableParam;\nthis.getParentTarget().syncLine(p.row);"
119
+ }
120
+ },
121
+ {
122
+ "type": "dropdown",
123
+ "label": "操作",
124
+ "width": 90,
125
+ "dropdownItems": [
126
+ {
127
+ "label": "复制",
128
+ "onClick": "let p = this.tableParam;\nthis.getParentTarget().copyRow(p.row);"
129
+ },
130
+ {
131
+ "label": "删除",
132
+ "onClick": "let p = this.tableParam;\nthis.getParentTarget().deleteRow(p.row, p.rowIndex);"
133
+ }
134
+ ]
135
+ }
136
+ ]
137
+ }
138
+ }
139
+ ```
140
+
141
+ ### 0.2 Java 脚本
142
+
143
+ 脚本编码建议:`getDynamicTableColumns`(配置在 data-table「接口动态列 → 脚本编码」)。
144
+ 返回类型:`List<Map<String, Object>>`,平台自动包装为响应 `objx` 数组(与 `objx.columns` 等价)。
145
+
146
+ **与表头脚本的差异:**
147
+
148
+ | 表头 | 数据表格列 |
149
+ |------|-----------|
150
+ | `colspan` | `width` |
151
+ | 无 | 展示列写 `formatS`(如 `n5`、`d1`、`render`) |
152
+ | 无 | 操作列写 `type` + 顶层 `label` + `options.onClick`;dropdown 写顶层 `dropdownItems`(无 `keyName`) |
153
+ | 无 | 多级表头写 `children` |
154
+
155
+ **请求入参(api 模式):** 前端 HTTP 请求体含 `formCode`、`taBm`、`dataId` 及内层 `data`(含 `formData` 与「脚本返回对应的参数值」)。**Java 脚本**通过 `this.getBinding().getVariables()` 读取绑定变量。
156
+
157
+ ```java
158
+ import java.util.ArrayList;
159
+ import java.util.HashMap;
160
+ import java.util.List;
161
+ import java.util.Map;
162
+
163
+ Map<String, Object> formData = this.getBinding().getVariables();
164
+ String billType = ((Map<String, Object>) formData.get("data")).get("billType").toString();
165
+ // 表单主表: ((Map<String, Object>) formData.get("data")).get("formData")
166
+
167
+ List<Map<String, Object>> columns = new ArrayList<>();
168
+
169
+ // ========== 1. 控件列 input ==========
170
+ Map<String, Object> c1 = new HashMap<>();
171
+ c1.put("type", "input");
172
+ c1.put("keyName", "lineNo");
173
+ c1.put("label", "行号");
174
+ c1.put("width", 80);
175
+ c1.put("required", true);
176
+ columns.add(c1);
177
+
178
+ // ========== 2. 控件列 number ==========
179
+ Map<String, Object> c2 = new HashMap<>();
180
+ c2.put("type", "number");
181
+ c2.put("keyName", "qty");
182
+ c2.put("label", "数量");
183
+ c2.put("width", 100);
184
+ Map<String, Object> c2Opt = new HashMap<>();
185
+ c2Opt.put("min", 0);
186
+ c2Opt.put("precision", 2);
187
+ c2.put("options", c2Opt);
188
+ columns.add(c2);
189
+
190
+ // ========== 3. 控件列 select(静态 optionItems) ==========
191
+ Map<String, Object> c3 = new HashMap<>();
192
+ c3.put("type", "select");
193
+ c3.put("keyName", "unitCode");
194
+ c3.put("label", "单位");
195
+ c3.put("width", 90);
196
+ List<Map<String, Object>> c3Items = new ArrayList<>();
197
+ Map<String, Object> c3Item1 = new HashMap<>();
198
+ c3Item1.put("label", "件");
199
+ c3Item1.put("value", "PCS");
200
+ c3Items.add(c3Item1);
201
+ Map<String, Object> c3Item2 = new HashMap<>();
202
+ c3Item2.put("label", "箱");
203
+ c3Item2.put("value", "BOX");
204
+ c3Items.add(c3Item2);
205
+ c3.put("optionItems", c3Items);
206
+ columns.add(c3);
207
+
208
+ // ========== 4. 控件列 date ==========
209
+ Map<String, Object> c4 = new HashMap<>();
210
+ c4.put("type", "date");
211
+ c4.put("keyName", "deliveryDate");
212
+ c4.put("label", "交货日期");
213
+ c4.put("width", 120);
214
+ columns.add(c4);
215
+
216
+ // ========== 5. 控件列 vabsearch ==========
217
+ Map<String, Object> c5 = new HashMap<>();
218
+ c5.put("type", "vabsearch");
219
+ c5.put("keyName", "materialCode");
220
+ c5.put("label", "物料");
221
+ c5.put("width", 160);
222
+ Map<String, Object> c5Opt = new HashMap<>();
223
+ c5Opt.put("vabSearchName", "materialName");
224
+ Map<String, Object> c5Dialog = new HashMap<>();
225
+ c5Dialog.put("formCode", "MATERIAL_LIST");
226
+ c5Dialog.put("valueSourceField", "materialCode");
227
+ c5Dialog.put("labelSourceField", "materialName");
228
+ c5Opt.put("searchDialogConfig", c5Dialog);
229
+ c5.put("options", c5Opt);
230
+ columns.add(c5);
231
+
232
+ // ========== 6. 展示列(formatS = n5) ==========
233
+ Map<String, Object> c6 = new HashMap<>();
234
+ c6.put("keyName", "lineAmount");
235
+ c6.put("formatS", "n5");
236
+ c6.put("label", "行金额");
237
+ c6.put("width", 120);
238
+ columns.add(c6);
239
+
240
+ // ========== 7. 展示列(formatS = render) ==========
241
+ Map<String, Object> c7 = new HashMap<>();
242
+ c7.put("keyName", "qtyWarn");
243
+ c7.put("formatS", "render");
244
+ c7.put("label", "库存预警");
245
+ c7.put("width", 100);
246
+ c7.put("render", "let q = params.row.qty || 0;\nreturn q < (params.row.safeQty || 0) ? '不足' : '正常';");
247
+ columns.add(c7);
248
+
249
+ // ========== 8. 展示列(审计列,只写 keyName) ==========
250
+ Map<String, Object> c8 = new HashMap<>();
251
+ c8.put("keyName", "_createBy");
252
+ c8.put("label", "创建人");
253
+ c8.put("width", 100);
254
+ columns.add(c8);
255
+
256
+ // ========== 9. 多级表头 ==========
257
+ Map<String, Object> c9Group = new HashMap<>();
258
+ c9Group.put("label", "价格信息");
259
+ List<Map<String, Object>> c9Children = new ArrayList<>();
260
+
261
+ Map<String, Object> c9a = new HashMap<>();
262
+ c9a.put("type", "number");
263
+ c9a.put("keyName", "unitPrice");
264
+ c9a.put("label", "单价");
265
+ c9a.put("width", 100);
266
+ Map<String, Object> c9aOpt = new HashMap<>();
267
+ c9aOpt.put("precision", 4);
268
+ c9a.put("options", c9aOpt);
269
+ c9Children.add(c9a);
270
+
271
+ Map<String, Object> c9b = new HashMap<>();
272
+ c9b.put("type", "number");
273
+ c9b.put("keyName", "taxRate");
274
+ c9b.put("label", "税率(%)");
275
+ c9b.put("width", 90);
276
+ Map<String, Object> c9bOpt = new HashMap<>();
277
+ c9bOpt.put("min", 0);
278
+ c9bOpt.put("max", 100);
279
+ c9bOpt.put("precision", 2);
280
+ c9b.put("options", c9bOpt);
281
+ c9Children.add(c9b);
282
+
283
+ c9Group.put("children", c9Children);
284
+ columns.add(c9Group);
285
+
286
+ // ========== 10. 操作列 a-link(无 keyName,默认不可排序/筛选) ==========
287
+ Map<String, Object> c10 = new HashMap<>();
288
+ c10.put("type", "a-link");
289
+ c10.put("label", "查看");
290
+ c10.put("width", 80);
291
+ Map<String, Object> c10Opt = new HashMap<>();
292
+ c10Opt.put("onClick", "let p = this.tableParam;\nthis.getParentTarget().openDetailDialog(p.row);");
293
+ c10.put("options", c10Opt);
294
+ columns.add(c10);
295
+
296
+ // ========== 11. 操作列 button ==========
297
+ Map<String, Object> c11 = new HashMap<>();
298
+ c11.put("type", "button");
299
+ c11.put("label", "同步");
300
+ c11.put("width", 80);
301
+ Map<String, Object> c11Opt = new HashMap<>();
302
+ c11Opt.put("type", "primary");
303
+ c11Opt.put("onClick", "let p = this.tableParam;\nthis.getParentTarget().syncLine(p.row);");
304
+ c11.put("options", c11Opt);
305
+ columns.add(c11);
306
+
307
+ // ========== 12. 操作列 dropdown(dropdownItems 写顶层) ==========
308
+ Map<String, Object> c12 = new HashMap<>();
309
+ c12.put("type", "dropdown");
310
+ c12.put("label", "操作");
311
+ c12.put("width", 90);
312
+ List<Map<String, Object>> dropdownItems = new ArrayList<>();
313
+
314
+ Map<String, Object> m1 = new HashMap<>();
315
+ m1.put("label", "复制");
316
+ m1.put("onClick", "let p = this.tableParam;\nthis.getParentTarget().copyRow(p.row);");
317
+ dropdownItems.add(m1);
318
+
319
+ Map<String, Object> m2 = new HashMap<>();
320
+ m2.put("label", "删除");
321
+ m2.put("onClick", "let p = this.tableParam;\nthis.getParentTarget().deleteRow(p.row, p.rowIndex);");
322
+ dropdownItems.add(m2);
323
+
324
+ c12.put("dropdownItems", dropdownItems);
325
+ columns.add(c12);
326
+
327
+ return columns;
328
+ ```
329
+
330
+ 下拉/搜索等控件若配置了 `formScriptCode`,须**单独维护**选项脚本,写法与主文档 **§5.3** 相同;列内 `onClick` / `render` 为前端 JS 字符串,表单数据用 `this.formModel`,行数据用 `this.tableParam.row`。
331
+
332
+ ---
333
+
334
+ ## 1. 调用流程
335
+
336
+ ```
337
+ data-table mounted
338
+ → 加载条件脚本(可选)
339
+ → script:执行 dynamicColumnsScript
340
+ → api:请求 dynamicColumnScriptCode
341
+ → 解析 objx → 合并静态列 → 生成列控件
342
+ → (业务需要时)脚本调用 loadDynamicColumns() 重新加载,见 §1.4
343
+ ```
344
+
345
+ | 设计器配置 | 属性名 |
346
+ |-----------|--------|
347
+ | 动态列来源 | `dynamicColumnSourceType`:`none` / `script` / `api` |
348
+ | 脚本编码 | `dynamicColumnScriptCode` |
349
+ | 额外参数 | `dynamicColumnScriptParam` |
350
+ | 转换脚本 | `dynamicSchemaConvert` |
351
+ | 加载条件 | `dynamicColumnLoadScript` |
352
+ | 动态列模式 | `dynamicColumnMode`:`append` / `replace` |
353
+ | 前端列脚本 | `dynamicColumnsScript` |
354
+
355
+ ### 1.1 请求参数
356
+
357
+ 与表头字段相同内层 `{ formData, ...extra }`,数据表格**额外**携带外层字段。Java 脚本读取方式:
358
+
359
+ ```java
360
+ Map<String, Object> formData = this.getBinding().getVariables();
361
+ String billType = ((Map<String, Object>) formData.get("data")).get("billType").toString();
362
+ ```
363
+
364
+ | 字段 | 说明 |
365
+ |------|------|
366
+ | `formCode` / `formVersion` | 表单编码与版本 |
367
+ | `taBm` | 表格绑定字段名 |
368
+ | `dataId` | 单据 ID |
369
+ | `data` | 内层载荷,含 `formData` 与额外参数 |
370
+
371
+ ```json
372
+ {
373
+ "formCode": "PO_ORDER_FORM",
374
+ "taBm": "orderItems",
375
+ "dataId": "10001",
376
+ "data": {
377
+ "dataId": "10001",
378
+ "formData": { "id": "10001", "billType": "PO" },
379
+ "bizType": "PO"
380
+ }
381
+ }
382
+ ```
383
+
384
+ ### 1.2 响应与 objx
385
+
386
+ 与表头字段相同,见主文档 §1.2、§1.3。列数组取 `objx` 或 `objx.columns`。
387
+
388
+ ### 1.3 前端列脚本(script 模式)
389
+
390
+ ```javascript
391
+ return [
392
+ { type: "input", keyName: "lineNo", label: "行号", width: 80 },
393
+ {
394
+ type: "select",
395
+ keyName: "warehouseCode",
396
+ label: "仓库",
397
+ width: 120,
398
+ options: {
399
+ formScriptEnabled: true,
400
+ formScriptCode: "warehouseList",
401
+ formScriptParam: "return { orgId: this.formModel.orgId }",
402
+ },
403
+ },
404
+ ];
405
+ ```
406
+
407
+ 列内脚本:`this.tableParam.row` / `this.formModel`。
408
+
409
+ ### 1.4 脚本重新加载(数据表格动态列)
410
+
411
+ 首次加载在 `data-table` `mounted` 时自动执行(可通过 `dynamicColumnLoadScript` 控制是否加载)。列需随主表或业务条件变化而更新时,在脚本里**手动**调用:
412
+
413
+ **任意字段 / 按钮脚本(写法相同):**
414
+
415
+ ```javascript
416
+ // dataTableName:设计器里 data-table 容器的 name
417
+ this.getWidgetRef("orderItemsTable").loadDynamicColumns();
418
+ ```
419
+
420
+ > 字段与容器共用表单级 `refList`,**无需** `getParentTarget().getWidgetRef(...)`。
421
+ > 脚本跑在**当前表格操作列**内时,也可写 `this.getParentTarget().loadDynamicColumns()`(父级即本表)。
422
+
423
+ **带回调 / loading:**
424
+
425
+ ```javascript
426
+ this.getWidgetRef("orderItemsTable").loadDynamicColumns({
427
+ isLoading: true,
428
+ success(res, columns) {
429
+ // api 模式:res 为接口响应;columns 为合并后的有效列
430
+ },
431
+ fail(err) {},
432
+ });
433
+ ```
434
+
435
+ | 项目 | 说明 |
436
+ |------|------|
437
+ | 方法 | `loadDynamicColumns(options?)` |
438
+ | `api` 模式 | 重新请求 `dynamicColumnScriptCode`,解析 `objx` 后合并列并刷新表格 |
439
+ | `script` 模式 | 重新执行 `dynamicColumnsScript` 并刷新列 |
440
+ | 行为 | 按 `dynamicColumnMode` 合并静态列 → 重建列控件 → `reloadColumn` |
441
+ | 前提 | `dynamicColumnSourceType` 为 `api` 或 `script`,且对应脚本已配置 |
442
+
443
+ 表头后台字段重新加载见主文档 **§1.5**(`loadDynamicSchema()`)。
444
+
445
+ ---
446
+
447
+ ## 2. 字段定义(在表头协议上的扩展)
448
+
449
+ ### 2.1 最小字段集
450
+
451
+ **与表头相同的部分**(详见主文档 §2.0):
452
+
453
+ ```
454
+ keyName + type + label + optionItems? + required? + options?
455
+ ```
456
+
457
+ 字段级属性(`required`、`readonly`、`disabled`、`hidden`、`defaultValue`、`placeholder`)写**顶层**,不写 `options`(详见主文档 §2.1)。
458
+
459
+ **数据表格额外顶层字段**(仅此几项,其余勿重复维护)。控件 `options` 默认值见主文档 **§2.0**,与默认值相同的不写:
460
+
461
+ | 字段 | 说明 | 默认 |
462
+ |------|------|------|
463
+ | `width` | 列宽 | 150 |
464
+ | `sortable` | 可排序 | true(操作列默认 false) |
465
+ | `filterable` | 可筛选 | true(操作列默认 false) |
466
+ | `formatS` | 展示列类型(见 §2.2) | 由 `type` 自动映射 |
467
+ | `children` | 多级表头 | - |
468
+
469
+ **三类列写法:**
470
+
471
+ | 类别 | 写法 | 示例 |
472
+ |------|------|------|
473
+ | 控件列 | `type` + `keyName` + `label` + `width` | input / select / date |
474
+ | 展示列 | `keyName` + `formatS` + `label` + `width` | d1 / n5 / render |
475
+ | 审计列 | `keyName` + `label` + `width` | `_createBy` 等(不写 `formatS`) |
476
+ | 操作列 | `type` + `label` + `width` + `options`(无 `keyName`) | a-link / button;多操作用 dropdown + 顶层 `dropdownItems`;**默认不可排序、不可筛选** |
477
+
478
+ ### 2.2 type → formatS 映射
479
+
480
+ 控件列**只写 `type`**,前端自动映射:
481
+
482
+ | type | formatS |
483
+ |------|---------|
484
+ | `input` | `editInput` |
485
+ | `number` | `editNumber` |
486
+ | `date` | `editDate` |
487
+ | `select` | `editSelect` |
488
+ | `vabsearch` | `editSearch` |
489
+ | `vabUpload` / `baseAttachment` | `editAttachment` |
490
+ | `status` | `editStatus` |
491
+ | `text` / `textarea` / `checkbox` / `radio` | 同名 |
492
+ | `a-text` | `aText` |
493
+ | `a-link` / `button` / `dropdown` | 同名 |
494
+
495
+ **展示列**(不写 `type`,只写 `formatS`):
496
+
497
+ | formatS | 说明 |
498
+ |---------|------|
499
+ | `d1`~`d5` | 日期格式 |
500
+ | `n1`~`n7` | 数字/百分比 |
501
+ | `render` | 自定义渲染(配 `render` 脚本) |
502
+ | `_createBy` / `_modifyBy` / `create_date` / `modify_date` | 审计列,只写 `keyName`(不写 `type` / `formatS`) |
503
+
504
+ 各控件 `options` 字段说明 → 主文档 **§3**(input / select / vabsearch 等,**勿在本文件重复维护**)。
505
+
506
+ **附件列 `keyName`:** 须为 `attachments_<唯一后缀>`,与主文档 **§3.9** 一致;行数据 `formModel[taBm][i][keyName]` 存文件数组,`getList` 的 `taBm` 同此完整名。
507
+
508
+ ## 3. 数据表格专用示例
509
+
510
+ 以下类型**不在表头场景使用**,仅在本节维护示例。
511
+
512
+ ### 3.1 展示列
513
+
514
+ ```json
515
+ {
516
+ "keyName": "deliveryDate",
517
+ "formatS": "d1",
518
+ "label": "交货日期",
519
+ "width": 120,
520
+ "utcTransformEnabled": true
521
+ }
522
+ ```
523
+
524
+ ```json
525
+ {
526
+ "keyName": "lineAmount",
527
+ "formatS": "n5",
528
+ "label": "行金额",
529
+ "width": 120
530
+ }
531
+ ```
532
+
533
+ ```json
534
+ {
535
+ "keyName": "qtyWarn",
536
+ "formatS": "render",
537
+ "label": "库存预警",
538
+ "width": 100,
539
+ "render": "let q = params.row.qty || 0;\nreturn q < (params.row.safeQty || 0) ? '不足' : '正常';"
540
+ }
541
+ ```
542
+
543
+ ```json
544
+ {
545
+ "keyName": "_createBy",
546
+ "label": "创建人",
547
+ "width": 100
548
+ }
549
+ ```
550
+
551
+ ### 3.2 操作列
552
+
553
+ 操作列(`a-link` / `button` / `dropdown`)**默认 `sortable: false`、`filterable: false`**,无需在 JSON 中重复声明;若需开启,可显式写 `"sortable": true` 或 `"filterable": true`。
554
+
555
+ ```json
556
+ {
557
+ "type": "a-link",
558
+ "label": "查看",
559
+ "width": 100,
560
+ "options": {
561
+ "onClick": "let p = this.tableParam;\nthis.getParentTarget().openDetailDialog(p.row);"
562
+ }
563
+ }
564
+ ```
565
+
566
+ ```json
567
+ {
568
+ "type": "button",
569
+ "label": "同步",
570
+ "width": 100,
571
+ "options": {
572
+ "type": "primary",
573
+ "onClick": "let p = this.tableParam;\nthis.getParentTarget().syncLine(p.row);"
574
+ }
575
+ }
576
+ ```
577
+
578
+ 多个操作用 `dropdown`,`dropdownItems` 写列顶层:
579
+
580
+ ```json
581
+ {
582
+ "type": "dropdown",
583
+ "label": "操作",
584
+ "width": 100,
585
+ "dropdownItems": [
586
+ {
587
+ "label": "复制",
588
+ "onClick": "let p = this.tableParam;\nthis.getParentTarget().copyRow(p.row);"
589
+ },
590
+ {
591
+ "label": "删除",
592
+ "onClick": "let p = this.tableParam;\nthis.getParentTarget().deleteRow(p.row, p.rowIndex);"
593
+ }
594
+ ]
595
+ }
596
+ ```
597
+
598
+ | 字段 | 位置 | 说明 |
599
+ |------|------|------|
600
+ | `label` | **顶层** | 按钮/链接/下拉触发文字 |
601
+ | `onClick` | `options` | 点击脚本(a-link / button) |
602
+ | `dropdownItems` | **顶层** | 下拉菜单项(必填) |
603
+ | `dropdownType` / `dropdownTrigger` | `options` | 下拉样式,**可选**;不传 `options` 时默认 `dropdownType: link`、`dropdownTrigger: hover` |
604
+
605
+ ### 3.3 多级表头
606
+
607
+ ```json
608
+ {
609
+ "label": "价格信息",
610
+ "children": [
611
+ {
612
+ "type": "number",
613
+ "keyName": "unitPrice",
614
+ "label": "单价",
615
+ "width": 100,
616
+ "options": { "precision": 4 }
617
+ }
618
+ ]
619
+ }
620
+ ```
621
+
622
+ ---
623
+
624
+ ## 4. 完整综合示例
625
+
626
+ 完整 JSON 与 Java 示例见文档开头 **§0**(含控件列、展示列、多级表头、操作列)。
627
+
628
+ ---
629
+
630
+ ## 5. 动态列模式
631
+
632
+ | dynamicColumnMode | 行为 |
633
+ |-------------------|------|
634
+ | `append` | 静态列 + 动态列 |
635
+ | `replace` | 仅保留动态列 |
636
+
637
+ ---
638
+
639
+ ## 6. 注意事项
640
+
641
+ 1. **只维护一套控件 options**:改 select/vabsearch 等配置时,只改主文档 §3。
642
+ 2. **`keyName` 为唯一绑定名**。
643
+ 3. **行数据键名一致**:`keyName` 须与 `formModel[taBm][i][keyName]` 一致。
644
+ 4. **字段级属性写顶层**:`required`、`readonly`、`disabled`、`hidden`、`defaultValue`、`placeholder` 写在列/字段顶层;`options` 只放控件扩展(如 `min`、`onClick`、`searchDialogConfig`)。操作列 `label`、`dropdownItems` 也写顶层。
645
+ 5. **列对齐 / 排序 / 筛选**:`align`、操作列 `sortable`/`filterable` 等由前端默认,见主文档 **§2.0** 及本文 **§3.2**。
646
+ 6. **动态规则**:显隐/必填等走「表单设置 → 动态字段规则」。
647
+ 7. **脚本字符串**:`formScriptParam` 等为 JS 字符串,表单数据用 `this.formModel`,行数据用 `this.tableParam.row`。
648
+
649
+ ---
650
+
651
+ ## 7. 相关文件
652
+
653
+ | 文件 | 说明 |
654
+ |------|------|
655
+ | [`后台字段(动态生成)JSON 说明.md`](./后台字段(动态生成)JSON%20说明.md) | **协议主文档**(控件类型、options、Java) |
656
+ | `utils/dynamicSchemaUtil.js` | 协议常量与规范化 |
657
+ | `form-render/container-item/data-table-mixin.js` | 动态列运行时 |