@lambo-design-mobile/workflow-approve 1.0.0-beta.22 → 1.0.0-beta.23

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.
@@ -31,7 +31,7 @@
31
31
  <div v-if="approvalForm.nextNode!==''">
32
32
  <van-cell title="下一环节" :value="approvalForm.nextNode"/>
33
33
  <div v-if="handleButtons && handleButtons.includes('appointHandler')">
34
- <van-cell @click="nextNodePopupShow = true" title="办理人员" value-class="approvalForm-value"
34
+ <van-cell @click="handleNextNodePopupShow" title="办理人员" value-class="approvalForm-value"
35
35
  :value="approvalForm.handlingPersonnel">
36
36
  <template #right-icon>
37
37
  <van-icon name="friends-o" size="16" style="line-height: inherit;padding-left: 5px"/>
@@ -115,33 +115,23 @@
115
115
  <!-- 底部按钮 等其他内容 -->
116
116
  <div>
117
117
  <van-popup v-model="popupShow" style="padding-top: 40px" closeable round position="bottom">
118
- <div v-if="handleButtons && handleButtons.includes('auditTo70')" class="popup-option" @click="audit('70')">
119
- {{ getAuditButtonStatus(70) }}
120
- </div>
121
- <div v-if="handleButtons && handleButtons.includes('auditTo40')" class="popup-option highlighted"
122
- @click="audit('40')">
123
- {{ getAuditButtonStatus(40) }}
124
- </div>
125
- <div v-if="handleButtons && handleButtons.includes('auditTo90')" class="popup-option" @click="audit('90')">
126
- {{ getAuditButtonStatus(90) }}
127
- </div>
128
- <div v-if="handleButtons && handleButtons.includes('auditTo80')" class="popup-option" @click="audit('80')">
129
- {{ getAuditButtonStatus(80) }}
130
- </div>
131
- <div v-if="handleButtons && handleButtons.includes('auditTo82')" class="popup-option" @click="audit('82')">
132
- {{ getAuditButtonStatus(82) }}
133
- </div>
134
- <div v-if="handleButtons && handleButtons.includes('auditTo50')" class="popup-option" @click="audit('50')">
135
- {{ getAuditButtonStatus(50) }}
136
- </div>
118
+ <template v-for="(button, index) of operationButtons">
119
+ <div :class="button.class" @click="button.action">
120
+ {{ button.label }}
121
+ </div>
122
+ </template>
137
123
  </van-popup>
138
124
  <div class="custom-bottom-bar">
139
- <div v-if="!isDetail && getVisibleButtonsCount() > 1" class="bar-item" @click="onMore">更多</div>
140
- <div v-else-if="!isDetail && getVisibleButtonsCount() === 1" class="bar-item" @click="handleSingleButton()">
125
+ <div v-if="!isDetail && operationButtons.length > 1" class="bar-item" @click="onMore">更多</div>
126
+ <div v-else-if="!isDetail && operationButtons.length === 1" class="bar-item" @click="handleSingleButton()">
141
127
  {{ getSingleButtonText() }}
142
128
  </div>
143
129
  <div class="bar-item" @click="onTrack">流程跟踪</div>
144
- <div v-if="!isDetail" class="bar-item approve" @click="audit('30')">{{ getAuditButtonStatus(30) }}</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>
145
135
  </div>
146
136
  </div>
147
137
  <van-popup @click-close-icon="handleSelect('cancel')" v-model="nextNodePopupShow"
@@ -156,29 +146,32 @@
156
146
  <van-field name="radio" label="人员类型" input-align="right">
157
147
  <template #input>
158
148
  <van-radio-group v-model="item.personType" direction="horizontal">
159
- <van-radio name="assignee">办理人</van-radio>
160
- <van-radio name="candidate">候选人</van-radio>
149
+ <van-radio v-if="!nextAssigneeSelectionType || nextAssigneeSelectionType === 'assignee'" name="assignee">办理人</van-radio>
150
+ <van-radio v-if="!nextAssigneeSelectionType || nextAssigneeSelectionType === 'candidate'" name="candidate">候选人</van-radio>
161
151
  </van-radio-group>
162
152
  </template>
163
153
  </van-field>
164
154
  <van-cell v-if="item.personType === 'assignee'" title="办理人"
165
155
  :value="(item.assignee.name ? item.assignee.name : '未指定') + ':' + (item.assignee.id ? item.assignee.id : '未指定')"
166
- @click="nextNodeHandleIndex=index;selectHandlePopupShow=true;">
156
+ @click="handleNextNodeAssigneeSelect(index, 'appointHandler')">
167
157
  </van-cell>
168
158
  <van-cell v-if="item.personType === 'candidate'" title="候选人" @click="toggleDetails(index)"
169
159
  :is-link="true" :arrow-direction="detailsVisible[index] ? 'down' : ''">
170
160
  </van-cell>
171
161
  <div v-if="detailsVisible[index] && item.personType === 'candidate'">
172
- <van-field @click="handleSelectUserPopupShow(index)" label="候选用户" readonly placeholder="点击选择"
162
+ <van-field v-if="useTransferUser" @click="handleNextNodeUserSelect(index, 'appointHandlerWithCandGroups')" label="候选用户" readonly placeholder="点击选择"
163
+ :value="(item.candidateGroups?.users ?? []).map(user => user.name).join(',')">
164
+ </van-field>
165
+ <van-field v-if="!useTransferUser && !useTransferOrgan" @click="handleCandUserSelect(index)" label="候选用户" readonly placeholder="点击选择"
173
166
  :value="(item.candidateGroups?.users ?? []).map(user => user.name).join(',')">
174
167
  </van-field>
175
- <van-field @click="handleSelectPositionPopupShow(index)" label="候选岗位" readonly placeholder="点击选择"
168
+ <van-field v-if="!useTransferUser && !useTransferOrgan" @click="handleSelectPositionPopupShow(index)" label="候选岗位" readonly placeholder="点击选择"
176
169
  :value="(item.candidateGroups?.positions ?? []).map(position => position.name).join(',')">
