Commit f6e036045269e2deb9f5c9d2d446a5f855d2ed65

Authored by YunaiV
1 parent 6c6050a3

bpm:初始化流程详情的 view 界面

yudao-ui-admin-vue3/src/views/bpm/processInstance/detail.vue
1 <template> 1 <template>
2 - <div class="app-container">  
3 - <!-- 审批信息 -->  
4 - <el-card  
5 - class="box-card"  
6 - v-loading="processInstanceLoading"  
7 - v-for="(item, index) in runningTasks"  
8 - :key="index"  
9 - >  
10 - <div slot="header" class="clearfix">  
11 - <span class="el-icon-picture-outline">审批任务【{{ item.name }}】</span>  
12 - </div>  
13 - <el-col :span="16" :offset="6">  
14 - <el-form  
15 - :ref="'form' + index"  
16 - :model="auditForms[index]"  
17 - :rules="auditRule"  
18 - label-width="100px"  
19 - >  
20 - <el-form-item label="流程名" v-if="processInstance && processInstance.name">  
21 - {{ processInstance.name }}  
22 - </el-form-item>  
23 - <el-form-item label="流程发起人" v-if="processInstance && processInstance.startUser">  
24 - {{ processInstance.startUser.nickname }}  
25 - <el-tag type="info" size="mini">{{ processInstance.startUser.deptName }}</el-tag>  
26 - </el-form-item>  
27 - <el-form-item label="审批建议" prop="reason">  
28 - <el-input  
29 - type="textarea"  
30 - v-model="auditForms[index].reason"  
31 - placeholder="请输入审批建议"  
32 - />  
33 - </el-form-item>  
34 - </el-form>  
35 - <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">  
36 - <el-button  
37 - icon="el-icon-edit-outline"  
38 - type="success"  
39 - size="mini"  
40 - @click="handleAudit(item, true)"  
41 - >通过</el-button  
42 - >  
43 - <el-button  
44 - icon="el-icon-circle-close"  
45 - type="danger"  
46 - size="mini"  
47 - @click="handleAudit(item, false)"  
48 - >不通过</el-button  
49 - >  
50 - <el-button  
51 - icon="el-icon-edit-outline"  
52 - type="primary"  
53 - size="mini"  
54 - @click="handleUpdateAssignee(item)"  
55 - >转办</el-button  
56 - >  
57 - <el-button  
58 - icon="el-icon-edit-outline"  
59 - type="primary"  
60 - size="mini"  
61 - @click="handleDelegate(item)"  
62 - >委派</el-button  
63 - >  
64 - <el-button  
65 - icon="el-icon-refresh-left"  
66 - type="warning"  
67 - size="mini"  
68 - @click="handleBack(item)"  
69 - >退回</el-button  
70 - >  
71 - </div>  
72 - </el-col>  
73 - </el-card> 2 + <ContentWrap>
  3 + <!-- TODO 审批信息 -->
  4 +
74 <!-- 申请信息 --> 5 <!-- 申请信息 -->
75 <el-card class="box-card" v-loading="processInstanceLoading"> 6 <el-card class="box-card" v-loading="processInstanceLoading">
76 - <div slot="header" class="clearfix"> 7 + <template #header>
77 <span class="el-icon-document">申请信息【{{ processInstance.name }}】</span> 8 <span class="el-icon-document">申请信息【{{ processInstance.name }}】</span>
78 - </div>  
79 - <el-col  
80 - v-if="  
81 - this.processInstance.processDefinition &&  
82 - this.processInstance.processDefinition.formType === 10  
83 - "  
84 - :span="16"  
85 - :offset="6"  
86 - > 9 + </template>
  10 + <!-- 情况一:流程表单 -->
  11 + <el-col v-if="processInstance?.processDefinition?.formType === 10" :span="16" :offset="6">
87 <div> 12 <div>
88 <parser :key="new Date().getTime()" :form-conf="detailForm" /> 13 <parser :key="new Date().getTime()" :form-conf="detailForm" />
89 </div> 14 </div>
90 </el-col> 15 </el-col>
91 - <div  
92 - v-if="  
93 - this.processInstance.processDefinition &&  
94 - this.processInstance.processDefinition.formType === 20  
95 - "  
96 - > 16 + <!-- 情况二:流程表单 -->
  17 + <div v-if="processInstance?.processDefinition?.formType === 20">
