weaver-e10-builder 1.0.2-beta
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/README.md +200 -0
- package/dist/commands/app.d.ts +3 -0
- package/dist/commands/app.d.ts.map +1 -0
- package/dist/commands/app.js +305 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +386 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/check.d.ts +3 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +106 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/compile.d.ts +3 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/compile.js +134 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/docx.d.ts +3 -0
- package/dist/commands/docx.d.ts.map +1 -0
- package/dist/commands/docx.js +54 -0
- package/dist/commands/docx.js.map +1 -0
- package/dist/commands/form-button.d.ts +3 -0
- package/dist/commands/form-button.d.ts.map +1 -0
- package/dist/commands/form-button.js +441 -0
- package/dist/commands/form-button.js.map +1 -0
- package/dist/commands/form-data.d.ts +3 -0
- package/dist/commands/form-data.d.ts.map +1 -0
- package/dist/commands/form-data.js +197 -0
- package/dist/commands/form-data.js.map +1 -0
- package/dist/commands/form-ebuilder-columns.d.ts +3 -0
- package/dist/commands/form-ebuilder-columns.d.ts.map +1 -0
- package/dist/commands/form-ebuilder-columns.js +118 -0
- package/dist/commands/form-ebuilder-columns.js.map +1 -0
- package/dist/commands/form-ebuilder-filter.d.ts +3 -0
- package/dist/commands/form-ebuilder-filter.d.ts.map +1 -0
- package/dist/commands/form-ebuilder-filter.js +135 -0
- package/dist/commands/form-ebuilder-filter.js.map +1 -0
- package/dist/commands/form-ebuilder-scope.d.ts +3 -0
- package/dist/commands/form-ebuilder-scope.d.ts.map +1 -0
- package/dist/commands/form-ebuilder-scope.js +321 -0
- package/dist/commands/form-ebuilder-scope.js.map +1 -0
- package/dist/commands/form-ebuilder-search.d.ts +3 -0
- package/dist/commands/form-ebuilder-search.d.ts.map +1 -0
- package/dist/commands/form-ebuilder-search.js +252 -0
- package/dist/commands/form-ebuilder-search.js.map +1 -0
- package/dist/commands/form-formula.d.ts +12 -0
- package/dist/commands/form-formula.d.ts.map +1 -0
- package/dist/commands/form-formula.js +252 -0
- package/dist/commands/form-formula.js.map +1 -0
- package/dist/commands/form-layout-init.d.ts +15 -0
- package/dist/commands/form-layout-init.d.ts.map +1 -0
- package/dist/commands/form-layout-init.js +162 -0
- package/dist/commands/form-layout-init.js.map +1 -0
- package/dist/commands/form-layout.d.ts +3 -0
- package/dist/commands/form-layout.d.ts.map +1 -0
- package/dist/commands/form-layout.js +236 -0
- package/dist/commands/form-layout.js.map +1 -0
- package/dist/commands/form-list-click.d.ts +3 -0
- package/dist/commands/form-list-click.d.ts.map +1 -0
- package/dist/commands/form-list-click.js +154 -0
- package/dist/commands/form-list-click.js.map +1 -0
- package/dist/commands/form-list-event.d.ts +3 -0
- package/dist/commands/form-list-event.d.ts.map +1 -0
- package/dist/commands/form-list-event.js +203 -0
- package/dist/commands/form-list-event.js.map +1 -0
- package/dist/commands/form-list-filter.d.ts +9 -0
- package/dist/commands/form-list-filter.d.ts.map +1 -0
- package/dist/commands/form-list-filter.js +107 -0
- package/dist/commands/form-list-filter.js.map +1 -0
- package/dist/commands/form-list-group.d.ts +11 -0
- package/dist/commands/form-list-group.d.ts.map +1 -0
- package/dist/commands/form-list-group.js +100 -0
- package/dist/commands/form-list-group.js.map +1 -0
- package/dist/commands/form-list-permission.d.ts +3 -0
- package/dist/commands/form-list-permission.d.ts.map +1 -0
- package/dist/commands/form-list-permission.js +127 -0
- package/dist/commands/form-list-permission.js.map +1 -0
- package/dist/commands/form-list-search-group.d.ts +3 -0
- package/dist/commands/form-list-search-group.d.ts.map +1 -0
- package/dist/commands/form-list-search-group.js +132 -0
- package/dist/commands/form-list-search-group.js.map +1 -0
- package/dist/commands/form-list-stats.d.ts +11 -0
- package/dist/commands/form-list-stats.d.ts.map +1 -0
- package/dist/commands/form-list-stats.js +139 -0
- package/dist/commands/form-list-stats.js.map +1 -0
- package/dist/commands/form-list-unit.d.ts +3 -0
- package/dist/commands/form-list-unit.d.ts.map +1 -0
- package/dist/commands/form-list-unit.js +95 -0
- package/dist/commands/form-list-unit.js.map +1 -0
- package/dist/commands/form-list-utils.d.ts +32 -0
- package/dist/commands/form-list-utils.d.ts.map +1 -0
- package/dist/commands/form-list-utils.js +76 -0
- package/dist/commands/form-list-utils.js.map +1 -0
- package/dist/commands/form-list.d.ts +3 -0
- package/dist/commands/form-list.d.ts.map +1 -0
- package/dist/commands/form-list.js +624 -0
- package/dist/commands/form-list.js.map +1 -0
- package/dist/commands/form-permission.d.ts +3 -0
- package/dist/commands/form-permission.d.ts.map +1 -0
- package/dist/commands/form-permission.js +193 -0
- package/dist/commands/form-permission.js.map +1 -0
- package/dist/commands/form-relate.d.ts +14 -0
- package/dist/commands/form-relate.d.ts.map +1 -0
- package/dist/commands/form-relate.js +140 -0
- package/dist/commands/form-relate.js.map +1 -0
- package/dist/commands/form-serial-mark.d.ts +18 -0
- package/dist/commands/form-serial-mark.d.ts.map +1 -0
- package/dist/commands/form-serial-mark.js +167 -0
- package/dist/commands/form-serial-mark.js.map +1 -0
- package/dist/commands/form-table-view.d.ts +3 -0
- package/dist/commands/form-table-view.d.ts.map +1 -0
- package/dist/commands/form-table-view.js +217 -0
- package/dist/commands/form-table-view.js.map +1 -0
- package/dist/commands/form-validate.d.ts +3 -0
- package/dist/commands/form-validate.d.ts.map +1 -0
- package/dist/commands/form-validate.js +122 -0
- package/dist/commands/form-validate.js.map +1 -0
- package/dist/commands/form-view-permission.d.ts +3 -0
- package/dist/commands/form-view-permission.d.ts.map +1 -0
- package/dist/commands/form-view-permission.js +86 -0
- package/dist/commands/form-view-permission.js.map +1 -0
- package/dist/commands/form-view.d.ts +3 -0
- package/dist/commands/form-view.d.ts.map +1 -0
- package/dist/commands/form-view.js +145 -0
- package/dist/commands/form-view.js.map +1 -0
- package/dist/commands/form.d.ts +3 -0
- package/dist/commands/form.d.ts.map +1 -0
- package/dist/commands/form.js +2467 -0
- package/dist/commands/form.js.map +1 -0
- package/dist/commands/icon.d.ts +3 -0
- package/dist/commands/icon.d.ts.map +1 -0
- package/dist/commands/icon.js +82 -0
- package/dist/commands/icon.js.map +1 -0
- package/dist/commands/matrix.d.ts +3 -0
- package/dist/commands/matrix.d.ts.map +1 -0
- package/dist/commands/matrix.js +587 -0
- package/dist/commands/matrix.js.map +1 -0
- package/dist/commands/menu-permission.d.ts +3 -0
- package/dist/commands/menu-permission.d.ts.map +1 -0
- package/dist/commands/menu-permission.js +188 -0
- package/dist/commands/menu-permission.js.map +1 -0
- package/dist/commands/menu.d.ts +16 -0
- package/dist/commands/menu.d.ts.map +1 -0
- package/dist/commands/menu.js +269 -0
- package/dist/commands/menu.js.map +1 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +345 -0
- package/dist/commands/project.js.map +1 -0
- package/dist/commands/role.d.ts +3 -0
- package/dist/commands/role.d.ts.map +1 -0
- package/dist/commands/role.js +224 -0
- package/dist/commands/role.js.map +1 -0
- package/dist/commands/user.d.ts +3 -0
- package/dist/commands/user.d.ts.map +1 -0
- package/dist/commands/user.js +49 -0
- package/dist/commands/user.js.map +1 -0
- package/dist/commands/workflow-action.d.ts +3 -0
- package/dist/commands/workflow-action.d.ts.map +1 -0
- package/dist/commands/workflow-action.js +1821 -0
- package/dist/commands/workflow-action.js.map +1 -0
- package/dist/commands/workflow-advanced.d.ts +3 -0
- package/dist/commands/workflow-advanced.d.ts.map +1 -0
- package/dist/commands/workflow-advanced.js +290 -0
- package/dist/commands/workflow-advanced.js.map +1 -0
- package/dist/commands/workflow-condition.d.ts +7 -0
- package/dist/commands/workflow-condition.d.ts.map +1 -0
- package/dist/commands/workflow-condition.js +562 -0
- package/dist/commands/workflow-condition.js.map +1 -0
- package/dist/commands/workflow-message.d.ts +3 -0
- package/dist/commands/workflow-message.d.ts.map +1 -0
- package/dist/commands/workflow-message.js +399 -0
- package/dist/commands/workflow-message.js.map +1 -0
- package/dist/commands/workflow-node-overtime.d.ts +3 -0
- package/dist/commands/workflow-node-overtime.d.ts.map +1 -0
- package/dist/commands/workflow-node-overtime.js +150 -0
- package/dist/commands/workflow-node-overtime.js.map +1 -0
- package/dist/commands/workflow-node-reject.d.ts +3 -0
- package/dist/commands/workflow-node-reject.d.ts.map +1 -0
- package/dist/commands/workflow-node-reject.js +115 -0
- package/dist/commands/workflow-node-reject.js.map +1 -0
- package/dist/commands/workflow-node-sign.d.ts +15 -0
- package/dist/commands/workflow-node-sign.d.ts.map +1 -0
- package/dist/commands/workflow-node-sign.js +100 -0
- package/dist/commands/workflow-node-sign.js.map +1 -0
- package/dist/commands/workflow-node-validate.d.ts +16 -0
- package/dist/commands/workflow-node-validate.d.ts.map +1 -0
- package/dist/commands/workflow-node-validate.js +169 -0
- package/dist/commands/workflow-node-validate.js.map +1 -0
- package/dist/commands/workflow-node.d.ts +3 -0
- package/dist/commands/workflow-node.d.ts.map +1 -0
- package/dist/commands/workflow-node.js +347 -0
- package/dist/commands/workflow-node.js.map +1 -0
- package/dist/commands/workflow-numbering.d.ts +17 -0
- package/dist/commands/workflow-numbering.d.ts.map +1 -0
- package/dist/commands/workflow-numbering.js +191 -0
- package/dist/commands/workflow-numbering.js.map +1 -0
- package/dist/commands/workflow-plan-range.d.ts +3 -0
- package/dist/commands/workflow-plan-range.d.ts.map +1 -0
- package/dist/commands/workflow-plan-range.js +116 -0
- package/dist/commands/workflow-plan-range.js.map +1 -0
- package/dist/commands/workflow-setting.d.ts +3 -0
- package/dist/commands/workflow-setting.d.ts.map +1 -0
- package/dist/commands/workflow-setting.js +440 -0
- package/dist/commands/workflow-setting.js.map +1 -0
- package/dist/commands/workflow-title.d.ts +14 -0
- package/dist/commands/workflow-title.d.ts.map +1 -0
- package/dist/commands/workflow-title.js +106 -0
- package/dist/commands/workflow-title.js.map +1 -0
- package/dist/commands/workflow.d.ts +26 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/commands/workflow.js +2359 -0
- package/dist/commands/workflow.js.map +1 -0
- package/dist/core/auth.d.ts +36 -0
- package/dist/core/auth.d.ts.map +1 -0
- package/dist/core/auth.js +351 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/client.d.ts +27 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +100 -0
- package/dist/core/client.js.map +1 -0
- package/dist/core/crypto.d.ts +11 -0
- package/dist/core/crypto.d.ts.map +1 -0
- package/dist/core/crypto.js +102 -0
- package/dist/core/crypto.js.map +1 -0
- package/dist/core/edition.d.ts +4 -0
- package/dist/core/edition.d.ts.map +1 -0
- package/dist/core/edition.js +19 -0
- package/dist/core/edition.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +118 -0
- package/dist/index.js.map +1 -0
- package/dist/services/action-detail.d.ts +112 -0
- package/dist/services/action-detail.d.ts.map +1 -0
- package/dist/services/action-detail.js +443 -0
- package/dist/services/action-detail.js.map +1 -0
- package/dist/services/condition-rules.d.ts +45 -0
- package/dist/services/condition-rules.d.ts.map +1 -0
- package/dist/services/condition-rules.js +207 -0
- package/dist/services/condition-rules.js.map +1 -0
- package/dist/services/form-create.d.ts +102 -0
- package/dist/services/form-create.d.ts.map +1 -0
- package/dist/services/form-create.js +835 -0
- package/dist/services/form-create.js.map +1 -0
- package/dist/services/form-ebuilder-filter.d.ts +60 -0
- package/dist/services/form-ebuilder-filter.d.ts.map +1 -0
- package/dist/services/form-ebuilder-filter.js +562 -0
- package/dist/services/form-ebuilder-filter.js.map +1 -0
- package/dist/services/form-table-view.d.ts +117 -0
- package/dist/services/form-table-view.d.ts.map +1 -0
- package/dist/services/form-table-view.js +882 -0
- package/dist/services/form-table-view.js.map +1 -0
- package/dist/services/form-view-permission.d.ts +30 -0
- package/dist/services/form-view-permission.d.ts.map +1 -0
- package/dist/services/form-view-permission.js +208 -0
- package/dist/services/form-view-permission.js.map +1 -0
- package/dist/services/form-view.d.ts +41 -0
- package/dist/services/form-view.d.ts.map +1 -0
- package/dist/services/form-view.js +334 -0
- package/dist/services/form-view.js.map +1 -0
- package/dist/services/formula-detail.d.ts +31 -0
- package/dist/services/formula-detail.d.ts.map +1 -0
- package/dist/services/formula-detail.js +66 -0
- package/dist/services/formula-detail.js.map +1 -0
- package/dist/services/rich-types.d.ts +35 -0
- package/dist/services/rich-types.d.ts.map +1 -0
- package/dist/services/rich-types.js +105 -0
- package/dist/services/rich-types.js.map +1 -0
- package/dist/services/workflow-layout.d.ts +118 -0
- package/dist/services/workflow-layout.d.ts.map +1 -0
- package/dist/services/workflow-layout.js +279 -0
- package/dist/services/workflow-layout.js.map +1 -0
- package/dist/services/workflow-operator.d.ts +36 -0
- package/dist/services/workflow-operator.d.ts.map +1 -0
- package/dist/services/workflow-operator.js +65 -0
- package/dist/services/workflow-operator.js.map +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/column-name.d.ts +13 -0
- package/dist/utils/column-name.d.ts.map +1 -0
- package/dist/utils/column-name.js +45 -0
- package/dist/utils/column-name.js.map +1 -0
- package/ecodecmd/package.json +27 -0
- package/ecodecmd/src/build.js +709 -0
- package/ecodecmd/src/compile.js +81 -0
- package/ecodecmd/src/index.js +48 -0
- package/ecodecmd/src/init.js +73 -0
- package/ecodecmd/src/lib/@babel/plugin-proposal-object-rest-spread@7.5.5/index.js +1795 -0
- package/ecodecmd/src/lib/@babel/plugin-proposal-optional-chaining@7.6.0/index.js +135 -0
- package/ecodecmd/src/lib/@babel/preset-env-standalone@7.5.5/babel-preset-env.js +76043 -0
- package/ecodecmd/src/lib/@babel/standalone@7.5.5/babel.js +90802 -0
- package/ecodecmd/src/lib/exec.js +77 -0
- package/ecodecmd/src/loaddep.js +154 -0
- package/ecodecmd/src/util/compress.js +58 -0
- package/ecodecmd/src/util/crypto-util.js +19 -0
- package/ecodecmd/src/util/index.js +153 -0
- package/ecodecmd/src/util/snowflake.js +144 -0
- package/package.json +43 -0
|
@@ -0,0 +1,2467 @@
|
|
|
1
|
+
import { writeFileSync, existsSync, mkdirSync, readFileSync } from 'fs';
|
|
2
|
+
import { AuthSession } from '../core/auth.js';
|
|
3
|
+
import { ApiClient } from '../core/client.js';
|
|
4
|
+
import { toColumnName, toTableName } from '../utils/column-name.js';
|
|
5
|
+
import { createCustomPhysicalForm, addInitialFieldsToCustomPhysicalForm, placeholderFromFormat, reapplyGroupLayout } from '../services/form-create.js';
|
|
6
|
+
import { getRichType, richIdUnits } from '../services/rich-types.js';
|
|
7
|
+
/**
|
|
8
|
+
* componentKey 大小写归一:用户/示例常写 "Textarea"/"Ebuilder" 等小写变体,但代码各处
|
|
9
|
+
* `=== 'TextArea'`、storageFieldType 按 canonical 大小写匹配。不归一会导致 type 存成小写
|
|
10
|
+
* "Textarea"(FLOW 渲染器按 type 查组件查不到 → 多行文本在流式布局下渲染异常)、TextArea
|
|
11
|
+
* 分支(content/maxLen)漏跑。建表 create 和加字段 add-field 都用。
|
|
12
|
+
*/
|
|
13
|
+
const CK_CANON = {
|
|
14
|
+
textarea: 'TextArea', ebuilder: 'EBuilder', datetime: 'DateTime',
|
|
15
|
+
idcard: 'IDCard', mobile: 'Mobile', email: 'Email', phone: 'Phone',
|
|
16
|
+
employee: 'Employee', department: 'Department', location: 'Location',
|
|
17
|
+
switch: 'Switch', select: 'Select', checkbox: 'CheckBox', radiobox: 'RadioBox',
|
|
18
|
+
money: 'Money', number: 'Number', date: 'Date', text: 'Text',
|
|
19
|
+
daterange: 'DateRange', detailtable: 'DetailTable', city: 'City',
|
|
20
|
+
};
|
|
21
|
+
function canonComponentKey(ck) {
|
|
22
|
+
return CK_CANON[String(ck).toLowerCase()] || ck;
|
|
23
|
+
}
|
|
24
|
+
const SYSTEM_FIELD_IDS = new Set([
|
|
25
|
+
'name', 'creator', 'create_time', 'update_time', 'id',
|
|
26
|
+
'updater', 'data_status', 'flow_status', 'current_step',
|
|
27
|
+
'batchpubtags', 'batchmytags', 'operate', 'data_read_status',
|
|
28
|
+
'flow_system_number',
|
|
29
|
+
]);
|
|
30
|
+
// EBuilder dataTitle(关联字段标题栏)默认解析:优先字段名含「名称」的业务字段,无则第一个业务字段。
|
|
31
|
+
// 需求:默认选带"名称"二字的字段(如 客户名称/类型名称),没有就默认选第一个。
|
|
32
|
+
function defaultEBuilderDataTitle(fields) {
|
|
33
|
+
const isBiz = (id) => /^\d{15,}$/.test(id) && !SYSTEM_FIELD_IDS.has(id);
|
|
34
|
+
const biz = (fields || []).filter((f) => isBiz(String(f.fieldId ?? '')));
|
|
35
|
+
const byName = biz.find((f) => String(f.fieldName ?? f.title ?? '').includes('名称'));
|
|
36
|
+
return byName?.fieldId || biz[0]?.fieldId || 'name';
|
|
37
|
+
}
|
|
38
|
+
export function registerFormCommands(program) {
|
|
39
|
+
const form = program.command('form').description('表单管理');
|
|
40
|
+
form.command('read')
|
|
41
|
+
.description('读取表单字段结构')
|
|
42
|
+
.argument('<objId>', '表单 objId')
|
|
43
|
+
.argument('[dir]', '输出目录(可选,用于写入 memo.md)')
|
|
44
|
+
.option('--out <file>', '将 JSON 输出写入文件(UTF-8,避免终端编码问题)')
|
|
45
|
+
.option('--app-id <id>', '应用 ID(提供时自动获取 Select 字段的选项)')
|
|
46
|
+
.action(async (objId, dir, cmdOpts) => {
|
|
47
|
+
const opts = program.opts();
|
|
48
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
49
|
+
if (!auth)
|
|
50
|
+
process.exit(1);
|
|
51
|
+
const client = new ApiClient(auth);
|
|
52
|
+
const baseUrl = auth.getBaseUrl();
|
|
53
|
+
// isReturnDetail='y' 让 getFields 返回 detailFields(按明细表分组的子表字段),否则只返 mainFields
|
|
54
|
+
const payload = { objId, userid: auth.userId, tenantKey: auth.tenantKey, isReturnDetail: 'y' };
|
|
55
|
+
const data = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, payload);
|
|
56
|
+
let fields = [];
|
|
57
|
+
let subFields = [];
|
|
58
|
+
let detailTables = [];
|
|
59
|
+
if (data) {
|
|
60
|
+
const d = data.data || data;
|
|
61
|
+
if (d?.mainFields)
|
|
62
|
+
fields = d.mainFields;
|
|
63
|
+
else if (Array.isArray(d))
|
|
64
|
+
fields = d;
|
|
65
|
+
// detailFields: [{ name, id, fields: [...] }],每个明细字段带 fieldId/subFormId/subFormName
|
|
66
|
+
// 之前 form read 只读 mainFields 完全丢弃明细,配动作流/联动拿不到明细 fieldId
|
|
67
|
+
if (Array.isArray(d?.detailFields)) {
|
|
68
|
+
detailTables = d.detailFields;
|
|
69
|
+
for (const dt of detailTables) {
|
|
70
|
+
const dtName = dt.name || dt.subFormName || '(未命名明细表)';
|
|
71
|
+
for (const sf of (dt.fields || [])) {
|
|
72
|
+
// 标注所属明细表名/subFormName,渲染与 memo 都用得上
|
|
73
|
+
if (!sf.subFormName)
|
|
74
|
+
sf.subFormName = dtName;
|
|
75
|
+
subFields.push(sf);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// If appId provided, fetch formId and then get options for Select fields
|
|
81
|
+
let formId = '';
|
|
82
|
+
if (cmdOpts?.appId) {
|
|
83
|
+
try {
|
|
84
|
+
// Get formId from appId
|
|
85
|
+
const listData = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${cmdOpts.appId}?apid=${cmdOpts.appId}`, { ebbusinessid: cmdOpts.appId });
|
|
86
|
+
const formInfo = (listData?.data || []).find((f) => f.id === objId);
|
|
87
|
+
formId = formInfo?.formId || '';
|
|
88
|
+
if (formId) {
|
|
89
|
+
// Fetch options for Select/RadioBox/CheckBox fields (skip system fields with string IDs)
|
|
90
|
+
// 主表 + 明细表(子表)字段都取:之前只遍历主表,子表 Select 的 options 一直拿不到
|
|
91
|
+
const fetchOptions = async (f) => {
|
|
92
|
+
const ftype = f.fieldType || f.fieldKey || '';
|
|
93
|
+
const isOptionField = ftype === 'Select' || ftype === 'RadioBox' || ftype === 'CheckBox';
|
|
94
|
+
// Use fieldId (actual field ID) not id (sequence number), and skip non-numeric IDs (system fields)
|
|
95
|
+
if (!isOptionField || !f.fieldId || !/^\d+$/.test(String(f.fieldId)))
|
|
96
|
+
return;
|
|
97
|
+
try {
|
|
98
|
+
const fieldData = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormField`, {
|
|
99
|
+
module: 'ebuilderform',
|
|
100
|
+
formTableField: {
|
|
101
|
+
formField: {
|
|
102
|
+
id: String(f.fieldId),
|
|
103
|
+
form: { id: formId },
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
}, { ebbusinessid: formId });
|
|
107
|
+
const options = fieldData?.data?.options || [];
|
|
108
|
+
if (options.length > 0) {
|
|
109
|
+
f.options = options.map((o) => ({ valueKey: o.valueKey, name: o.name }));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch { /* ignore individual field failures */ }
|
|
113
|
+
};
|
|
114
|
+
for (const f of fields)
|
|
115
|
+
await fetchOptions(f);
|
|
116
|
+
for (const f of subFields)
|
|
117
|
+
await fetchOptions(f);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch { /* ignore */ }
|
|
121
|
+
}
|
|
122
|
+
const allFields = [...fields, ...subFields];
|
|
123
|
+
// 布局属性补读(T12):readOnly/required/hidden 在 getFields/getFormField 都不返回,
|
|
124
|
+
// 但布局 getConfig 里有(CLI 建表时写入的布局属性)。按字段 id 合并进输出。
|
|
125
|
+
if (formId) {
|
|
126
|
+
try {
|
|
127
|
+
const preLayout = await client.post(`${baseUrl}/api/ebuilder/form/builder/getFormLayout`, { formId, module: 'ebuilderform', customParam: {}, formTableIdList: [],
|
|
128
|
+
designerParam: { onlyLayout: false, convert: false }, form: { id: formId } }, { ebbusinessid: formId });
|
|
129
|
+
const layoutId = preLayout?.data?.formLayout?.id || '';
|
|
130
|
+
if (layoutId) {
|
|
131
|
+
const cfgRes = await client.post(`${baseUrl}/api/ebuilder/form/form/view/relate/getConfig`, { formId, module: 'ebuilderform', apiModule: 'ebuilder/form', layoutId,
|
|
132
|
+
customParam: { terminal: '', objId: formId }, initMode: 5, groupFieldIds: [],
|
|
133
|
+
designerParam: { sourceModule: 'ebuilderform' } }, { ebbusinessid: formId });
|
|
134
|
+
const attrByFieldId = {};
|
|
135
|
+
for (const c of (cfgRes?.data?.configs || []))
|
|
136
|
+
attrByFieldId[String(c.id)] = c;
|
|
137
|
+
for (const f of allFields) {
|
|
138
|
+
const c = attrByFieldId[String(f.fieldId)];
|
|
139
|
+
if (!c)
|
|
140
|
+
continue;
|
|
141
|
+
if (c.readOnly !== undefined)
|
|
142
|
+
f.readOnly = c.readOnly;
|
|
143
|
+
if (c.required !== undefined)
|
|
144
|
+
f.required = c.required;
|
|
145
|
+
if (c.show !== undefined)
|
|
146
|
+
f.hidden = !c.show;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch { /* 布局属性补读失败不影响主流程 */ }
|
|
151
|
+
}
|
|
152
|
+
if (cmdOpts?.out) {
|
|
153
|
+
// Write JSON to file with UTF-8 encoding, bypassing terminal encoding
|
|
154
|
+
writeFileSync(cmdOpts.out, JSON.stringify(allFields, null, 2), 'utf-8');
|
|
155
|
+
console.error(`已写入: ${cmdOpts.out}(主表 ${fields.length} + 明细表 ${subFields.length})`);
|
|
156
|
+
}
|
|
157
|
+
else if (opts.json) {
|
|
158
|
+
console.log(JSON.stringify(allFields, null, 2));
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
console.log(`表单 ${objId} 共 ${fields.length} 个主表字段${subFields.length ? ` + ${subFields.length} 个明细表字段` : ''}:`);
|
|
162
|
+
for (const f of fields) {
|
|
163
|
+
if (SYSTEM_FIELD_IDS.has(f.fieldId))
|
|
164
|
+
continue;
|
|
165
|
+
console.log(` [${f.fieldKey || f.fieldType}] ${f.fieldName} (fieldId: ${f.fieldId || f.id})`);
|
|
166
|
+
if (f.options)
|
|
167
|
+
console.log(` 选项: ${JSON.stringify(f.options)}`);
|
|
168
|
+
}
|
|
169
|
+
if (subFields.length) {
|
|
170
|
+
console.log(` -- 明细表字段 --`);
|
|
171
|
+
for (const f of subFields) {
|
|
172
|
+
if (SYSTEM_FIELD_IDS.has(f.fieldId))
|
|
173
|
+
continue;
|
|
174
|
+
const parent = f.subFormName || '';
|
|
175
|
+
console.log(` [${f.fieldKey || f.fieldType}] ${f.fieldName} (fieldId: ${f.fieldId || f.id}${parent ? `, 明细表: ${parent}` : ''})`);
|
|
176
|
+
if (f.options)
|
|
177
|
+
console.log(` 选项: ${JSON.stringify(f.options)}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Write memo.md if dir specified
|
|
182
|
+
if (dir && allFields.length > 0) {
|
|
183
|
+
if (!existsSync(dir))
|
|
184
|
+
mkdirSync(dir, { recursive: true });
|
|
185
|
+
let memo = `## 表单\n- 表单ID:${objId}\n`;
|
|
186
|
+
for (const f of fields) {
|
|
187
|
+
if (SYSTEM_FIELD_IDS.has(f.fieldId))
|
|
188
|
+
continue;
|
|
189
|
+
memo += `- 字段名称:${f.fieldName}, 字段类型:${f.fieldKey}, 字段id:${f.fieldId || f.id}\n`;
|
|
190
|
+
if (f.fieldOptions)
|
|
191
|
+
memo += ` 选项:${JSON.stringify(f.fieldOptions)}\n`;
|
|
192
|
+
}
|
|
193
|
+
if (subFields.length) {
|
|
194
|
+
memo += `\n### 明细表字段\n`;
|
|
195
|
+
for (const f of subFields) {
|
|
196
|
+
if (SYSTEM_FIELD_IDS.has(f.fieldId))
|
|
197
|
+
continue;
|
|
198
|
+
memo += `- 字段名称:${f.fieldName}, 字段类型:${f.fieldKey}, 字段id:${f.fieldId}${f.subFormName ? `, 明细表:${f.subFormName}` : ''}\n`;
|
|
199
|
+
if (f.fieldOptions)
|
|
200
|
+
memo += ` 选项:${JSON.stringify(f.fieldOptions)}\n`;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
memo += '- 字段名称:数据ID, 字段类型:Number, 字段id:id\n';
|
|
204
|
+
writeFileSync(`${dir}/memo.md`, memo, 'utf-8');
|
|
205
|
+
console.error('表单信息已记录于 memo.md');
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
form.command('list')
|
|
209
|
+
.description('列出应用下的表单')
|
|
210
|
+
.argument('<appId>', '应用 ID')
|
|
211
|
+
.option('--out <file>', '将 JSON 输出写入文件(UTF-8)')
|
|
212
|
+
.action(async (appId, cmdOpts) => {
|
|
213
|
+
const opts = program.opts();
|
|
214
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
215
|
+
if (!auth)
|
|
216
|
+
process.exit(1);
|
|
217
|
+
const client = new ApiClient(auth);
|
|
218
|
+
const baseUrl = auth.getBaseUrl();
|
|
219
|
+
const data = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
220
|
+
if (data?.code !== 200 || !data?.status) {
|
|
221
|
+
console.error(`查询失败: ${data?.msg || '未知错误'}`);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
const forms = data.data || [];
|
|
225
|
+
if (cmdOpts?.out) {
|
|
226
|
+
writeFileSync(cmdOpts.out, JSON.stringify(forms, null, 2), 'utf-8');
|
|
227
|
+
console.error(`已写入: ${cmdOpts.out}`);
|
|
228
|
+
}
|
|
229
|
+
else if (opts.json) {
|
|
230
|
+
console.log(JSON.stringify(forms, null, 2));
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
console.log(`共 ${forms.length} 个表单:`);
|
|
234
|
+
for (const f of forms) {
|
|
235
|
+
console.log(` ${f.id?.padEnd(22)} ${f.formName || ''}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
form.command('create')
|
|
240
|
+
.description('创建表单')
|
|
241
|
+
.argument('<appId>', '应用 ID')
|
|
242
|
+
.argument('<name>', '表单名称')
|
|
243
|
+
.argument('[dir]', '输出目录(可选,记录 memo.md)')
|
|
244
|
+
.requiredOption('--fields <json>', '字段定义 JSON 文件路径(如 @fields.json),格式:[{"componentKey":"Text","fieldName":"姓名"}]')
|
|
245
|
+
.option('--no-menu', '不自动添加台账菜单(默认建表后自动加)')
|
|
246
|
+
.option('--layout <type>', '表单布局类型:FLOW(流式)/EXCEL(表格,缺省)。布局类型建表后不可切换', 'EXCEL')
|
|
247
|
+
.action(async (appId, name, dir, cmdOpts) => {
|
|
248
|
+
const opts = program.opts();
|
|
249
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
250
|
+
if (!auth)
|
|
251
|
+
process.exit(1);
|
|
252
|
+
const client = new ApiClient(auth);
|
|
253
|
+
const baseUrl = auth.getBaseUrl();
|
|
254
|
+
const { randomUUID } = await import('crypto');
|
|
255
|
+
// Fields are required — parse from file (must start with @)
|
|
256
|
+
if (!cmdOpts.fields.startsWith('@')) {
|
|
257
|
+
console.error('--fields 必须为文件路径(以 @ 开头),如 --fields @fields.json');
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
const filePath = cmdOpts.fields.slice(1);
|
|
261
|
+
if (!existsSync(filePath)) {
|
|
262
|
+
console.error(`文件不存在: ${filePath}`);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
let fieldDefs;
|
|
266
|
+
try {
|
|
267
|
+
fieldDefs = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
console.error('--fields 文件 JSON 解析失败');
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
if (!Array.isArray(fieldDefs) || fieldDefs.length === 0) {
|
|
274
|
+
console.error('--fields 必须是非空数组');
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
// Normalize: accept fieldType as componentKey alias (add-field/read use "fieldType"),
|
|
278
|
+
// and options (string[]) as a shorthand for fieldOptions:[{name}].
|
|
279
|
+
// componentKey 大小写归一(canonComponentKey):用户/示例常写 "Textarea"/"Ebuilder" 等,
|
|
280
|
+
// 但 storageFieldType 与各 `=== 'TextArea'` 分支按 canonical 大小写匹配,不归一会导致 type 存成
|
|
281
|
+
// 小写 "Textarea"(FLOW 渲染异常)、TextArea 分支(content/maxLen)漏跑。
|
|
282
|
+
fieldDefs = fieldDefs.map((f) => {
|
|
283
|
+
const out = { ...f };
|
|
284
|
+
if (!out.componentKey && out.fieldType)
|
|
285
|
+
out.componentKey = out.fieldType;
|
|
286
|
+
if (out.componentKey)
|
|
287
|
+
out.componentKey = canonComponentKey(out.componentKey);
|
|
288
|
+
if (Array.isArray(out.options) && !out.fieldOptions) {
|
|
289
|
+
out.fieldOptions = out.options.map((o) => typeof o === 'string' ? { name: o } : o);
|
|
290
|
+
}
|
|
291
|
+
// DetailTable 子字段的 options 同样归一化:之前只转顶层,子表 Select 用 options 简写时
|
|
292
|
+
// Step 7b 找不到 fieldOptions,子表选项配不上(布局 + 选项配置两处都依赖 fieldOptions)
|
|
293
|
+
if (out.componentKey === 'DetailTable' && Array.isArray(out.formFields)) {
|
|
294
|
+
out.formFields = out.formFields.map((sf) => {
|
|
295
|
+
const so = { ...sf };
|
|
296
|
+
if (so.componentKey)
|
|
297
|
+
so.componentKey = canonComponentKey(so.componentKey);
|
|
298
|
+
if (Array.isArray(so.options) && !so.fieldOptions) {
|
|
299
|
+
so.fieldOptions = so.options.map((o) => typeof o === 'string' ? { name: o } : o);
|
|
300
|
+
}
|
|
301
|
+
return so;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
return out;
|
|
305
|
+
});
|
|
306
|
+
for (const f of fieldDefs) {
|
|
307
|
+
if (!f.componentKey) {
|
|
308
|
+
console.error(`字段 "${f.fieldName}" 缺少 componentKey(或 fieldType)`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const makeField = (fid, fname, ck, row, col, fieldOptions, isDetail, subFields) => {
|
|
313
|
+
const x = col * 950;
|
|
314
|
+
const y = 35 + row * 52;
|
|
315
|
+
// DetailTable 父字段:照真实 createFormWithFormLayout payload 构造(明细表=表格列布局,label 在顶部、
|
|
316
|
+
// 子字段为列)。之前用主表那套(columnSpan:0.5/labelPosition:'left')导致明细渲染成左右。
|
|
317
|
+
if (isDetail) {
|
|
318
|
+
const dtX = 297, dtW = 1295, dtH = 41;
|
|
319
|
+
return {
|
|
320
|
+
fieldId: fid, fieldName: fname, componentKey: 'DetailTable',
|
|
321
|
+
fieldOptions: [],
|
|
322
|
+
row, columnSpan: 1, labelSpan: 0, labelPosition: 'top',
|
|
323
|
+
required: false, readOnly: false,
|
|
324
|
+
totalWidth: dtW, textAlign: 'center',
|
|
325
|
+
coords: { x: dtX, y, w: dtW, h: dtH },
|
|
326
|
+
formFields: subFields || [],
|
|
327
|
+
actionButton: 10, selectAllSpan: 0.05, sequenceSpan: 0.05, isTotal: false,
|
|
328
|
+
actionButtonAlign: 'left', titleEnabled: false, innerSpan: 0,
|
|
329
|
+
isAddForRelation: false, format: 'yyyy-MM-dd', isCopyUpload: false,
|
|
330
|
+
richEditor: false, creatByAI: true, detailTable: true,
|
|
331
|
+
xInterval: [dtX, dtX + dtW], yInterval: [y, y + dtH],
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
// 主表字段:完整样式(与真实 payload 主字段一致)
|
|
335
|
+
const f = {
|
|
336
|
+
row, columnSpan: 0.5, labelSpan: 0.42, labelPosition: 'left',
|
|
337
|
+
required: false, readOnly: false, cellHeight: 36, cellWidth: 260,
|
|
338
|
+
backgroundColor: '#e7f3fc', totalWidth: 450, textColor: '#000000',
|
|
339
|
+
labelLineColor: '#90badd', textSize: 14, textAlign: 'left',
|
|
340
|
+
kvCoords: { x, y, w: 450, h: 33 },
|
|
341
|
+
contentLineColor: '#90badd',
|
|
342
|
+
labelLineStyle: { top: 1, right: 1, bottom: 1, left: 1 },
|
|
343
|
+
contentLineStyle: { top: 1, right: 1, bottom: 1, left: 1 },
|
|
344
|
+
sequenceSpan: 0, titleEnabled: false, innerSpan: 0,
|
|
345
|
+
isAddForRelation: false, format: 'yyyy-MM-dd', isCopyUpload: false,
|
|
346
|
+
richEditor: false, creatByAI: true,
|
|
347
|
+
detailTable: false,
|
|
348
|
+
fieldId: fid, fieldName: fname, componentKey: ck,
|
|
349
|
+
coords: { x, y, w: 450, h: 33 }, createBy: 0,
|
|
350
|
+
};
|
|
351
|
+
if (fieldOptions) {
|
|
352
|
+
f.fieldOptions = fieldOptions.map(o => ({ id: randomUUID().slice(0, 8), label: o.name, value: o.valueKey || randomUUID().slice(0, 8) }));
|
|
353
|
+
}
|
|
354
|
+
// EBuilder association field
|
|
355
|
+
if (ck === 'EBuilder') {
|
|
356
|
+
f.sformId = '';
|
|
357
|
+
f.dataTitle = '';
|
|
358
|
+
f.formName = '';
|
|
359
|
+
f.virtualForm = false;
|
|
360
|
+
f.displayType = 'line';
|
|
361
|
+
f.isAddForRelation = true;
|
|
362
|
+
f.viewDataDetail = true;
|
|
363
|
+
f.pcDisplaySelectedCount = { type: 'default' };
|
|
364
|
+
f.mobileDisplaySelectedCount = { type: 'default' };
|
|
365
|
+
f.quickAddParams = [];
|
|
366
|
+
f.relateViewData = [];
|
|
367
|
+
f.relateField = null;
|
|
368
|
+
f.virtualTable = false;
|
|
369
|
+
f.richEditor = false;
|
|
370
|
+
}
|
|
371
|
+
return f;
|
|
372
|
+
};
|
|
373
|
+
const buildSubFields = (subDefs) => subDefs.map((sf, si) => {
|
|
374
|
+
const sfid = randomUUID().slice(0, 8);
|
|
375
|
+
// 照真实 payload 明细子字段构造:表格列单元格,textAlign:center,无 labelPosition/labelSpan/kvCoords,
|
|
376
|
+
// cellWidth 每列不同(这里用默认 140),coords 小。之前用主表样式导致明细左右。
|
|
377
|
+
const cellW = 140;
|
|
378
|
+
const sx = 297 + si * (cellW + 10);
|
|
379
|
+
const sub = {
|
|
380
|
+
required: false, readOnly: false,
|
|
381
|
+
cellHeight: 41, cellWidth: cellW,
|
|
382
|
+
backgroundColor: '#e7f3fc', totalWidth: cellW, textColor: '#000000',
|
|
383
|
+
labelLineColor: '#90badd', textSize: 14, textAlign: 'center',
|
|
384
|
+
contentLineColor: '#90badd',
|
|
385
|
+
labelLineStyle: { top: 1, right: 1, bottom: 1, left: 1 },
|
|
386
|
+
contentLineStyle: { top: 1, right: 1, bottom: 1, left: 1 },
|
|
387
|
+
sequenceSpan: 0, titleEnabled: false, innerSpan: 0,
|
|
388
|
+
isAddForRelation: false, format: 'yyyy-MM-dd', isCopyUpload: false,
|
|
389
|
+
richEditor: false, creatByAI: true, detailTable: false,
|
|
390
|
+
fieldId: sfid, fieldName: sf.fieldName, componentKey: sf.componentKey,
|
|
391
|
+
coords: { x: sx, y: 435, w: 33, h: 20 }, createBy: 0,
|
|
392
|
+
};
|
|
393
|
+
if (sf.fieldOptions) {
|
|
394
|
+
sub.fieldOptions = sf.fieldOptions.map(o => ({ id: randomUUID().slice(0, 8), label: o.name, value: o.valueKey || randomUUID().slice(0, 8) }));
|
|
395
|
+
}
|
|
396
|
+
return sub;
|
|
397
|
+
});
|
|
398
|
+
// 真实 createFormWithFormLayout payload 证明平台无字段数/payload 大小限制(20 主字段 + 10 子字段一次调用成功)。
|
|
399
|
+
// 之前的 MAX_INITIAL_FIELDS=10 分批是多余且有害的——把超出的 DetailTable 推进 saveFormFieldNew 分批路径,
|
|
400
|
+
// 而那不是建子表的端点,导致多明细表丢失。故所有字段一次性进 layout,DetailTable 置末。
|
|
401
|
+
const initialDefs = [
|
|
402
|
+
...fieldDefs.filter(f => f.componentKey !== 'DetailTable'),
|
|
403
|
+
...fieldDefs.filter(f => f.componentKey === 'DetailTable'),
|
|
404
|
+
];
|
|
405
|
+
const formFields = [];
|
|
406
|
+
let rowIdx = 0;
|
|
407
|
+
const sortedDefs = initialDefs;
|
|
408
|
+
for (const f of sortedDefs) {
|
|
409
|
+
const fid = randomUUID().slice(0, 8);
|
|
410
|
+
const isDetail = f.componentKey === 'DetailTable';
|
|
411
|
+
const isEBuilder = f.componentKey === 'EBuilder';
|
|
412
|
+
const subFields = isDetail && f.formFields ? buildSubFields(f.formFields) : undefined;
|
|
413
|
+
// DetailTable takes full width (colSpan 2), other fields alternate cols
|
|
414
|
+
const col = isDetail ? 0 : rowIdx % 2;
|
|
415
|
+
const field = makeField(fid, f.fieldName, f.componentKey, Math.floor(rowIdx / 2), col, f.fieldOptions, isDetail, subFields);
|
|
416
|
+
// Pass through field properties that the API natively supports
|
|
417
|
+
if (f.required !== undefined)
|
|
418
|
+
field.required = f.required;
|
|
419
|
+
if (f.readOnly !== undefined)
|
|
420
|
+
field.readOnly = f.readOnly;
|
|
421
|
+
if (f.unique !== undefined)
|
|
422
|
+
field.unique = f.unique;
|
|
423
|
+
// EBuilder association: set target form properties
|
|
424
|
+
if (isEBuilder) {
|
|
425
|
+
if (f.sformId)
|
|
426
|
+
field.sformId = f.sformId;
|
|
427
|
+
if (f.dataTitle)
|
|
428
|
+
field.dataTitle = f.dataTitle;
|
|
429
|
+
if (f.formName)
|
|
430
|
+
field.formName = f.formName;
|
|
431
|
+
}
|
|
432
|
+
field.dataKey = toColumnName(f.fieldName);
|
|
433
|
+
field.columnName = field.dataKey;
|
|
434
|
+
formFields.push(field);
|
|
435
|
+
// DetailTable occupies 2 rows of layout space
|
|
436
|
+
rowIdx += isDetail ? 2 : 1;
|
|
437
|
+
}
|
|
438
|
+
const tableName = 'uf_' + toTableName(name);
|
|
439
|
+
const totalRows = Math.ceil(formFields.length / 2);
|
|
440
|
+
const maxY = 35 + totalRows * 52 + 100;
|
|
441
|
+
const payload = {
|
|
442
|
+
system: false, refresh: false, tokenLongWarning: false, streamClosed: false,
|
|
443
|
+
layoutThemeType: 0, colNums: 2,
|
|
444
|
+
pageLayout: { sidePadding: 0.05, centerPadding: 0 },
|
|
445
|
+
supportTableType: ['physical'], mainTablePrefix: 'uf_',
|
|
446
|
+
createMenu: true, formNameLabelId: '', formTitleLabelId: '',
|
|
447
|
+
tableType: 'physical', groupId: '-1',
|
|
448
|
+
cost: { id: 'ebform.script', totalTimeMillis: 0, roots: [] },
|
|
449
|
+
actualClass: 'com.weaver.ebuilder.form.ai.entity.create.form.AiImg2FormResponse',
|
|
450
|
+
formName: name, formTitle: name, appId,
|
|
451
|
+
groups: [{
|
|
452
|
+
id: 'group0', formFields, innerSpan: 0, closeCollapse: false,
|
|
453
|
+
border: {
|
|
454
|
+
border: '1px solid #90badd', borderTop: '1px solid #90badd',
|
|
455
|
+
borderBottom: '1px solid #90badd', borderLeft: '1px solid #90badd',
|
|
456
|
+
borderRight: '1px solid #90badd',
|
|
457
|
+
},
|
|
458
|
+
virtualGroup: true, yInterval: [0, maxY + 43],
|
|
459
|
+
}],
|
|
460
|
+
origShape: [maxY + 43, 1892],
|
|
461
|
+
formValues: { formName: name, formTitle: name, tableName, tableType: 'physical' },
|
|
462
|
+
};
|
|
463
|
+
// Use standard creation flow for controllable table names
|
|
464
|
+
const tableSuffix = toTableName(name);
|
|
465
|
+
const layoutType = String(cmdOpts.layout || 'EXCEL').toUpperCase();
|
|
466
|
+
if (layoutType !== 'FLOW' && layoutType !== 'EXCEL') {
|
|
467
|
+
console.error(`--layout 只支持 FLOW(流式) 或 EXCEL(表格),收到: ${cmdOpts.layout}`);
|
|
468
|
+
process.exit(1);
|
|
469
|
+
}
|
|
470
|
+
let objId = '';
|
|
471
|
+
let formId = '';
|
|
472
|
+
let fieldGroups = [];
|
|
473
|
+
try {
|
|
474
|
+
const result = await createCustomPhysicalForm(client, baseUrl, appId, name, tableSuffix, false, layoutType);
|
|
475
|
+
objId = result.objId;
|
|
476
|
+
formId = result.formId;
|
|
477
|
+
console.error(`表单已创建,ID: ${objId}` + (formId ? ` formId: ${formId}` : ''));
|
|
478
|
+
if (result.storageType === 'share') {
|
|
479
|
+
console.error(` 存储类型: 共享表(无独立物理表名)`);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
console.error(` 数据库表名: ${result.tableName}`);
|
|
483
|
+
}
|
|
484
|
+
// Create fields and layout
|
|
485
|
+
const fieldDefsForStandard = fieldDefs.map(f => ({
|
|
486
|
+
componentKey: f.componentKey, fieldName: f.fieldName,
|
|
487
|
+
required: f.required, readOnly: f.readOnly, unique: f.unique,
|
|
488
|
+
hidden: f.hidden, formFields: f.formFields,
|
|
489
|
+
precision: f.precision, pointSize: f.pointSize,
|
|
490
|
+
percent: f.percent, defaultValue: f.defaultValue,
|
|
491
|
+
group: f.group, multiple: f.multiple,
|
|
492
|
+
singleRow: f.singleRow,
|
|
493
|
+
// 文本最大长度(Text 缺省 100、TextArea 缺省 4000)+ 日期精度 format(Date/DateTime)需透传,
|
|
494
|
+
// 否则 fields.json 标注的 maxLen/format 被丢(bug:长度默认 100 不生效、日期精度丢失)。
|
|
495
|
+
maxLen: f.maxLen, format: f.format,
|
|
496
|
+
}));
|
|
497
|
+
const fieldResult = await addInitialFieldsToCustomPhysicalForm(client, baseUrl, appId, objId, formId, fieldDefsForStandard, auth.tenantKey, layoutType);
|
|
498
|
+
fieldGroups = fieldResult.fieldGroups || [];
|
|
499
|
+
console.error(` 字段+布局已写入: ${fieldDefsForStandard.length} 个字段`);
|
|
500
|
+
const detailTables = fieldResult.detailTables || [];
|
|
501
|
+
for (const dt of detailTables) {
|
|
502
|
+
console.error(` 明细表: ${dt.definition.fieldName} subFormId: ${dt.subFormId}`);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
catch (e) {
|
|
506
|
+
console.error(`创建失败: ${e.message}`);
|
|
507
|
+
// Clean up the empty form shell so failed creates don't leave orphans.
|
|
508
|
+
if (objId) {
|
|
509
|
+
try {
|
|
510
|
+
await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/delete/${objId}?apid=${appId}&objId=${objId}`, { ebbusinessid: appId });
|
|
511
|
+
console.error(` 已清理空表壳: ${objId}`);
|
|
512
|
+
}
|
|
513
|
+
catch { /* best effort */ }
|
|
514
|
+
}
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
517
|
+
// Auto-set permission: 所有人可查看
|
|
518
|
+
try {
|
|
519
|
+
const permPayload = [{
|
|
520
|
+
belong: 'obj',
|
|
521
|
+
belongId: objId,
|
|
522
|
+
codeType: 'obj',
|
|
523
|
+
rightType: '1',
|
|
524
|
+
orgType: 'all',
|
|
525
|
+
orgId: '0',
|
|
526
|
+
orgName: '所有人',
|
|
527
|
+
orgTypeName: '所有人',
|
|
528
|
+
layoutOrder: 0,
|
|
529
|
+
isPcMobileSameLayout: '0',
|
|
530
|
+
creatorRelated: '0',
|
|
531
|
+
orgVirtualType: '',
|
|
532
|
+
excludeObj: '',
|
|
533
|
+
exportDetails: 0,
|
|
534
|
+
}];
|
|
535
|
+
const permRes = await client.post(`${baseUrl}/api/bs/ebuilder/form/permission/addRight?objId=${objId}&apid=${appId}`, permPayload, { ebbusinessid: appId });
|
|
536
|
+
if (permRes?.code === 200 && permRes?.status) {
|
|
537
|
+
console.error(' 权限已设置: 所有人可查看');
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
catch (e) {
|
|
541
|
+
console.error(` 权限设置失败: ${e.message}`);
|
|
542
|
+
}
|
|
543
|
+
// Resolve formId once for both EBuilder and relateFrom steps
|
|
544
|
+
let resolvedFormId = formId || '';
|
|
545
|
+
const resolveFormId = async () => {
|
|
546
|
+
if (!resolvedFormId) {
|
|
547
|
+
const formList = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
548
|
+
const formInfo = (formList?.data || []).find((f) => f.id === objId);
|
|
549
|
+
resolvedFormId = formInfo?.formId || '';
|
|
550
|
+
}
|
|
551
|
+
return resolvedFormId;
|
|
552
|
+
};
|
|
553
|
+
// Step 2: EBuilder fields need follow-up binding to target forms.
|
|
554
|
+
// 无 sformId 的 EBuilder 会被建成裸关联字段(isAddForRelation=false,无目标表),
|
|
555
|
+
// 这是"关联ebuilder不对"的常见来源,明确警告避免静默。
|
|
556
|
+
const ebNoTarget = fieldDefs.filter(f => f.componentKey === 'EBuilder' && !f.sformId);
|
|
557
|
+
if (ebNoTarget.length > 0) {
|
|
558
|
+
console.error(` ⚠ ${ebNoTarget.length} 个 EBuilder 字段缺少 sformId,将建成无关联裸字段:`);
|
|
559
|
+
for (const f of ebNoTarget)
|
|
560
|
+
console.error(` - ${f.fieldName}(需在 fields.json 指定 sformId=<目标表单objId> 与 dataTitle=<显示字段>)`);
|
|
561
|
+
}
|
|
562
|
+
const ebFields = fieldDefs.filter(f => f.componentKey === 'EBuilder' && f.sformId);
|
|
563
|
+
if (ebFields.length > 0) {
|
|
564
|
+
console.error(` 绑定 ${ebFields.length} 个 EBuilder 关联字段...`);
|
|
565
|
+
// Resolve target form names and field IDs from sformId
|
|
566
|
+
const formList = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
567
|
+
const allForms = formList?.data || [];
|
|
568
|
+
const formNameById = new Map();
|
|
569
|
+
for (const fm of allForms) {
|
|
570
|
+
formNameById.set(fm.id, fm.formName || fm.name || fm.id);
|
|
571
|
+
}
|
|
572
|
+
// Resolve dataTitle: if not a numeric field ID, look up target form fields
|
|
573
|
+
const targetFieldCache = new Map(); // sformId → mainFields(保持 getFields 顺序)
|
|
574
|
+
const loadTargetFields = async (sformId) => {
|
|
575
|
+
if (!targetFieldCache.has(sformId)) {
|
|
576
|
+
try {
|
|
577
|
+
const fd = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
578
|
+
objId: sformId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
579
|
+
});
|
|
580
|
+
targetFieldCache.set(sformId, fd?.data?.mainFields || []);
|
|
581
|
+
}
|
|
582
|
+
catch {
|
|
583
|
+
targetFieldCache.set(sformId, []);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return targetFieldCache.get(sformId);
|
|
587
|
+
};
|
|
588
|
+
const resolveDataTitle = async (sformId, dataTitle) => {
|
|
589
|
+
// If already a numeric ID (like "1293009997708083201"), use as-is
|
|
590
|
+
if (/^\d{15,}$/.test(dataTitle))
|
|
591
|
+
return dataTitle;
|
|
592
|
+
const fields = await loadTargetFields(sformId);
|
|
593
|
+
// Special case: "name" → 优先字段名含「名称」的业务字段,无则第一个业务字段
|
|
594
|
+
if (dataTitle === 'name')
|
|
595
|
+
return defaultEBuilderDataTitle(fields);
|
|
596
|
+
// Try exact match first, then partial match(按名称查目标表字段)
|
|
597
|
+
const nameMap = new Map(fields.map((f) => [f.fieldName, f.fieldId]));
|
|
598
|
+
return nameMap.get(dataTitle) || [...nameMap.entries()].find(([k]) => k.includes(dataTitle))?.[1] || 'name';
|
|
599
|
+
};
|
|
600
|
+
await resolveFormId();
|
|
601
|
+
if (!resolvedFormId) {
|
|
602
|
+
const formList = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
603
|
+
const formInfo = (formList?.data || []).find((f) => f.id === objId);
|
|
604
|
+
resolvedFormId = formInfo?.formId || '';
|
|
605
|
+
}
|
|
606
|
+
// Use getFormFieldPage to get full field info including columnName
|
|
607
|
+
const fieldData = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
608
|
+
objId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
609
|
+
});
|
|
610
|
+
const allFields = fieldData?.data?.mainFields || [];
|
|
611
|
+
let detailFieldsEB = [];
|
|
612
|
+
try {
|
|
613
|
+
const dtRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormFieldPage?apid=${appId}`, {
|
|
614
|
+
pageNo: 1, pageSize: 50, module: 'ebuilderform',
|
|
615
|
+
customParam: { appId, objId },
|
|
616
|
+
formFieldSearchEntity: { isDelete: 0, status: 'enable', formId: resolvedFormId },
|
|
617
|
+
}, { ebbusinessid: resolvedFormId });
|
|
618
|
+
detailFieldsEB = dtRes?.data?.pageDatas?.result || dtRes?.data?.data?.records || dtRes?.data?.data || dtRes?.data?.records || [];
|
|
619
|
+
}
|
|
620
|
+
catch { /* fallback */ }
|
|
621
|
+
const formTableId = detailFieldsEB[0]?.formTableId || '';
|
|
622
|
+
const fieldByName = new Map();
|
|
623
|
+
for (const f of allFields) {
|
|
624
|
+
const df = detailFieldsEB.find((d) => d.fieldId === f.fieldId);
|
|
625
|
+
const colName = df?.columnName || df?.dataKey || '';
|
|
626
|
+
fieldByName.set(f.fieldName, {
|
|
627
|
+
fieldId: f.fieldId,
|
|
628
|
+
dataKey: colName,
|
|
629
|
+
columnName: colName,
|
|
630
|
+
formTableId: df?.formTableId || formTableId,
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
// Update each EBuilder field with target form association
|
|
634
|
+
for (const ef of ebFields) {
|
|
635
|
+
const finfo = fieldByName.get(ef.fieldName);
|
|
636
|
+
if (!finfo) {
|
|
637
|
+
console.error(` 警告: 找不到字段 "${ef.fieldName}",跳过`);
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
// 目标表单业务字段(关联浏览弹窗显示列候选,去系统字段 fieldId 非数字)。
|
|
641
|
+
// 注意:用 ef.sformId 查目标表,不能复用源表的 allFields。
|
|
642
|
+
let targetBizFields = [];
|
|
643
|
+
try {
|
|
644
|
+
const tfd = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
645
|
+
objId: ef.sformId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
646
|
+
});
|
|
647
|
+
// 默认显示列最多 5 个(去系统字段后取前 5 个业务字段);fields.json 显式写 displayColumns 不受此限制
|
|
648
|
+
targetBizFields = (tfd?.data?.mainFields || [])
|
|
649
|
+
.filter((f) => /^\d+$/.test(String(f.fieldId)))
|
|
650
|
+
.map((f) => ({ id: String(f.fieldId), name: String(f.fieldName || f.title || '') }))
|
|
651
|
+
.slice(0, 5);
|
|
652
|
+
}
|
|
653
|
+
catch { /* 目标表查询失败则显示列留空 */ }
|
|
654
|
+
const jsonObj = {
|
|
655
|
+
formTableId: finfo.formTableId,
|
|
656
|
+
isAllFormData: false,
|
|
657
|
+
viewDataDetail: true,
|
|
658
|
+
quickAddParams: [],
|
|
659
|
+
groupId: '-1',
|
|
660
|
+
hasDisplayFormat: false,
|
|
661
|
+
openRelateView: false,
|
|
662
|
+
title: ef.fieldName, type: 'EBuilder',
|
|
663
|
+
dataCount: 0,
|
|
664
|
+
dataKey: finfo.columnName,
|
|
665
|
+
systemField: true,
|
|
666
|
+
columnTypeSwitch: false,
|
|
667
|
+
appId,
|
|
668
|
+
formName: ef.formName || formNameById.get(ef.sformId || '') || ef.sformId || '',
|
|
669
|
+
relateViewData: [],
|
|
670
|
+
singleEdit: true,
|
|
671
|
+
componentKey: 'Ebuilder',
|
|
672
|
+
typeLen: 64,
|
|
673
|
+
fieldId: finfo.fieldId, formId: resolvedFormId,
|
|
674
|
+
virtualTable: false,
|
|
675
|
+
columnType: 'VARCHAR',
|
|
676
|
+
displayType: 'line',
|
|
677
|
+
isDefault: false,
|
|
678
|
+
dataTitle: await resolveDataTitle(ef.sformId || '', ef.dataTitle || 'name'),
|
|
679
|
+
showOrder: 0,
|
|
680
|
+
isAddForRelation: true,
|
|
681
|
+
sformId: ef.sformId,
|
|
682
|
+
columnName: finfo.columnName,
|
|
683
|
+
content: [],
|
|
684
|
+
formEntity: {},
|
|
685
|
+
filterQueryParams: '{}',
|
|
686
|
+
filterParams: '{}',
|
|
687
|
+
relateField: null,
|
|
688
|
+
virtualForm: false,
|
|
689
|
+
isSingle: ef.multiple ? false : true,
|
|
690
|
+
isUnique: true,
|
|
691
|
+
// 关联浏览弹窗显示列:缺省自动用目标表全部业务字段(去系统字段);
|
|
692
|
+
// fields.json 写 displayColumns 可指定字段名列表
|
|
693
|
+
ffDisplayColumns: Array.isArray(ef.displayColumns) && ef.displayColumns.length
|
|
694
|
+
? ef.displayColumns
|
|
695
|
+
.map((name) => targetBizFields.find((f) => f.name === name))
|
|
696
|
+
.filter((f) => !!f)
|
|
697
|
+
: targetBizFields,
|
|
698
|
+
};
|
|
699
|
+
const payload = {
|
|
700
|
+
formId: resolvedFormId, module: 'ebuilderform',
|
|
701
|
+
customParam: { appId, objId },
|
|
702
|
+
jsonObj: JSON.stringify(jsonObj),
|
|
703
|
+
};
|
|
704
|
+
const updateRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, payload, { ebbusinessid: resolvedFormId });
|
|
705
|
+
if (updateRes?.code === 200 && updateRes?.status) {
|
|
706
|
+
console.error(` 已关联: ${ef.fieldName} → ${ef.sformId}`);
|
|
707
|
+
// 关联浏览弹窗显示列:Step2 直接带 ffDisplayColumns 进 jsonObj 不落库,
|
|
708
|
+
// 需用 getFormField 的完整字段配置 + ffDisplayColumns 单独 save(对齐 ebuilder-columns)。
|
|
709
|
+
try {
|
|
710
|
+
const gf = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormField`, { module: 'ebuilderform', formTableField: { formField: { id: finfo.fieldId, form: { id: resolvedFormId } } } }, { ebbusinessid: resolvedFormId });
|
|
711
|
+
const full = gf?.data || {};
|
|
712
|
+
if (full.fieldId && Array.isArray(jsonObj.ffDisplayColumns) && jsonObj.ffDisplayColumns.length) {
|
|
713
|
+
await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, { formId: resolvedFormId, module: 'ebuilderform', customParam: { appId, objId }, jsonObj: JSON.stringify({ ...full, ffDisplayColumns: jsonObj.ffDisplayColumns }) }, { ebbusinessid: resolvedFormId });
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
catch (e) {
|
|
717
|
+
console.error(` 关联显示列设置失败: ${e.message}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
console.error(` 关联失败: ${ef.fieldName} — ${updateRes?.msg || '未知错误'}`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
// Step 3: Configure ffRelateField for fields that auto-fill from EBuilder records
|
|
726
|
+
const relateFields = fieldDefs.filter(f => f.relateFrom?.ebuilderField && f.relateFrom?.sourceFieldId);
|
|
727
|
+
if (relateFields.length > 0) {
|
|
728
|
+
console.error(` 绑定 ${relateFields.length} 个字段联动...`);
|
|
729
|
+
await resolveFormId();
|
|
730
|
+
// Re-fetch fields to get updated IDs (EBuilder binding may have changed things)
|
|
731
|
+
const rfFieldData = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
732
|
+
objId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
733
|
+
});
|
|
734
|
+
const rfAllFields = rfFieldData?.data?.mainFields || [];
|
|
735
|
+
// Use getFormFieldPage to get full field info including columnName
|
|
736
|
+
let detailFields = [];
|
|
737
|
+
try {
|
|
738
|
+
const dtRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormFieldPage?apid=${appId}`, {
|
|
739
|
+
pageNo: 1, pageSize: 50, module: 'ebuilderform',
|
|
740
|
+
customParam: { appId, objId },
|
|
741
|
+
formFieldSearchEntity: { isDelete: 0, status: 'enable', formId: resolvedFormId },
|
|
742
|
+
}, { ebbusinessid: resolvedFormId });
|
|
743
|
+
detailFields = dtRes?.data?.pageDatas?.result || dtRes?.data?.data?.records || dtRes?.data?.data || dtRes?.data?.records || [];
|
|
744
|
+
}
|
|
745
|
+
catch { /* fallback */ }
|
|
746
|
+
const rfByName = new Map();
|
|
747
|
+
const rfFormTableId = detailFields[0]?.formTableId || '';
|
|
748
|
+
for (const f of rfAllFields) {
|
|
749
|
+
const df = detailFields.find((d) => d.fieldId === f.fieldId);
|
|
750
|
+
const colName = df?.columnName || df?.dataKey || '';
|
|
751
|
+
rfByName.set(f.fieldName, {
|
|
752
|
+
fieldId: f.fieldId,
|
|
753
|
+
dataKey: colName,
|
|
754
|
+
columnName: colName,
|
|
755
|
+
formTableId: df?.formTableId || rfFormTableId,
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
// Resolve formId if not already have it
|
|
759
|
+
await resolveFormId();
|
|
760
|
+
for (const rf of relateFields) {
|
|
761
|
+
const myInfo = rfByName.get(rf.fieldName);
|
|
762
|
+
const ebInfo = rfByName.get(rf.relateFrom.ebuilderField);
|
|
763
|
+
if (!myInfo) {
|
|
764
|
+
console.error(` 警告: 找不到字段 "${rf.fieldName}",跳过联动`);
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
if (!ebInfo) {
|
|
768
|
+
console.error(` 警告: 找不到关联字段 "${rf.relateFrom.ebuilderField}",跳过联动`);
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
const jsonObj = {
|
|
772
|
+
formTableId: myInfo.formTableId,
|
|
773
|
+
groupId: '-1', isUnique: false,
|
|
774
|
+
hasDisplayFormat: false,
|
|
775
|
+
title: rf.fieldName,
|
|
776
|
+
type: rf.componentKey === 'Text' ? 'Text' : rf.componentKey,
|
|
777
|
+
dataCount: 0,
|
|
778
|
+
dataKey: myInfo.columnName,
|
|
779
|
+
systemField: true,
|
|
780
|
+
readonlyTransLink: false,
|
|
781
|
+
columnTypeSwitch: false,
|
|
782
|
+
singleEdit: true,
|
|
783
|
+
componentKey: rf.componentKey,
|
|
784
|
+
typeLen: rf.componentKey === 'Text' ? 100 : 10,
|
|
785
|
+
fieldId: myInfo.fieldId,
|
|
786
|
+
maxLen: rf.componentKey === 'Text' ? 100 : undefined,
|
|
787
|
+
formId: resolvedFormId,
|
|
788
|
+
ffRelateField: {
|
|
789
|
+
component: { key: rf.componentKey },
|
|
790
|
+
isField: true,
|
|
791
|
+
title: rf.relateFrom.sourceFieldId,
|
|
792
|
+
key: rf.relateFrom.sourceFieldId,
|
|
793
|
+
isMultiple: false,
|
|
794
|
+
fieldId: ebInfo.fieldId,
|
|
795
|
+
},
|
|
796
|
+
columnType: 'VARCHAR',
|
|
797
|
+
isDefault: false,
|
|
798
|
+
unique: false,
|
|
799
|
+
showOrder: 0,
|
|
800
|
+
columnName: myInfo.columnName,
|
|
801
|
+
content: [],
|
|
802
|
+
formEntity: {},
|
|
803
|
+
};
|
|
804
|
+
const payload = {
|
|
805
|
+
formId: resolvedFormId,
|
|
806
|
+
module: 'ebuilderform',
|
|
807
|
+
customParam: { appId, objId },
|
|
808
|
+
jsonObj: JSON.stringify(jsonObj),
|
|
809
|
+
};
|
|
810
|
+
const updateRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, payload, { ebbusinessid: resolvedFormId });
|
|
811
|
+
if (updateRes?.code === 200 && updateRes?.status) {
|
|
812
|
+
console.error(` 已联动: ${rf.fieldName} ← ${rf.relateFrom.ebuilderField}.${rf.relateFrom.sourceFieldId}`);
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
console.error(` 联动失败: ${rf.fieldName} — ${updateRes?.msg || '未知错误'}`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
// Step 5: Configure Employee/Department fields — isSingle 必须经 saveFormFieldNew 重保存落库
|
|
820
|
+
// (batch/save 初建不带 isSingle 到 layout comp)。之前只对 defaultValue:true 的跑 → 没标默认值
|
|
821
|
+
// 的 Employee/Department 漏 isSingle → 平台默认多选(bug:标记单选仍多选)。现对所有 Employee/Department
|
|
822
|
+
// 跑设 isSingle;defaultExtend(默认当前用户/部门)仅 defaultValue:true 时加。
|
|
823
|
+
const personFields = fieldDefs.filter(f => f.componentKey === 'Employee' || f.componentKey === 'Department');
|
|
824
|
+
if (personFields.length > 0) {
|
|
825
|
+
const withDefault = personFields.filter(f => f.defaultValue === true);
|
|
826
|
+
console.error(` 配置 ${personFields.length} 个 人员/部门 字段 isSingle${withDefault.length ? `(其中 ${withDefault.length} 个带默认当前用户/部门)` : ''}...`);
|
|
827
|
+
await resolveFormId();
|
|
828
|
+
// Reuse already fetched field details from snDetailFields or refetch
|
|
829
|
+
let perDetailFields = [];
|
|
830
|
+
try {
|
|
831
|
+
const dtRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormFieldPage?apid=${appId}`, {
|
|
832
|
+
pageNo: 1, pageSize: 50, module: 'ebuilderform',
|
|
833
|
+
customParam: { appId, objId },
|
|
834
|
+
formFieldSearchEntity: { isDelete: 0, status: 'enable', formId: resolvedFormId },
|
|
835
|
+
}, { ebbusinessid: resolvedFormId });
|
|
836
|
+
perDetailFields = dtRes?.data?.pageDatas?.result || dtRes?.data?.data?.records || dtRes?.data?.data || dtRes?.data?.records || [];
|
|
837
|
+
}
|
|
838
|
+
catch { /* fallback */ }
|
|
839
|
+
const perFieldData = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
840
|
+
objId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
841
|
+
});
|
|
842
|
+
const perAllFields = perFieldData?.data?.mainFields || [];
|
|
843
|
+
for (const pf of personFields) {
|
|
844
|
+
// 排除同名系统字段(fieldId 非数字,如 creator/create_time),优先自定义字段
|
|
845
|
+
const perField = perAllFields.find((f) => f.fieldName === pf.fieldName && /^\d+$/.test(String(f.fieldId)));
|
|
846
|
+
const perDetail = perDetailFields.find((d) => d.fieldId === perField?.fieldId);
|
|
847
|
+
if (!perField)
|
|
848
|
+
continue;
|
|
849
|
+
const colName = perDetail?.columnName || perDetail?.dataKey || '';
|
|
850
|
+
const colType = perDetail?.columnType || 'BIGINT';
|
|
851
|
+
const isDept = pf.componentKey === 'Department';
|
|
852
|
+
const typeLabel = isDept ? 'Department' : 'Employee';
|
|
853
|
+
const labelCN = isDept ? '部门' : '人员';
|
|
854
|
+
const jsonObj = {
|
|
855
|
+
type: typeLabel,
|
|
856
|
+
title: pf.fieldName,
|
|
857
|
+
isSingle: pf.multiple ? false : true,
|
|
858
|
+
allList: [],
|
|
859
|
+
componentList: [],
|
|
860
|
+
displayType: 'space',
|
|
861
|
+
viewDataDetail: true,
|
|
862
|
+
showDimension: true,
|
|
863
|
+
pcDisplaySelectedCount: { type: 'default' },
|
|
864
|
+
mobileDisplaySelectedCount: { type: 'default' },
|
|
865
|
+
formTableId: perDetail?.formTableId || '',
|
|
866
|
+
dataKey: colName,
|
|
867
|
+
columnName: colName,
|
|
868
|
+
fieldId: perField.fieldId,
|
|
869
|
+
formId: resolvedFormId,
|
|
870
|
+
showOrder: '0.00',
|
|
871
|
+
groupId: '-1',
|
|
872
|
+
columnType: colType,
|
|
873
|
+
isUnique: true,
|
|
874
|
+
};
|
|
875
|
+
// 默认当前用户/部门仅 fields.json defaultValue:true 时配(否则不自动配默认值)
|
|
876
|
+
if (pf.defaultValue === true) {
|
|
877
|
+
jsonObj.defaultExtend = { type: 'current', relation: 'current', virtualId: '1' };
|
|
878
|
+
}
|
|
879
|
+
const payload2 = {
|
|
880
|
+
formId: resolvedFormId,
|
|
881
|
+
module: 'ebuilderform',
|
|
882
|
+
customParam: { appId, objId },
|
|
883
|
+
jsonObj: JSON.stringify(jsonObj),
|
|
884
|
+
};
|
|
885
|
+
const updateRes2 = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, payload2, { ebbusinessid: resolvedFormId });
|
|
886
|
+
if (updateRes2?.code === 200 && updateRes2?.status) {
|
|
887
|
+
console.error(` 已配置: ${pf.fieldName} isSingle=${pf.multiple ? false : true}${pf.defaultValue === true ? ` + 默认当前${labelCN}` : ''}`);
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
console.error(` 配置失败: ${pf.fieldName} — ${updateRes2?.msg || '未知错误'}`);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
// Step 6: Configure Date/DateTime fields with default current date.
|
|
895
|
+
// DateTime 日期-时间:format 带时间,时间默认时:分(yyyy-MM-dd HH:mm 不含秒)。
|
|
896
|
+
const dateFields = fieldDefs.filter(f => f.componentKey === 'Date' || f.componentKey === 'DateTime');
|
|
897
|
+
if (dateFields.length > 0) {
|
|
898
|
+
console.error(` 配置 ${dateFields.length} 个 日期/日期时间 字段精度与默认值...`);
|
|
899
|
+
await resolveFormId();
|
|
900
|
+
let dtDetailFields = [];
|
|
901
|
+
try {
|
|
902
|
+
const dtRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormFieldPage?apid=${appId}`, {
|
|
903
|
+
pageNo: 1, pageSize: 50, module: 'ebuilderform',
|
|
904
|
+
customParam: { appId, objId },
|
|
905
|
+
formFieldSearchEntity: { isDelete: 0, status: 'enable', formId: resolvedFormId },
|
|
906
|
+
}, { ebbusinessid: resolvedFormId });
|
|
907
|
+
dtDetailFields = dtRes?.data?.pageDatas?.result || dtRes?.data?.data?.records || dtRes?.data?.data || dtRes?.data?.records || [];
|
|
908
|
+
}
|
|
909
|
+
catch { /* fallback */ }
|
|
910
|
+
const dtFieldData = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
911
|
+
objId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
912
|
+
});
|
|
913
|
+
const dtAllFields = dtFieldData?.data?.mainFields || [];
|
|
914
|
+
for (const df of dateFields) {
|
|
915
|
+
// 排除同名系统字段(fieldId 非数字),优先自定义字段
|
|
916
|
+
const dtField = dtAllFields.find((f) => f.fieldName === df.fieldName && /^\d+$/.test(String(f.fieldId)));
|
|
917
|
+
const dtDetail = dtDetailFields.find((d) => d.fieldId === dtField?.fieldId);
|
|
918
|
+
if (!dtField)
|
|
919
|
+
continue;
|
|
920
|
+
const colName = dtDetail?.columnName || dtDetail?.dataKey || '';
|
|
921
|
+
// Date 默认精度 年月日(yyyy-MM-dd,对齐浏览器抓包);DateTime 默认带时:分(yyyy-MM-dd HH:mm 不含秒)。
|
|
922
|
+
// 默认值不填(isDefault:false);fields.json 写 format 覆盖精度、defaultValue:true 配动态当天默认值。
|
|
923
|
+
// 日期限制:minField 最早不得早于 / maxField 最晚不得晚于另一字段(对齐抓包 min.dateField.checkValue)。
|
|
924
|
+
const dtIsDt = df.componentKey === 'DateTime';
|
|
925
|
+
const dtFmt = df.format || (dtIsDt ? 'yyyy-MM-dd HH:mm' : 'yyyy-MM-dd');
|
|
926
|
+
const dtHasDefault = df.defaultValue === true;
|
|
927
|
+
const dtMinTargetId = df.minField ? dtAllFields.find((f) => f.fieldName === df.minField)?.fieldId : '';
|
|
928
|
+
const dtMaxTargetId = df.maxField ? dtAllFields.find((f) => f.fieldName === df.maxField)?.fieldId : '';
|
|
929
|
+
const jsonObj = {
|
|
930
|
+
type: 'Date', title: df.fieldName,
|
|
931
|
+
placeholder: placeholderFromFormat(dtFmt),
|
|
932
|
+
earlyDate: false, lateDate: false,
|
|
933
|
+
earlyType: 'currentDay', lateType: 'currentDay',
|
|
934
|
+
format: dtFmt,
|
|
935
|
+
isSystemDate: false,
|
|
936
|
+
timePanelStyle: { type: 'system' },
|
|
937
|
+
isDefault: dtHasDefault,
|
|
938
|
+
...(dtHasDefault ? { defaultExtend: { date: { type: 'current' }, time: { customValue: '00:00', type: 'custom' } } } : {}),
|
|
939
|
+
...(dtMinTargetId ? { min: { type: 'dateField', checkValue: String(dtMinTargetId) }, earlyDate: true } : {}),
|
|
940
|
+
...(dtMaxTargetId ? { max: { type: 'dateField', checkValue: String(dtMaxTargetId) }, lateDate: true } : {}),
|
|
941
|
+
formTableId: dtDetail?.formTableId || '',
|
|
942
|
+
dataKey: colName, columnName: colName,
|
|
943
|
+
fieldId: dtField.fieldId, formId: resolvedFormId,
|
|
944
|
+
showOrder: '0.00', groupId: '-1',
|
|
945
|
+
};
|
|
946
|
+
const payload = {
|
|
947
|
+
formId: resolvedFormId, module: 'ebuilderform',
|
|
948
|
+
customParam: { appId, objId },
|
|
949
|
+
jsonObj: JSON.stringify(jsonObj),
|
|
950
|
+
};
|
|
951
|
+
const updateRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, payload, { ebbusinessid: resolvedFormId });
|
|
952
|
+
if (updateRes?.code === 200 && updateRes?.status) {
|
|
953
|
+
console.error(` 已配置: ${df.fieldName} 精度 ${dtFmt}${dtHasDefault ? ' + 默认当前日期' : ''}`);
|
|
954
|
+
}
|
|
955
|
+
else {
|
|
956
|
+
console.error(` 配置失败: ${df.fieldName} — ${updateRes?.msg || '未知错误'}`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
// Step 6b: Number/Money 千分位与大写(isDelimiter/isCapital/capitalFormat)。
|
|
961
|
+
// batch/save 建字段不落这些高级属性,需 saveFormFieldNew 补(对齐 Date Step6)。
|
|
962
|
+
const numMoneyFields = fieldDefs.filter(f => f.componentKey === 'Number' || f.componentKey === 'Money');
|
|
963
|
+
if (numMoneyFields.length > 0) {
|
|
964
|
+
console.error(` 配置 ${numMoneyFields.length} 个 数字/金额 千分位与大写...`);
|
|
965
|
+
try {
|
|
966
|
+
const nmFieldData = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
967
|
+
objId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
968
|
+
});
|
|
969
|
+
const nmAllFields = nmFieldData?.data?.mainFields || [];
|
|
970
|
+
for (const nf of numMoneyFields) {
|
|
971
|
+
const nmField = nmAllFields.find((f) => f.fieldName === nf.fieldName && /^\d+$/.test(String(f.fieldId)));
|
|
972
|
+
if (!nmField)
|
|
973
|
+
continue;
|
|
974
|
+
const nmGf = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormField`, {
|
|
975
|
+
module: 'ebuilderform', formTableField: { formField: { id: nmField.fieldId, form: { id: resolvedFormId } } },
|
|
976
|
+
}, { ebbusinessid: resolvedFormId });
|
|
977
|
+
const nmFull = nmGf?.data || {};
|
|
978
|
+
if (!nmFull.fieldId)
|
|
979
|
+
continue;
|
|
980
|
+
// 千分位:Money 默认开(delimiter:false 关闭)、Number 默认关(delimiter:true 开启)
|
|
981
|
+
const nmIsDelim = nf.componentKey === 'Money' ? nf.delimiter !== false : nf.delimiter === true;
|
|
982
|
+
const nmIsCap = nf.capital === true;
|
|
983
|
+
const nmPatched = {
|
|
984
|
+
...nmFull,
|
|
985
|
+
isDelimiter: nmIsDelim,
|
|
986
|
+
isCapital: nmIsCap,
|
|
987
|
+
capitalFormat: 'onlyUpperCase',
|
|
988
|
+
capitalSetting: 'system',
|
|
989
|
+
capitalPosition: 'outside',
|
|
990
|
+
capitalPrecision: 'yuan',
|
|
991
|
+
isExchangeRates: false,
|
|
992
|
+
};
|
|
993
|
+
const nmUp = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, {
|
|
994
|
+
formId: resolvedFormId, module: 'ebuilderform', customParam: { appId, objId },
|
|
995
|
+
jsonObj: JSON.stringify(nmPatched),
|
|
996
|
+
}, { ebbusinessid: resolvedFormId });
|
|
997
|
+
if (nmUp?.code === 200 && nmUp?.status) {
|
|
998
|
+
console.error(` 已配置: ${nf.fieldName}${nmIsDelim ? ' 千分位' : ''}${nmIsCap ? ' 大写' : ''}`);
|
|
999
|
+
}
|
|
1000
|
+
else {
|
|
1001
|
+
console.error(` 配置失败: ${nf.fieldName} — ${nmUp?.msg || '未知错误'}`);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
catch (e) {
|
|
1006
|
+
console.error(` 千分位/大写配置异常: ${e.message}`);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
// Step 7: Configure Select/RadioBox/CheckBox field options and defaults
|
|
1010
|
+
const optionFields = fieldDefs.filter(f => f.fieldOptions && f.fieldOptions.length > 0
|
|
1011
|
+
&& (f.componentKey === 'Select' || f.componentKey === 'RadioBox' || f.componentKey === 'CheckBox'));
|
|
1012
|
+
if (optionFields.length > 0) {
|
|
1013
|
+
await resolveFormId();
|
|
1014
|
+
console.error(` 配置 ${optionFields.length} 个选项字段...`);
|
|
1015
|
+
// Generate option IDs
|
|
1016
|
+
const optIdCount = optionFields.reduce((sum, f) => sum + (f.fieldOptions?.length || 0), 0);
|
|
1017
|
+
const genData = await client.post(`${baseUrl}/api/form/local/generatorIds?size=${optIdCount + 1}&apid=${appId}`, {});
|
|
1018
|
+
const genIds = genData?.data?.generatorIds || [];
|
|
1019
|
+
let genIdx = 0;
|
|
1020
|
+
// Read existing physical column names from the data source so that
|
|
1021
|
+
// re-saving the field (to attach options) does NOT overwrite the
|
|
1022
|
+
// build-time column. Without this, saveFormFieldNew reassigns a
|
|
1023
|
+
// platform `f_<id>` column and clobbers our `zt_`/`mc_` naming.
|
|
1024
|
+
const dsFieldCol = new Map();
|
|
1025
|
+
try {
|
|
1026
|
+
const dsForm = new FormData();
|
|
1027
|
+
for (const [k, v] of Object.entries({
|
|
1028
|
+
groupId: 'weaver-ebuilder-form-service', sourceId: objId, sourceType: 'LOGIC',
|
|
1029
|
+
detailFieldsGroup: 'true', enableMerge: 'true', needTableName: 'true', params: '[]',
|
|
1030
|
+
}))
|
|
1031
|
+
dsForm.append(k, v);
|
|
1032
|
+
const dsRes = await client.postMultipart(`${baseUrl}/api/ebuilder/common/ds/fields`, dsForm, { ebbusinessid: appId });
|
|
1033
|
+
for (const f of (Array.isArray(dsRes?.data) ? dsRes.data : [])
|
|
1034
|
+
.flatMap((g) => g?.fields ?? [])
|
|
1035
|
+
.filter((f) => !f?.isDetailtable)) {
|
|
1036
|
+
if (f.physicalField && !/[一-龥]/.test(String(f.physicalField))) {
|
|
1037
|
+
dsFieldCol.set(String(f.id), String(f.physicalField));
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
catch { /* non-critical: fall back to toColumnName */ }
|
|
1042
|
+
for (const ofd of optionFields) {
|
|
1043
|
+
try {
|
|
1044
|
+
// Find the field's actual fieldId from the form
|
|
1045
|
+
const fieldsRes = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, { objId, userid: auth.userId, tenantKey: auth.tenantKey });
|
|
1046
|
+
const mainFields = fieldsRes?.data?.mainFields || [];
|
|
1047
|
+
const targetField = mainFields.find(f => f.fieldName === ofd.fieldName);
|
|
1048
|
+
if (!targetField) {
|
|
1049
|
+
console.error(` ⚠ 未找到字段: ${ofd.fieldName}`);
|
|
1050
|
+
genIdx += ofd.fieldOptions.length;
|
|
1051
|
+
continue;
|
|
1052
|
+
}
|
|
1053
|
+
const { randomUUID } = await import('crypto');
|
|
1054
|
+
const defaultVk = ofd.defaultValue;
|
|
1055
|
+
const options = ofd.fieldOptions.map((o, i) => {
|
|
1056
|
+
const optId = genIds[genIdx++] || String(Date.now() + i);
|
|
1057
|
+
return {
|
|
1058
|
+
other: false,
|
|
1059
|
+
selectionId: optId,
|
|
1060
|
+
defOption: defaultVk ? (o.valueKey === defaultVk) : false,
|
|
1061
|
+
styleConfig: {}, archive: false, divisionBranch: false,
|
|
1062
|
+
subCompanyCatalogues: [], dataCount: i === 0 ? ofd.fieldOptions.length : 0,
|
|
1063
|
+
score: '0', uid: randomUUID(), children: [], extra: {},
|
|
1064
|
+
name: o.name, isLimit: false,
|
|
1065
|
+
valueKey: optId,
|
|
1066
|
+
componentKey: 'Option', order: i, fieldId: targetField.fieldId,
|
|
1067
|
+
};
|
|
1068
|
+
});
|
|
1069
|
+
// Use the EXISTING physical column name from the data source (build-time
|
|
1070
|
+
// value) so we don't overwrite it. Fall back to toColumnName (pinyin
|
|
1071
|
+
// initials + dup) only if the data source didn't return one.
|
|
1072
|
+
const existingCol = dsFieldCol.get(String(targetField.fieldId));
|
|
1073
|
+
const fieldDataKey = existingCol || toColumnName(ofd.fieldName);
|
|
1074
|
+
// 下拉多选是独立类型 SelectMultiple(非 Select+开关)。Select + multiple → type/componentKey=SelectMultiple
|
|
1075
|
+
const isSelectMulti = ofd.componentKey === 'Select' && ofd.multiple === true;
|
|
1076
|
+
const optCompKey = isSelectMulti ? 'SelectMultiple' : ofd.componentKey;
|
|
1077
|
+
const jsonObj = {
|
|
1078
|
+
formTableId: targetField.formTableId || '', hideScore: true,
|
|
1079
|
+
groupId: '-1', hasDisplayFormat: false,
|
|
1080
|
+
title: ofd.fieldName, type: optCompKey, dataCount: 1,
|
|
1081
|
+
dataKey: fieldDataKey,
|
|
1082
|
+
systemField: true, columnTypeSwitch: false, options,
|
|
1083
|
+
showType: ofd.componentKey, singleEdit: true,
|
|
1084
|
+
componentKey: optCompKey, typeLen: 20,
|
|
1085
|
+
fieldId: targetField.fieldId, formId: resolvedFormId,
|
|
1086
|
+
columnType: 'VARCHAR', hasChild: false, showOrder: 9,
|
|
1087
|
+
columnName: existingCol || fieldDataKey,
|
|
1088
|
+
};
|
|
1089
|
+
const payload = {
|
|
1090
|
+
formId: resolvedFormId, module: 'ebuilderform',
|
|
1091
|
+
customParam: { appId, objId },
|
|
1092
|
+
jsonObj: JSON.stringify(jsonObj),
|
|
1093
|
+
};
|
|
1094
|
+
const optRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, payload, { ebbusinessid: resolvedFormId });
|
|
1095
|
+
if (optRes?.code === 200 && optRes?.status) {
|
|
1096
|
+
const matchedOpt = defaultVk
|
|
1097
|
+
? ofd.fieldOptions.find(o => o.valueKey === defaultVk)
|
|
1098
|
+
: null;
|
|
1099
|
+
const defMsg = matchedOpt ? ` (默认: ${matchedOpt.name})` : '';
|
|
1100
|
+
console.error(` 已配置: ${ofd.fieldName} ${ofd.fieldOptions.length} 个选项${defMsg}`);
|
|
1101
|
+
}
|
|
1102
|
+
else {
|
|
1103
|
+
console.error(` 配置失败: ${ofd.fieldName} — ${optRes?.msg || '未知错误'}`);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
catch (e) {
|
|
1107
|
+
console.error(` 配置异常: ${ofd.fieldName} — ${e.message}`);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
// Step 7b: 明细表子字段的 Select/RadioBox/CheckBox 选项。
|
|
1112
|
+
// Step 7 只处理主表字段,子字段选项从不配置 → 明细表 Select 无选项。
|
|
1113
|
+
const detailOptionDefs = fieldDefs
|
|
1114
|
+
.filter(f => f.componentKey === 'DetailTable' && Array.isArray(f.formFields))
|
|
1115
|
+
.flatMap(dt => (dt.formFields || [])
|
|
1116
|
+
.filter(sf => sf.fieldOptions && sf.fieldOptions.length > 0
|
|
1117
|
+
&& (sf.componentKey === 'Select' || sf.componentKey === 'RadioBox' || sf.componentKey === 'CheckBox'))
|
|
1118
|
+
.map(sf => ({ ...sf, __detailTable: dt.fieldName })));
|
|
1119
|
+
if (detailOptionDefs.length > 0) {
|
|
1120
|
+
await resolveFormId();
|
|
1121
|
+
console.error(` 配置 ${detailOptionDefs.length} 个明细子字段选项...`);
|
|
1122
|
+
// 1. getFields(isReturnDetail='y') 拿子字段 fieldId/subFormId;子表 formTableId 从布局取
|
|
1123
|
+
let subFieldMap = new Map();
|
|
1124
|
+
let subFormTableId = '';
|
|
1125
|
+
try {
|
|
1126
|
+
const dfRes = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
1127
|
+
objId, userid: auth.userId, tenantKey: auth.tenantKey, isReturnDetail: 'y',
|
|
1128
|
+
});
|
|
1129
|
+
for (const dt of (dfRes?.data?.detailFields || [])) {
|
|
1130
|
+
const dtName = dt.name || dt.subFormName || '';
|
|
1131
|
+
for (const sf of (dt.fields || []))
|
|
1132
|
+
subFieldMap.set(`${dtName}/${sf.fieldName}`, sf);
|
|
1133
|
+
}
|
|
1134
|
+
const layoutRes = await client.post(`${baseUrl}/api/ebuilder/form/builder/getFormLayout`, {
|
|
1135
|
+
formId: resolvedFormId, module: 'ebuilderform', customParam: {}, formTableIdList: [],
|
|
1136
|
+
designerParam: { onlyLayout: false, convert: false }, form: { id: resolvedFormId },
|
|
1137
|
+
}, { ebbusinessid: resolvedFormId });
|
|
1138
|
+
const pages = (layoutRes?.data?.formLayoutMultiple?.pageLayouts || layoutRes?.data?.formLayout?.pageLayouts || []);
|
|
1139
|
+
for (const p of pages) {
|
|
1140
|
+
for (const c of (p.comps || [])) {
|
|
1141
|
+
if (String(c.type) === 'DetailTable')
|
|
1142
|
+
subFormTableId = c.config?.formTableId || '';
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
catch { /* 拿不到就跳过 */ }
|
|
1147
|
+
// 2. 生成选项 ID
|
|
1148
|
+
const subOptCount = detailOptionDefs.reduce((s, sf) => s + (sf.fieldOptions?.length || 0), 0);
|
|
1149
|
+
const subGen = await client.post(`${baseUrl}/api/form/local/generatorIds?size=${subOptCount + 1}&apid=${appId}`, {});
|
|
1150
|
+
const subGenIds = subGen?.data?.generatorIds || [];
|
|
1151
|
+
let subGenIdx = 0;
|
|
1152
|
+
for (const sfDef of detailOptionDefs) {
|
|
1153
|
+
try {
|
|
1154
|
+
const sf = subFieldMap.get(`${sfDef.__detailTable}/${sfDef.fieldName}`);
|
|
1155
|
+
if (!sf) {
|
|
1156
|
+
console.error(` ⚠ 未找到明细子字段: ${sfDef.__detailTable}.${sfDef.fieldName}`);
|
|
1157
|
+
subGenIdx += sfDef.fieldOptions.length;
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
const subFieldId = String(sf.fieldId);
|
|
1161
|
+
const subFormId = String(sf.subFormId || '');
|
|
1162
|
+
// 读子字段当前信息(拿 dataKey/columnName,保存时保留列名)
|
|
1163
|
+
let cur = {};
|
|
1164
|
+
try {
|
|
1165
|
+
const gf = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormField`, {
|
|
1166
|
+
module: 'ebuilderform',
|
|
1167
|
+
formTableField: { formField: { id: subFieldId, form: { id: resolvedFormId } } },
|
|
1168
|
+
}, { ebbusinessid: resolvedFormId });
|
|
1169
|
+
cur = gf?.data || {};
|
|
1170
|
+
}
|
|
1171
|
+
catch { /* best effort */ }
|
|
1172
|
+
// getFormField 读不到该子字段(字段尚未提交/查询失败)时跳过,避免用最小 jsonObj 覆盖破坏
|
|
1173
|
+
if (!cur?.fieldId) {
|
|
1174
|
+
console.error(` ⚠ 未读到子字段 ${sfDef.fieldName}(fieldId=${subFieldId}),跳过选项配置`);
|
|
1175
|
+
subGenIdx += sfDef.fieldOptions.length;
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
const dataKey = cur.dataKey || toColumnName(sfDef.fieldName);
|
|
1179
|
+
const { randomUUID } = await import('crypto');
|
|
1180
|
+
const options = sfDef.fieldOptions.map((o, i) => {
|
|
1181
|
+
const optId = subGenIds[subGenIdx++] || String(Date.now() + i);
|
|
1182
|
+
return {
|
|
1183
|
+
other: false, selectionId: optId, defOption: false,
|
|
1184
|
+
styleConfig: {}, archive: false, divisionBranch: false,
|
|
1185
|
+
subCompanyCatalogues: [], dataCount: i === 0 ? sfDef.fieldOptions.length : 0,
|
|
1186
|
+
score: '0', uid: randomUUID(), children: [], extra: {},
|
|
1187
|
+
name: o.name, isLimit: false, valueKey: optId,
|
|
1188
|
+
componentKey: 'Option', order: i, fieldId: subFieldId,
|
|
1189
|
+
};
|
|
1190
|
+
});
|
|
1191
|
+
const jsonObj = {
|
|
1192
|
+
formTableId: subFormTableId || cur.formTableId || '', hideScore: true, groupId: '-1',
|
|
1193
|
+
hasDisplayFormat: false, title: sfDef.fieldName, type: sfDef.componentKey, dataCount: 1,
|
|
1194
|
+
dataKey, systemField: true, columnTypeSwitch: false, options,
|
|
1195
|
+
showType: sfDef.componentKey, singleEdit: true, componentKey: sfDef.componentKey, typeLen: 20,
|
|
1196
|
+
fieldId: subFieldId, formId: resolvedFormId, columnType: 'VARCHAR',
|
|
1197
|
+
hasChild: false, showOrder: 9, columnName: dataKey, subFormId,
|
|
1198
|
+
};
|
|
1199
|
+
const payload = {
|
|
1200
|
+
formId: resolvedFormId, module: 'ebuilderform',
|
|
1201
|
+
customParam: { appId, objId },
|
|
1202
|
+
jsonObj: JSON.stringify(jsonObj),
|
|
1203
|
+
};
|
|
1204
|
+
const optRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, payload, { ebbusinessid: resolvedFormId });
|
|
1205
|
+
if (optRes?.code === 200 && optRes?.status) {
|
|
1206
|
+
console.error(` 已配置: 明细子字段 ${sfDef.fieldName} ${sfDef.fieldOptions.length} 个选项`);
|
|
1207
|
+
}
|
|
1208
|
+
else {
|
|
1209
|
+
console.error(` 配置失败: ${sfDef.fieldName} — ${optRes?.msg || '未知错误'}`);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
catch (e) {
|
|
1213
|
+
console.error(` 配置异常: ${sfDef.fieldName} — ${e.message}`);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
// Step 8: 字段公式(fields.json 的 formula 属性 → 创建公式并绑定到目标字段)。
|
|
1218
|
+
// 公式语法与 `form formula set --expression` 一致:{主表.字段名} 引用其他字段。
|
|
1219
|
+
// 扁平化:主表字段 formula + 明细子字段 formula(明细子字段嵌在 DetailTable.formFields,
|
|
1220
|
+
// 不扁平会被 fieldDefs.filter 漏掉,导致明细公式建表时不绑定,需事后 form formula set 补)
|
|
1221
|
+
const formulaDefs = [];
|
|
1222
|
+
for (const f of fieldDefs) {
|
|
1223
|
+
if (f.componentKey === 'DetailTable' && Array.isArray(f.formFields)) {
|
|
1224
|
+
for (const sf of f.formFields) {
|
|
1225
|
+
if (sf.formula)
|
|
1226
|
+
formulaDefs.push({ fieldName: String(sf.fieldName), formula: sf.formula, detailTable: String(f.fieldName) });
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
else if (f.formula) {
|
|
1230
|
+
formulaDefs.push({ fieldName: String(f.fieldName), formula: f.formula });
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
if (formulaDefs.length > 0) {
|
|
1234
|
+
await resolveFormId();
|
|
1235
|
+
console.error(` 配置 ${formulaDefs.length} 个字段公式...`);
|
|
1236
|
+
let allFields = [];
|
|
1237
|
+
let detailFields = [];
|
|
1238
|
+
try {
|
|
1239
|
+
const fRes = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
1240
|
+
objId, userid: auth.userId, tenantKey: auth.tenantKey, isReturnDetail: 'y',
|
|
1241
|
+
});
|
|
1242
|
+
allFields = fRes?.data?.mainFields || [];
|
|
1243
|
+
// detailFields: [{ name, id, fields:[{fieldName,fieldId,fieldKey,...}] }](公式可引用明细字段,如 SUM({付款信息.付款金额}))
|
|
1244
|
+
detailFields = fRes?.data?.detailFields || [];
|
|
1245
|
+
}
|
|
1246
|
+
catch { /* 读不到字段则公式会报"引用字段不存在" */ }
|
|
1247
|
+
for (const fd of formulaDefs) {
|
|
1248
|
+
try {
|
|
1249
|
+
const formula = (typeof fd.formula === 'string')
|
|
1250
|
+
? { expression: fd.formula, returnType: 'Number' }
|
|
1251
|
+
: (fd.formula || {});
|
|
1252
|
+
const expression = formula.expression || formula.formulaExpress || '';
|
|
1253
|
+
if (!expression) {
|
|
1254
|
+
console.error(` ⚠ ${fd.fieldName} 公式表达式为空`);
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
1257
|
+
// 目标字段:主表字段查 allFields,明细子字段查所属明细表的 fields(带 detailTable 标记)
|
|
1258
|
+
let targetField;
|
|
1259
|
+
if (fd.detailTable) {
|
|
1260
|
+
const dt = detailFields.find((d) => d.name === fd.detailTable || String(d.id) === fd.detailTable);
|
|
1261
|
+
targetField = dt && (dt.fields || []).find((f) => f.fieldName === fd.fieldName && /^\d+$/.test(String(f.fieldId)));
|
|
1262
|
+
}
|
|
1263
|
+
else {
|
|
1264
|
+
targetField = allFields.find((f) => f.fieldName === fd.fieldName && /^\d+$/.test(String(f.fieldId)));
|
|
1265
|
+
}
|
|
1266
|
+
if (!targetField) {
|
|
1267
|
+
console.error(` ⚠ 未找到公式目标字段: ${fd.detailTable ? fd.detailTable + '.' : ''}${fd.fieldName}`);
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
const targetFieldId = String(targetField.fieldId);
|
|
1271
|
+
// 解析 {主表.字段名} / {明细表名.字段名} 引用(不去重:同一字段引用多次时每个出现位置
|
|
1272
|
+
// 都要一个参数,否则平台报「参数列表与执行语句不一致」)
|
|
1273
|
+
const refRegex = /\{(?:主表|([^.}]+))\.([^}]+)\}/g;
|
|
1274
|
+
const refNames = [];
|
|
1275
|
+
let rm;
|
|
1276
|
+
while ((rm = refRegex.exec(expression)) !== null) {
|
|
1277
|
+
const table = rm[1] ? rm[1].trim() : '主表';
|
|
1278
|
+
const field = rm[2].trim();
|
|
1279
|
+
refNames.push({ table, field });
|
|
1280
|
+
}
|
|
1281
|
+
const parameters = [];
|
|
1282
|
+
let refMissing = false;
|
|
1283
|
+
for (const ref of refNames) {
|
|
1284
|
+
let rf;
|
|
1285
|
+
if (ref.table === '主表') {
|
|
1286
|
+
rf = allFields.find((f) => f.fieldName === ref.field || f.title === ref.field || String(f.fieldId) === ref.field);
|
|
1287
|
+
}
|
|
1288
|
+
else {
|
|
1289
|
+
const dt = detailFields.find((d) => d.name === ref.table || String(d.id) === ref.table);
|
|
1290
|
+
rf = dt && (dt.fields || []).find((f) => f.fieldName === ref.field || String(f.fieldId) === ref.field);
|
|
1291
|
+
}
|
|
1292
|
+
if (!rf) {
|
|
1293
|
+
console.error(` ⚠ 公式引用字段不存在: ${ref.table}.${ref.field}`);
|
|
1294
|
+
refMissing = true;
|
|
1295
|
+
break;
|
|
1296
|
+
}
|
|
1297
|
+
const param = {
|
|
1298
|
+
fieldId: String(rf.fieldId), formId: resolvedFormId, module: 'ebuilderform',
|
|
1299
|
+
properKey: rf.fieldKey || rf.type || 'Text', title: rf.title || rf.fieldName,
|
|
1300
|
+
name: rf.fieldName || rf.title, appId, id: null,
|
|
1301
|
+
};
|
|
1302
|
+
if (ref.table !== '主表') {
|
|
1303
|
+
const dt = detailFields.find((d) => d.name === ref.table || String(d.id) === ref.table);
|
|
1304
|
+
if (dt) {
|
|
1305
|
+
param.sFormId = String(dt.id);
|
|
1306
|
+
param.sFormName = dt.name;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
parameters.push(param);
|
|
1310
|
+
}
|
|
1311
|
+
if (refMissing)
|
|
1312
|
+
continue;
|
|
1313
|
+
const formulaName = formula.name || `公式_${fd.fieldName}`;
|
|
1314
|
+
const savePayload = {
|
|
1315
|
+
expressSql: expression, name: formulaName, parameters,
|
|
1316
|
+
returnType: formula.returnType || 'Number', validateType: 'normal',
|
|
1317
|
+
referenceType: 'assign',
|
|
1318
|
+
extendParam: JSON.stringify({ exceptIds: targetFieldId, createType: 'once' }),
|
|
1319
|
+
formulaId: null, dataSources: [], formulaType: 'normal',
|
|
1320
|
+
mainDataSource: [null], tempVariableList: [], nameAuto: false,
|
|
1321
|
+
module: 'ebuilderform', multiFuncSepType: 1,
|
|
1322
|
+
};
|
|
1323
|
+
const saveRes = await client.post(`${baseUrl}/api/excel/formula/save`, savePayload);
|
|
1324
|
+
if (!saveRes?.resultJson?.excute) {
|
|
1325
|
+
console.error(` 公式保存失败: ${saveRes?.resultJson?.error || saveRes?.msg || '未知错误'}`);
|
|
1326
|
+
continue;
|
|
1327
|
+
}
|
|
1328
|
+
const expressId = saveRes.resultJson.id;
|
|
1329
|
+
// 浏览器实测绑定分两步:
|
|
1330
|
+
// a) saveFormFieldNew 重存该字段,jsonObj 带 ffExpress(字段↔公式关联)
|
|
1331
|
+
// b) saveSourceForm 注册公式源(sourceForms + filter.fieldId 指明目标字段)
|
|
1332
|
+
try {
|
|
1333
|
+
const gf = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormField`, {
|
|
1334
|
+
module: 'ebuilderform',
|
|
1335
|
+
formTableField: { formField: { id: targetFieldId, form: { id: resolvedFormId } } },
|
|
1336
|
+
}, { ebbusinessid: resolvedFormId });
|
|
1337
|
+
const cur = gf?.data || {};
|
|
1338
|
+
// 读不到目标字段时跳过绑定(公式已创建,避免用最小 jsonObj 覆盖破坏字段)
|
|
1339
|
+
if (!cur?.fieldId) {
|
|
1340
|
+
console.error(` ⚠ 未读到目标字段 ${fd.fieldName}(fieldId=${targetFieldId}),公式已创建但未绑定`);
|
|
1341
|
+
continue;
|
|
1342
|
+
}
|
|
1343
|
+
const bindJson = { ...cur, ffExpress: { id: expressId, name: formulaName } };
|
|
1344
|
+
const bindRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, {
|
|
1345
|
+
formId: resolvedFormId, module: 'ebuilderform',
|
|
1346
|
+
customParam: { appId, objId },
|
|
1347
|
+
jsonObj: JSON.stringify(bindJson),
|
|
1348
|
+
}, { ebbusinessid: resolvedFormId });
|
|
1349
|
+
if (bindRes?.code !== 200 || !bindRes?.status) {
|
|
1350
|
+
console.error(` 公式绑定失败: ${fd.fieldName} — ${bindRes?.msg || '未知错误'}`);
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
await client.post(`${baseUrl}/api/excel/formula/library/saveSourceForm`, {
|
|
1354
|
+
sourceForms: [{ id: resolvedFormId, module: 'ebuilderform' }],
|
|
1355
|
+
expressId,
|
|
1356
|
+
filter: { fieldId: targetFieldId },
|
|
1357
|
+
});
|
|
1358
|
+
console.error(` 已配置: ${fd.fieldName} 公式 ${expression}`);
|
|
1359
|
+
}
|
|
1360
|
+
catch (e) {
|
|
1361
|
+
console.error(` 公式绑定异常: ${fd.fieldName} — ${e.message}`);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
catch (e) {
|
|
1365
|
+
console.error(` 公式配置异常: ${fd.fieldName} — ${e.message}`);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
// Auto-add 台账 (data-list) menu — unless --no-menu
|
|
1370
|
+
let menuId = null;
|
|
1371
|
+
// 字段分组分配:放到所有 saveFormFieldNew(选项 Step7/公式 Step8/关联/流水号等)
|
|
1372
|
+
// 之后做——这些重保存会重置 field.groupId,先分配会被冲掉。此处是最后写入,保住分组归属。
|
|
1373
|
+
if (fieldGroups.length > 0) {
|
|
1374
|
+
try {
|
|
1375
|
+
for (const g of fieldGroups) {
|
|
1376
|
+
await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/updateFormFieldGroup?apid=${appId}`, { module: 'ebuilderform', formFieldGroup: { id: g.groupId }, form: { id: formId },
|
|
1377
|
+
fieldList: g.fieldIds.map((id) => ({ id })), customParam: { appId, objId } }, { ebbusinessid: formId });
|
|
1378
|
+
}
|
|
1379
|
+
console.error(` 字段分组已分配: ${fieldGroups.map((g) => `${g.name}(${g.fieldIds.length})`).join(', ')}`);
|
|
1380
|
+
// 分组分配后重新 initLayout:平台新版 initLayout 据 saveFieldGroup 分组记录自动生成
|
|
1381
|
+
// Collapse 容器,但 create 流程里 initLayout 发生在分配之前,需分配后再跑一次(对齐浏览器
|
|
1382
|
+
// 抓包 payload:configs 平铺、分组字段前置、无 groupFieldIds、styleTemplateId=007)。
|
|
1383
|
+
try {
|
|
1384
|
+
const reapplied = await reapplyGroupLayout(client, baseUrl, appId, formId, objId, new Set(fieldGroups.flatMap((g) => g.fieldIds)), cmdOpts.layout);
|
|
1385
|
+
if (reapplied)
|
|
1386
|
+
console.error(' 分组布局已重排');
|
|
1387
|
+
}
|
|
1388
|
+
catch (e) {
|
|
1389
|
+
console.error(` 分组布局重排失败: ${e.message}`);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
catch (e) {
|
|
1393
|
+
console.error(` 字段分组分配失败: ${e.message}(分组已建,字段未分配,可在设计器手动拖入分组)`);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
if (cmdOpts.menu !== false) {
|
|
1397
|
+
try {
|
|
1398
|
+
const { addFormTableViewMenu } = await import('./menu.js');
|
|
1399
|
+
menuId = await addFormTableViewMenu(client, baseUrl, appId, objId, name);
|
|
1400
|
+
console.error(` 台账菜单已添加${menuId ? ` (id=${menuId})` : ''}`);
|
|
1401
|
+
}
|
|
1402
|
+
catch (e) {
|
|
1403
|
+
console.error(` 台账菜单添加失败: ${e.message}(可用 \`e10-cli menu add-table ${appId} ${objId}\` 手动补加)`);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
// Fallback: some on-prem environments don't auto-generate the default
|
|
1407
|
+
// nListView display columns (tableField stays empty → empty 台账). Patch
|
|
1408
|
+
// it here so the 台账 actually shows fields. No-op when already populated
|
|
1409
|
+
// (cloud envs auto-generate); never overwrites user-configured columns.
|
|
1410
|
+
try {
|
|
1411
|
+
const { ensureDefaultListDisplayFields } = await import('../services/form-table-view.js');
|
|
1412
|
+
const ensured = await ensureDefaultListDisplayFields(client, baseUrl, appId, objId);
|
|
1413
|
+
if (ensured.ensured) {
|
|
1414
|
+
console.error(` 显示列已兜底补全: ${ensured.visibleFieldCount} 列(环境未自动生成,CLI 补加)`);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
catch (e) {
|
|
1418
|
+
console.error(` 显示列兜底失败: ${e.message}(可用 \`e10-cli form table-view ensure ${objId} ${appId}\` 手动补加)`);
|
|
1419
|
+
}
|
|
1420
|
+
if (opts.json) {
|
|
1421
|
+
console.log(JSON.stringify({ objId, name, menuId }));
|
|
1422
|
+
}
|
|
1423
|
+
else {
|
|
1424
|
+
console.log(`完成: ${objId}`);
|
|
1425
|
+
}
|
|
1426
|
+
});
|
|
1427
|
+
const linkageCmd = form.command('linkage').description('表单字段显隐联动管理');
|
|
1428
|
+
linkageCmd.command('add')
|
|
1429
|
+
.description('添加字段显隐联动规则')
|
|
1430
|
+
.argument('<appId>', '应用 ID')
|
|
1431
|
+
.argument('<objId>', '表单 objId')
|
|
1432
|
+
.argument('<name>', '联动规则名称')
|
|
1433
|
+
.requiredOption('--config <json>', '联动配置 JSON(@文件路径),格式见文档')
|
|
1434
|
+
.action(async (appId, objId, name, cmdOpts) => {
|
|
1435
|
+
const opts = program.opts();
|
|
1436
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
1437
|
+
if (!auth)
|
|
1438
|
+
process.exit(1);
|
|
1439
|
+
const client = new ApiClient(auth);
|
|
1440
|
+
const baseUrl = auth.getBaseUrl();
|
|
1441
|
+
// Parse config
|
|
1442
|
+
let configStr = cmdOpts.config;
|
|
1443
|
+
if (configStr.startsWith('@')) {
|
|
1444
|
+
const { readFileSync, existsSync } = await import('fs');
|
|
1445
|
+
const filePath = configStr.slice(1);
|
|
1446
|
+
if (!existsSync(filePath)) {
|
|
1447
|
+
console.error(`配置文件不存在: ${filePath}`);
|
|
1448
|
+
process.exit(1);
|
|
1449
|
+
}
|
|
1450
|
+
configStr = readFileSync(filePath, 'utf-8');
|
|
1451
|
+
}
|
|
1452
|
+
let config;
|
|
1453
|
+
try {
|
|
1454
|
+
config = JSON.parse(configStr);
|
|
1455
|
+
}
|
|
1456
|
+
catch {
|
|
1457
|
+
console.error('JSON 解析失败');
|
|
1458
|
+
process.exit(1);
|
|
1459
|
+
}
|
|
1460
|
+
// 校验联动 prop:必填且平台支持 hide/show/edit/readonly/required/hideLine(隐藏行,抓包实测平台枚举是 hideLine;
|
|
1461
|
+
// hideRow 保留兼容旧写法)
|
|
1462
|
+
const SUPPORTED_PROPS = ['hide', 'show', 'edit', 'readonly', 'required', 'hideLine', 'hideRow'];
|
|
1463
|
+
const missingProps = (config.actions || []).filter((a) => !a.prop);
|
|
1464
|
+
const invalidProps = (config.actions || [])
|
|
1465
|
+
.filter((a) => a.prop)
|
|
1466
|
+
.map((a) => a.prop)
|
|
1467
|
+
.filter((p) => !SUPPORTED_PROPS.includes(String(p)));
|
|
1468
|
+
if (missingProps.length > 0 || invalidProps.length > 0) {
|
|
1469
|
+
if (missingProps.length > 0) {
|
|
1470
|
+
console.error(`联动 action 缺少 prop(联动效果类型必填)`);
|
|
1471
|
+
}
|
|
1472
|
+
if (invalidProps.length > 0) {
|
|
1473
|
+
console.error(`联动 prop 不支持: ${[...new Set(invalidProps)].join(', ')}`);
|
|
1474
|
+
console.error(` 平台支持: ${SUPPORTED_PROPS.join(', ')}`);
|
|
1475
|
+
}
|
|
1476
|
+
process.exit(1);
|
|
1477
|
+
}
|
|
1478
|
+
// Get formId from appId
|
|
1479
|
+
const listData = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
1480
|
+
const formInfo = (listData?.data || []).find((f) => f.id === objId);
|
|
1481
|
+
const formId = formInfo?.formId || '';
|
|
1482
|
+
if (!formId) {
|
|
1483
|
+
console.error('无法获取 formId,请确认 appId 和 objId 正确');
|
|
1484
|
+
process.exit(1);
|
|
1485
|
+
}
|
|
1486
|
+
// Build payload
|
|
1487
|
+
const payload = {
|
|
1488
|
+
fdEffect: {
|
|
1489
|
+
formId,
|
|
1490
|
+
effectModule: 'ebuilderform',
|
|
1491
|
+
effectType: 'form',
|
|
1492
|
+
effectName: '当前表单',
|
|
1493
|
+
optionList: [{ effectValue: formId, effectName: '当前表单' }],
|
|
1494
|
+
},
|
|
1495
|
+
condition: {
|
|
1496
|
+
targetId: formId,
|
|
1497
|
+
targetName: formInfo?.formName || '表单',
|
|
1498
|
+
targetModule: 'ebuilderform',
|
|
1499
|
+
conditionType: 'FIELD',
|
|
1500
|
+
conditionDetails: config.conditions.map((c) => ({
|
|
1501
|
+
targetId: c.fieldId,
|
|
1502
|
+
detailSymbol: c.operator || 'EQUAL',
|
|
1503
|
+
sourceType: 'CONSTANT',
|
|
1504
|
+
sourceContent: JSON.stringify({
|
|
1505
|
+
formField: { id: c.fieldId },
|
|
1506
|
+
...(c.options ? { dataOptions: c.options } : { content: c.value }),
|
|
1507
|
+
}),
|
|
1508
|
+
})),
|
|
1509
|
+
formulaId: '',
|
|
1510
|
+
formulaName: '',
|
|
1511
|
+
},
|
|
1512
|
+
linkageDetails: config.actions.map((a) => ({
|
|
1513
|
+
formId,
|
|
1514
|
+
id: '0',
|
|
1515
|
+
linkageObj: 'field',
|
|
1516
|
+
linkageProp: a.prop, // hide, show, edit, readonly, required, hideRow
|
|
1517
|
+
linkageShowValue: a.fields.map((f) => ({
|
|
1518
|
+
dataKey: f.dataKey || '',
|
|
1519
|
+
groupName: '表单字段',
|
|
1520
|
+
address: f.isDetail ? '明细表' : '主表',
|
|
1521
|
+
addressType: f.isDetail ? 'detail' : 'form',
|
|
1522
|
+
groupId: 'formField',
|
|
1523
|
+
name: f.name,
|
|
1524
|
+
descType: f.fieldType || '',
|
|
1525
|
+
id: f.fieldId,
|
|
1526
|
+
content: f.name,
|
|
1527
|
+
desc: f.desc || '',
|
|
1528
|
+
})),
|
|
1529
|
+
detailOptions: [],
|
|
1530
|
+
})),
|
|
1531
|
+
module: 'ebuilderform',
|
|
1532
|
+
formId,
|
|
1533
|
+
refId: formId,
|
|
1534
|
+
enableState: '1',
|
|
1535
|
+
linkageName: name,
|
|
1536
|
+
customParam: { appId, objId },
|
|
1537
|
+
};
|
|
1538
|
+
const data = await client.post(`${baseUrl}/api/ebuilder/form/form/setting/viewlinkage/add?apid=${appId}`, payload, { ebbusinessid: formId });
|
|
1539
|
+
if (data?.code === 200 && data?.status) {
|
|
1540
|
+
const linkageId = data?.data || '';
|
|
1541
|
+
console.log(opts.json
|
|
1542
|
+
? JSON.stringify({ success: true, linkageId, name })
|
|
1543
|
+
: `联动规则已添加: ${name}` + (linkageId ? ` (id: ${linkageId})` : ''));
|
|
1544
|
+
}
|
|
1545
|
+
else {
|
|
1546
|
+
console.error(`添加失败: ${data?.msg || '未知错误'}`);
|
|
1547
|
+
process.exit(1);
|
|
1548
|
+
}
|
|
1549
|
+
});
|
|
1550
|
+
form.command('field-options')
|
|
1551
|
+
.description('给已有 Select/RadioBox/CheckBox 字段设置选项和默认值')
|
|
1552
|
+
.argument('<objId>', '表单 objId')
|
|
1553
|
+
.argument('<appId>', '应用 ID')
|
|
1554
|
+
.argument('<fieldId>', '字段 fieldId')
|
|
1555
|
+
.requiredOption('--options <json>', '选项列表:[{"name":"高","valueKey":"xxx"}...] 或 @文件路径')
|
|
1556
|
+
.option('--default <valueKey>', '默认选项 valueKey')
|
|
1557
|
+
.action(async (objId, appId, fieldId, cmdOpts) => {
|
|
1558
|
+
const opts = program.opts();
|
|
1559
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
1560
|
+
if (!auth)
|
|
1561
|
+
process.exit(1);
|
|
1562
|
+
const client = new ApiClient(auth);
|
|
1563
|
+
const baseUrl = auth.getBaseUrl();
|
|
1564
|
+
// Get formId
|
|
1565
|
+
const formList = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
1566
|
+
const formInfo = (formList?.data || []).find((f) => f.id === objId);
|
|
1567
|
+
const formId = formInfo?.formId || '';
|
|
1568
|
+
const formTableId = formInfo?.formTableId || '';
|
|
1569
|
+
if (!formId) {
|
|
1570
|
+
console.error('无法获取 formId');
|
|
1571
|
+
process.exit(1);
|
|
1572
|
+
}
|
|
1573
|
+
// Parse options
|
|
1574
|
+
let optStr = cmdOpts.options;
|
|
1575
|
+
if (optStr.startsWith('@')) {
|
|
1576
|
+
const { readFileSync, existsSync } = await import('fs');
|
|
1577
|
+
if (!existsSync(optStr.slice(1))) {
|
|
1578
|
+
console.error('选项文件不存在');
|
|
1579
|
+
process.exit(1);
|
|
1580
|
+
}
|
|
1581
|
+
optStr = readFileSync(optStr.slice(1), 'utf-8');
|
|
1582
|
+
}
|
|
1583
|
+
let optionDefs;
|
|
1584
|
+
try {
|
|
1585
|
+
optionDefs = JSON.parse(optStr);
|
|
1586
|
+
}
|
|
1587
|
+
catch {
|
|
1588
|
+
console.error('选项 JSON 解析失败');
|
|
1589
|
+
process.exit(1);
|
|
1590
|
+
}
|
|
1591
|
+
if (!Array.isArray(optionDefs) || optionDefs.length === 0) {
|
|
1592
|
+
console.error('--options 必须是非空数组');
|
|
1593
|
+
process.exit(1);
|
|
1594
|
+
}
|
|
1595
|
+
// Get current field info for title, dataKey etc.
|
|
1596
|
+
let currentField = {};
|
|
1597
|
+
try {
|
|
1598
|
+
const fData = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormField`, { module: 'ebuilderform', formTableField: { formField: { id: fieldId, form: { id: formId } } } }, { ebbusinessid: formId });
|
|
1599
|
+
currentField = fData?.data || {};
|
|
1600
|
+
}
|
|
1601
|
+
catch { /* best effort */ }
|
|
1602
|
+
// Build options with defOption
|
|
1603
|
+
const { randomUUID } = await import('crypto');
|
|
1604
|
+
// valueKey 统一由平台生成(与 form create 一致)。已有数据的字段系统会拒绝修改(保险),
|
|
1605
|
+
// 所以无需保留旧 key:能改的字段必然没数据,全新生成最安全、也绝不会踩 selectionId 的 parseLong。
|
|
1606
|
+
// 用户显式传纯数字 valueKey 时尊重(进阶用),非数字忽略并告警。
|
|
1607
|
+
const isNumeric = (vk) => vk !== undefined && vk !== null && vk !== '' && /^\d+$/.test(String(vk));
|
|
1608
|
+
let genIds = [];
|
|
1609
|
+
try {
|
|
1610
|
+
const genRes = await client.post(`${baseUrl}/api/form/local/generatorIds?size=${optionDefs.length + 1}&apid=${appId}`, {});
|
|
1611
|
+
genIds = genRes?.data?.generatorIds || [];
|
|
1612
|
+
}
|
|
1613
|
+
catch { /* fallback to timestamp */ }
|
|
1614
|
+
const options = optionDefs.map((o, i) => {
|
|
1615
|
+
// 优先级:纯数字 valueKey > 平台新 ID > 时间戳兜底
|
|
1616
|
+
let valueKey = isNumeric(o.valueKey) ? String(o.valueKey) : '';
|
|
1617
|
+
if (!valueKey)
|
|
1618
|
+
valueKey = genIds[i] || String(Date.now() + i);
|
|
1619
|
+
if (o.valueKey && !isNumeric(o.valueKey)) {
|
|
1620
|
+
console.error(` ⚠ ${o.name}: 忽略非数字 valueKey "${o.valueKey}"(平台 selectionId 只接受数字),已重新生成 ${valueKey}`);
|
|
1621
|
+
}
|
|
1622
|
+
return {
|
|
1623
|
+
other: false,
|
|
1624
|
+
selectionId: valueKey,
|
|
1625
|
+
defOption: cmdOpts.default === valueKey,
|
|
1626
|
+
styleConfig: {}, archive: false, divisionBranch: false, subCompanyCatalogues: [],
|
|
1627
|
+
dataCount: i === 0 ? optionDefs.length : 0,
|
|
1628
|
+
score: '0', uid: randomUUID(), children: [], extra: {},
|
|
1629
|
+
name: o.name, isLimit: false, valueKey,
|
|
1630
|
+
componentKey: 'Option', order: i, fieldId,
|
|
1631
|
+
};
|
|
1632
|
+
});
|
|
1633
|
+
// Build updated field jsonObj — merge with current field to keep title/dataKey
|
|
1634
|
+
const jsonObj = {
|
|
1635
|
+
...currentField,
|
|
1636
|
+
formTableId,
|
|
1637
|
+
options,
|
|
1638
|
+
showType: 'Select',
|
|
1639
|
+
singleEdit: true,
|
|
1640
|
+
componentKey: 'Select',
|
|
1641
|
+
typeLen: 20,
|
|
1642
|
+
fieldId,
|
|
1643
|
+
formId,
|
|
1644
|
+
columnType: 'VARCHAR',
|
|
1645
|
+
hasChild: false,
|
|
1646
|
+
hideScore: true,
|
|
1647
|
+
groupId: currentField.groupId || '-1',
|
|
1648
|
+
hasDisplayFormat: currentField.hasDisplayFormat ?? false,
|
|
1649
|
+
title: currentField.title || '',
|
|
1650
|
+
dataCount: currentField.dataCount || 1,
|
|
1651
|
+
dataKey: currentField.dataKey || '',
|
|
1652
|
+
systemField: currentField.systemField ?? true,
|
|
1653
|
+
columnTypeSwitch: currentField.columnTypeSwitch ?? false,
|
|
1654
|
+
showOrder: currentField.showOrder || 9,
|
|
1655
|
+
columnName: currentField.columnName || '',
|
|
1656
|
+
};
|
|
1657
|
+
const payload = {
|
|
1658
|
+
formId, module: 'ebuilderform',
|
|
1659
|
+
customParam: { appId, objId },
|
|
1660
|
+
jsonObj: JSON.stringify(jsonObj),
|
|
1661
|
+
};
|
|
1662
|
+
const data = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, payload, { ebbusinessid: formId });
|
|
1663
|
+
if (data?.code === 200 && data?.status) {
|
|
1664
|
+
const saved = options.map(o => ({ name: o.name, valueKey: o.valueKey }));
|
|
1665
|
+
const defMsg = cmdOpts.default ? ` (默认: ${saved.find(o => o.valueKey === cmdOpts.default)?.name || cmdOpts.default})` : '';
|
|
1666
|
+
if (opts.json) {
|
|
1667
|
+
console.log(JSON.stringify({ success: true, fieldId, options: saved, default: cmdOpts.default }, null, 2));
|
|
1668
|
+
}
|
|
1669
|
+
else {
|
|
1670
|
+
console.log(`字段选项已更新: ${options.length} 个选项${defMsg}`);
|
|
1671
|
+
// 回显 valueKey,方便后续 --default / form data add 用
|
|
1672
|
+
for (const o of saved)
|
|
1673
|
+
console.log(` ${o.name} => ${o.valueKey}`);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
else {
|
|
1677
|
+
console.error(`更新失败: ${data?.msg || '未知错误'}`);
|
|
1678
|
+
process.exit(1);
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
form.command('add-field')
|
|
1682
|
+
.description('给表单添加字段')
|
|
1683
|
+
.argument('<objId>', '表单 objId')
|
|
1684
|
+
.argument('<appId>', '应用 ID')
|
|
1685
|
+
.argument('<fieldName>', '字段名')
|
|
1686
|
+
.argument('<fieldType>', '字段类型')
|
|
1687
|
+
.argument('[title]', '字段标题')
|
|
1688
|
+
.option('--sform-id <id>', '关联的目标表单 objId(仅 EBuilder 类型)')
|
|
1689
|
+
.option('--data-title <field>', '目标表单的显示字段名(仅 EBuilder;缺省 name=目标表含"名称"的字段,无则第一个业务字段)')
|
|
1690
|
+
.option('--form-name <name>', '目标表单名称(仅 EBuilder 类型,可选)')
|
|
1691
|
+
.option('--format <fmt>', '日期精度格式(Date/DateTime/DateRange),如 yyyy-MM-dd、yyyy-MM-dd HH:mm')
|
|
1692
|
+
.option('--min-field <field>', 'Date/DateTime 日期限制:最早不得早于该字段(字段名,min.dateField)')
|
|
1693
|
+
.option('--max-field <field>', 'Date/DateTime 日期限制:最晚不得晚于该字段(字段名,max.dateField)')
|
|
1694
|
+
.option('--precision <n>', '小数位数(Number/Money 类型,缺省 Number=0 整数、Money=2、百分比=1)')
|
|
1695
|
+
.option('--percent', 'Number 百分比显示(numberType=percentage)')
|
|
1696
|
+
.option('--default <value>', 'Number/Money 默认值(content + isDefault,如 工时单价默认 1000)')
|
|
1697
|
+
.option('--delimiter', 'Number/Money 千分位(Money 默认开,--no-delimiter 关闭)')
|
|
1698
|
+
.option('--capital', 'Number/Money 大写金额显示')
|
|
1699
|
+
.option('--sub-form-id <id>', '明细表 subFormId(在指定明细表里加字段)')
|
|
1700
|
+
.option('--multiple', '多选(人员/部门/EBuilder/Browser/下拉Select,默认单选。下拉多选=SelectMultiple 类型)')
|
|
1701
|
+
.option('--max-len <n>', '文本最大长度(Text 缺省 100、TextArea 缺省 4000)')
|
|
1702
|
+
.option('--single-row', '独占一行显示(布局 singleRow:true,其余字段仍两列)')
|
|
1703
|
+
.action(async (objId, appId, fieldName, fieldType, title, cmdOptsRaw) => {
|
|
1704
|
+
// fieldType 大小写归一(与建表一致):用户常传 "Textarea"/"Ebuilder",不归一则 type 存小写
|
|
1705
|
+
// "Textarea"(FLOW 渲染异常)、TextArea 分支漏跑。
|
|
1706
|
+
fieldType = canonComponentKey(fieldType);
|
|
1707
|
+
const opts = program.opts();
|
|
1708
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
1709
|
+
if (!auth)
|
|
1710
|
+
process.exit(1);
|
|
1711
|
+
const client = new ApiClient(auth);
|
|
1712
|
+
const baseUrl = auth.getBaseUrl();
|
|
1713
|
+
// Get formId from objId
|
|
1714
|
+
const formList = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
1715
|
+
const formInfo = (formList?.data || []).find((f) => f.id === objId);
|
|
1716
|
+
const formId = formInfo?.formId || '';
|
|
1717
|
+
const formTableId = formInfo?.formTableId || '';
|
|
1718
|
+
if (!formId) {
|
|
1719
|
+
console.error('无法获取 formId,请确认 objId 和 appId 正确');
|
|
1720
|
+
process.exit(1);
|
|
1721
|
+
}
|
|
1722
|
+
// DateRange 需要 3 个字段 ID(主 + 开始 + 结束),普通字段 1 个,富组件按 richIdUnits
|
|
1723
|
+
const isDateRange = fieldType === 'DateRange' || fieldType === 'DateInterval';
|
|
1724
|
+
const idCount = isDateRange ? 3 : richIdUnits(fieldType, {});
|
|
1725
|
+
const genData = await client.post(`${baseUrl}/api/form/local/generatorIds?size=${idCount}&apid=${appId}`, {});
|
|
1726
|
+
const genIds = genData?.data?.generatorIds || [];
|
|
1727
|
+
const fieldId = genIds[0] || String(Date.now());
|
|
1728
|
+
const startId = genIds[1] || '';
|
|
1729
|
+
const endId = genIds[2] || '';
|
|
1730
|
+
const cmdOpts = cmdOptsRaw || {};
|
|
1731
|
+
const sformId = cmdOpts.sformId || '';
|
|
1732
|
+
const dataTitle = cmdOpts.dataTitle || '';
|
|
1733
|
+
const formName = cmdOpts.formName || '';
|
|
1734
|
+
// Generate column name: pinyin initials + timestamp suffix
|
|
1735
|
+
const colName = toColumnName(fieldName);
|
|
1736
|
+
// Build jsonObj based on field type
|
|
1737
|
+
const jsonObj = {
|
|
1738
|
+
type: fieldType,
|
|
1739
|
+
title: title || fieldName,
|
|
1740
|
+
formTableId, fieldId, groupId: '-1', formId,
|
|
1741
|
+
showOrder: '0.00',
|
|
1742
|
+
dataKey: colName,
|
|
1743
|
+
columnName: colName,
|
|
1744
|
+
};
|
|
1745
|
+
// 明细表字段:--sub-form-id 时 formTableId 用明细表的,jsonObj 带 subFormId
|
|
1746
|
+
if (cmdOpts.subFormId) {
|
|
1747
|
+
const eb = await client.post(`${baseUrl}/api/bs/esb/setting/component/getEbField`, { tableId: objId, appId });
|
|
1748
|
+
const dt = (eb?.data?.detail || []).find((d) => String(d.detailTableId) === String(cmdOpts.subFormId));
|
|
1749
|
+
if (!dt) {
|
|
1750
|
+
console.error(`未找到明细表 subFormId=${cmdOpts.subFormId}`);
|
|
1751
|
+
process.exit(1);
|
|
1752
|
+
}
|
|
1753
|
+
Object.assign(jsonObj, { subFormId: cmdOpts.subFormId });
|
|
1754
|
+
const detailFormTableId = dt.fields?.[0]?.formTableId || '';
|
|
1755
|
+
if (detailFormTableId)
|
|
1756
|
+
jsonObj.formTableId = detailFormTableId;
|
|
1757
|
+
}
|
|
1758
|
+
// 富组件:映射到平台真实 type/componentKey + 附加配置(对齐 batch/save/formField 抓包)。
|
|
1759
|
+
// 之前 jsonObj 只有 type 没有 componentKey → 平台 FormFieldDto.getComponentKey() 为 null → NPE。
|
|
1760
|
+
const rich = getRichType(fieldType);
|
|
1761
|
+
if (rich) {
|
|
1762
|
+
jsonObj.type = rich.platformType;
|
|
1763
|
+
jsonObj.componentKey = rich.platformComponentKey;
|
|
1764
|
+
if (rich.build) {
|
|
1765
|
+
const extra = richIdUnits(fieldType, {}) - 1;
|
|
1766
|
+
let extraIds = [];
|
|
1767
|
+
if (extra > 0) {
|
|
1768
|
+
const extraRes = await client.post(`${baseUrl}/api/form/local/generatorIds?size=${extra}&apid=${appId}`, {});
|
|
1769
|
+
extraIds = (extraRes?.data?.generatorIds || []).map(String);
|
|
1770
|
+
}
|
|
1771
|
+
const extraCfg = await rich.build({
|
|
1772
|
+
def: { multiple: cmdOpts.multiple }, fieldId, formId, formTableId, dataKey: colName, columnName: colName,
|
|
1773
|
+
showOrder: '0.00', extraIds,
|
|
1774
|
+
});
|
|
1775
|
+
Object.assign(jsonObj, extraCfg);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
// 非富组件也补 componentKey(平台要求,否则 FormFieldDto.getComponentKey() 为 null → NPE)。
|
|
1779
|
+
// 富组件已在上方设置 platformComponentKey;此处 fallback 到类型名(平台对常见类型有归一容错)。
|
|
1780
|
+
if (!jsonObj.componentKey)
|
|
1781
|
+
jsonObj.componentKey = fieldType;
|
|
1782
|
+
// Number/Money decimal precision: --precision 指定,缺省 Number=0 整数、Money=2、百分比=1
|
|
1783
|
+
// 百分比:--percent → numberType=percentage;默认值:--default → content+isDefault
|
|
1784
|
+
// 实测:设默认值必须连 isDelimiter/stepMode/step/isCapital 一起,否则平台丢默认(只设 content+isDefault 不落库)
|
|
1785
|
+
if (fieldType === 'Number' || fieldType === 'Money') {
|
|
1786
|
+
const defaultPrec = fieldType === 'Money' ? '2' : '0';
|
|
1787
|
+
const isPercent = fieldType === 'Number' && cmdOpts.percent === true;
|
|
1788
|
+
// 注意:commander 把 --default 映射到 opts.default(非 defaultValue)
|
|
1789
|
+
const hasDefault = cmdOpts.default !== undefined && cmdOpts.default !== null && cmdOpts.default !== '';
|
|
1790
|
+
// 千分位:Money 默认开(--no-delimiter 关闭)、Number 默认关(--delimiter 开启)
|
|
1791
|
+
const isDelim = fieldType === 'Money' ? cmdOpts.delimiter !== false : cmdOpts.delimiter === true;
|
|
1792
|
+
const isCap = cmdOpts.capital === true;
|
|
1793
|
+
const patch = {
|
|
1794
|
+
pointSize: cmdOpts.precision !== undefined ? cmdOpts.precision : (isPercent ? '1' : defaultPrec),
|
|
1795
|
+
isDefault: hasDefault,
|
|
1796
|
+
isDelimiter: isDelim,
|
|
1797
|
+
isCapital: isCap,
|
|
1798
|
+
capitalFormat: 'onlyUpperCase',
|
|
1799
|
+
capitalPosition: 'outside',
|
|
1800
|
+
capitalSetting: 'system',
|
|
1801
|
+
capitalPrecision: 'yuan',
|
|
1802
|
+
isExchangeRates: false,
|
|
1803
|
+
};
|
|
1804
|
+
if (fieldType === 'Number')
|
|
1805
|
+
patch.numberType = isPercent ? 'percentage' : 'number';
|
|
1806
|
+
// content 始终带(无默认值时空串)——浏览器抓包 Number 字段 getFormField 恒有 content:"",
|
|
1807
|
+
// 缺 content 在 FLOW 渲染异常(与多行文本同因)。
|
|
1808
|
+
if (fieldType === 'Number')
|
|
1809
|
+
patch.content = hasDefault ? String(cmdOpts.default) : '';
|
|
1810
|
+
if (hasDefault) {
|
|
1811
|
+
patch.stepMode = false;
|
|
1812
|
+
patch.step = 1;
|
|
1813
|
+
}
|
|
1814
|
+
Object.assign(jsonObj, patch);
|
|
1815
|
+
}
|
|
1816
|
+
if (fieldType === 'TextArea') {
|
|
1817
|
+
// 多行文本:对齐浏览器抓包——content 恒 ""(无默认值时空串)、maxLen=4000。
|
|
1818
|
+
// 缺 content 在 FLOW 渲染异常;maxLen 不发则前端最大长度回显异常。
|
|
1819
|
+
Object.assign(jsonObj, { content: '', maxLen: 4000 });
|
|
1820
|
+
}
|
|
1821
|
+
if (fieldType === 'EInvoice') {
|
|
1822
|
+
Object.assign(jsonObj, {
|
|
1823
|
+
isSingle: false,
|
|
1824
|
+
invoiceType: 1,
|
|
1825
|
+
});
|
|
1826
|
+
delete jsonObj.maxLen;
|
|
1827
|
+
delete jsonObj.unique;
|
|
1828
|
+
delete jsonObj.scan;
|
|
1829
|
+
delete jsonObj.readonlyTransLink;
|
|
1830
|
+
}
|
|
1831
|
+
else if (fieldType === 'Date' || fieldType === 'DateTime') {
|
|
1832
|
+
// 日期:默认精度 年月日(yyyy-MM-dd,对齐浏览器抓包);DateTime:默认带时:分(yyyy-MM-dd HH:mm 不含秒)。
|
|
1833
|
+
// 默认值不填(isDefault:false);--format 覆盖精度。
|
|
1834
|
+
const isDt = fieldType === 'DateTime';
|
|
1835
|
+
const fmt = cmdOpts.format || (isDt ? 'yyyy-MM-dd HH:mm' : 'yyyy-MM-dd');
|
|
1836
|
+
Object.assign(jsonObj, {
|
|
1837
|
+
type: 'Date',
|
|
1838
|
+
format: fmt,
|
|
1839
|
+
placeholder: placeholderFromFormat(fmt),
|
|
1840
|
+
timePanelStyle: { type: 'system' },
|
|
1841
|
+
isDefault: false,
|
|
1842
|
+
});
|
|
1843
|
+
// 日期限制:--min-field 最早不得早于 / --max-field 最晚不得晚于另一字段(对齐抓包 min.dateField)。
|
|
1844
|
+
// 目标字段 fieldId 从 getFields mainFields 按字段名解析。
|
|
1845
|
+
if (cmdOpts.minField || cmdOpts.maxField) {
|
|
1846
|
+
try {
|
|
1847
|
+
const tfd = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
1848
|
+
objId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
1849
|
+
});
|
|
1850
|
+
const tfields = tfd?.data?.mainFields || [];
|
|
1851
|
+
const minTarget = cmdOpts.minField ? tfields.find((f) => f.fieldName === cmdOpts.minField)?.fieldId : '';
|
|
1852
|
+
const maxTarget = cmdOpts.maxField ? tfields.find((f) => f.fieldName === cmdOpts.maxField)?.fieldId : '';
|
|
1853
|
+
if (minTarget) {
|
|
1854
|
+
jsonObj.min = { type: 'dateField', checkValue: String(minTarget) };
|
|
1855
|
+
jsonObj.earlyDate = true;
|
|
1856
|
+
}
|
|
1857
|
+
if (maxTarget) {
|
|
1858
|
+
jsonObj.max = { type: 'dateField', checkValue: String(maxTarget) };
|
|
1859
|
+
jsonObj.lateDate = true;
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
catch { /* 解析失败则不加限制 */ }
|
|
1863
|
+
}
|
|
1864
|
+
delete jsonObj.maxLen;
|
|
1865
|
+
delete jsonObj.unique;
|
|
1866
|
+
delete jsonObj.scan;
|
|
1867
|
+
delete jsonObj.readonlyTransLink;
|
|
1868
|
+
}
|
|
1869
|
+
else if (fieldType === 'City') {
|
|
1870
|
+
// 城市/地区选择(对齐浏览器 payload:region 模式 + 系统级联层级 + 字母索引)
|
|
1871
|
+
Object.assign(jsonObj, {
|
|
1872
|
+
isSingle: false,
|
|
1873
|
+
levelSetting: 'system',
|
|
1874
|
+
isDefault: false,
|
|
1875
|
+
useLetterIndex: true,
|
|
1876
|
+
mode: 'region',
|
|
1877
|
+
});
|
|
1878
|
+
delete jsonObj.maxLen;
|
|
1879
|
+
delete jsonObj.unique;
|
|
1880
|
+
delete jsonObj.scan;
|
|
1881
|
+
delete jsonObj.readonlyTransLink;
|
|
1882
|
+
}
|
|
1883
|
+
else if (fieldType === 'EBuilder') {
|
|
1884
|
+
if (!sformId) {
|
|
1885
|
+
console.error('EBuilder 类型必须指定 --sform-id(目标表单 objId)');
|
|
1886
|
+
process.exit(1);
|
|
1887
|
+
}
|
|
1888
|
+
// Resolve formName from target form if not provided
|
|
1889
|
+
let resolvedFormName = formName;
|
|
1890
|
+
if (!resolvedFormName) {
|
|
1891
|
+
try {
|
|
1892
|
+
const tlist = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
1893
|
+
const tf = (tlist?.data || []).find((f) => String(f.id) === String(sformId));
|
|
1894
|
+
resolvedFormName = tf?.formName || tf?.name || sformId;
|
|
1895
|
+
}
|
|
1896
|
+
catch {
|
|
1897
|
+
resolvedFormName = sformId;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
// Resolve dataTitle:纯数字 fieldId 直接用;'name'/字段名 → 查目标表字段。
|
|
1901
|
+
// 未传 --data-title 时按 name 处理:默认优先字段名含「名称」的业务字段(如 客户名称/类型名称),无则第一个业务字段。
|
|
1902
|
+
let resolvedDataTitle = dataTitle || 'name';
|
|
1903
|
+
if (!/^\d{15,}$/.test(resolvedDataTitle)) {
|
|
1904
|
+
try {
|
|
1905
|
+
const tfd = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
1906
|
+
objId: sformId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
1907
|
+
});
|
|
1908
|
+
const tfields = tfd?.data?.mainFields || [];
|
|
1909
|
+
if (resolvedDataTitle === 'name') {
|
|
1910
|
+
resolvedDataTitle = defaultEBuilderDataTitle(tfields);
|
|
1911
|
+
}
|
|
1912
|
+
else {
|
|
1913
|
+
const m = tfields.find((f) => f.fieldName === resolvedDataTitle)
|
|
1914
|
+
|| tfields.find((f) => String(f.fieldName || '').includes(resolvedDataTitle));
|
|
1915
|
+
resolvedDataTitle = m?.fieldId || resolvedDataTitle;
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
catch { /* 解析失败保留原值 */ }
|
|
1919
|
+
}
|
|
1920
|
+
Object.assign(jsonObj, {
|
|
1921
|
+
sformId,
|
|
1922
|
+
dataTitle: resolvedDataTitle || 'name',
|
|
1923
|
+
formName: resolvedFormName,
|
|
1924
|
+
virtualForm: false,
|
|
1925
|
+
displayType: 'line',
|
|
1926
|
+
isAddForRelation: true,
|
|
1927
|
+
isSingle: cmdOpts.multiple ? false : true, // 默认单选;--multiple 多选
|
|
1928
|
+
viewDataDetail: true,
|
|
1929
|
+
pcDisplaySelectedCount: { type: 'default' },
|
|
1930
|
+
mobileDisplaySelectedCount: { type: 'default' },
|
|
1931
|
+
quickAddParams: [],
|
|
1932
|
+
relateViewData: [],
|
|
1933
|
+
relateField: null,
|
|
1934
|
+
appId,
|
|
1935
|
+
virtualTable: false,
|
|
1936
|
+
});
|
|
1937
|
+
}
|
|
1938
|
+
else if (fieldType === 'Flow') {
|
|
1939
|
+
// 浏览-流程字段(对齐浏览器 payload:type=Flow + relateExt.module=workflow)
|
|
1940
|
+
if (!sformId) {
|
|
1941
|
+
console.error('Flow 类型必须指定 --sform-id(目标流程绑定的表单 objId)');
|
|
1942
|
+
process.exit(1);
|
|
1943
|
+
}
|
|
1944
|
+
let resolvedFormName = formName;
|
|
1945
|
+
if (!resolvedFormName) {
|
|
1946
|
+
try {
|
|
1947
|
+
const tlist = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
1948
|
+
const tf = (tlist?.data || []).find((f) => String(f.id) === String(sformId));
|
|
1949
|
+
resolvedFormName = tf?.formName || tf?.name || sformId;
|
|
1950
|
+
}
|
|
1951
|
+
catch {
|
|
1952
|
+
resolvedFormName = sformId;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
Object.assign(jsonObj, {
|
|
1956
|
+
isAddForRelation: true,
|
|
1957
|
+
sformId,
|
|
1958
|
+
formName: resolvedFormName,
|
|
1959
|
+
conditionOperate: 'and',
|
|
1960
|
+
filterParams: '{}',
|
|
1961
|
+
filterQueryParams: '{}',
|
|
1962
|
+
columnFields: [],
|
|
1963
|
+
displayType: 'line',
|
|
1964
|
+
viewDataDetail: true,
|
|
1965
|
+
pcDisplaySelectedCount: { type: 'default' },
|
|
1966
|
+
mobileDisplaySelectedCount: { type: 'default' },
|
|
1967
|
+
relateExt: { module: 'workflow' },
|
|
1968
|
+
relateField: null,
|
|
1969
|
+
quickAddParams: [],
|
|
1970
|
+
});
|
|
1971
|
+
}
|
|
1972
|
+
else if (isDateRange) {
|
|
1973
|
+
// 日期区间:补 start/end 两个 DateComponent + rangeInterval(对齐浏览器 payload)
|
|
1974
|
+
const format = cmdOpts.format || 'yyyy-MM-dd';
|
|
1975
|
+
const rangeTitle = title || fieldName;
|
|
1976
|
+
const start = {
|
|
1977
|
+
componentKey: 'DateComponent', content: '', describe: '', earlyDate: 'false',
|
|
1978
|
+
format, isDefault: 'false', lateDate: 'false',
|
|
1979
|
+
placeholder: placeholderFromFormat(format),
|
|
1980
|
+
position: '', relateMultiple: 'false', required: 'false', size: 'medium',
|
|
1981
|
+
title: `${rangeTitle}(开始时间)`, fieldId: startId,
|
|
1982
|
+
min: { type: 'currentHour' }, max: { type: 'currentHour' },
|
|
1983
|
+
};
|
|
1984
|
+
const end = { ...start, title: `${rangeTitle}(结束时间)`, fieldId: endId };
|
|
1985
|
+
Object.assign(jsonObj, {
|
|
1986
|
+
isDefault: false,
|
|
1987
|
+
start, end,
|
|
1988
|
+
hasDisplayFormat: true,
|
|
1989
|
+
rangeInterval: { limitType: 'max', timeUnit: 'day' },
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
else if (fieldType === 'IDCard') {
|
|
1993
|
+
// 身份证号:平台原生简单字段,浏览器抓包 payload 极简(type/title/formTableId/dataKey/
|
|
1994
|
+
// columnName/fieldId/formId/showOrder/groupId 即全部)——无 componentKey、无文本长度配置。
|
|
1995
|
+
delete jsonObj.componentKey;
|
|
1996
|
+
delete jsonObj.maxLen;
|
|
1997
|
+
delete jsonObj.unique;
|
|
1998
|
+
delete jsonObj.scan;
|
|
1999
|
+
delete jsonObj.readonlyTransLink;
|
|
2000
|
+
}
|
|
2001
|
+
else {
|
|
2002
|
+
// 文本最大长度:--max-len 指定,缺省 Text=100、TextArea=4000(对齐浏览器抓包;TextArea 在上面分支已设 content:"")
|
|
2003
|
+
const defaultMaxLen = fieldType === 'TextArea' ? 4000 : 100;
|
|
2004
|
+
const maxLen = cmdOpts.maxLen !== undefined ? Number(cmdOpts.maxLen) : defaultMaxLen;
|
|
2005
|
+
Object.assign(jsonObj, {
|
|
2006
|
+
maxLen: Number.isFinite(maxLen) && maxLen > 0 ? maxLen : defaultMaxLen,
|
|
2007
|
+
unique: false, scan: false,
|
|
2008
|
+
readonlyTransLink: false,
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
// 人员/部门多选:默认单选,--multiple 改多选(isSingle:false)。EBuilder 已在上文分支设过。
|
|
2012
|
+
if (fieldType === 'Employee' || fieldType === 'Department') {
|
|
2013
|
+
jsonObj.isSingle = cmdOpts.multiple ? false : true;
|
|
2014
|
+
}
|
|
2015
|
+
// 下拉多选是独立类型 SelectMultiple(非 Select + 开关)。浏览器抓包:type:"SelectMultiple",
|
|
2016
|
+
// 无 componentKey/maxLen/unique/scan(else 分支给 Select 加的 junk 要清掉),options 单独配。
|
|
2017
|
+
if (fieldType === 'Select' && cmdOpts.multiple) {
|
|
2018
|
+
jsonObj.type = 'SelectMultiple';
|
|
2019
|
+
jsonObj.componentKey = 'SelectMultiple';
|
|
2020
|
+
delete jsonObj.maxLen;
|
|
2021
|
+
delete jsonObj.unique;
|
|
2022
|
+
delete jsonObj.scan;
|
|
2023
|
+
delete jsonObj.readonlyTransLink;
|
|
2024
|
+
}
|
|
2025
|
+
const payload = {
|
|
2026
|
+
formId, module: 'ebuilderform',
|
|
2027
|
+
customParam: { appId, objId },
|
|
2028
|
+
jsonObj: JSON.stringify(jsonObj),
|
|
2029
|
+
};
|
|
2030
|
+
const data = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, payload, { ebbusinessid: formId });
|
|
2031
|
+
if (data?.code === 200 && data?.status) {
|
|
2032
|
+
const msg = opts.json ? JSON.stringify({ fieldId, fieldName }) : `字段已添加: ${fieldName} (fieldId=${fieldId})`;
|
|
2033
|
+
console.log(msg);
|
|
2034
|
+
// 关联浏览弹窗显示列:add-field 的 EBuilder 字段自动配 ffDisplayColumns = 目标表全部业务字段(去系统字段),
|
|
2035
|
+
// 对齐 create Step2。直接带 ffDisplayColumns 进 jsonObj 不落库,需 getFormField 完整配置 + 单独 save。
|
|
2036
|
+
if (fieldType === 'EBuilder' && sformId) {
|
|
2037
|
+
try {
|
|
2038
|
+
const tfd = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
2039
|
+
objId: sformId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
2040
|
+
});
|
|
2041
|
+
// 默认显示列最多 5 个(去系统字段后取前 5 个业务字段)
|
|
2042
|
+
const bizCols = (tfd?.data?.mainFields || [])
|
|
2043
|
+
.filter((f) => /^\d+$/.test(String(f.fieldId)))
|
|
2044
|
+
.map((f) => ({ id: String(f.fieldId), name: String(f.fieldName || f.title || '') }))
|
|
2045
|
+
.slice(0, 5);
|
|
2046
|
+
if (bizCols.length) {
|
|
2047
|
+
const gf = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormField`, { module: 'ebuilderform', formTableField: { formField: { id: fieldId, form: { id: formId } } } }, { ebbusinessid: formId });
|
|
2048
|
+
const full = gf?.data || {};
|
|
2049
|
+
if (full.fieldId) {
|
|
2050
|
+
await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, { formId, module: 'ebuilderform', customParam: { appId, objId }, jsonObj: JSON.stringify({ ...full, ffDisplayColumns: bizCols }) }, { ebbusinessid: formId });
|
|
2051
|
+
console.error(` 关联显示列已自动配置 (${bizCols.length} 列)`);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
catch (e) {
|
|
2056
|
+
console.error(` 关联显示列设置失败: ${e.message}`);
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
// Always relayout so new field appears in the form
|
|
2060
|
+
console.error(' 重新生成布局...');
|
|
2061
|
+
try {
|
|
2062
|
+
const preLayout = await client.post(`${baseUrl}/api/ebuilder/form/builder/getFormLayout`, {
|
|
2063
|
+
formId, module: 'ebuilderform', customParam: {}, formTableIdList: [],
|
|
2064
|
+
designerParam: { onlyLayout: false, convert: false }, form: { id: formId },
|
|
2065
|
+
}, { ebbusinessid: formId });
|
|
2066
|
+
const layoutId = preLayout?.data?.formLayout?.id || '';
|
|
2067
|
+
const layoutMultiId = preLayout?.data?.formLayout?.multipleLayoutId || '';
|
|
2068
|
+
const layoutName = preLayout?.data?.formLayoutMultiple?.name || preLayout?.data?.formLayout?.name || '';
|
|
2069
|
+
const layoutType = preLayout?.data?.formLayoutMultiple?.layoutType || 'EXCEL';
|
|
2070
|
+
const configRes = await client.post(`${baseUrl}/api/ebuilder/form/form/view/relate/getConfig`, {
|
|
2071
|
+
formId, module: 'ebuilderform', apiModule: 'ebuilder/form',
|
|
2072
|
+
layoutId, customParam: { terminal: '', objId: formId },
|
|
2073
|
+
initMode: 5, groupFieldIds: [], designerParam: { sourceModule: 'ebuilderform' },
|
|
2074
|
+
}, { ebbusinessid: formId });
|
|
2075
|
+
const rawConfigs = configRes?.data?.configs || [];
|
|
2076
|
+
// saveFormFieldNew fields may not appear in getConfig — manually append
|
|
2077
|
+
const CK = {
|
|
2078
|
+
Text: 'Text', Textarea: 'TextArea', Number: 'NumberComponent', Money: 'Money',
|
|
2079
|
+
Date: 'DateComponent', DateTime: 'DateComponent', Select: 'Select', CheckBox: 'CheckBox', RadioBox: 'RadioBox',
|
|
2080
|
+
Employee: 'Employee', Department: 'Department', EBuilder: 'Ebuilder',
|
|
2081
|
+
Phone: 'Phone', Email: 'Email', Mobile: 'Mobile', Location: 'Location', Switch: 'Switch', IDCard: 'IDCard',
|
|
2082
|
+
};
|
|
2083
|
+
const newFieldConfig = {
|
|
2084
|
+
id: fieldId, name: title || fieldName,
|
|
2085
|
+
componentKey: jsonObj.componentKey || CK[fieldType] || fieldType,
|
|
2086
|
+
display: true, show: true, readOnly: false,
|
|
2087
|
+
required: false, disableEdit: false, singleRow: cmdOpts.singleRow === true,
|
|
2088
|
+
isDelete: false, type: fieldType, dataKey: colName,
|
|
2089
|
+
};
|
|
2090
|
+
const existingIds = new Set(rawConfigs.map((c) => String(c.id)));
|
|
2091
|
+
if (cmdOpts.subFormId) {
|
|
2092
|
+
// 明细字段:加进对应 DetailTable 的 children(明细字段嵌在 DetailTable 里,不是顶层)
|
|
2093
|
+
const dtComp = rawConfigs.find((c) => String(c.id) === String(cmdOpts.subFormId) && c.type === 'DetailTable');
|
|
2094
|
+
if (dtComp) {
|
|
2095
|
+
const childIds = new Set((dtComp.children || []).map((x) => String(x.id)));
|
|
2096
|
+
if (!childIds.has(fieldId)) {
|
|
2097
|
+
dtComp.children = dtComp.children || [];
|
|
2098
|
+
dtComp.children.push({ ...newFieldConfig, showOrder: dtComp.children.length });
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
else {
|
|
2102
|
+
console.error(` ⚠ 布局里未找到明细表 subFormId=${cmdOpts.subFormId},字段已加但未进布局`);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
else if (!existingIds.has(fieldId)) {
|
|
2106
|
+
rawConfigs.push({ ...newFieldConfig, showOrder: rawConfigs.length });
|
|
2107
|
+
}
|
|
2108
|
+
const configs = rawConfigs.map((c) => ({
|
|
2109
|
+
...c,
|
|
2110
|
+
display: c.system ? c.display : true,
|
|
2111
|
+
show: c.system ? c.show : true,
|
|
2112
|
+
// 新加字段 saveFormFieldNew 后 getConfig 返回 readOnly:true(未铺列默认),
|
|
2113
|
+
// existingIds 命中时 newFieldConfig(readOnly:false) 会被丢弃,这里强制覆盖回 false,
|
|
2114
|
+
// 否则任何 add-field 新字段(Text/Employee/EBuilder 都中招)默认不可编辑。
|
|
2115
|
+
...(String(c.id) === String(fieldId) ? { readOnly: false } : {}),
|
|
2116
|
+
}));
|
|
2117
|
+
await client.post(`${baseUrl}/api/ebuilder/form/form/view/relate/initLayout?apid=${appId}`, {
|
|
2118
|
+
formId, module: 'ebuilderform', apiModule: 'ebuilder/form',
|
|
2119
|
+
layoutId, generateCollapse: true,
|
|
2120
|
+
styleTemplateId: layoutType === 'FLOW' ? '820000000000000001' : '700000000000000008', styleTemplateType: 1,
|
|
2121
|
+
configs, addLayout: false, ecodeType: ['js', 'css'],
|
|
2122
|
+
initMode: '1', customParam: { terminal: '', objId: formId },
|
|
2123
|
+
formLayoutMultiple: { isSys: true, id: layoutMultiId, name: layoutName, terminalType: 'PC', layoutType },
|
|
2124
|
+
designerParam: { onlyLayout: false },
|
|
2125
|
+
}, { ebbusinessid: formId });
|
|
2126
|
+
console.error(' 布局已更新');
|
|
2127
|
+
}
|
|
2128
|
+
catch (e) {
|
|
2129
|
+
console.error(` 布局更新失败: ${e.message}`);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
else {
|
|
2133
|
+
console.error(`添加失败: ${data?.msg || JSON.stringify(data) || '未知错误'}`);
|
|
2134
|
+
process.exit(1);
|
|
2135
|
+
}
|
|
2136
|
+
});
|
|
2137
|
+
form.command('edit-field')
|
|
2138
|
+
.description('编辑已有字段的属性')
|
|
2139
|
+
.argument('<objId>', '表单 objId')
|
|
2140
|
+
.argument('<appId>', '应用 ID')
|
|
2141
|
+
.argument('<fieldId>', '字段 ID')
|
|
2142
|
+
.option('--title <title>', '修改字段标题')
|
|
2143
|
+
.option('--required', '设为必填')
|
|
2144
|
+
.option('--no-required', '取消必填')
|
|
2145
|
+
.option('--sform-id <id>', 'EBuilder 关联的目标表单 objId(更新关联)')
|
|
2146
|
+
.option('--data-title <field>', '目标表单显示字段名/fieldId(默认 name=目标表含"名称"的字段,无则第一个业务字段)')
|
|
2147
|
+
.option('--form-name <name>', '目标表单名称(默认查目标表单名)')
|
|
2148
|
+
.option('--relate-from <fieldName>', 'EBuilder 字段名:选关联记录后自动带出值到当前字段')
|
|
2149
|
+
.option('--relate-from-source <fieldId>', '目标表单源字段 fieldId(复制哪个字段的值)')
|
|
2150
|
+
.option('--multiple', '设为多选(人员/部门/EBuilder/Browser)')
|
|
2151
|
+
.option('--no-multiple', '设为单选(人员/部门/EBuilder/Browser)')
|
|
2152
|
+
.action(async (objId, appId, fieldId, editOpts) => {
|
|
2153
|
+
const opts = program.opts();
|
|
2154
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
2155
|
+
if (!auth)
|
|
2156
|
+
process.exit(1);
|
|
2157
|
+
const client = new ApiClient(auth);
|
|
2158
|
+
const baseUrl = auth.getBaseUrl();
|
|
2159
|
+
// Get formId — try getList first, fall back to searching by fieldId
|
|
2160
|
+
let formId = '';
|
|
2161
|
+
let resolvedObjId = objId;
|
|
2162
|
+
const formList = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
2163
|
+
const formInfo = (formList?.data || []).find((f) => String(f.id) === String(objId));
|
|
2164
|
+
formId = formInfo?.formId || '';
|
|
2165
|
+
// If not found in getList (e.g. workflow sub-table objId), search by fieldId
|
|
2166
|
+
if (!formId) {
|
|
2167
|
+
const pageRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormFieldPage?apid=${appId}`, {
|
|
2168
|
+
pageNo: 1, pageSize: 100, module: 'ebuilderform',
|
|
2169
|
+
customParam: { appId },
|
|
2170
|
+
formFieldSearchEntity: { isDelete: 0, status: 'enable' },
|
|
2171
|
+
}, { ebbusinessid: appId });
|
|
2172
|
+
const records = pageRes?.data?.pageDatas?.result || pageRes?.data?.data?.records || pageRes?.data?.data || [];
|
|
2173
|
+
const match = records.find((r) => String(r.fieldId) === String(fieldId));
|
|
2174
|
+
if (match?.formId) {
|
|
2175
|
+
formId = match.formId;
|
|
2176
|
+
// Find the correct objId for this form (for saveFormFieldNew payload)
|
|
2177
|
+
const matchForm = (formList?.data || []).find((f) => f.formId === formId);
|
|
2178
|
+
if (matchForm)
|
|
2179
|
+
resolvedObjId = matchForm.id;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
if (!formId) {
|
|
2183
|
+
console.error('无法获取 formId,请确认 fieldId 和 appId 正确');
|
|
2184
|
+
process.exit(1);
|
|
2185
|
+
}
|
|
2186
|
+
// Get formTableId for the payload
|
|
2187
|
+
const formTableId = formInfo?.formTableId || '';
|
|
2188
|
+
// Try to get current field config, but if it fails (e.g. workflow sub-table),
|
|
2189
|
+
// construct a minimal jsonObj from known parameters
|
|
2190
|
+
let jsonObj = null;
|
|
2191
|
+
try {
|
|
2192
|
+
const fieldDetailRes = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormFieldPage?apid=${appId}`, {
|
|
2193
|
+
pageNo: 1, pageSize: 200, module: 'ebuilderform',
|
|
2194
|
+
customParam: { appId },
|
|
2195
|
+
formFieldSearchEntity: { isDelete: 0, status: 'enable', formId },
|
|
2196
|
+
}, { ebbusinessid: formId });
|
|
2197
|
+
const records = fieldDetailRes?.data?.pageDatas?.result || fieldDetailRes?.data?.data?.records || fieldDetailRes?.data?.data || [];
|
|
2198
|
+
const fieldDetail = records.find((r) => String(r.fieldId) === String(fieldId));
|
|
2199
|
+
if (fieldDetail?.jsonObj) {
|
|
2200
|
+
jsonObj = typeof fieldDetail.jsonObj === 'string'
|
|
2201
|
+
? JSON.parse(fieldDetail.jsonObj)
|
|
2202
|
+
: fieldDetail.jsonObj;
|
|
2203
|
+
if (fieldDetail.formId)
|
|
2204
|
+
formId = fieldDetail.formId;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
catch { /* fall through to minimal jsonObj */ }
|
|
2208
|
+
if (!jsonObj) {
|
|
2209
|
+
// Couldn't get field via getFormFieldPage — try getFields
|
|
2210
|
+
const fieldsRes = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
2211
|
+
objId: resolvedObjId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
2212
|
+
});
|
|
2213
|
+
const allFields = [...(fieldsRes?.data?.mainFields || []), ...(fieldsRes?.data?.subFields || [])];
|
|
2214
|
+
const gf = allFields.find((f) => String(f.fieldId) === String(fieldId));
|
|
2215
|
+
let colName = gf?.columnName || gf?.dataKey || '';
|
|
2216
|
+
if (!colName || /[^\x00-\x7F]/.test(colName)) {
|
|
2217
|
+
// 中文/空 columnName(getFields 对 EBuilder 不返回 columnName):用拼音首字母,
|
|
2218
|
+
// 不用 fld_<id>(否则物理列变 fld_xxx,破坏"数据库字段名=首字母拼音"约定)
|
|
2219
|
+
colName = toColumnName(String(gf?.fieldName || '')) || 'fld_' + fieldId.slice(-8);
|
|
2220
|
+
}
|
|
2221
|
+
jsonObj = {
|
|
2222
|
+
type: gf?.fieldKey || gf?.fieldType || 'Text',
|
|
2223
|
+
title: gf?.fieldName || '',
|
|
2224
|
+
fieldId, formId, groupId: '-1',
|
|
2225
|
+
formTableId: gf?.formTableId || formTableId,
|
|
2226
|
+
showOrder: '0.00',
|
|
2227
|
+
dataKey: colName, columnName: colName,
|
|
2228
|
+
optionSerials: [],
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
// 兜底补 componentKey:saveFormFieldNew 要求(getComponentKey() 为 null 会 NPE)。
|
|
2232
|
+
// getFields 兜底路径建不出 componentKey,这里按类型映射补上。
|
|
2233
|
+
if (!jsonObj.componentKey) {
|
|
2234
|
+
const CK = {
|
|
2235
|
+
Date: 'DateComponent', DateTime: 'DateComponent', Text: 'Text', Textarea: 'TextArea', Number: 'NumberComponent',
|
|
2236
|
+
Money: 'Money', Select: 'Select', CheckBox: 'CheckBox', RadioBox: 'RadioBox',
|
|
2237
|
+
Employee: 'Employee', Department: 'Department', EBuilder: 'Ebuilder',
|
|
2238
|
+
Phone: 'Phone', Email: 'Email', Mobile: 'Mobile', Location: 'Location', Switch: 'Switch', IDCard: 'IDCard',
|
|
2239
|
+
};
|
|
2240
|
+
jsonObj.componentKey = CK[jsonObj.type] || jsonObj.type;
|
|
2241
|
+
}
|
|
2242
|
+
// Apply title change
|
|
2243
|
+
if (editOpts.title) {
|
|
2244
|
+
jsonObj.title = editOpts.title;
|
|
2245
|
+
}
|
|
2246
|
+
// Apply required
|
|
2247
|
+
if (editOpts.required) {
|
|
2248
|
+
jsonObj.required = true;
|
|
2249
|
+
}
|
|
2250
|
+
else if (editOpts.noRequired) {
|
|
2251
|
+
jsonObj.required = false;
|
|
2252
|
+
}
|
|
2253
|
+
// Apply EBuilder association update(对齐 create Step 2 的 EBuilder 绑定 jsonObj 结构)。
|
|
2254
|
+
// 触发条件:--sform-id 换关联,或 --multiple/--no-multiple 切多选(无 --sform-id 时从
|
|
2255
|
+
// getFormLayout 拉现有关联 sformId/dataTitle/formName,缺了重保存会丢关联)。
|
|
2256
|
+
const isEbEdit = ['EBuilder', 'Ebuilder'].includes(jsonObj.type) && (editOpts.sformId || editOpts.dataTitle !== undefined || editOpts.multiple !== undefined);
|
|
2257
|
+
if (isEbEdit) {
|
|
2258
|
+
let sformId = editOpts.sformId || '';
|
|
2259
|
+
let fallbackDataTitle = '';
|
|
2260
|
+
let fallbackFormName = '';
|
|
2261
|
+
// getFields 对 EBuilder 不返回 columnName/dataKey,兜底会按字段名算坏 dataKey
|
|
2262
|
+
//(中文名+数字 → "2")。从 getFormLayout 拉完整 config,补 sformId + dataKey/columnName。
|
|
2263
|
+
if (!sformId || !jsonObj.dataKey || /[一-龥]/.test(String(jsonObj.dataKey))) {
|
|
2264
|
+
try {
|
|
2265
|
+
const layoutRes = await client.post(`${baseUrl}/api/ebuilder/form/builder/getFormLayout`, { formId, module: 'ebuilderform', customParam: {}, formTableIdList: [],
|
|
2266
|
+
designerParam: { onlyLayout: false, convert: false }, form: { id: formId } }, { ebbusinessid: formId });
|
|
2267
|
+
const lPages = layoutRes?.data?.formLayout?.pageLayouts || layoutRes?.data?.pageLayouts || [];
|
|
2268
|
+
const lComps = (lPages || []).flatMap((p) => Array.isArray(p.comps) ? p.comps : []);
|
|
2269
|
+
const full = lComps.find((c) => String(c?.config?.fieldId) === String(fieldId) || String(c?.id) === String(fieldId))?.config;
|
|
2270
|
+
if (full?.sformId) {
|
|
2271
|
+
sformId = sformId || full.sformId;
|
|
2272
|
+
fallbackDataTitle = fallbackDataTitle || full.dataTitle || '';
|
|
2273
|
+
fallbackFormName = fallbackFormName || full.formName || '';
|
|
2274
|
+
}
|
|
2275
|
+
if (full?.dataKey) {
|
|
2276
|
+
jsonObj.dataKey = full.dataKey;
|
|
2277
|
+
if (full.columnName)
|
|
2278
|
+
jsonObj.columnName = full.columnName;
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
catch { /* 拿不到关联则只切多选 */ }
|
|
2282
|
+
}
|
|
2283
|
+
if (sformId) {
|
|
2284
|
+
// 解析 dataTitle:纯数字 fieldId 直接用;'name' → 优先字段名含「名称」的业务字段,无则第一个;否则按名称查目标表字段
|
|
2285
|
+
let dataTitle = editOpts.dataTitle || fallbackDataTitle || 'name';
|
|
2286
|
+
if (!/^\d{15,}$/.test(dataTitle)) {
|
|
2287
|
+
try {
|
|
2288
|
+
const tfd = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
2289
|
+
objId: sformId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
2290
|
+
});
|
|
2291
|
+
const tfields = tfd?.data?.mainFields || [];
|
|
2292
|
+
if (dataTitle === 'name') {
|
|
2293
|
+
dataTitle = defaultEBuilderDataTitle(tfields);
|
|
2294
|
+
}
|
|
2295
|
+
else {
|
|
2296
|
+
const m = tfields.find(f => f.fieldName === dataTitle)
|
|
2297
|
+
|| tfields.find(f => String(f.fieldName || '').includes(dataTitle));
|
|
2298
|
+
dataTitle = m?.fieldId || dataTitle;
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
catch { /* 解析失败保留原值 */ }
|
|
2302
|
+
}
|
|
2303
|
+
// formName 默认查目标表单名
|
|
2304
|
+
let formName = editOpts.formName || fallbackFormName || '';
|
|
2305
|
+
if (!formName) {
|
|
2306
|
+
try {
|
|
2307
|
+
const tlist = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/getList/${appId}?apid=${appId}`, { ebbusinessid: appId });
|
|
2308
|
+
const tf = (tlist?.data || []).find((f) => String(f.id) === String(sformId));
|
|
2309
|
+
formName = tf?.formName || tf?.name || sformId;
|
|
2310
|
+
}
|
|
2311
|
+
catch {
|
|
2312
|
+
formName = sformId;
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
jsonObj.type = 'EBuilder';
|
|
2316
|
+
jsonObj.componentKey = 'Ebuilder';
|
|
2317
|
+
jsonObj.sformId = sformId;
|
|
2318
|
+
jsonObj.dataTitle = dataTitle;
|
|
2319
|
+
jsonObj.formName = formName;
|
|
2320
|
+
jsonObj.appId = appId;
|
|
2321
|
+
jsonObj.isAddForRelation = true;
|
|
2322
|
+
jsonObj.viewDataDetail = true;
|
|
2323
|
+
jsonObj.displayType = 'line';
|
|
2324
|
+
jsonObj.virtualForm = false;
|
|
2325
|
+
jsonObj.virtualTable = false;
|
|
2326
|
+
jsonObj.isSingle = editOpts.multiple ? false : true; // 默认单选;--multiple 多选
|
|
2327
|
+
jsonObj.isUnique = true;
|
|
2328
|
+
jsonObj.relateField = null;
|
|
2329
|
+
jsonObj.relateViewData = jsonObj.relateViewData || [];
|
|
2330
|
+
jsonObj.quickAddParams = jsonObj.quickAddParams || [];
|
|
2331
|
+
}
|
|
2332
|
+
else if (editOpts.multiple !== undefined) {
|
|
2333
|
+
// 拿不到关联:只切多选,不动关联配置
|
|
2334
|
+
jsonObj.isSingle = editOpts.multiple ? false : true;
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
// 多选切换(人员/部门/Browser):--multiple 多选 / --no-multiple 单选。
|
|
2338
|
+
// EBuilder 已在上文分支处理。
|
|
2339
|
+
if (!['EBuilder', 'Ebuilder'].includes(jsonObj.type) && editOpts.multiple !== undefined) {
|
|
2340
|
+
jsonObj.isSingle = editOpts.multiple ? false : true;
|
|
2341
|
+
}
|
|
2342
|
+
// Apply relateFrom(值带出 / ffRelateField):选 EBuilder 关联记录后自动带出目标表单字段值。
|
|
2343
|
+
// ffRelateField 不在字段的 getFormFieldPage record / otherProperties 里,由平台存到关联配置中。
|
|
2344
|
+
if (editOpts.relateFrom) {
|
|
2345
|
+
// 找 EBuilder 字段(从 getFormFieldPage)
|
|
2346
|
+
const ebRecord = await (async () => {
|
|
2347
|
+
try {
|
|
2348
|
+
const r2 = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/getFormFieldPage?apid=${appId}`, {
|
|
2349
|
+
pageNo: 1, pageSize: 200, module: 'ebuilderform',
|
|
2350
|
+
customParam: { appId },
|
|
2351
|
+
formFieldSearchEntity: { isDelete: 0, status: 'enable', formId },
|
|
2352
|
+
}, { ebbusinessid: formId });
|
|
2353
|
+
const recs = r2?.data?.pageDatas?.result || r2?.data?.data?.records || r2?.data?.data || [];
|
|
2354
|
+
return recs.find((r) => r.title === editOpts.relateFrom || r.fieldName === editOpts.relateFrom);
|
|
2355
|
+
}
|
|
2356
|
+
catch {
|
|
2357
|
+
return null;
|
|
2358
|
+
}
|
|
2359
|
+
})();
|
|
2360
|
+
if (!ebRecord || !ebRecord.fieldId || !ebRecord.sformId) {
|
|
2361
|
+
console.error(` 警告: 找不到 EBuilder 字段 "${editOpts.relateFrom}" 或其 sformId,跳过 relateFrom 配置`);
|
|
2362
|
+
}
|
|
2363
|
+
else {
|
|
2364
|
+
const sourceFieldId = editOpts.relateFromSource || '';
|
|
2365
|
+
// 从目标表单查源字段名(ffRelateField.title)
|
|
2366
|
+
let sourceTitle = sourceFieldId;
|
|
2367
|
+
try {
|
|
2368
|
+
const tfd = await client.post(`${baseUrl}/api/ebuilder/form/dataset/v1/getFields`, {
|
|
2369
|
+
objId: ebRecord.sformId, userid: auth.userId, tenantKey: auth.tenantKey,
|
|
2370
|
+
});
|
|
2371
|
+
const tf = tfd?.data?.mainFields || [];
|
|
2372
|
+
const sf = tf.find((f) => String(f.fieldId) === String(sourceFieldId));
|
|
2373
|
+
if (sf)
|
|
2374
|
+
sourceTitle = sf.fieldName || sourceFieldId;
|
|
2375
|
+
}
|
|
2376
|
+
catch { /* 留 fieldId 兜底 */ }
|
|
2377
|
+
jsonObj.ffRelateField = {
|
|
2378
|
+
component: { key: jsonObj.type || 'Text' },
|
|
2379
|
+
isField: true,
|
|
2380
|
+
title: sourceTitle,
|
|
2381
|
+
key: sourceFieldId,
|
|
2382
|
+
isMultiple: false,
|
|
2383
|
+
fieldId: ebRecord.fieldId,
|
|
2384
|
+
};
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
const payload = {
|
|
2388
|
+
formId, module: 'ebuilderform',
|
|
2389
|
+
customParam: { appId, objId: resolvedObjId },
|
|
2390
|
+
jsonObj: JSON.stringify(jsonObj),
|
|
2391
|
+
};
|
|
2392
|
+
const data = await client.post(`${baseUrl}/api/ebuilder/form/form/field/manage/saveFormFieldNew?apid=${appId}`, payload, { ebbusinessid: formId });
|
|
2393
|
+
if (data?.code === 200 && data?.status) {
|
|
2394
|
+
const changes = [];
|
|
2395
|
+
if (editOpts.title)
|
|
2396
|
+
changes.push(`title="${editOpts.title}"`);
|
|
2397
|
+
if (editOpts.required)
|
|
2398
|
+
changes.push('required');
|
|
2399
|
+
if (editOpts.noRequired)
|
|
2400
|
+
changes.push('!required');
|
|
2401
|
+
if (editOpts.sformId)
|
|
2402
|
+
changes.push(`EBuilder→${editOpts.sformId}`);
|
|
2403
|
+
if (editOpts.dataTitle !== undefined)
|
|
2404
|
+
changes.push(`dataTitle=${editOpts.dataTitle}`);
|
|
2405
|
+
if (editOpts.relateFrom)
|
|
2406
|
+
changes.push(`relateFrom ${editOpts.relateFrom}→${editOpts.relateFromSource || ''}`);
|
|
2407
|
+
if (editOpts.multiple !== undefined)
|
|
2408
|
+
changes.push(editOpts.multiple ? 'multiple' : '!multiple');
|
|
2409
|
+
const msg = opts.json ? JSON.stringify({ fieldId, changes }) : `字段已更新: ${fieldId} (${changes.join(', ') || 'no changes'})`;
|
|
2410
|
+
console.log(msg);
|
|
2411
|
+
}
|
|
2412
|
+
else {
|
|
2413
|
+
console.error(`编辑失败: ${data?.msg || '未知错误'}`);
|
|
2414
|
+
process.exit(1);
|
|
2415
|
+
}
|
|
2416
|
+
});
|
|
2417
|
+
form.command('delete')
|
|
2418
|
+
.description('删除表单')
|
|
2419
|
+
.argument('<objId>', '表单 objId')
|
|
2420
|
+
.argument('<appId>', '应用 ID')
|
|
2421
|
+
.action(async (objId, appId) => {
|
|
2422
|
+
const opts = program.opts();
|
|
2423
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
2424
|
+
if (!auth)
|
|
2425
|
+
process.exit(1);
|
|
2426
|
+
const client = new ApiClient(auth);
|
|
2427
|
+
const baseUrl = auth.getBaseUrl();
|
|
2428
|
+
const data = await client.get(`${baseUrl}/api/bs/ebuilder/form/obj/delete/${objId}?apid=${appId}&objId=${objId}`, { ebbusinessid: appId });
|
|
2429
|
+
if (data?.code === 200 && data?.status) {
|
|
2430
|
+
const msg = opts.json ? JSON.stringify({ success: true, objId }) : `表单已删除: ${objId}`;
|
|
2431
|
+
console.log(msg);
|
|
2432
|
+
}
|
|
2433
|
+
else {
|
|
2434
|
+
console.error(`删除失败: ${data?.msg || '未知错误'}`);
|
|
2435
|
+
process.exit(1);
|
|
2436
|
+
}
|
|
2437
|
+
});
|
|
2438
|
+
form.command('list-view')
|
|
2439
|
+
.description('查看表单的台账/数据列表')
|
|
2440
|
+
.argument('<objId>', '表单 objId')
|
|
2441
|
+
.argument('<appId>', '应用 ID')
|
|
2442
|
+
.action(async (objId, appId) => {
|
|
2443
|
+
const opts = program.opts();
|
|
2444
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
2445
|
+
if (!auth)
|
|
2446
|
+
process.exit(1);
|
|
2447
|
+
const client = new ApiClient(auth);
|
|
2448
|
+
const baseUrl = auth.getBaseUrl();
|
|
2449
|
+
const data = await client.post(`${baseUrl}/api/bs/ebuilder/form/list/getNList?apid=${appId}&appId=${appId}&tpaid=${objId}&objId=${objId}`, { objId });
|
|
2450
|
+
if (data?.code !== 200 || !data?.status) {
|
|
2451
|
+
console.error(`查询失败: ${data?.msg || '未知错误'}`);
|
|
2452
|
+
process.exit(1);
|
|
2453
|
+
}
|
|
2454
|
+
const lists = data?.data || [];
|
|
2455
|
+
if (opts.json) {
|
|
2456
|
+
console.log(JSON.stringify(lists, null, 2));
|
|
2457
|
+
}
|
|
2458
|
+
else {
|
|
2459
|
+
console.log(`表单 ${objId} 共 ${lists.length} 个台账:`);
|
|
2460
|
+
for (const l of lists) {
|
|
2461
|
+
console.log(` [${l.id}] ${l.name || '(未命名)'} viewType=${l.viewType}`);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
});
|
|
2465
|
+
return form;
|
|
2466
|
+
}
|
|
2467
|
+
//# sourceMappingURL=form.js.map
|