177
170
  </van-field>
178
- <van-field @click="handleSelectRolePopupShow(index)" label="候选角色" readonly placeholder="点击选择"
171
+ <van-field v-if="!useTransferUser && !useTransferOrgan" @click="handleSelectRolePopupShow(index)" label="候选角色" readonly placeholder="点击选择"
179
172
  :value="(item.candidateGroups?.roles ?? []).map(role => role.name).join(',')">
180
173
  </van-field>
181
- <van-field @click="handleSelectOrganizePopupShow(index)" label="候选组织" readonly placeholder="点击选择"
174
+ <van-field v-if="!useTransferUser" @click="handleSelectOrganizePopupShow(index)" label="候选组织" readonly placeholder="点击选择"
182
175
  :value="(item.candidateGroups?.organs ?? []).map(organ => organ.name).join(',')">
183
176
  </van-field>
184
177
  </div>
@@ -236,17 +229,19 @@
236
229
  <div class="bar-item approve" @click="handleSelect('select')">确认</div>
237
230
  </div>
238
231
  </van-popup>
239
- <van-popup v-if="selectHandlePopupShow" v-model="selectHandlePopupShow" closeable round position="bottom"
240
- :style="{ height: '80%' }" style="background: #f7f8fa">
241
- <select-handle title="指定办理人" :multi-select="false" :procType="procType" @selectHandle="handleSelectResult"></select-handle>
232
+ <van-popup v-if="selectUserPopupShow" v-model="selectUserPopupShow" closeable round position="bottom"
233
+ :style="{ height: '80%' }" :class="selectUserPopupParams.class">
234
+ <select-handle :title="selectUserPopupParams.title" :multi-select="selectUserPopupParams.multiSelect" :procType="procType" :organ-tree-type="selectUserPopupParams.organTreeType"
235
+ :org-list="selectUserPopupParams.orgList" :user-list="selectUserPopupParams.userList" @selectHandle="selectUserPopupParams.action"></select-handle>
242
236
  </van-popup>
243
237
  <van-popup v-if="selectOrganizePopupShow" v-model="selectOrganizePopupShow" closeable round position="bottom"
244
238
  :style="{ height: '80%' }">
245
- <select-organize ref="selectOrganize" :all-organize="false" :organize-id-list="permissionsScope.O" @selectOrganizeHandle="selectOrganizeHandle" ></select-organize>
239
+ <select-organize ref="selectOrganize" :treetype-id="orgTreeType" :all-organize="false" :organize-id-list="permissionsScope.O" @selectOrganizeHandle="selectOrganizeHandle" ></select-organize>
246
240
  </van-popup>
247
- <van-popup v-if="selectUserPopupShow" v-model="selectUserPopupShow" closeable round position="bottom"
241
+ <van-popup v-if="selectCandUserPopupShow" v-model="selectCandUserPopupShow" closeable round position="bottom"
248
242
  :style="{ height: '80%' }">
249
- <select-handle title="选择用户" :multi-select="true" :procType="procType" @selectHandle="selectUserHandle" ></select-handle>
243
+ <select-handle title="选择用户" :multi-select="true" :procType="procType" :organ-tree-type="selectUserPopupParams.organTreeType" :org-list="selectUserPopupParams.orgList"
244
+ :user-list="selectUserPopupParams.userList" @selectHandle="selectCandUserHandle"></select-handle>
250
245
  </van-popup>
251
246
  <van-popup v-if="selectPositionPopupShow" v-model="selectPositionPopupShow" closeable round position="bottom"
252
247
  :style="{ height: '80%' }">
@@ -256,7 +251,23 @@
256
251
  :style="{ height: '80%' }">
257
252
  <select-normal-list title="选择角色" :id-list="permissionsScope.R" @selectNormalListHandle="selectRoleHandle" :parse-function="parseRoleFunction"></select-normal-list>
258
253
  </van-popup>
259
- <van-dialog v-model="nodeListDialogShow" @confirm="processJumpSpecifiedNode()" @close="nodeListDialogShowClose()"
254
+ <van-popup v-if="selectReductionUserPopupShow" v-model="selectReductionUserPopupShow" closeable round position="bottom" :style="{ height: '80%' }">
255
+ <div id="reductionListContent" style="background: #f7f8fa">
256
+ <div class="van-nav-bar__content">
257
+ <div class="van-nav-bar__title">{{ getAuditButtonStatus(83) }}</div>
258
+ </div>
259
+ <van-list style="padding-top: 15px">
260
+ <select-handle-card ref="selectHandleCard" :multi-select="true" :person-list="unapprovedAssigneeList"
261
+ :result.sync="reductionUsers" :show-status="false">
262
+ </select-handle-card>
263
+ </van-list>
264
+ </div>
265
+ <div v-if="reductionUsers.length > 0" class="custom-bottom-bar">
266
+ <div class="bar-item" @click="handleReductionUserSelect('cancel')">取消</div>
267
+ <div class="bar-item approve" @click="handleReductionUserSelect('select')">选择</div>
268
+ </div>
269
+ </van-popup>
270
+ <van-dialog v-model="nodeListDialogShow" @confirm="processJumpSpecifiedNode()" @close="nodeListDialogShowClose()" :before-close="onBeforeClose"
260
271
  :title="getAuditButtonStatus(auditResult)" show-cancel-button>
261
272
  <van-radio-group v-model="radio">
262
273
  <van-cell-group>
@@ -267,7 +278,7 @@
267
278
  </div>
268
279
  <div class="table-row"
269
280
  v-for="(item, index) in nodeList" :key="index"
270
- @click="radio = index">
281
+ @click="selectNode(item, index)">
271
282
  <div>{{ item.taskName }}</div>
272
283
  <div>
273
284
  <van-tag v-if="item.auditResult" :type="getAuditStatus(item.auditResult).type">