97 <router-link 18 <router-link
98 :to=" 19 :to="
99 - this.processInstance.processDefinition.formCustomViewPath + 20 + processInstance.processDefinition.formCustomViewPath +
100 '?id=' + 21 '?id=' +
101 - this.processInstance.businessKey 22 + processInstance.businessKey
102 " 23 "
103 > 24 >
104 <el-button type="primary">点击查看</el-button> 25 <el-button type="primary">点击查看</el-button>
105 </router-link> 26 </router-link>
106 </div> 27 </div>
107 </el-card> 28 </el-card>
108 -  
109 - <!-- 审批记录 -->  
110 - <el-card class="box-card" v-loading="tasksLoad">  
111 - <div slot="header" class="clearfix">  
112 - <span class="el-icon-picture-outline">审批记录</span>  
113 - </div>  
114 - <el-col :span="16" :offset="4">  
115 - <div class="block">  
116 - <el-timeline>  
117 - <el-timeline-item  
118 - v-for="(item, index) in tasks"  
119 - :key="index"  
120 - :icon="getTimelineItemIcon(item)"  
121 - :type="getTimelineItemType(item)"  
122 - >  
123 - <p style="font-weight: 700">任务:{{ item.name }}</p>  
124 - <el-card :body-style="{ padding: '10px' }">  
125 - <label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px">  
126 - 审批人:{{ item.assigneeUser.nickname }}  
127 - <el-tag type="info" size="mini">{{ item.assigneeUser.deptName }}</el-tag>  
128 - </label>  
129 - <label style="font-weight: normal" v-if="item.createTime">创建时间:</label>  
130 - <label style="color: #8a909c; font-weight: normal">{{  
131 - parseTime(item.createTime)  
132 - }}</label>  
133 - <label v-if="item.endTime" style="margin-left: 30px; font-weight: normal"  
134 - >审批时间:</label  
135 - >  
136 - <label v-if="item.endTime" style="color: #8a909c; font-weight: normal">  
137 - {{ parseTime(item.endTime) }}</label  
138 - >  
139 - <label v-if="item.durationInMillis" style="margin-left: 30px; font-weight: normal"  
140 - >耗时:</label  
141 - >  
142 - <label v-if="item.durationInMillis" style="color: #8a909c; font-weight: normal">  
143 - {{ getDateStar(item.durationInMillis) }}  
144 - </label>  
145 - <p v-if="item.reason">  
146 - <el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>  
147 - </p>  
148 - </el-card>  
149 - </el-timeline-item>  
150 - </el-timeline>  
151 - </div>  
152 - </el-col>  
153 - </el-card>  
154 -  
155 - <!-- 高亮流程图 -->  
156 - <el-card class="box-card" v-loading="processInstanceLoading">  
157 - <div slot="header" class="clearfix">  
158 - <span class="el-icon-picture-outline">流程图</span>  
159 - </div>  
160 - <my-process-viewer  
161 - key="designer"  
162 - v-model="bpmnXML"  
163 - v-bind="bpmnControlForm"  
164 - :activityData="activityList"  
165 - :processInstanceData="processInstance"  
166 - :taskData="tasks"  
167 - />  
168 - </el-card>  
169 -  
170 - <!-- 对话框(转派审批人) -->  
171 - <el-dialog title="转派审批人" :visible.sync="updateAssignee.open" width="500px" append-to-body>  
172 - <el-form  
173 - ref="updateAssigneeForm"  
174 - :model="updateAssignee.form"  
175 - :rules="updateAssignee.rules"  
176 - label-width="110px"  
177 - >  
178 - <el-form-item label="新审批人" prop="assigneeUserId">  
179 - <el-select v-model="updateAssignee.form.assigneeUserId" clearable style="width: 100%">  
180 - <el-option  
181 - v-for="item in userOptions"  
182 - :key="parseInt(item.id)"  
183 - :label="item.nickname"  
184 - :value="parseInt(item.id)"  
185 - />  
186 - </el-select>  
187 - </el-form-item>  
188 - </el-form>  
189 - <div slot="footer" class="dialog-footer">  
190 - <el-button type="primary" @click="submitUpdateAssigneeForm">确 定</el-button>  
191 - <el-button @click="cancelUpdateAssigneeForm">取 消</el-button>  
192 - </div>  
193 - </el-dialog>  
194 - </div> 29 + </ContentWrap>
195 </template> 30 </template>
  31 +<script setup lang="ts">
  32 +import * as ProcessInstanceApi from '@/api/bpm/processInstance'
  33 +
  34 +const { query } = useRoute() // 查询参数
  35 +const message = useMessage() // 消息弹窗
  36 +
  37 +// ========== 审批信息 ==========
  38 +const id = query.id as unknown as number
  39 +const processInstanceLoading = ref(false) // 流程实例的加载中
  40 +const processInstance = ref({}) // 流程实例
  41 +
  42 +// ========== 申请信息 ==========
  43 +
  44 +// ========== 初始化 ==========
  45 +onMounted(() => {
  46 + // 1. 获得流程实例相关
  47 + processInstanceLoading.value = true
  48 + ProcessInstanceApi.getProcessInstanceApi(id)
  49 + .then((data) => {
  50 + if (!data) {
  51 + message.error('查询不到流程信息!')
  52 + return
  53 + }
  54 + processInstance.value = data
196 55
197 -<script>  
198 -import { getProcessDefinitionBpmnXML } from "@/api/bpm/definition"  
199 -import { DICT_TYPE, getDictOptions } from "@/utils/dict"  
200 -import store from "@/store"  
201 -import { decodeFields } from "@/utils/formGenerator"  
202 -import Parser from '@/components/parser/Parser'  
203 -import { getProcessInstanceApi } from "@/api/bpm/processInstance"  
204 -import { approveTask, getTaskListByProcessInstanceId, rejectTask, updateTaskAssignee } from "@/api/bpm/task"  
205 -import { getDate } from "@/utils/dateUtils"  
206 -import { getListSimpleUsersApi } from "@/api/system/user"  
207 -import { getActivityList } from "@/api/bpm/activity"  
208 -  
209 -// 流程实例的详情页,可用于审批  
210 -export default {  
211 - name: "ProcessInstanceDetail",  
212 - components: {  
213 - Parser  
214 - },  
215 - data () {  
216 - return {  
217 - // 遮罩层  
218 - processInstanceLoading: true,  
219 - // 流程实例  
220 - id: undefined, // 流程实例的编号  
221 - processInstance: {},  
222 -  
223 - // 流程表单详情  
224 - detailForm: {  
225 - fields: []  
226 - },  
227 -  
228 - // BPMN 数据  
229 - bpmnXML: null,  
230 - bpmnControlForm: {  
231 - prefix: "flowable"  
232 - },  
233 - activityList: [],  
234 -  
235 - // 审批记录  
236 - tasksLoad: true,  
237 - tasks: [],  
238 -  
239 - // 审批表单  
240 - runningTasks: [],  
241 - auditForms: [],  
242 - auditRule: {  
243 - reason: [{ required: true, message: "审批建议不能为空", trigger: "blur" }],  
244 - },  
245 -  
246 - // 转派审批人  
247 - userOptions: [],  
248 - updateAssignee: {  
249 - open: false,  
250 - form: {  
251 - assigneeUserId: undefined,  
252 - },  
253 - rules: {  
254 - assigneeUserId: [{ required: true, message: "新审批人不能为空", trigger: "change" }],  
255 - }  
256 - }, 56 + // TODO 设置表单信息
257 57
258 - // 数据字典  
259 - categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),  
260 - }  
261 - },  
262 - created () {  
263 - this.id = this.$route.query.id  
264 - if (!this.id) {  
265 - this.$message.error('未传递 id 参数,无法查看流程信息')  
266 - return  
267 - }  
268 - this.getDetail() 58 + // TODO 加载流程图
269 59
270 - // 获得用户列表  
271 - this.userOptions = []  
272 - getListSimpleUsersApi().then(response => {  
273 - this.userOptions.push(...response.data) 60 + // TODO 加载活动列表
  61 + })
  62 + .finally(() => {
  63 + processInstanceLoading.value = false
274 }) 64 })
275 - },  
276 - methods: {  
277 - /** 获得流程实例 */  
278 - getDetail () {  
279 - // 获得流程实例相关  
280 - this.processInstanceLoading = true  
281 - getProcessInstanceApi(this.id).then(response => {  
282 - if (!response.data) {  
283 - this.$message.error('查询不到流程信息!')  
284 - return  
285 - }  
286 - // 设置流程信息  
287 - this.processInstance = response.data  
288 -  
289 - // 设置表单信息  
290 - if (this.processInstance.processDefinition.formType === 10) {  
291 - this.detailForm = {  
292 - ...JSON.parse(this.processInstance.processDefinition.formConf),  
293 - disabled: true, // 表单禁用  
294 - formBtns: false, // 按钮隐藏  
295 - fields: decodeFields(this.processInstance.processDefinition.formFields)  
296 - }  
297 - // 设置表单的值  
298 - this.detailForm.fields.forEach(item => {  
299 - const val = this.processInstance.formVariables[item.__vModel__]  
300 - if (val) {  
301 - item.__config__.defaultValue = val  
302 - }  
303 - })  
304 - }  
305 -  
306 - // 加载流程图  
307 - getProcessDefinitionBpmnXML(this.processInstance.processDefinition.id).then(response => {  
308 - this.bpmnXML = response.data  
309 - })  
310 - // 加载活动列表  
311 - getActivityList({  
312 - processInstanceId: this.processInstance.id  
313 - }).then(response => {  
314 - this.activityList = response.data  
315 - })  
316 -  
317 - // 取消加载中  
318 - this.processInstanceLoading = false  
319 - })  
320 -  
321 - // 获得流程任务列表(审批记录)  
322 - this.tasksLoad = true  
323 - this.runningTasks = []  
324 - this.auditForms = []  
325 - getTaskListByProcessInstanceId(this.id).then(response => {  
326 - // 审批记录  
327 - this.tasks = []  
328 - // 移除已取消的审批  
329 - response.data.forEach(task => {  
330 - if (task.result !== 4) {  
331 - this.tasks.push(task)  
332 - }  
333 - })  
334 - // 排序,将未完成的排在前面,已完成的排在后面;  
335 - this.tasks.sort((a, b) => {  
336 - // 有已完成的情况,按照完成时间倒序  
337 - if (a.endTime && b.endTime) {  
338 - return b.endTime - a.endTime  
339 - } else if (a.endTime) {  
340 - return 1  
341 - } else if (b.endTime) {  
342 - return -1  
343 - // 都是未完成,按照创建时间倒序  
344 - } else {  
345 - return b.createTime - a.createTime  
346 - }  
347 - })  
348 -  
349 - // 需要审核的记录  
350 - const userId = store.getters.userId  
351 - this.tasks.forEach(task => {  
352 - if (task.result !== 1) { // 只有待处理才需要  
353 - return  
354 - }  
355 - if (!task.assigneeUser || task.assigneeUser.id !== userId) { // 自己不是处理人  
356 - return  
357 - }  
358 - this.runningTasks.push({ ...task })  
359 - this.auditForms.push({  
360 - reason: ''  
361 - })  
362 - })  
363 -  
364 - // 取消加载中  
365 - this.tasksLoad = false  
366 - })  
367 - },  
368 - /** 处理选择流程的按钮操作 **/  
369 - handleSelect (row) {  
370 - // 设置选择的流程  
371 - this.selectProcessInstance = row  
372 65
373 - // 流程表单  
374 - if (row.formId) {  
375 - // 设置对应的表单  
376 - this.detailForm = {  
377 - ...JSON.parse(row.formConf),  
378 - fields: decodeFields(row.formFields)  
379 - }  
380 - } else if (row.formCustomCreatePath) {  
381 - this.$router.push({ path: row.formCustomCreatePath })  
382 - // 这里暂时无需加载流程图,因为跳出到另外个 Tab;  
383 - }  
384 - },  
385 - getDateStar (ms) {  
386 - return getDate(ms)  
387 - },  
388 - getTimelineItemIcon (item) {  
389 - if (item.result === 1) {  
390 - return 'el-icon-time'  
391 - }  
392 - if (item.result === 2) {  
393 - return 'el-icon-check'  
394 - }  
395 - if (item.result === 3) {  
396 - return 'el-icon-close'  
397 - }  
398 - if (item.result === 4) {  
399 - return 'el-icon-remove-outline'  
400 - }  
401 - return ''  
402 - },  
403 - getTimelineItemType (item) {  
404 - if (item.result === 1) {  
405 - return 'primary'  
406 - }  
407 - if (item.result === 2) {  
408 - return 'success'  
409 - }  
410 - if (item.result === 3) {  
411 - return 'danger'  
412 - }  
413 - if (item.result === 4) {  
414 - return 'info'  
415 - }  
416 - return ''  
417 - },  
418 - /** 处理审批通过和不通过的操作 */  
419 - handleAudit (task, pass) {  
420 - const index = this.runningTasks.indexOf(task)  
421 - this.$refs['form' + index][0].validate(valid => {  
422 - if (!valid) {  
423 - return  
424 - }  
425 - const data = {  
426 - id: task.id,  
427 - reason: this.auditForms[index].reason  
428 - }  
429 - if (pass) {  
430 - approveTask(data).then(response => {  
431 - this.$modal.msgSuccess("审批通过成功!")  
432 - this.getDetail() // 获得最新详情  
433 - })  
434 - } else {  
435 - rejectTask(data).then(response => {  
436 - this.$modal.msgSuccess("审批不通过成功!")  
437 - this.getDetail() // 获得最新详情  
438 - })  
439 - }  
440 - })  
441 - },  
442 - /** 处理转派审批人 */  
443 - handleUpdateAssignee (task) {  
444 - // 设置表单  
445 - this.resetUpdateAssigneeForm()  
446 - this.updateAssignee.form.id = task.id  
447 - // 设置为打开  
448 - this.updateAssignee.open = true  
449 - },  
450 - /** 提交转派审批人 */  
451 - submitUpdateAssigneeForm () {  
452 - this.$refs['updateAssigneeForm'].validate(valid => {  
453 - if (!valid) {  
454 - return  
455 - }  
456 - updateTaskAssignee(this.updateAssignee.form).then(response => {  
457 - this.$modal.msgSuccess("转派任务成功!")  
458 - this.updateAssignee.open = false  
459 - this.getDetail() // 获得最新详情  
460 - })  
461 - })  
462 - },  
463 - /** 取消转派审批人 */  
464 - cancelUpdateAssigneeForm () {  
465 - this.updateAssignee.open = false  
466 - this.resetUpdateAssigneeForm()  
467 - },  
468 - /** 重置转派审批人 */  
469 - resetUpdateAssigneeForm () {  
470 - this.updateAssignee.form = {  
471 - id: undefined,  
472 - assigneeUserId: undefined,  
473 - }  
474 - this.resetForm("updateAssigneeForm")  
475 - },  
476 - /** 处理审批退回的操作 */  
477 - handleDelegate (task) {  
478 - this.$modal.msgError("暂不支持【委派】功能,可以使用【转派】替代!")  
479 - },  
480 - /** 处理审批退回的操作 */  
481 - handleBack (task) {  
482 - this.$modal.msgError("暂不支持【退回】功能!")  
483 - // 可参考 http://blog.wya1.com/article/636697030/details/7296  
484 - // const data = {  
485 - // id: task.id,  
486 - // assigneeUserId: 1  
487 - // }  
488 - // backTask(data).then(response => {  
489 - // this.$modal.msgSuccess("回退成功!");  
490 - // this.getDetail(); // 获得最新详情  
491 - // });  
492 - }  
493 - }  
494 -}; 66 + // 2. TODO
  67 +})
