@steedos-labs/plugin-workflow 3.0.85 → 3.0.86

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.
@@ -5,7 +5,7 @@
5
5
  <link rel="shortcut icon" type="image/svg+xml" href="/images/logo.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>designer</title>
8
- <script type="module" crossorigin src="/api/workflow/designer-v2/assets/index-DpFVuLlI.js"></script>
8
+ <script type="module" crossorigin src="/api/workflow/designer-v2/assets/index-CUTUedfe.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/api/workflow/designer-v2/assets/index-B56yvQDB.css">
10
10
  </head>
11
11
  <body>
@@ -1,5 +1,5 @@
1
1
  waitForThing(window, 'antd').then(function(){
2
- var v = '3.0.85';
2
+ var v = '3.0.86';
3
3
  loadJs('/amis-renderer/amis-renderer.js?v=' + v);
4
4
  loadCss('/amis-renderer/amis-renderer.css?v=' + v)
5
5
  })
@@ -8,8 +8,245 @@
8
8
  var _eval;
9
9
 
10
10
  _eval = require('eval');
11
- const { getCollection } = require('../utils/collection');
11
+ const { getCollection, _makeNewID } = require('../utils/collection');
12
12
  const _ = require('lodash');
13
+
14
+ const escapeRegExp = (str) => String(str || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
15
+
16
+ const getFieldKey = (field) => field?.code || field?.name;
17
+
18
+ const isAutoNumberTypeField = (field) => {
19
+ const fieldType = (field?.type || '').toString().toLowerCase();
20
+ return fieldType === 'autonumber' || field?.type === 'autoNumber';
21
+ };
22
+
23
+ const extractAutoNumberName = (formula) => {
24
+ if (typeof formula !== 'string') return null;
25
+ const match = formula.match(/auto_number\s*\(\s*['"]?([^'"()]+?)['"]?\s*\)/);
26
+ return match ? match[1].trim() : null;
27
+ };
28
+
29
+ const getAutoNumberRuleName = (field, options = {}) => {
30
+ if (field?.default_value && typeof field.default_value === 'string') {
31
+ const byFormula = extractAutoNumberName(field.default_value);
32
+ if (byFormula) return byFormula;
33
+ }
34
+
35
+ const fromProps =
36
+ field?.auto_number_name ||
37
+ field?.autoNumberName ||
38
+ field?.auto_number_rule ||
39
+ field?.autoNumberRule ||
40
+ field?.auto_number_rule_name ||
41
+ field?.autoNumberRuleName;
42
+ if (fromProps && typeof fromProps === 'string') {
43
+ return fromProps.trim();
44
+ }
45
+
46
+ const formName = options.formName || options.formId || 'form';
47
+ const fieldKey = getFieldKey(field);
48
+ const fieldLabel = field?.label || fieldKey;
49
+ return `${formName} / ${fieldLabel} (${fieldKey})`;
50
+ };
51
+
52
+ const getAutoNumberRuleId = (spaceId, field, options = {}) => {
53
+ const fromProps =
54
+ field?.auto_number_id ||
55
+ field?.autoNumberId ||
56
+ field?.auto_number_rule_id ||
57
+ field?.autoNumberRuleId;
58
+ if (fromProps && typeof fromProps === 'string') {
59
+ return fromProps.trim();
60
+ }
61
+ };
62
+
63
+ const padNumber = (num, length) => {
64
+ if (length <= 0) return String(num);
65
+ const str = String(num);
66
+ return str.length >= length ? str : '0'.repeat(length - str.length) + str;
67
+ };
68
+
69
+ const formatDatePart = (dateFormat, yyyy, mm, dd) => {
70
+ const mmStr = mm < 10 ? '0' + mm : String(mm);
71
+ const ddStr = dd < 10 ? '0' + dd : String(dd);
72
+ switch (dateFormat) {
73
+ case 'YYYY': return String(yyyy);
74
+ case 'YYYYMM': return String(yyyy) + mmStr;
75
+ case 'YYYYMMDD': return String(yyyy) + mmStr + ddStr;
76
+ default: return '';
77
+ }
78
+ };
79
+
80
+ const shouldResetNumber = (resetCycle, storedRule, yyyy, mm, dd) => {
81
+ switch (resetCycle) {
82
+ case 'yearly': return yyyy !== storedRule.year;
83
+ case 'monthly': return yyyy !== storedRule.year || mm !== storedRule.month;
84
+ case 'daily': return yyyy !== storedRule.year || mm !== storedRule.month || dd !== storedRule.day;
85
+ default: return false;
86
+ }
87
+ };
88
+
89
+ const buildAutoNumberValue = (fieldConfig, number, date = new Date()) => {
90
+ const prefix = fieldConfig.autoNumberPrefix || '';
91
+ const suffix = fieldConfig.autoNumberSuffix || '';
92
+ const paddingLen = fieldConfig.autoNumberPadding != null ? fieldConfig.autoNumberPadding : 4;
93
+ const dateFormat = fieldConfig.autoNumberDateFormat || 'YYYYMM';
94
+ const yyyy = date.getFullYear();
95
+ const mm = date.getMonth() + 1;
96
+ const dd = date.getDate();
97
+ const datePart = formatDatePart(dateFormat, yyyy, mm, dd);
98
+ const paddedNumber = padNumber(number, paddingLen);
99
+
100
+ return datePart ? `${prefix}${datePart}-${paddedNumber}${suffix}` : `${prefix}${paddedNumber}${suffix}`;
101
+ };
102
+
103
+ const isAutoNumberPreviewValue = (value, fieldConfig) => {
104
+ if (typeof value !== 'string' || !value) return false;
105
+
106
+ const prefix = escapeRegExp(fieldConfig.autoNumberPrefix || '');
107
+ const suffix = escapeRegExp(fieldConfig.autoNumberSuffix || '');
108
+ const paddingLen = fieldConfig.autoNumberPadding != null ? fieldConfig.autoNumberPadding : 4;
109
+ const firstSeq = escapeRegExp(padNumber(1, paddingLen));
110
+ const dateFormat = fieldConfig.autoNumberDateFormat || 'YYYYMM';
111
+ let datePattern = '';
112
+
113
+ if (dateFormat === 'YYYY') datePattern = '\\d{4}';
114
+ if (dateFormat === 'YYYYMM') datePattern = '\\d{6}';
115
+ if (dateFormat === 'YYYYMMDD') datePattern = '\\d{8}';
116
+
117
+ const pattern = datePattern
118
+ ? `^${prefix}${datePattern}-${firstSeq}${suffix}$`
119
+ : `^${prefix}${firstSeq}${suffix}$`;
120
+
121
+ return new RegExp(pattern).test(value);
122
+ };
123
+
124
+ const isAutoNumberField = (field) => {
125
+ if (!field) return false;
126
+ const hasLegacyAutoNumberFormula = field.default_value && typeof field.default_value === 'string' && field.default_value.trim().startsWith('auto_number(');
127
+ return hasLegacyAutoNumberFormula || isAutoNumberTypeField(field);
128
+ };
129
+
130
+ const collectAutoNumberFields = (fields, permissions = {}, options = {}) => {
131
+ const autoNumberFields = [];
132
+ const pushedFieldKeys = new Set();
133
+
134
+ const canGenerateAutoNumber = (field) => {
135
+ const fieldKey = getFieldKey(field);
136
+ if (!fieldKey) return false;
137
+ return permissions[fieldKey] === 'editable';
138
+ };
139
+
140
+ const tryPushAutoNumberField = (field) => {
141
+ if (!isAutoNumberField(field) || !canGenerateAutoNumber(field)) {
142
+ return;
143
+ }
144
+ const fieldKey = getFieldKey(field);
145
+ const autoNumberName = getAutoNumberRuleName(field, options);
146
+ if (!fieldKey || !autoNumberName || pushedFieldKeys.has(fieldKey)) {
147
+ return;
148
+ }
149
+ pushedFieldKeys.add(fieldKey);
150
+
151
+ const fieldInfo = {
152
+ code: fieldKey,
153
+ auto_number_name: autoNumberName,
154
+ legacy_auto_number_name: fieldKey,
155
+ };
156
+
157
+ if (isAutoNumberTypeField(field)) {
158
+ fieldInfo.isNewType = true;
159
+ const autoNumberId = getAutoNumberRuleId(options.spaceId, field, options);
160
+ if (autoNumberId) {
161
+ fieldInfo.auto_number_id = autoNumberId;
162
+ }
163
+ fieldInfo.autoNumberPrefix = field.autoNumberPrefix;
164
+ fieldInfo.autoNumberSuffix = field.autoNumberSuffix;
165
+ fieldInfo.autoNumberPadding = field.autoNumberPadding;
166
+ fieldInfo.autoNumberDateFormat = field.autoNumberDateFormat;
167
+ fieldInfo.autoNumberResetCycle = field.autoNumberResetCycle;
168
+ }
169
+
170
+ autoNumberFields.push(fieldInfo);
171
+ };
172
+
173
+ for (const field of fields || []) {
174
+ if (field.type === 'section') {
175
+ for (const sectionField of field.fields || []) {
176
+ tryPushAutoNumberField(sectionField);
177
+ }
178
+ } else {
179
+ tryPushAutoNumberField(field);
180
+ }
181
+ }
182
+
183
+ return autoNumberFields;
184
+ };
185
+
186
+ const generateAutoNumberFromFieldConfig = async (spaceId, fieldConfig) => {
187
+ const db = await getCollection('instance_number_rules');
188
+ const ruleId = fieldConfig.auto_number_id;
189
+ const ruleName = fieldConfig.auto_number_name;
190
+ const resetCycle = fieldConfig.autoNumberResetCycle || 'monthly';
191
+ const now = new Date();
192
+ const yyyy = now.getFullYear();
193
+ const mm = now.getMonth() + 1;
194
+ const dd = now.getDate();
195
+
196
+ let numberRules = ruleId
197
+ ? await db.findOne({ _id: ruleId })
198
+ : await db.findOne({ space: spaceId, name: ruleName });
199
+
200
+ if (!numberRules) {
201
+ const legacyRuleName = fieldConfig.legacy_auto_number_name;
202
+ const legacyRule = legacyRuleName && legacyRuleName !== ruleName
203
+ ? await db.findOne({ space: spaceId, name: legacyRuleName })
204
+ : null;
205
+ await db.insertOne({
206
+ _id: ruleId || _makeNewID(),
207
+ space: spaceId,
208
+ name: ruleName,
209
+ year: legacyRule?.year || yyyy,
210
+ month: legacyRule?.month || mm,
211
+ day: legacyRule?.day || dd,
212
+ first_number: legacyRule?.first_number || 1,
213
+ number: legacyRule?.number || 0,
214
+ rules: ''
215
+ });
216
+ numberRules = ruleId
217
+ ? await db.findOne({ _id: ruleId })
218
+ : await db.findOne({ space: spaceId, name: ruleName });
219
+ }
220
+
221
+ let nextNumber = (numberRules.number || 0) + 1;
222
+ if (shouldResetNumber(resetCycle, numberRules, yyyy, mm, dd)) {
223
+ nextNumber = numberRules.first_number || 1;
224
+ }
225
+
226
+ const autoNumber = buildAutoNumberValue(fieldConfig, nextNumber, now);
227
+
228
+ await db.updateOne(
229
+ { _id: numberRules._id },
230
+ { $set: { name: ruleName, year: yyyy, month: mm, day: dd, number: nextNumber } }
231
+ );
232
+
233
+ return autoNumber;
234
+ };
235
+
236
+ const fillAutoNumberValues = async ({ spaceId, values, fields, permissions, formId, formName }) => {
237
+ const nextValues = values || {};
238
+ const autoNumberFields = collectAutoNumberFields(fields, permissions, { spaceId, formId, formName });
239
+
240
+ for (const field of autoNumberFields) {
241
+ if (!field.isNewType) continue;
242
+ const value = nextValues[field.code];
243
+ if (value && !isAutoNumberPreviewValue(value, field)) continue;
244
+ nextValues[field.code] = await generateAutoNumberFromFieldConfig(spaceId, field);
245
+ }
246
+
247
+ return nextValues;
248
+ };
249
+
13
250
  module.exports = {
14
251
  instanceNumberBuilder: async function (spaceId, name) {
15
252
  var _NUMBER, _YYYY, context, date, e, numberRules, padding, res, rules, script;
@@ -80,5 +317,12 @@ module.exports = {
80
317
  };
81
318
  }
82
319
  return res;
83
- }
320
+ },
321
+ collectAutoNumberFields,
322
+ fillAutoNumberValues,
323
+ generateAutoNumberFromFieldConfig,
324
+ getAutoNumberRuleName,
325
+ getFieldKey,
326
+ isAutoNumberPreviewValue,
327
+ isAutoNumberTypeField
84
328
  };
