Commit 0aa72b320033625d6db0be60168fa8e9a4b1bd34

Authored by YunaiV
1 parent 2774671a

bpm:流程详情的时间轴

yudao-ui-admin-vue3/src/utils/formatTime.ts
... ... @@ -147,3 +147,30 @@ export function formatAxis(param: Date): string {
147 147 else if (hour < 22) return '晚上好'
148 148 else return '夜里好'
149 149 }
  150 +
  151 +/**
  152 + * 将毫秒,转换成时间字符串。例如说,xx 分钟
  153 + *
  154 + * @param ms 毫秒
  155 + * @returns {string} 字符串
  156 + */
  157 +export function formatPast2(ms) {
  158 + const day = Math.floor(ms / (24 * 60 * 60 * 1000))
  159 + const hour = Math.floor(ms / (60 * 60 * 1000) - day * 24)
  160 + const minute = Math.floor(ms / (60 * 1000) - day * 24 * 60 - hour * 60)
  161 + const second = Math.floor(ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60)
  162 + if (day > 0) {
  163 + return day + '天' + hour + '小时' + minute + '分钟'
  164 + }
  165 + if (hour > 0) {
  166 + return hour + '小时' + minute + '分钟'
  167 + }
  168 + if (minute > 0) {
  169 + return minute + '分钟'
  170 + }
  171 + if (second > 0) {
  172 + return second + '秒'
  173 + } else {
  174 + return 0 + '秒'
  175 + }
  176 +}
... ...
yudao-ui-admin-vue3/src/views/bpm/processInstance/detail.vue
... ... @@ -25,14 +25,63 @@
25 25 processInstance.businessKey
26 26 "
27 27 >
28   - <el-button type="primary">点击查看</el-button>
  28 + <XButton type="primary" preIcon="ep:view" title="点击查看" />
29 29 </router-link>
30 30 </div>
31 31 </el-card>
  32 +
  33 + <!-- 审批记录 -->
  34 + <el-card class="box-card" v-loading="tasksLoad">
  35 + <template #header>
  36 + <span class="el-icon-picture-outline">审批记录</span>
  37 + </template>
  38 + <el-col :span="16" :offset="4">
  39 + <div class="block">
  40 + <el-timeline>
  41 + <el-timeline-item
  42 + v-for="(item, index) in tasks"
  43 + :key="index"
  44 + :icon="getTimelineItemIcon(item)"
  45 + :type="getTimelineItemType(item)"
  46 + >
  47 + <p style="font-weight: 700">任务:{{ item.name }}</p>
  48 + <el-card :body-style="{ padding: '10px' }">
  49 + <label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px">
  50 + 审批人:{{ item.assigneeUser.nickname }}
  51 + <el-tag type="info" size="mini">{{ item.assigneeUser.deptName }}</el-tag>
  52 + </label>
  53 + <label style="font-weight: normal" v-if="item.createTime">创建时间:</label>
  54 + <label style="color: #8a909c; font-weight: normal">
  55 + {{ dayjs(item?.createTime).format('YYYY-MM-DD HH:mm:ss') }}
  56 + </label>
  57 + <label v-if="item.endTime" style="margin-left: 30px; font-weight: normal">
  58 + 审批时间:
  59 + </label>
  60 + <label v-if="item.endTime" style="color: #8a909c; font-weight: normal">
  61 + {{ dayjs(item?.endTime).format('YYYY-MM-DD HH:mm:ss') }}
  62 + </label>
  63 + <label v-if="item.durationInMillis" style="margin-left: 30px; font-weight: normal">
  64 + 耗时:
  65 + </label>
  66 + <label v-if="item.durationInMillis" style="color: #8a909c; font-weight: normal">
  67 + {{ formatPast2(item?.durationInMillis) }}
  68 + </label>
  69 + <p v-if="item.reason">
  70 + <el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>
  71 + </p>
  72 + </el-card>
  73 + </el-timeline-item>
  74 + </el-timeline>
  75 + </div>
  76 + </el-col>
  77 + </el-card>
32 78 </ContentWrap>
33 79 </template>
34 80 <script setup lang="ts">
  81 +import dayjs from 'dayjs'
35 82 import * as ProcessInstanceApi from '@/api/bpm/processInstance'
  83 +import * as TaskApi from '@/api/bpm/task'
  84 +import { formatPast2 } from '@/utils/formatTime'
36 85  
37 86 const { query } = useRoute() // 查询参数
38 87 const message = useMessage() // 消息弹窗
... ... @@ -41,6 +90,11 @@ const message = useMessage() // 消息弹窗
41 90 const id = query.id as unknown as number
42 91 const processInstanceLoading = ref(false) // 流程实例的加载中
43 92 const processInstance = ref({}) // 流程实例
  93 +const runningTasks = ref([]) // 运行中的任务
  94 +const auditForms = ref([]) // 审批任务的表单
  95 +// const auditRule = reactive({
  96 +// reason: [{ required: true, message: '审批建议不能为空', trigger: 'blur' }]
  97 +// })
44 98  
45 99 // ========== 申请信息 ==========
46 100 import { setConfAndFields2 } from '@/utils/formCreate'
... ... @@ -53,6 +107,41 @@ const detailForm = ref({
53 107 value: {}
54 108 })
55 109  
  110 +// ========== 审批记录 ==========
  111 +const tasksLoad = ref(true)
  112 +const tasks = ref([])
  113 +
  114 +const getTimelineItemIcon = (item) => {
  115 + if (item.result === 1) {
  116 + return 'el-icon-time'
  117 + }
  118 + if (item.result === 2) {
  119 + return 'el-icon-check'
  120 + }
  121 + if (item.result === 3) {
  122 + return 'el-icon-close'
  123 + }
  124 + if (item.result === 4) {
  125 + return 'el-icon-remove-outline'
  126 + }
  127 + return ''
  128 +}
  129 +const getTimelineItemType = (item) => {
  130 + if (item.result === 1) {
  131 + return 'primary'
  132 + }
  133 + if (item.result === 2) {
  134 + return 'success'
  135 + }
  136 + if (item.result === 3) {
  137 + return 'danger'
  138 + }
  139 + if (item.result === 4) {
  140 + return 'info'
  141 + }
  142 + return ''
  143 +}
  144 +