495 </script> 68 </script>
496 69
497 <style lang="scss"> 70 <style lang="scss">
yudao-ui-admin-vue3/src/views/bpm/processInstance/detail2.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 审批信息 -->
  4 + <el-card
  5 + class="box-card"
  6 + v-loading="processInstanceLoading"
  7 + v-for="(item, index) in runningTasks"
  8 + :key="index"
  9 + >
  10 + <div slot="header" class="clearfix">
  11 + <span class="el-icon-picture-outline">审批任务【{{ item.name }}】</span>
  12 + </div>
  13 + <el-col :span="16" :offset="6">
  14 + <el-form
  15 + :ref="'form' + index"
  16 + :model="auditForms[index]"
  17 + :rules="auditRule"
  18 + label-width="100px"
  19 + >
  20 + <el-form-item label="流程名" v-if="processInstance && processInstance.name">
  21 + {{ processInstance.name }}
  22 + </el-form-item>
  23 + <el-form-item label="流程发起人" v-if="processInstance && processInstance.startUser">
  24 + {{ processInstance.startUser.nickname }}
  25 + <el-tag type="info" size="mini">{{ processInstance.startUser.deptName }}</el-tag>
  26 + </el-form-item>
  27 + <el-form-item label="审批建议" prop="reason">
  28 + <el-input
  29 + type="textarea"
  30 + v-model="auditForms[index].reason"
  31 + placeholder="请输入审批建议"
  32 + />
  33 + </el-form-item>
  34 + </el-form>
  35 + <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
  36 + <el-button
  37 + icon="el-icon-edit-outline"
  38 + type="success"
  39 + size="mini"
  40 + @click="handleAudit(item, true)"
  41 + >通过</el-button
  42 + >
  43 + <el-button
  44 + icon="el-icon-circle-close"
  45 + type="danger"
  46 + size="mini"
  47 + @click="handleAudit(item, false)"
  48 + >不通过</el-button
  49 + >
  50 + <el-button
  51 + icon="el-icon-edit-outline"
  52 + type="primary"
  53 + size="mini"
  54 + @click="handleUpdateAssignee(item)"
  55 + >转办</el-button
  56 + >
  57 + <el-button
  58 + icon="el-icon-edit-outline"
  59 + type="primary"
  60 + size="mini"
  61 + @click="handleDelegate(item)"
  62 + >委派</el-button
  63 + >
  64 + <el-button
  65 + icon="el-icon-refresh-left"
  66 + type="warning"
  67 + size="mini"
  68 + @click="handleBack(item)"
  69 + >退回</el-button
  70 + >
  71 + </div>
  72 + </el-col>
  73 + </el-card>
  74 + <!-- 申请信息 -->
  75 + <el-card class="box-card" v-loading="processInstanceLoading">
  76 + <div slot="header" class="clearfix">
  77 + <span class="el-icon-document">申请信息【{{ processInstance.name }}】</span>
  78 + </div>
  79 + <el-col
  80 + v-if="
  81 + this.processInstance.processDefinition &&
  82 + this.processInstance.processDefinition.formType === 10
  83 + "
  84 + :span="16"
  85 + :offset="6"
  86 + >
  87 + <div>
  88 + <parser :key="new Date().getTime()" :form-conf="detailForm" />
  89 + </div>
  90 + </el-col>
  91 + <div
  92 + v-if="
  93 + this.processInstance.processDefinition &&
  94 + this.processInstance.processDefinition.formType === 20
  95 + "
  96 + >
  97 + <router-link
  98 + :to="
  99 + this.processInstance.processDefinition.formCustomViewPath +
  100 + '?id=' +
  101 + this.processInstance.businessKey
  102 + "
  103 + >
  104 + <el-button type="primary">点击查看</el-button>
  105 + </router-link>
  106 + </div>
  107 + </el-card>
  108 +
  109 + <!-- 审批记录 -->
  110 + <el-card class="box-card" v-loading="tasksLoad">
  111 + <div slot="header" class="clearfix">
  112 + <span class="el-icon-picture-outline">审批记录</span>
  113 + </div>
  114 + <el-col :span="16" :offset="4">
  115 + <div class="block">
  116 + <el-timeline>
  117 + <el-timeline-item
  118 + v-for="(item, index) in tasks"
  119 + :key="index"
  120 + :icon="getTimelineItemIcon(item)"
  121 + :type="getTimelineItemType(item)"
  122 + >
  123 + <p style="font-weight: 700">任务:{{ item.name }}</p>
  124 + <el-card :body-style="{ padding: '10px' }">
  125 + <label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px">
  126 + 审批人:{{ item.assigneeUser.nickname }}
  127 + <el-tag type="info" size="mini">{{ item.assigneeUser.deptName }}</el-tag>
  128 + </label>
  129 + <label style="font-weight: normal" v-if="item.createTime">创建时间:</label>
  130 + <label style="color: #8a909c; font-weight: normal">{{
  131 + parseTime(item.createTime)
  132 + }}</label>
  133 + <label v-if="item.endTime" style="margin-left: 30px; font-weight: normal"
  134 + >审批时间:</label
  135 + >
  136 + <label v-if="item.endTime" style="color: #8a909c; font-weight: normal">
  137 + {{ parseTime(item.endTime) }}</label
  138 + >
  139 + <label v-if="item.durationInMillis" style="margin-left: 30px; font-weight: normal"
  140 + >耗时:</label
  141 + >
  142 + <label v-if="item.durationInMillis" style="color: #8a909c; font-weight: normal">
  143 + {{ getDateStar(item.durationInMillis) }}
  144 + </label>
  145 + <p v-if="item.reason">
  146 + <el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>
  147 + </p>
  148 + </el-card>
  149 + </el-timeline-item>
  150 + </el-timeline>
  151 + </div>
  152 + </el-col>
  153 + </el-card>
  154 +
  155 + <!-- 高亮流程图 -->
  156 + <el-card class="box-card" v-loading="processInstanceLoading">
  157 + <div slot="header" class="clearfix">
  158 + <span class="el-icon-picture-outline">流程图</span>
  159 + </div>
  160 + <my-process-viewer
  161 + key="designer"
  162 + v-model="bpmnXML"
  163 + v-bind="bpmnControlForm"
  164 + :activityData="activityList"
  165 + :processInstanceData="processInstance"
  166 + :taskData="tasks"
  167 + />
  168 + </el-card>
  169 +
  170 + <!-- 对话框(转派审批人) -->
  171 + <el-dialog title="转派审批人" :visible.sync="updateAssignee.open" width="500px" append-to-body>
  172 + <el-form
  173 + ref="updateAssigneeForm"
  174 + :model="updateAssignee.form"
  175 + :rules="updateAssignee.rules"
  176 + label-width="110px"
  177 + >
  178 + <el-form-item label="新审批人" prop="assigneeUserId">
  179 + <el-select v-model="updateAssignee.form.assigneeUserId" clearable style="width: 100%">
  180 + <el-option
  181 + v-for="item in userOptions"
  182 + :key="parseInt(item.id)"
  183 + :label="item.nickname"
  184 + :value="parseInt(item.id)"
  185 + />
  186 + </el-select>
  187 + </el-form-item>
  188 + </el-form>
  189 + <div slot="footer" class="dialog-footer">
  190 + <el-button type="primary" @click="submitUpdateAssigneeForm">确 定</el-button>
  191 + <el-button @click="cancelUpdateAssigneeForm">取 消</el-button>
  192 + </div>
  193 + </el-dialog>
  194 + </div>
  195 +</template>
  196 +
  197 +<script>
  198 +import { getProcessDefinitionBpmnXML } from "@/api/bpm/definition"
  199 +import { DICT_TYPE, getDictOptions } from "@/utils/dict"
  200 +import store from "@/store"
  201 +import { decodeFields } from "@/utils/formGenerator"
  202 +import Parser from '@/components/parser/Parser'
  203 +import { getProcessInstanceApi } from "@/api/bpm/processInstance"
  204 +import { approveTask, getTaskListByProcessInstanceId, rejectTask, updateTaskAssignee } from "@/api/bpm/task"
  205 +import { getDate } from "@/utils/dateUtils"
  206 +import { getListSimpleUsersApi } from "@/api/system/user"
  207 +import { getActivityList } from "@/api/bpm/activity"
  208 +
  209 +// 流程实例的详情页,可用于审批
  210 +export default {
  211 + name: "ProcessInstanceDetail",
  212 + components: {
  213 + Parser
  214 + },
  215 + data () {
  216 + return {
  217 +
  218 + // 流程实例
  219 + id: undefined, // 流程实例的编号
  220 + processInstance: {},
  221 +
  222 + // 流程表单详情
  223 + detailForm: {
  224 + fields: []
  225 + },
  226 +
  227 + // BPMN 数据
  228 + bpmnXML: null,
  229 + bpmnControlForm: {
  230 + prefix: "flowable"
  231 + },
  232 + activityList: [],
  233 +
  234 + // 审批记录
  235 + tasksLoad: true,
  236 + tasks: [],
  237 +
  238 + // 审批表单
  239 + runningTasks: [],
  240 + auditForms: [],
  241 + auditRule: {
  242 + reason: [{ required: true, message: "审批建议不能为空", trigger: "blur" }],
  243 + },
  244 +
  245 + // 转派审批人
  246 + userOptions: [],
  247 + updateAssignee: {
  248 + open: false,
  249 + form: {
  250 + assigneeUserId: undefined,
  251 + },
  252 + rules: {
  253 + assigneeUserId: [{ required: true, message: "新审批人不能为空", trigger: "change" }],
  254 + }
  255 + },
  256 +
  257 + // 数据字典
  258 + categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
  259 + }
  260 + },
  261 + created () {
  262 + this.id = this.$route.query.id
  263 + if (!this.id) {
  264 + this.$message.error('未传递 id 参数,无法查看流程信息')
  265 + return
  266 + }
  267 + this.getDetail()
  268 +
  269 + // 获得用户列表
  270 + this.userOptions = []
  271 + getListSimpleUsersApi().then(response => {
  272 + this.userOptions.push(...response.data)
  273 + })
  274 + },
  275 + methods: {
  276 + /** 获得流程实例 */
  277 + getDetail () {
  278 + // 获得流程实例相关
  279 + this.processInstanceLoading = true
  280 + getProcessInstanceApi(this.id).then(response => {
  281 + if (!response.data) {
  282 + this.$message.error('查询不到流程信息!')
  283 + return
  284 + }
  285 + // 设置流程信息
  286 + this.processInstance = response.data
  287 +
  288 + // 设置表单信息
  289 + if (this.processInstance.processDefinition.formType === 10) {
  290 + this.detailForm = {
  291 + ...JSON.parse(this.processInstance.processDefinition.formConf),
  292 + disabled: true, // 表单禁用
  293 + formBtns: false, // 按钮隐藏
  294 + fields: decodeFields(this.processInstance.processDefinition.formFields)
  295 + }
  296 + // 设置表单的值
  297 + this.detailForm.fields.forEach(item => {
  298 + const val = this.processInstance.formVariables[item.__vModel__]
  299 + if (val) {
  300 + item.__config__.defaultValue = val
  301 + }
  302 + })
  303 + }
  304 +
  305 + // 加载流程图
  306 + getProcessDefinitionBpmnXML(this.processInstance.processDefinition.id).then(response => {
  307 + this.bpmnXML = response.data
  308 + })
  309 + // 加载活动列表
  310 + getActivityList({
  311 + processInstanceId: this.processInstance.id
  312 + }).then(response => {
  313 + this.activityList = response.data
  314 + })
  315 +
  316 + // 取消加载中
  317 + this.processInstanceLoading = false
  318 + })
  319 +
  320 + // 获得流程任务列表(审批记录)
  321 + this.tasksLoad = true
  322 + this.runningTasks = []
  323 + this.auditForms = []
  324 + getTaskListByProcessInstanceId(this.id).then(response => {
  325 + // 审批记录
  326 + this.tasks = []
  327 + // 移除已取消的审批
  328 + response.data.forEach(task => {
  329 + if (task.result !== 4) {
  330 + this.tasks.push(task)
  331 + }
  332 + })
  333 + // 排序,将未完成的排在前面,已完成的排在后面;
  334 + this.tasks.sort((a, b) => {
  335 + // 有已完成的情况,按照完成时间倒序
  336 + if (a.endTime && b.endTime) {
  337 + return b.endTime - a.endTime
  338 + } else if (a.endTime) {
  339 + return 1
  340 + } else if (b.endTime) {
  341 + return -1
  342 + // 都是未完成,按照创建时间倒序
  343 + } else {
  344 + return b.createTime - a.createTime
  345 + }
  346 + })
  347 +
  348 + // 需要审核的记录
  349 + const userId = store.getters.userId
  350 + this.tasks.forEach(task => {
  351 + if (task.result !== 1) { // 只有待处理才需要
  352 + return
  353 + }
  354 + if (!task.assigneeUser || task.assigneeUser.id !== userId) { // 自己不是处理人
  355 + return
  356 + }
  357 + this.runningTasks.push({ ...task })
  358 + this.auditForms.push({
  359 + reason: ''
  360 + })
  361 + })
  362 +
  363 + // 取消加载中
  364 + this.tasksLoad = false
  365 + })
  366 + },
  367 + /** 处理选择流程的按钮操作 **/
  368 + handleSelect (row) {
  369 + // 设置选择的流程
  370 + this.selectProcessInstance = row
  371 +
  372 + // 流程表单
  373 + if (row.formId) {
  374 + // 设置对应的表单
  375 + this.detailForm = {
  376 + ...JSON.parse(row.formConf),
  377 + fields: decodeFields(row.formFields)
  378 + }
  379 + } else if (row.formCustomCreatePath) {
  380 + this.$router.push({ path: row.formCustomCreatePath })
  381 + // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
  382 + }
  383 + },
  384 + getDateStar (ms) {
  385 + return getDate(ms)
  386 + },
  387 + getTimelineItemIcon (item) {
  388 + if (item.result === 1) {
  389 + return 'el-icon-time'
  390 + }
  391 + if (item.result === 2) {
  392 + return 'el-icon-check'
  393 + }
  394 + if (item.result === 3) {
  395 + return 'el-icon-close'
  396 + }
  397 + if (item.result === 4) {
  398 + return 'el-icon-remove-outline'
  399 + }
  400 + return ''
  401 + },
  402 + getTimelineItemType (item) {
  403 + if (item.result === 1) {
  404 + return 'primary'
  405 + }
  406 + if (item.result === 2) {
  407 + return 'success'
  408 + }
  409 + if (item.result === 3) {
  410 + return 'danger'
  411 + }
  412 + if (item.result === 4) {
  413 + return 'info'
  414 + }
  415 + return ''
  416 + },
  417 + /** 处理审批通过和不通过的操作 */
  418 + handleAudit (task, pass) {
  419 + const index = this.runningTasks.indexOf(task)
  420 + this.$refs['form' + index][0].validate(valid => {
  421 + if (!valid) {
  422 + return
  423 + }
  424 + const data = {
  425 + id: task.id,
  426 + reason: this.auditForms[index].reason
  427 + }
  428 + if (pass) {
  429 + approveTask(data).then(response => {
  430 + this.$modal.msgSuccess("审批通过成功!")
  431 + this.getDetail() // 获得最新详情
  432 + })
  433 + } else {
  434 + rejectTask(data).then(response => {
  435 + this.$modal.msgSuccess("审批不通过成功!")
  436 + this.getDetail() // 获得最新详情
  437 + })
  438 + }
  439 + })
  440 + },
  441 + /** 处理转派审批人 */
  442 + handleUpdateAssignee (task) {
  443 + // 设置表单
  444 + this.resetUpdateAssigneeForm()
  445 + this.updateAssignee.form.id = task.id
  446 + // 设置为打开
  447 + this.updateAssignee.open = true
  448 + },
  449 + /** 提交转派审批人 */
  450 + submitUpdateAssigneeForm () {
  451 + this.$refs['updateAssigneeForm'].validate(valid => {
  452 + if (!valid) {
  453 + return
  454 + }
  455 + updateTaskAssignee(this.updateAssignee.form).then(response => {
  456 + this.$modal.msgSuccess("转派任务成功!")
  457 + this.updateAssignee.open = false
  458 + this.getDetail() // 获得最新详情
  459 + })
  460 + })
  461 + },
  462 + /** 取消转派审批人 */
  463 + cancelUpdateAssigneeForm () {
  464 + this.updateAssignee.open = false
  465 + this.resetUpdateAssigneeForm()
  466 + },
  467 + /** 重置转派审批人 */
  468 + resetUpdateAssigneeForm () {
  469 + this.updateAssignee.form = {
  470 + id: undefined,
  471 + assigneeUserId: undefined,
  472 + }
  473 + this.resetForm("updateAssigneeForm")
  474 + },
  475 + /** 处理审批退回的操作 */
  476 + handleDelegate (task) {
  477 + this.$modal.msgError("暂不支持【委派】功能,可以使用【转派】替代!")
  478 + },
  479 + /** 处理审批退回的操作 */
  480 + handleBack (task) {
  481 + this.$modal.msgError("暂不支持【退回】功能!")
  482 + // 可参考 http://blog.wya1.com/article/636697030/details/7296
  483 + // const data = {
  484 + // id: task.id,
  485 + // assigneeUserId: 1
  486 + // }
  487 + // backTask(data).then(response => {
  488 + // this.$modal.msgSuccess("回退成功!");
  489 + // this.getDetail(); // 获得最新详情
  490 + // });
  491 + }
  492 + }
  493 +};
  494 +</script>
  495 +
  496 +<style lang="scss">
  497 +.my-process-designer {
  498 + height: calc(100vh - 200px);
  499 +}
  500 +
  501 +.box-card {
  502 + width: 100%;
  503 + margin-bottom: 20px;
  504 +}
  505 +</style>