@@ -13,6 +13,7 @@ const WorkflowManager = require('./workflow_manager');
13
13
  const { getCollection, _makeNewID } = require('../utils/collection');
14
14
  const WorkingTime = require('../utils/workingTime');
15
15
  const pushManager = require('./push_manager');
16
+ const autoNumberManager = require('./instance_number_rules');
16
17
  const { t } = require('@steedos/i18n')
17
18
  const moment = require('moment')
18
19
  const { getObject } = require('@steedos/objectql');
@@ -1568,9 +1569,20 @@ UUFlowManager.workflow_engine = async function (approve_from_client, current_use
1568
1569
  setObj[`${key_str}read_date`] = new Date();
1569
1570
  }
1570
1571
 
1572
+ const form = await UUFlowManager.getForm(instance.form);
1573
+ const form_v = await UUFlowManager.getFormVersion(form, instance.form_version);
1574
+ const valuesWithAutoNumber = await autoNumberManager.fillAutoNumberValues({
1575
+ spaceId: space_id,
1576
+ values,
1577
+ fields: form_v.fields || [],
1578
+ permissions: step.permissions,
1579
+ formId: instance.form,
1580
+ formName: form.name
1581
+ });
1582
+
1571
1583
  // Filter approve values based on permissions
1572
1584
  setObj[`${key_str}values`] = await UUFlowManager.getApproveValues(
1573
- values,
1585
+ valuesWithAutoNumber,
1574
1586
  step.permissions,
1575
1587
  instance.form,
1576
1588
  instance.form_version
@@ -3514,6 +3526,7 @@ UUFlowManager.submit_instance = async function (instance_from_client, user_info)
3514
3526
  // 先执行暂存的操作
3515
3527
  // ================begin================
3516
3528
  const form = await UUFlowManager.getForm(instance.form, { fields: { historys: 0 } });
3529
+ const form_v = await UUFlowManager.getFormVersion(form, instance.form_version);
3517
3530
  // 获取Flow当前版本开始节点
3518
3531
  const start_step = flow.current.steps.find(step => step.step_type === 'start');
3519
3532
 
@@ -3560,6 +3573,15 @@ UUFlowManager.submit_instance = async function (instance_from_client, user_info)
3560
3573
  instance_traces[0].name = start_step.name;
3561
3574
  }
