weaver-e10-builder 1.2.5 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/org-employee-template.xlsx +0 -0
- package/dist/commands/form-add-physical-field.d.ts +3 -0
- package/dist/commands/form-add-physical-field.d.ts.map +1 -0
- package/dist/commands/form-add-physical-field.js +125 -0
- package/dist/commands/form-add-physical-field.js.map +1 -0
- package/dist/commands/form-button.d.ts.map +1 -1
- package/dist/commands/form-button.js +28 -5
- package/dist/commands/form-button.js.map +1 -1
- package/dist/commands/form-field-linkage.d.ts.map +1 -1
- package/dist/commands/form-field-linkage.js +27 -6
- package/dist/commands/form-field-linkage.js.map +1 -1
- package/dist/commands/form-formula-linkage.d.ts +3 -0
- package/dist/commands/form-formula-linkage.d.ts.map +1 -0
- package/dist/commands/form-formula-linkage.js +434 -0
- package/dist/commands/form-formula-linkage.js.map +1 -0
- package/dist/commands/form-formula.d.ts.map +1 -1
- package/dist/commands/form-formula.js +22 -3
- package/dist/commands/form-formula.js.map +1 -1
- package/dist/commands/form-list-click.d.ts.map +1 -1
- package/dist/commands/form-list-click.js +2 -1
- package/dist/commands/form-list-click.js.map +1 -1
- package/dist/commands/form-list-permission.js +1 -1
- package/dist/commands/form-list-permission.js.map +1 -1
- package/dist/commands/form-native.d.ts.map +1 -1
- package/dist/commands/form-native.js +11 -3
- package/dist/commands/form-native.js.map +1 -1
- package/dist/commands/form-physical-table.d.ts +3 -0
- package/dist/commands/form-physical-table.d.ts.map +1 -0
- package/dist/commands/form-physical-table.js +195 -0
- package/dist/commands/form-physical-table.js.map +1 -0
- package/dist/commands/form.d.ts.map +1 -1
- package/dist/commands/form.js +246 -549
- package/dist/commands/form.js.map +1 -1
- package/dist/commands/matrix.d.ts.map +1 -1
- package/dist/commands/matrix.js +175 -36
- package/dist/commands/matrix.js.map +1 -1
- package/dist/commands/org.d.ts +10 -0
- package/dist/commands/org.d.ts.map +1 -0
- package/dist/commands/org.js +402 -0
- package/dist/commands/org.js.map +1 -0
- package/dist/commands/role.d.ts.map +1 -1
- package/dist/commands/role.js +106 -62
- package/dist/commands/role.js.map +1 -1
- package/dist/commands/wf-form.d.ts.map +1 -1
- package/dist/commands/wf-form.js +501 -26
- package/dist/commands/wf-form.js.map +1 -1
- package/dist/commands/workflow-auth-resp.d.ts.map +1 -1
- package/dist/commands/workflow-auth-resp.js +53 -5
- package/dist/commands/workflow-auth-resp.js.map +1 -1
- package/dist/commands/workflow-create-auth-resp.d.ts.map +1 -1
- package/dist/commands/workflow-create-auth-resp.js +5 -1
- package/dist/commands/workflow-create-auth-resp.js.map +1 -1
- package/dist/commands/workflow-operator-sign.js +1 -1
- package/dist/commands/workflow-operator-sign.js.map +1 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +78 -9
- package/dist/commands/workflow.js.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/services/action-detail.d.ts.map +1 -1
- package/dist/services/action-detail.js +5 -4
- package/dist/services/action-detail.js.map +1 -1
- package/dist/services/actionflow/components/action_eb_update_compiler.d.ts.map +1 -1
- package/dist/services/actionflow/components/action_eb_update_compiler.js +22 -7
- package/dist/services/actionflow/components/action_eb_update_compiler.js.map +1 -1
- package/dist/services/actionflow/components/approve_create.d.ts.map +1 -1
- package/dist/services/actionflow/components/approve_create.js +6 -3
- package/dist/services/actionflow/components/approve_create.js.map +1 -1
- package/dist/services/actionflow/components/custom_set_form.d.ts.map +1 -1
- package/dist/services/actionflow/components/custom_set_form.js +35 -1
- package/dist/services/actionflow/components/custom_set_form.js.map +1 -1
- package/dist/services/actionflow/components/custom_trigger.d.ts.map +1 -1
- package/dist/services/actionflow/components/custom_trigger.js +4 -1
- package/dist/services/actionflow/components/custom_trigger.js.map +1 -1
- package/dist/services/actionflow/components/dml_execute.d.ts.map +1 -1
- package/dist/services/actionflow/components/dml_execute.js +7 -3
- package/dist/services/actionflow/components/dml_execute.js.map +1 -1
- package/dist/services/actionflow/components/document_actions.d.ts.map +1 -1
- package/dist/services/actionflow/components/document_actions.js +7 -3
- package/dist/services/actionflow/components/document_actions.js.map +1 -1
- package/dist/services/actionflow/components/hrm_actions.d.ts.map +1 -1
- package/dist/services/actionflow/components/hrm_actions.js +7 -3
- package/dist/services/actionflow/components/hrm_actions.js.map +1 -1
- package/dist/services/actionflow/components/multiple_execution.d.ts.map +1 -1
- package/dist/services/actionflow/components/multiple_execution.js +14 -5
- package/dist/services/actionflow/components/multiple_execution.js.map +1 -1
- package/dist/services/actionflow/components/output.js +1 -1
- package/dist/services/actionflow/components/output.js.map +1 -1
- package/dist/services/actionflow/components/send_message.js +2 -2
- package/dist/services/actionflow/components/send_message.js.map +1 -1
- package/dist/services/actionflow/components/workflow_actions.d.ts.map +1 -1
- package/dist/services/actionflow/components/workflow_actions.js +7 -3
- package/dist/services/actionflow/components/workflow_actions.js.map +1 -1
- package/dist/services/actionflow/context_preflight.d.ts.map +1 -1
- package/dist/services/actionflow/context_preflight.js +12 -2
- package/dist/services/actionflow/context_preflight.js.map +1 -1
- package/dist/services/actionflow/context_provider.d.ts.map +1 -1
- package/dist/services/actionflow/context_provider.js +21 -2
- package/dist/services/actionflow/context_provider.js.map +1 -1
- package/dist/services/actionflow/custom_trigger_context.d.ts +10 -0
- package/dist/services/actionflow/custom_trigger_context.d.ts.map +1 -0
- package/dist/services/actionflow/custom_trigger_context.js +46 -0
- package/dist/services/actionflow/custom_trigger_context.js.map +1 -0
- package/dist/services/actionflow/filter_rules.js +2 -2
- package/dist/services/actionflow/filter_rules.js.map +1 -1
- package/dist/services/actionflow/formula_service.d.ts +9 -1
- package/dist/services/actionflow/formula_service.d.ts.map +1 -1
- package/dist/services/actionflow/formula_service.js +59 -21
- package/dist/services/actionflow/formula_service.js.map +1 -1
- package/dist/services/actionflow/id_generator.d.ts +2 -0
- package/dist/services/actionflow/id_generator.d.ts.map +1 -1
- package/dist/services/actionflow/id_generator.js +13 -0
- package/dist/services/actionflow/id_generator.js.map +1 -1
- package/dist/services/actionflow/preflight_service.d.ts.map +1 -1
- package/dist/services/actionflow/preflight_service.js +6 -1
- package/dist/services/actionflow/preflight_service.js.map +1 -1
- package/dist/services/actionflow/workflow_compiler.d.ts.map +1 -1
- package/dist/services/actionflow/workflow_compiler.js +89 -35
- package/dist/services/actionflow/workflow_compiler.js.map +1 -1
- package/dist/services/auth-resp-condition.d.ts +35 -0
- package/dist/services/auth-resp-condition.d.ts.map +1 -0
- package/dist/services/auth-resp-condition.js +111 -0
- package/dist/services/auth-resp-condition.js.map +1 -0
- package/dist/services/condition-rules.d.ts.map +1 -1
- package/dist/services/condition-rules.js +4 -1
- package/dist/services/condition-rules.js.map +1 -1
- package/dist/services/esb-interface/document-parser.d.ts.map +1 -1
- package/dist/services/esb-interface/document-parser.js +62 -5
- package/dist/services/esb-interface/document-parser.js.map +1 -1
- package/dist/services/form-association.d.ts +19 -0
- package/dist/services/form-association.d.ts.map +1 -1
- package/dist/services/form-association.js +64 -0
- package/dist/services/form-association.js.map +1 -1
- package/dist/services/form-button-action.d.ts +7 -0
- package/dist/services/form-button-action.d.ts.map +1 -1
- package/dist/services/form-button-action.js +11 -1
- package/dist/services/form-button-action.js.map +1 -1
- package/dist/services/form-create.d.ts +4 -0
- package/dist/services/form-create.d.ts.map +1 -1
- package/dist/services/form-create.js +18 -12
- package/dist/services/form-create.js.map +1 -1
- package/dist/services/form-field-build.d.ts +71 -0
- package/dist/services/form-field-build.d.ts.map +1 -0
- package/dist/services/form-field-build.js +436 -0
- package/dist/services/form-field-build.js.map +1 -0
- package/dist/services/form-field-patch.d.ts +3 -1
- package/dist/services/form-field-patch.d.ts.map +1 -1
- package/dist/services/form-field-patch.js +23 -9
- package/dist/services/form-field-patch.js.map +1 -1
- package/dist/services/form-formula-linkage.d.ts +349 -0
- package/dist/services/form-formula-linkage.d.ts.map +1 -0
- package/dist/services/form-formula-linkage.js +546 -0
- package/dist/services/form-formula-linkage.js.map +1 -0
- package/dist/services/form-layout-insert.d.ts +84 -0
- package/dist/services/form-layout-insert.d.ts.map +1 -0
- package/dist/services/form-layout-insert.js +244 -0
- package/dist/services/form-layout-insert.js.map +1 -0
- package/dist/services/form-linkage-condition.d.ts +159 -0
- package/dist/services/form-linkage-condition.d.ts.map +1 -0
- package/dist/services/form-linkage-condition.js +307 -0
- package/dist/services/form-linkage-condition.js.map +1 -0
- package/dist/services/form-physical-table.d.ts +115 -0
- package/dist/services/form-physical-table.d.ts.map +1 -0
- package/dist/services/form-physical-table.js +153 -0
- package/dist/services/form-physical-table.js.map +1 -0
- package/dist/services/formula-detail.d.ts +98 -6
- package/dist/services/formula-detail.d.ts.map +1 -1
- package/dist/services/formula-detail.js +208 -26
- package/dist/services/formula-detail.js.map +1 -1
- package/dist/services/matrix-rows.d.ts +43 -0
- package/dist/services/matrix-rows.d.ts.map +1 -0
- package/dist/services/matrix-rows.js +104 -0
- package/dist/services/matrix-rows.js.map +1 -0
- package/dist/services/org-department.d.ts +114 -0
- package/dist/services/org-department.d.ts.map +1 -0
- package/dist/services/org-department.js +209 -0
- package/dist/services/org-department.js.map +1 -0
- package/dist/services/org-employee.d.ts +191 -0
- package/dist/services/org-employee.d.ts.map +1 -0
- package/dist/services/org-employee.js +400 -0
- package/dist/services/org-employee.js.map +1 -0
- package/dist/services/role-member.d.ts +99 -0
- package/dist/services/role-member.d.ts.map +1 -0
- package/dist/services/role-member.js +206 -0
- package/dist/services/role-member.js.map +1 -0
- package/dist/services/workflow-info.d.ts +83 -0
- package/dist/services/workflow-info.d.ts.map +1 -0
- package/dist/services/workflow-info.js +97 -0
- package/dist/services/workflow-info.js.map +1 -0
- package/dist/services/workflow-odoc-setting.d.ts +1 -1
- package/dist/services/workflow-odoc-setting.d.ts.map +1 -1
- package/dist/services/workflow-odoc-setting.js +15 -6
- package/dist/services/workflow-odoc-setting.js.map +1 -1
- package/dist/utils/column-name.d.ts +24 -0
- package/dist/utils/column-name.d.ts.map +1 -1
- package/dist/utils/column-name.js +16 -0
- package/dist/utils/column-name.js.map +1 -1
- package/ecodecmd/package.json +1 -2
- package/ecodecmd/src/loaddep.js +19 -9
- package/package.json +4 -4
- package/skill-docs/SKILL.md +13 -9
- package/skill-docs/_catalog.md +86 -81
- package/skill-docs/actionflows/SKILL.md +173 -170
- package/skill-docs/actionflows/references/components/logic/Output.md +1 -1
- package/skill-docs/actionflows/references/components/trigger/CustomTrigger.md +32 -0
- package/skill-docs/actionflows/references/components/workflow/ApproveCreate.md +1 -1
- package/skill-docs/references/common/guide-plan-template.md +9 -3
- package/skill-docs/references/common/ref-cli-commands.md +54 -23
- package/skill-docs/references/esb-interface/guide-import.md +4 -1
- package/skill-docs/references/form/guide-create-form.md +13 -0
- package/skill-docs/references/form/guide-field-linkage.md +2 -0
- package/skill-docs/references/form/guide-form-association.md +1 -1
- package/skill-docs/references/form/guide-form-formula-functions.md +36 -0
- package/skill-docs/references/form/guide-form-layout-incremental.md +75 -0
- package/skill-docs/references/form/guide-form-linkage.md +62 -4
- package/skill-docs/references/form/guide-form-physical-table.md +146 -0
- package/skill-docs/references/form/guide-formula-linkage.md +250 -0
- package/skill-docs/references/form/ref-form-fields.md +6 -0
- package/skill-docs/references/hrm/guide-matrix.md +79 -16
- package/skill-docs/references/hrm/guide-org-employee.md +124 -0
- package/skill-docs/references/report/cell-config-echo-guide.md +1 -1
- package/skill-docs/references/report/dataset-query-filter-guide.md +3 -3
- package/skill-docs/references/report/date-range-query-reference.md +2 -2
- package/skill-docs/references/report/query-condition-api-guide.md +11 -11
- package/skill-docs/references/report/report-designer-feature-map.md +1 -1
- package/skill-docs/references/report/report-permission-guide.md +2 -2
- package/skill-docs/references/workflow/guide-workflow-auth-resp.md +48 -22
- package/skill-docs/references/workflow/guide-workflow-odoc-setting.md +248 -0
- package/skill-docs/references/workflow/guide-workflow-odoc.md +23 -51
- package/skill-docs/references/workflow/guide-workflow.md +1 -1
package/dist/commands/wf-form.js
CHANGED
|
@@ -6,6 +6,10 @@ import { getRichType, resolveBrowserConfig, richIdUnits, BROWSER_TYPE_CATALOG }
|
|
|
6
6
|
import { dropFieldComponents, prepareLayoutForSave } from '../services/form-field-drop.js';
|
|
7
7
|
import { BROWSER_BEAN_SEARCH, pickBrowserBean, browserBeanCandidates, toBrowserType } from '../services/browser.js';
|
|
8
8
|
import { randomUUID } from 'node:crypto';
|
|
9
|
+
import { readFileSync } from 'node:fs';
|
|
10
|
+
import { buildConditionDetail, resolveWorkflowEffect, readLinkageConfig } from '../services/form-linkage-condition.js';
|
|
11
|
+
import { resolveFormulaRefs } from '../services/formula-detail.js';
|
|
12
|
+
import { FLOW_ROUTE, assertAssignableTarget, buildAssignExpression, buildDrivingColumnsBody, buildDrivingEventsBody, buildFdEffect, buildFormulaLinkageName, buildFormulaSavePayload, buildLinkageApiUrl, buildLinkageDeleteBody, buildLinkageListBody, buildLinkageQueryBody, buildLinkageSavePayload, buildLinkageUpdatePayload, buildMonitorActions, buildPreCheckBody, describeColumn, extractEffect, extractTriggerState, findColumnById, isAssignableTarget, moduleColumnsOf, partitionDrivingColumns, resolveDrivingColumn, resolveDrivingEvent, summarizeLinkage, targetDriftWarning, toBareLabel, } from '../services/form-formula-linkage.js';
|
|
9
13
|
/** 流程表单选项配色(对齐浏览器抓包 styleConfig.OptionBackground,循环取用)。 */
|
|
10
14
|
const OPTION_COLORS = ['#FFBD4F', '#FF716E', '#3BD8C3', '#5B8FF9', '#5AD8A6', '#F6BD16', '#E86452', '#6DC8EC', '#945FB9', '#FF99C3'];
|
|
11
15
|
/**
|
|
@@ -34,7 +38,7 @@ function canon(ck) {
|
|
|
34
38
|
textarea: 'TextArea', text: 'Text', number: 'Number', money: 'Money',
|
|
35
39
|
date: 'Date', datetime: 'DateTime', select: 'Select', selectmultiple: 'SelectMultiple',
|
|
36
40
|
// 平台的浏览按钮类型名是 RelateBrowser(不是 Browser)——写错会被拒「程序异常」
|
|
37
|
-
employee: 'Employee', department: 'Department', browser: 'RelateBrowser',
|
|
41
|
+
employee: 'Employee', department: 'Department', subcompany: 'SubCompany', browser: 'RelateBrowser',
|
|
38
42
|
relatebrowser: 'RelateBrowser', 浏览: 'RelateBrowser',
|
|
39
43
|
file: 'FileComponent', filecomponent: 'FileComponent', upload: 'FileComponent', 附件: 'FileComponent',
|
|
40
44
|
ebuilder: 'EBuilder', eb: 'EBuilder', flow: 'Flow',
|
|
@@ -278,7 +282,7 @@ async function genIds(client, baseUrl, n) {
|
|
|
278
282
|
*/
|
|
279
283
|
export const WF_FORM_HANDLED = new Set([
|
|
280
284
|
'Number', 'Money', 'TextArea', 'Date', 'DateTime',
|
|
281
|
-
'Employee', 'Department', 'RelateBrowser', 'Select', 'EBuilder', 'Flow',
|
|
285
|
+
'Employee', 'Department', 'SubCompany', 'RelateBrowser', 'Select', 'EBuilder', 'Flow',
|
|
282
286
|
]);
|
|
283
287
|
/**
|
|
284
288
|
* 构建字段 jsonObj(add-field / add-fields 共用)。返回未带 fieldId 的模板,调用方填 fieldId。
|
|
@@ -327,6 +331,16 @@ export async function buildFieldJsonObj(client, baseUrl, fieldName, fieldType, t
|
|
|
327
331
|
jsonObj.defaultExtend = { date: { type: 'current' }, time: { customValue: '00:00', type: 'custom' } };
|
|
328
332
|
}
|
|
329
333
|
}
|
|
334
|
+
else if (ck === 'SubCompany') {
|
|
335
|
+
Object.assign(jsonObj, {
|
|
336
|
+
componentKey: 'Subcompany',
|
|
337
|
+
isSingle: o.multiple ? false : true,
|
|
338
|
+
displayType: 'space', defaultExtend: { type: 'custom', relation: 'current' },
|
|
339
|
+
viewDataDetail: true, showDimension: true,
|
|
340
|
+
pcDisplaySelectedCount: { type: 'default' },
|
|
341
|
+
mobileDisplaySelectedCount: { type: 'default' },
|
|
342
|
+
});
|
|
343
|
+
}
|
|
330
344
|
else if (ck === 'Employee' || ck === 'Department') {
|
|
331
345
|
jsonObj.isSingle = o.multiple ? false : true;
|
|
332
346
|
if (o.defaultCurrent && ck === 'Employee') {
|
|
@@ -453,13 +467,13 @@ export function registerWorkflowFormCommands(program) {
|
|
|
453
467
|
}
|
|
454
468
|
// ---------- wf-form add-field ----------
|
|
455
469
|
wfForm.command('add-field')
|
|
456
|
-
.description('给流程表单加字段(Text/TextArea/Number/Money/Date/DateTime/Select/Employee/Department/浏览按钮/EBuilder/Flow)')
|
|
470
|
+
.description('给流程表单加字段(Text/TextArea/Number/Money/Date/DateTime/Select/Employee/Department/SubCompany/浏览按钮/EBuilder/Flow)')
|
|
457
471
|
.argument('<formId>', '流程表单 formId')
|
|
458
472
|
.argument('<fieldName>', '字段名')
|
|
459
473
|
.argument('<fieldType>', '字段类型')
|
|
460
474
|
.argument('[title]', '字段显示名(不传同 fieldName)')
|
|
461
475
|
.option('--sub-form-id <id>', '在指定明细子表里加字段(明细表 subFormId)')
|
|
462
|
-
.option('--multiple', '多选(Employee/Department/浏览按钮/下拉Select→SelectMultiple)')
|
|
476
|
+
.option('--multiple', '多选(Employee/Department/SubCompany/浏览按钮/下拉Select→SelectMultiple)')
|
|
463
477
|
.option('--precision <n>', '小数位(Number 缺省 0 / Money 缺省 2 / 百分比缺省 1)')
|
|
464
478
|
.option('--percent', 'Number 百分比显示(numberType=percentage)')
|
|
465
479
|
.option('--default <value>', 'Number/Money 默认值')
|
|
@@ -864,7 +878,12 @@ export function registerWorkflowFormCommands(program) {
|
|
|
864
878
|
.description('显隐联动管理(/api/workflow/core/form/setting/viewlinkage)')
|
|
865
879
|
.argument('<formId>', '流程表单 formId')
|
|
866
880
|
.argument('<name>', '联动名称')
|
|
867
|
-
.requiredOption('--config <json>', '联动配置 JSON:{"conditions":[{"field":"请假天数","operator":"EQUAL","value":"3"}],"actions":[{"prop":"hide","fields":["请假事由"]}]}。prop: hide/show/edit/readonly/required/hideLine;
|
|
881
|
+
.requiredOption('--config <json>', '联动配置 JSON:{"conditions":[{"field":"请假天数","operator":"EQUAL","value":"3"}],"actions":[{"prop":"hide","fields":["请假事由"]}]}。prop: hide/show/edit/readonly/required/hideLine;'
|
|
882
|
+
+ 'operator: EQUAL/NOT_EQUAL/CONTAIN/NOT_CONTAIN/GREATER/LESS/GREATER_EQUAL/LESS_EQUAL,'
|
|
883
|
+
+ '**为空写 NULL(或 EMPTY,会归一)、不为空写 NOT_NULL**(默认 EQUAL);'
|
|
884
|
+
+ '顶层可选 "detailType":"AND|OR"(界面上「所有」/「任一」);'
|
|
885
|
+
+ '"effect" 指定生效维度:不写=当前表单,"workFlow"=当前工作流,"includeNode"=选择节点(后者另需 "nodeId")')
|
|
886
|
+
.option('--workflow-id <id>', '流程 ID。生效维度选「当前工作流」或「选择节点」时必给(该联动挂在流程上,refId 也是它)')
|
|
868
887
|
.action(async (formId, name, cmdOpts) => {
|
|
869
888
|
try {
|
|
870
889
|
const opts = program.opts();
|
|
@@ -874,13 +893,7 @@ export function registerWorkflowFormCommands(program) {
|
|
|
874
893
|
const client = new ApiClient(auth);
|
|
875
894
|
const baseUrl = auth.getBaseUrl();
|
|
876
895
|
const ebb = { ebbusinessid: formId };
|
|
877
|
-
|
|
878
|
-
try {
|
|
879
|
-
cfg = JSON.parse(cmdOpts.config);
|
|
880
|
-
}
|
|
881
|
-
catch {
|
|
882
|
-
throw new Error('config JSON 解析失败');
|
|
883
|
-
}
|
|
896
|
+
const cfg = readLinkageConfig(cmdOpts.config);
|
|
884
897
|
if (!Array.isArray(cfg.conditions) || !Array.isArray(cfg.actions) || !cfg.actions.length) {
|
|
885
898
|
throw new Error('config 需含 conditions[] 和非空 actions[]');
|
|
886
899
|
}
|
|
@@ -906,15 +919,18 @@ export function registerWorkflowFormCommands(program) {
|
|
|
906
919
|
// 这里曾写 `configs.find(c => !c.system)?.name ? '流程表单' : '流程表单'`——两个分支同值等于没写,
|
|
907
920
|
// 且 configs 是**布局字段**数组、取到的是字段名而非表单名,照字面"修好"反而会把字段名当表单名发出去。
|
|
908
921
|
const formName = '流程表单';
|
|
909
|
-
// conditions
|
|
922
|
+
// conditions。算子的归一与「为空不能带 sourceContent」那套规则和 eb 表那条路**同一份**
|
|
923
|
+
// (见 services/form-linkage-condition.ts)—— 两边都是同一个平台接口族,踩的是同一个坑。
|
|
924
|
+
const condWarnings = [];
|
|
910
925
|
const conditionDetails = cfg.conditions.map((c) => {
|
|
911
926
|
const f = findField(c.field);
|
|
912
927
|
if (!f)
|
|
913
928
|
throw new Error(`条件字段未找到: "${c.field}",可用: ${allFields.map((x) => x.name).join(', ')}`);
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
929
|
+
const { detail, warnings: w } = buildConditionDetail({
|
|
930
|
+
fieldId: String(f.id), operator: c.operator, value: c.value, options: c.options,
|
|
931
|
+
});
|
|
932
|
+
condWarnings.push(...w);
|
|
933
|
+
return detail;
|
|
918
934
|
});
|
|
919
935
|
// actions
|
|
920
936
|
const linkageDetails = cfg.actions.map((a) => ({
|
|
@@ -927,12 +943,30 @@ export function registerWorkflowFormCommands(program) {
|
|
|
927
943
|
}),
|
|
928
944
|
detailOptions: [],
|
|
929
945
|
}));
|
|
946
|
+
// 生效维度。工作流表单这条路由上设计器提供四种(当前表单 / 表单布局 / 当前工作流 / 选择节点),
|
|
947
|
+
// 解析统一收在 services/form-linkage-condition.ts 的 `resolveWorkflowEffect` 里 ——
|
|
948
|
+
// 函数联动那边用的是同一份,别再内联第二遍。
|
|
949
|
+
const { effect, refId } = await resolveWorkflowEffect(client, baseUrl, {
|
|
950
|
+
module: MODULE, route: ROUTE, formId,
|
|
951
|
+
dimension: cfg.effect,
|
|
952
|
+
workflowId: cmdOpts.workflowId,
|
|
953
|
+
nodeKey: cfg.nodeId || cfg.node,
|
|
954
|
+
layoutId: cfg.layoutId,
|
|
955
|
+
});
|
|
956
|
+
const fdEffect = buildFdEffect(formId, effect, MODULE);
|
|
930
957
|
const payload = {
|
|
931
|
-
fdEffect
|
|
932
|
-
|
|
958
|
+
fdEffect,
|
|
959
|
+
// targetName 设计器不发,去掉;targetModule 是 workflow
|
|
960
|
+
condition: { targetId: formId, targetModule: MODULE, conditionType: 'FIELD', conditionDetails, formulaId: '', formulaName: '' },
|
|
933
961
|
linkageDetails,
|
|
934
|
-
|
|
962
|
+
// ⚠ `refId` 是**这条联动挂在谁身上**,在这条路由上就是**流程 ID**;原来写死 formId 是错的。
|
|
963
|
+
// 注意它**不等于** optionList 的取值 —— 选「选择节点」时取值是节点 ID,refId 仍是流程 ID
|
|
964
|
+
// (设计器报文里这两处就是这么分的,别照着「取值」抄)。refId 由 resolveWorkflowEffect 一并给出。
|
|
965
|
+
module: MODULE, formId, refId,
|
|
966
|
+
enableState: '1', linkageName: name,
|
|
935
967
|
};
|
|
968
|
+
for (const w of condWarnings)
|
|
969
|
+
console.error(` ⚠ ${w}`);
|
|
936
970
|
const r = await client.post(`${baseUrl}${ROUTE}/setting/viewlinkage/add`, payload, ebb);
|
|
937
971
|
if (r?.code !== 200 || !r?.status)
|
|
938
972
|
throw new Error(`联动添加失败: ${r?.msg || '未知错误'}`);
|
|
@@ -1000,6 +1034,7 @@ export function registerWorkflowFormCommands(program) {
|
|
|
1000
1034
|
.requiredOption('--target <fieldId>', '目标字段 ID(公式结果写入此字段)')
|
|
1001
1035
|
.option('--name <name>', '公式名称(默认自动生成)')
|
|
1002
1036
|
.option('--return-type <type>', '返回类型: Number / String / Date', 'Number')
|
|
1037
|
+
.option('--formula-id <id>', '要**更新**的已有公式 ID(原地改,不新建)。不传则新建一条')
|
|
1003
1038
|
.action(async (formId, cmdOpts) => {
|
|
1004
1039
|
try {
|
|
1005
1040
|
const opts = program.opts();
|
|
@@ -1016,19 +1051,34 @@ export function registerWorkflowFormCommands(program) {
|
|
|
1016
1051
|
process.exitCode = 1;
|
|
1017
1052
|
return;
|
|
1018
1053
|
}
|
|
1019
|
-
|
|
1054
|
+
// `--formula-id` 传已有 id 就是**原地更新**那张公式(不新建、返回的 id 不变)。
|
|
1055
|
+
// 名字缺省沿用原公式名 —— 否则会被改成「公式_<时间戳>」,看着像新建了一条。
|
|
1056
|
+
let formulaName = cmdOpts.name;
|
|
1057
|
+
if (!formulaName && cmdOpts.formulaId) {
|
|
1058
|
+
try {
|
|
1059
|
+
const d = await client.post(`${baseUrl}/api/excel/formula/detail`, { formulaId: cmdOpts.formulaId });
|
|
1060
|
+
formulaName = d?.resultJson?.data?.name || undefined;
|
|
1061
|
+
}
|
|
1062
|
+
catch { /* 读不到就退回自动生成 */ }
|
|
1063
|
+
}
|
|
1064
|
+
formulaName = formulaName || `公式_${Date.now()}`;
|
|
1020
1065
|
const savePayload = {
|
|
1021
1066
|
expressSql: cmdOpts.expression, name: formulaName, parameters: parsed.parameters,
|
|
1022
1067
|
returnType: cmdOpts.returnType, validateType: 'normal', referenceType: 'assign',
|
|
1023
1068
|
extendParam: JSON.stringify({ exceptIds: cmdOpts.target, createType: 'once' }),
|
|
1024
|
-
formulaId: null, dataSources: [], formulaType: 'normal', mainDataSource: [null],
|
|
1069
|
+
formulaId: cmdOpts.formulaId || null, dataSources: [], formulaType: 'normal', mainDataSource: [null],
|
|
1025
1070
|
tempVariableList: [], nameAuto: false, module: MODULE, multiFuncSepType: 1,
|
|
1026
1071
|
};
|
|
1027
1072
|
const saveRes = await client.post(`${baseUrl}/api/excel/formula/save`, savePayload);
|
|
1028
1073
|
if (!saveRes?.resultJson?.excute)
|
|
1029
1074
|
throw new Error(`公式保存失败: ${saveRes?.resultJson?.error || saveRes?.msg || '未知错误'}`);
|
|
1030
1075
|
const expressId = saveRes.resultJson.id;
|
|
1031
|
-
|
|
1076
|
+
// 说要更新就必须真的更新了 —— 平台万一悄悄新建一条,绑定会跟过去、旧公式留成孤儿。
|
|
1077
|
+
if (cmdOpts.formulaId && String(expressId) !== String(cmdOpts.formulaId)) {
|
|
1078
|
+
throw new Error(`期望原地更新 ${cmdOpts.formulaId},平台返回的是 ${expressId}`
|
|
1079
|
+
+ '(很可能新建了一条,旧公式已成为孤儿,请先在公式列表里核对)');
|
|
1080
|
+
}
|
|
1081
|
+
console.error(` ${cmdOpts.formulaId ? '公式已更新' : '公式已创建'}: ${expressId} (${saveRes.resultJson.formula})`);
|
|
1032
1082
|
// ffExpress 绑字段(saveFormFieldNew 重存带 ffExpress)
|
|
1033
1083
|
try {
|
|
1034
1084
|
const gf = await client.post(`${baseUrl}${ROUTE}/field/manage/getFormField`, { module: MODULE, formTableField: { formField: { id: cmdOpts.target, form: { id: formId } } } }, ebb);
|
|
@@ -1294,7 +1344,7 @@ export function registerWorkflowFormCommands(program) {
|
|
|
1294
1344
|
.argument('<formId>', '流程表单 formId')
|
|
1295
1345
|
.argument('<fieldId>', '字段 fieldId(被带出的目标字段,如 联系方式)')
|
|
1296
1346
|
.option('--title <title>', '修改字段标题')
|
|
1297
|
-
.option('--multiple', '设为多选(Employee/Department/Browser isSingle=false;Select→SelectMultiple)')
|
|
1347
|
+
.option('--multiple', '设为多选(Employee/Department/SubCompany/Browser isSingle=false;Select→SelectMultiple)')
|
|
1298
1348
|
.option('--single', '设为单选(反向)')
|
|
1299
1349
|
.option('--relate-from <关联字段>', 'EB关联带出:选 EBuilder 关联记录(如关联客户)后自动带出本字段值。传关联字段名/fieldId')
|
|
1300
1350
|
.option('--relate-from-source <源字段>', '目标表单的源字段(如客户信息表的联系方式)fieldId 或字段名,--relate-from 必填。传字段名需 --sform-id 解析')
|
|
@@ -1321,7 +1371,7 @@ export function registerWorkflowFormCommands(program) {
|
|
|
1321
1371
|
if (o.multiple === true || o.single === true) {
|
|
1322
1372
|
const multi = o.multiple === true;
|
|
1323
1373
|
const t = jsonObj.type;
|
|
1324
|
-
if (t === 'Employee' || t === 'Department' || t === 'RelateBrowser') {
|
|
1374
|
+
if (t === 'Employee' || t === 'Department' || t === 'SubCompany' || t === 'RelateBrowser') {
|
|
1325
1375
|
jsonObj.isSingle = !multi;
|
|
1326
1376
|
changes.push(`isSingle=${!multi}`);
|
|
1327
1377
|
}
|
|
@@ -1331,7 +1381,7 @@ export function registerWorkflowFormCommands(program) {
|
|
|
1331
1381
|
changes.push(`type=${jsonObj.type}`);
|
|
1332
1382
|
}
|
|
1333
1383
|
else {
|
|
1334
|
-
throw new Error(`字段类型 ${t} 不支持多选切换(仅 Employee/Department/浏览按钮/Select)`);
|
|
1384
|
+
throw new Error(`字段类型 ${t} 不支持多选切换(仅 Employee/Department/SubCompany/浏览按钮/Select)`);
|
|
1335
1385
|
}
|
|
1336
1386
|
}
|
|
1337
1387
|
// EB关联带出(ffRelateField):选关联记录后自动带出目标表字段值
|
|
@@ -2031,6 +2081,431 @@ export function registerWorkflowFormCommands(program) {
|
|
|
2031
2081
|
process.exitCode = 1;
|
|
2032
2082
|
}
|
|
2033
2083
|
});
|
|
2084
|
+
// ---------- wf-form formula-linkage(流程表单的函数联动)----------
|
|
2085
|
+
const flCmd = wfForm.command('formula-linkage')
|
|
2086
|
+
.description('函数联动(流程表单,linkageType=excel):list / read / events / columns / add / update / delete');
|
|
2087
|
+
/** 这条路由的公共取数:驱动事件 + 驱动列(字段来源也从驱动列来,见 `drivingColumnsAsFieldSource`)。 */
|
|
2088
|
+
async function wfLinkageContext(client, baseUrl, formId, workflowId) {
|
|
2089
|
+
const ebb = { ebbusinessid: formId };
|
|
2090
|
+
const loc = { formId, refId: workflowId || formId };
|
|
2091
|
+
const [ev, col] = await Promise.all([
|
|
2092
|
+
client.post(`${baseUrl}${buildLinkageApiUrl(FLOW_ROUTE, 'findFormDrivingEvents')}`, buildDrivingEventsBody(FLOW_ROUTE, loc), ebb),
|
|
2093
|
+
client.post(`${baseUrl}${buildLinkageApiUrl(FLOW_ROUTE, 'findFormDrivingColumns')}`, buildDrivingColumnsBody(FLOW_ROUTE, loc), ebb),
|
|
2094
|
+
]);
|
|
2095
|
+
if (ev?.code !== 200 || !ev?.status)
|
|
2096
|
+
throw new Error(`查询触发事件失败: ${ev?.msg || '未知错误'}`);
|
|
2097
|
+
if (col?.code !== 200 || !col?.status)
|
|
2098
|
+
throw new Error(`查询可引用字段失败: ${col?.msg || '未知错误'}`);
|
|
2099
|
+
const columns = (col.data?.drivingColumns || []);
|
|
2100
|
+
return { events: (ev.data || []), columns, source: drivingColumnsAsFieldSource(columns) };
|
|
2101
|
+
}
|
|
2102
|
+
/** 解析这条路由的生效维度(四选一)。 */
|
|
2103
|
+
async function wfEffectOf(client, baseUrl, formId, o) {
|
|
2104
|
+
return resolveWorkflowEffect(client, baseUrl, {
|
|
2105
|
+
module: MODULE, route: ROUTE, formId,
|
|
2106
|
+
dimension: o.effect, workflowId: o.workflowId, nodeKey: o.node, layoutId: o.layoutId,
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
/** 回读一条,读不到就报错。 */
|
|
2110
|
+
async function wfReadLinkage(client, baseUrl, formId, linkageId, workflowId) {
|
|
2111
|
+
const refId = workflowId || formId;
|
|
2112
|
+
const ebb = { ebbusinessid: formId };
|
|
2113
|
+
const res = await client.post(`${baseUrl}${buildLinkageApiUrl(FLOW_ROUTE, 'queryById')}`, buildLinkageQueryBody(FLOW_ROUTE, { id: linkageId, formId, refId }), ebb);
|
|
2114
|
+
if (res?.code !== 200 || !res?.status)
|
|
2115
|
+
throw new Error(`查询失败: ${res?.msg || '未知错误'}`);
|
|
2116
|
+
if (String(res?.data?.linkageType) !== 'excel') {
|
|
2117
|
+
throw new Error(`这条的 linkageType 是 ${res?.data?.linkageType},不是函数联动`);
|
|
2118
|
+
}
|
|
2119
|
+
return res.data;
|
|
2120
|
+
}
|
|
2121
|
+
flCmd.command('list')
|
|
2122
|
+
.description('列出该流程表单下的函数联动')
|
|
2123
|
+
.argument('<formId>', '流程表单 formId')
|
|
2124
|
+
.option('--workflow-id <id>', '流程 ID(refId;不给按 formId 兜底)')
|
|
2125
|
+
.option('--keyword <text>', '按联动名过滤')
|
|
2126
|
+
.action(async (formId, cmdOpts) => {
|
|
2127
|
+
try {
|
|
2128
|
+
const opts = program.opts();
|
|
2129
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
2130
|
+
if (!auth)
|
|
2131
|
+
throw new Error('无法加载认证');
|
|
2132
|
+
const client = new ApiClient(auth);
|
|
2133
|
+
const baseUrl = auth.getBaseUrl();
|
|
2134
|
+
const refId = cmdOpts.workflowId || formId;
|
|
2135
|
+
const res = await client.post(`${baseUrl}${buildLinkageApiUrl(FLOW_ROUTE, 'fullPageQueryByFormId')}`, buildLinkageListBody(FLOW_ROUTE, { formId, refId, keyword: cmdOpts.keyword }), { ebbusinessid: formId });
|
|
2136
|
+
if (res?.code !== 200 || !res?.status)
|
|
2137
|
+
throw new Error(`查询失败: ${res?.msg || '未知错误'}`);
|
|
2138
|
+
const total = Number(res?.data?.total ?? 0);
|
|
2139
|
+
const records = res?.data?.records || [];
|
|
2140
|
+
if (opts.json) {
|
|
2141
|
+
console.log(JSON.stringify(records, null, 2));
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
console.log(`共 ${total} 条函数联动:`);
|
|
2145
|
+
for (const r of records) {
|
|
2146
|
+
const s = summarizeLinkage(r);
|
|
2147
|
+
console.log(` [${s.id}] ${s.name} (${s.state ? '启用' : '停用'})`);
|
|
2148
|
+
console.log(` 生效范围: ${wfEffectLabel(s.effect)}`);
|
|
2149
|
+
console.log(` 触发: ${s.eventNames.join('、') || '—'} ← ${s.triggerNames.join('、') || '—'}`);
|
|
2150
|
+
}
|
|
2151
|
+
if (records.length)
|
|
2152
|
+
console.log(' (列表不返回公式正文,要表达式/参数用 wf-form formula-linkage read)');
|
|
2153
|
+
if (total === 0)
|
|
2154
|
+
console.log(` ℹ 查不到时先确认 --workflow-id:不传是按 formId=${formId} 当 refId 查的`);
|
|
2155
|
+
}
|
|
2156
|
+
catch (e) {
|
|
2157
|
+
console.error(`查询函数联动失败: ${e.message}`);
|
|
2158
|
+
process.exitCode = 1;
|
|
2159
|
+
}
|
|
2160
|
+
});
|
|
2161
|
+
flCmd.command('read')
|
|
2162
|
+
.description('查看一条函数联动(含公式表达式与参数)')
|
|
2163
|
+
.argument('<formId>', '流程表单 formId')
|
|
2164
|
+
.argument('<linkageId>', '联动 ID')
|
|
2165
|
+
.option('--workflow-id <id>', '流程 ID(refId;不给按 formId 兜底)')
|
|
2166
|
+
.action(async (formId, linkageId, cmdOpts) => {
|
|
2167
|
+
try {
|
|
2168
|
+
const opts = program.opts();
|
|
2169
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
2170
|
+
if (!auth)
|
|
2171
|
+
throw new Error('无法加载认证');
|
|
2172
|
+
const client = new ApiClient(auth);
|
|
2173
|
+
const baseUrl = auth.getBaseUrl();
|
|
2174
|
+
const data = await wfReadLinkage(client, baseUrl, formId, linkageId, cmdOpts.workflowId);
|
|
2175
|
+
if (opts.json) {
|
|
2176
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
const s = summarizeLinkage(data);
|
|
2180
|
+
console.log(`[${s.id}] ${s.name} (${s.state ? '启用' : '停用'})`);
|
|
2181
|
+
console.log(` refId: ${data?.refId || '—'}`);
|
|
2182
|
+
console.log(` 生效范围: ${wfEffectLabel(s.effect)}`);
|
|
2183
|
+
console.log(` 触发: ${s.eventNames.join('、') || '—'} ← ${s.triggerNames.join('、') || '—'}`);
|
|
2184
|
+
console.log(` 公式: ${s.excelName} (excelId=${s.excelId})`);
|
|
2185
|
+
console.log(` 表达式: ${s.expression || '-'}`);
|
|
2186
|
+
if (s.parameters.length) {
|
|
2187
|
+
console.log(` 参数 (${s.parameters.length}):`);
|
|
2188
|
+
for (const p of s.parameters)
|
|
2189
|
+
console.log(` ${p.fieldId} ${p.title || p.name || ''} [${p.properKey || ''}]`);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
catch (e) {
|
|
2193
|
+
console.error(`查询函数联动失败: ${e.message}`);
|
|
2194
|
+
process.exitCode = 1;
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
flCmd.command('events')
|
|
2198
|
+
.description('列出可用的触发事件(供 --event 取值)')
|
|
2199
|
+
.argument('<formId>', '流程表单 formId')
|
|
2200
|
+
.option('--workflow-id <id>', '流程 ID(refId;不给按 formId 兜底)')
|
|
2201
|
+
.action(async (formId, cmdOpts) => {
|
|
2202
|
+
try {
|
|
2203
|
+
const opts = program.opts();
|
|
2204
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
2205
|
+
if (!auth)
|
|
2206
|
+
throw new Error('无法加载认证');
|
|
2207
|
+
const client = new ApiClient(auth);
|
|
2208
|
+
const baseUrl = auth.getBaseUrl();
|
|
2209
|
+
const { events } = await wfLinkageContext(client, baseUrl, formId, cmdOpts.workflowId);
|
|
2210
|
+
if (opts.json) {
|
|
2211
|
+
console.log(JSON.stringify(events, null, 2));
|
|
2212
|
+
return;
|
|
2213
|
+
}
|
|
2214
|
+
console.log(`共 ${events.length} 个触发事件:`);
|
|
2215
|
+
for (const e of events) {
|
|
2216
|
+
console.log(` ${e.groupName} / ${e.name} ${e.type} ${e.selectColumn ? '(需 --trigger 指定字段)' : '(表单级,无需 --trigger)'}`);
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
catch (e) {
|
|
2220
|
+
console.error(`查询触发事件失败: ${e.message}`);
|
|
2221
|
+
process.exitCode = 1;
|
|
2222
|
+
}
|
|
2223
|
+
});
|
|
2224
|
+
flCmd.command('columns')
|
|
2225
|
+
.description('列出可用于公式引用与触发的字段')
|
|
2226
|
+
.argument('<formId>', '流程表单 formId')
|
|
2227
|
+
.option('--workflow-id <id>', '流程 ID(refId;不给按 formId 兜底)')
|
|
2228
|
+
.option('--keyword <text>', '按字段名过滤')
|
|
2229
|
+
.action(async (formId, cmdOpts) => {
|
|
2230
|
+
try {
|
|
2231
|
+
const opts = program.opts();
|
|
2232
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
2233
|
+
if (!auth)
|
|
2234
|
+
throw new Error('无法加载认证');
|
|
2235
|
+
const client = new ApiClient(auth);
|
|
2236
|
+
const baseUrl = auth.getBaseUrl();
|
|
2237
|
+
const { columns } = await wfLinkageContext(client, baseUrl, formId, cmdOpts.workflowId);
|
|
2238
|
+
if (opts.json) {
|
|
2239
|
+
console.log(JSON.stringify(columns, null, 2));
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
const kw = cmdOpts.keyword;
|
|
2243
|
+
const shown = kw ? columns.filter((c) => String(c.name).includes(kw) || String(c.dataKey || '').includes(kw)) : columns;
|
|
2244
|
+
const { assignable, unassignable } = partitionDrivingColumns(shown, FLOW_ROUTE);
|
|
2245
|
+
const drifted = partitionDrivingColumns(columns, FLOW_ROUTE).drifted;
|
|
2246
|
+
console.log(`可作赋值目标 (${assignable.length}) —— 也能作引用与触发:`);
|
|
2247
|
+
for (const c of assignable)
|
|
2248
|
+
console.log(describeColumn(c, FLOW_ROUTE));
|
|
2249
|
+
console.log(`\n不可作赋值目标 (${unassignable.length}) —— 仍可作引用与触发:`);
|
|
2250
|
+
for (const c of unassignable)
|
|
2251
|
+
console.log(describeColumn(c, FLOW_ROUTE));
|
|
2252
|
+
if (drifted.length) {
|
|
2253
|
+
console.log(`\n⚠ ${drifted.length} 列形似系统字段但不在本路由的不可赋值清单里,已按「可赋值」处理:`);
|
|
2254
|
+
for (const c of drifted)
|
|
2255
|
+
console.log(` ${targetDriftWarning(c, FLOW_ROUTE)}`);
|
|
2256
|
+
}
|
|
2257
|
+
console.log(`\n共 ${columns.length} 列(其中 ${columns.filter((c) => isAssignableTarget(c, FLOW_ROUTE)).length} 列可作赋值目标)`);
|
|
2258
|
+
}
|
|
2259
|
+
catch (e) {
|
|
2260
|
+
console.error(`查询可引用字段失败: ${e.message}`);
|
|
2261
|
+
process.exitCode = 1;
|
|
2262
|
+
}
|
|
2263
|
+
});
|
|
2264
|
+
flCmd.command('add')
|
|
2265
|
+
.description('新增函数联动:事件 + 触发字段 + 赋值目标 + 公式')
|
|
2266
|
+
.argument('<formId>', '流程表单 formId')
|
|
2267
|
+
.requiredOption('--target <field>', '赋值目标字段(被写入)')
|
|
2268
|
+
.requiredOption('--expression <expr>', '公式右侧函数体;也接受整段赋值式,或用 @文件 传长表达式')
|
|
2269
|
+
.option('--event <event>', '触发事件(名称或 type,见 events),默认 changeField 值改变时', 'changeField')
|
|
2270
|
+
.option('--trigger <fields...>', '触发字段,可多个')
|
|
2271
|
+
.option('--effect <dimension>', '生效维度: form=当前表单(默认)/ formLayout=表单布局 / workFlow=当前工作流 / includeNode=选择节点')
|
|
2272
|
+
.option('--workflow-id <id>', '流程 ID(refId,也是 workFlow/includeNode 的取值来源;不给按 formId 兜底)')
|
|
2273
|
+
.option('--node <node>', 'includeNode 的节点(id 或节点名)')
|
|
2274
|
+
.option('--layout-id <id>', 'formLayout 指定的布局 id(不传取默认布局)')
|
|
2275
|
+
.option('--name <name>', '联动名称(默认「<公式名> - 公式_<时间戳>」)')
|
|
2276
|
+
.option('--formula-name <name>', '公式名称')
|
|
2277
|
+
.option('--formula-id <id>', '复用/更新已有公式(传了就原地更新那张公式)')
|
|
2278
|
+
.action(async (formId, cmdOpts) => {
|
|
2279
|
+
try {
|
|
2280
|
+
const opts = program.opts();
|
|
2281
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
2282
|
+
if (!auth)
|
|
2283
|
+
throw new Error('无法加载认证');
|
|
2284
|
+
const client = new ApiClient(auth);
|
|
2285
|
+
const baseUrl = auth.getBaseUrl();
|
|
2286
|
+
const ebb = { ebbusinessid: formId };
|
|
2287
|
+
const refId = cmdOpts.workflowId || formId;
|
|
2288
|
+
const { events, columns, source } = await wfLinkageContext(client, baseUrl, formId, cmdOpts.workflowId);
|
|
2289
|
+
const event = resolveDrivingEvent(events, cmdOpts.event);
|
|
2290
|
+
const targetCol = resolveDrivingColumn(columns, cmdOpts.target, FLOW_ROUTE);
|
|
2291
|
+
assertAssignableTarget(targetCol, FLOW_ROUTE);
|
|
2292
|
+
const drift = targetDriftWarning(targetCol, FLOW_ROUTE);
|
|
2293
|
+
if (drift)
|
|
2294
|
+
console.error(` ⚠ ${drift}`);
|
|
2295
|
+
const triggerCols = (cmdOpts.trigger || []).map((t) => resolveDrivingColumn(columns, t, FLOW_ROUTE));
|
|
2296
|
+
if (!event.selectColumn && triggerCols.length) {
|
|
2297
|
+
console.error(` ⚠ 事件「${event.name}」是表单级的,不需要触发字段,--trigger 已忽略`);
|
|
2298
|
+
}
|
|
2299
|
+
const monitorActions = buildMonitorActions(event, triggerCols);
|
|
2300
|
+
const { effect } = await wfEffectOf(client, baseUrl, formId, cmdOpts);
|
|
2301
|
+
const { expression, stripped } = buildAssignExpression(targetCol, readExpressionArg(cmdOpts.expression));
|
|
2302
|
+
if (stripped)
|
|
2303
|
+
console.error(' ℹ 表达式自带赋值式,已按 --target 校验左侧并沿用右侧');
|
|
2304
|
+
console.error(` 联动: ${effect.effectName} / ${event.name} ← ${triggerCols.map((c) => toBareLabel(c)).join('、') || '(表单级)'} → 填 ${toBareLabel(targetCol)}(${targetCol.id})`);
|
|
2305
|
+
// 这条路由的 parameter **不带 appId 键**,所以 ctx 里不给 appId
|
|
2306
|
+
const parsed = resolveFormulaRefs(expression, {
|
|
2307
|
+
mainFields: source.mainFields, detailTables: source.detailTables,
|
|
2308
|
+
moduleColumns: moduleColumnsOf(columns, FLOW_ROUTE),
|
|
2309
|
+
formId, module: FLOW_ROUTE.module, hint: FLOW_ROUTE.columnsHint,
|
|
2310
|
+
});
|
|
2311
|
+
for (const w of parsed.warnings)
|
|
2312
|
+
console.error(` ⚠ ${w}`);
|
|
2313
|
+
if (parsed.errors.length) {
|
|
2314
|
+
for (const e of parsed.errors)
|
|
2315
|
+
console.error(` ❌ ${e}`);
|
|
2316
|
+
process.exitCode = 1;
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
if (parsed.parameters[0]?.fieldId !== String(targetCol.id)) {
|
|
2320
|
+
throw new Error(`赋值目标没有出现在公式参数表首位(首位是 ${parsed.parameters[0]?.fieldId ?? '空'}),已中止`);
|
|
2321
|
+
}
|
|
2322
|
+
const formulaName = cmdOpts.formulaName || '函数联动公式';
|
|
2323
|
+
const saveRes = await client.post(`${baseUrl}/api/excel/formula/save`, buildFormulaSavePayload({
|
|
2324
|
+
expression: parsed.normalizedExpression, name: formulaName,
|
|
2325
|
+
parameters: parsed.parameters, formulaId: cmdOpts.formulaId || null, route: FLOW_ROUTE,
|
|
2326
|
+
}));
|
|
2327
|
+
if (!saveRes?.resultJson?.excute) {
|
|
2328
|
+
throw new Error(`公式保存失败: ${saveRes?.resultJson?.error || saveRes?.msg || '未知错误'}`);
|
|
2329
|
+
}
|
|
2330
|
+
const excelId = String(saveRes.resultJson.id || '');
|
|
2331
|
+
console.error(` 公式: ${excelId} ${formulaName}`);
|
|
2332
|
+
const linkageName = cmdOpts.name || buildFormulaLinkageName(formulaName, new Date());
|
|
2333
|
+
const linkRes = await client.post(`${baseUrl}${buildLinkageApiUrl(FLOW_ROUTE, 'saveFormDataLinkage')}`, buildLinkageSavePayload({
|
|
2334
|
+
formId, appId: '', objId: '', linkageName, excelId, excelName: formulaName,
|
|
2335
|
+
monitorActions, route: FLOW_ROUTE, refId,
|
|
2336
|
+
}), ebb);
|
|
2337
|
+
if (linkRes?.code !== 200 || !linkRes?.status) {
|
|
2338
|
+
throw new Error(`联动保存失败: ${linkRes?.msg || '未知错误'}(公式已创建 excelId=${excelId},可用 --formula-id ${excelId} 复用重试)`);
|
|
2339
|
+
}
|
|
2340
|
+
const linkageId = String(linkRes?.data?.id || '');
|
|
2341
|
+
console.error(` 联动已创建: ${linkageId}`);
|
|
2342
|
+
// preCheck 不通过不中止 —— 联动已经落库了,中止只会让人以为没建成
|
|
2343
|
+
const pre = await client.post(`${baseUrl}${buildLinkageApiUrl(FLOW_ROUTE, 'preCheck')}`, buildPreCheckBody(FLOW_ROUTE, { formId, linkageIds: [linkageId], refId }), ebb);
|
|
2344
|
+
if (pre?.code !== 200 || !pre?.status) {
|
|
2345
|
+
console.error(` ⚠ 平台校验未通过: ${pre?.msg || '未知错误'}(联动已落库,请在设计器里复核)`);
|
|
2346
|
+
}
|
|
2347
|
+
const rb = await client.post(`${baseUrl}${buildLinkageApiUrl(FLOW_ROUTE, 'queryById')}`, buildLinkageQueryBody(FLOW_ROUTE, { id: linkageId, formId, refId }), ebb);
|
|
2348
|
+
const s = summarizeLinkage(rb?.data);
|
|
2349
|
+
if (s.expression && s.expression !== parsed.normalizedExpression) {
|
|
2350
|
+
console.error(` ⚠ 平台回读的表达式与提交不一致,以平台为准: ${s.expression}`);
|
|
2351
|
+
}
|
|
2352
|
+
const out = {
|
|
2353
|
+
success: true, linkageId, excelId, formId, refId, effect: effect.effectName,
|
|
2354
|
+
expression: s.expression || parsed.normalizedExpression,
|
|
2355
|
+
target: { id: targetCol.id, name: toBareLabel(targetCol) },
|
|
2356
|
+
event: event.type, triggers: triggerCols.map((c) => ({ id: c.id, name: toBareLabel(c) })),
|
|
2357
|
+
};
|
|
2358
|
+
console.log(opts.json ? JSON.stringify(out, null, 2)
|
|
2359
|
+
: `函数联动已创建: [${linkageId}] ${effect.effectName} / ${event.name} ← ${triggerCols.map((c) => toBareLabel(c)).join('、') || '(表单级)'} → ${toBareLabel(targetCol)}`);
|
|
2360
|
+
}
|
|
2361
|
+
catch (e) {
|
|
2362
|
+
console.error(`新增函数联动失败: ${e.message}`);
|
|
2363
|
+
process.exitCode = 1;
|
|
2364
|
+
}
|
|
2365
|
+
});
|
|
2366
|
+
flCmd.command('update')
|
|
2367
|
+
.description('改已有函数联动的触发条件/生效维度/名称/启停(不传的项沿用现有)')
|
|
2368
|
+
.argument('<formId>', '流程表单 formId')
|
|
2369
|
+
.argument('<linkageId>', '联动 ID')
|
|
2370
|
+
.option('--event <event>', '改触发事件')
|
|
2371
|
+
.option('--trigger <fields...>', '改触发字段')
|
|
2372
|
+
.option('--effect <dimension>', '改生效维度: form / formLayout / workFlow / includeNode')
|
|
2373
|
+
.option('--workflow-id <id>', '流程 ID(refId;不给沿用记录里的)')
|
|
2374
|
+
.option('--node <node>', 'includeNode 的节点')
|
|
2375
|
+
.option('--layout-id <id>', 'formLayout 指定的布局')
|
|
2376
|
+
.option('--name <name>', '改联动名称')
|
|
2377
|
+
.option('--state <onoff>', '启用或停用: on / off')
|
|
2378
|
+
.action(async (formId, linkageId, cmdOpts) => {
|
|
2379
|
+
try {
|
|
2380
|
+
const opts = program.opts();
|
|
2381
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
2382
|
+
if (!auth)
|
|
2383
|
+
throw new Error('无法加载认证');
|
|
2384
|
+
const client = new ApiClient(auth);
|
|
2385
|
+
const baseUrl = auth.getBaseUrl();
|
|
2386
|
+
const ebb = { ebbusinessid: formId };
|
|
2387
|
+
const record = await wfReadLinkage(client, baseUrl, formId, linkageId, cmdOpts.workflowId);
|
|
2388
|
+
const refId = String(record?.refId || cmdOpts.workflowId || formId);
|
|
2389
|
+
const cur = extractTriggerState(record);
|
|
2390
|
+
const { events, columns } = await wfLinkageContext(client, baseUrl, formId, cmdOpts.workflowId);
|
|
2391
|
+
const event = resolveDrivingEvent(events, cmdOpts.event || cur.eventType);
|
|
2392
|
+
const triggerInput = cmdOpts.trigger?.length ? cmdOpts.trigger : cur.triggerIds;
|
|
2393
|
+
const triggerCols = [];
|
|
2394
|
+
for (const t of triggerInput) {
|
|
2395
|
+
const col = findColumnById(columns, t) || (() => { try {
|
|
2396
|
+
return resolveDrivingColumn(columns, t, FLOW_ROUTE);
|
|
2397
|
+
}
|
|
2398
|
+
catch {
|
|
2399
|
+
return undefined;
|
|
2400
|
+
} })();
|
|
2401
|
+
if (!col)
|
|
2402
|
+
throw new Error(`触发字段「${t}」在当前表单里找不到了(可能已被删除)。用 wf-form formula-linkage columns 复核`);
|
|
2403
|
+
triggerCols.push(col);
|
|
2404
|
+
}
|
|
2405
|
+
const monitorActions = buildMonitorActions(event, triggerCols);
|
|
2406
|
+
const effect = cmdOpts.effect
|
|
2407
|
+
? (await wfEffectOf(client, baseUrl, formId, cmdOpts)).effect
|
|
2408
|
+
: extractEffect(record);
|
|
2409
|
+
let linkageState = record?.linkageState !== false;
|
|
2410
|
+
if (cmdOpts.state) {
|
|
2411
|
+
const v = String(cmdOpts.state).toLowerCase();
|
|
2412
|
+
if (v !== 'on' && v !== 'off')
|
|
2413
|
+
throw new Error('--state 只接受 on / off');
|
|
2414
|
+
linkageState = v === 'on';
|
|
2415
|
+
}
|
|
2416
|
+
const res = await client.post(`${baseUrl}${buildLinkageApiUrl(FLOW_ROUTE, 'updateFormDataLinkage')}`, buildLinkageUpdatePayload(record, {
|
|
2417
|
+
appId: '', objId: '',
|
|
2418
|
+
linkageName: cmdOpts.name || String(record?.linkageName || ''),
|
|
2419
|
+
linkageState, monitorActions, effect, route: FLOW_ROUTE, refId,
|
|
2420
|
+
}), ebb);
|
|
2421
|
+
if (res?.code !== 200 || !res?.status)
|
|
2422
|
+
throw new Error(`更新失败: ${res?.msg || '未知错误'}`);
|
|
2423
|
+
await client.post(`${baseUrl}${buildLinkageApiUrl(FLOW_ROUTE, 'preCheck')}`, buildPreCheckBody(FLOW_ROUTE, { formId, linkageIds: [linkageId], refId }), ebb);
|
|
2424
|
+
const after = summarizeLinkage(await wfReadLinkage(client, baseUrl, formId, linkageId, refId));
|
|
2425
|
+
const out = { success: true, linkageId, name: after.name, state: after.state, effect: wfEffectLabel(after.effect), event: after.eventNames.join('、'), triggers: after.triggerNames };
|
|
2426
|
+
console.log(opts.json ? JSON.stringify(out, null, 2)
|
|
2427
|
+
: `函数联动已更新: [${linkageId}] ${after.name} (${after.state ? '启用' : '停用'}) ${wfEffectLabel(after.effect)} / ${after.eventNames.join('、')} ← ${after.triggerNames.join('、')}`);
|
|
2428
|
+
}
|
|
2429
|
+
catch (e) {
|
|
2430
|
+
console.error(`更新函数联动失败: ${e.message}`);
|
|
2431
|
+
process.exitCode = 1;
|
|
2432
|
+
}
|
|
2433
|
+
});
|
|
2434
|
+
flCmd.command('delete')
|
|
2435
|
+
.description('删除函数联动(支持批量)')
|
|
2436
|
+
.argument('<formId>', '流程表单 formId')
|
|
2437
|
+
.argument('<linkageIds...>', '联动 ID')
|
|
2438
|
+
.option('--workflow-id <id>', '流程 ID(refId;不给按 formId 兜底)')
|
|
2439
|
+
.action(async (formId, linkageIds, cmdOpts) => {
|
|
2440
|
+
try {
|
|
2441
|
+
const opts = program.opts();
|
|
2442
|
+
const auth = await AuthSession.fromOptions(opts);
|
|
2443
|
+
if (!auth)
|
|
2444
|
+
throw new Error('无法加载认证');
|
|
2445
|
+
const client = new ApiClient(auth);
|
|
2446
|
+
const baseUrl = auth.getBaseUrl();
|
|
2447
|
+
const refId = cmdOpts.workflowId || formId;
|
|
2448
|
+
const res = await client.post(`${baseUrl}${buildLinkageApiUrl(FLOW_ROUTE, 'deleteFormDataLinkage')}`, buildLinkageDeleteBody(FLOW_ROUTE, { ids: linkageIds, formId, refId }), { ebbusinessid: formId });
|
|
2449
|
+
if (res?.code !== 200 || !res?.status)
|
|
2450
|
+
throw new Error(`删除失败: ${res?.msg || '未知错误'}`);
|
|
2451
|
+
const deleted = res?.data || [];
|
|
2452
|
+
if (!deleted.length)
|
|
2453
|
+
throw new Error('没有删掉任何一条(回显 0 条)。先 list 核对 id 是否存在、是否属于本表单');
|
|
2454
|
+
// 服务端回显的 id 与传入的 linkageId 不是同一套(与 eb 表那条路由一样),
|
|
2455
|
+
// 所以打印**用户传的 id**,回显只在数量对不上时提一句
|
|
2456
|
+
const mismatch = deleted.length !== linkageIds.length;
|
|
2457
|
+
console.log(opts.json ? JSON.stringify({ success: true, deleted: linkageIds, serverEcho: deleted }, null, 2)
|
|
2458
|
+
: `已删除 ${linkageIds.length} 条函数联动: ${linkageIds.join(', ')}`);
|
|
2459
|
+
if (mismatch)
|
|
2460
|
+
console.error(` ⚠ 服务端回显 ${deleted.length} 条(${deleted.join(', ')}),与请求的 ${linkageIds.length} 条对不上,请用 list 复核`);
|
|
2461
|
+
}
|
|
2462
|
+
catch (e) {
|
|
2463
|
+
console.error(`删除函数联动失败: ${e.message}`);
|
|
2464
|
+
process.exitCode = 1;
|
|
2465
|
+
}
|
|
2466
|
+
});
|
|
2034
2467
|
return wfForm;
|
|
2035
2468
|
}
|
|
2469
|
+
/** `--expression @file` 支持:公式里常带引号与换行,走文件比在命令行里转义省事。 */
|
|
2470
|
+
function readExpressionArg(raw) {
|
|
2471
|
+
const v = String(raw || '');
|
|
2472
|
+
if (!v.startsWith('@'))
|
|
2473
|
+
return v;
|
|
2474
|
+
try {
|
|
2475
|
+
return readFileSync(v.slice(1), 'utf-8').trim();
|
|
2476
|
+
}
|
|
2477
|
+
catch (e) {
|
|
2478
|
+
throw new Error(`读取表达式文件失败: ${v.slice(1)}(${e?.message || e})`);
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
/** 生效维度的可读写法:当前表单 / 表单布局(布局名)/ 当前工作流(流程名【V1】)/ 选择节点(节点名)。 */
|
|
2482
|
+
function wfEffectLabel(e) {
|
|
2483
|
+
if (!e || e.effectType === 'form')
|
|
2484
|
+
return '当前表单';
|
|
2485
|
+
return e.optionName ? `${e.effectName}(${e.optionName})` : String(e.effectName || e.effectType);
|
|
2486
|
+
}
|
|
2487
|
+
/**
|
|
2488
|
+
* 把驱动列映射成 `resolveFormulaRefs` 认的字段来源(形状对齐 eb 表 `getEbField` 的 main/detail)。
|
|
2489
|
+
*
|
|
2490
|
+
* **故意不走 `getConfig`**:那条路要先拿 `getFormLayout` 给的 layoutId,而流程表单这个接口
|
|
2491
|
+
* 对部分表单直接 500(`Form.isPhysicalTable()` 上 NPE,`form` 为 null),连既有的
|
|
2492
|
+
* `wf-form read` 都读不出来;退一步用空 layoutId 调 `getConfig`,回的是 9 个系统字段、**零业务字段**。
|
|
2493
|
+
* 而驱动列本来就是这条路由自己给的字段清单 —— 业务字段与模块字段都在里面,id/名称/类型齐备,够用。
|
|
2494
|
+
*/
|
|
2495
|
+
function drivingColumnsAsFieldSource(columns) {
|
|
2496
|
+
const business = columns.filter((c) => String(c.groupId) !== 'moduleField');
|
|
2497
|
+
const strayDetail = business.find((c) => c.address && String(c.address) !== '主表');
|
|
2498
|
+
if (strayDetail) {
|
|
2499
|
+
// 没有实测过流程表单的明细字段在驱动列里长什么样(连 detailTableId 都拿不到),
|
|
2500
|
+
// 与其猜一个 sFormId 发出去,不如停下来
|
|
2501
|
+
throw new Error(`驱动列里出现了非主表字段「${strayDetail.name}」(address=${strayDetail.address})。`
|
|
2502
|
+
+ '流程表单的明细字段尚未支持,请反馈这条表单的 findFormDrivingColumns 返回');
|
|
2503
|
+
}
|
|
2504
|
+
return {
|
|
2505
|
+
mainFields: business.map((c) => ({
|
|
2506
|
+
id: c.id, showName: toBareLabel(c), dataKey: c.dataKey, componentKey: c.descType,
|
|
2507
|
+
})),
|
|
2508
|
+
detailTables: [],
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2036
2511
|
//# sourceMappingURL=wf-form.js.map
|