@@ -281,9 +292,23 @@
281
292
  <van-radio :name='index'/>
282
293
  </div>
283
294
  </div>
295
+ <div v-if="showProcessControl" class="reject-attribute">
296
+ <div>驳回的节点通过后:</div>
297
+ <van-radio-group v-model="rejectAttribute">
298
+ <van-radio v-for="(item, index) in rejectAttributeList" :key="index" :name="item.type">{{ item.name }}</van-radio>
299
+ </van-radio-group>
300
+ </div>
284
301
  </van-cell-group>
285
302
  </van-radio-group>
286
303
  </van-dialog>
304
+ <van-dialog v-model="rejectAttributesBoxShow" @confirm="processRejectNode()" @close="rejectAttributesDialogShowClose()"
305
+ title="驳回的节点通过后" show-cancel-button>
306
+ <div class="reject-attribute">
307
+ <van-radio-group v-model="rejectAttribute">
308
+ <van-radio v-for="(item, index) in rejectAttributeList" :key="index" :name="item.type">{{ item.name }}</van-radio>
309
+ </van-radio-group>
310
+ </div>
311
+ </van-dialog>
287
312
  </div>
288
313
  </template>
289
314
 
@@ -297,9 +322,15 @@ import {
297
322
  getNodesBehind,
298
323
  getPosition,
299
324
  getPreNode,
300
- getHandleButtons,
325
+ getNodeData,
301
326
  getProcessHis,
302
- getProcessType, getRole
327
+ getProcessType, getRole,
328
+ getTransferRange,
329
+ updateMultitaskInstance,
330
+ getUnapprovedListOfMultiNode,
331
+ getProcessTodoList,
332
+ getProcessAttributes,
333
+ listAll
303
334
  } from "../api";
304
335
  import {Dialog, Toast} from "vant";
305
336
  import UploadFile from '@lambo-design-mobile/upload-file';
@@ -310,6 +341,8 @@ import SelectOrganize from "./SelectOrganize.vue";
310
341
  import SelectNormalList from "./SelectNormalList.vue";
311
342
  import ApprovalNodeCell from "./ApprovalNodeCell.vue";
312
343
  import {getAuditStatus} from "./js/global";
344
+ import {NodeType} from "./utils/const";
345
+ import SelectHandleCard from "./SelectHandleCard.vue";
313
346
 
314
347
 
315
348
  export default {
@@ -319,9 +352,84 @@ export default {
319
352
  },
320
353
  showAuditOpinion(){
321
354
  return this.handleButtons && (this.handleButtons.includes('auditOpinion') || this.handleButtons.includes('appointHandler') || this.handleButtons.includes('attachmentFile'))
355
+ },
356
+ operationButtons(){
357
+ const showButtons = !this.appointTask && !this.rejectedTask && !this.revokeDelegateTask && !this.coSignVotingTask
358
+ const handleButtons = [
359
+ {
360
+ id: 'delegateTask',
361
+ label: this.getAuditButtonStatus(84),
362
+ condition: this.curNodeType === NodeType.UserTask &&
363
+ this.handleButtons?.includes('delegateTask') &&
364
+ showButtons,
365
+ class: 'popup-option',
366
+ action: () => this.audit('84'),
367
+ },
368
+ {
369
+ id: 'addMultitaskInstance',
370
+ label: this.getAuditButtonStatus(81),
371
+ condition: this.curNodeType === NodeType.MultiNode &&
372
+ this.handleButtons?.includes('addMultitaskInstance') &&
373
+ showButtons,
374
+ class: 'popup-option',
375
+ action: () => this.showUpdateMultitaskInstanceModal('81'),
376
+ },
377
+ {
378
+ id: 'reductionMultitaskInstance',
379
+ label: this.getAuditButtonStatus(83),
380
+ condition: this.curNodeType === NodeType.MultiNode &&
381
+ this.handleButtons?.includes('reductionMultitaskInstance') &&
382
+ showButtons,
383
+ class: 'popup-option',
384
+ action: () => this.showUpdateMultitaskInstanceModal('83'),
385
+ },
386
+ {
387
+ id: 'auditTo70',
388
+ label: this.getAuditButtonStatus(70),
389
+ condition: this.handleButtons && this.handleButtons.includes('auditTo70') && showButtons,
390
+ class: 'popup-option',
391
+ action: () => this.audit('70'),
392
+ },
393
+ {
394
+ id: 'auditTo40',
395
+ label: this.getAuditButtonStatus(40),
396
+ condition: this.handleButtons && this.handleButtons.includes('auditTo40') && showButtons,
397
+ class: 'popup-option highlighted',
398
+ action: () => this.audit('40'),
399
+ },
400
+ {
401
+ id: 'auditTo90',
402
+ label: this.getAuditButtonStatus(90),
403
+ condition: this.handleButtons && this.handleButtons.includes('auditTo90') && showButtons,
404
+ class: 'popup-option',
405
+ action: () => this.audit('90'),
406
+ },
407
+ {
408
+ id: 'auditTo80',
409
+ label: this.getAuditButtonStatus(80),
410
+ condition: this.handleButtons && this.handleButtons.includes('auditTo80') && showButtons,
411
+ class: 'popup-option',
412
+ action: () => this.audit('80'),
413
+ },
414
+ {
415
+ id: 'auditTo82',
416
+ label: this.getAuditButtonStatus(82),
417
+ condition: this.handleButtons && this.handleButtons.includes('auditTo82') && showButtons,
418
+ class: 'popup-option',
419
+ action: () => this.audit('82'),
420
+ },
421
+ {
422
+ id: 'auditTo50',
423
+ label: this.getAuditButtonStatus(50),
424
+ condition: this.handleButtons && this.handleButtons.includes('auditTo50') && showButtons,
425
+ class: 'popup-option',
426
+ action: () => this.audit('50'),
427
+ },
428
+ ]
429
+ return handleButtons.filter(item => item.condition)
322
430
  }
323
431
  },