3562
3575
 
3576
+ values = await autoNumberManager.fillAutoNumberValues({
3577
+ spaceId: space_id,
3578
+ values,
3579
+ fields: form_v.fields || [],
3580
+ permissions: step.permissions,
3581
+ formId: instance.form,
3582
+ formName: form.name
3583
+ });
3584
+
3563
3585
  // 调整approves的values删除values中在当前步骤中没有编辑权限的字段值
3564
3586
  instance_traces[0].approves[0].values = await UUFlowManager.getApproveValues(
3565
3587
  values,
@@ -5200,4 +5222,4 @@ UUFlowManager.caculateExtras = async function (values = {}, formDoc, formVersion
5200
5222
  return _.isEmpty(extras) ? undefined : extras;
5201
5223
  };
5202
5224
 
5203
- module.exports = UUFlowManager;
5225
+ module.exports = UUFlowManager;
@@ -1,11 +1,11 @@
1
1
  const express = require("express");
2
2
  const router = express.Router();
3
- const { instanceNumberBuilder } = require("../manager/instance_number_rules");
3
+ const autoNumberManager = require("../manager/instance_number_rules");
4
+ const { instanceNumberBuilder } = autoNumberManager;
4
5
  const objectql = require('@steedos/objectql');
5
6
  const _ = require('lodash');
6
7
  const UUFlowManager = require('../manager/uuflow_manager');
7
8
  const { requireAuthentication } = require("@steedos/auth");
8
- const { getCollection } = require('../utils/collection');
9
9
 
10
10
  // 获取用户当前的 approve
11
11
  const getUserApprove = ({ instance, userId }) => {
@@ -39,192 +39,25 @@ const getUserApprove = ({ instance, userId }) => {
39
39
  return currentApprove;
40
40
  };
41
41
 
42
- // ===== 新设计器 autoNumber 字段:基于字段配置直接生成编号 =====
43
-
44
- // 序号补零
45
- const padNumber = (num, length) => {
46
- if (length <= 0) return String(num);
47
- const str = String(num);
48
- return str.length >= length ? str : '0'.repeat(length - str.length) + str;
49
- };
50
-
51
- // 根据日期格式生成日期片段
52
- const formatDatePart = (dateFormat, yyyy, mm, dd) => {
53
- const mmStr = mm < 10 ? '0' + mm : String(mm);
54
- const ddStr = dd < 10 ? '0' + dd : String(dd);
55
- switch (dateFormat) {
56
- case 'YYYY': return String(yyyy);
57
- case 'YYYYMM': return String(yyyy) + mmStr;
58
- case 'YYYYMMDD': return String(yyyy) + mmStr + ddStr;
59
- default: return ''; // 'none' 或 undefined
60
- }
61
- };
62
-
63
- // 根据重置周期判断是否需要重置序号
64
- const shouldResetNumber = (resetCycle, storedRule, yyyy, mm, dd) => {
65
- switch (resetCycle) {
66
- case 'yearly': return yyyy !== storedRule.year;
67
- case 'monthly': return yyyy !== storedRule.year || mm !== storedRule.month;
68
- case 'daily': return yyyy !== storedRule.year || mm !== storedRule.month || dd !== storedRule.day;
69
- default: return false; // 'none' - 永不重置
70
- }
71
- };
72
-
73
42
  /**
74
43
  * 基于字段配置生成自动编号(新设计器 autoNumber 类型字段)
75
44
  * 同样使用 instance_number_rules 集合做序号持久化,如果尚未创建规则记录则自动创建
76
45
  */
77
46
  const generateAutoNumberFromFieldConfig = async (spaceId, fieldConfig) => {
78
- const db = await getCollection("instance_number_rules");
79
-
80
- const ruleName = fieldConfig.auto_number_name;
81
- const prefix = fieldConfig.autoNumberPrefix || '';
82
- const suffix = fieldConfig.autoNumberSuffix || '';
83
- const paddingLen = fieldConfig.autoNumberPadding != null ? fieldConfig.autoNumberPadding : 4;
84
- const dateFormat = fieldConfig.autoNumberDateFormat || 'YYYYMM';
85
- const resetCycle = fieldConfig.autoNumberResetCycle || 'monthly';
86
-
87
- const now = new Date();
88
- const yyyy = now.getFullYear();
89
- const mm = now.getMonth() + 1;
90
- const dd = now.getDate();
91
-
92
- let numberRules = await db.findOne({ space: spaceId, name: ruleName });
93
-
94
- if (!numberRules) {
95
- // 首次使用该字段,自动创建规则记录
96
- await db.insertOne({
97
- space: spaceId,
98
- name: ruleName,
99
- year: yyyy,
100
- month: mm,
101
- day: dd,
102
- first_number: 1,
103
- number: 0,
104
- rules: '' // 新类型字段不使用 rules 模板
105
- });
106
- numberRules = await db.findOne({ space: spaceId, name: ruleName });
107
- }
108
-
109
- let nextNumber = (numberRules.number || 0) + 1;
110
-
111
- if (shouldResetNumber(resetCycle, numberRules, yyyy, mm, dd)) {
112
- nextNumber = numberRules.first_number || 1;
113
- }
114
-
115
- const paddedNumber = padNumber(nextNumber, paddingLen);
116
- const datePart = formatDatePart(dateFormat, yyyy, mm, dd);
117
- const autoNumber = prefix + datePart + paddedNumber + suffix;
118
-
119
- await db.updateOne(
120
- { _id: numberRules._id },
121
- { $set: { year: yyyy, month: mm, day: dd, number: nextNumber } }
122
- );
123
-
124
- return autoNumber;
47
+ return autoNumberManager.generateAutoNumberFromFieldConfig(spaceId, fieldConfig);
125
48
  };
126
49
 
127
50
  // 获取可编辑的自动编号字段
128
- const getEditableAutoNumberFields = async (step, formId, formVersion) => {
51
+ const getEditableAutoNumberFields = async (step, formId, formVersion, spaceId) => {
129
52
  const permissions = step.permissions || {};
130
53
  const form = await UUFlowManager.getForm(formId);
131
54
  const formV = await UUFlowManager.getFormVersion(form, formVersion);
132
-
133
- const autoNumberFields = [];
134
- const pushedFieldKeys = new Set();
135
-
136
- const getFieldKey = (field) => field?.code || field?.name;
137
- const isAutoNumberTypeField = (field) => {
138
- const fieldType = (field?.type || '').toString().toLowerCase();
139
- return fieldType === 'autonumber' || field?.type === 'autoNumber';
140
- };
141
- const canGenerateAutoNumber = (field) => {
142
- const fieldKey = getFieldKey(field);
143
- if (!fieldKey) return false;
144
- const permission = permissions[fieldKey];
145
- if (permission === 'editable') return true;
146
- return false;
147
- };
148
-
149
- // 检查字段是否是自动编号字段(通过 formula 判断)
150
- const isAutoNumberField = (field) => {
151
- if (!field) return false;
152
- const hasLegacyAutoNumberFormula = field.default_value && typeof field.default_value === 'string' && field.default_value.trim().startsWith('auto_number(');
153
- return hasLegacyAutoNumberFormula || isAutoNumberTypeField(field);
154
- };
155
-
156
- // 从 formula 中提取自动编号规则名称
157
- const extractAutoNumberName = (formula) => {
158
- // 匹配 auto_number("xxx") 或 auto_number('xxx') 或 auto_number(xxx)
159
- const match = formula.match(/auto_number\s*\(\s*['"]?([^'"()]+?)['"]?\s*\)/);
160
- return match ? match[1].trim() : null;
161
- };
162
-
163
- const getAutoNumberRuleName = (field) => {
164
- // 1) 兼容旧字段公式:default_value = auto_number(...)
165
- if (field?.default_value && typeof field.default_value === 'string') {
166
- const byFormula = extractAutoNumberName(field.default_value);
167
- if (byFormula) return byFormula;
168
- }
169
55
 
170
- // 2) 兼容常见字段属性命名
171
- const fromProps =
172
- field?.auto_number_name ||
173
- field?.autoNumberName ||
174
- field?.auto_number_rule ||
175
- field?.autoNumberRule ||
176
- field?.auto_number_rule_name ||
177
- field?.autoNumberRuleName;
178
- if (fromProps && typeof fromProps === 'string') {
179
- return fromProps.trim();
180
- }
181
-
182
- // 3) 对于新设计器 autoNumber 字段,默认回退到字段标识(code/name)
183
- return getFieldKey(field);
184
- };
185
-
186
- const tryPushAutoNumberField = (field) => {
187
- if (!isAutoNumberField(field) || !canGenerateAutoNumber(field)) {
188
- return;
189
- }
190
- const fieldKey = getFieldKey(field);
191
- const autoNumberName = getAutoNumberRuleName(field);
192
- if (!fieldKey || !autoNumberName || pushedFieldKeys.has(fieldKey)) {
193
- return;
194
- }
195
- pushedFieldKeys.add(fieldKey);
196
-
197
- const fieldInfo = {
198
- code: fieldKey,
199
- auto_number_name: autoNumberName,
200
- };
201
-
202
- // 新设计器 autoNumber 字段,携带配置属性用于直接生成编号
203
- if (isAutoNumberTypeField(field)) {
204
- fieldInfo.isNewType = true;
205
- fieldInfo.autoNumberPrefix = field.autoNumberPrefix;
206
- fieldInfo.autoNumberSuffix = field.autoNumberSuffix;
207
- fieldInfo.autoNumberPadding = field.autoNumberPadding;
208
- fieldInfo.autoNumberDateFormat = field.autoNumberDateFormat;
209
- fieldInfo.autoNumberResetCycle = field.autoNumberResetCycle;
210
- }
211
-
212
- autoNumberFields.push(fieldInfo);
213
- };
214
-
215
- for (const field of formV.fields || []) {
216
- if (field.type === "section") {
217
- // 处理 section 中的字段
218
- for (const sectionField of field.fields || []) {
219
- tryPushAutoNumberField(sectionField);
220
- }
221
- } else {
222
- // 处理普通字段
223
- tryPushAutoNumberField(field);
224
- }
225
- }
226
-
227
- return autoNumberFields;
56
+ return autoNumberManager.collectAutoNumberFields(formV.fields || [], permissions, {
57
+ spaceId,
58
+ formId,
59
+ formName: form.name,
60
+ });
228
61
  };
229
62
 
230
63
  router.post('/api/workflow/v2/auto_number', requireAuthentication, async function (req, res) {
@@ -319,7 +152,7 @@ router.post('/api/workflow/v2/auto_number', requireAuthentication, async functio
319
152
  }
320
153
 
321
154
  // 获取可编辑的自动编号字段
322
- const autoNumberFields = await getEditableAutoNumberFields(step, instance.form, instance.form_version);
155
+ const autoNumberFields = await getEditableAutoNumberFields(step, instance.form, instance.form_version, instance.space);
323
156
  if (autoNumberFields.length === 0) {
324
157
  return res.status(200).send({
325
158
  success: true,
@@ -379,4 +212,4 @@ router.post('/api/workflow/v2/auto_number', requireAuthentication, async functio
379
212
  });
380
213
 
381
214
 
382
- exports.default = router;
215
+ exports.default = router;
@@ -40,6 +40,15 @@ const FlowversionAPI = {
40
40
  return str.replace(/\"/g, "&quot;").replace(/\n/g, "<br/>");
41
41
  },
42
42
 
43
+ getSafeEdgeLabel: function (str) {
44
+ // Mermaid edge label (A-->|text|B) 对部分符号(如括号/竖线)解析不稳定,先做保守清洗。
45
+ return FlowversionAPI.replaceErrorSymbol(String(str || ''))
46
+ .replace(/[|]/g, '|')
47
+ .replace(/[()()\[\]{}<>]/g, ' ')
48
+ .replace(/\s+/g, ' ')
49
+ .trim();
50
+ },
51
+
43
52
  getStepHandlerName: async function (step, insId, instance) {
44
53
  const db = {
45
54
  users: await getCollection('users'),
@@ -194,8 +203,12 @@ const FlowversionAPI = {
194
203
  let toStep = steps.find(s => s._id === line.to_step);
195
204
  let toStepName = await FlowversionAPI.getStepName(toStep, cachedStepNames, instance_id, instance);
196
205
  if (step.step_type === "condition" && line.name) {
197
- let lineName = FlowversionAPI.replaceErrorSymbol(line.name);
198
- nodes.push(` ${step._id}("${stepName}")-->|${lineName}|${line.to_step}("${toStepName}")`);
206
+ let lineName = FlowversionAPI.getSafeEdgeLabel(line.name);
207
+ if (lineName) {
208
+ nodes.push(` ${step._id}("${stepName}")-->|${lineName}|${line.to_step}("${toStepName}")`);
209
+ } else {
210
+ nodes.push(` ${step._id}("${stepName}")-->${line.to_step}("${toStepName}")`);
211
+ }
199
212
  } else {
200
213
  nodes.push(` ${step._id}("${stepName}")-->${line.to_step}("${toStepName}")`);
201
214
  }
@@ -1056,4 +1069,4 @@ router.get('/api/workflow/chart/traces', requireAuthentication, async function (
1056
1069
  router.get('/api/workflow/chart/traces_expand', requireAuthentication, async function (req, res) {
1057
1070
  await FlowversionAPI.sendHtmlResponse(req, res, "traces_expand");
1058
1071
  });
1059
- exports.default = router;
1072
+ exports.default = router;
@@ -192,7 +192,7 @@ router.post("/api/workflow/v2/instance/forward", requireAuthentication, async fu
192
192
 
193
193
  // Process fields
194
194
  for (const field of fields) {
195
- const exists_field = _.find(old_fields, f => f.type == field.type && f.code == field.code);
195
+ const exists_field = _.find(old_fields, f => f.code == field.code);
196
196
  if (exists_field) common_fields.push(field);
197
197
 
198
198
  const select_input_field = _.find(old_fields, f =>
@@ -241,6 +241,10 @@ router.post("/api/workflow/v2/instance/forward", requireAuthentication, async fu
241
241
  old_v = new_multiSelected.join(",");
242
242
  }
243
243
 
244
+ if (f.type == "lookup" && _.isObject(old_v) && old_v._id) {
245
+ old_v = old_v._id;
246
+ }
247
+
244
248
  new_values[key] = old_v;
245
249
  }
246
250
  }
@@ -281,6 +285,10 @@ router.post("/api/workflow/v2/instance/forward", requireAuthentication, async fu
281
285
  old_v = new_multiSelected.join(",");
282
286
  }
283
287
 
288
+ if (f.type == "lookup" && _.isObject(old_v) && old_v._id) {
289
+ old_v = old_v._id;
290
+ }
291
+
284
292
  new_table_row_values[key] = old_v;
285
293
  }
286
294
  }
@@ -324,6 +332,10 @@ router.post("/api/workflow/v2/instance/forward", requireAuthentication, async fu
324
332
  old_v = new_multiSelected.join(",");
325
333
  }
326
334
 
335
+ if (field.type == "lookup" && _.isObject(old_v) && old_v._id) {
336
+ old_v = old_v._id;
337
+ }
338
+
327
339
  new_values[key] = old_v;
328
340
  }
329
341
  }
@@ -208,13 +208,14 @@ function buildFormDesignSystemPrompt({ chineseFieldNames = false, isUpgrade = fa
208
208
 
209
209
  - autoNumber 类型(自动编号):
210
210
  - 自动编号字段始终为只读(readonly: true),运行时由系统自动生成唯一编号
211
+ - auto_number_name: 字符串,可选,编号规则名称;留空时系统按表单和字段自动生成
211
212
  - autoNumberPrefix: 字符串,编号前缀,如 "SN-"、"HT-",默认 ""
212
213
  - autoNumberSuffix: 字符串,编号后缀,默认 ""
213
214
  - autoNumberPadding: 数字,序号位数(补零),默认 4,如 4 → 0001
214
215
  - autoNumberDateFormat: 字符串,日期片段格式,可选值:"none"(无日期) | "YYYY"(年) | "YYYYMM"(年月,默认) | "YYYYMMDD"(年月日)
215
216
  - autoNumberResetCycle: 字符串,序号重置周期,可选值:"none"(不重置) | "yearly"(每年) | "monthly"(每月,默认) | "daily"(每天)
216
217
  - 生成的编号格式示例:前缀 + 日期 + 序号 + 后缀,如 "SN-202603-0001"
217
- - 示例:{ "type": "autoNumber", "label": "合同编号", "name": "contract_no", "readonly": true, "autoNumberPrefix": "HT-", "autoNumberDateFormat": "YYYYMM", "autoNumberPadding": 4, "autoNumberResetCycle": "monthly" }
218
+ - 示例:{ "type": "autoNumber", "label": "合同编号", "name": "contract_no", "readonly": true, "auto_number_name": "合同编号", "autoNumberPrefix": "HT-", "autoNumberDateFormat": "YYYYMM", "autoNumberPadding": 4, "autoNumberResetCycle": "monthly" }
218
219
 
219
220
  - 动态显隐条件(任何字段都可设置):${chineseFieldNames ? `
220
221
  - visibilityExpression: 字符串,条件表达式,控制字段显示/隐藏。当表达式结果为 true 时字段可见,否则隐藏。
@@ -438,11 +439,11 @@ ${formulaRule}
438
439
  - 迁移规则:
439
440
  - 遇到字段的 default_value 包含 \`auto_number(...)\` 时,将该字段的 type 改为 "autoNumber",设置 readonly: true
440
441
  - 删除原有的 default_value 属性
441
- - 如果旧版编号规则名称中能推断出前缀、日期格式等信息,相应设置 autoNumberPrefix、autoNumberDateFormat 等属性;否则使用默认值
442
+ - 如果旧版编号规则名称中能推断出前缀、日期格式等信息,相应设置 auto_number_name、autoNumberPrefix、autoNumberDateFormat 等属性;否则使用默认值
442
443
  - 默认配置:autoNumberPrefix: ""、autoNumberPadding: 4、autoNumberDateFormat: "YYYYMM"、autoNumberResetCycle: "monthly"
443
444
  - 迁移示例:
444
445
  旧版字段:{ "name": "合同编号", "type": "text", "default_value": "auto_number('contract_no')" }
445
- 新版字段:{ "name": "contract_no", "label": "合同编号", "type": "autoNumber", "readonly": true, "autoNumberPrefix": "", "autoNumberPadding": 4, "autoNumberDateFormat": "YYYYMM", "autoNumberResetCycle": "monthly" }
446
+ 新版字段:{ "name": "contract_no", "label": "合同编号", "type": "autoNumber", "readonly": true, "auto_number_name": "contract_no", "autoNumberPrefix": "", "autoNumberPadding": 4, "autoNumberDateFormat": "YYYYMM", "autoNumberResetCycle": "monthly" }
446
447
  - migrationLog 记录格式:"字段 <name>: default_value auto_number(...) 转换为 autoNumber 类型"
447
448
 
448
449
  ### 旧版 HTML 字段类型迁移