56 145 // ========== 初始化 ==========
57 146 onMounted(() => {
58 147 // 1. 获得流程实例相关
... ... @@ -88,7 +177,53 @@ onMounted(() =&gt; {
88 177 processInstanceLoading.value = false
89 178 })
90 179  
91   - // 2. TODO
  180 + // 2. 获得流程任务列表(审批记录)
  181 + tasksLoad.value = true
  182 + runningTasks.value = []
  183 + auditForms.value = []
  184 + TaskApi.getTaskListByProcessInstanceId(id)
  185 + .then((data) => {
  186 + // 审批记录
  187 + tasks.value = []
  188 + // 移除已取消的审批
  189 + data.forEach((task) => {
  190 + if (task.result !== 4) {
  191 + tasks.value.push(task)
  192 + }
  193 + })
  194 + // 排序,将未完成的排在前面,已完成的排在后面;
  195 + tasks.value.sort((a, b) => {
  196 + // 有已完成的情况,按照完成时间倒序
  197 + if (a.endTime && b.endTime) {
  198 + return b.endTime - a.endTime
  199 + } else if (a.endTime) {
  200 + return 1
  201 + } else if (b.endTime) {
  202 + return -1
  203 + // 都是未完成,按照创建时间倒序
  204 + } else {
  205 + return b.createTime - a.createTime
  206 + }
  207 + })
  208 +
  209 + // 需要审核的记录
  210 + // const userId = store.getters.userId
  211 + // this.tasks.forEach(task => {
  212 + // if (task.result !== 1) { // 只有待处理才需要
  213 + // return
  214 + // }
  215 + // if (!task.assigneeUser || task.assigneeUser.id !== userId) { // 自己不是处理人
  216 + // return
  217 + // }
  218 + // this.runningTasks.push({ ...task })
  219 + // this.auditForms.push({
  220 + // reason: ''
  221 + // })
  222 + // })
  223 + })
  224 + .finally(() => {
  225 + tasksLoad.value = false
  226 + })
92 227 })
93 228 </script>
94 229  
... ...
yudao-ui-admin-vue3/src/views/bpm/processInstance/detail2.vue
... ... @@ -71,86 +71,8 @@
71 71 </div>
72 72 </el-col>
73 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 74  
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>
  75 +
154 76  
155 77 <!-- 高亮流程图 -->
156 78 <el-card class="box-card" v-loading="processInstanceLoading">
... ... @@ -209,21 +131,9 @@ import { getActivityList } from &quot;@/api/bpm/activity&quot;
209 131 // 流程实例的详情页,可用于审批
210 132 export default {
211 133 name: "ProcessInstanceDetail",
212   - components: {
213   - Parser
214   - },
215 134 data () {
216 135 return {
217 136  
218   - // 流程实例
219   - id: undefined, // 流程实例的编号
220   - processInstance: {},
221   -
222   - // 流程表单详情
223   - detailForm: {
224   - fields: []
225   - },
226   -
227 137 // BPMN 数据
228 138 bpmnXML: null,
229 139 bpmnControlForm: {
... ... @@ -231,17 +141,6 @@ export default {
231 141 },
232 142 activityList: [],
233 143  
234   - // 审批记录
235   - tasksLoad: true,
236   - tasks: [],
237   -
238   - // 审批表单
239   - runningTasks: [],
240   - auditForms: [],
241   - auditRule: {
242   - reason: [{ required: true, message: "审批建议不能为空", trigger: "blur" }],
243   - },
244   -
245 144 // 转派审批人
246 145 userOptions: [],
247 146 updateAssignee: {
... ... @@ -278,142 +177,23 @@ export default {
278 177 // 获得流程实例相关
279 178 this.processInstanceLoading = true
280 179 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 180 // 加载流程图
306 181 getProcessDefinitionBpmnXML(this.processInstance.processDefinition.id).then(response => {
307 182 this.bpmnXML = response.data
308 183 })
  184 +
309 185 // 加载活动列表
310 186 getActivityList({
311 187 processInstanceId: this.processInstance.id
312 188 }).then(response => {
313 189 this.activityList = response.data
314 190 })
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 191 })
366 192 },
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 193 getDateStar (ms) {
385 194 return getDate(ms)
386 195 },
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   - },
  196 +
417 197 /** 处理审批通过和不通过的操作 */
418 198 handleAudit (task, pass) {
419 199 const index = this.runningTasks.indexOf(task)
... ...
yudao-ui-admin/src/views/bpm/processInstance/detail.vue
... ... @@ -275,23 +275,6 @@ export default {
275 275 this.tasksLoad = false;
276 276 });
277 277 },
278   - /** 处理选择流程的按钮操作 **/
279   - handleSelect(row) {
280   - // 设置选择的流程
281   - this.selectProcessInstance = row;
282   -
283   - // 流程表单
284   - if (row.formId) {
285   - // 设置对应的表单
286   - this.detailForm = {
287   - ...JSON.parse(row.formConf),
288   - fields: decodeFields(row.formFields)
289   - }
290   - } else if (row.formCustomCreatePath) {
291   - this.$router.push({ path: row.formCustomCreatePath});
292   - // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
293   - }
294   - },
295 278 getDateStar(ms) {
296 279 return getDate(ms);
297 280 },
... ...