@steedos-labs/plugin-workflow 3.0.43 → 3.0.45
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/designer/dist/amis-renderer/amis-renderer.css +1 -1
- package/designer/dist/amis-renderer/amis-renderer.js +1 -1
- package/designer/dist/assets/{index-C3K_lyOl.css → index-B5zmn1Wt.css} +1 -1
- package/designer/dist/assets/{index-XZWBSnmF.js → index-D7W5yEF4.js} +268 -268
- package/designer/dist/index.html +3 -3
- package/main/default/objects/instance_tasks/buttons/instance_new.button.yml +1 -1
- package/main/default/objects/instances/buttons/instance_new.button.yml +1 -1
- package/main/default/objects/workflow_designer_backups.object.yml +5 -0
- package/main/default/pages/flowdetail.page.amis.json +0 -31
- package/main/default/routes/am.router.js +1 -2
- package/main/default/routes/api_workflow_ai_form_design_stream.router.js +36 -1
- package/main/default/routes/api_workflow_next_step.router.js +8 -1
- package/main/default/routes/api_workflow_next_step_users.router.js +4 -1
- package/main/default/routes/api_workflow_submit.router.js +31 -2
- package/main/default/routes/flow_form_design.ejs +2 -1
- package/main/default/routes/object_workflows.router.js +3 -3
- package/main/default/test/test_migrateApprovalCommentsField.js +220 -0
- package/main/default/test/test_rollbackApprovalCommentsField.js +235 -0
- package/main/default/triggers/amis_form_design.trigger.js +2 -1
- package/main/default/utils/designerManager.js +2 -5
- package/package.json +1 -1
- package/public/amis-renderer/amis-renderer.css +1 -1
- package/public/amis-renderer/amis-renderer.js +1 -1
- package/public/workflow/index.css +24 -1
- package/src/rests/approvalCommentsConsole.js +9 -0
- package/src/rests/getPageSchema.js +58 -0
- package/src/rests/index.js +1 -0
- package/src/rests/migrateApprovalCommentsField.js +31 -12
- package/src/rests/rollbackApprovalCommentsField.js +32 -21
|
@@ -306,11 +306,13 @@ tbody .color-priority-muted *{
|
|
|
306
306
|
|
|
307
307
|
.steedos-instance-detail-wrapper{
|
|
308
308
|
height: calc(100vh - 101px);
|
|
309
|
+
height: calc(100dvh - 101px);
|
|
309
310
|
}
|
|
310
311
|
|
|
311
312
|
@media (max-width: 768px){
|
|
312
313
|
.steedos-instance-detail-wrapper{
|
|
313
314
|
height: calc(100vh - 64px);
|
|
315
|
+
height: calc(100dvh - 64px);
|
|
314
316
|
}
|
|
315
317
|
}
|
|
316
318
|
|
|
@@ -553,7 +555,28 @@ tbody .color-priority-muted *{
|
|
|
553
555
|
background: var(--button-primary-default-bg-color) !important
|
|
554
556
|
}
|
|
555
557
|
|
|
556
|
-
.ant-btn-color-primary
|
|
558
|
+
.ant-btn-color-primary {
|
|
557
559
|
background: var(--button-primary-default-bg-color) !important;
|
|
558
560
|
border-color: var(--button-primary-default-bg-color) !important;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.ant-btn-color-primary:disabled{
|
|
564
|
+
border-color: #d9d9d9 !important;
|
|
565
|
+
color: rgba(0, 0, 0, 0.25) !important;
|
|
566
|
+
background: rgba(0, 0, 0, 0.04) !important;
|
|
567
|
+
box-shadow: none !important;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
.select-popup-high-z {
|
|
571
|
+
z-index: 9999 !important;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.md\:grid-cols-2 {
|
|
575
|
+
@media (width >= 48rem) {
|
|
576
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.ant-modal-root .multiselect-form-mode.ant-select-multiple .ant-select-selector .ant-select-selection-wrap{
|
|
581
|
+
min-height: 36px !important;
|
|
559
582
|
}
|
|
@@ -273,6 +273,15 @@ module.exports = {
|
|
|
273
273
|
</div>
|
|
274
274
|
|
|
275
275
|
<div class="tab-content active" id="execute">
|
|
276
|
+
<div style="background:#fffbe6;border-left:4px solid #d97706;border-radius:6px;padding:20px 24px;margin-bottom:24px;">
|
|
277
|
+
<p style="font-weight:bold;font-size:16px;margin-bottom:12px;">⚠️ 注意事项</p>
|
|
278
|
+
<ol style="padding-left:20px;line-height:2;color:#2d3748;">
|
|
279
|
+
<li>升级和还原脚本不但会修改表单<strong>当前版本</strong>(<code style="background:#edf2f7;padding:2px 6px;border-radius:3px;color:#e53e3e;">current</code> 中的字段),也会修改<strong>历史版本</strong>(<code style="background:#edf2f7;padding:2px 6px;border-radius:3px;color:#e53e3e;">historys</code> 中的字段)。</li>
|
|
280
|
+
<li>升级和还原脚本会<strong>递归处理嵌套 section 分组字段</strong>,即 <code style="background:#edf2f7;padding:2px 6px;border-radius:3px;color:#e53e3e;">current.fields[*].fields[*]</code> 和 <code style="background:#edf2f7;padding:2px 6px;border-radius:3px;color:#e53e3e;">historys[*].fields[*].fields[*]</code> 中的字段均会被处理。</li>
|
|
281
|
+
<li>升级后,如果在流程的<strong>表单设计器中保存过</strong>,该表单就<strong>无法还原到最初的状态</strong>了。因此,对于可能需要执行还原操作的表单,务必在升级后先测试确认一次,确认正常后再在表单设计器中执行保存操作。</li>
|
|
282
|
+
</ol>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
276
285
|
<div class="operation-panel">
|
|
277
286
|
<h3>🧪 集成测试 (Integration Test)</h3>
|
|
278
287
|
<p style="color: #666; margin-bottom: 15px;">
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const objectql = require('@steedos/objectql');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
rest: {
|
|
5
|
+
method: 'GET',
|
|
6
|
+
fullPath: '/api/workflow/pages/schema'
|
|
7
|
+
},
|
|
8
|
+
params: {
|
|
9
|
+
pageId: { type: 'string' }
|
|
10
|
+
},
|
|
11
|
+
async handler(ctx) {
|
|
12
|
+
const { pageId } = ctx.params;
|
|
13
|
+
const userSession = ctx.meta.user;
|
|
14
|
+
|
|
15
|
+
if (!pageId) {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 直接 broker.call page.getMeSchema,完全绕过 vm2 沙箱
|
|
20
|
+
// 参考 steedos-platform/services/service-pages/main/default/routes/page_schema.router.js
|
|
21
|
+
// flow_selector 这个 page 的 type 固定为 'page'
|
|
22
|
+
const result = await objectql.getSteedosSchema().broker.call('page.getMeSchema', {
|
|
23
|
+
type: 'page',
|
|
24
|
+
pageId: pageId
|
|
25
|
+
}, {
|
|
26
|
+
meta: { user: userSession }
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!result || !result.schema) {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// result.schema 可能是字符串(JSON)或对象,统一处理
|
|
34
|
+
let schema = result.schema;
|
|
35
|
+
if (typeof schema === 'string') {
|
|
36
|
+
try {
|
|
37
|
+
schema = JSON.parse(schema);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 返回与旧接口一致的结构,包含 inject + assets(tailwind CSS)+ body
|
|
44
|
+
// 参考 steedos-platform/services/service-pages/main/default/functions/pages_schema.function.yml
|
|
45
|
+
return {
|
|
46
|
+
type: 'inject',
|
|
47
|
+
assets: [
|
|
48
|
+
{
|
|
49
|
+
type: 'css',
|
|
50
|
+
id: 'page-css',
|
|
51
|
+
src: `/api/v6/pages/${pageId}/current/tailwind.css`,
|
|
52
|
+
location: 'start'
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
body: [schema]
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
};
|
package/src/rests/index.js
CHANGED
|
@@ -16,4 +16,5 @@ module.exports = {
|
|
|
16
16
|
rollbackApprovalCommentsField: require('./rollbackApprovalCommentsField'),
|
|
17
17
|
integrationTestApprovalComments: require('./integrationTestApprovalComments'),
|
|
18
18
|
approvalCommentsConsole: require('./approvalCommentsConsole'),
|
|
19
|
+
getPageSchema: require('./getPageSchema'),
|
|
19
20
|
}
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
* from old format to new steedos-field format with config object.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// MongoDB connection
|
|
10
|
+
// MongoDB connection (lazy-loaded so pure helper functions are testable without the driver)
|
|
11
|
+
let MongoClient = null;
|
|
13
12
|
let client = null;
|
|
14
13
|
let db = null;
|
|
15
14
|
|
|
@@ -18,6 +17,10 @@ async function connectToMongoDB() {
|
|
|
18
17
|
return db;
|
|
19
18
|
}
|
|
20
19
|
|
|
20
|
+
if (!MongoClient) {
|
|
21
|
+
MongoClient = require('mongodb').MongoClient;
|
|
22
|
+
}
|
|
23
|
+
|
|
21
24
|
const mongoUrl = process.env.MONGO_URL || 'mongodb://127.0.0.1:27017/steedos';
|
|
22
25
|
try {
|
|
23
26
|
client = new MongoClient(mongoUrl);
|
|
@@ -313,7 +316,8 @@ function processAmisSchema(amisSchemaStr) {
|
|
|
313
316
|
}
|
|
314
317
|
|
|
315
318
|
/**
|
|
316
|
-
* Process fields array and return bulk operations
|
|
319
|
+
* Process fields array and return bulk operations.
|
|
320
|
+
* Recursively handles nested fields inside section/panel/group containers.
|
|
317
321
|
*/
|
|
318
322
|
function processFieldsArray(fields, pathPrefix) {
|
|
319
323
|
const updates = [];
|
|
@@ -325,12 +329,13 @@ function processFieldsArray(fields, pathPrefix) {
|
|
|
325
329
|
|
|
326
330
|
for (let i = 0; i < fields.length; i++) {
|
|
327
331
|
const field = fields[i];
|
|
332
|
+
const fieldPath = `${pathPrefix}.${i}`;
|
|
328
333
|
const transformation = transformField(field);
|
|
329
334
|
|
|
330
335
|
if (transformation) {
|
|
331
336
|
updates.push({
|
|
332
337
|
index: i,
|
|
333
|
-
path:
|
|
338
|
+
path: fieldPath,
|
|
334
339
|
update: transformation,
|
|
335
340
|
fieldId: field._id || field.code || field.name, // Track field for counting
|
|
336
341
|
hasAmisField: !!field._amisField // Track if we need to unset _amisField
|
|
@@ -338,15 +343,22 @@ function processFieldsArray(fields, pathPrefix) {
|
|
|
338
343
|
|
|
339
344
|
// Unset original properties that are being backed up
|
|
340
345
|
if (field._amisField) {
|
|
341
|
-
unsets.push(`${
|
|
346
|
+
unsets.push(`${fieldPath}._amisField`);
|
|
342
347
|
}
|
|
343
348
|
if (field.formula) {
|
|
344
|
-
unsets.push(`${
|
|
349
|
+
unsets.push(`${fieldPath}.formula`);
|
|
345
350
|
}
|
|
346
351
|
if (field.default_value) {
|
|
347
|
-
unsets.push(`${
|
|
352
|
+
unsets.push(`${fieldPath}.default_value`);
|
|
348
353
|
}
|
|
349
354
|
}
|
|
355
|
+
|
|
356
|
+
// Recursively process nested fields inside container types (section/panel/group/etc.)
|
|
357
|
+
if (Array.isArray(field.fields)) {
|
|
358
|
+
const nested = processFieldsArray(field.fields, `${fieldPath}.fields`);
|
|
359
|
+
for (const u of nested.updates) updates.push(u);
|
|
360
|
+
for (const u of nested.unsets) unsets.push(u);
|
|
361
|
+
}
|
|
350
362
|
}
|
|
351
363
|
|
|
352
364
|
return { updates, unsets };
|
|
@@ -362,6 +374,13 @@ module.exports = {
|
|
|
362
374
|
dryRun: { type: 'boolean', optional: true, convert: true },
|
|
363
375
|
fid: { type: 'string', optional: true } // Flow ID to process single flow
|
|
364
376
|
},
|
|
377
|
+
// Exported for testing
|
|
378
|
+
_helpers: {
|
|
379
|
+
matchesApprovalPattern,
|
|
380
|
+
transformField,
|
|
381
|
+
processFieldsArray,
|
|
382
|
+
processAmisSchema
|
|
383
|
+
},
|
|
365
384
|
async handler(ctx) {
|
|
366
385
|
const { user } = ctx.meta;
|
|
367
386
|
|
|
@@ -422,10 +441,10 @@ module.exports = {
|
|
|
422
441
|
formUpdated = true;
|
|
423
442
|
formFieldCount += updates.length; // Count fields, not operations
|
|
424
443
|
|
|
425
|
-
// Build $set updates for current.fields
|
|
444
|
+
// Build $set updates for current.fields (including nested section/panel/group fields)
|
|
426
445
|
for (const update of updates) {
|
|
427
446
|
for (const key in update.update) {
|
|
428
|
-
formUpdate[
|
|
447
|
+
formUpdate[`${update.path}.${key}`] = update.update[key];
|
|
429
448
|
}
|
|
430
449
|
}
|
|
431
450
|
|
|
@@ -450,10 +469,10 @@ module.exports = {
|
|
|
450
469
|
formUpdated = true;
|
|
451
470
|
formFieldCount += updates.length; // Count fields, not operations
|
|
452
471
|
|
|
453
|
-
// Build $set updates for historys.fields
|
|
472
|
+
// Build $set updates for historys.fields (including nested section/panel/group fields)
|
|
454
473
|
for (const update of updates) {
|
|
455
474
|
for (const key in update.update) {
|
|
456
|
-
formUpdate[
|
|
475
|
+
formUpdate[`${update.path}.${key}`] = update.update[key];
|
|
457
476
|
}
|
|
458
477
|
}
|
|
459
478
|
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
* back to their original input type format.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// MongoDB connection
|
|
10
|
+
// MongoDB connection (lazy-loaded so pure helper functions are testable without the driver)
|
|
11
|
+
let MongoClient = null;
|
|
13
12
|
let client = null;
|
|
14
13
|
let db = null;
|
|
15
14
|
|
|
@@ -18,6 +17,10 @@ async function connectToMongoDB() {
|
|
|
18
17
|
return db;
|
|
19
18
|
}
|
|
20
19
|
|
|
20
|
+
if (!MongoClient) {
|
|
21
|
+
MongoClient = require('mongodb').MongoClient;
|
|
22
|
+
}
|
|
23
|
+
|
|
21
24
|
const mongoUrl = process.env.MONGO_URL || 'mongodb://127.0.0.1:27017/steedos';
|
|
22
25
|
try {
|
|
23
26
|
client = new MongoClient(mongoUrl);
|
|
@@ -53,6 +56,7 @@ function processFieldsArrayForRollback(fields, pathPrefix, formId, formName) {
|
|
|
53
56
|
|
|
54
57
|
for (let i = 0; i < fields.length; i++) {
|
|
55
58
|
const field = fields[i];
|
|
59
|
+
const fieldPath = `${pathPrefix}.${i}`;
|
|
56
60
|
|
|
57
61
|
// Only rollback fields that were transformed (Idempotency principle)
|
|
58
62
|
if (field.__approval_comments_transformed === true) {
|
|
@@ -63,7 +67,7 @@ function processFieldsArrayForRollback(fields, pathPrefix, formId, formName) {
|
|
|
63
67
|
const originalType = field.__approval_comments_original_type || 'input';
|
|
64
68
|
updates.push({
|
|
65
69
|
index: i,
|
|
66
|
-
path: `${
|
|
70
|
+
path: `${fieldPath}.type`,
|
|
67
71
|
value: originalType
|
|
68
72
|
});
|
|
69
73
|
|
|
@@ -71,7 +75,7 @@ function processFieldsArrayForRollback(fields, pathPrefix, formId, formName) {
|
|
|
71
75
|
if (field.__approval_comments_original_formula) {
|
|
72
76
|
updates.push({
|
|
73
77
|
index: i,
|
|
74
|
-
path: `${
|
|
78
|
+
path: `${fieldPath}.formula`,
|
|
75
79
|
value: field.__approval_comments_original_formula
|
|
76
80
|
});
|
|
77
81
|
}
|
|
@@ -80,7 +84,7 @@ function processFieldsArrayForRollback(fields, pathPrefix, formId, formName) {
|
|
|
80
84
|
if (field.__approval_comments_original_default_value) {
|
|
81
85
|
updates.push({
|
|
82
86
|
index: i,
|
|
83
|
-
path: `${
|
|
87
|
+
path: `${fieldPath}.default_value`,
|
|
84
88
|
value: field.__approval_comments_original_default_value
|
|
85
89
|
});
|
|
86
90
|
}
|
|
@@ -89,29 +93,38 @@ function processFieldsArrayForRollback(fields, pathPrefix, formId, formName) {
|
|
|
89
93
|
if (field.__approval_comments_original__amisField) {
|
|
90
94
|
updates.push({
|
|
91
95
|
index: i,
|
|
92
|
-
path: `${
|
|
96
|
+
path: `${fieldPath}._amisField`,
|
|
93
97
|
value: field.__approval_comments_original__amisField
|
|
94
98
|
});
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
// Remove added properties
|
|
98
|
-
unsets.push(`${
|
|
99
|
-
unsets.push(`${
|
|
100
|
-
unsets.push(`${
|
|
101
|
-
unsets.push(`${
|
|
102
|
-
unsets.push(`${
|
|
103
|
-
unsets.push(`${
|
|
102
|
+
unsets.push(`${fieldPath}.__approval_comments_transformed`);
|
|
103
|
+
unsets.push(`${fieldPath}.config`);
|
|
104
|
+
unsets.push(`${fieldPath}.__approval_comments_original__amisField`);
|
|
105
|
+
unsets.push(`${fieldPath}.__approval_comments_original_type`); // Remove backup
|
|
106
|
+
unsets.push(`${fieldPath}.__approval_comments_original_formula`); // Remove backup
|
|
107
|
+
unsets.push(`${fieldPath}.__approval_comments_original_default_value`); // Remove backup
|
|
104
108
|
} catch (error) {
|
|
105
109
|
// Silent Failure principle: Record error but continue processing
|
|
106
110
|
fieldErrors.push({
|
|
107
111
|
formId: formId,
|
|
108
112
|
formName: formName,
|
|
109
|
-
fieldPath:
|
|
113
|
+
fieldPath: fieldPath,
|
|
110
114
|
fieldCode: field.code || field.name || 'unknown',
|
|
111
115
|
error: error.message
|
|
112
116
|
});
|
|
113
117
|
}
|
|
114
118
|
}
|
|
119
|
+
|
|
120
|
+
// Recursively process nested fields inside container types (section/panel/group/etc.)
|
|
121
|
+
if (Array.isArray(field.fields)) {
|
|
122
|
+
const nested = processFieldsArrayForRollback(field.fields, `${fieldPath}.fields`, formId, formName);
|
|
123
|
+
for (const u of nested.updates) updates.push(u);
|
|
124
|
+
for (const u of nested.unsets) unsets.push(u);
|
|
125
|
+
fieldCount += nested.fieldCount;
|
|
126
|
+
for (const e of nested.fieldErrors) fieldErrors.push(e);
|
|
127
|
+
}
|
|
115
128
|
}
|
|
116
129
|
|
|
117
130
|
return { updates, unsets, fieldCount, fieldErrors };
|
|
@@ -127,6 +140,10 @@ module.exports = {
|
|
|
127
140
|
dryRun: { type: 'boolean', optional: true, convert: true },
|
|
128
141
|
fid: { type: 'string', optional: true } // Flow ID to rollback single flow
|
|
129
142
|
},
|
|
143
|
+
// Exported for testing
|
|
144
|
+
_helpers: {
|
|
145
|
+
processFieldsArrayForRollback
|
|
146
|
+
},
|
|
130
147
|
async handler(ctx) {
|
|
131
148
|
const { user } = ctx.meta;
|
|
132
149
|
|
|
@@ -140,13 +157,7 @@ module.exports = {
|
|
|
140
157
|
const formsCollection = await getCollection('forms');
|
|
141
158
|
|
|
142
159
|
// Build query filter
|
|
143
|
-
const query = {
|
|
144
|
-
state: 'enabled', // Only process enabled forms
|
|
145
|
-
$or: [
|
|
146
|
-
{ 'current.fields.__approval_comments_transformed': true },
|
|
147
|
-
{ 'historys.fields.__approval_comments_transformed': true }
|
|
148
|
-
]
|
|
149
|
-
};
|
|
160
|
+
const query = { state: 'enabled' }; // Only process enabled forms
|
|
150
161
|
|
|
151
162
|
// If fid is provided, query flows collection to get form ID
|
|
152
163
|
if (fid) {
|