ProcessInstanceTimeline.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <!-- 审批详情的右侧:审批流 -->
  2. <template>
  3. <el-timeline class="pt-20px">
  4. <!-- 遍历每个审批节点 -->
  5. <el-timeline-item
  6. v-for="(activity, index) in approveNodes"
  7. :key="index"
  8. size="large"
  9. :icon="getApprovalNodeIcon(activity.status, activity.nodeType)"
  10. :color="getApprovalNodeColor(activity.status)"
  11. >
  12. <div class="flex flex-col items-start">
  13. <div class="font-bold"> {{ activity.name }}</div>
  14. <div class="flex items-center flex-wrap mt-1">
  15. <!-- 情况一:遍历每个审批节点下的【进行中】task 任务 -->
  16. <div v-for="(task, idx) in activity.tasks" :key="idx" class="flex items-center">
  17. <div class="flex flex-col pr-2">
  18. <div class="position-relative" v-if="task.assigneeUser || task.ownerUser">
  19. <!-- 信息:头像 -->
  20. <el-avatar
  21. v-if="task.assigneeUser && task.assigneeUser.avatar"
  22. :size="36"
  23. :src="task.assigneeUser.avatar"
  24. />
  25. <el-avatar v-else-if="task.assigneeUser && task.assigneeUser.nickname">
  26. {{ task.assigneeUser.nickname.substring(0, 1) }}
  27. </el-avatar>
  28. <el-avatar
  29. v-else-if="task.ownerUser && task.ownerUser.avatar"
  30. :src="task.ownerUser.avatar"
  31. />
  32. <el-avatar v-else-if="task.ownerUser && task.ownerUser.nickname">
  33. {{ task.ownerUser.nickname.substring(0, 1) }}
  34. </el-avatar>
  35. <!-- 信息:任务 ICON -->
  36. <div
  37. class="position-absolute top-26px left-26px bg-#fff rounded-full flex items-center p-2px"
  38. >
  39. <Icon
  40. :size="12"
  41. :icon="statusIconMap2[task.status]?.icon"
  42. :color="statusIconMap2[task.status]?.color"
  43. />
  44. </div>
  45. </div>
  46. <div class="flex flex-col mt-1">
  47. <!-- 信息:昵称 -->
  48. <div
  49. v-if="task.assigneeUser && task.assigneeUser.nickname"
  50. class="text-12px text-align-center"
  51. >
  52. {{ task.assigneeUser.nickname }}
  53. </div>
  54. <div
  55. v-else-if="task.ownerUser && task.ownerUser.nickname"
  56. class="text-10px text-align-center"
  57. >
  58. {{ task.ownerUser.nickname }}
  59. </div>
  60. <div
  61. v-if="task.reason && activity.nodeType === NodeType.USER_TASK_NODE"
  62. class="text-#a5a5a5 text-13px mt-1"
  63. >
  64. 审批意见:{{ task.reason }}
  65. </div>
  66. </div>
  67. </div>
  68. </div>
  69. <!-- 情况二:遍历每个审批节点下的【候选的】task 任务。例如说,1)依次审批,2)未来的审批任务等 -->
  70. <div
  71. v-for="(user, idx1) in activity.candidateUsers"
  72. :key="idx1"
  73. class="flex items-center"
  74. >
  75. <div class="flex items-center flex-col pr-2">
  76. <div class="position-relative">
  77. <!-- 信息:头像 -->
  78. <el-avatar :size="36" v-if="user.avatar" :src="user.avatar" />
  79. <el-avatar v-else-if="user.nickname && user.nickname">
  80. {{ user.nickname.substring(0, 1) }}
  81. </el-avatar>
  82. <!-- 信息:任务 ICON -->
  83. <div
  84. class="position-absolute top-26px left-26px bg-#fff rounded-full flex items-center p-2px"
  85. >
  86. <Icon
  87. :size="12"
  88. :icon="statusIconMap2['-1']?.icon"
  89. :color="statusIconMap2['-1']?.color"
  90. />
  91. </div>
  92. </div>
  93. <div class="flex flex-col mt-1">
  94. <!-- 信息:昵称 -->
  95. <div v-if="user.nickname" class="text-10px text-align-center">
  96. {{ user.nickname }}
  97. </div>
  98. </div>
  99. </div>
  100. </div>
  101. </div>
  102. <!-- 信息:时间 -->
  103. <div
  104. v-if="activity.status !== TaskStatusEnum.NOT_START"
  105. class="text-#a5a5a5 text-13px mt-1"
  106. >
  107. {{ getApprovalNodeTime(activity) }}
  108. </div>
  109. </div>
  110. </el-timeline-item>
  111. </el-timeline>
  112. </template>
  113. <script lang="ts" setup>
  114. import { formatDate } from '@/utils/formatTime'
  115. import * as ProcessInstanceApi from '@/api/bpm/processInstance'
  116. import { TaskStatusEnum } from '@/api/bpm/task'
  117. import { NodeType } from '@/components/SimpleProcessDesignerV2/src/consts'
  118. import { Check, Close, Loading, Clock, Minus, Delete } from '@element-plus/icons-vue'
  119. defineOptions({ name: 'BpmProcessInstanceTimeline' })
  120. defineProps<{
  121. approveNodes: ProcessInstanceApi.ApprovalNodeInfo[] // 审批节点信息
  122. }>()
  123. // 审批节点
  124. const statusIconMap2 = {
  125. // 未开始
  126. '-1': { color: '#e5e7ec', icon: 'ep-clock' },
  127. // 待审批
  128. '0': { color: '#e5e7ec', icon: 'ep:loading' },
  129. // 审批中
  130. '1': { color: '#448ef7', icon: 'ep:loading' },
  131. // 审批通过
  132. '2': { color: '#00b32a', icon: 'ep:circle-check-filled' },
  133. // 审批不通过
  134. '3': { color: '#f46b6c', icon: 'fa-solid:times-circle' },
  135. // 取消
  136. '4': { color: '#cccccc', icon: 'ep:delete-filled' },
  137. // 退回
  138. '5': { color: '#f46b6c', icon: 'ep:remove-filled' },
  139. // 委派中
  140. '6': { color: '#448ef7', icon: 'ep:loading' },
  141. // 审批通过中
  142. '7': { color: '#00b32a', icon: 'ep:circle-check-filled' }
  143. }
  144. const statusIconMap = {
  145. // 审批未开始
  146. '-1': { color: '#e5e7ec', icon: Clock },
  147. '0': { color: '#e5e7ec', icon: Clock },
  148. // 审批中
  149. '1': { color: '#448ef7', icon: Loading },
  150. // 审批通过
  151. '2': { color: '#00b32a', icon: Check },
  152. // 审批不通过
  153. '3': { color: '#f46b6c', icon: Close },
  154. // 已取消
  155. '4': { color: '#cccccc', icon: Delete },
  156. // 退回
  157. '5': { color: '#f46b6c', icon: Minus },
  158. // 委派中
  159. '6': { color: '#448ef7', icon: Loading },
  160. // 审批通过中
  161. '7': { color: '#00b32a', icon: Check }
  162. }
  163. const getApprovalNodeIcon = (taskStatus: number, nodeType: NodeType) => {
  164. if (taskStatus == TaskStatusEnum.NOT_START) {
  165. return statusIconMap[taskStatus]?.icon
  166. }
  167. if (nodeType === NodeType.START_USER_NODE || nodeType === NodeType.USER_TASK_NODE) {
  168. return statusIconMap[taskStatus]?.icon
  169. }
  170. }
  171. const getApprovalNodeColor = (taskStatus: number) => {
  172. return statusIconMap[taskStatus]?.color
  173. }
  174. const getApprovalNodeTime = (node: ProcessInstanceApi.ApprovalNodeInfo) => {
  175. if (node.nodeType === NodeType.START_USER_NODE && node.startTime) {
  176. return `发起时间:${formatDate(node.startTime)}`
  177. }
  178. if (node.endTime) {
  179. return `审批时间:${formatDate(node.endTime)}`
  180. }
  181. if (node.startTime) {
  182. return `创建时间:${formatDate(node.startTime)}`
  183. }
  184. }
  185. </script>