@lambo-design-mobile/workflow-approve 1.0.0-beta.23 → 1.0.0-beta.25
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/CHANGELOG.md +15 -0
- package/api.js +25 -0
- package/package.json +3 -3
- package/src/FlowApproval.vue +192 -31
- package/src/SelectHandle.vue +50 -34
- package/src/WorkflowDiagram.vue +416 -37
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
## [1.0.0-beta.25](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/compare/@lambo-design-mobile/workflow-approve@1.0.0-beta.24...@lambo-design-mobile/workflow-approve@1.0.0-beta.25) (2026-03-18)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### ✨ Features | 新功能
|
|
6
|
+
|
|
7
|
+
* **审批组件:** 选人框查询问题修复 ([7712bab](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/commit/7712babcead1d9838ef17672370fe2a1a37f828d))
|
|
8
|
+
* **审批组件:** 增加驳回判断优化 ([90c270b](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/commit/90c270b13f13a44d83502c0081d885a8083be75e))
|
|
9
|
+
|
|
10
|
+
## [1.0.0-beta.24](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/compare/@lambo-design-mobile/workflow-approve@1.0.0-beta.23...@lambo-design-mobile/workflow-approve@1.0.0-beta.24) (2026-03-03)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### ✨ Features | 新功能
|
|
14
|
+
|
|
15
|
+
* **审批组件:** 增加驳回至会签指定节点功能;流程跟踪增加预测路线功能 ([a5a85b5](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/commit/a5a85b582a051cb2f88259a0e8680e6f033f6254))
|
|
16
|
+
|
|
2
17
|
## [1.0.0-beta.23](http://git.inspur.com/ecbh/lambo-design/lambo-design/-/compare/@lambo-design-mobile/workflow-approve@1.0.0-beta.22...@lambo-design-mobile/workflow-approve@1.0.0-beta.23) (2026-02-26)
|
|
3
18
|
|
|
4
19
|
|
package/api.js
CHANGED
|
@@ -140,6 +140,19 @@ export const printData = (applyId, instanceId, procId) => {
|
|
|
140
140
|
})
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
// 渲染未来节点(预测路线)
|
|
144
|
+
export const renderFutureNode = (processInstanceId, currentTaskId) => {
|
|
145
|
+
const params = {
|
|
146
|
+
processInstanceId,
|
|
147
|
+
currentTaskId
|
|
148
|
+
};
|
|
149
|
+
return ajax.request({
|
|
150
|
+
url: config.smartFlowServerContext + "/manage/processTodo/renderFutureNode",
|
|
151
|
+
method: 'get',
|
|
152
|
+
params: params,
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
143
156
|
export const getPrintData = (applyId, procId) => {
|
|
144
157
|
const params = {
|
|
145
158
|
applyId: applyId,
|
|
@@ -404,3 +417,15 @@ export const listAll = (procId, applyId, taskNode, taskId) => {
|
|
|
404
417
|
params: params,
|
|
405
418
|
});
|
|
406
419
|
}
|
|
420
|
+
|
|
421
|
+
export const getDoneDetail = (taskNode, applyId) => {
|
|
422
|
+
const params = {
|
|
423
|
+
taskNode: taskNode,
|
|
424
|
+
applyId: applyId,
|
|
425
|
+
};
|
|
426
|
+
return ajax.request({
|
|
427
|
+
url: config.smartFlowServerContext + "/manage/processDone/getDoneDetail",
|
|
428
|
+
method: 'get',
|
|
429
|
+
params: params,
|
|
430
|
+
});
|
|
431
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lambo-design-mobile/workflow-approve",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.25",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "lambo",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"standard-version": "^9.5.0",
|
|
17
17
|
"@lambo-design-mobile/lambo-scan-code": "^1.0.0-beta.1",
|
|
18
|
-
"@lambo-design-mobile/
|
|
19
|
-
"@lambo-design-mobile/
|
|
18
|
+
"@lambo-design-mobile/upload-file": "^1.0.0-beta.16",
|
|
19
|
+
"@lambo-design-mobile/shared": "^1.0.0-beta.21"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"release": "pnpm release-beta && git push --follow-tags && pnpm re-publish",
|
package/src/FlowApproval.vue
CHANGED
|
@@ -115,23 +115,16 @@
|
|
|
115
115
|
<!-- 底部按钮 等其他内容 -->
|
|
116
116
|
<div>
|
|
117
117
|
<van-popup v-model="popupShow" style="padding-top: 40px" closeable round position="bottom">
|
|
118
|
-
<template v-for="(button, index) of operationButtons">
|
|
118
|
+
<template v-for="(button, index) of operationButtons.moreOperations">
|
|
119
119
|
<div :class="button.class" @click="button.action">
|
|
120
120
|
{{ button.label }}
|
|
121
121
|
</div>
|
|
122
122
|
</template>
|
|
123
123
|
</van-popup>
|
|
124
124
|
<div class="custom-bottom-bar">
|
|
125
|
-
<div v-
|
|
126
|
-
|
|
127
|
-
{{ getSingleButtonText() }}
|
|
125
|
+
<div v-for="(button, index) of operationButtons.bottomButtons" :class="button.class" @click="button.action">
|
|
126
|
+
{{ button.label }}
|
|
128
127
|
</div>
|
|
129
|
-
<div class="bar-item" @click="onTrack">流程跟踪</div>
|
|
130
|
-
<div v-if="!isDetail && !revokeDelegateTask && !appointTask && !coSignVotingTask" class="bar-item approve" @click="audit('30')">{{ getAuditButtonStatus(30) }}</div>
|
|
131
|
-
<div v-if="!isDetail && revokeDelegateTask" class="bar-item approve" @click="audit('62')">{{ getAuditButtonStatus(62) }}</div>
|
|
132
|
-
<div v-if="!isDetail && appointTask" class="bar-item approve" @click="audit('61')">{{ getAuditButtonStatus(61) }}</div>
|
|
133
|
-
<div v-if="coSignVotingTask" class="bar-item disagree" @click="audit('32')">{{ getAuditButtonStatus(32) }}</div>
|
|
134
|
-
<div v-if="coSignVotingTask" class="bar-item approve" @click="audit('31')">{{ getAuditButtonStatus(31) }}</div>
|
|
135
128
|
</div>
|
|
136
129
|
</div>
|
|
137
130
|
<van-popup @click-close-icon="handleSelect('cancel')" v-model="nextNodePopupShow"
|
|
@@ -292,6 +285,9 @@
|
|
|
292
285
|
<van-radio :name='index'/>
|
|
293
286
|
</div>
|
|
294
287
|
</div>
|
|
288
|
+
<div v-if="showRejectToSelected" class="table-row">
|
|
289
|
+
<van-field @click="handleSelectRejectUser" label="选择驳回人员" readonly placeholder="点击选择" :value="rejectUsers.name"></van-field>
|
|
290
|
+
</div>
|
|
295
291
|
<div v-if="showProcessControl" class="reject-attribute">
|
|
296
292
|
<div>驳回的节点通过后:</div>
|
|
297
293
|
<van-radio-group v-model="rejectAttribute">
|
|
@@ -302,13 +298,21 @@
|
|
|
302
298
|
</van-radio-group>
|
|
303
299
|
</van-dialog>
|
|
304
300
|
<van-dialog v-model="rejectAttributesBoxShow" @confirm="processRejectNode()" @close="rejectAttributesDialogShowClose()"
|
|
305
|
-
title="
|
|
306
|
-
<div class="reject-attribute">
|
|
301
|
+
title="驳回设置" show-cancel-button>
|
|
302
|
+
<div v-if="showProcessControl" class="reject-attribute">
|
|
303
|
+
<div>驳回的节点通过后:</div>
|
|
307
304
|
<van-radio-group v-model="rejectAttribute">
|
|
308
305
|
<van-radio v-for="(item, index) in rejectAttributeList" :key="index" :name="item.type">{{ item.name }}</van-radio>
|
|
309
306
|
</van-radio-group>
|
|
310
307
|
</div>
|
|
308
|
+
<div v-if="showRejectToSelected" class="reject-attribute">
|
|
309
|
+
<van-field @click="handleSelectRejectUser" label="选择驳回人员" readonly placeholder="点击选择" :value="rejectUsers.name"></van-field>
|
|
310
|
+
</div>
|
|
311
311
|
</van-dialog>
|
|
312
|
+
<van-popup v-if="selectRejectUserPopupShow" v-model="selectRejectUserPopupShow" closeable round position="bottom"
|
|
313
|
+
:style="{ height: '80%' }">
|
|
314
|
+
<select-handle title="选择驳回人员" :multi-select="true" :procType="procType" :user-list="rejectUserList" @selectHandle="selectRejectUserHandle"></select-handle>
|
|
315
|
+
</van-popup>
|
|
312
316
|
</div>
|
|
313
317
|
</template>
|
|
314
318
|
|
|
@@ -330,7 +334,8 @@ import {
|
|
|
330
334
|
getUnapprovedListOfMultiNode,
|
|
331
335
|
getProcessTodoList,
|
|
332
336
|
getProcessAttributes,
|
|
333
|
-
listAll
|
|
337
|
+
listAll,
|
|
338
|
+
getDoneDetail
|
|
334
339
|
} from "../api";
|
|
335
340
|
import {Dialog, Toast} from "vant";
|
|
336
341
|
import UploadFile from '@lambo-design-mobile/upload-file';
|
|
@@ -353,9 +358,59 @@ export default {
|
|
|
353
358
|
showAuditOpinion(){
|
|
354
359
|
return this.handleButtons && (this.handleButtons.includes('auditOpinion') || this.handleButtons.includes('appointHandler') || this.handleButtons.includes('attachmentFile'))
|
|
355
360
|
},
|
|
356
|
-
operationButtons(){
|
|
357
|
-
|
|
358
|
-
const
|
|
361
|
+
operationButtons() {
|
|
362
|
+
// 流程跟踪按钮
|
|
363
|
+
const processTraceButton = {
|
|
364
|
+
id: 'processTrace',
|
|
365
|
+
label: '流程跟踪',
|
|
366
|
+
condition: this.handleButtons?.includes('processTrace'),
|
|
367
|
+
class: 'bar-item',
|
|
368
|
+
action: () => this.onTrack(),
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// 审批按钮
|
|
372
|
+
const auditButtonDefinitions = [
|
|
373
|
+
{
|
|
374
|
+
id: 'auditTo30',
|
|
375
|
+
label: this.getAuditButtonStatus(30),
|
|
376
|
+
condition: this.handleButtons?.includes('auditTo30') && !this.isDetail && !this.revokeDelegateTask && !this.appointTask && !this.coSignVotingTask,
|
|
377
|
+
class: 'bar-item approve',
|
|
378
|
+
action: () => this.audit('30'),
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
id: 'auditTo62',
|
|
382
|
+
label: this.getAuditButtonStatus(62),
|
|
383
|
+
condition: !this.isDetail && this.revokeDelegateTask,
|
|
384
|
+
class: 'bar-item approve',
|
|
385
|
+
action: () => this.audit('62'),
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
id: 'auditTo61',
|
|
389
|
+
label: this.getAuditButtonStatus(61),
|
|
390
|
+
condition: !this.isDetail && this.appointTask,
|
|
391
|
+
class: 'bar-item approve',
|
|
392
|
+
action: () => this.audit('61'),
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: 'auditTo32',
|
|
396
|
+
label: this.getAuditButtonStatus(32),
|
|
397
|
+
condition: this.coSignVotingTask,
|
|
398
|
+
class: 'bar-item disagree',
|
|
399
|
+
action: () => this.audit('32'),
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
id: 'auditTo31',
|
|
403
|
+
label: this.getAuditButtonStatus(31),
|
|
404
|
+
condition: this.coSignVotingTask,
|
|
405
|
+
class: 'bar-item approve',
|
|
406
|
+
action: () => this.audit('31'),
|
|
407
|
+
},
|
|
408
|
+
]
|
|
409
|
+
|
|
410
|
+
// 实际可以展示的审批按钮
|
|
411
|
+
const validAuditButtons = auditButtonDefinitions.filter(item => item.condition)
|
|
412
|
+
const showButtons = !this.appointTask && !this.rejectedTask && !this.revokeDelegateTask && !this.coSignVotingTask && !this.isDetail
|
|
413
|
+
const handleButtonsDefinitions = [
|
|
359
414
|
{
|
|
360
415
|
id: 'delegateTask',
|
|
361
416
|
label: this.getAuditButtonStatus(84),
|
|
@@ -363,6 +418,7 @@ export default {
|
|
|
363
418
|
this.handleButtons?.includes('delegateTask') &&
|
|
364
419
|
showButtons,
|
|
365
420
|
class: 'popup-option',
|
|
421
|
+
barClass: 'bar-item approve',
|
|
366
422
|
action: () => this.audit('84'),
|
|
367
423
|
},
|
|
368
424
|
{
|
|
@@ -372,6 +428,7 @@ export default {
|
|
|
372
428
|
this.handleButtons?.includes('addMultitaskInstance') &&
|
|
373
429
|
showButtons,
|
|
374
430
|
class: 'popup-option',
|
|
431
|
+
barClass: 'bar-item approve',
|
|
375
432
|
action: () => this.showUpdateMultitaskInstanceModal('81'),
|
|
376
433
|
},
|
|
377
434
|
{
|
|
@@ -381,6 +438,7 @@ export default {
|
|
|
381
438
|
this.handleButtons?.includes('reductionMultitaskInstance') &&
|
|
382
439
|
showButtons,
|
|
383
440
|
class: 'popup-option',
|
|
441
|
+
barClass: 'bar-item disagree',
|
|
384
442
|
action: () => this.showUpdateMultitaskInstanceModal('83'),
|
|
385
443
|
},
|
|
386
444
|
{
|
|
@@ -388,6 +446,7 @@ export default {
|
|
|
388
446
|
label: this.getAuditButtonStatus(70),
|
|
389
447
|
condition: this.handleButtons && this.handleButtons.includes('auditTo70') && showButtons,
|
|
390
448
|
class: 'popup-option',
|
|
449
|
+
barClass: 'bar-item disagree',
|
|
391
450
|
action: () => this.audit('70'),
|
|
392
451
|
},
|
|
393
452
|
{
|
|
@@ -395,6 +454,7 @@ export default {
|
|
|
395
454
|
label: this.getAuditButtonStatus(40),
|
|
396
455
|
condition: this.handleButtons && this.handleButtons.includes('auditTo40') && showButtons,
|
|
397
456
|
class: 'popup-option highlighted',
|
|
457
|
+
barClass: 'bar-item disagree',
|
|
398
458
|
action: () => this.audit('40'),
|
|
399
459
|
},
|
|
400
460
|
{
|
|
@@ -402,6 +462,7 @@ export default {
|
|
|
402
462
|
label: this.getAuditButtonStatus(90),
|
|
403
463
|
condition: this.handleButtons && this.handleButtons.includes('auditTo90') && showButtons,
|
|
404
464
|
class: 'popup-option',
|
|
465
|
+
barClass: 'bar-item disagree',
|
|
405
466
|
action: () => this.audit('90'),
|
|
406
467
|
},
|
|
407
468
|
{
|
|
@@ -409,6 +470,7 @@ export default {
|
|
|
409
470
|
label: this.getAuditButtonStatus(80),
|
|
410
471
|
condition: this.handleButtons && this.handleButtons.includes('auditTo80') && showButtons,
|
|
411
472
|
class: 'popup-option',
|
|
473
|
+
barClass: 'bar-item approve',
|
|
412
474
|
action: () => this.audit('80'),
|
|
413
475
|
},
|
|
414
476
|
{
|
|
@@ -416,6 +478,7 @@ export default {
|
|
|
416
478
|
label: this.getAuditButtonStatus(82),
|
|
417
479
|
condition: this.handleButtons && this.handleButtons.includes('auditTo82') && showButtons,
|
|
418
480
|
class: 'popup-option',
|
|
481
|
+
barClass: 'bar-item approve',
|
|
419
482
|
action: () => this.audit('82'),
|
|
420
483
|
},
|
|
421
484
|
{
|
|
@@ -423,10 +486,46 @@ export default {
|
|
|
423
486
|
label: this.getAuditButtonStatus(50),
|
|
424
487
|
condition: this.handleButtons && this.handleButtons.includes('auditTo50') && showButtons,
|
|
425
488
|
class: 'popup-option',
|
|
489
|
+
barClass: 'bar-item disagree',
|
|
426
490
|
action: () => this.audit('50'),
|
|
427
491
|
},
|
|
428
492
|
]
|
|
429
|
-
|
|
493
|
+
|
|
494
|
+
// 实际更多操作按钮
|
|
495
|
+
let moreOperations = handleButtonsDefinitions.filter(item => item.condition)
|
|
496
|
+
|
|
497
|
+
let moreButtons = []
|
|
498
|
+
let borrowedButton = null;
|
|
499
|
+
// 如果审批按钮为空,且更多操作里有按钮,则借调最后一个
|
|
500
|
+
if (validAuditButtons.length === 0 && moreOperations.length > 0) {
|
|
501
|
+
borrowedButton = moreOperations.pop();
|
|
502
|
+
borrowedButton.class = borrowedButton.barClass
|
|
503
|
+
}
|
|
504
|
+
if (!this.isDetail) {
|
|
505
|
+
if (moreOperations.length > 1) {
|
|
506
|
+
moreButtons.push({
|
|
507
|
+
id: 'onMore',
|
|
508
|
+
label: '更多',
|
|
509
|
+
condition: true,
|
|
510
|
+
class: 'bar-item',
|
|
511
|
+
action: () => this.onMore(),
|
|
512
|
+
})
|
|
513
|
+
} else if (moreOperations.length === 1) {
|
|
514
|
+
moreButtons.push({
|
|
515
|
+
id: moreOperations[0].id,
|
|
516
|
+
label: moreOperations[0].label,
|
|
517
|
+
condition: true,
|
|
518
|
+
class: 'bar-item',
|
|
519
|
+
action: () => moreOperations[0].action(),
|
|
520
|
+
})
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
const baseList = [...moreButtons, processTraceButton]
|
|
524
|
+
if (borrowedButton) {
|
|
525
|
+
baseList.splice(2, 0, borrowedButton);
|
|
526
|
+
}
|
|
527
|
+
const bottomButtons = [...baseList, ...validAuditButtons]
|
|
528
|
+
return { bottomButtons, moreOperations }
|
|
430
529
|
}
|
|
431
530
|
},
|
|
432
531
|
components: {ApprovalNodeCell, SelectNormalList, SelectOrganize, Tree, SelectHandle, UploadFile, SelectHandleCard},
|
|
@@ -562,6 +661,7 @@ export default {
|
|
|
562
661
|
permissionsScope: {},
|
|
563
662
|
selectOrganizePopupShow: false,
|
|
564
663
|
selectCandUserPopupShow: false,
|
|
664
|
+
selectRejectUserPopupShow: false,
|
|
565
665
|
selectUserPopupShow: false,
|
|
566
666
|
selectRolePopupShow: false,
|
|
567
667
|
selectReductionUserPopupShow: false,
|
|
@@ -575,20 +675,24 @@ export default {
|
|
|
575
675
|
organTreeType: '',
|
|
576
676
|
action: (checkResult) => { console.log('未定义回调方法') },
|
|
577
677
|
},
|
|
678
|
+
showRejectToSelected: false,
|
|
578
679
|
showProcessControl: false,
|
|
579
680
|
rejectAttributesBoxShow: false,
|
|
580
681
|
rejectAttribute: 'inSequence',
|
|
581
682
|
rejectAttributeList: [],
|
|
582
683
|
taskAuditUserList: [],
|
|
684
|
+
rejectUserList: [],
|
|
583
685
|
targetTaskNodeProcessControl: '',
|
|
686
|
+
rejectUsers: {
|
|
687
|
+
name: '全部办理人',
|
|
688
|
+
id: 'all'
|
|
689
|
+
},
|
|
584
690
|
};
|
|
585
691
|
},
|
|
586
692
|
methods: {
|
|
587
693
|
getAuditStatus,
|
|
588
694
|
initData() {
|
|
589
|
-
|
|
590
|
-
this.getHandleButtons();
|
|
591
|
-
}
|
|
695
|
+
this.getHandleButtons();
|
|
592
696
|
if (!this.isDetail) this.getTaskStatus();
|
|
593
697
|
this.getProcessHistory();
|
|
594
698
|
this.getNextNodes();
|
|
@@ -639,10 +743,10 @@ export default {
|
|
|
639
743
|
if (resp.data.code === '200') {
|
|
640
744
|
const todoData = resp.data.data.rows[0]
|
|
641
745
|
// 委派任务状态
|
|
642
|
-
self.revokeDelegateTask = todoData
|
|
643
|
-
self.appointTask = todoData
|
|
746
|
+
self.revokeDelegateTask = todoData?.delegateStatus ? todoData.delegateStatus === '10' : false
|
|
747
|
+
self.appointTask = todoData?.delegateStatus ? todoData.delegateStatus === '20' : false
|
|
644
748
|
// 判断节点是否是被驳回的节点
|
|
645
|
-
const taskExtensionAttributes = todoData
|
|
749
|
+
const taskExtensionAttributes = todoData?.taskExtensionAttributes ? JSON.parse(todoData.taskExtensionAttributes) : ''
|
|
646
750
|
if (taskExtensionAttributes && taskExtensionAttributes.rejectAttributes) {
|
|
647
751
|
const rejectAttributes = taskExtensionAttributes.rejectAttributes
|
|
648
752
|
if (rejectAttributes && rejectAttributes.type && rejectAttributes.type != 'inSequence') self.rejectedTask = true
|
|
@@ -654,8 +758,8 @@ export default {
|
|
|
654
758
|
getNodeData(this.procId, this.taskNode).then(resp => {
|
|
655
759
|
if (resp.data.code === '200') {
|
|
656
760
|
const data = resp.data.data[0]
|
|
657
|
-
this.handleButtons = data.handleButtons ? data.handleButtons : ["processTrace", "auditHistory", "auditOpinion", "attachmentFile", "auditTo30", "auditTo70", "auditTo40", "auditTo90", "auditTo80", "auditTo82", "auditTo50"];
|
|
658
|
-
this.coSignVotingTask =
|
|
761
|
+
this.handleButtons = this.handleButtons ? this.handleButtons : data.handleButtons ? data.handleButtons : ["processTrace", "auditHistory", "auditOpinion", "attachmentFile", "auditTo30", "auditTo70", "auditTo40", "auditTo90", "auditTo80", "auditTo82", "auditTo50"];
|
|
762
|
+
this.coSignVotingTask = this.handleButtons && this.handleButtons.includes('coSignVoting')
|
|
659
763
|
this.nextAssigneeSelectionType = data.nextAssigneeSelectionType;
|
|
660
764
|
this.passName = data.handleName;
|
|
661
765
|
this.rejectName = data.rejectName;
|
|
@@ -1011,6 +1115,8 @@ export default {
|
|
|
1011
1115
|
procId: this.procId,
|
|
1012
1116
|
instanceId: this.instanceId,
|
|
1013
1117
|
taskNode: this.taskNode,
|
|
1118
|
+
taskId: this.taskId,
|
|
1119
|
+
predictButtonEnabled: this.handleButtons && this.handleButtons.includes('predictiveRoute'),
|
|
1014
1120
|
}
|
|
1015
1121
|
})
|
|
1016
1122
|
},
|
|
@@ -1051,13 +1157,13 @@ export default {
|
|
|
1051
1157
|
console.log('点击了审批:', auditResult);
|
|
1052
1158
|
|
|
1053
1159
|
if (this.approvalForm.auditOpinion === '' || this.approvalForm.auditOpinion == null) {
|
|
1054
|
-
if (!this.handleButtons || (this.handleButtons.includes('auditOpinion') &&
|
|
1160
|
+
if (!this.handleButtons || (this.handleButtons.includes('auditOpinion') && auditResult != '30' && auditResult != '31')) {
|
|
1055
1161
|
this.updateActiveTab()
|
|
1056
1162
|
this.approvalError = true
|
|
1057
1163
|
Toast({message: '请输入审批意见', duration: '500'});
|
|
1058
1164
|
return
|
|
1059
1165
|
} else {
|
|
1060
|
-
this.
|
|
1166
|
+
this.approvalForm.auditOpinion = this.getAuditButtonStatus(auditResult);
|
|
1061
1167
|
}
|
|
1062
1168
|
}
|
|
1063
1169
|
|
|
@@ -1155,7 +1261,15 @@ export default {
|
|
|
1155
1261
|
self.executionCompleted(true, null, null, auditResult, self.taskId)
|
|
1156
1262
|
}, 1000)
|
|
1157
1263
|
} else {
|
|
1158
|
-
|
|
1264
|
+
// 前序节点只有一个会签节点才可选择驳回人员
|
|
1265
|
+
self.showRejectToSelected = result.data.length === 1 && result.data[0].handleButtons?.includes('rejectToSelected')
|
|
1266
|
+
self.nodeList = Array.isArray(result.data) ? result.data : []
|
|
1267
|
+
self.radio = 0
|
|
1268
|
+
if (self.showRejectToSelected && !self.handleButtons?.includes('rejectProcessControl')) {
|
|
1269
|
+
self.rejectAttributesBoxShow = true
|
|
1270
|
+
} else {
|
|
1271
|
+
self.handleButtons && self.handleButtons.includes('rejectProcessControl') ? self.getProcessAttributes() : self.auditRequest(self.auditParams)
|
|
1272
|
+
}
|
|
1159
1273
|
}
|
|
1160
1274
|
})
|
|
1161
1275
|
}).catch(() => {
|
|
@@ -1192,6 +1306,10 @@ export default {
|
|
|
1192
1306
|
getProcessAttributes(self.procId, self.taskNode).then(resp => {
|
|
1193
1307
|
if (resp.data.code === '200') {
|
|
1194
1308
|
if ((self.auditParams.auditResult == '70' && !resp.data.data.jumpFirstNode) || (self.auditParams.auditResult == '40' && (resp.data.data.multiNode || resp.data.data.preNodeGateway))) {
|
|
1309
|
+
if (self.showRejectToSelected && self.nodeList.length > 0) {
|
|
1310
|
+
self.rejectAttributesBoxShow = true
|
|
1311
|
+
return
|
|
1312
|
+
}
|
|
1195
1313
|
self.auditRequest(self.auditParams)
|
|
1196
1314
|
return
|
|
1197
1315
|
} else {
|
|
@@ -1237,10 +1355,9 @@ export default {
|
|
|
1237
1355
|
)
|
|
1238
1356
|
}
|
|
1239
1357
|
}
|
|
1358
|
+
self.showProcessControl = self.rejectAttributeList.length > 0 && self.handleButtons && self.handleButtons.includes('rejectProcessControl')
|
|
1240
1359
|
if (self.auditParams.auditResult == '70' || self.auditParams.auditResult == '40') {
|
|
1241
1360
|
self.rejectAttributesBoxShow = self.rejectAttributeList.length > 0 && self.handleButtons && self.handleButtons.includes('rejectProcessControl')
|
|
1242
|
-
} else if (self.auditParams.auditResult == '90'){
|
|
1243
|
-
self.showProcessControl = self.rejectAttributeList.length > 0 && self.handleButtons && self.handleButtons.includes('rejectProcessControl')
|
|
1244
1361
|
}
|
|
1245
1362
|
}
|
|
1246
1363
|
}).catch((err) => {
|
|
@@ -1261,6 +1378,9 @@ export default {
|
|
|
1261
1378
|
code: self.auditResult,
|
|
1262
1379
|
name: self.getAuditStatus(self.auditResult).text
|
|
1263
1380
|
}
|
|
1381
|
+
if (self.rejectUsers.id !== 'all'){
|
|
1382
|
+
auditParams.rejectUsers = self.rejectUsers.id
|
|
1383
|
+
}
|
|
1264
1384
|
self.showToastLoading('处理审批中');
|
|
1265
1385
|
//确认执行审批逻辑
|
|
1266
1386
|
audit(auditParams).then((resp) => { // 使用箭头函数
|
|
@@ -1339,9 +1459,10 @@ export default {
|
|
|
1339
1459
|
}
|
|
1340
1460
|
},
|
|
1341
1461
|
selectNode(currentRow, index) {
|
|
1342
|
-
console.log(currentRow);
|
|
1343
1462
|
this.radio = index
|
|
1344
1463
|
this.targetTaskNodeProcessControl = currentRow.processControl
|
|
1464
|
+
this.rejectUsers = { name: '全部办理人', id: 'all' }
|
|
1465
|
+
this.showRejectToSelected = currentRow.rejectToSelected
|
|
1345
1466
|
},
|
|
1346
1467
|
processJumpSpecifiedNode() {
|
|
1347
1468
|
const self = this;
|
|
@@ -1355,8 +1476,42 @@ export default {
|
|
|
1355
1476
|
self.auditParams.rejectAttribute = self.rejectAttribute ? self.rejectAttribute : 'inSequence'
|
|
1356
1477
|
self.auditRequest(self.auditParams)
|
|
1357
1478
|
},
|
|
1479
|
+
handleSelectRejectUser() {
|
|
1480
|
+
let self = this
|
|
1481
|
+
getDoneDetail(self.nodeList[self.radio].taskNode, self.applyId).then(function (resp) {
|
|
1482
|
+
if (resp.data.code === '200') {
|
|
1483
|
+
const data = resp.data.data.rows
|
|
1484
|
+
const latestAudit = data.reduce((max, current) => {
|
|
1485
|
+
return current.auditDate > max.auditDate ? current : max;
|
|
1486
|
+
});
|
|
1487
|
+
const seenNames = new Set();
|
|
1488
|
+
self.rejectUserList = data.filter(item => {
|
|
1489
|
+
if (item.parentExecutionId && latestAudit.parentExecutionId && item.parentExecutionId !== latestAudit.parentExecutionId) {
|
|
1490
|
+
return false;
|
|
1491
|
+
}
|
|
1492
|
+
if (seenNames.has(item.auditName)) {
|
|
1493
|
+
return false;
|
|
1494
|
+
}
|
|
1495
|
+
seenNames.add(item.auditName);
|
|
1496
|
+
return true;
|
|
1497
|
+
}).map(item => {
|
|
1498
|
+
return {
|
|
1499
|
+
...item,
|
|
1500
|
+
userName: item.auditName,
|
|
1501
|
+
userId: item.auditId,
|
|
1502
|
+
organName: item.auditUserOrgan?.split(':')[1]
|
|
1503
|
+
}
|
|
1504
|
+
});
|
|
1505
|
+
self.selectRejectUserPopupShow = true
|
|
1506
|
+
} else {
|
|
1507
|
+
Toast.fail(resp.data.message)
|
|
1508
|
+
}
|
|
1509
|
+
})
|
|
1510
|
+
},
|
|
1358
1511
|
rejectAttributesDialogShowClose(){
|
|
1359
1512
|
//重置
|
|
1513
|
+
this.showRejectToSelected = false
|
|
1514
|
+
this.showProcessControl = false
|
|
1360
1515
|
this.rejectAttributesBoxShow = false
|
|
1361
1516
|
this.auditResult = '';
|
|
1362
1517
|
this.radio = '';
|
|
@@ -1451,6 +1606,12 @@ export default {
|
|
|
1451
1606
|
this.handleSelect('select')
|
|
1452
1607
|
}
|
|
1453
1608
|
this.selectUserPopupShow = false;
|
|
1609
|
+
this.selectCandUserPopupShow = false;
|
|
1610
|
+
},
|
|
1611
|
+
selectRejectUserHandle(checkResult) {
|
|
1612
|
+
this.rejectUsers.name = checkResult.map(item => item.userName).join(',')
|
|
1613
|
+
this.rejectUsers.id = checkResult.map(item => item.userId).join(',')
|
|
1614
|
+
this.selectRejectUserPopupShow = false;
|
|
1454
1615
|
},
|
|
1455
1616
|
selectPositionHandle(handle, result) {
|
|
1456
1617
|
if (handle === 'select') {
|
package/src/SelectHandle.vue
CHANGED
|
@@ -144,11 +144,15 @@ export default {
|
|
|
144
144
|
this.personList.rows = this.userRangeList
|
|
145
145
|
this.personList.total = this.userRangeList.length
|
|
146
146
|
return
|
|
147
|
-
}
|
|
148
|
-
this.useUserTransferRange = false
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
}
|
|
148
|
+
this.useUserTransferRange = false
|
|
149
|
+
this.personList.total = 0;
|
|
150
|
+
this.personList.rows = [];
|
|
151
|
+
this.finished = false;
|
|
152
|
+
this.loading = false;
|
|
153
|
+
if (this.orgList && this.orgList.length > 0){
|
|
154
|
+
this.organizeIdList = this.orgList.map(item => item.orgId)
|
|
155
|
+
this.orgTreeType = this.organTreeType;
|
|
152
156
|
this.searchForm = {
|
|
153
157
|
...this.searchForm,
|
|
154
158
|
orgTreeType: this.organTreeType,
|
|
@@ -180,33 +184,43 @@ export default {
|
|
|
180
184
|
})
|
|
181
185
|
}
|
|
182
186
|
},
|
|
183
|
-
handleLoad() {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
handleLoad() {
|
|
188
|
+
if (this.loading || this.finished) {
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
console.log("触发加载")
|
|
192
|
+
this.loading = true;
|
|
193
|
+
if (this.useUserTransferRange){
|
|
194
|
+
this.personList.rows = this.searchForm.userName ? this.userRangeList.map(item => item.userName === this.searchForm.userName) : this.userRangeList
|
|
195
|
+
this.personList.total = this.personList.rows.length;
|
|
196
|
+
this.finished = true;
|
|
197
|
+
this.loading = false;
|
|
198
|
+
return
|
|
199
|
+
}
|
|
192
200
|
const offset = this.personList.rows.length;
|
|
193
201
|
const limit = 10;
|
|
194
202
|
|
|
195
|
-
getUserList(offset, limit, this.searchForm).then(res => {
|
|
196
|
-
const result = res.data;
|
|
197
|
-
if (result.code === "1") {
|
|
198
|
-
//返回的数据添加到 personList 中
|
|
199
|
-
|
|
200
|
-
this.personList.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
203
|
+
getUserList(offset, limit, this.searchForm).then(res => {
|
|
204
|
+
const result = res.data;
|
|
205
|
+
if (result.code === "1") {
|
|
206
|
+
//返回的数据添加到 personList 中
|
|
207
|
+
const newRows = Array.isArray(result.data.rows) ? result.data.rows : [];
|
|
208
|
+
this.personList.rows = this.personList.rows.concat(newRows);
|
|
209
|
+
const total = Number(result.data.total);
|
|
210
|
+
if (!Number.isNaN(total)) {
|
|
211
|
+
this.personList.total = total;
|
|
212
|
+
}
|
|
213
|
+
const noMoreByPage = newRows.length < limit;
|
|
214
|
+
const noMoreByTotal = this.personList.total > 0 && this.personList.rows.length >= this.personList.total;
|
|
215
|
+
this.finished = noMoreByPage || noMoreByTotal;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this.loading = false;
|
|
219
|
+
|
|
220
|
+
}).catch(error => {
|
|
221
|
+
console.error('Error fetching data:', error);
|
|
222
|
+
this.loading = false;
|
|
223
|
+
});
|
|
210
224
|
},
|
|
211
225
|
|
|
212
226
|
extractUsers(permScope) {
|
|
@@ -240,11 +254,13 @@ export default {
|
|
|
240
254
|
resetSearch() {
|
|
241
255
|
this.initSearch()
|
|
242
256
|
},
|
|
243
|
-
resetAndLoadPersonList() {
|
|
244
|
-
this.personList.total = 0;
|
|
245
|
-
this.personList.rows = [];
|
|
246
|
-
this.
|
|
247
|
-
|
|
257
|
+
resetAndLoadPersonList() {
|
|
258
|
+
this.personList.total = 0;
|
|
259
|
+
this.personList.rows = [];
|
|
260
|
+
this.finished = false;
|
|
261
|
+
this.loading = false;
|
|
262
|
+
this.handleLoad();
|
|
263
|
+
},
|
|
248
264
|
handleSelect(handle) {
|
|
249
265
|
if (handle === 'select') {
|
|
250
266
|
// 触发自定义事件 'selectHandle',并传递 this.checkResult
|
package/src/WorkflowDiagram.vue
CHANGED
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
<div class="containers">
|
|
11
11
|
<div ref="canvas" class="canvas"></div>
|
|
12
12
|
</div>
|
|
13
|
+
<div v-if="predictButtonEnabled" class="predict-toggle">
|
|
14
|
+
<van-button
|
|
15
|
+
type="primary"
|
|
16
|
+
size="small"
|
|
17
|
+
:loading="loadingFutureNodes"
|
|
18
|
+
@click="toggleNodeDisplay"
|
|
19
|
+
>
|
|
20
|
+
{{ showAllNodes ? '显示预测路线' : '显示设计路线' }}
|
|
21
|
+
</van-button>
|
|
22
|
+
</div>
|
|
13
23
|
|
|
14
24
|
<!--审批流程追踪-->
|
|
15
25
|
<div class="title-info">
|
|
@@ -74,7 +84,8 @@
|
|
|
74
84
|
</template>
|
|
75
85
|
<script>
|
|
76
86
|
import Viewer from "bpmn-js/lib/Viewer";
|
|
77
|
-
import {
|
|
87
|
+
import { Toast } from "vant";
|
|
88
|
+
import {getHisAudit, getPrintData, printData, renderFutureNode, getNodeData} from "../api";
|
|
78
89
|
import ApprovalNodeCell from "./ApprovalNodeCell.vue";
|
|
79
90
|
import { getAuditStatus } from './js/global';
|
|
80
91
|
import FlowNodeCell from "./FlowNodeCell.vue"; // 根据路径修改
|
|
@@ -90,6 +101,11 @@ export default {
|
|
|
90
101
|
type: String,
|
|
91
102
|
required: false,
|
|
92
103
|
},
|
|
104
|
+
taskId: {
|
|
105
|
+
type: String,
|
|
106
|
+
required: false,
|
|
107
|
+
default: '',
|
|
108
|
+
},
|
|
93
109
|
taskNode: {
|
|
94
110
|
type: String,
|
|
95
111
|
required: false,
|
|
@@ -98,18 +114,56 @@ export default {
|
|
|
98
114
|
type: String,
|
|
99
115
|
required: true,
|
|
100
116
|
},
|
|
117
|
+
predictButtonEnabled: {
|
|
118
|
+
type: Boolean,
|
|
119
|
+
required: false,
|
|
120
|
+
},
|
|
101
121
|
},
|
|
102
122
|
mounted() {
|
|
123
|
+
if (this.predictButtonEnabled == null && this.taskNode){
|
|
124
|
+
getNodeData(this.procId, this.taskNode).then(resp => {
|
|
125
|
+
if (resp.data.code === '200') {
|
|
126
|
+
const data = resp.data.data[0]
|
|
127
|
+
this.predictButtonEnabled = data.handleButtons && data.handleButtons.includes("predictiveRoute")
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
}
|
|
103
131
|
this.getPrintData()
|
|
104
132
|
// this.getHisAudit()
|
|
105
133
|
this.onTrack()
|
|
106
134
|
},
|
|
135
|
+
beforeDestroy() {
|
|
136
|
+
this.removeTouchGesture();
|
|
137
|
+
if (this.bpmnViewer) {
|
|
138
|
+
this.bpmnViewer.destroy();
|
|
139
|
+
this.bpmnViewer = null;
|
|
140
|
+
}
|
|
141
|
+
},
|
|
107
142
|
data() {
|
|
108
143
|
return {
|
|
109
144
|
showTaskNodeDetail: false,
|
|
110
145
|
nodeDetailsList: [],
|
|
111
146
|
currentIndex: 0,
|
|
112
147
|
auditData: [],
|
|
148
|
+
tableData: [],
|
|
149
|
+
|
|
150
|
+
bpmnViewer: null,
|
|
151
|
+
diagramReady: false,
|
|
152
|
+
|
|
153
|
+
// 预测路线相关状态
|
|
154
|
+
showAllNodes: true,
|
|
155
|
+
loadingFutureNodes: false,
|
|
156
|
+
futureNodeData: null,
|
|
157
|
+
|
|
158
|
+
// 已办/待办集合(用于预测路线显示时保留已执行路径)
|
|
159
|
+
doneLightSet: new Set(),
|
|
160
|
+
doneTaskSet: new Set(),
|
|
161
|
+
donePointIdSet: new Set(),
|
|
162
|
+
doneNodeIdSet: new Set(),
|
|
163
|
+
todoPointIdSet: new Set(),
|
|
164
|
+
|
|
165
|
+
// 触摸手势事件句柄(用于重复初始化时清理)
|
|
166
|
+
touchGestureHandlers: null,
|
|
113
167
|
}
|
|
114
168
|
},
|
|
115
169
|
computed: {
|
|
@@ -125,8 +179,9 @@ export default {
|
|
|
125
179
|
getPrintData() {
|
|
126
180
|
getPrintData(this.applyId, this.procId).then(resp => {
|
|
127
181
|
if (resp.data.code === '200') {
|
|
128
|
-
|
|
129
|
-
this.
|
|
182
|
+
const data = Array.isArray(resp.data.data) ? resp.data.data : [];
|
|
183
|
+
this.tableData = data;
|
|
184
|
+
this.auditData = data;
|
|
130
185
|
}
|
|
131
186
|
})
|
|
132
187
|
},
|
|
@@ -137,16 +192,317 @@ export default {
|
|
|
137
192
|
}
|
|
138
193
|
})
|
|
139
194
|
},
|
|
140
|
-
|
|
195
|
+
|
|
196
|
+
// 切换“设计路线 / 预测路线”显示
|
|
197
|
+
async toggleNodeDisplay() {
|
|
198
|
+
if (this.showAllNodes) {
|
|
199
|
+
const success = await this.loadFutureNodes();
|
|
200
|
+
if (!success) return;
|
|
201
|
+
this.showAllNodes = false;
|
|
202
|
+
this.$emit('toggle-predictive', {showPredictive: true, futureNodeData: this.futureNodeData});
|
|
203
|
+
} else {
|
|
204
|
+
this.showAllNodesInDiagram();
|
|
205
|
+
this.showAllNodes = true;
|
|
206
|
+
this.$emit('toggle-predictive', {showPredictive: false, futureNodeData: null});
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
// 加载未来节点(预测路线)数据
|
|
211
|
+
async loadFutureNodes() {
|
|
212
|
+
if (!this.instanceId || !this.taskId) {
|
|
213
|
+
Toast('缺少必要参数:流程实例ID或当前任务ID');
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
if (!this.diagramReady) {
|
|
217
|
+
Toast('流程图尚未加载完成,请稍后重试');
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.loadingFutureNodes = true;
|
|
222
|
+
try {
|
|
223
|
+
const resp = await renderFutureNode(this.instanceId, this.taskId);
|
|
224
|
+
if (resp.data.code === '200' && resp.data.data) {
|
|
225
|
+
const futureData = resp.data.data;
|
|
226
|
+
const currentNodeId = futureData.currentNodeId;
|
|
227
|
+
const elementRegistry = this.bpmnViewer && this.bpmnViewer.get('elementRegistry');
|
|
228
|
+
if (currentNodeId && elementRegistry) {
|
|
229
|
+
const currentElement = elementRegistry.get(currentNodeId);
|
|
230
|
+
if (currentElement && this.isElementInSubProcess(currentElement)) {
|
|
231
|
+
Toast('预测路线暂不支持子流程节点,请查看设计路线');
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
this.futureNodeData = futureData;
|
|
236
|
+
this.showFutureNodesOnly();
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
Toast.fail(`加载预测路线失败:${resp.data.message || '未知错误'}`);
|
|
240
|
+
return false;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
Toast.fail(`加载预测路线失败:${error.message || '未知错误'}`);
|
|
243
|
+
return false;
|
|
244
|
+
} finally {
|
|
245
|
+
this.loadingFutureNodes = false;
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
// 只显示预测路线相关节点(并保留已办路径)
|
|
250
|
+
showFutureNodesOnly() {
|
|
251
|
+
if (!this.futureNodeData || !this.bpmnViewer) return;
|
|
252
|
+
|
|
253
|
+
const elementRegistry = this.bpmnViewer.get('elementRegistry');
|
|
254
|
+
|
|
255
|
+
// 获取所有元素
|
|
256
|
+
const allElements = elementRegistry.getAll();
|
|
257
|
+
|
|
258
|
+
// 从后端数据中提取节点和边信息
|
|
259
|
+
const {nodes = [], edges = [], futureNodeIds = []} = this.futureNodeData;
|
|
260
|
+
|
|
261
|
+
// 构建节点ID到节点数据的映射
|
|
262
|
+
const nodeMap = new Map();
|
|
263
|
+
nodes.forEach(node => {
|
|
264
|
+
nodeMap.set(node.id, node);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// 构建边ID到边数据的映射
|
|
268
|
+
const edgeMap = new Map();
|
|
269
|
+
edges.forEach(edge => {
|
|
270
|
+
edgeMap.set(edge.id, edge);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// 已办连线和节点集合(使用 Set 便于判断)
|
|
274
|
+
const doneLightSet = this.doneLightSet instanceof Set ? this.doneLightSet : new Set(this.doneLightSet || []);
|
|
275
|
+
const doneNodeSet = this.doneNodeIdSet instanceof Set ? this.doneNodeIdSet : new Set(this.doneNodeIdSet || []);
|
|
276
|
+
const hiddenNodeSet = new Set(this.futureNodeData.hiddenNodeIds || []);
|
|
277
|
+
const hiddenEdgeSet = new Set(this.futureNodeData.hiddenEdgeIds || []);
|
|
278
|
+
|
|
279
|
+
const futureNodeIdSet = new Set(futureNodeIds || []);
|
|
280
|
+
void futureNodeIdSet;
|
|
281
|
+
|
|
282
|
+
const visibleSubProcessIds = new Set(this.futureNodeData.visibleSubProcessIds || []);
|
|
283
|
+
|
|
284
|
+
// 遍历所有元素
|
|
285
|
+
allElements.forEach(element => {
|
|
286
|
+
const gfx = elementRegistry.getGraphics(element);
|
|
287
|
+
if (!gfx) return;
|
|
288
|
+
|
|
289
|
+
// 开始事件和结束事件
|
|
290
|
+
if (element.type === 'bpmn:StartEvent' || element.type === 'bpmn:EndEvent') {
|
|
291
|
+
const nodeData = nodeMap.get(element.id) || {};
|
|
292
|
+
const nodeFutureVisible = nodeData.hasOwnProperty('futureVisible')
|
|
293
|
+
? nodeData.futureVisible
|
|
294
|
+
: !hiddenNodeSet.has(element.id);
|
|
295
|
+
const parentSubProcess = element.parent && element.parent.type === 'bpmn:SubProcess' ? element.parent : null;
|
|
296
|
+
const parentVisible = parentSubProcess ? visibleSubProcessIds.has(parentSubProcess.id) : true;
|
|
297
|
+
const parentDone = parentSubProcess && doneNodeSet.has(parentSubProcess.id);
|
|
298
|
+
if (parentSubProcess && !parentVisible && !parentDone) {
|
|
299
|
+
gfx.style.display = 'none';
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const shouldDisplay = nodeFutureVisible || doneNodeSet.has(element.id);
|
|
303
|
+
if (!shouldDisplay) {
|
|
304
|
+
gfx.style.display = 'none';
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
gfx.style.display = '';
|
|
308
|
+
gfx.style.opacity = '1';
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// 处理任务节点
|
|
313
|
+
if (element.type === 'bpmn:UserTask' ||
|
|
314
|
+
element.type === 'bpmn:ServiceTask' ||
|
|
315
|
+
element.type === 'bpmn:Task' ||
|
|
316
|
+
element.type === 'bpmn:CallActivity') {
|
|
317
|
+
|
|
318
|
+
const nodeData = nodeMap.get(element.id) || {};
|
|
319
|
+
const nodeFutureVisible = nodeData.hasOwnProperty('futureVisible')
|
|
320
|
+
? nodeData.futureVisible
|
|
321
|
+
: !hiddenNodeSet.has(element.id);
|
|
322
|
+
const parentId = nodeData.parentSubProcessId;
|
|
323
|
+
const parentIsDone = parentId && doneNodeSet.has(parentId);
|
|
324
|
+
const isDoneTask = doneNodeSet.has(element.id) || parentIsDone;
|
|
325
|
+
const shouldDisplay = nodeFutureVisible || isDoneTask;
|
|
326
|
+
|
|
327
|
+
if (!shouldDisplay) {
|
|
328
|
+
gfx.style.display = 'none';
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
gfx.style.display = '';
|
|
333
|
+
gfx.style.opacity = '1';
|
|
334
|
+
|
|
335
|
+
if (nodeData.futureHighlight) {
|
|
336
|
+
const visualElement = gfx.querySelector('.djs-visual > :nth-child(1)');
|
|
337
|
+
if (visualElement) {
|
|
338
|
+
visualElement.style.stroke = '#FFD700';
|
|
339
|
+
visualElement.style.strokeWidth = '2px';
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// 处理连线
|
|
345
|
+
if (element.type === 'bpmn:SequenceFlow') {
|
|
346
|
+
const edgeData = edgeMap.get(element.id) || {};
|
|
347
|
+
|
|
348
|
+
// 修改:严格判断连线是否已办,只有连线本身在 doneLightSet 中才视为已办
|
|
349
|
+
const isDoneLine = doneLightSet.has(element.id);
|
|
350
|
+
|
|
351
|
+
const edgeFutureVisible = edgeData.hasOwnProperty('futureVisible')
|
|
352
|
+
? edgeData.futureVisible
|
|
353
|
+
: !hiddenEdgeSet.has(element.id);
|
|
354
|
+
|
|
355
|
+
if (!(edgeFutureVisible || isDoneLine)) {
|
|
356
|
+
gfx.style.display = 'none';
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
gfx.style.display = '';
|
|
361
|
+
gfx.style.opacity = '1';
|
|
362
|
+
|
|
363
|
+
const shouldHighlight = edgeData.futureHighlight === true;
|
|
364
|
+
if (shouldHighlight) {
|
|
365
|
+
const canvas = this.bpmnViewer.get('canvas');
|
|
366
|
+
if (canvas && canvas.hasMarker(element.id, 'done-line')) {
|
|
367
|
+
canvas.removeMarker(element.id, 'done-line');
|
|
368
|
+
}
|
|
369
|
+
const visualElement = gfx.querySelector('.djs-visual > :nth-child(1)');
|
|
370
|
+
if (visualElement) {
|
|
371
|
+
visualElement.style.stroke = '#FFD700';
|
|
372
|
+
visualElement.style.strokeWidth = '2px';
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// 处理网关节点
|
|
378
|
+
if (typeof element.type === 'string' && element.type.includes('Gateway')) {
|
|
379
|
+
const nodeData = nodeMap.get(element.id) || {};
|
|
380
|
+
const nodeFutureVisible = nodeData.hasOwnProperty('futureVisible')
|
|
381
|
+
? nodeData.futureVisible
|
|
382
|
+
: !hiddenNodeSet.has(element.id);
|
|
383
|
+
|
|
384
|
+
// 检查是否同时连接到已办节点和预测路径上的节点
|
|
385
|
+
const hasIncomingDone = element.incoming?.some(conn => {
|
|
386
|
+
const source = conn.source;
|
|
387
|
+
return doneLightSet.has(conn.id) || (source && doneNodeSet.has(source.id));
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const hasOutgoingFuture = element.outgoing?.some(conn => {
|
|
391
|
+
const target = conn.target;
|
|
392
|
+
const connData = edgeMap.get(conn.id) || {};
|
|
393
|
+
return connData.futureVisible || (target && nodeMap.get(target.id)?.futureVisible);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// 严格网关显示条件:预测可见 / 已办 / 同时连接已办与预测(避免孤立网关)
|
|
397
|
+
const shouldDisplay = nodeFutureVisible || doneNodeSet.has(element.id) || (hasIncomingDone && hasOutgoingFuture);
|
|
398
|
+
if (!shouldDisplay) {
|
|
399
|
+
gfx.style.display = 'none';
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
gfx.style.display = '';
|
|
403
|
+
gfx.style.opacity = '1';
|
|
404
|
+
const visualElement = gfx.querySelector('.djs-visual > :nth-child(1)');
|
|
405
|
+
if (visualElement) {
|
|
406
|
+
if (nodeData.futureHighlight) {
|
|
407
|
+
visualElement.style.stroke = '#FFD700';
|
|
408
|
+
visualElement.style.strokeWidth = '2px';
|
|
409
|
+
} else {
|
|
410
|
+
visualElement.style.stroke = '#000000';
|
|
411
|
+
visualElement.style.strokeWidth = '2px';
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// 处理子流程节点(SubProcess)
|
|
417
|
+
if (element.type === 'bpmn:SubProcess') {
|
|
418
|
+
const nodeData = nodeMap.get(element.id) || {};
|
|
419
|
+
const nodeFutureVisible = nodeData.hasOwnProperty('futureVisible')
|
|
420
|
+
? nodeData.futureVisible
|
|
421
|
+
: visibleSubProcessIds.has(element.id);
|
|
422
|
+
const shouldDisplay = nodeFutureVisible || doneNodeSet.has(element.id);
|
|
423
|
+
if (!shouldDisplay) {
|
|
424
|
+
gfx.style.display = 'none';
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
gfx.style.display = '';
|
|
428
|
+
gfx.style.opacity = '1';
|
|
429
|
+
const visualElement = gfx.querySelector('.djs-visual > :nth-child(1)');
|
|
430
|
+
if (visualElement) {
|
|
431
|
+
if (nodeData.futureHighlight) {
|
|
432
|
+
visualElement.style.stroke = '#FFD700';
|
|
433
|
+
visualElement.style.strokeWidth = '2px';
|
|
434
|
+
} else {
|
|
435
|
+
visualElement.style.stroke = '#000000';
|
|
436
|
+
visualElement.style.strokeWidth = '2px';
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// 处理 label(如连线名称)
|
|
442
|
+
if (element.type === 'label' && element.labelTarget) {
|
|
443
|
+
const target = element.labelTarget;
|
|
444
|
+
const targetEdgeData = edgeMap.get(target.id) || {};
|
|
445
|
+
const futureLabelVisible = targetEdgeData.hasOwnProperty('futureLabelVisible')
|
|
446
|
+
? targetEdgeData.futureLabelVisible
|
|
447
|
+
: false;
|
|
448
|
+
const targetIsDoneLine = doneLightSet.has(target.id);
|
|
449
|
+
gfx.style.display = (futureLabelVisible || targetIsDoneLine) ? '' : 'none';
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
},
|
|
453
|
+
|
|
454
|
+
// 恢复显示全部节点(设计路线)
|
|
455
|
+
showAllNodesInDiagram() {
|
|
456
|
+
if (!this.bpmnViewer) return;
|
|
457
|
+
this.futureNodeData = null;
|
|
458
|
+
this.showTaskNodeDetail = false;
|
|
459
|
+
this.nodeDetailsList = [];
|
|
460
|
+
this.currentIndex = 0;
|
|
461
|
+
|
|
462
|
+
// 重新加载流程图是最可靠的恢复方式(避免样式残留)
|
|
463
|
+
this.onTrack();
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
isElementInSubProcess(element) {
|
|
467
|
+
let parent = element && element.parent;
|
|
468
|
+
while (parent) {
|
|
469
|
+
if (parent.type === 'bpmn:SubProcess') {
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
parent = parent.parent;
|
|
473
|
+
}
|
|
474
|
+
return false;
|
|
475
|
+
},
|
|
476
|
+
|
|
477
|
+
removeTouchGesture() {
|
|
478
|
+
if (!this.touchGestureHandlers) return;
|
|
479
|
+
const {container, onTouchStart, onTouchMove, onTouchEnd} = this.touchGestureHandlers;
|
|
480
|
+
if (container) {
|
|
481
|
+
container.removeEventListener('touchstart', onTouchStart);
|
|
482
|
+
container.removeEventListener('touchmove', onTouchMove);
|
|
483
|
+
container.removeEventListener('touchend', onTouchEnd);
|
|
484
|
+
}
|
|
485
|
+
this.touchGestureHandlers = null;
|
|
486
|
+
},
|
|
487
|
+
|
|
488
|
+
async onTrack() {
|
|
489
|
+
this.removeTouchGesture();
|
|
490
|
+
if (this.bpmnViewer) {
|
|
491
|
+
this.bpmnViewer.destroy();
|
|
492
|
+
this.bpmnViewer = null;
|
|
493
|
+
}
|
|
494
|
+
this.diagramReady = false;
|
|
495
|
+
|
|
141
496
|
// 初始化 Viewer
|
|
142
497
|
this.bpmnViewer = new Viewer({
|
|
143
498
|
container: this.$refs.canvas
|
|
144
499
|
});
|
|
145
500
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
501
|
+
try {
|
|
502
|
+
// 获取并加载流程数据
|
|
503
|
+
const resp = await printData(this.applyId, this.instanceId, this.procId);
|
|
504
|
+
const result = resp && resp.data;
|
|
505
|
+
if (result && result.code === '200') {
|
|
150
506
|
const bpmnXmlStr = result.data.processXml;
|
|
151
507
|
await this.bpmnViewer.importXML(bpmnXmlStr);
|
|
152
508
|
|
|
@@ -154,48 +510,58 @@ export default {
|
|
|
154
510
|
canvas.zoom('fit-viewport', 'auto');
|
|
155
511
|
canvas.zoom(0.6); // 放大视图
|
|
156
512
|
|
|
157
|
-
|
|
513
|
+
// 给流程节点添加固定样式
|
|
158
514
|
this.doPrint(result.data.historicData);
|
|
515
|
+
this.diagramReady = true;
|
|
159
516
|
|
|
160
517
|
// 添加手势缩放和移动功能
|
|
161
518
|
this.addTouchGesture();
|
|
162
519
|
// 监听元素点击事件
|
|
163
520
|
this.addElementClickListener();
|
|
164
521
|
|
|
522
|
+
return;
|
|
165
523
|
}
|
|
166
|
-
}).catch(err => {
|
|
167
|
-
console.error("Error fetching BPMN data:", err);
|
|
168
|
-
});
|
|
169
524
|
|
|
525
|
+
this.diagramReady = false;
|
|
526
|
+
Toast.fail(`加载流程图失败:${(result && result.message) || '未知错误'}`);
|
|
527
|
+
} catch (err) {
|
|
528
|
+
this.diagramReady = false;
|
|
529
|
+
console.error("Error fetching BPMN data:", err);
|
|
530
|
+
Toast.fail(`加载流程图失败:${err.message || '未知错误'}`);
|
|
531
|
+
}
|
|
170
532
|
},
|
|
171
533
|
doPrint(printData) {
|
|
172
|
-
const {doneLightSet, donePointSet, doneTaskSet, todoPointSet} =
|
|
173
|
-
|
|
534
|
+
const {doneLightSet, donePointSet, doneTaskSet, todoPointSet} = printData || {};
|
|
535
|
+
|
|
536
|
+
// 保存已办连线和节点集合,供预测路线显示时使用
|
|
537
|
+
this.doneLightSet = new Set(doneLightSet || []);
|
|
538
|
+
this.doneTaskSet = new Set(doneTaskSet || []);
|
|
539
|
+
this.donePointIdSet = new Set(donePointSet || []);
|
|
540
|
+
this.doneNodeIdSet = new Set([
|
|
541
|
+
...this.doneTaskSet,
|
|
542
|
+
...this.donePointIdSet
|
|
543
|
+
]);
|
|
544
|
+
this.todoPointIdSet = new Set(todoPointSet || []);
|
|
545
|
+
|
|
174
546
|
const canvas = this.bpmnViewer.get("canvas");
|
|
175
547
|
const elementRegistry = this.bpmnViewer.get('elementRegistry');
|
|
176
548
|
elementRegistry.filter((item) => item.type === 'bpmn:UserTask');
|
|
177
|
-
for (
|
|
178
|
-
if (
|
|
179
|
-
canvas.addMarker(doneLightSet[k], "done-line");
|
|
180
|
-
}
|
|
549
|
+
for (const id of this.doneLightSet) {
|
|
550
|
+
if (id) canvas.addMarker(id, "done-line");
|
|
181
551
|
}
|
|
182
|
-
for (
|
|
183
|
-
if (
|
|
184
|
-
canvas.addMarker(donePointSet[k], "done-point");
|
|
185
|
-
}
|
|
552
|
+
for (const id of this.donePointIdSet) {
|
|
553
|
+
if (id) canvas.addMarker(id, "done-point");
|
|
186
554
|
}
|
|
187
|
-
for (
|
|
188
|
-
if (
|
|
189
|
-
canvas.addMarker(doneTaskSet[k], "done-task");
|
|
190
|
-
}
|
|
555
|
+
for (const id of this.doneTaskSet) {
|
|
556
|
+
if (id) canvas.addMarker(id, "done-task");
|
|
191
557
|
}
|
|
192
|
-
for (
|
|
193
|
-
if (
|
|
194
|
-
canvas.addMarker(todoPointSet[k], "todo-point");
|
|
195
|
-
}
|
|
558
|
+
for (const id of this.todoPointIdSet) {
|
|
559
|
+
if (id) canvas.addMarker(id, "todo-point");
|
|
196
560
|
}
|
|
197
561
|
},
|
|
198
562
|
addTouchGesture() {
|
|
563
|
+
if (!this.bpmnViewer) return;
|
|
564
|
+
this.removeTouchGesture();
|
|
199
565
|
const canvas = this.bpmnViewer.get('canvas');
|
|
200
566
|
const container = this.$refs.canvas;
|
|
201
567
|
let initialDistance = null;
|
|
@@ -208,7 +574,7 @@ export default {
|
|
|
208
574
|
return Math.sqrt(dx * dx + dy * dy);
|
|
209
575
|
};
|
|
210
576
|
|
|
211
|
-
|
|
577
|
+
const onTouchStart = (event) => {
|
|
212
578
|
if (event.touches.length === 2) {
|
|
213
579
|
initialDistance = calculateDistance(event.touches[0], event.touches[1]);
|
|
214
580
|
} else if (event.touches.length === 1) {
|
|
@@ -218,9 +584,9 @@ export default {
|
|
|
218
584
|
};
|
|
219
585
|
isPanning = true;
|
|
220
586
|
}
|
|
221
|
-
}
|
|
587
|
+
};
|
|
222
588
|
|
|
223
|
-
|
|
589
|
+
const onTouchMove = (event) => {
|
|
224
590
|
if (event.touches.length === 2 && initialDistance !== null) {
|
|
225
591
|
const currentDistance = calculateDistance(event.touches[0], event.touches[1]);
|
|
226
592
|
const zoomFactor = currentDistance / initialDistance;
|
|
@@ -250,18 +616,25 @@ export default {
|
|
|
250
616
|
|
|
251
617
|
event.preventDefault(); // 阻止默认的触摸事件
|
|
252
618
|
}
|
|
253
|
-
}
|
|
619
|
+
};
|
|
254
620
|
|
|
255
|
-
|
|
621
|
+
const onTouchEnd = () => {
|
|
256
622
|
initialDistance = null; // 重置初始距离
|
|
257
623
|
isPanning = false; // 停止平移
|
|
258
|
-
}
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
container.addEventListener('touchstart', onTouchStart);
|
|
627
|
+
container.addEventListener('touchmove', onTouchMove);
|
|
628
|
+
container.addEventListener('touchend', onTouchEnd);
|
|
629
|
+
|
|
630
|
+
this.touchGestureHandlers = {container, onTouchStart, onTouchMove, onTouchEnd};
|
|
259
631
|
},
|
|
260
632
|
addElementClickListener() {
|
|
261
633
|
const eventBus = this.bpmnViewer.get('eventBus');
|
|
262
634
|
eventBus.on('element.click', (event) => {
|
|
263
635
|
const elementId = event.element.id;
|
|
264
|
-
|
|
636
|
+
const tableData = Array.isArray(this.tableData) ? this.tableData : [];
|
|
637
|
+
let matchedRecords = tableData.filter(item => item.taskNode === elementId);
|
|
265
638
|
|
|
266
639
|
if (matchedRecords.length > 0) {
|
|
267
640
|
const getTime = (record) => {
|
|
@@ -293,6 +666,12 @@ export default {
|
|
|
293
666
|
<style scoped>
|
|
294
667
|
@import 'styles/global.css';
|
|
295
668
|
|
|
669
|
+
.predict-toggle {
|
|
670
|
+
display: flex;
|
|
671
|
+
justify-content: center;
|
|
672
|
+
margin-top: 12px;
|
|
673
|
+
}
|
|
674
|
+
|
|
296
675
|
.canvas {
|
|
297
676
|
width: 100%;
|
|
298
677
|
height: 250px;
|