@lambo-design-mobile/workflow-approve 1.0.0-beta.1

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.
@@ -0,0 +1,1027 @@
1
+ <template>
2
+ <div>
3
+ <van-tabs ref="tabs" style="padding-bottom: 60px" v-model="active" @click="onTabClick" animated>
4
+ <slot name="business-content"/>
5
+ <van-tab title="流程信息">
6
+ <div style="padding: 0 10px">
7
+ <div v-if="!isDetail">
8
+ <div class="title-info">
9
+ <van-icon class="info-icon" style="background-color: #0d88ff" name="cluster-o"/>
10
+ 审批信息
11
+ </div>
12
+ <div class="approvalForm-section">
13
+ <van-cell-group>
14
+ <van-field
15
+ v-model="approvalForm.auditOpinion"
16
+ rows="3"
17
+ autosize
18
+ required
19
+ label="审批意见"
20
+ type="textarea"
21
+ maxlength="50"
22
+ placeholder="请输入"
23
+ show-word-limit
24
+ />
25
+ <div v-if="approvalForm.nextNode!==''">
26
+ <van-cell title="下一环节" :value="approvalForm.nextNode"/>
27
+ <van-cell @click="nextNodePopupShow = true" title="办理人员" value-class="approvalForm-value"
28
+ :value="approvalForm.handlingPersonnel">
29
+ <template #right-icon>
30
+ <van-icon name="friends-o" size="16" style="line-height: inherit;padding-left: 5px"/>
31
+ </template>
32
+ </van-cell>
33
+ <div class="van-hairline--bottom"></div>
34
+ </div>
35
+ <div v-else>
36
+ <van-cell title="下一环节" value="无下一环节"/>
37
+ <div class="van-hairline--bottom"></div>
38
+ </div>
39
+ <div v-if="handleButtons.includes('attachmentFile')">
40
+ <upload-file :multiple="true"
41
+ :oss-server-context="config.smartFlowServerContext"
42
+ :oss-file-put-url="config.ossFilePutUrl"
43
+ :oss-file-get-url="config.ossFileGetUrl"
44
+ :fileList="fileList"
45
+ @upload-result="uploadFile">
46
+ </upload-file>
47
+ </div>
48
+
49
+ </van-cell-group>
50
+ </div>
51
+ </div>
52
+ <div>
53
+ <div class="title-info">
54
+ <van-icon class="info-icon" style="background-color: #ff7f02" name="orders-o"/>
55
+ 审批记录
56
+ </div>
57
+ <div class="approvalForm-records">
58
+ <van-collapse v-model="activeNames">
59
+ <van-collapse-item v-for="(items,index) in processHistory" :key="index" :title="items[0].taskName"
60
+ :name="index">
61
+ <div v-for="(item,index) in items" :key="index" class="record-item">
62
+ <van-cell center>
63
+ <template #icon>
64
+ <div
65
+ v-if="['通过', '跳转指定节点'].includes(getAuditStatus(item.auditResult).text)"
66
+ class="completed-icon">
67
+ <van-icon class="iconfont" class-prefix='icon' :name=getAuditStatus(item.auditResult).icon
68
+ size="30px" color="#fff"/>
69
+ </div>
70
+ <div v-else class="pending-icon">
71
+ <van-icon class="iconfont" class-prefix='icon' :name=getAuditStatus(item.auditResult).icon
72
+ size="30px" color="#fff"/>
73
+ </div>
74
+ </template>
75
+ <template #title>
76
+ <div :style="{ color: getAuditStatus(item.auditResult).color }"
77
+ style="font-size: 16px;font-weight: bold;padding:5px 0;">
78
+ {{ item.auditName[0] }}
79
+ </div>
80
+ <div>{{ item.auditOrganName[0] }}
81
+ <span v-if="item.auditComment" @click="showAuditDetail(item.auditComment)">
82
+ | 审批意见 <van-icon name="comment-circle-o"/>
83
+ </span>
84
+ </div>
85
+ </template>
86
+ <template #label>
87
+ <div>{{ item.startDate }}{{ item.auditDate }}</div>
88
+ </template>
89
+ <template #default>
90
+ <span :style="{ color: getAuditStatus(item.auditResult).color }">{{
91
+ getAuditStatus(item.auditResult).text
92
+ }}</span>
93
+ </template>
94
+ </van-cell>
95
+ <div v-if="index !== items.length - 1" class="van-hairline--bottom"></div>
96
+ </div>
97
+ </van-collapse-item>
98
+
99
+ </van-collapse>
100
+ </div>
101
+ </div>
102
+ <div v-if="handleButtons.includes('attachmentFile')">
103
+ <div class="title-info">
104
+ <van-icon class="info-icon" style="background-color: #029ea0" name="link-o"/>
105
+ 查看附件
106
+ </div>
107
+ <div v-if="files.length > 0">
108
+ <div class="attachments">
109
+ <div v-for="item in files" :key="item.name">
110
+ <van-cell style="margin-bottom: 20px;"
111
+ center>
112
+ <template v-slot:icon>
113
+ <div class="file-icon" :style="{ backgroundColor: getBackgroundColor(item.fileName) }">
114
+ {{ getFileIconText(item.fileName) }}
115
+ </div>
116
+ </template>
117
+ <template v-slot:title>
118
+ <div style="font-size: 15px;font-weight: bold;padding:5px 0;
119
+ word-wrap: break-word;word-break: break-all; white-space: normal;">
120
+ {{ item.fileName }}
121
+ </div>
122
+ <div style="color: #969799;font-size: 14px;">
123
+ {{ item.uploadUserName + ' ' + '|' + ' ' + item.taskName }}
124
+ </div>
125
+ </template>
126
+ <template v-slot:label>
127
+ <div>上传时间</div>
128
+ </template>
129
+ <template v-slot:default>
130
+ <span @click="getAttach(item)" class="iconfont icon-cloud-download"
131
+ style="font-size: 20px;color: #027efe"></span>
132
+ </template>
133
+ </van-cell>
134
+ </div>
135
+ </div>
136
+ </div>
137
+ <div v-else>
138
+ <van-empty description="暂无附件"/>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ </van-tab>
143
+ </van-tabs>
144
+ <!-- 底部按钮 等其他内容 -->
145
+ <div>
146
+ <van-popup v-model="popupShow" style="padding-top: 40px" closeable round position="bottom">
147
+ <div v-if="handleButtons.includes('auditTo70')" class="popup-option" @click="audit('70')">
148
+ {{ getAuditButtonStatus(70) }}
149
+ </div>
150
+ <div v-if="handleButtons.includes('auditTo40')" class="popup-option highlighted"
151
+ @click="audit('40')">
152
+ {{ getAuditButtonStatus(40) }}
153
+ </div>
154
+ <div v-if="handleButtons.includes('auditTo90')" class="popup-option" @click="audit('90')">
155
+ {{ getAuditButtonStatus(90) }}
156
+ </div>
157
+ <div v-if="handleButtons.includes('auditTo80')" class="popup-option" @click="audit('80')">
158
+ {{ getAuditButtonStatus(80) }}
159
+ </div>
160
+ <div v-if="handleButtons.includes('auditTo82')" class="popup-option" @click="audit('82')">
161
+ {{ getAuditButtonStatus(82) }}
162
+ </div>
163
+ <div v-if="handleButtons.includes('auditTo50')" class="popup-option" @click="audit('50')">
164
+ {{ getAuditButtonStatus(50) }}
165
+ </div>
166
+ </van-popup>
167
+ <div class="custom-bottom-bar">
168
+ <div v-if="!isDetail" class="bar-item" @click="onMore">更多</div>
169
+ <div class="bar-item" @click="onTrack">流程跟踪</div>
170
+ <div v-if="!isDetail" class="bar-item approve" @click="audit('30')">{{ getAuditButtonStatus(30) }}</div>
171
+ </div>
172
+ </div>
173
+ <van-popup @click-close-icon="handleSelect('cancel')" v-model="nextNodePopupShow"
174
+ style="height:70%; background:#f7f8fa;padding-bottom:60px" closeable round
175
+ position="bottom">
176
+ <div class="van-nav-bar__content" style="height: 55px">
177
+ <div class="van-nav-bar__title"> 指定下一环节 办理人员</div>
178
+ </div>
179
+ <van-cell-group v-for="(item,index) in nextNodeProcessedData" :key="index"
180
+ style="margin-bottom: 10px;" inset>
181
+ <van-cell title="节点名称" :value="item.name"></van-cell>
182
+ <van-field name="radio" label="人员类型" input-align="right">
183
+ <template #input>
184
+ <van-radio-group v-model="item.personType" direction="horizontal">
185
+ <van-radio name="assignee">办理人</van-radio>
186
+ <van-radio name="candidate">候选人</van-radio>
187
+ </van-radio-group>
188
+ </template>
189
+ </van-field>
190
+ <van-cell v-if="item.personType === 'assignee'" title="办理人"
191
+ :value="(item.assignee.name ? item.assignee.name : '未指定') + ':' + (item.assignee.id ? item.assignee.id : '未指定')"
192
+ @click="nextNodeHandleIndex=index;selectHandlePopupShow=true;">
193
+ </van-cell>
194
+ <van-cell v-if="item.personType === 'candidate'" title="候选人" @click="toggleDetails(index)"
195
+ :is-link="true" :arrow-direction="detailsVisible[index] ? 'down' : ''">
196
+ </van-cell>
197
+ <div v-if="detailsVisible[index] && item.personType === 'candidate'">
198
+ <van-cell @click="clickCandidateGroup()" title-style="padding-left:10px" title="候选用户"
199
+ :value="(item.candidateGroups?.users ?? []).map(user => user.name).join(',')">
200
+ </van-cell>
201
+ <van-cell @click="clickCandidateGroup()" title-style="padding-left:10px" title="候选岗位"
202
+ :value="(item.candidateGroups?.positions ?? []).map(position => position.name).join(',')">
203
+ </van-cell>
204
+ <van-cell @click="clickCandidateGroup()" title-style="padding-left:10px" title="候选角色"
205
+ :value="(item.candidateGroups?.roles ?? []).map(role => role.name).join(',')">
206
+ </van-cell>
207
+ <van-cell @click="clickCandidateGroup()" title-style="padding-left:10px" title="候选组织"
208
+ :value="(item.candidateGroups?.organs ?? []).map(organ => organ.name).join(',')">
209
+ </van-cell>
210
+ </div>
211
+ <van-cell center title="停留时间">
212
+ <template #default>
213
+ <div style="display: flex; align-items: center;">
214
+ <div style="display: flex; align-items: center; margin-right: 16px;">
215
+ <van-stepper disable-input button-size="22" min="0" v-model="item.remainDay"/>
216
+ <span style="margin-left: 4px;">天</span>
217
+ </div>
218
+ <div style="display: flex; align-items: center;">
219
+ <van-stepper disable-input button-size="22" min="0" v-model="item.remainTime"/>
220
+ <span style="margin-left: 4px;">小时</span>
221
+ </div>
222
+ </div>
223
+ </template>
224
+ </van-cell>
225
+ <van-cell title="任务提前">
226
+ <template #default>
227
+ <div style="display: flex; align-items: center;">
228
+ <div style="display: flex; align-items: center; margin-right: 16px;">
229
+ <van-stepper disable-input button-size="22" min="0" v-model="item.inAdvanceDay"/>
230
+ <span style="margin-left: 4px;">天</span>
231
+ </div>
232
+ <div style="display: flex; align-items: center;">
233
+ <van-stepper disable-input button-size="22" min="0" v-model="item.inAdvanceTime"/>
234
+ <span style="margin-left: 4px;">小时</span>
235
+ </div>
236
+ </div>
237
+ </template>
238
+ </van-cell>
239
+ <van-field
240
+ readonly
241
+ clickable
242
+ input-align="right"
243
+ name="picker"
244
+ :value="(handleTypeList.find(handleType => handleType.value === item.processing) || {}).label"
245
+ label="处理方式"
246
+ placeholder="点击选择处理方式"
247
+ @click="$set(showPicker, index, true)"
248
+ />
249
+ <van-popup v-model="showPicker[index]" position="bottom">
250
+ <van-picker
251
+ show-toolbar
252
+ :columns="handleTypeList.map(handleType => handleType.label)"
253
+ @confirm="(value, selectedIndex) => onHandleTypeConfirm(value, selectedIndex, index)"
254
+ @cancel="$set(showPicker, index, false)"
255
+ />
256
+ </van-popup>
257
+ </van-cell-group>
258
+ <div class="custom-bottom-bar">
259
+ <div class="bar-item" @click="handleSelect('cancel')">重置</div>
260
+ <div class="bar-item approve" @click="handleSelect('select')">确认</div>
261
+ </div>
262
+ </van-popup>
263
+ <van-popup v-model="selectHandlePopupShow" closeable round position="bottom"
264
+ :style="{ height: '80%' }" style="background: #f7f8fa">
265
+ <select-handle @selectHandle="handleSelectResult"></select-handle>
266
+ </van-popup>
267
+ <van-dialog v-model="nodeListDialogShow" @confirm="processJumpSpecifiedNode()" @close="nodeListDialogShowClose()"
268
+ :title="getAuditButtonStatus(auditResult)" show-cancel-button>
269
+ <van-radio-group v-model="radio">
270
+ <van-cell-group>
271
+ <div class="table-header">
272
+ <div>节点名称</div>
273
+ <div>节点状态</div>
274
+ <div>选择</div>
275
+ </div>
276
+ <div class="table-row"
277
+ v-for="(item, index) in nodeList" :key="index"
278
+ @click="radio = index">
279
+ <div>{{ item.taskName }}</div>
280
+ <div>
281
+ <van-tag v-if="item.auditResult" :type="getAuditStatus(item.auditResult).type">
282
+ {{ getAuditStatus(item.auditResult).text }}
283
+ </van-tag>
284
+ <van-tag type="warning" v-else>
285
+ 待审批
286
+ </van-tag>
287
+ </div>
288
+ <div>
289
+ <van-radio :name='index'/>
290
+ </div>
291
+ </div>
292
+ </van-cell-group>
293
+ </van-radio-group>
294
+ </van-dialog>
295
+ </div>
296
+ </template>
297
+
298
+ <script>
299
+ import {
300
+ audit, config,
301
+ getAllPreNodes,
302
+ getAttachmentList,
303
+ getNextNodes,
304
+ getNodesBehind,
305
+ getPreNode,
306
+ getProcessHis
307
+ } from "../api";
308
+ import {Dialog, Toast} from "vant";
309
+ import UploadFile from '@lambo-design-mobile/upload-file';
310
+ import {flutterUtil} from "./utils/flutterUtil";
311
+ import SelectHandle from "./SelectHandle.vue";
312
+
313
+
314
+ export default {
315
+ computed: {
316
+ config() {
317
+ return config
318
+ }
319
+ },
320
+ components: {SelectHandle, UploadFile},
321
+ props: {
322
+ //业务表单保存方法
323
+ businessFormSave: {
324
+ type: Function,
325
+ required: false
326
+ },
327
+ //按钮执行完毕回调方法
328
+ executionCompleted: {
329
+ type: Function,
330
+ required: false,
331
+ default: () => {
332
+ } // 默认是一个空函数
333
+ },
334
+ // 是否是流程详情页面
335
+ isDetail: {
336
+ type: Boolean,
337
+ required: false,
338
+ default: false
339
+ },
340
+ processTraceRouterName: {
341
+ type: String,
342
+ required: true,
343
+ }
344
+ },
345
+ data() {
346
+ return {
347
+ auditResult: '',
348
+ //初始化路由数据
349
+ taskNode: this.$route.query.taskNode,
350
+ procId: this.$route.query.procId,
351
+ instanceId: this.$route.query.instanceId,
352
+ applyId: this.$route.query.applyId,
353
+ formUrl: this.$route.query.formUrl,
354
+ taskId: this.$route.query.taskId,
355
+ handleButtons: this.$route.query.handleButtons,
356
+
357
+ approvalForm: {
358
+ auditOpinion: '',
359
+ nextNode: '',
360
+ handlingPersonnel: '',
361
+ nodeConfigMaps: '',
362
+ },
363
+
364
+ processHistory: [],
365
+ tableData: [],
366
+
367
+ bpmnViewer: null,
368
+
369
+ activeNames: [0],
370
+ popupShow: false,
371
+
372
+ nextNodePopupShow: false,
373
+ nextNodeHandleIndex: 0,
374
+ nextNodeProcessedData: [], // 用于存储处理下一节点的数据
375
+ detailsVisible: {}, // 用于跟踪每个项目的详细内容是否可见
376
+ handleTypeList: [
377
+ {
378
+ value: '00',
379
+ label: '只预警不处理'
380
+ },
381
+ {
382
+ value: '10',
383
+ label: '自动同意'
384
+ },
385
+ {
386
+ value: '20',
387
+ label: '直接终止流程'
388
+ },
389
+ {
390
+ value: '90',
391
+ label: '自动驳回'
392
+ }
393
+ ],
394
+ handleTypeValue: '',
395
+ showPicker: {},
396
+
397
+ nodeListDialogShow: false,
398
+ nodeList: [],
399
+ radio: '',
400
+
401
+ selectHandlePopupShow: false,
402
+
403
+ active: 0,
404
+ //给附件列表展示
405
+ files: [],
406
+ //文件上传列表
407
+ fileList: []
408
+ };
409
+ },
410
+ methods: {
411
+ initData() {
412
+ this.getProcessHistory();
413
+ this.getNextNodes();
414
+ this.getAttachmentList();
415
+ //设置提示持续时间默认为500ms
416
+ Toast.setDefaultOptions({duration: 500});
417
+ },
418
+ getProcessHistory() {
419
+ getProcessHis(this.applyId, this.instanceId, this.procId, this.taskId).then(resp => {
420
+ if (resp.data.code === '200') {
421
+ this.processHistory = resp.data.data
422
+ }
423
+ })
424
+ },
425
+ getNextNodes() {
426
+ getNextNodes(this.procId, this.taskNode).then(resp => {
427
+ const result = resp.data;
428
+ if (result.code === '200') {
429
+ this.nextNodeProcessedData = this.processData(result.data);
430
+ this.approvalForm.nextNode = result.data.map(node => node.name).join(',');
431
+ this.approvalForm.handlingPersonnel = result.data
432
+ .map(node => node.assignee?.name || '未指定')
433
+ .join(',');
434
+ }
435
+ })
436
+ },
437
+ processData(data) {
438
+ return data.map(node => {
439
+ node.personType = node.assignee ? "assignee" : "candidate";
440
+ if (node.assignee) {
441
+ node.candidateGroups = {
442
+ organs: [],
443
+ roles: [],
444
+ positions: [],
445
+ users: [],
446
+ }
447
+ } else {
448
+ node.assignee = {
449
+ name: "",
450
+ id: ""
451
+ }
452
+ }
453
+ if (node.timeLimit) {
454
+ let expireTime = node.timeLimit.split(";")[0].split(":")[1];
455
+ let warningTime = node.timeLimit.split(";")[1].split(":")[1];
456
+ let handleType = node.timeLimit.split(";")[2].split(":")[1];
457
+ let days = expireTime.slice(0, expireTime.indexOf("D")); //过期天
458
+ let hourOfDay = expireTime.slice(expireTime.indexOf("D") + 1, expireTime.indexOf("H")); //过期小时
459
+ let daysWarn = warningTime.slice(0, warningTime.indexOf("D")); //警告天
460
+ let hourOfDayWarn = warningTime.slice(warningTime.indexOf("D") + 1, warningTime.indexOf("H")); //警告小时
461
+ node.remainDay = parseInt(days);
462
+ node.remainTime = parseInt(hourOfDay);
463
+ node.inAdvanceDay = parseInt(daysWarn);
464
+ node.inAdvanceTime = parseInt(hourOfDayWarn);
465
+ node.processing = handleType;
466
+ } else {
467
+ node.remainDay = 0;
468
+ node.remainTime = 0;
469
+ node.inAdvanceDay = 0;
470
+ node.inAdvanceTime = 0;
471
+ node.processing = "00";
472
+ }
473
+ return node;
474
+ });
475
+ },
476
+ toggleDetails(index) {
477
+ this.$set(this.detailsVisible, index, !this.detailsVisible[index]);
478
+ },
479
+ onHandleTypeConfirm(selectedLabel, selectedIndex, index) {
480
+ // 更新 nextNodeProcessedData 为对应的 value
481
+ this.nextNodeProcessedData[index].processing = this.handleTypeList[selectedIndex].value;
482
+ // 关闭选择器
483
+ this.showPicker[index] = false;
484
+ },
485
+ clickCandidateGroup() {
486
+ Toast("请在电脑端指定节点候选人")
487
+ },
488
+ formattedCandidateGroupData(candidateGroups) {
489
+ // 获取ID 并通过分号连接
490
+ const organIds = candidateGroups.organs.map(organ => organ.id).join(';');
491
+ const userIds = candidateGroups.users.map(user => user.id).join(';');
492
+ const positionIds = candidateGroups.positions.map(position => position.id).join(';');
493
+ const roleIds = candidateGroups.roles.map(role => role.id).join(';');
494
+ // 拼接成目标格式
495
+ return `O:${organIds},U:${userIds},P:${positionIds},R:${roleIds},RP:,T:`;
496
+ },
497
+ handleSelect(handle) {
498
+ if (handle === 'select') {
499
+ //处理审批参数
500
+ let params = this.nextNodeProcessedData.reduce((acc, item) => {
501
+ acc[item.id] = {
502
+ needUpdate: 'true',
503
+ }
504
+ if (item.personType === "assignee") {
505
+ acc[item.id].assignee = item.assignee.id
506
+ }
507
+ if (item.personType === "candidate") {
508
+ acc[item.id].candidateGroup = this.formattedCandidateGroupData(item.candidateGroups)
509
+ }
510
+ let timeLimit = 'expireTime:0D0H;warningTime:0D0H;handleType:00'
511
+ if (!(item.remainDay === 0 && item.remainTime === 0 && item.inAdvanceDay === 0 && item.inAdvanceTime === 0)) {
512
+ timeLimit = 'expireTime:' + parseInt(item.remainDay) + "D" + parseInt(item.remainTime) + "H" + ";warningTime:" + parseInt(item.inAdvanceDay) + "D" + parseInt(item.inAdvanceTime) + "H;" + "handleType:" + item.processing;
513
+ acc[item.id].timeLimit = timeLimit
514
+ } else {
515
+ acc[item.id].timeLimit = ''
516
+ }
517
+ return acc;
518
+ }, {});
519
+ this.approvalForm.nodeConfigMaps = JSON.stringify(params)
520
+ this.approvalForm.handlingPersonnel = this.nextNodeProcessedData
521
+ .map(node => node.assignee?.name || '未指定')
522
+ .join(',');
523
+ this.nextNodePopupShow = false;
524
+ } else {
525
+ this.getNextNodes();
526
+ this.approvalForm.nodeConfigMaps = '';
527
+ this.nextNodePopupShow = false;
528
+ }
529
+ },
530
+ getAttachmentList() {
531
+ getAttachmentList(this.procId, this.applyId).then(resp => {
532
+ if (resp.data.code === '200') {
533
+ // 将 API 返回的数据赋值给 files
534
+ this.files = resp.data.data.rows;
535
+ }
536
+ });
537
+ },
538
+ onTabClick() {
539
+
540
+
541
+ },
542
+ getBackgroundColor(name) {
543
+ const ext = name.split('.').pop().toLowerCase();
544
+ switch (ext) {
545
+ case 'doc':
546
+ return '#027efe';
547
+ case 'docx':
548
+ return '#027efe';
549
+ case 'xls':
550
+ return '#02d1a6';
551
+ case 'xlsx':
552
+ return '#02d1a6';
553
+ case 'pdf':
554
+ return '#e55d56';
555
+ default:
556
+ return 'rgb(180,169,169)';
557
+ }
558
+ },
559
+ getFileIconText(name) {
560
+ const ext = name.split('.').pop().toLowerCase();
561
+ if (ext.startsWith('doc')) return 'W';
562
+ if (ext.startsWith('xls')) return 'X';
563
+ if (ext === 'pdf') return 'P';
564
+ return ext.charAt(0).toUpperCase();
565
+ },
566
+ getAuditStatus(auditResult) {
567
+ const statusMap = {
568
+ '30': {text: '通过', icon: "tongguo", color: '#0d88ff', type: 'success'},
569
+ '40': {text: '驳回上一节点', icon: "bohui", color: '#ed4014', type: 'danger'},
570
+ '50': {text: '驳回到原点', icon: "bohui", color: '#ed4014', type: 'danger'},
571
+ '51': {text: '流程作废', icon: "liuchengzuofei", color: '#ed4014', type: 'danger'},
572
+ '60': {text: '撤回', icon: "chehui", color: '#ed4014', type: 'warning'},
573
+ '80': {text: '跳转指定节点', icon: "tiaozhuan", color: '#0d88ff', type: 'primary'},
574
+ '90': {text: '驳回指定节点', icon: "bohui", color: '#ed4014', type: 'primary'},
575
+ };
576
+ return {
577
+ text: (statusMap[auditResult] && statusMap[auditResult].text) || '待审批',
578
+ icon: (statusMap[auditResult] && statusMap[auditResult].icon) || 'daishenpi',
579
+ color: (statusMap[auditResult] && statusMap[auditResult].color) || '#ff9900',
580
+ type: (statusMap[auditResult] && statusMap[auditResult].type) || 'warning',
581
+ };
582
+ },
583
+ getAuditButtonStatus(auditResult) {
584
+ let statusMap = {
585
+ '30': '通过',
586
+ '40': '驳回上一节点',
587
+ '50': '直接结束流程',
588
+ '70': '驳回到原点',
589
+ '80': '跳转指定节点',
590
+ '82': '指定他人处理',
591
+ '90': '驳回指定节点',
592
+ };
593
+ return statusMap[auditResult];
594
+ },
595
+
596
+ onMore() {
597
+ console.log("点击了更多");
598
+ this.popupShow = true
599
+ },
600
+ onTrack() {
601
+ const routeName = this.processTraceRouterName;
602
+ this.$router.push({
603
+ name: routeName,
604
+ query: {
605
+ applyId: this.applyId,
606
+ procId: this.procId,
607
+ }
608
+ })
609
+ },
610
+ audit(auditResult) {
611
+ console.log('点击了审批:', auditResult);
612
+
613
+ if (this.approvalForm.auditOpinion === '' || this.approvalForm.auditOpinion == null) {
614
+ if (!this.handleButtons || this.handleButtons.includes('auditOpinion')) {
615
+ Toast({message: '请输入审批意见', duration: '500'});
616
+ return
617
+ } else {
618
+ this.auditParams.auditOpinion = this.getAuditButtonStatus(self.auditResult);
619
+ }
620
+ }
621
+
622
+ // on confirm
623
+ this.auditResult = auditResult
624
+ this.submit()
625
+
626
+ // 关闭弹窗等
627
+ this.popupShow = false;
628
+
629
+ },
630
+ submit() {
631
+ let self = this;
632
+ self.auditParams = {
633
+ procId: self.procId,
634
+ applyId: self.applyId,
635
+ taskId: self.taskId,
636
+ auditOpinion: self.approvalForm.auditOpinion,
637
+ auditResult: self.auditResult,
638
+ nodeConfigMaps: self.approvalForm.nodeConfigMaps,
639
+
640
+ fileListStr: JSON.stringify(self.fileList),
641
+ //字段不清楚 暂不传递
642
+ params: null,
643
+ }
644
+ self.businessFormSave ? self.businessFormSave(self.handleSaveResult) : self.handleSaveResult(true)
645
+ },
646
+ handleSaveResult(saveResult) {
647
+ if (saveResult) {
648
+ this.executeButtonAction();
649
+ } else {
650
+ console.error('保存失败');
651
+ }
652
+ },
653
+
654
+ executeButtonAction() {
655
+ const self = this;
656
+ if (self.auditResult === '82') {
657
+ // 处理转办逻辑
658
+ this.selectHandlePopupShow = true;
659
+ } else if (self.auditResult === '80') {
660
+ // 处理调转到指定节点逻辑
661
+ self.getNodesBehind()
662
+ } else if (self.auditResult === '90') {
663
+ // 处理驳回到指定节点逻辑
664
+ self.getAllPreNodes()
665
+ } else if (self.auditResult === '40') {
666
+ Dialog.confirm({
667
+ message: '确定执行:' + self.getAuditButtonStatus(self.auditResult) + ' ?',
668
+ }).then(() => {
669
+ // 处理驳回上一节点逻辑
670
+ getPreNode(self.auditParams).then(function (resp) {
671
+ let result = resp.data
672
+ if (result.code === '30010') {
673
+ Dialog.confirm({
674
+ title: "提示",
675
+ message: result.message,
676
+ }).then(() => {
677
+ // on confirm
678
+ self.auditParams.auditResult = '70'
679
+ self.auditRequest(self.auditParams)
680
+ }).catch(() => {
681
+ })
682
+ } else {
683
+ self.auditRequest(self.auditParams)
684
+ }
685
+ })
686
+ }).catch(() => {
687
+ // on cancel
688
+ });
689
+ } else {
690
+ Dialog.confirm({
691
+ message: '确定执行:' + self.getAuditButtonStatus(self.auditResult) + ' ?',
692
+ }).then(() => {
693
+ self.auditRequest(self.auditParams)
694
+ }).catch(() => {
695
+ // on cancel
696
+ });
697
+ }
698
+ },
699
+ showToastLoading(message) {
700
+ //展示审批加载提示
701
+ return Toast.loading({
702
+ duration: 0, // 持续展示 toast
703
+ forbidClick: true,
704
+ message: message,
705
+ })
706
+ },
707
+ auditRequest(auditParams) {
708
+ let self = this
709
+ let auditResult = {
710
+ code: self.auditResult,
711
+ name: self.getAuditStatus(self.auditResult).text
712
+ }
713
+ self.showToastLoading('处理审批中');
714
+ //确认执行审批逻辑
715
+ audit(auditParams).then((resp) => { // 使用箭头函数
716
+ let result = resp.data
717
+ if (result.code === '200') {
718
+ if (result.data) {
719
+ if (Array.isArray(result.data)) {
720
+ let taskIds = result.data.map(item => item.id).join(',');
721
+ this.executionCompleted(true, result.data[0].processInstanceId, taskIds, auditResult, self.taskId);
722
+ } else if (result.data && typeof result.data === 'object') {
723
+ let taskIds = result.data.id; // 处理单个对象的情况
724
+ this.executionCompleted(true, result.data.processInstanceId, taskIds, auditResult, self.taskId);
725
+ }
726
+ } else {
727
+ this.executionCompleted(true, null, null);
728
+ }
729
+ Toast.success(result.message);
730
+ } else if (result.code === '20002') {
731
+ // 流程结束
732
+ this.executionCompleted(true, '流程已结束', '流程已结束', auditResult, self.taskId)
733
+ Toast.success(result.message);
734
+ } else {
735
+ this.executionCompleted(false, null, null, auditResult, self.taskId)
736
+ Toast.fail(result.message);
737
+ }
738
+ })
739
+ },
740
+ getNodesBehind() {
741
+ getNodesBehind(this.procId, this.taskId).then(resp => {
742
+ if (resp.data.code === '200') {
743
+ if (resp.data.data.length > 0) {
744
+ this.nodeList = resp.data.data
745
+ this.nodeListDialogShow = true
746
+ } else {
747
+ Toast.fail('当前流程无后续节点')
748
+ }
749
+ } else {
750
+ Toast.fail(resp.data.message)
751
+ }
752
+ })
753
+ },
754
+ getAllPreNodes() {
755
+ getAllPreNodes(this.procId, this.taskId).then(resp => {
756
+ if (resp.data.code === '200') {
757
+ if (resp.data.data.length > 0) {
758
+ this.nodeList = resp.data.data
759
+ this.nodeListDialogShow = true
760
+ } else {
761
+ Toast.fail('当前流程无前序节点')
762
+ }
763
+ } else {
764
+ Toast.fail(resp.data.message)
765
+ }
766
+ })
767
+ },
768
+ nodeListDialogShowClose() {
769
+ //重置
770
+ this.nodeListDialogShow = false
771
+ this.auditResult = '';
772
+ this.radio = '';
773
+ },
774
+ processJumpSpecifiedNode() {
775
+ const self = this;
776
+ self.auditParams.targetTaskNode = self.nodeList[self.radio].taskNode;
777
+ self.auditRequest(self.auditParams)
778
+ },
779
+
780
+ showAuditDetail(auditComment) {
781
+ Dialog.alert({
782
+ title: '审批意见',
783
+ message: auditComment,
784
+ }).then(() => {
785
+ // on close
786
+ });
787
+ },
788
+ uploadFile(fileList) {
789
+ fileList.forEach(item => {
790
+ console.log(item)
791
+ })
792
+ },
793
+ getAttach(item) {
794
+ let fjUrl = window.location.origin +
795
+ // api/smart-flow-server + /manage/oss/file/get/ + xxx
796
+ config.smartFlowServerContext + "/manage/oss/file/get/" + item.fileId
797
+ let fjName = item.fileName
798
+ flutterUtil.openFile(fjUrl, fjName);
799
+ //window.open(config.smartFlowServerContext + "/manage/oss/file/get/" + item.fileId, "_blank");
800
+ },
801
+ updateActiveTab() {
802
+ this.$nextTick(() => {
803
+ const tabsElement = this.$refs.tabs;
804
+ if (tabsElement) {
805
+ const numberOfTabs = tabsElement.$el.querySelectorAll('.van-tab').length;
806
+ this.active = numberOfTabs - 1;
807
+ }
808
+ });
809
+ },
810
+ handleSelectResult(checkResult) {
811
+ console.log('Selected result:', checkResult);
812
+ const self = this;
813
+ if (this.nextNodePopupShow === true) {
814
+ console.log("这里是选择办理人")
815
+ this.nextNodeProcessedData[this.nextNodeHandleIndex].assignee.id = checkResult[0].userId;
816
+ this.nextNodeProcessedData[this.nextNodeHandleIndex].assignee.name = checkResult[0].userName;
817
+ } else {
818
+ console.log("这里是选择转办人")
819
+ self.auditParams.selectedUserId = checkResult[0].userId;
820
+ self.auditRequest(self.auditParams);
821
+ }
822
+ self.selectHandlePopupShow = false;
823
+ },
824
+ },
825
+ activated() {
826
+ this.updateActiveTab();
827
+ },
828
+ mounted() {
829
+ this.initData()
830
+ // 默认选中最后一个tab页
831
+ this.updateActiveTab();
832
+ }
833
+
834
+ };
835
+ </script>
836
+
837
+ <style scoped>
838
+ @import 'styles/global.css';
839
+
840
+ ::v-deep .van-collapse-item__content {
841
+ padding: 0 0;
842
+ }
843
+
844
+ ::v-deep .van-uploader__file-name {
845
+ margin-top: 0;
846
+ }
847
+
848
+ ::v-deep .van-uploader__upload {
849
+ margin: 0;
850
+ }
851
+
852
+ /* 自定义标签页文字的样式 */
853
+ ::v-deep .van-tab--active .van-tab__text {
854
+ color: #3478f6; /* 设置激活状态下的文字颜色为蓝色 */
855
+ font-size: 16px; /* 增大激活状态下的字体大小 */
856
+ }
857
+
858
+ ::v-deep .van-tabs__line {
859
+ display: none;
860
+ }
861
+
862
+ .custom-bottom-bar {
863
+ position: fixed;
864
+ bottom: 0;
865
+ width: 100%;
866
+ display: flex;
867
+ justify-content: space-around;
868
+ background-color: #fff;
869
+ padding: 10px 0;
870
+ border-top: 1px solid #eaeaea;
871
+ box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.05);
872
+ z-index: 1000;
873
+ }
874
+
875
+ .bar-item {
876
+ flex: 1;
877
+ text-align: center;
878
+ padding: 10px 0;
879
+ font-size: 16px;
880
+ border-radius: 4px;
881
+ }
882
+
883
+ .bar-item:active {
884
+ background-color: #e6e6e6; /* 点击时的背景颜色 */
885
+ }
886
+
887
+ .approve {
888
+ color: #fff;
889
+ background: linear-gradient(90deg, #0096FF, #1677FF);
890
+ border-radius: 9px;
891
+ margin: 0 20px;
892
+ }
893
+
894
+ .approve:active {
895
+ background-color: #1989fa;
896
+ }
897
+
898
+ .title-info {
899
+ font-weight: bold;
900
+ font-size: 14px;
901
+ padding: 20px 10px;
902
+ }
903
+
904
+ .completed-icon {
905
+ width: 45px; /* 自定义宽度 */
906
+ height: 45px; /* 自定义高度 */
907
+ background-clip: padding-box; /* 使背景不填充边框 */
908
+
909
+ border-radius: 50%;
910
+ margin-right: 15px;
911
+
912
+ display: flex;
913
+ align-items: center;
914
+ justify-content: center;
915
+
916
+ background: linear-gradient(90deg, #0096FF, #1677FF);
917
+ border: 5px solid rgb(229, 244, 255); /* 半透明边框,颜色和背景色相同 */
918
+ }
919
+
920
+ .pending-icon {
921
+ width: 45px; /* 自定义宽度 */
922
+ height: 45px; /* 自定义高度 */
923
+ background-clip: padding-box; /* 使背景不填充边框 */
924
+
925
+ border-radius: 50%;
926
+ margin-right: 15px;
927
+
928
+ display: flex;
929
+ align-items: center;
930
+ justify-content: center;
931
+
932
+ background: linear-gradient(90deg, #FF7E00, #FFA200);
933
+ border: 5px solid rgb(255, 245, 229); /* 半透明边框,颜色和背景色相同 */
934
+ }
935
+
936
+ .info-icon {
937
+ display: inline-block;
938
+ color: white; /* 自定义文字颜色 */
939
+ padding: 3px 3px; /* 标签内边距 */
940
+ border-radius: 4px; /* 标签圆角 */
941
+ font-size: 13px; /* 字体大小 */
942
+ margin-right: 5px;
943
+ }
944
+
945
+ .file-icon {
946
+ display: flex;
947
+ justify-content: center;
948
+ align-items: center;
949
+ width: 40px;
950
+ height: 40px;
951
+
952
+ font-weight: bold;
953
+ color: white; /* 自定义文字颜色 */
954
+ padding: 3px 3px; /* 标签内边距 */
955
+ border-radius: 8px; /* 标签圆角 */
956
+ font-size: 25px; /* 字体大小 */
957
+ margin-right: 15px;
958
+ }
959
+
960
+ .van-cell__value {
961
+ flex: 0 auto;
962
+ }
963
+
964
+ .popup-option {
965
+ padding: 16px;
966
+ text-align: center;
967
+ border-bottom: 1px solid #ebebeb;
968
+ color: #333;
969
+ font-size: 16px;
970
+ cursor: pointer;
971
+ }
972
+
973
+ .popup-option:last-child {
974
+ border-bottom: none;
975
+ }
976
+
977
+ .popup-option.highlighted {
978
+ color: #0d88ff;
979
+ font-weight: bold;
980
+ }
981
+
982
+ .approvalForm-value {
983
+ flex: 1;
984
+ white-space: nowrap; /* 防止文本换行 */
985
+ overflow: hidden; /* 超出部分隐藏 */
986
+ text-overflow: ellipsis; /* 用省略号表示被隐藏的文本 */
987
+ }
988
+
989
+ .popup-content {
990
+ padding: 16px;
991
+ font-size: 14px;
992
+ color: #333;
993
+ white-space: pre-wrap; /* 保留换行和空格 */
994
+ }
995
+
996
+ .table-header {
997
+ display: flex;
998
+ background-color: #f8f8f8;
999
+ font-size: 15px;
1000
+ }
1001
+
1002
+ .table-row {
1003
+ display: flex;
1004
+ border-bottom: 1px solid #ebedf0;
1005
+ font-size: 14px;
1006
+ }
1007
+
1008
+ .table-header > div,
1009
+ .table-row > div {
1010
+ flex: 1;
1011
+ text-align: center;
1012
+ padding: 10px;
1013
+ }
1014
+
1015
+ .table-row:last-child {
1016
+ border-bottom: none;
1017
+ }
1018
+
1019
+ .van-radio {
1020
+ justify-content: center;
1021
+ }
1022
+
1023
+ ::v-deep .van-dialog__header {
1024
+ padding: 10px 0
1025
+ }
1026
+
1027
+ </style>