Commit f6e036045269e2deb9f5c9d2d446a5f855d2ed65

Authored by YunaiV
1 parent 6c6050a3

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

yudao-ui-admin-vue3/src/views/bpm/processInstance/detail.vue
1 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 6 <el-card class="box-card" v-loading="processInstanceLoading">
76   - <div slot="header" class="clearfix">
  7 + <template #header>
77 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 12 <div>
88 13 <parser :key="new Date().getTime()" :form-conf="detailForm" />
89 14 </div>
90 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 18 <router-link
98 19 :to="
99   - this.processInstance.processDefinition.formCustomViewPath +
  20 + processInstance.processDefinition.formCustomViewPath +
100 21 '?id=' +
101   - this.processInstance.businessKey
  22 + processInstance.businessKey
102 23 "
103 24 >
104 25 <el-button type="primary">点击查看</el-button>
105 26 </router-link>
106 27 </div>
107 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 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 68 </script>
496 69  
497 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>
... ...