weaver-e10-builder 1.5.1 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/form-add-detail-table.js +3 -3
- package/dist/commands/form-add-detail-table.js.map +1 -1
- package/dist/commands/form-add-physical-field.d.ts +3 -0
- package/dist/commands/form-add-physical-field.d.ts.map +1 -0
- package/dist/commands/form-add-physical-field.js +126 -0
- package/dist/commands/form-add-physical-field.js.map +1 -0
- package/dist/commands/form-data.d.ts.map +1 -1
- package/dist/commands/form-data.js +125 -0
- package/dist/commands/form-data.js.map +1 -1
- package/dist/commands/form-ebuilder-columns.d.ts.map +1 -1
- package/dist/commands/form-ebuilder-columns.js +3 -3
- package/dist/commands/form-ebuilder-columns.js.map +1 -1
- package/dist/commands/form-ebuilder-scope.d.ts.map +1 -1
- package/dist/commands/form-ebuilder-scope.js +3 -4
- package/dist/commands/form-ebuilder-scope.js.map +1 -1
- package/dist/commands/form-field-lang.d.ts.map +1 -1
- package/dist/commands/form-field-lang.js +3 -4
- package/dist/commands/form-field-lang.js.map +1 -1
- package/dist/commands/form-field-linkage.d.ts.map +1 -1
- package/dist/commands/form-field-linkage.js +4 -3
- package/dist/commands/form-field-linkage.js.map +1 -1
- package/dist/commands/form-formula-linkage.d.ts.map +1 -1
- package/dist/commands/form-formula-linkage.js +4 -2
- package/dist/commands/form-formula-linkage.js.map +1 -1
- package/dist/commands/form-formula.d.ts.map +1 -1
- package/dist/commands/form-formula.js +5 -5
- package/dist/commands/form-formula.js.map +1 -1
- package/dist/commands/form-layout.d.ts.map +1 -1
- package/dist/commands/form-layout.js +6 -0
- package/dist/commands/form-layout.js.map +1 -1
- package/dist/commands/form-list-click.d.ts.map +1 -1
- package/dist/commands/form-list-click.js +3 -2
- package/dist/commands/form-list-click.js.map +1 -1
- package/dist/commands/form-list-permission.js +1 -1
- package/dist/commands/form-list-permission.js.map +1 -1
- package/dist/commands/form-physical-table.d.ts +3 -0
- package/dist/commands/form-physical-table.d.ts.map +1 -0
- package/dist/commands/form-physical-table.js +195 -0
- package/dist/commands/form-physical-table.js.map +1 -0
- package/dist/commands/form-relate.d.ts.map +1 -1
- package/dist/commands/form-relate.js +3 -2
- package/dist/commands/form-relate.js.map +1 -1
- package/dist/commands/form-validate.d.ts.map +1 -1
- package/dist/commands/form-validate.js +3 -3
- package/dist/commands/form-validate.js.map +1 -1
- package/dist/commands/form-view.d.ts.map +1 -1
- package/dist/commands/form-view.js +2 -0
- package/dist/commands/form-view.js.map +1 -1
- package/dist/commands/form.d.ts +9 -3
- package/dist/commands/form.d.ts.map +1 -1
- package/dist/commands/form.js +150 -571
- package/dist/commands/form.js.map +1 -1
- package/dist/commands/org.d.ts.map +1 -1
- package/dist/commands/org.js +131 -7
- package/dist/commands/org.js.map +1 -1
- package/dist/commands/special.d.ts +24 -0
- package/dist/commands/special.d.ts.map +1 -0
- package/dist/commands/special.js +487 -0
- package/dist/commands/special.js.map +1 -0
- package/dist/commands/wf-form.d.ts.map +1 -1
- package/dist/commands/wf-form.js +4 -4
- package/dist/commands/wf-form.js.map +1 -1
- package/dist/commands/workflow-message.d.ts.map +1 -1
- package/dist/commands/workflow-message.js +1 -2
- package/dist/commands/workflow-message.js.map +1 -1
- package/dist/commands/workflow-operator-sign.js +1 -1
- package/dist/commands/workflow-operator-sign.js.map +1 -1
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -1
- package/dist/services/actionflow/components/action_eb_update_compiler.d.ts.map +1 -1
- package/dist/services/actionflow/components/action_eb_update_compiler.js +54 -12
- package/dist/services/actionflow/components/action_eb_update_compiler.js.map +1 -1
- package/dist/services/actionflow/components/index.d.ts.map +1 -1
- package/dist/services/actionflow/components/index.js +6 -0
- package/dist/services/actionflow/components/index.js.map +1 -1
- package/dist/services/actionflow/components/multiple_execution.d.ts.map +1 -1
- package/dist/services/actionflow/components/multiple_execution.js +73 -13
- package/dist/services/actionflow/components/multiple_execution.js.map +1 -1
- package/dist/services/actionflow/components/output.d.ts.map +1 -1
- package/dist/services/actionflow/components/output.js +144 -5
- package/dist/services/actionflow/components/output.js.map +1 -1
- package/dist/services/actionflow/components/send_message.d.ts.map +1 -1
- package/dist/services/actionflow/components/send_message.js +60 -32
- package/dist/services/actionflow/components/send_message.js.map +1 -1
- package/dist/services/actionflow/components/serverless_action.d.ts +5 -0
- package/dist/services/actionflow/components/serverless_action.d.ts.map +1 -0
- package/dist/services/actionflow/components/serverless_action.js +134 -0
- package/dist/services/actionflow/components/serverless_action.js.map +1 -0
- package/dist/services/actionflow/components/webhook_trigger.d.ts +5 -0
- package/dist/services/actionflow/components/webhook_trigger.d.ts.map +1 -0
- package/dist/services/actionflow/components/webhook_trigger.js +85 -0
- package/dist/services/actionflow/components/webhook_trigger.js.map +1 -0
- package/dist/services/actionflow/context_preflight.d.ts +6 -0
- package/dist/services/actionflow/context_preflight.d.ts.map +1 -1
- package/dist/services/actionflow/context_preflight.js +247 -7
- package/dist/services/actionflow/context_preflight.js.map +1 -1
- package/dist/services/actionflow/context_provider.d.ts.map +1 -1
- package/dist/services/actionflow/context_provider.js +67 -3
- package/dist/services/actionflow/context_provider.js.map +1 -1
- package/dist/services/actionflow/custom_trigger_context.d.ts.map +1 -1
- package/dist/services/actionflow/custom_trigger_context.js +9 -2
- package/dist/services/actionflow/custom_trigger_context.js.map +1 -1
- package/dist/services/actionflow/formula_service.d.ts +2 -0
- package/dist/services/actionflow/formula_service.d.ts.map +1 -1
- package/dist/services/actionflow/formula_service.js +381 -63
- package/dist/services/actionflow/formula_service.js.map +1 -1
- package/dist/services/actionflow/generation_service.d.ts.map +1 -1
- package/dist/services/actionflow/generation_service.js +9 -0
- package/dist/services/actionflow/generation_service.js.map +1 -1
- package/dist/services/actionflow/param_group_dto.d.ts +5 -0
- package/dist/services/actionflow/param_group_dto.d.ts.map +1 -0
- package/dist/services/actionflow/param_group_dto.js +181 -0
- package/dist/services/actionflow/param_group_dto.js.map +1 -0
- package/dist/services/actionflow/preflight_service.d.ts.map +1 -1
- package/dist/services/actionflow/preflight_service.js +53 -11
- package/dist/services/actionflow/preflight_service.js.map +1 -1
- package/dist/services/actionflow/readback_verifier.d.ts.map +1 -1
- package/dist/services/actionflow/readback_verifier.js +14 -1
- package/dist/services/actionflow/readback_verifier.js.map +1 -1
- package/dist/services/actionflow/update_service.d.ts.map +1 -1
- package/dist/services/actionflow/update_service.js +14 -8
- package/dist/services/actionflow/update_service.js.map +1 -1
- package/dist/services/actionflow/webhook_source.d.ts +4 -0
- package/dist/services/actionflow/webhook_source.d.ts.map +1 -0
- package/dist/services/actionflow/webhook_source.js +99 -0
- package/dist/services/actionflow/webhook_source.js.map +1 -0
- package/dist/services/actionflow/workflow_compiler.d.ts.map +1 -1
- package/dist/services/actionflow/workflow_compiler.js +1 -0
- package/dist/services/actionflow/workflow_compiler.js.map +1 -1
- package/dist/services/form-create.d.ts +45 -2
- package/dist/services/form-create.d.ts.map +1 -1
- package/dist/services/form-create.js +78 -8
- package/dist/services/form-create.js.map +1 -1
- package/dist/services/form-data-openapi.d.ts +142 -0
- package/dist/services/form-data-openapi.d.ts.map +1 -0
- package/dist/services/form-data-openapi.js +363 -0
- package/dist/services/form-data-openapi.js.map +1 -0
- package/dist/services/form-field-build.d.ts +71 -0
- package/dist/services/form-field-build.d.ts.map +1 -0
- package/dist/services/form-field-build.js +436 -0
- package/dist/services/form-field-build.js.map +1 -0
- package/dist/services/form-field-template.d.ts +71 -0
- package/dist/services/form-field-template.d.ts.map +1 -0
- package/dist/services/form-field-template.js +103 -0
- package/dist/services/form-field-template.js.map +1 -0
- package/dist/services/form-field-validate.d.ts +26 -0
- package/dist/services/form-field-validate.d.ts.map +1 -0
- package/dist/services/form-field-validate.js +121 -0
- package/dist/services/form-field-validate.js.map +1 -0
- package/dist/services/form-id.d.ts +50 -0
- package/dist/services/form-id.d.ts.map +1 -0
- package/dist/services/form-id.js +110 -0
- package/dist/services/form-id.js.map +1 -0
- package/dist/services/form-layout-grid.d.ts +123 -0
- package/dist/services/form-layout-grid.d.ts.map +1 -0
- package/dist/services/form-layout-grid.js +177 -0
- package/dist/services/form-layout-grid.js.map +1 -0
- package/dist/services/form-layout-insert.d.ts +24 -1
- package/dist/services/form-layout-insert.d.ts.map +1 -1
- package/dist/services/form-layout-insert.js +192 -27
- package/dist/services/form-layout-insert.js.map +1 -1
- package/dist/services/form-physical-table.d.ts +115 -0
- package/dist/services/form-physical-table.d.ts.map +1 -0
- package/dist/services/form-physical-table.js +153 -0
- package/dist/services/form-physical-table.js.map +1 -0
- package/dist/services/form-table-view.d.ts.map +1 -1
- package/dist/services/form-table-view.js +2 -11
- package/dist/services/form-table-view.js.map +1 -1
- package/dist/services/form-view.d.ts +2 -0
- package/dist/services/form-view.d.ts.map +1 -1
- package/dist/services/form-view.js +57 -7
- package/dist/services/form-view.js.map +1 -1
- package/dist/services/form-virtual.d.ts.map +1 -1
- package/dist/services/form-virtual.js +6 -1
- package/dist/services/form-virtual.js.map +1 -1
- package/dist/services/org-employee.d.ts +105 -1
- package/dist/services/org-employee.d.ts.map +1 -1
- package/dist/services/org-employee.js +186 -5
- package/dist/services/org-employee.js.map +1 -1
- package/dist/services/special-page.d.ts +85 -0
- package/dist/services/special-page.d.ts.map +1 -0
- package/dist/services/special-page.js +129 -0
- package/dist/services/special-page.js.map +1 -0
- package/dist/services/special-route.d.ts +92 -0
- package/dist/services/special-route.d.ts.map +1 -0
- package/dist/services/special-route.js +93 -0
- package/dist/services/special-route.js.map +1 -0
- package/dist/services/workflow-node-layout.d.ts +13 -1
- package/dist/services/workflow-node-layout.d.ts.map +1 -1
- package/dist/services/workflow-node-layout.js +11 -3
- package/dist/services/workflow-node-layout.js.map +1 -1
- package/package.json +1 -1
- package/skill-docs/SKILL.md +5 -5
- package/skill-docs/_catalog.md +1 -0
- package/skill-docs/actionflows/SKILL.md +5 -4
- package/skill-docs/actionflows/references/components/logic/MultipleExecution.md +38 -38
- package/skill-docs/actionflows/references/components/logic/Output.md +9 -2
- package/skill-docs/actionflows/references/components/message/SendMessage.md +3 -3
- package/skill-docs/actionflows/references/components/serverless/ServerlessAction.md +42 -0
- package/skill-docs/actionflows/references/components/trigger/WebHookTrigger.md +89 -0
- package/skill-docs/actionflows/references/dynamic_capability_matrix.md +2 -2
- package/skill-docs/references/common/ref-cli-commands.md +11 -3
- package/skill-docs/references/form/guide-form-association.md +1 -1
- package/skill-docs/references/form/guide-form-data.md +30 -0
- package/skill-docs/references/form/guide-form-layout-incremental.md +18 -3
- package/skill-docs/references/form/guide-form-linkage.md +2 -2
- package/skill-docs/references/form/guide-form-physical-table.md +146 -0
- package/skill-docs/references/form/ref-form-fields.md +1 -1
- package/skill-docs/references/hrm/guide-org-employee.md +71 -3
- package/skill-docs/references/report/cell-config-echo-guide.md +1 -1
- package/skill-docs/references/report/dataset-query-filter-guide.md +3 -3
- package/skill-docs/references/report/date-range-query-reference.md +2 -2
- package/skill-docs/references/report/query-condition-api-guide.md +11 -11
- package/skill-docs/references/report/report-designer-feature-map.md +1 -1
- package/skill-docs/references/report/report-permission-guide.md +2 -2
- package/skill-docs/references/workflow/guide-workflow-odoc-setting.md +6 -6
- package/skill-docs/references/workflow/guide-workflow.md +1 -1
package/dist/commands/form.js
CHANGED
|
@@ -2,97 +2,34 @@ import { writeFileSync, existsSync, readFileSync } from 'fs';
|
|
|
2
2
|
import { AuthSession } from '../core/auth.js';
|
|
3
3
|
import { ApiClient } from '../core/client.js';
|
|
4
4
|
import { toColumnName, toTableName, resolveFieldKeys } from '../utils/column-name.js';
|
|
5
|
-
import { createCustomPhysicalForm,
|
|
6
|
-
import {
|
|
7
|
-
import { RICH_TYPE_MAP, getRichType, richIdUnits, BROWSER_KINDS } from '../services/rich-types.js';
|
|
5
|
+
import { createCustomPhysicalForm, applyInitialFieldsToNewForm, placeholderFromFormat, reapplyGroupLayout, queryFormStorageSetting } from '../services/form-create.js';
|
|
6
|
+
import { getRichType, richIdUnits, BROWSER_KINDS } from '../services/rich-types.js';
|
|
8
7
|
import { dropFieldComponents, prepareLayoutForSave } from '../services/form-field-drop.js';
|
|
9
8
|
import { describeEditBaseFailure, resolveEditBase as resolveEditBasePure } from '../services/form-edit-base.js';
|
|
10
9
|
import { applyFieldPatches, platformFieldShape } from '../services/form-field-patch.js';
|
|
11
10
|
import { ensureDisplayColumns } from '../services/form-ebuilder-columns.js';
|
|
12
|
-
import { insertFieldIntoLayout, patchFieldLayout } from '../services/form-layout-insert.js';
|
|
11
|
+
import { insertFieldIntoLayout, patchFieldLayout, NO_FIELD_TYPE_TEMPLATE } from '../services/form-layout-insert.js';
|
|
12
|
+
import { fetchFieldTypeTemplates, findTemplateByType } from '../services/form-field-template.js';
|
|
13
13
|
import { buildLinkageAddPayload, addLinkage, resolveEffect, readLinkageConfig } from '../services/form-linkage-condition.js';
|
|
14
|
-
|
|
14
|
+
// 字段 jsonObj 的构造器(含 SYSTEM_FIELD_IDS / defaultEBuilderDataTitle)——
|
|
15
|
+
// 原先内联在下面 add-field 的 action 里,为了给「按物理表加字段」第二个入口复用而抽出去。
|
|
16
|
+
// 本文件仍有别处用这两个符号,所以在这里回导。
|
|
17
|
+
import { SYSTEM_FIELD_IDS, defaultEBuilderDataTitle, buildFieldJsonObject } from '../services/form-field-build.js';
|
|
18
|
+
// objId → 表单记录/formId。obj/getList 不含三方表单(专项应用),所以内部带了 getInfoById 兜底。
|
|
19
|
+
import { resolveFormRecord, resolveFormId, appendThirdPartyForms, resolveObjIdByFormId } from '../services/form-id.js';
|
|
20
|
+
// 字段类型归一 / 校验:与「专项建表」共用同一份(原为本文件内联)。
|
|
21
|
+
import { FIELD_TYPE_CHOICES, canonComponentKey, assertFieldType, normalizeFieldDef } from '../services/form-field-validate.js';
|
|
22
|
+
import { listFormTables, readFieldRecords, mapFieldsToTables } from '../services/form-physical-table.js';
|
|
15
23
|
import { buildFormulaParameters, defaultReturnType } from '../services/formula-detail.js';
|
|
16
24
|
import { registerOptionTemplateCommand } from './form-option-template.js';
|
|
17
25
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
const CK_CANON = {
|
|
24
|
-
textarea: 'TextArea', ebuilder: 'EBuilder', datetime: 'DateTime',
|
|
25
|
-
idcard: 'IDCard', mobile: 'Mobile', email: 'Email', phone: 'Phone',
|
|
26
|
-
employee: 'Employee', department: 'Department', subcompany: 'SubCompany', location: 'Location',
|
|
27
|
-
switch: 'Switch', select: 'Select', checkbox: 'CheckBox', radiobox: 'RadioBox',
|
|
28
|
-
money: 'Money', number: 'Number', date: 'Date', text: 'Text',
|
|
29
|
-
daterange: 'DateRange', detailtable: 'DetailTable', city: 'City',
|
|
30
|
-
};
|
|
31
|
-
function canonComponentKey(ck) {
|
|
32
|
-
return CK_CANON[String(ck).toLowerCase()] || ck;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* 代码里另有分支/别名、不在 CK_CANON 与 RICH_TYPE_MAP 两处的字段类型。
|
|
36
|
-
* **宁宽勿窄**:多列一个只是照旧送服务器,漏一个会把合法类型误杀,所以这里尽量收全。
|
|
37
|
-
* 新增字段类型时记得同步。
|
|
38
|
-
*/
|
|
39
|
-
const EXTRA_FIELD_TYPES = [
|
|
40
|
-
'Flow', 'SelectMultiple', 'DateInterval', 'ColorPicker', 'DataTable',
|
|
41
|
-
'DocFolder', 'DocFloder', 'Fold', 'Placeholder', 'Sharer', 'FileComponent',
|
|
42
|
-
'EbuilderAppTag', 'Attachment', 'Image', 'Signature', 'RichText', 'RelateBrowser',
|
|
43
|
-
// EInvoice 电子发票:下方 add-field 有专属构建分支(isSingle/invoiceType),此前漏登记 →
|
|
44
|
-
// 被 assertFieldType 挡成死代码。AudioRecord/VideoRecord 语音视频:极简 jsonObj,平台原样透传
|
|
45
|
-
// (见 rich-types.ts 的说明),无需富组件模板。
|
|
46
|
-
'EInvoice', 'AudioRecord', 'VideoRecord',
|
|
47
|
-
];
|
|
48
|
-
/** 可用的字段类型 = 基础组件(CK_CANON 的值)∪ 富组件(RICH_TYPE_MAP 的键)∪ 上述补充。
|
|
49
|
-
* 导出仅为让测试能盯住"平台支持的合法类型不许漏登记"(漏了会被 assertFieldType 误杀)。 */
|
|
50
|
-
export const FIELD_TYPE_CHOICES = [...new Set([
|
|
51
|
-
...Object.values(CK_CANON),
|
|
52
|
-
...Object.keys(RICH_TYPE_MAP),
|
|
53
|
-
...EXTRA_FIELD_TYPES,
|
|
54
|
-
])].sort();
|
|
55
|
-
/**
|
|
56
|
-
* 校验字段类型名。不校验的话错类型会**直送服务器**,换回一段
|
|
57
|
-
* `Cannot invoke "ComponentType.name()" because ... getComponentKey() is null`——
|
|
58
|
-
* 既看不出哪错了,也看不出该填什么。这里先拦下来并列出可选值。
|
|
59
|
-
*/
|
|
60
|
-
function assertFieldType(fieldType, where = '') {
|
|
61
|
-
if (FIELD_TYPE_CHOICES.includes(fieldType))
|
|
62
|
-
return;
|
|
63
|
-
console.error(`${where}不支持的字段类型: ${fieldType}`);
|
|
64
|
-
console.error(` 可选: ${FIELD_TYPE_CHOICES.join(' / ')}`);
|
|
65
|
-
process.exit(1);
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* 校验选项定义。**键名必须是 `name` / `valueKey`**——写成 `{value,label}` 这类
|
|
69
|
-
* 不会被拦下,而是静默存成「没有名字的选项」(平台侧 fieldOptionName 为空),
|
|
70
|
-
* 之后 `form data add` 按选项名写数据会直接失效,且读回也看不出哪里错了。
|
|
26
|
+
* 字段类型归一 / 校验 / 校验清单已抽到 `services/form-field-validate.ts` ——
|
|
27
|
+
* 「标准建表」与「专项建表」两个入口共用同一份,避免两处真相。
|
|
28
|
+
*
|
|
29
|
+
* `FIELD_TYPE_CHOICES` 在这里**原样 re-export**:
|
|
30
|
+
* `form-add-physical-field.ts` 与 `test/form-field-types.test.ts` 仍从本模块取它。
|
|
71
31
|
*/
|
|
72
|
-
|
|
73
|
-
options.forEach((o, i) => {
|
|
74
|
-
if (typeof o === 'string')
|
|
75
|
-
return; // 字符串简写,归一化时会补成 {name}
|
|
76
|
-
const where = `字段「${fieldName}」第 ${i + 1} 个选项`;
|
|
77
|
-
if (typeof o?.name !== 'string' || !o.name.trim()) {
|
|
78
|
-
const got = o && typeof o === 'object' ? Object.keys(o).join(' / ') : String(o);
|
|
79
|
-
console.error(`${where}缺少 name(收到键:${got || '(无)'})`);
|
|
80
|
-
console.error(' 正确写法: {"name":"待入职","valueKey":"1"}(valueKey 省略则由平台生成)');
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
const vk = o.valueKey;
|
|
84
|
-
if (vk !== undefined && vk !== null && String(vk) !== '' && !/^\d+$/.test(String(vk))) {
|
|
85
|
-
console.error(`${where}的 valueKey「${vk}」必须是数字(平台规范,下游按数字引用)`);
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
const SYSTEM_FIELD_IDS = new Set([
|
|
91
|
-
'name', 'creator', 'create_time', 'update_time', 'id',
|
|
92
|
-
'updater', 'data_status', 'flow_status', 'current_step',
|
|
93
|
-
'batchpubtags', 'batchmytags', 'operate', 'data_read_status',
|
|
94
|
-
'flow_system_number',
|
|
95
|
-
]);
|
|
32
|
+
export { FIELD_TYPE_CHOICES };
|
|
96
33
|
/** 系统字段的中文名 → 物理字段 id(form read 实测的平台自带字段)。建表时字段与此完全重名 → 跳过新建、直接复用系统字段。 */
|
|
97
34
|
const SYSTEM_FIELD_NAME_TO_ID = {
|
|
98
35
|
'标题': 'name',
|
|
@@ -117,14 +54,6 @@ const SYSTEM_FIELD_CFG_ID = {
|
|
|
117
54
|
'最后更新人': 'updater',
|
|
118
55
|
'数据状态': 'dataStatus',
|
|
119
56
|
};
|
|
120
|
-
// EBuilder dataTitle(关联字段标题栏)默认解析:优先字段名含「名称」的业务字段,无则第一个业务字段。
|
|
121
|
-
// 需求:默认选带"名称"二字的字段(如 客户名称/类型名称),没有就默认选第一个。
|
|
122
|
-
function defaultEBuilderDataTitle(fields) {
|
|
123
|
-
const isBiz = (id) => /^\d{15,}$/.test(id) && !SYSTEM_FIELD_IDS.has(id);
|
|
124
|
-
const biz = (fields || []).filter((f) => isBiz(String(f.fieldId ?? '')));
|
|
125
|
-
const byName = biz.find((f) => String(f.fieldName ?? f.title ?? '').includes('名称'));
|
|
126
|
-
return byName?.fieldId || biz[0]?.fieldId || 'name';
|
|
127
|
-
}
|
|
128
57
|
/**
|
|
129
58
|
* 只读地看一眼 `getFields` 眼里这个字段长什么样 —— **仅用于把错误信息说清楚,绝不作为写回基底**。
|
|
130
59
|
*
|
|
@@ -149,11 +78,21 @@ async function peekViaGetFields(client, baseUrl, objId, fieldId, auth) {
|
|
|
149
78
|
* 单独抽出来是为了让「读不到 → 中止」这条路清晰:**失败要能被调用方看见**,
|
|
150
79
|
* 不能吞掉之后改走别的接口重建一个定义。
|
|
151
80
|
*/
|
|
152
|
-
|
|
81
|
+
/**
|
|
82
|
+
* 读表单的字段记录。
|
|
83
|
+
*
|
|
84
|
+
* ⚠ **主表与明细表是两套结果,不带 `subFormId` 时只返主表字段。**
|
|
85
|
+
* 明细字段必须显式带 `subFormId` 才查得到 —— 少了这个参数,刚建好的明细字段会「查不到」,
|
|
86
|
+
* 表现为「字段已建立,但读回字段定义失败」。
|
|
87
|
+
*/
|
|
88
|
+
async function fetchFieldPageRecords(client, baseUrl, appId, formId, subFormId) {
|
|
153
89
|
const r = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormFieldPage?apid=${appId}`, {
|
|
154
90
|
pageNo: 1, pageSize: 500, module: 'ebuilderform',
|
|
155
91
|
customParam: { appId },
|
|
156
|
-
formFieldSearchEntity: {
|
|
92
|
+
formFieldSearchEntity: {
|
|
93
|
+
isDelete: 0, status: 'enable', formId,
|
|
94
|
+
...(subFormId ? { subFormId: String(subFormId) } : {}),
|
|
95
|
+
},
|
|
157
96
|
}, { ebbusinessid: formId });
|
|
158
97
|
return r?.data?.pageDatas?.result || r?.data?.data?.records || r?.data?.data || [];
|
|
159
98
|
}
|
|
@@ -204,8 +143,7 @@ export function registerFormCommands(program) {
|
|
|
204
143
|
if (cmdOpts?.appId) {
|
|
205
144
|
try {
|
|
206
145
|
// Get formId from appId
|
|
207
|
-
const
|
|
208
|
-
const formInfo = (listData?.data || []).find((f) => f.id === objId);
|
|
146
|
+
const formInfo = await resolveFormRecord(client, baseUrl, cmdOpts.appId, objId);
|
|
209
147
|
formId = formInfo?.formId || '';
|
|
210
148
|
if (formId) {
|
|
211
149
|
// Fetch options for Select/RadioBox/CheckBox fields (skip system fields with string IDs)
|
|
@@ -284,6 +222,22 @@ export function registerFormCommands(program) {
|
|
|
284
222
|
}
|
|
285
223
|
catch { /* 布局属性补读失败不影响主流程 */ }
|
|
286
224
|
}
|
|
225
|
+
// 所属物理表(设计器「字段管理」的主表/其他)—— getFields 不带这个信息,
|
|
226
|
+
// 得用 getFormAGroupList(表清单)+ getFormFieldPage(字段记录上的 formTableId)拼出来。
|
|
227
|
+
if (formId && cmdOpts?.appId) {
|
|
228
|
+
try {
|
|
229
|
+
const scope = { formId, appId: cmdOpts.appId, objId };
|
|
230
|
+
const tables = await listFormTables(client, baseUrl, cmdOpts.appId, scope);
|
|
231
|
+
const records = await readFieldRecords(client, baseUrl, cmdOpts.appId, scope);
|
|
232
|
+
const byField = mapFieldsToTables(records, tables);
|
|
233
|
+
for (const f of allFields) {
|
|
234
|
+
const t = byField[String(f.fieldId)];
|
|
235
|
+
if (t)
|
|
236
|
+
f.formTable = { id: t.formTableId, name: t.name, type: t.tableType, tableName: t.tableName };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch { /* 物理表归属读不到不影响主流程 */ }
|
|
240
|
+
}
|
|
287
241
|
if (cmdOpts?.out) {
|
|
288
242
|
// Write JSON to file with UTF-8 encoding, bypassing terminal encoding
|
|
289
243
|
writeFileSync(cmdOpts.out, JSON.stringify(allFields, null, 2), 'utf-8');
|
|
@@ -294,10 +248,13 @@ export function registerFormCommands(program) {
|
|
|
294
248
|
}
|
|
295
249
|
else {
|
|
296
250
|
console.log(`表单 ${objId} 共 ${fields.length} 个主表字段${subFields.length ? ` + ${subFields.length} 个明细表字段` : ''}:`);
|
|
251
|
+
// 主表那排 chip 上写的是表单名,逐行重复没信息量,显示成「主表」;附属物理表用它的真名
|
|
252
|
+
const tableLabel = (f) => (f.formTable ? (f.formTable.type === 'MAIN' ? '主表' : f.formTable.name) : '');
|
|
297
253
|
for (const f of fields) {
|
|
298
254
|
if (SYSTEM_FIELD_IDS.has(f.fieldId))
|
|
299
255
|
continue;
|
|
300
|
-
|
|
256
|
+
const t = tableLabel(f);
|
|
257
|
+
console.log(` [${f.fieldKey || f.fieldType}] ${f.fieldName} (fieldId: ${f.fieldId || f.id})${t ? ` · 物理表: ${t}` : ''}`);
|
|
301
258
|
if (f.options)
|
|
302
259
|
console.log(` 选项: ${JSON.stringify(f.options)}`);
|
|
303
260
|
if (f.optionTemplate)
|
|
@@ -333,6 +290,8 @@ export function registerFormCommands(program) {
|
|
|
333
290
|
process.exit(1);
|
|
334
291
|
}
|
|
335
292
|
const forms = data.data || [];
|
|
293
|
+
// obj/getList 不含三方表单(专项应用)—— 补上。标准应用没有三方表单,等于空转。
|
|
294
|
+
await appendThirdPartyForms(client, baseUrl, appId, forms);
|
|
336
295
|
if (cmdOpts?.out) {
|
|
337
296
|
writeFileSync(cmdOpts.out, JSON.stringify(forms, null, 2), 'utf-8');
|
|
338
297
|
console.error(`已写入: ${cmdOpts.out}`);
|
|
@@ -396,22 +355,8 @@ export function registerFormCommands(program) {
|
|
|
396
355
|
// componentKey 大小写归一(canonComponentKey):用户/示例常写 "Textarea"/"Ebuilder" 等,
|
|
397
356
|
// 但 storageFieldType 与各 `=== 'TextArea'` 分支按 canonical 大小写匹配,不归一会导致 type 存成
|
|
398
357
|
// 小写 "Textarea"(FLOW 渲染异常)、TextArea 分支(content/maxLen)漏跑。
|
|
399
|
-
// 归一化 +
|
|
400
|
-
|
|
401
|
-
const out = { ...raw };
|
|
402
|
-
if (!out.componentKey && out.fieldType)
|
|
403
|
-
out.componentKey = out.fieldType;
|
|
404
|
-
if (out.componentKey)
|
|
405
|
-
out.componentKey = canonComponentKey(out.componentKey);
|
|
406
|
-
const label = out.fieldName || out.title || '(未命名)';
|
|
407
|
-
assertFieldType(out.componentKey || '', `字段「${label}」`);
|
|
408
|
-
if (Array.isArray(out.options) && !out.fieldOptions) {
|
|
409
|
-
out.fieldOptions = out.options.map((o) => typeof o === 'string' ? { name: o } : o);
|
|
410
|
-
}
|
|
411
|
-
if (Array.isArray(out.fieldOptions))
|
|
412
|
-
assertFieldOptions(label, out.fieldOptions);
|
|
413
|
-
return out;
|
|
414
|
-
};
|
|
358
|
+
// 归一化 + 就地校验(类型名/选项键名)见 services/form-field-validate.ts 的 normalizeFieldDef ——
|
|
359
|
+
// 顶层与明细子字段共用它,且与「专项建表」共用同一份实现。
|
|
415
360
|
fieldDefs = fieldDefs.map((f) => {
|
|
416
361
|
const out = normalizeFieldDef(f);
|
|
417
362
|
// DetailTable 子字段同样归一化:之前只转顶层,子表 Select 用 options 简写时
|
|
@@ -629,42 +574,10 @@ export function registerFormCommands(program) {
|
|
|
629
574
|
else {
|
|
630
575
|
console.error(` 数据库表名: ${result.tableName}`);
|
|
631
576
|
}
|
|
632
|
-
// Create fields and layout
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
hidden: f.hidden, formFields: f.formFields,
|
|
637
|
-
enableSubDetail: f.enableSubDetail, defaultExpandRow: f.defaultExpandRow,
|
|
638
|
-
subDetailConfig: f.subDetailConfig,
|
|
639
|
-
precision: f.precision, pointSize: f.pointSize,
|
|
640
|
-
percent: f.percent, defaultValue: f.defaultValue,
|
|
641
|
-
group: f.group, multiple: f.multiple,
|
|
642
|
-
singleRow: f.singleRow,
|
|
643
|
-
// 文本最大长度(Text 缺省 100、TextArea 缺省 4000)+ 日期精度 format(Date/DateTime)需透传,
|
|
644
|
-
// 否则 fields.json 标注的 maxLen/format 被丢(bug:长度默认 100 不生效、日期精度丢失)。
|
|
645
|
-
maxLen: f.maxLen, format: f.format,
|
|
646
|
-
// 字段编码 dataKey / 数据库列名 columnName —— 两者是两个东西,可分别指定。
|
|
647
|
-
// 这里是**白名单透传**,漏一项就会静默丢(maxLen/format 历史上就漏过)。
|
|
648
|
-
dataKey: f.dataKey, columnName: f.columnName,
|
|
649
|
-
}));
|
|
650
|
-
const fieldResult = await addInitialFieldsToCustomPhysicalForm(client, baseUrl, appId, objId, formId, fieldDefsForStandard, auth.tenantKey, layoutType, systemOverrides);
|
|
651
|
-
fieldGroups = fieldResult.fieldGroups || [];
|
|
652
|
-
console.error(` 字段+布局已写入: ${fieldDefsForStandard.length} 个字段`);
|
|
653
|
-
const detailTables = fieldResult.detailTables || [];
|
|
654
|
-
for (const dt of detailTables) {
|
|
655
|
-
console.error(` 明细表: ${dt.definition.fieldName} subFormId: ${dt.subFormId}`);
|
|
656
|
-
}
|
|
657
|
-
const subDetailService = new FormSubDetailService(client, baseUrl, appId, objId, formId);
|
|
658
|
-
for (const dt of detailTables) {
|
|
659
|
-
const def = dt.definition;
|
|
660
|
-
if (def.enableSubDetail && Array.isArray(def.subDetailConfig) && def.subDetailConfig.length > 0) {
|
|
661
|
-
await subDetailService.set(def.fieldName, def.subDetailConfig, Boolean(def.defaultExpandRow));
|
|
662
|
-
console.error(` 二级明细已配置: ${def.fieldName} → ${def.subDetailConfig.map((x) => x.subForm).join(', ')}`);
|
|
663
|
-
}
|
|
664
|
-
else if (def.enableSubDetail) {
|
|
665
|
-
throw new Error(`明细表 ${def.fieldName} 已声明 enableSubDetail,但 subDetailConfig 为空`);
|
|
666
|
-
}
|
|
667
|
-
}
|
|
577
|
+
// Create fields and layout —— 白名单映射 + 明细/二级明细那一整步已抽到
|
|
578
|
+
// services/form-create.ts 的 applyInitialFieldsToNewForm,「专项建表」共用同一条链。
|
|
579
|
+
const fieldResult = await applyInitialFieldsToNewForm(client, baseUrl, appId, objId, formId, fieldDefs, { tenantKey: auth.tenantKey, layoutType, systemOverrides });
|
|
580
|
+
fieldGroups = fieldResult.fieldGroups;
|
|
668
581
|
}
|
|
669
582
|
catch (e) {
|
|
670
583
|
console.error(`创建失败: ${e.message}`);
|
|
@@ -708,8 +621,7 @@ export function registerFormCommands(program) {
|
|
|
708
621
|
let resolvedFormId = formId || '';
|
|
709
622
|
const resolveFormId = async () => {
|
|
710
623
|
if (!resolvedFormId) {
|
|
711
|
-
const
|
|
712
|
-
const formInfo = (formList?.data || []).find((f) => f.id === objId);
|
|
624
|
+
const formInfo = await resolveFormRecord(client, baseUrl, appId, objId);
|
|
713
625
|
resolvedFormId = formInfo?.formId || '';
|
|
714
626
|
}
|
|
715
627
|
return resolvedFormId;
|
|
@@ -729,6 +641,8 @@ export function registerFormCommands(program) {
|
|
|
729
641
|
// Resolve target form names and field IDs from sformId
|
|
730
642
|
const formList = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
731
643
|
const allForms = formList?.data || [];
|
|
644
|
+
// 目标表可能是三方表单(专项应用),它不在 getList 里 —— 补上
|
|
645
|
+
await appendThirdPartyForms(client, baseUrl, appId, allForms);
|
|
732
646
|
const formNameById = new Map();
|
|
733
647
|
for (const fm of allForms) {
|
|
734
648
|
formNameById.set(fm.id, fm.formName || fm.name || fm.id);
|
|
@@ -763,8 +677,7 @@ export function registerFormCommands(program) {
|
|
|
763
677
|
};
|
|
764
678
|
await resolveFormId();
|
|
765
679
|
if (!resolvedFormId) {
|
|
766
|
-
const
|
|
767
|
-
const formInfo = (formList?.data || []).find((f) => f.id === objId);
|
|
680
|
+
const formInfo = await resolveFormRecord(client, baseUrl, appId, objId);
|
|
768
681
|
resolvedFormId = formInfo?.formId || '';
|
|
769
682
|
}
|
|
770
683
|
// Use getFormFieldPage to get full field info including columnName
|
|
@@ -979,6 +892,8 @@ export function registerFormCommands(program) {
|
|
|
979
892
|
try {
|
|
980
893
|
const formList = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
981
894
|
allForms = formList?.data || [];
|
|
895
|
+
// 目标可能是三方表单(专项应用),它不在 getList 里 —— 补上
|
|
896
|
+
await appendThirdPartyForms(client, baseUrl, appId, allForms);
|
|
982
897
|
}
|
|
983
898
|
catch { /* 查不到则 formName 用 sformId */ }
|
|
984
899
|
// 读已建字段(拿 fieldId)
|
|
@@ -1870,8 +1785,7 @@ export function registerFormCommands(program) {
|
|
|
1870
1785
|
process.exit(1);
|
|
1871
1786
|
}
|
|
1872
1787
|
// Get formId from appId
|
|
1873
|
-
const
|
|
1874
|
-
const formInfo = (listData?.data || []).find((f) => f.id === objId);
|
|
1788
|
+
const formInfo = await resolveFormRecord(client, baseUrl, appId, objId);
|
|
1875
1789
|
const formId = formInfo?.formId || '';
|
|
1876
1790
|
if (!formId) {
|
|
1877
1791
|
console.error('无法获取 formId,请确认 appId 和 objId 正确');
|
|
@@ -1916,8 +1830,7 @@ export function registerFormCommands(program) {
|
|
|
1916
1830
|
const client = new ApiClient(auth);
|
|
1917
1831
|
const baseUrl = auth.getBaseUrl();
|
|
1918
1832
|
// objId → formId
|
|
1919
|
-
const
|
|
1920
|
-
const formInfo = (listData?.data || []).find((f) => String(f.id) === String(objId));
|
|
1833
|
+
const formInfo = await resolveFormRecord(client, baseUrl, appId, objId);
|
|
1921
1834
|
const formId = formInfo?.formId || '';
|
|
1922
1835
|
if (!formId) {
|
|
1923
1836
|
console.error('无法获取 formId,请确认 objId 正确');
|
|
@@ -1964,8 +1877,7 @@ export function registerFormCommands(program) {
|
|
|
1964
1877
|
const client = new ApiClient(auth);
|
|
1965
1878
|
const baseUrl = auth.getBaseUrl();
|
|
1966
1879
|
// Get formId
|
|
1967
|
-
const
|
|
1968
|
-
const formInfo = (formList?.data || []).find((f) => f.id === objId);
|
|
1880
|
+
const formInfo = await resolveFormRecord(client, baseUrl, appId, objId);
|
|
1969
1881
|
const formId = formInfo?.formId || '';
|
|
1970
1882
|
const formTableId = formInfo?.formTableId || '';
|
|
1971
1883
|
if (!formId) {
|
|
@@ -1994,13 +1906,33 @@ export function registerFormCommands(program) {
|
|
|
1994
1906
|
console.error('--options 必须是非空数组');
|
|
1995
1907
|
process.exit(1);
|
|
1996
1908
|
}
|
|
1997
|
-
//
|
|
1998
|
-
|
|
1909
|
+
// 读**现状**(title / dataKey / type / labelObj …)—— 这是「读现状 → 改 → 写回」的底。
|
|
1910
|
+
//
|
|
1911
|
+
// ⚠ 这里**不许降级成 `{}`**。以前读失败只在 catch 里吞掉、`currentField` 变成空对象,
|
|
1912
|
+
// 于是 title 写成空串,平台回「字段标题不能为空」—— 报的是标题,真因是**读没成功**,
|
|
1913
|
+
// 用户怎么去补 title 都没用。宁可中止:拿不到现状就写,等于拿一份空对象覆盖整个字段。
|
|
1914
|
+
let currentField = null;
|
|
1915
|
+
let readErr = '';
|
|
1999
1916
|
try {
|
|
2000
1917
|
const fData = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormField`, { module: 'ebuilderform', formTableField: { formField: { id: fieldId, form: { id: formId } } } }, { ebbusinessid: formId });
|
|
2001
|
-
|
|
1918
|
+
if (fData?.code === 200 && fData?.status !== false && fData?.data)
|
|
1919
|
+
currentField = fData.data;
|
|
1920
|
+
else
|
|
1921
|
+
readErr = fData?.msg || `code=${fData?.code}`;
|
|
1922
|
+
}
|
|
1923
|
+
catch (e) {
|
|
1924
|
+
readErr = e.message;
|
|
1925
|
+
}
|
|
1926
|
+
if (!currentField || !Object.keys(currentField).length) {
|
|
1927
|
+
console.error(`读取字段现状失败(${readErr || '接口未返回字段'}),已中止、未改动任何东西。`);
|
|
1928
|
+
console.error(' 本命令是「读现状 → 改 → 写回」,读不到现状就写会把字段的其余属性清空。');
|
|
1929
|
+
process.exit(1);
|
|
1930
|
+
}
|
|
1931
|
+
if (!String(currentField.title ?? '').trim()) {
|
|
1932
|
+
console.error(`读回的字段没有 title(fieldId=${fieldId}),已中止、未改动任何东西。`);
|
|
1933
|
+
console.error(' 标题在平台里存的是多语言标签 id(labelObj.title);请先用 `form read` 确认这个字段的状态。');
|
|
1934
|
+
process.exit(1);
|
|
2002
1935
|
}
|
|
2003
|
-
catch { /* best effort */ }
|
|
2004
1936
|
// Build options with defOption
|
|
2005
1937
|
const { randomUUID } = await import('crypto');
|
|
2006
1938
|
// valueKey=用户自定义选项值,必须是纯数字(平台规范+下游引用按数字处理),非数字直接报错;
|
|
@@ -2052,7 +1984,7 @@ export function registerFormCommands(program) {
|
|
|
2052
1984
|
hideScore: true,
|
|
2053
1985
|
groupId: currentField.groupId || '-1',
|
|
2054
1986
|
hasDisplayFormat: currentField.hasDisplayFormat ?? false,
|
|
2055
|
-
title: currentField.title
|
|
1987
|
+
title: currentField.title,
|
|
2056
1988
|
dataCount: currentField.dataCount || 1,
|
|
2057
1989
|
dataKey: currentField.dataKey || '',
|
|
2058
1990
|
systemField: currentField.systemField ?? true,
|
|
@@ -2130,8 +2062,7 @@ export function registerFormCommands(program) {
|
|
|
2130
2062
|
const client = new ApiClient(auth);
|
|
2131
2063
|
const baseUrl = auth.getBaseUrl();
|
|
2132
2064
|
// Get formId from objId
|
|
2133
|
-
const
|
|
2134
|
-
const formInfo = (formList?.data || []).find((f) => f.id === objId);
|
|
2065
|
+
const formInfo = await resolveFormRecord(client, baseUrl, appId, objId);
|
|
2135
2066
|
const formId = formInfo?.formId || '';
|
|
2136
2067
|
const formTableId = formInfo?.formTableId || '';
|
|
2137
2068
|
if (!formId) {
|
|
@@ -2160,402 +2091,16 @@ export function registerFormCommands(program) {
|
|
|
2160
2091
|
columnName: cmdOpts.columnName,
|
|
2161
2092
|
});
|
|
2162
2093
|
const colName = keys.dataKey; // 布局 comp 等仍按字段编码引用
|
|
2163
|
-
//
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
columnName: keys.columnName,
|
|
2171
|
-
|
|
2172
|
-
// 明细表字段:--sub-form-id 时 jsonObj 带 subFormId —— **字段落点由它决定**。
|
|
2173
|
-
// 必须用 resolveDetailTable 而不是直接查 getEbField:后者列不出空明细表,
|
|
2174
|
-
// 用户把明细字段删光后就再也加不回去(详见 services/form-sub-detail.ts)。
|
|
2175
|
-
if (cmdOpts.subFormId) {
|
|
2176
|
-
const sub = await resolveDetailTable(client, baseUrl, appId, objId, formId, cmdOpts.subFormId);
|
|
2177
|
-
if (!sub) {
|
|
2178
|
-
console.error(`未找到明细表 subFormId=${cmdOpts.subFormId}(getEbField 与 querySubForms 均无此表,请确认 id)`);
|
|
2179
|
-
process.exit(1);
|
|
2180
|
-
}
|
|
2181
|
-
Object.assign(jsonObj, { subFormId: cmdOpts.subFormId });
|
|
2182
|
-
// 空表拿不到物理表 id,保持主表的值不动:实测带主表 formTableId 一样落进明细表。
|
|
2183
|
-
if (sub.formTableId)
|
|
2184
|
-
jsonObj.formTableId = sub.formTableId;
|
|
2185
|
-
}
|
|
2186
|
-
// 富组件:映射到平台真实 type/componentKey + 附加配置(对齐 batch/save/formField 抓包)。
|
|
2187
|
-
// 之前 jsonObj 只有 type 没有 componentKey → 平台 FormFieldDto.getComponentKey() 为 null → NPE。
|
|
2188
|
-
const rich = getRichType(fieldType);
|
|
2189
|
-
if (rich) {
|
|
2190
|
-
jsonObj.type = rich.platformType;
|
|
2191
|
-
jsonObj.componentKey = rich.platformComponentKey;
|
|
2192
|
-
if (rich.build) {
|
|
2193
|
-
const extra = richIdUnits(fieldType, {}) - 1;
|
|
2194
|
-
let extraIds = [];
|
|
2195
|
-
if (extra > 0) {
|
|
2196
|
-
const extraRes = await client.post(`${baseUrl}/api/form/local/generatorIds?size=${extra}&apid=${appId}`, {});
|
|
2197
|
-
extraIds = (extraRes?.data?.generatorIds || []).map(String);
|
|
2198
|
-
}
|
|
2199
|
-
const extraCfg = await rich.build({
|
|
2200
|
-
def: {
|
|
2201
|
-
multiple: cmdOpts.multiple,
|
|
2202
|
-
browserType: cmdOpts.browserType,
|
|
2203
|
-
browserFormSource: cmdOpts.browserFormSource,
|
|
2204
|
-
}, fieldId, formId, formTableId, dataKey: keys.dataKey, columnName: keys.columnName,
|
|
2205
|
-
showOrder: '0.00', extraIds,
|
|
2206
|
-
});
|
|
2207
|
-
Object.assign(jsonObj, extraCfg);
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
// 非富组件也补 componentKey(平台要求,否则 FormFieldDto.getComponentKey() 为 null → NPE)。
|
|
2211
|
-
// 富组件已在上方设置 platformComponentKey;此处 fallback 到类型名(平台对常见类型有归一容错)。
|
|
2212
|
-
if (!jsonObj.componentKey)
|
|
2213
|
-
jsonObj.componentKey = fieldType;
|
|
2214
|
-
// Number/Money decimal precision: --precision 指定,缺省 Number=0 整数、Money=2、百分比=1
|
|
2215
|
-
// 百分比:--percent → numberType=percentage;默认值:--default → content+isDefault
|
|
2216
|
-
// 实测:设默认值必须连 isDelimiter/stepMode/step/isCapital 一起,否则平台丢默认(只设 content+isDefault 不落库)
|
|
2217
|
-
if (fieldType === 'Number' || fieldType === 'Money') {
|
|
2218
|
-
const defaultPrec = fieldType === 'Money' ? '2' : '0';
|
|
2219
|
-
const isPercent = fieldType === 'Number' && cmdOpts.percent === true;
|
|
2220
|
-
// 注意:commander 把 --default 映射到 opts.default(非 defaultValue)
|
|
2221
|
-
const hasDefault = cmdOpts.default !== undefined && cmdOpts.default !== null && cmdOpts.default !== '';
|
|
2222
|
-
// 千分位:Money 默认开(--no-delimiter 关闭)、Number 默认关(--delimiter 开启)
|
|
2223
|
-
const isDelim = fieldType === 'Money' ? cmdOpts.delimiter !== false : cmdOpts.delimiter === true;
|
|
2224
|
-
const isCap = cmdOpts.capital === true;
|
|
2225
|
-
const patch = {
|
|
2226
|
-
pointSize: cmdOpts.precision !== undefined ? cmdOpts.precision : (isPercent ? '1' : defaultPrec),
|
|
2227
|
-
isDefault: hasDefault,
|
|
2228
|
-
isDelimiter: isDelim,
|
|
2229
|
-
isCapital: isCap,
|
|
2230
|
-
capitalFormat: 'onlyUpperCase',
|
|
2231
|
-
capitalPosition: 'outside',
|
|
2232
|
-
capitalSetting: 'system',
|
|
2233
|
-
capitalPrecision: 'yuan',
|
|
2234
|
-
isExchangeRates: false,
|
|
2235
|
-
};
|
|
2236
|
-
if (fieldType === 'Number')
|
|
2237
|
-
patch.numberType = isPercent ? 'percentage' : 'number';
|
|
2238
|
-
// content 始终带(无默认值时空串)——浏览器抓包 Number 字段 getFormField 恒有 content:"",
|
|
2239
|
-
// 缺 content 在 FLOW 渲染异常(与多行文本同因)。
|
|
2240
|
-
if (fieldType === 'Number')
|
|
2241
|
-
patch.content = hasDefault ? String(cmdOpts.default) : '';
|
|
2242
|
-
if (hasDefault) {
|
|
2243
|
-
patch.stepMode = false;
|
|
2244
|
-
patch.step = 1;
|
|
2245
|
-
}
|
|
2246
|
-
Object.assign(jsonObj, patch);
|
|
2247
|
-
}
|
|
2248
|
-
if (fieldType === 'TextArea') {
|
|
2249
|
-
// 多行文本:对齐浏览器抓包——content 恒 ""(无默认值时空串)、maxLen=4000。
|
|
2250
|
-
// 缺 content 在 FLOW 渲染异常;maxLen 不发则前端最大长度回显异常。
|
|
2251
|
-
Object.assign(jsonObj, { content: '', maxLen: 4000 });
|
|
2252
|
-
}
|
|
2253
|
-
if (fieldType === 'EInvoice') {
|
|
2254
|
-
Object.assign(jsonObj, {
|
|
2255
|
-
isSingle: false,
|
|
2256
|
-
invoiceType: 1,
|
|
2257
|
-
});
|
|
2258
|
-
delete jsonObj.maxLen;
|
|
2259
|
-
delete jsonObj.unique;
|
|
2260
|
-
delete jsonObj.scan;
|
|
2261
|
-
delete jsonObj.readonlyTransLink;
|
|
2262
|
-
}
|
|
2263
|
-
else if (fieldType === 'Date' || fieldType === 'DateTime') {
|
|
2264
|
-
// 日期:默认精度 年月日(yyyy-MM-dd,对齐浏览器抓包);DateTime:默认带时:分(yyyy-MM-dd HH:mm 不含秒)。
|
|
2265
|
-
// 默认值不填(isDefault:false);--format 覆盖精度。
|
|
2266
|
-
const isDt = fieldType === 'DateTime';
|
|
2267
|
-
const fmt = cmdOpts.format || (isDt ? 'yyyy-MM-dd HH:mm' : 'yyyy-MM-dd');
|
|
2268
|
-
Object.assign(jsonObj, {
|
|
2269
|
-
type: 'Date',
|
|
2270
|
-
format: fmt,
|
|
2271
|
-
placeholder: placeholderFromFormat(fmt),
|
|
2272
|
-
timePanelStyle: { type: 'system' },
|
|
2273
|
-
isDefault: false,
|
|
2274
|
-
});
|
|
2275
|
-
// 日期限制:--min-field 最早不得早于 / --max-field 最晚不得晚于另一字段(对齐抓包 min.dateField)。
|
|
2276
|
-
// 目标字段 fieldId 从 getFields mainFields 按字段名解析。
|
|
2277
|
-
if (cmdOpts.minField || cmdOpts.maxField) {
|
|
2278
|
-
try {
|
|
2279
|
-
const tfd = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
2280
|
-
objId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
2281
|
-
});
|
|
2282
|
-
const tfields = tfd?.data?.mainFields || [];
|
|
2283
|
-
const minTarget = cmdOpts.minField ? tfields.find((f) => f.fieldName === cmdOpts.minField)?.fieldId : '';
|
|
2284
|
-
const maxTarget = cmdOpts.maxField ? tfields.find((f) => f.fieldName === cmdOpts.maxField)?.fieldId : '';
|
|
2285
|
-
if (minTarget) {
|
|
2286
|
-
jsonObj.min = { type: 'dateField', checkValue: String(minTarget) };
|
|
2287
|
-
jsonObj.earlyDate = true;
|
|
2288
|
-
}
|
|
2289
|
-
if (maxTarget) {
|
|
2290
|
-
jsonObj.max = { type: 'dateField', checkValue: String(maxTarget) };
|
|
2291
|
-
jsonObj.lateDate = true;
|
|
2292
|
-
}
|
|
2293
|
-
}
|
|
2294
|
-
catch { /* 解析失败则不加限制 */ }
|
|
2295
|
-
}
|
|
2296
|
-
delete jsonObj.maxLen;
|
|
2297
|
-
delete jsonObj.unique;
|
|
2298
|
-
delete jsonObj.scan;
|
|
2299
|
-
delete jsonObj.readonlyTransLink;
|
|
2300
|
-
}
|
|
2301
|
-
else if (fieldType === 'SubCompany') {
|
|
2302
|
-
Object.assign(jsonObj, {
|
|
2303
|
-
componentKey: 'Subcompany', isSingle: cmdOpts.multiple ? false : true,
|
|
2304
|
-
displayType: 'space', defaultExtend: { type: 'custom', relation: 'current' },
|
|
2305
|
-
viewDataDetail: true, showDimension: true,
|
|
2306
|
-
pcDisplaySelectedCount: { type: 'default' },
|
|
2307
|
-
mobileDisplaySelectedCount: { type: 'default' },
|
|
2308
|
-
});
|
|
2309
|
-
delete jsonObj.maxLen;
|
|
2310
|
-
delete jsonObj.unique;
|
|
2311
|
-
delete jsonObj.scan;
|
|
2312
|
-
delete jsonObj.readonlyTransLink;
|
|
2313
|
-
}
|
|
2314
|
-
else if (fieldType === 'City') {
|
|
2315
|
-
// 城市/地区选择(对齐浏览器 payload:region 模式 + 系统级联层级 + 字母索引)
|
|
2316
|
-
Object.assign(jsonObj, {
|
|
2317
|
-
isSingle: false,
|
|
2318
|
-
levelSetting: 'system',
|
|
2319
|
-
isDefault: false,
|
|
2320
|
-
useLetterIndex: true,
|
|
2321
|
-
mode: 'region',
|
|
2322
|
-
});
|
|
2323
|
-
delete jsonObj.maxLen;
|
|
2324
|
-
delete jsonObj.unique;
|
|
2325
|
-
delete jsonObj.scan;
|
|
2326
|
-
delete jsonObj.readonlyTransLink;
|
|
2327
|
-
}
|
|
2328
|
-
else if (fieldType === 'EBuilder') {
|
|
2329
|
-
if (!sformId) {
|
|
2330
|
-
console.error('EBuilder 类型必须指定 --sform-id(目标表单 objId)');
|
|
2331
|
-
process.exit(1);
|
|
2332
|
-
}
|
|
2333
|
-
// Resolve formName from target form if not provided
|
|
2334
|
-
let resolvedFormName = formName;
|
|
2335
|
-
if (!resolvedFormName) {
|
|
2336
|
-
try {
|
|
2337
|
-
const tlist = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
2338
|
-
const tf = (tlist?.data || []).find((f) => String(f.id) === String(sformId));
|
|
2339
|
-
resolvedFormName = tf?.formName || tf?.name || sformId;
|
|
2340
|
-
}
|
|
2341
|
-
catch {
|
|
2342
|
-
resolvedFormName = sformId;
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2345
|
-
// Resolve dataTitle:纯数字 fieldId 直接用;'name'/字段名 → 查目标表字段。
|
|
2346
|
-
// 未传 --data-title 时按 name 处理:默认优先字段名含「名称」的业务字段(如 客户名称/类型名称),无则第一个业务字段。
|
|
2347
|
-
let resolvedDataTitle = dataTitle || 'name';
|
|
2348
|
-
if (!/^\d{15,}$/.test(resolvedDataTitle)) {
|
|
2349
|
-
try {
|
|
2350
|
-
const tfd = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
2351
|
-
objId: sformId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
2352
|
-
});
|
|
2353
|
-
const tfields = tfd?.data?.mainFields || [];
|
|
2354
|
-
if (resolvedDataTitle === 'name') {
|
|
2355
|
-
resolvedDataTitle = defaultEBuilderDataTitle(tfields);
|
|
2356
|
-
}
|
|
2357
|
-
else {
|
|
2358
|
-
const m = tfields.find((f) => f.fieldName === resolvedDataTitle)
|
|
2359
|
-
|| tfields.find((f) => String(f.fieldName || '').includes(resolvedDataTitle));
|
|
2360
|
-
resolvedDataTitle = m?.fieldId || resolvedDataTitle;
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
|
-
catch { /* 解析失败保留原值 */ }
|
|
2364
|
-
}
|
|
2365
|
-
Object.assign(jsonObj, {
|
|
2366
|
-
sformId,
|
|
2367
|
-
dataTitle: resolvedDataTitle || 'name',
|
|
2368
|
-
formName: resolvedFormName,
|
|
2369
|
-
virtualForm: false,
|
|
2370
|
-
displayType: 'line',
|
|
2371
|
-
isAddForRelation: true,
|
|
2372
|
-
isSingle: cmdOpts.multiple ? false : true, // 默认单选;--multiple 多选
|
|
2373
|
-
viewDataDetail: true,
|
|
2374
|
-
pcDisplaySelectedCount: { type: 'default' },
|
|
2375
|
-
mobileDisplaySelectedCount: { type: 'default' },
|
|
2376
|
-
quickAddParams: [],
|
|
2377
|
-
relateViewData: [],
|
|
2378
|
-
relateField: null,
|
|
2379
|
-
appId,
|
|
2380
|
-
virtualTable: false,
|
|
2381
|
-
});
|
|
2382
|
-
}
|
|
2383
|
-
else if (fieldType === 'Flow') {
|
|
2384
|
-
// 浏览-流程字段(对齐浏览器 payload:type=Flow + relateExt.module=workflow)
|
|
2385
|
-
if (!sformId) {
|
|
2386
|
-
console.error('Flow 类型必须指定 --sform-id(目标流程绑定的表单 objId)');
|
|
2387
|
-
process.exit(1);
|
|
2388
|
-
}
|
|
2389
|
-
let resolvedFormName = formName;
|
|
2390
|
-
if (!resolvedFormName) {
|
|
2391
|
-
try {
|
|
2392
|
-
const tlist = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
2393
|
-
const tf = (tlist?.data || []).find((f) => String(f.id) === String(sformId));
|
|
2394
|
-
resolvedFormName = tf?.formName || tf?.name || sformId;
|
|
2395
|
-
}
|
|
2396
|
-
catch {
|
|
2397
|
-
resolvedFormName = sformId;
|
|
2398
|
-
}
|
|
2399
|
-
}
|
|
2400
|
-
Object.assign(jsonObj, {
|
|
2401
|
-
isAddForRelation: true,
|
|
2402
|
-
sformId,
|
|
2403
|
-
formName: resolvedFormName,
|
|
2404
|
-
conditionOperate: 'and',
|
|
2405
|
-
filterParams: '{}',
|
|
2406
|
-
filterQueryParams: '{}',
|
|
2407
|
-
columnFields: [],
|
|
2408
|
-
displayType: 'line',
|
|
2409
|
-
viewDataDetail: true,
|
|
2410
|
-
pcDisplaySelectedCount: { type: 'default' },
|
|
2411
|
-
mobileDisplaySelectedCount: { type: 'default' },
|
|
2412
|
-
relateExt: { module: 'workflow' },
|
|
2413
|
-
relateField: null,
|
|
2414
|
-
quickAddParams: [],
|
|
2415
|
-
});
|
|
2416
|
-
}
|
|
2417
|
-
else if (isDateRange) {
|
|
2418
|
-
// 日期区间:补 start/end 两个 DateComponent + rangeInterval(对齐浏览器 payload)
|
|
2419
|
-
const format = cmdOpts.format || 'yyyy-MM-dd';
|
|
2420
|
-
const rangeTitle = title || fieldName;
|
|
2421
|
-
const start = {
|
|
2422
|
-
componentKey: 'DateComponent', content: '', describe: '', earlyDate: 'false',
|
|
2423
|
-
format, isDefault: 'false', lateDate: 'false',
|
|
2424
|
-
placeholder: placeholderFromFormat(format),
|
|
2425
|
-
position: '', relateMultiple: 'false', required: 'false', size: 'medium',
|
|
2426
|
-
title: `${rangeTitle}(开始时间)`, fieldId: startId,
|
|
2427
|
-
min: { type: 'currentHour' }, max: { type: 'currentHour' },
|
|
2428
|
-
};
|
|
2429
|
-
const end = { ...start, title: `${rangeTitle}(结束时间)`, fieldId: endId };
|
|
2430
|
-
Object.assign(jsonObj, {
|
|
2431
|
-
isDefault: false,
|
|
2432
|
-
start, end,
|
|
2433
|
-
hasDisplayFormat: true,
|
|
2434
|
-
rangeInterval: { limitType: 'max', timeUnit: 'day' },
|
|
2435
|
-
});
|
|
2436
|
-
}
|
|
2437
|
-
else if (fieldType === 'IDCard') {
|
|
2438
|
-
// 身份证号:平台原生简单字段,浏览器抓包 payload 极简(type/title/formTableId/dataKey/
|
|
2439
|
-
// columnName/fieldId/formId/showOrder/groupId 即全部)——无 componentKey、无文本长度配置。
|
|
2440
|
-
delete jsonObj.componentKey;
|
|
2441
|
-
delete jsonObj.maxLen;
|
|
2442
|
-
delete jsonObj.unique;
|
|
2443
|
-
delete jsonObj.scan;
|
|
2444
|
-
delete jsonObj.readonlyTransLink;
|
|
2445
|
-
}
|
|
2446
|
-
else {
|
|
2447
|
-
// 文本最大长度:--max-len 指定,缺省 Text=100、TextArea=4000(对齐浏览器抓包;TextArea 在上面分支已设 content:"")
|
|
2448
|
-
const defaultMaxLen = fieldType === 'TextArea' ? 4000 : 100;
|
|
2449
|
-
const maxLen = cmdOpts.maxLen !== undefined ? Number(cmdOpts.maxLen) : defaultMaxLen;
|
|
2450
|
-
Object.assign(jsonObj, {
|
|
2451
|
-
maxLen: Number.isFinite(maxLen) && maxLen > 0 ? maxLen : defaultMaxLen,
|
|
2452
|
-
unique: false, scan: false,
|
|
2453
|
-
readonlyTransLink: false,
|
|
2454
|
-
});
|
|
2455
|
-
}
|
|
2456
|
-
// 人员/部门多选:默认单选,--multiple 改多选(isSingle:false)。EBuilder 已在上文分支设过。
|
|
2457
|
-
if (fieldType === 'Employee' || fieldType === 'Department') {
|
|
2458
|
-
jsonObj.isSingle = cmdOpts.multiple ? false : true;
|
|
2459
|
-
}
|
|
2460
|
-
// 下拉多选是独立类型 SelectMultiple(非 Select + 开关)。浏览器抓包:type:"SelectMultiple",
|
|
2461
|
-
// 无 componentKey/maxLen/unique/scan(else 分支给 Select 加的 junk 要清掉),options 单独配。
|
|
2462
|
-
if (fieldType === 'Select' && cmdOpts.multiple) {
|
|
2463
|
-
jsonObj.type = 'SelectMultiple';
|
|
2464
|
-
jsonObj.componentKey = 'SelectMultiple';
|
|
2465
|
-
delete jsonObj.maxLen;
|
|
2466
|
-
delete jsonObj.unique;
|
|
2467
|
-
delete jsonObj.scan;
|
|
2468
|
-
delete jsonObj.readonlyTransLink;
|
|
2469
|
-
}
|
|
2470
|
-
// Select 绑定选项预设模板:--option-template(ID 或名称)。
|
|
2471
|
-
// 浏览器抓包(2026-09-09):jsonObj 带 templateId+templateName+publicTemplate:false+options:[](选项不内联),
|
|
2472
|
-
// 平台渲染时按模板拉选项。模板管理见 `form option-template`。
|
|
2473
|
-
if (fieldType === 'Select' && cmdOpts.optionTemplate) {
|
|
2474
|
-
// 先清 else 分支给文本类加的 junk + options 占位(模板字段不发选项)
|
|
2475
|
-
delete jsonObj.maxLen;
|
|
2476
|
-
delete jsonObj.unique;
|
|
2477
|
-
delete jsonObj.scan;
|
|
2478
|
-
delete jsonObj.readonlyTransLink;
|
|
2479
|
-
const tplIdOrName = cmdOpts.optionTemplate;
|
|
2480
|
-
// 解析模板:纯数字按 ID 直查详情;否则在 custom+system 列表按名称精确匹配
|
|
2481
|
-
let tplId = '';
|
|
2482
|
-
let tplName = '';
|
|
2483
|
-
const readTpl = async (id) => {
|
|
2484
|
-
const r = await client.post(`${baseUrl}/api/form/datasources/queryDataSourcesByTemplateValueKey`, { templateId: id });
|
|
2485
|
-
return r?.formDataTemplate?.id ? r.formDataTemplate : null;
|
|
2486
|
-
};
|
|
2487
|
-
if (/^\d+$/.test(tplIdOrName)) {
|
|
2488
|
-
const t = await readTpl(tplIdOrName);
|
|
2489
|
-
if (!t) {
|
|
2490
|
-
console.error(`选项模板 ${tplIdOrName} 不存在`);
|
|
2491
|
-
process.exit(1);
|
|
2492
|
-
}
|
|
2493
|
-
tplId = String(t.id);
|
|
2494
|
-
tplName = String(t.name);
|
|
2495
|
-
}
|
|
2496
|
-
else {
|
|
2497
|
-
for (const type of ['custom', 'system']) {
|
|
2498
|
-
const list = await client.post(`${baseUrl}/api/form/datasources/queryDataList`, { pageNo: 1, pageSize: 100, name: tplIdOrName, type });
|
|
2499
|
-
const hit = (list?.templatePage?.result || []).find((r) => r.name === tplIdOrName);
|
|
2500
|
-
if (hit) {
|
|
2501
|
-
tplId = String(hit.id);
|
|
2502
|
-
tplName = String(hit.name);
|
|
2503
|
-
break;
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
if (!tplId) {
|
|
2507
|
-
console.error(`未找到名为「${tplIdOrName}」的选项模板(custom+system 均已查)`);
|
|
2508
|
-
process.exit(1);
|
|
2509
|
-
}
|
|
2510
|
-
}
|
|
2511
|
-
Object.assign(jsonObj, {
|
|
2512
|
-
hideScore: true,
|
|
2513
|
-
optionBackgroundStyle: 'default',
|
|
2514
|
-
options: [],
|
|
2515
|
-
templateScore: {},
|
|
2516
|
-
currentLevel: '1',
|
|
2517
|
-
templateId: tplId,
|
|
2518
|
-
templateName: tplName,
|
|
2519
|
-
publicTemplate: false,
|
|
2520
|
-
parentId: [],
|
|
2521
|
-
});
|
|
2522
|
-
if (cmdOpts.multiple) {
|
|
2523
|
-
console.error('提示:模板下拉当前按单选(currentLevel=1)绑定;多选+模板组合未验证');
|
|
2524
|
-
}
|
|
2525
|
-
}
|
|
2526
|
-
// 默认值 / 数据库字段名 / 描述:与 edit-field 共用同一套补丁规则。
|
|
2527
|
-
// 放在最后 —— 前面各类型分支会写 content(如 TextArea 恒置 ''),先打补丁会被冲掉。
|
|
2528
|
-
const patch = applyFieldPatches(jsonObj, {
|
|
2529
|
-
defaultValue: cmdOpts.default,
|
|
2530
|
-
dataKey: cmdOpts.dataKey,
|
|
2531
|
-
columnName: cmdOpts.columnName,
|
|
2532
|
-
desc: cmdOpts.desc,
|
|
2094
|
+
// 字段 jsonObj 的构造抽到 services/form-field-build.ts —— 与「按物理表加字段」共用同一份。
|
|
2095
|
+
// 原先这约 340 行内联在这里,多一个入口就会变成两处真相。
|
|
2096
|
+
const jsonObj = await buildFieldJsonObject({
|
|
2097
|
+
client, baseUrl, appId, objId,
|
|
2098
|
+
userId: auth.userId, tenantKey: auth.tenantKey,
|
|
2099
|
+
fieldType, fieldName, title, formId, formTableId,
|
|
2100
|
+
fieldId, startId, endId,
|
|
2101
|
+
dataKey: keys.dataKey, columnName: keys.columnName,
|
|
2102
|
+
opts: cmdOpts,
|
|
2533
2103
|
});
|
|
2534
|
-
if (patch.errors.length > 0) {
|
|
2535
|
-
for (const e of patch.errors)
|
|
2536
|
-
console.error(` ❌ ${e}`);
|
|
2537
|
-
console.error(' 已中止,未做任何改动。');
|
|
2538
|
-
process.exit(1);
|
|
2539
|
-
}
|
|
2540
|
-
for (const w of patch.warnings)
|
|
2541
|
-
console.error(` ⚠ ${w}`);
|
|
2542
|
-
// 必填**只存在于布局 comp 里**(`config.required`)—— 字段记录没有这个键。所以下面那句
|
|
2543
|
-
// 只是形式上的补齐,真正生效的是布局落点:增量插入的 `overrides.required`,
|
|
2544
|
-
// 或 --reinit 时 configs 里那条 newFieldConfig。两处都写全了用户才看不出差别。
|
|
2545
|
-
if (cmdOpts.required === true)
|
|
2546
|
-
jsonObj.required = true;
|
|
2547
|
-
// 字段标题在平台里存的是**多语言标签 id**(字段记录的 `labelObj.title`),不是文本本身。
|
|
2548
|
-
// 不建标签也能显示(设计器退回读 `title`),但只要有谁引用了标签而这个标签不存在,
|
|
2549
|
-
// 标题就会**静默变空** —— 平台既不校验也不报错。所以顺手建一个真的,不留自造 id 的口子。
|
|
2550
|
-
// 建失败只告警:字段本身才是主操作,不该被标签服务拖垮。
|
|
2551
|
-
try {
|
|
2552
|
-
const titleLabelId = await createMultilangLabel(client, baseUrl, title || fieldName, 'ebuilderform');
|
|
2553
|
-
jsonObj.labelObj = { ...(jsonObj.labelObj || {}), title: titleLabelId };
|
|
2554
|
-
jsonObj.titleLabelId = titleLabelId;
|
|
2555
|
-
}
|
|
2556
|
-
catch (e) {
|
|
2557
|
-
console.error(` ⚠ 标题的多语言标签未建立(字段不受影响,设计器会退回用 title 显示): ${e.message}`);
|
|
2558
|
-
}
|
|
2559
2104
|
const payload = {
|
|
2560
2105
|
formId, module: 'ebuilderform',
|
|
2561
2106
|
customParam: { appId, objId },
|
|
@@ -2585,11 +2130,13 @@ export function registerFormCommands(program) {
|
|
|
2585
2130
|
// 把错误讲清楚,让用户自己决定要不要 --reinit。
|
|
2586
2131
|
if (!cmdOpts.reinit) {
|
|
2587
2132
|
try {
|
|
2588
|
-
|
|
2133
|
+
// ⚠ 明细字段**必须带 subFormId** 才查得到(不带只返主表字段)——
|
|
2134
|
+
// 少了它,刚建好的明细字段会「查不到」,报成「读回字段定义失败」。
|
|
2135
|
+
const fieldDef = (await fetchFieldPageRecords(client, baseUrl, appId, formId, cmdOpts.subFormId))
|
|
2589
2136
|
.find((f) => String(f.fieldId) === String(fieldId));
|
|
2590
2137
|
if (!fieldDef)
|
|
2591
2138
|
throw new Error('字段已建立,但读回字段定义失败');
|
|
2592
|
-
const
|
|
2139
|
+
const insertArgs = {
|
|
2593
2140
|
fieldId, labelFieldId, title: title || fieldName, fieldDef,
|
|
2594
2141
|
subFormId: cmdOpts.subFormId,
|
|
2595
2142
|
// 对齐 initLayout 那条路对新字段的默认:可编辑、显示。
|
|
@@ -2599,12 +2146,38 @@ export function registerFormCommands(program) {
|
|
|
2599
2146
|
required: cmdOpts.required === true,
|
|
2600
2147
|
...(cmdOpts.singleRow === true ? { singleRow: true } : {}),
|
|
2601
2148
|
},
|
|
2602
|
-
}
|
|
2149
|
+
};
|
|
2150
|
+
let placed;
|
|
2151
|
+
try {
|
|
2152
|
+
placed = await insertFieldIntoLayout(client, baseUrl, formId, insertArgs);
|
|
2153
|
+
}
|
|
2154
|
+
catch (e) {
|
|
2155
|
+
// 本表还没有这个类型 → 去要一份**平台按该字段生成的模板**再试一次。
|
|
2156
|
+
// 只在真缺模板时才多打这一次接口,正常路径零开销。
|
|
2157
|
+
if (e?.code !== NO_FIELD_TYPE_TEMPLATE)
|
|
2158
|
+
throw e;
|
|
2159
|
+
const templates = await fetchFieldTypeTemplates(client, baseUrl, appId, formId);
|
|
2160
|
+
// 主表字段按 fieldId 命中;明细表字段拿不到(接口不返回明细),
|
|
2161
|
+
// 退而借**主表同类型**的那一份 —— 模板是按类型而不是按作用域的
|
|
2162
|
+
// (`config.flow` 是空对象、没有 `excel`),作用域由插入方自己写。
|
|
2163
|
+
const tpl = templates.get(String(fieldId))?.comp
|
|
2164
|
+
?? findTemplateByType(templates, String(fieldDef.componentKey ?? fieldType))?.comp;
|
|
2165
|
+
if (!tpl)
|
|
2166
|
+
throw e;
|
|
2167
|
+
console.error(' 本表还没有这个类型,改用平台给的字段模板合成组件...');
|
|
2168
|
+
placed = await insertFieldIntoLayout(client, baseUrl, formId, { ...insertArgs, fieldTypeTemplate: tpl });
|
|
2169
|
+
}
|
|
2603
2170
|
console.error(` 布局已更新: excel[${placed.row},${placed.col}](增量插入,未重建)`);
|
|
2171
|
+
// 网格补格这类「改成功了但不干净」的情况要让人看见,不能闷掉。
|
|
2172
|
+
for (const w of placed.warnings)
|
|
2173
|
+
console.error(` ⚠ ${w}`);
|
|
2604
2174
|
}
|
|
2605
2175
|
catch (e) {
|
|
2606
2176
|
console.error(` 布局更新失败: ${e.message}`);
|
|
2607
|
-
|
|
2177
|
+
// ⚠ 别写「重跑不会重复建字段」——字段已经建出来了,重跑这条命令会再次去建同一个
|
|
2178
|
+
// 字段,撞库列重名而失败。要说清是「先删掉再重跑」。
|
|
2179
|
+
console.error(' 字段本身已建立,但没进布局。要重试请先 `form delete-field` 删掉这个字段再跑,');
|
|
2180
|
+
console.error(' 或直接按上面那条修好布局(字段不用重建)。');
|
|
2608
2181
|
}
|
|
2609
2182
|
}
|
|
2610
2183
|
else {
|
|
@@ -2729,8 +2302,11 @@ export function registerFormCommands(program) {
|
|
|
2729
2302
|
// Get formId — try getList first, fall back to searching by fieldId
|
|
2730
2303
|
let formId = '';
|
|
2731
2304
|
let resolvedObjId = objId;
|
|
2305
|
+
// 这份列表下面还要再用一次(formId → objId 的反查),所以保留不动;
|
|
2306
|
+
// formInfo 走共用解析 —— obj/getList 命不中时按 id 直取,覆盖三方表单。
|
|
2732
2307
|
const formList = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
2733
|
-
const formInfo = (formList?.data || []).find((f) => String(f.id) === String(objId))
|
|
2308
|
+
const formInfo = (formList?.data || []).find((f) => String(f.id) === String(objId))
|
|
2309
|
+
|| await resolveFormRecord(client, baseUrl, appId, objId);
|
|
2734
2310
|
formId = formInfo?.formId || '';
|
|
2735
2311
|
// If not found in getList (e.g. workflow sub-table objId), search by fieldId
|
|
2736
2312
|
if (!formId) {
|
|
@@ -2747,6 +2323,12 @@ export function registerFormCommands(program) {
|
|
|
2747
2323
|
const matchForm = (formList?.data || []).find((f) => f.formId === formId);
|
|
2748
2324
|
if (matchForm)
|
|
2749
2325
|
resolvedObjId = matchForm.id;
|
|
2326
|
+
else {
|
|
2327
|
+
// 三方表单不在 getList 里 —— 按 formId 反查补一次
|
|
2328
|
+
const byFormId = await resolveObjIdByFormId(client, baseUrl, appId, formId);
|
|
2329
|
+
if (byFormId)
|
|
2330
|
+
resolvedObjId = byFormId;
|
|
2331
|
+
}
|
|
2750
2332
|
}
|
|
2751
2333
|
}
|
|
2752
2334
|
if (!formId) {
|
|
@@ -2963,8 +2545,7 @@ export function registerFormCommands(program) {
|
|
|
2963
2545
|
let formName = editOpts.formName || fallbackFormName || '';
|
|
2964
2546
|
if (!formName) {
|
|
2965
2547
|
try {
|
|
2966
|
-
const
|
|
2967
|
-
const tf = (tlist?.data || []).find((f) => String(f.id) === String(sformId));
|
|
2548
|
+
const tf = await resolveFormRecord(client, baseUrl, appId, sformId);
|
|
2968
2549
|
formName = tf?.formName || tf?.name || sformId;
|
|
2969
2550
|
}
|
|
2970
2551
|
catch {
|
|
@@ -3160,8 +2741,7 @@ export function registerFormCommands(program) {
|
|
|
3160
2741
|
process.exit(1);
|
|
3161
2742
|
const client = new ApiClient(auth);
|
|
3162
2743
|
const baseUrl = auth.getBaseUrl();
|
|
3163
|
-
const
|
|
3164
|
-
const formInfo = (formList?.data || []).find((f) => String(f.id) === String(objId));
|
|
2744
|
+
const formInfo = await resolveFormRecord(client, baseUrl, appId, objId);
|
|
3165
2745
|
const formId = formInfo?.formId || '';
|
|
3166
2746
|
if (!formId) {
|
|
3167
2747
|
console.error('无法获取 formId,请确认 objId 和 appId 正确');
|
|
@@ -3269,9 +2849,8 @@ export function registerFormCommands(program) {
|
|
|
3269
2849
|
// eb 表 layout/field-manage 接口认 formId(≠objId):从应用表单列表按 objId 解析 formId
|
|
3270
2850
|
let formId = '';
|
|
3271
2851
|
try {
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
formId = hit?.formId || '';
|
|
2852
|
+
// 原实现这条请求没带 ebbusinessid 头;走共用解析后会带上(与其余 12 处一致)。
|
|
2853
|
+
formId = await resolveFormId(client, baseUrl, appId, objId);
|
|
3275
2854
|
}
|
|
3276
2855
|
catch { /* 忽略,下面报错 */ }
|
|
3277
2856
|
if (!formId)
|