@steedos-labs/plugin-workflow 3.0.12 → 3.0.13
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/AI_PLUGIN_GUIDE.md +939 -0
- package/main/default/applications/approve_workflow.app.yml +159 -3
- package/main/default/manager/handlers_manager.js +1 -1
- package/main/default/manager/instance_number_rules.js +84 -0
- package/main/default/manager/instance_tasks_manager.js +79 -1
- package/main/default/manager/uuflow_manager.js +55 -45
- package/main/default/objects/instances/buttons/instance_reassign.button.yml +5 -4
- package/main/default/pages/instance_detail.page.amis.json +1 -99
- package/main/default/pages/instance_tasks_detail.page.amis.json +0 -100
- package/main/default/pages/page_instance_view.page.amis.json +1 -1
- package/main/default/routes/api_auto_number.router.js +233 -0
- package/main/default/routes/api_files.router.js +21 -0
- package/main/default/routes/api_have_read.router.js +20 -2
- package/main/default/routes/api_workflow_chart.router.js +23 -3
- package/main/default/routes/api_workflow_next_step.router.js +111 -30
- package/package.json +1 -1
- package/public/workflow/index.css +12 -4
- package/main/default/pages/instance_tasks_list.page.amis.json +0 -330
- package/main/default/pages/instance_tasks_list.page.yml +0 -12
- package/main/default/pages/instances_list.page.amis.json +0 -327
- package/main/default/pages/instances_list.page.yml +0 -12
|
@@ -1,106 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "page",
|
|
3
3
|
"body": [
|
|
4
|
-
{
|
|
5
|
-
"type": "wrapper",
|
|
6
|
-
"visibleOn": "${AND(display != 'split', ${window:innerWidth > 768})}",
|
|
7
|
-
"className": "bg-white p-0 flex-shrink-0 min-w-[240px] lg:order-first lg:flex lg:flex-col rounded shadow my-4 ml-4 mr-1 my-4 sm:rounded",
|
|
8
|
-
"body": [
|
|
9
|
-
{
|
|
10
|
-
"type": "service",
|
|
11
|
-
"className": "w-full h-full",
|
|
12
|
-
"onEvent": {
|
|
13
|
-
"@data.changed.steedos_keyvalues": {
|
|
14
|
-
"actions": [
|
|
15
|
-
{
|
|
16
|
-
"actionType": "reload"
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
"body": [
|
|
22
|
-
{
|
|
23
|
-
"type": "button",
|
|
24
|
-
"label": "刷新",
|
|
25
|
-
"className": "instance-nav-reload hidden",
|
|
26
|
-
"onEvent": {
|
|
27
|
-
"click": {
|
|
28
|
-
"actions": [{
|
|
29
|
-
"actionType": "reload",
|
|
30
|
-
"componentId": "u:instanceNav"
|
|
31
|
-
}]
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"type": "input-tree",
|
|
37
|
-
"treeContainerClassName": "h-full",
|
|
38
|
-
"name": "tree",
|
|
39
|
-
"className": "instance-box-tree w-full",
|
|
40
|
-
"id": "u:9f3dd961ca12",
|
|
41
|
-
"stacked": true,
|
|
42
|
-
"multiple": false,
|
|
43
|
-
"enableNodePath": false,
|
|
44
|
-
"hideRoot": true,
|
|
45
|
-
"showIcon": true,
|
|
46
|
-
"initiallyOpen": false,
|
|
47
|
-
"virtualThreshold": 100000,
|
|
48
|
-
"value":"/app/${appId}/${objectName}/grid/${listName}",
|
|
49
|
-
"size": "md",
|
|
50
|
-
"onEvent": {
|
|
51
|
-
"change": {
|
|
52
|
-
"actions": [
|
|
53
|
-
{
|
|
54
|
-
"args": {
|
|
55
|
-
"link": "${event.data.value}",
|
|
56
|
-
"blank": false
|
|
57
|
-
},
|
|
58
|
-
"actionType": "link"
|
|
59
|
-
}
|
|
60
|
-
]
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
"menuTpl": {
|
|
64
|
-
"type": "wrapper",
|
|
65
|
-
"className": "flex flex-row p-0 m-0",
|
|
66
|
-
"body": [
|
|
67
|
-
{
|
|
68
|
-
"type": "tpl",
|
|
69
|
-
"className": "flex-1 w-6/12",
|
|
70
|
-
"tpl": "${label}"
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
"type": "tpl",
|
|
74
|
-
"className": "-mx-11 ",
|
|
75
|
-
"tpl": "",
|
|
76
|
-
"badge": {
|
|
77
|
-
"className": "h-0",
|
|
78
|
-
"offset": [
|
|
79
|
-
-20,
|
|
80
|
-
12
|
|
81
|
-
],
|
|
82
|
-
"mode": "text",
|
|
83
|
-
"text": "${tag}",
|
|
84
|
-
"overflowCount": 999
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
]
|
|
88
|
-
},
|
|
89
|
-
"unfoldedLevel": 2,
|
|
90
|
-
"source": "${options}"
|
|
91
|
-
}
|
|
92
|
-
],
|
|
93
|
-
"id": "u:instanceNav",
|
|
94
|
-
"api": {
|
|
95
|
-
"method": "get",
|
|
96
|
-
"url": "${context.rootUrl}/api/${appId}/workflow/nav",
|
|
97
|
-
"headers": {
|
|
98
|
-
"Authorization": "Bearer ${context.tenantId},${context.authToken}"
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
]
|
|
103
|
-
},
|
|
104
4
|
{
|
|
105
5
|
"type": "wrapper",
|
|
106
6
|
"className": "steedos-instance-detail-wrapper m-0 p-0 flex-1 focus:outline-none lg:order-last sm:m-4 shadow sm:rounded",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"bodyClassName": "p-0 flex flex-1 overflow-hidden h-full",
|
|
39
39
|
"name": "amis-root-workflow",
|
|
40
40
|
"initApi": {
|
|
41
|
-
"url": "
|
|
41
|
+
"url": "/api/workflow/v2/instance/${recordId}/permission",
|
|
42
42
|
"method": "get",
|
|
43
43
|
"data": {},
|
|
44
44
|
"requestAdaptor": "",
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { instanceNumberBuilder } = require("../manager/instance_number_rules");
|
|
4
|
+
const objectql = require('@steedos/objectql');
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
const UUFlowManager = require('../manager/uuflow_manager');
|
|
7
|
+
const { requireAuthentication } = require("@steedos/auth");
|
|
8
|
+
|
|
9
|
+
// 获取用户当前的 approve
|
|
10
|
+
const getUserApprove = ({ instance, userId }) => {
|
|
11
|
+
const currentTrace = _.find(instance.traces, (trace) => {
|
|
12
|
+
return trace.is_finished != true;
|
|
13
|
+
});
|
|
14
|
+
let currentApprove = null;
|
|
15
|
+
if (currentTrace) {
|
|
16
|
+
currentApprove = _.find(currentTrace.approves, (approve) => {
|
|
17
|
+
return approve.is_finished != true && approve.handler == userId;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//传阅的approve返回最新一条
|
|
22
|
+
if (!currentApprove || currentApprove.type == "cc") {
|
|
23
|
+
// 当前是传阅
|
|
24
|
+
_.each(instance.traces, function (t) {
|
|
25
|
+
_.each(t.approves, function (a) {
|
|
26
|
+
if (a.type == "cc" && a.handler == userId && a.is_finished == false) {
|
|
27
|
+
currentApprove = a;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!currentApprove) return;
|
|
34
|
+
|
|
35
|
+
if (currentApprove._id) {
|
|
36
|
+
currentApprove.id = currentApprove._id;
|
|
37
|
+
}
|
|
38
|
+
return currentApprove;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// 获取可编辑的自动编号字段
|
|
42
|
+
const getEditableAutoNumberFields = async (step, formId, formVersion) => {
|
|
43
|
+
const permissions = step.permissions || {};
|
|
44
|
+
const form = await UUFlowManager.getForm(formId);
|
|
45
|
+
const formV = await UUFlowManager.getFormVersion(form, formVersion);
|
|
46
|
+
|
|
47
|
+
const autoNumberFields = [];
|
|
48
|
+
|
|
49
|
+
// 检查字段是否是自动编号字段(通过 formula 判断)
|
|
50
|
+
const isAutoNumberField = (field) => {
|
|
51
|
+
return field.default_value && typeof field.default_value === 'string' && field.default_value.trim().startsWith('auto_number(');
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// 从 formula 中提取自动编号规则名称
|
|
55
|
+
const extractAutoNumberName = (formula) => {
|
|
56
|
+
// 匹配 auto_number("xxx") 或 auto_number('xxx') 或 auto_number(xxx)
|
|
57
|
+
const match = formula.match(/auto_number\s*\(\s*['"]?([^'"()]+?)['"]?\s*\)/);
|
|
58
|
+
return match ? match[1].trim() : null;
|
|
59
|
+
};
|
|
60
|
+
for (const field of formV.fields || []) {
|
|
61
|
+
|
|
62
|
+
if (field.type === "section") {
|
|
63
|
+
// 处理 section 中的字段
|
|
64
|
+
for (const sectionField of field.fields || []) {
|
|
65
|
+
if (isAutoNumberField(sectionField) && permissions[sectionField.code] === "editable") {
|
|
66
|
+
const autoNumberName = extractAutoNumberName(sectionField.default_value);
|
|
67
|
+
if (autoNumberName) {
|
|
68
|
+
autoNumberFields.push({
|
|
69
|
+
code: sectionField.code,
|
|
70
|
+
auto_number_name: autoNumberName
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
// 处理普通字段
|
|
77
|
+
if (isAutoNumberField(field) && permissions[field.code] === "editable") {
|
|
78
|
+
const autoNumberName = extractAutoNumberName(field.default_value);
|
|
79
|
+
if (autoNumberName) {
|
|
80
|
+
autoNumberFields.push({
|
|
81
|
+
code: field.code,
|
|
82
|
+
auto_number_name: autoNumberName
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return autoNumberFields;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
router.post('/api/workflow/v2/auto_number', requireAuthentication, async function (req, res) {
|
|
93
|
+
try {
|
|
94
|
+
const userSession = req.user;
|
|
95
|
+
const userId = userSession.userId;
|
|
96
|
+
const { instanceId } = req.body;
|
|
97
|
+
|
|
98
|
+
if (!instanceId) {
|
|
99
|
+
return res.status(200).send({
|
|
100
|
+
error: '缺少必填参数: instanceId'
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 获取 instance 信息
|
|
105
|
+
const instancesCollection = await objectql.getObject('instances');
|
|
106
|
+
const instance = await instancesCollection.findOne(instanceId);
|
|
107
|
+
|
|
108
|
+
if (!instance) {
|
|
109
|
+
return res.status(200).send({
|
|
110
|
+
error: '未找到该实例'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 获取当前用户的 approve
|
|
115
|
+
const currentApprove = getUserApprove({ instance, userId });
|
|
116
|
+
|
|
117
|
+
if (!currentApprove) {
|
|
118
|
+
return res.status(200).send({
|
|
119
|
+
error: '未找到当前用户的审批节点'
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 如果是传阅类型,则不处理自动编号
|
|
124
|
+
if (currentApprove.type === 'cc') {
|
|
125
|
+
return res.status(200).send({
|
|
126
|
+
success: true,
|
|
127
|
+
message: '传阅节点不处理自动编号'
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 查找当前 trace 和 approve 的位置
|
|
132
|
+
let traceIndex = -1;
|
|
133
|
+
let approveIndex = -1;
|
|
134
|
+
|
|
135
|
+
for (let i = 0; i < instance.traces.length; i++) {
|
|
136
|
+
const trace = instance.traces[i];
|
|
137
|
+
if (trace.approves && trace.approves.length > 0) {
|
|
138
|
+
const index = trace.approves.findIndex(app => app._id === currentApprove._id);
|
|
139
|
+
if (index !== -1) {
|
|
140
|
+
traceIndex = i;
|
|
141
|
+
approveIndex = index;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (traceIndex === -1 || approveIndex === -1) {
|
|
148
|
+
return res.status(200).send({
|
|
149
|
+
error: '未找到对应的 trace 和 approve'
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const currentTrace = instance.traces[traceIndex];
|
|
154
|
+
|
|
155
|
+
// 获取当前 step
|
|
156
|
+
const flowsCollection = await objectql.getObject('flows');
|
|
157
|
+
const flow = await flowsCollection.findOne(instance.flow);
|
|
158
|
+
|
|
159
|
+
if (!flow) {
|
|
160
|
+
return res.status(200).send({
|
|
161
|
+
error: '未找到对应的流程'
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let step = null;
|
|
166
|
+
if (flow.current && flow.current.steps) {
|
|
167
|
+
step = flow.current.steps.find(s => s._id === currentTrace.step);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!step && flow.historys) {
|
|
171
|
+
for (const h of flow.historys) {
|
|
172
|
+
step = h.steps.find(s => s._id === currentTrace.step);
|
|
173
|
+
if (step) break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!step) {
|
|
178
|
+
return res.status(200).send({
|
|
179
|
+
error: '未找到对应的步骤'
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 获取可编辑的自动编号字段
|
|
184
|
+
const autoNumberFields = await getEditableAutoNumberFields(step, instance.form, instance.form_version);
|
|
185
|
+
|
|
186
|
+
if (autoNumberFields.length === 0) {
|
|
187
|
+
return res.status(200).send({
|
|
188
|
+
success: true,
|
|
189
|
+
message: '当前步骤没有可编辑的自动编号字段'
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 为每个自动编号字段生成编号
|
|
194
|
+
const result = {};
|
|
195
|
+
const updateObj = {};
|
|
196
|
+
|
|
197
|
+
for (const field of autoNumberFields) {
|
|
198
|
+
// 检查字段是否已有值
|
|
199
|
+
if (currentApprove.values && currentApprove.values[field.code]) {
|
|
200
|
+
result[field.code] = currentApprove.values[field.code];
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 生成自动编号
|
|
205
|
+
const autoNumber = await instanceNumberBuilder(instance.space, field.auto_number_name);
|
|
206
|
+
|
|
207
|
+
if (autoNumber && autoNumber._error) {
|
|
208
|
+
throw autoNumber._error;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
result[field.code] = autoNumber;
|
|
212
|
+
updateObj[`traces.${traceIndex}.approves.${approveIndex}.values.${field.code}`] = autoNumber;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 更新 approve 的 values
|
|
216
|
+
if (Object.keys(updateObj).length > 0) {
|
|
217
|
+
await instancesCollection.update(instanceId, updateObj);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
res.status(200).send({
|
|
221
|
+
success: true,
|
|
222
|
+
fields: result
|
|
223
|
+
});
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.error(error);
|
|
226
|
+
res.status(200).send({
|
|
227
|
+
error: error.message
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
exports.default = router;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { requireAuthentication } = require("@steedos/auth");
|
|
4
|
+
const { getObject } = require('@steedos/objectql')
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
|
|
7
|
+
router.delete('/api/workflow/v2/attachment/:instanceId/:id', requireAuthentication, async function (req, res) {
|
|
8
|
+
try {
|
|
9
|
+
const { instanceId, id} = req.params;
|
|
10
|
+
await getObject('cfs_instances_filerecord').delete(id);
|
|
11
|
+
res.status(200).send({
|
|
12
|
+
status: 0
|
|
13
|
+
})
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error(error);
|
|
16
|
+
res.status(200).send({
|
|
17
|
+
error: error.message
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
exports.default = router;
|
|
@@ -30,6 +30,8 @@ router.post('/api/workflow/v2/set_have_read', requireAuthentication, async funct
|
|
|
30
30
|
success: true
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
+
|
|
34
|
+
|
|
33
35
|
const db = {
|
|
34
36
|
instances: await getCollection('instances'),
|
|
35
37
|
instance_tasks: await getCollection('instance_tasks')
|
|
@@ -40,7 +42,8 @@ router.post('/api/workflow/v2/set_have_read', requireAuthentication, async funct
|
|
|
40
42
|
fields: {
|
|
41
43
|
"instance": 1,
|
|
42
44
|
"is_read": 1,
|
|
43
|
-
"type": 1
|
|
45
|
+
"type": 1,
|
|
46
|
+
space: 1
|
|
44
47
|
}
|
|
45
48
|
});
|
|
46
49
|
if(!instance_task){
|
|
@@ -48,6 +51,21 @@ router.post('/api/workflow/v2/set_have_read', requireAuthentication, async funct
|
|
|
48
51
|
success: true
|
|
49
52
|
});
|
|
50
53
|
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
// 更新当前用户相关的通知为已读
|
|
57
|
+
const notificationsObj = objectql.getObject('notifications');
|
|
58
|
+
await notificationsObj.directUpdateMany([
|
|
59
|
+
['owner', '=', userId],
|
|
60
|
+
['is_read', '!=', true],
|
|
61
|
+
['related_to.o', '=', 'instances'],
|
|
62
|
+
['related_to.ids', '=', instance_task.instance]
|
|
63
|
+
], {
|
|
64
|
+
is_read: true
|
|
65
|
+
});
|
|
66
|
+
console.log(`b6-microservice.broadcast`, {name: '$notification.users', data: {tenantId: instance_task.space, users: [userId], message: null}});
|
|
67
|
+
await broker.call('b6-microservice.broadcast', {name: '$notification.users', data: {tenantId: instance_task.space, users: [userId], message: null}})
|
|
68
|
+
|
|
51
69
|
if (instance_task.is_read){
|
|
52
70
|
return res.status(200).send({
|
|
53
71
|
success: true
|
|
@@ -63,7 +81,7 @@ router.post('/api/workflow/v2/set_have_read', requireAuthentication, async funct
|
|
|
63
81
|
}
|
|
64
82
|
else{
|
|
65
83
|
await set_approve_have_read(ins._id, myApprove.trace, myApprove._id, { userId });
|
|
66
|
-
}
|
|
84
|
+
}
|
|
67
85
|
return res.status(200).send({
|
|
68
86
|
success: true
|
|
69
87
|
});
|
|
@@ -450,6 +450,7 @@ const FlowversionAPI = {
|
|
|
450
450
|
let query = req.query;
|
|
451
451
|
let instance_id = query.instance_id;
|
|
452
452
|
let direction = query.direction || 'TD';
|
|
453
|
+
let noAutoFit = query.noAutoFit === '1' || query.noAutoFit === 'true';
|
|
453
454
|
const allowDirections = ['TB', 'BT', 'RL', 'LR', 'TD'];
|
|
454
455
|
if (!_.includes(allowDirections, direction)) {
|
|
455
456
|
return this.writeResponse(res, 500, "Invalid direction. The value of direction should be in ['TB', 'BT', 'RL', 'LR', 'TD']");
|
|
@@ -619,6 +620,7 @@ const FlowversionAPI = {
|
|
|
619
620
|
});
|
|
620
621
|
$(function(){
|
|
621
622
|
var graphNodes = ${JSON.stringify(graphSyntax)};
|
|
623
|
+
var noAutoFit = ${noAutoFit ? 'true' : 'false'};
|
|
622
624
|
//方便前面可以通过调用mermaid.currentNodes调式,特意增加currentNodes属性。
|
|
623
625
|
mermaid.currentNodes = graphNodes;
|
|
624
626
|
var graphSyntax = graphNodes.join("\\n");
|
|
@@ -634,12 +636,30 @@ const FlowversionAPI = {
|
|
|
634
636
|
callback(id);
|
|
635
637
|
}
|
|
636
638
|
bindFunctions(element[0]);
|
|
639
|
+
|
|
640
|
+
// 修复SVG左侧内容被裁剪的问题
|
|
641
|
+
if(!noAutoFit) {
|
|
642
|
+
setTimeout(function() {
|
|
643
|
+
var svg = $("svg");
|
|
644
|
+
if(svg.length > 0) {
|
|
645
|
+
// 关键修复:允许SVG溢出显示,解决左侧节点被裁剪的问题
|
|
646
|
+
svg.css('overflow', 'visible');
|
|
647
|
+
}
|
|
648
|
+
}, 50);
|
|
649
|
+
}
|
|
637
650
|
};
|
|
638
651
|
mermaid.render(id, graphSyntax, insertSvg, element[0]);
|
|
652
|
+
|
|
653
|
+
var currentZoom = 1;
|
|
639
654
|
var zoomSVG = function(zoom){
|
|
640
|
-
var
|
|
641
|
-
|
|
642
|
-
|
|
655
|
+
var svg = $("svg");
|
|
656
|
+
currentZoom = currentZoom * zoom;
|
|
657
|
+
var baseWidth = svg.data('baseWidth') || svg.width();
|
|
658
|
+
if(!svg.data('baseWidth')) {
|
|
659
|
+
svg.data('baseWidth', baseWidth);
|
|
660
|
+
}
|
|
661
|
+
var newWidth = baseWidth * currentZoom;
|
|
662
|
+
svg.css("maxWidth",newWidth + "px").width(newWidth);
|
|
643
663
|
}
|
|
644
664
|
//支持鼠标滚轮缩放画布
|
|
645
665
|
$(window).on("mousewheel",function(event){
|
|
@@ -11,6 +11,8 @@ const { requireAuthentication } = require("@steedos/auth");
|
|
|
11
11
|
const _ = require('lodash');
|
|
12
12
|
const objectql = require('@steedos/objectql');
|
|
13
13
|
const UUFlowManager = require('../manager/uuflow_manager');
|
|
14
|
+
const HandlersManager = require('../manager/handlers_manager');
|
|
15
|
+
const WorkflowManager = require('../manager/workflow_manager');
|
|
14
16
|
|
|
15
17
|
const getFlowVersion = (flow, flowVersionId) => {
|
|
16
18
|
if (flow.current._id === flowVersionId) {
|
|
@@ -53,7 +55,7 @@ const isSkipStep = function (instance, step) {
|
|
|
53
55
|
return _.includes(instance.skip_steps, step._id)
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
const getNextSteps = async (flow, flowVersionId, instance, currentStep, judge, autoFormDoc, fields, showSkipStep) => {
|
|
58
|
+
const getNextSteps = async (flow, flowVersionId, instance, currentStep, judge, autoFormDoc, fields, showSkipStep, userId) => {
|
|
57
59
|
if (!currentStep)
|
|
58
60
|
return;
|
|
59
61
|
|
|
@@ -149,42 +151,39 @@ const getNextSteps = async (flow, flowVersionId, instance, currentStep, judge, a
|
|
|
149
151
|
//去除重复
|
|
150
152
|
nextSteps = _.compact(_.uniqBy(nextSteps, 'id'));
|
|
151
153
|
|
|
152
|
-
|
|
153
|
-
for (const nextStep of nextSteps) {
|
|
154
|
+
const conditionResults = await Promise.all(nextSteps.map(async (nextStep) => {
|
|
154
155
|
if (nextStep.step_type == "condition") {
|
|
155
|
-
|
|
156
|
-
|
|
156
|
+
let currentJudge = judge;
|
|
157
|
+
if (!currentJudge && nextStep.step_type == 'sign') {
|
|
158
|
+
currentJudge = 'approved';
|
|
157
159
|
}
|
|
158
|
-
|
|
159
|
-
condition_next_steps = condition_next_steps.concat(conditionSteps);
|
|
160
|
+
return await getNextSteps(flow, flowVersionId, instance, nextStep, currentJudge, autoFormDoc, fields, showSkipStep, userId);
|
|
160
161
|
}
|
|
161
|
-
|
|
162
|
+
return [];
|
|
163
|
+
}));
|
|
164
|
+
const condition_next_steps = _.flatten(conditionResults);
|
|
162
165
|
|
|
163
166
|
nextSteps = nextSteps.concat(condition_next_steps);
|
|
164
167
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
for (const nextStep of nextSteps) {
|
|
168
|
+
const skipStepPromises = nextSteps.map(async (nextStep) => {
|
|
168
169
|
if (nextStep.step_type != "condition") {
|
|
169
170
|
if (!showSkipStep && isSkipStep(instance, nextStep)) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
let nextStepsAfterSkipStep = await getNextSteps(flow, flowVersionId, instance, nextStep, judge, autoFormDoc, fields, showSkipStep);
|
|
174
|
-
let stepsWithOutCurrentStep = [];
|
|
175
|
-
for (const s of nextStepsAfterSkipStep) {
|
|
176
|
-
if (currentStep.id == s.id) {
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
stepsWithOutCurrentStep.push(s);
|
|
171
|
+
let currentJudge = judge;
|
|
172
|
+
if (!currentJudge && nextStep.step_type == 'sign') {
|
|
173
|
+
currentJudge = 'approved';
|
|
180
174
|
}
|
|
175
|
+
let nextStepsAfterSkipStep = await getNextSteps(flow, flowVersionId, instance, nextStep, currentJudge, autoFormDoc, fields, showSkipStep, userId);
|
|
181
176
|
// 后续步骤不应包含当前步骤
|
|
182
|
-
|
|
177
|
+
return nextStepsAfterSkipStep.filter(s => currentStep.id != s.id);
|
|
183
178
|
} else {
|
|
184
|
-
|
|
179
|
+
return [nextStep];
|
|
185
180
|
}
|
|
186
181
|
}
|
|
187
|
-
|
|
182
|
+
return [];
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const nestedSteps = await Promise.all(skipStepPromises);
|
|
186
|
+
let rev_nextSteps = _.flatten(nestedSteps);
|
|
188
187
|
|
|
189
188
|
//去除重复
|
|
190
189
|
rev_nextSteps = _.compact(_.uniqBy(rev_nextSteps, 'id'));
|
|
@@ -193,17 +192,98 @@ const getNextSteps = async (flow, flowVersionId, instance, currentStep, judge, a
|
|
|
193
192
|
if (currentStep.step_type == "counterSign" && rev_nextSteps.length > 1 && !currentStep.oneClickRejection) {
|
|
194
193
|
rev_nextSteps = [];
|
|
195
194
|
}
|
|
195
|
+
|
|
196
|
+
// 如果步骤的 always_enter_step (始终进入此步骤(默认选中,历史流程如果未配置,也是选中)) 值为false,那么需要运行脚本 enter_step_condition (步骤条件)
|
|
197
|
+
// 如结果中包含条件节点则需要继续计算条件节点下一步
|
|
198
|
+
const needRemoveSteps = {}, needAddSteps = {};
|
|
199
|
+
await Promise.all(rev_nextSteps.map(step =>
|
|
200
|
+
caculateNextStepsByEnterStepCondition(flow, flowVersionId, instance, step, autoFormDoc, fields, needRemoveSteps, needAddSteps, userId)
|
|
201
|
+
));
|
|
202
|
+
if (!_.isEmpty(needRemoveSteps) && !_.isEmpty(needAddSteps)) {
|
|
203
|
+
for (const id in needAddSteps) {
|
|
204
|
+
if (Object.hasOwnProperty.call(needAddSteps, id)) {
|
|
205
|
+
const index = rev_nextSteps.findIndex(item => item.id == id)
|
|
206
|
+
if (-1 === index) {
|
|
207
|
+
rev_nextSteps.push(needAddSteps[id])
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
for (const id in needRemoveSteps) {
|
|
212
|
+
if (Object.hasOwnProperty.call(needRemoveSteps, id)) {
|
|
213
|
+
const index = rev_nextSteps.findIndex(item => item.id == id)
|
|
214
|
+
if (-1 != index) {
|
|
215
|
+
rev_nextSteps.splice(index, 1)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
var conditionNextSteps = new Array();
|
|
220
|
+
|
|
221
|
+
for (const [idx, nextStep] of rev_nextSteps.entries()) {
|
|
222
|
+
if (nextStep.step_type == "condition") {
|
|
223
|
+
if(!judge && nextStep.step_type == 'sign'){
|
|
224
|
+
judge = 'approved'
|
|
225
|
+
}
|
|
226
|
+
conditionNextSteps = conditionNextSteps.concat(await getNextSteps(instance, nextStep, judge, autoFormDoc, fields, showSkipStep, userId));
|
|
227
|
+
// 移除条件节点
|
|
228
|
+
rev_nextSteps.splice(idx, 1)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
rev_nextSteps = rev_nextSteps.concat(conditionNextSteps);
|
|
232
|
+
//去除重复
|
|
233
|
+
rev_nextSteps = _.compact(_.uniqBy(rev_nextSteps, 'id'));
|
|
234
|
+
}
|
|
235
|
+
|
|
196
236
|
return rev_nextSteps;
|
|
197
237
|
}
|
|
198
238
|
|
|
239
|
+
async function caculateNextStepsByEnterStepCondition(flow, flowVersionId, instance, step, autoFormDoc, fields, needRemoveSteps = {}, needAddSteps = {}, userId) {
|
|
240
|
+
if (false === step.always_enter_step) {
|
|
241
|
+
const stepId = step.id;
|
|
242
|
+
// 获取step处理人
|
|
243
|
+
const users = await HandlersManager.getHandlers(instance._id, stepId, userId);
|
|
244
|
+
const spaceId = instance.space;
|
|
245
|
+
const approvers = await Promise.all(users.map(user => WorkflowManager.getFormulaUserObject(spaceId, user)));
|
|
246
|
+
|
|
247
|
+
let enterStepCondition = step.enter_step_condition;
|
|
248
|
+
const fieldValues = await UUFlowManager.initFormulaValues(instance, autoFormDoc);
|
|
249
|
+
fieldValues.step = {
|
|
250
|
+
...step,
|
|
251
|
+
approvers
|
|
252
|
+
};
|
|
253
|
+
fieldValues.loginUserId = userId; // 当前登录用户
|
|
254
|
+
if (!UUFlowManager.isAmisFormula(enterStepCondition)) {
|
|
255
|
+
throw new Error('Not Amis Formula, Contact Admin Please.')
|
|
256
|
+
}
|
|
257
|
+
let result = UUFlowManager.calculateConditionWithAmis(fieldValues, enterStepCondition);
|
|
258
|
+
if (false === result) {
|
|
259
|
+
needRemoveSteps[step.id] = step;
|
|
260
|
+
if (step.lines && step.lines.length > 0) {
|
|
261
|
+
const toStepIds = step.lines.map(function(s){ return s.to_step});
|
|
262
|
+
await Promise.all(toStepIds.map(async (stepId) => {
|
|
263
|
+
const s = getStep(flow, flowVersionId, stepId);
|
|
264
|
+
if (s) {
|
|
265
|
+
await caculateNextStepsByEnterStepCondition(flow, flowVersionId, instance, s, autoFormDoc, fields, needRemoveSteps, needAddSteps, userId);
|
|
266
|
+
}
|
|
267
|
+
}));
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
needAddSteps[step.id] = step;
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
needAddSteps[step.id] = step;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
199
278
|
router.post('/api/workflow/v2/nextStep', requireAuthentication, async function (req, res) {
|
|
200
279
|
try {
|
|
201
280
|
const userSession = req.user;
|
|
281
|
+
const { userId } = userSession;
|
|
202
282
|
const { instanceId, flowId, step, judge, values, flowVersionId } = req.body;
|
|
203
283
|
const flow = await objectql.getObject('flows').findOne(flowId);
|
|
204
284
|
const instance = await objectql.getObject('instances').findOne(instanceId);
|
|
205
285
|
|
|
206
|
-
const resNextSteps = await getNextSteps(flow, flowVersionId, instance, step, judge, values);
|
|
286
|
+
const resNextSteps = await getNextSteps(flow, flowVersionId, instance, step, judge, values, null, null, userId);
|
|
207
287
|
res.status(200).send({
|
|
208
288
|
'nextSteps': resNextSteps
|
|
209
289
|
});
|
|
@@ -228,17 +308,18 @@ const calcSteps = async function(instance, flow, flowVersionId, formFields, valu
|
|
|
228
308
|
|
|
229
309
|
_steps.push(step);
|
|
230
310
|
try {
|
|
231
|
-
var nextSteps = await getNextSteps(flow, flowVersionId, instance, step, judge, values);
|
|
232
|
-
|
|
311
|
+
var nextSteps = await getNextSteps(flow, flowVersionId, instance, step, judge, values, null, null, instance.submitter);
|
|
312
|
+
const recursiveResults = await Promise.all(nextSteps.map(async (nextStep) => {
|
|
233
313
|
try {
|
|
234
314
|
if (!_.includes(track, nextStep._id)) {
|
|
235
|
-
|
|
236
|
-
_steps = _steps.concat(__steps)
|
|
315
|
+
return await calcSteps(instance, flow, flowVersionId, formFields, values, nextStep, track.concat(_.map(_steps, '_id')));
|
|
237
316
|
}
|
|
238
317
|
} catch (e) {
|
|
239
318
|
console.log(e)
|
|
240
319
|
}
|
|
241
|
-
|
|
320
|
+
return [];
|
|
321
|
+
}));
|
|
322
|
+
_steps = _steps.concat(_.flatten(recursiveResults));
|
|
242
323
|
} catch (error) {
|
|
243
324
|
console.log(error)
|
|
244
325
|
}
|