324
- components: {ApprovalNodeCell, SelectNormalList, SelectOrganize, Tree, SelectHandle, UploadFile},
432
+ components: {ApprovalNodeCell, SelectNormalList, SelectOrganize, Tree, SelectHandle, UploadFile, SelectHandleCard},
325
433
  props: {
326
434
  //业务表单保存方法
327
435
  businessFormSave: {
@@ -341,6 +449,18 @@ export default {
341
449
  required: false,
342
450
  default: false
343
451
  },
452
+ //审批人来源
453
+ auditGroup: {
454
+ type: String,
455
+ required: false,
456
+ default: '',
457
+ },
458
+ //默认审批意见
459
+ defaultAuditOpinion: {
460
+ type: String,
461
+ required: false,
462
+ default: '',
463
+ },
344
464
  processTraceRouterName: {
345
465
  type: String,
346
466
  required: true,
@@ -372,12 +492,13 @@ export default {
372
492
  handleButtons: this.$route.query.handleButtons,
373
493
 
374
494
  approvalForm: {
375
- auditOpinion: '',
495
+ auditOpinion: this.defaultAuditOpinion,
376
496
  nextNode: '',
377
497
  handlingPersonnel: '',
378
498
  nodeConfigMaps: '',
379
499
  },
380
500
  approvalError: false,
501
+ orgTreeType: '00',
381
502
 
382
503
  processHistory: [],
383
504
  tableData: [],
@@ -386,7 +507,15 @@ export default {
386
507
 
387
508
  activeNames: [],
388
509
  popupShow: false,
510
+ appointTask: false,
511
+ revokeDelegateTask: false,
512
+ rejectedTask: false,
513
+ coSignVotingTask: false,
514
+ curNodeType: NodeType.UserTask,
389
515
 
516
+ nextAssigneeSelectionType: '',
517
+ useTransferUser: false,
518
+ useTransferOrgan: false,
390
519
  nextNodePopupShow: false,
391
520
  nextNodeHandleIndex: 0,
392
521
  nextNodeProcessedData: [], // 用于存储处理下一节点的数据
@@ -411,6 +540,8 @@ export default {
411
540
  ],
412
541
  handleTypeValue: '',
413
542
  showPicker: {},
543
+ rejectName: '驳回',
544
+ passName: '通过',
414
545
 
415
546
  nodeListDialogShow: false,
416
547
  nodeList: [],
@@ -423,12 +554,33 @@ export default {
423
554
  files: [],
424
555
  //文件上传列表
425
556
  fileList: [],
557
+ // 可以减签的人员
558
+ unapprovedAssigneeList: [],
559
+ // 减签人员
560
+ reductionUsers: [],
426
561
 
427
562
  permissionsScope: {},
428
563
  selectOrganizePopupShow: false,
564
+ selectCandUserPopupShow: false,
429
565
  selectUserPopupShow: false,
430
566
  selectRolePopupShow: false,
567
+ selectReductionUserPopupShow: false,
431
568
  selectPositionPopupShow: false,
569
+ selectUserPopupParams: {
570
+ title: '选择用户',
571
+ class: '',
572
+ multiSelect: true,
573
+ userList: [],
574
+ orgList: [],
575
+ organTreeType: '',
576
+ action: (checkResult) => { console.log('未定义回调方法') },
577
+ },
578
+ showProcessControl: false,
579
+ rejectAttributesBoxShow: false,
580
+ rejectAttribute: 'inSequence',
581
+ rejectAttributeList: [],
582
+ taskAuditUserList: [],
583
+ targetTaskNodeProcessControl: '',
432
584
  };
433
585
  },
434
586
  methods: {
@@ -437,6 +589,7 @@ export default {
437
589
  if (!this.handleButtons){
438
590
  this.getHandleButtons();
439
591
  }
592
+ if (!this.isDetail) this.getTaskStatus();
440
593
  this.getProcessHistory();
441
594
  this.getNextNodes();
442
595
  this.getAttachmentList();
@@ -473,10 +626,42 @@ export default {
473
626
  }
474
627
  },
475
628
 
629
+ getTaskStatus() {
630
+ const self = this
631
+ let params = {
632
+ procId: self.procId,
633
+ applyId: self.applyId,
634
+ taskNode: self.taskNode,
635
+ taskId: self.taskId,
636
+ auditGroup: self.auditGroup,
637
+ }
638
+ getProcessTodoList(0, 10, params).then(resp => {
639
+ if (resp.data.code === '200') {
640
+ const todoData = resp.data.data.rows[0]
641
+ // 委派任务状态
642
+ self.revokeDelegateTask = todoData.delegateStatus ? todoData.delegateStatus === '10' : false
643
+ self.appointTask = todoData.delegateStatus ? todoData.delegateStatus === '20' : false
644
+ // 判断节点是否是被驳回的节点
645
+ const taskExtensionAttributes = todoData.taskExtensionAttributes ? JSON.parse(todoData.taskExtensionAttributes) : ''
646
+ if (taskExtensionAttributes && taskExtensionAttributes.rejectAttributes) {
647
+ const rejectAttributes = taskExtensionAttributes.rejectAttributes
648
+ if (rejectAttributes && rejectAttributes.type && rejectAttributes.type != 'inSequence') self.rejectedTask = true
649
+ }
650
+ }
651
+ })
652
+ },
476
653
  getHandleButtons() {
477
- getHandleButtons(this.procId, this.taskNode).then(resp => {
654
+ getNodeData(this.procId, this.taskNode).then(resp => {
478
655
  if (resp.data.code === '200') {
479
- this.handleButtons = resp.data.data[0]?.handleButtons;
656
+ 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 = data.handleButtons && data.handleButtons.includes('coSignVoting')
659
+ this.nextAssigneeSelectionType = data.nextAssigneeSelectionType;
660
+ this.passName = data.handleName;
661
+ this.rejectName = data.rejectName;
662
+ if (data.hasOwnProperty('isSequential')) {
663
+ this.curNodeType = data.isSequential ? NodeType.SequentialMultiNode : NodeType.MultiNode;
664
+ }
480
665
  }
481
666
  })
482
667
  },
@@ -573,7 +758,7 @@ export default {
573
758
  },
574
759
  processData(data) {
575
760
  return data.map(node => {
576
- node.personType = node.assignee ? "assignee" : "candidate";
761
+ node.personType = this.nextAssigneeSelectionType ? this.nextAssigneeSelectionType : node.assignee ? "assignee" : "candidate";
577
762
  if (node.assignee) {
578
763
  node.candidateGroups = {
579
764
  organs: [],
@@ -610,7 +795,10 @@ export default {
610
795
  return node;
611
796
  });
612
797
  },
613
- toggleDetails(index) {
798
+ async toggleDetails(index) {
799
+ if (!this.detailsVisible[index]){
800
+ await this.fetchTransferRange('appointHandlerWithCandGroups')
801
+ }
614
802
  this.$set(this.detailsVisible, index, !this.detailsVisible[index]);
615
803
  },
616
804
  onHandleTypeConfirm(selectedLabel, selectedIndex, index) {
@@ -631,6 +819,13 @@ export default {
631
819
  // 拼接成目标格式
632
820
  return `O:${organIds},U:${userIds},P:${positionIds},R:${roleIds},RP:,T:`;
633
821
  },
822
+ handleReductionUserSelect(handle) {
823
+ if (handle === 'select') {
824
+ this.updateMultitaskInstance('83', this.reductionUsers.map(item => item.userId).join(','))
825
+ } else {
826
+ this.checkResult = [];
827
+ }
828
+ },
634
829
  handleSelect(handle) {
635
830
  if (handle === 'select') {
636
831
  //处理审批参数
@@ -666,11 +861,77 @@ export default {
666
861
  },
667
862
  handleSelectOrganizePopupShow(index) {
668
863
  this.nextNodeHandleIndex = index;
864
+ if (this.useTransferOrgan){
865
+ this.permissionsScope.O = this.selectUserPopupParams.orgList.map(item => item.orgCode)
866
+ }
669
867
  this.selectOrganizePopupShow=true;
670
868
  },
671
- handleSelectUserPopupShow(index) {
869
+ handleCandUserSelect(index){
672
870
  this.nextNodeHandleIndex = index;
673
- this.selectUserPopupShow=true;
871
+ this.selectUserPopupParams.userList = []
872
+ this.selectUserPopupParams.orgList = []
873
+ this.selectCandUserPopupShow = true
874
+ },
875
+ async handleNextNodePopupShow(){
876
+ // 指定下一环节选人为单/多选并且给出具体选人范围则直接弹出选人框
877
+ if (this.nextAssigneeSelectionType){
878
+ const actionType = this.nextAssigneeSelectionType === 'assignee' ? 'appointHandler' : 'appointHandlerWithCandGroups'
879
+ const action = this.nextAssigneeSelectionType === 'assignee' ? (checkResult) => this.handleAssigneeSelectResult(checkResult) : (checkResult) => this.selectCandUserHandle(checkResult)
880
+ await this.fetchTransferRange(actionType);
881
+ if (this.selectUserPopupParams.userList?.length > 0 || this.selectUserPopupParams.orgList?.length > 0){
882
+ this.handleSelectUserPopupShow('选择用户', this.nextAssigneeSelectionType === 'candidate', action, actionType)
883
+ return
884
+ }
885
+ }
886
+ this.nextNodePopupShow = true;
887
+ },
888
+ handleNextNodeAssigneeSelect(index, actionType){
889
+ this.nextNodeHandleIndex = index;
890
+ this.handleSelectUserPopupShow('选择用户', true, (checkResult) => this.handleAssigneeSelectResult(checkResult), actionType)
891
+ },
892
+ handleNextNodeUserSelect(index, actionType){
893
+ this.nextNodeHandleIndex = index;
894
+ this.handleSelectUserPopupShow('选择用户', true, (checkResult) => this.selectCandUserHandle(checkResult), actionType)
895
+ },
896
+ async handleSelectUserPopupShow(title, multiSelect, action, actionType) {
897
+ this.selectUserPopupParams.title = title
898
+ this.selectUserPopupParams.multiSelect = multiSelect
899
+ this.selectUserPopupParams.action = action
900
+ const success = await this.fetchTransferRange(actionType);
901
+ if (success) {
902
+ this.selectUserPopupShow = true;
903
+ }
904
+ },
905
+ async fetchTransferRange(actionType) {
906
+ try {
907
+ const params = {
908
+ procType: this.procType,
909
+ procId: this.procId,
910
+ taskNode: this.taskNode,
911
+ applyId: this.applyId,
912
+ taskId: this.taskId,
913
+ instanceId: this.instanceId,
914
+ actionType: actionType,
915
+ };
916
+ const res = await getTransferRange(params);
917
+ const result = res.data;
918
+ if (result.code == 200) {
919
+ this.selectUserPopupParams.organTreeType = result.data.organTreeType;
920
+ this.selectUserPopupParams.userList = result.data.userList;
921
+ this.selectUserPopupParams.orgList = result.data.orgList;
922
+ if (actionType === 'appointHandlerWithCandGroups'){
923
+ this.useTransferUser = result.data.userList?.length > 0
924
+ this.useTransferOrgan = result.data.orgList?.length > 0
925
+ }
926
+ return true;
927
+ } else {
928
+ console.warn('获取选择人员范围失败,返回码:', result.code);
929
+ return false;
930
+ }
931
+ } catch (err) {
932
+ console.error('获取选择人员范围异常', err);
933
+ return false;
934
+ }
674
935
  },
675
936
  handleSelectRolePopupShow(index) {
676
937
  this.nextNodeHandleIndex = index;
@@ -719,13 +980,20 @@ export default {
719
980
 
720
981
  getAuditButtonStatus(auditResult) {
721
982
  let statusMap = {
722
- '30': '通过',
723
- '40': '驳回上一节点',
983
+ '30': `${this.passName}`,
984
+ '31': '同意',
985
+ '32': '不同意',
986
+ '40': `${this.rejectName}上一节点`,
724
987
  '50': '直接结束流程',
725
- '70': '驳回到原点',
988
+ '61': '交回',
989
+ '62': '撤回委派',
990
+ '70': `${this.rejectName}到原点`,
726
991
  '80': '跳转指定节点',
992
+ '81': '加签',
727
993
  '82': '指定他人处理',
728
- '90': '驳回指定节点',
994
+ '83': '减签',
995
+ '84': '委派',
996
+ '90': `${this.rejectName}指定节点`,
729
997
  };
730
998
  return statusMap[auditResult];
731
999
  },
@@ -746,17 +1014,50 @@ export default {
746
1014
  }
747
1015
  })
748
1016
  },
1017
+ // 加减签
1018
+ showUpdateMultitaskInstanceModal(updateType){
1019
+ this.selectHandlePopupShow = true
1020
+ const self = this
1021
+ if (updateType === '81') {
1022
+ self.handleSelectUserPopupShow('加签', true, (checkResult) => this.updateMultitaskInstance('81', checkResult.map(item => item.userId).join(',')), 'addMultitaskInstance')
1023
+ } else if (updateType === '83') {
1024
+ getUnapprovedListOfMultiNode(self.taskId).then(res => {
1025
+ if (res.data.code === '200') {
1026
+ self.unapprovedAssigneeList = res.data.data.map(item => ({...item, userId: item.auditId, organName: item.auditUserOrgan ? item.auditUserOrgan.split(':')[1] : '未知部门'}))
1027
+ self.selectReductionUserPopupShow = true
1028
+ } else {
1029
+ Toast.fail(resp.data.message)
1030
+ }
1031
+ this.selectUserPopupShow = false;
1032
+ }).catch(err => {
1033
+ console.log(err)
1034
+ this.selectUserPopupShow = false;
1035
+ })
1036
+ }
1037
+ },
1038
+ updateMultitaskInstance(updateType, assignees) {
1039
+ const self = this
1040
+ updateMultitaskInstance(self.taskId, self.applyId, assignees, updateType).then(res => {
1041
+ res.data.code == 200 ? Toast.success(res.data.message) :Toast.fail(res.data.message)
1042
+ self.selectUserPopupShow = false;
1043
+ self.selectReductionUserPopupShow = false
1044
+ }).catch(err => {
1045
+ console.log(err)
1046
+ self.selectUserPopupShow = false;
1047
+ self.selectReductionUserPopupShow = false
1048
+ })
1049
+ },
749
1050
  audit(auditResult) {
750
1051
  console.log('点击了审批:', auditResult);
751
1052
 
752
1053
  if (this.approvalForm.auditOpinion === '' || this.approvalForm.auditOpinion == null) {
753
- if (!this.handleButtons || this.handleButtons.includes('auditOpinion')) {
1054
+ if (!this.handleButtons || (this.handleButtons.includes('auditOpinion') && this.auditResult !== '30' && this.auditResult !== '31')) {
754
1055
  this.updateActiveTab()
755
1056
  this.approvalError = true
756
1057
  Toast({message: '请输入审批意见', duration: '500'});
757
1058
  return
758
1059
  } else {
759
- this.auditParams.auditOpinion = this.getAuditButtonStatus(this.auditResult);
1060
+ this.auditParams.auditOpinion = this.getAuditButtonStatus(auditResult);
760
1061
  }
761
1062
  }
762
1063
 
@@ -785,21 +1086,35 @@ export default {
785
1086
  //字段不清楚 暂不传递
786
1087
  params: null,
787
1088
  }
788
- self.businessFormSave ? self.businessFormSave(self.handleSaveResult) : self.handleSaveResult(true)
1089
+ let auditResult = {
1090
+ code: self.auditResult,
1091
+ name: self.getAuditButtonStatus(self.auditResult)
1092
+ }
1093
+ self.businessFormSave ? self.businessFormSave(self.handleSaveResult, auditResult) : self.handleSaveResult(true)
789
1094
  },
790
- handleSaveResult(saveResult) {
1095
+ handleSaveResult(saveResult, businessParams) {
791
1096
  if (saveResult) {
792
- this.executeButtonAction();
1097
+ this.executeButtonAction(businessParams);
793
1098
  } else {
794
1099
  console.error('保存失败');
795
1100
  }
796
1101
  },
797
1102
 
798
- executeButtonAction() {
1103
+ executeButtonAction(businessParams) {
799
1104
  const self = this;
1105
+ if (businessParams) {
1106
+ self.auditParams.params = JSON.stringify(businessParams)
1107
+ }
1108
+ let auditResult = {
1109
+ code: self.auditResult,
1110
+ name: self.getAuditButtonStatus(self.auditResult)
1111
+ }
800
1112
  if (self.auditResult === '82') {
801
1113
  // 处理转办逻辑
802
- this.selectHandlePopupShow = true;
1114
+ this.handleSelectUserPopupShow('指定办理人', false, (checkResult) => this.handleSelectResult(checkResult), 'auditTo82')
1115
+ } else if (self.auditResult === '84') {
1116
+ // 处理委派逻辑
1117
+ this.handleSelectUserPopupShow('委派', false, (checkResult) => this.handleDelegateTaskSelectResult(checkResult), 'delegateTaskTask')
803
1118
  } else if (self.auditResult === '80') {
804
1119
  // 处理调转到指定节点逻辑
805
1120
  self.getNodesBehind()
@@ -813,6 +1128,7 @@ export default {
813
1128
  // 处理驳回上一节点逻辑
814
1129
  getPreNode(self.auditParams).then(function (resp) {
815
1130
  let result = resp.data
1131
+ // 上一节点为原点
816
1132
  if (result.code === '30010') {
817
1133
  Dialog.confirm({
818
1134
  title: "提示",
@@ -823,8 +1139,23 @@ export default {
823
1139
  self.auditRequest(self.auditParams)
824
1140
  }).catch(() => {
825
1141
  })
1142
+ } else if (result.code == '30013' || result.code == '30014') {
1143
+ //前序节点为子流程或当前节点为子流程第一个节点不可驳回上一级节点
1144
+ const message = self.rejectName ? self.replaceName(result.message, '驳回', self.rejectName) : result.message
1145
+ Toast.fail(message)
1146
+ //避免未提示消息直接回调
1147
+ setTimeout(() => {
1148
+ self.executionCompleted(false, null, null, auditResult, self.taskId)
1149
+ }, 1000)
1150
+ } else if (result.code == '10012') {
1151
+ // 数据同步
1152
+ self.disable = false
1153
+ Toast.fail(result.message)
1154
+ setTimeout(() => {
1155
+ self.executionCompleted(true, null, null, auditResult, self.taskId)
1156
+ }, 1000)
826
1157
  } else {
827
- self.auditRequest(self.auditParams)
1158
+ self.handleButtons && self.handleButtons.includes('rejectProcessControl') ? self.getProcessAttributes() : self.auditRequest(self.auditParams)
828
1159
  }
829
1160
  })
830
1161
  }).catch(() => {
@@ -848,6 +1179,82 @@ export default {
848
1179
  message: message,
849
1180
  })
850
1181
  },
1182
+ getProcessAttributes(targetTaskNodeProcessControl){
1183
+ const self = this
1184
+ self.rejectAttributeList = []
1185
+ if (!targetTaskNodeProcessControl || targetTaskNodeProcessControl != 'jump') {
1186
+ self.rejectAttributeList.push({
1187
+ _checked: true,
1188
+ type: 'inSequence',
1189
+ name: '按顺序流转'
1190
+ })
1191
+ }
1192
+ getProcessAttributes(self.procId, self.taskNode).then(resp => {
1193
+ if (resp.data.code === '200') {
1194
+ if ((self.auditParams.auditResult == '70' && !resp.data.data.jumpFirstNode) || (self.auditParams.auditResult == '40' && (resp.data.data.multiNode || resp.data.data.preNodeGateway))) {
1195
+ self.auditRequest(self.auditParams)
1196
+ return
1197
+ } else {
1198
+ let param = {
1199
+ procId: self.procId,
1200
+ applyId: self.applyId,
1201
+ taskNode: self.taskNode,
1202
+ taskId: self.curTaskId,
1203
+ }
1204
+ listAll(self.procId, self.applyId, self.taskNode, self.taskId).then(todoResp => {
1205
+ if (todoResp.data.code === '200') {
1206
+ const rows = todoResp.data.data.rows
1207
+ self.taskAuditUserList = rows.map(item => item.auditName)
1208
+ const isMultiNode = resp.data.data.multiNode
1209
+ if (isMultiNode) {
1210
+ const parentExecutionId = String(rows[0].parentExecutionId).trim();
1211
+ const nodeDone = self.processHistory.filter(item => item[0].taskNode === self.taskNode && String(item[0].parentExecutionId).trim() === parentExecutionId)
1212
+ if (nodeDone) {
1213
+ const doneAuditName = nodeDone[0].flatMap(item => item.auditName);
1214
+ self.taskAuditUserList = new Set([...doneAuditName, ...self.taskAuditUserList])
1215
+ }
1216
+ }
1217
+ if (self.auditParams.auditResult == '40' || self.taskAuditUserList.length === 1) {
1218
+ self.rejectAttributeList.push(
1219
+ {
1220
+ type: 'returnToCurrentAuditUser',
1221
+ name: '返回我'
1222
+ }
1223
+ )
1224
+ } else {
1225
+ self.rejectAttributeList.push(
1226
+ {
1227
+ type: 'returnToCurrentNode',
1228
+ name: '返回本节点'
1229
+ }
1230
+ )
1231
+ if (!isMultiNode) {
1232
+ self.rejectAttributeList.push(
1233
+ {
1234
+ type: 'returnToCurrentAuditUser',
1235
+ name: '返回我'
1236
+ }
1237
+ )
1238
+ }
1239
+ }
1240
+ if (self.auditParams.auditResult == '70' || self.auditParams.auditResult == '40') {
1241
+ 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
+ }
1245
+ }
1246
+ }).catch((err) => {
1247
+ console.log(err)
1248
+ })
1249
+ }
1250
+ } else {
1251
+ self.$Message.error(resp.data.message)
1252
+ }
1253
+ }).catch(err => {
1254
+ console.log(err);
1255
+ Toast.fail('获取流程配置失败')
1256
+ })
1257
+ },
851
1258
  auditRequest(auditParams) {
852
1259
  let self = this
853
1260
  let auditResult = {
@@ -879,6 +1286,7 @@ export default {
879
1286
  this.executionCompleted(false, null, null, auditResult, self.taskId)
880
1287
  Toast.fail(result.message);
881
1288
  }
1289
+ this.selectUserPopupShow = false;
882
1290
  })
883
1291
  },
884
1292
  getNodesBehind() {
@@ -914,12 +1322,46 @@ export default {
914
1322
  this.nodeListDialogShow = false
915
1323
  this.auditResult = '';
916
1324
  this.radio = '';
1325
+ this.rejectAttribute = 'inSequence';
1326
+ this.targetTaskNodeProcessControl = ''
1327
+ },
1328
+ onBeforeClose(action, done) {
1329
+ const self = this;
1330
+ if (action === 'confirm') {
1331
+ if (!self.nodeList[self.radio]) {
1332
+ Toast.fail('请选择驳回节点!');
1333
+ done(false);
1334
+ return;
1335
+ }
1336
+ done(true);
1337
+ } else {
1338
+ done(true);
1339
+ }
1340
+ },
1341
+ selectNode(currentRow, index) {
1342
+ console.log(currentRow);
1343
+ this.radio = index
1344
+ this.targetTaskNodeProcessControl = currentRow.processControl
917
1345
  },
918
1346
  processJumpSpecifiedNode() {
919
1347
  const self = this;
1348
+ if (!self.nodeList[self.radio]) return;
920
1349
  self.auditParams.targetTaskNode = self.nodeList[self.radio].taskNode;
1350
+ self.auditParams.rejectAttribute = self.rejectAttribute ? self.rejectAttribute : 'inSequence'
921
1351
  self.auditRequest(self.auditParams)
922
1352
  },
1353
+ processRejectNode() {
1354
+ const self = this;
1355
+ self.auditParams.rejectAttribute = self.rejectAttribute ? self.rejectAttribute : 'inSequence'
1356
+ self.auditRequest(self.auditParams)
1357
+ },
1358
+ rejectAttributesDialogShowClose(){
1359
+ //重置
1360
+ this.rejectAttributesBoxShow = false
1361
+ this.auditResult = '';
1362
+ this.radio = '';
1363
+ this.rejectAttribute = 'inSequence';
1364
+ },
923
1365
 
924
1366
  showAuditDetail(auditComment) {
925
1367
  Dialog.alert({
@@ -951,6 +1393,21 @@ export default {
951
1393
  }
952
1394
  });
953
1395
  },
1396
+ handleDelegateTaskSelectResult(checkResult) {
1397
+ console.log('Selected result:', checkResult);
1398
+ if (!checkResult) {
1399
+ Toast('请选择委派人员')
1400
+ return
1401
+ }
1402
+ this.auditParams.selectedUserId = checkResult[0].userId
1403
+ Dialog.confirm({
1404
+ message: '确定执行:' + this.getAuditButtonStatus(this.auditResult) + ' ?',
1405
+ }).then(() => {
1406
+ this.auditRequest(this.auditParams)
1407
+ }).catch(() => {
1408
+ // on cancel
1409
+ });
1410
+ },
954
1411
  handleSelectResult(checkResult) {
955
1412
  console.log('Selected result:', checkResult);
956
1413
  const self = this;
@@ -963,7 +1420,16 @@ export default {
963
1420
  self.auditParams.selectedUserId = checkResult[0].userId;
964
1421
  self.auditRequest(self.auditParams);
965
1422
  }
966
- self.selectHandlePopupShow = false;
1423
+ self.selectUserPopupShow = false;
1424
+ },
1425
+ handleAssigneeSelectResult(checkResult) {
1426
+ console.log('Selected result:', checkResult);
1427
+ this.nextNodeProcessedData[this.nextNodeHandleIndex].assignee.id = checkResult[0].userId;
1428
+ this.nextNodeProcessedData[this.nextNodeHandleIndex].assignee.name = checkResult[0].userName;
1429
+ if (!this.nextNodePopupShow){
1430
+ this.handleSelect('select')
1431
+ }
1432
+ this.selectUserPopupShow = false;
967
1433
  },
968
1434
  selectOrganizeHandle(handle, checkedNodes) {
969
1435
  if (handle === 'select') {
@@ -975,12 +1441,15 @@ export default {
975
1441
  }
976
1442
  this.selectOrganizePopupShow = false
977
1443
  },
978
- selectUserHandle(checkResult) {
1444
+ selectCandUserHandle(checkResult) {
979
1445
  this.nextNodeProcessedData[this.nextNodeHandleIndex].candidateGroups.users =
980
1446
  checkResult.map(item => ({
981
1447
  name: item.userName,
982
1448
  id: item.userId,
983
1449
  }));
1450
+ if (!this.nextNodePopupShow){
1451
+ this.handleSelect('select')
1452
+ }
984
1453
  this.selectUserPopupShow = false;
985
1454
  },
986
1455
  selectPositionHandle(handle, result) {
@@ -1015,6 +1484,18 @@ export default {
1015
1484
  if (!this.activeFirstTab) {
1016
1485
  this.updateActiveTab();
1017
1486
  }
1487
+ },
1488
+ watch: {
1489
+ targetTaskNodeProcessControl(val) {
1490
+ const self = this
1491
+ if (self.handleButtons && self.handleButtons.includes('rejectProcessControl')) {
1492
+ if (val == 'inSequence') {
1493
+ self.showProcessControl = false
1494
+ return
1495
+ }
1496
+ self.getProcessAttributes(val)
1497
+ }
1498
+ },
1018
1499
  }
1019
1500
 
1020
1501
  };
@@ -1086,6 +1567,17 @@ export default {
1086
1567
  background-color: #1989fa;
1087
1568
  }
1088
1569
 
1570
+ .disagree {
1571
+ color: #fff;
1572
+ background: linear-gradient(90deg, #ff4d4f, #f5222d);
1573
+ border-radius: 9px;
1574
+ margin: 0 20px;
1575
+ }
1576
+
1577
+ .disagree:active {
1578
+ background-color: #ff7875;
1579
+ }
1580
+
1089
1581
  .title-info {
1090
1582
  font-weight: bold;
1091
1583
  font-size: 14px;
@@ -1158,7 +1650,7 @@ export default {
1158
1650
  font-size: 15px;
1159
1651
  }
1160
1652
 
1161
- .table-row {
1653
+ .table-row, .reject-attribute {
1162
1654
  display: flex;
1163
1655
  border-bottom: 1px solid #ebedf0;
1164
1656
  font-size: 14px;
@@ -1175,6 +1667,14 @@ export default {
1175
1667
  border-bottom: none;
1176
1668
  }
1177
1669
 
1670
+ .reject-attribute > div {
1671
+ padding: 10px 10px 10px 5px;
1672
+ }
1673
+ .reject-attribute .van-radio {
1674
+ padding: 5px;
1675
+ justify-content: flex-start !important;
1676
+ }
1677
+
1178
1678
  .van-radio {
1179
1679
  justify-content: center;
1180
1680
  }