提交 | 用户 | 时间
820397 1 <template>
3e359e 2   <ContentWrap :bodyStyle="{ padding: '10px 20px 0' }" class="position-relative">
H 3     <div class="processInstance-wrap-main">
4       <el-scrollbar>
5         <img
6           class="position-absolute right-20px"
7           width="150"
8           :src="auditIconsMap[processInstance.status]"
9           alt=""
820397 10         />
3e359e 11         <div class="text-#878c93 h-15px">编号:{{ id }}</div>
H 12         <el-divider class="!my-8px" />
13         <div class="flex items-center gap-5 mb-10px h-40px">
14           <div class="text-26px font-bold mb-5px">{{ processInstance.name }}</div>
15           <dict-tag
16             v-if="processInstance.status"
17             :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS"
18             :value="processInstance.status"
19           />
20         </div>
820397 21
3e359e 22         <div class="flex items-center gap-5 mb-10px text-13px h-35px">
H 23           <div
24             class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600"
25           >
26             <el-avatar
27               :size="28"
28               v-if="processInstance?.startUser?.avatar"
29               :src="processInstance?.startUser?.avatar"
30             />
31             <el-avatar :size="28" v-else-if="processInstance?.startUser?.nickname">
32               {{ processInstance?.startUser?.nickname.substring(0, 1) }}
33             </el-avatar>
34             {{ processInstance?.startUser?.nickname }}
35           </div>
36           <div class="text-#878c93"> {{ formatDate(processInstance.startTime) }} 提交 </div>
37         </div>
820397 38
3e359e 39         <el-tabs v-model="activeTab">
H 40           <!-- 表单信息 -->
41           <el-tab-pane label="审批详情" name="form">
42             <div class="form-scroll-area">
43               <el-scrollbar>
44                 <el-row>
45                   <el-col :span="17" class="!flex !flex-col formCol">
46                     <!-- 表单信息 -->
47                     <div
48                       v-loading="processInstanceLoading"
49                       class="form-box flex flex-col mb-30px flex-1"
50                     >
51                       <!-- 情况一:流程表单 -->
52                       <el-col v-if="processDefinition?.formType === 10">
53                         <form-create
54                           v-model="detailForm.value"
55                           v-model:api="fApi"
56                           :option="detailForm.option"
57                           :rule="detailForm.rule"
58                         />
59                       </el-col>
60                       <!-- 情况二:业务表单 -->
61                       <div v-if="processDefinition?.formType === 20">
62                         <BusinessFormComponent :id="processInstance.businessKey" />
63                       </div>
64                     </div>
65                   </el-col>
66                   <el-col :span="7">
67                     <!-- 审批记录时间线 -->
68                     <ProcessInstanceTimeline :activity-nodes="activityNodes" />
69                   </el-col>
70                 </el-row>
71               </el-scrollbar>
72             </div>
73           </el-tab-pane>
820397 74
3e359e 75           <!-- 流程图 -->
H 76           <el-tab-pane label="流程图" name="diagram">
77             <div class="form-scroll-area">
78               <ProcessInstanceSimpleViewer
79                 v-show="
80                   processDefinition.modelType && processDefinition.modelType === BpmModelType.SIMPLE
81                 "
82                 :loading="processInstanceLoading"
83                 :model-view="processModelView"
84               />
85               <ProcessInstanceBpmnViewer
86                 v-show="
87                   processDefinition.modelType && processDefinition.modelType === BpmModelType.BPMN
88                 "
89                 :loading="processInstanceLoading"
90                 :model-view="processModelView"
91               />
92             </div>
93           </el-tab-pane>
94
95           <!-- 流转记录 -->
96           <el-tab-pane label="流转记录" name="record">
97             <div class="form-scroll-area">
98               <el-scrollbar>
99                 <ProcessInstanceTaskList :loading="processInstanceLoading" :id="id" />
100               </el-scrollbar>
101             </div>
102           </el-tab-pane>
103
104           <!-- 流转评论 TODO 待开发 -->
105           <el-tab-pane label="流转评论" name="comment" v-if="false">
106             <div class="form-scroll-area">
107               <el-scrollbar> 流转评论 </el-scrollbar>
108             </div>
109           </el-tab-pane>
110         </el-tabs>
111
112         <div class="b-t-solid border-t-1px border-[var(--el-border-color)]">
113           <!-- 操作栏按钮 -->
114           <ProcessInstanceOperationButton
115             ref="operationButtonRef"
116             :process-instance="processInstance"
117             :process-definition="processDefinition"
118             :userOptions="userOptions"
119             @success="refresh"
120           />
121         </div>
122       </el-scrollbar>
123     </div>
820397 124   </ContentWrap>
H 125 </template>
126 <script lang="ts" setup>
3e359e 127 import { formatDate } from '@/utils/formatTime'
H 128 import { DICT_TYPE } from '@/utils/dict'
129 import { BpmModelType } from '@/utils/constants'
820397 130 import { setConfAndFields2 } from '@/utils/formCreate'
H 131 import { registerComponent } from '@/utils/routerHelper'
3e359e 132 import type { ApiAttrs } from '@form-create/element-ui/types/config'
H 133 import * as ProcessInstanceApi from '@/api/bpm/processInstance'
820397 134 import * as UserApi from '@/api/system/user'
3e359e 135 import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
H 136 import ProcessInstanceSimpleViewer from './ProcessInstanceSimpleViewer.vue'
137 import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
138 import ProcessInstanceOperationButton from './ProcessInstanceOperationButton.vue'
139 import ProcessInstanceTimeline from './ProcessInstanceTimeline.vue'
140 import { FieldPermissionType } from '@/components/SimpleProcessDesignerV2/src/consts'
141 import { TaskStatusEnum } from '@/api/bpm/task'
142 import runningSvg from '@/assets/svgs/bpm/running.svg'
143 import approveSvg from '@/assets/svgs/bpm/approve.svg'
144 import rejectSvg from '@/assets/svgs/bpm/reject.svg'
145 import cancelSvg from '@/assets/svgs/bpm/cancel.svg'
820397 146
H 147 defineOptions({ name: 'BpmProcessInstanceDetail' })
3e359e 148 const props = defineProps<{
H 149   id: string // 流程实例的编号
150   taskId?: string // 任务编号
151   activityId?: string //流程活动编号,用于抄送查看
152 }>()
820397 153 const message = useMessage() // 消息弹窗
H 154 const processInstanceLoading = ref(false) // 流程实例的加载中
155 const processInstance = ref<any>({}) // 流程实例
3e359e 156 const processDefinition = ref<any>({}) // 流程定义
H 157 const processModelView = ref<any>({}) // 流程模型视图
158 const operationButtonRef = ref() // 操作按钮组件 ref
159 const auditIconsMap = {
160   [TaskStatusEnum.RUNNING]: runningSvg,
161   [TaskStatusEnum.APPROVE]: approveSvg,
162   [TaskStatusEnum.REJECT]: rejectSvg,
163   [TaskStatusEnum.CANCEL]: cancelSvg
164 }
820397 165
H 166 // ========== 申请信息 ==========
167 const fApi = ref<ApiAttrs>() //
168 const detailForm = ref({
169   rule: [],
170   option: {},
171   value: {}
172 }) // 流程实例的表单详情
173
174 /** 获得详情 */
175 const getDetail = () => {
3e359e 176   getApprovalDetail()
H 177
178   getProcessModelView()
820397 179 }
H 180
181 /** 加载流程实例 */
3e359e 182 const BusinessFormComponent = ref<any>(null) // 异步组件
H 183 /** 获取审批详情 */
184 const getApprovalDetail = async () => {
185   processInstanceLoading.value = true
820397 186   try {
3e359e 187     const param = {
H 188       processInstanceId: props.id,
189       activityId: props.activityId,
190       taskId: props.taskId
191     }
192     const data = await ProcessInstanceApi.getApprovalDetail(param)
820397 193     if (!data) {
3e359e 194       message.error('查询不到审批详情信息!')
H 195       return
196     }
197     if (!data.processDefinition || !data.processInstance) {
820397 198       message.error('查询不到流程信息!')
H 199       return
200     }
3e359e 201     processInstance.value = data.processInstance
H 202     processDefinition.value = data.processDefinition
820397 203
H 204     // 设置表单信息
3e359e 205     if (processDefinition.value.formType === 10) {
H 206       // 获取表单字段权限
207       const formFieldsPermission = data.formFieldsPermission
208
209       if (detailForm.value.rule.length > 0) {
210         // 避免刷新 form-create 显示不了
211         detailForm.value.value = processInstance.value.formVariables
212       } else {
213         setConfAndFields2(
214           detailForm,
215           processDefinition.value.formConf,
216           processDefinition.value.formFields,
217           processInstance.value.formVariables
218         )
219       }
820397 220       nextTick().then(() => {
H 221         fApi.value?.btn.show(false)
222         fApi.value?.resetBtn.show(false)
3e359e 223         //@ts-ignore
820397 224         fApi.value?.disabled(true)
3e359e 225         // 设置表单字段权限
H 226         if (formFieldsPermission) {
227           Object.keys(data.formFieldsPermission).forEach((item) => {
228             setFieldPermission(item, formFieldsPermission[item])
229           })
230         }
820397 231       })
H 232     } else {
233       // 注意:data.processDefinition.formCustomViewPath 是组件的全路径,例如说:/crm/contract/detail/index.vue
234       BusinessFormComponent.value = registerComponent(data.processDefinition.formCustomViewPath)
235     }
236
3e359e 237     // 获取审批节点,显示 Timeline 的数据
H 238     activityNodes.value = data.activityNodes
239
240     // 获取待办任务显示操作按钮
241     operationButtonRef.value?.loadTodoTask(data.todoTask)
820397 242   } finally {
H 243     processInstanceLoading.value = false
244   }
245 }
246
3e359e 247 /** 获取流程模型视图*/
H 248 const getProcessModelView = async () => {
249   if (BpmModelType.BPMN === processDefinition.value?.modelType) {
250     // 重置,解决 BPMN 流程图刷新不会重新渲染问题
251     processModelView.value = {
252       bpmnXml: ''
253     }
254   }
255   const data = await ProcessInstanceApi.getProcessInstanceBpmnModelView(props.id)
256   if (data) {
257     processModelView.value = data
258   }
259 }
820397 260
3e359e 261 // 审批节点信息
H 262 const activityNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([])
263 /**
264  * 设置表单权限
265  */
266 const setFieldPermission = (field: string, permission: string) => {
267   if (permission === FieldPermissionType.READ) {
268     //@ts-ignore
269     fApi.value?.disabled(true, field)
270   }
271   if (permission === FieldPermissionType.WRITE) {
272     //@ts-ignore
273     fApi.value?.disabled(false, field)
274   }
275   if (permission === FieldPermissionType.NONE) {
276     //@ts-ignore
277     fApi.value?.hidden(true, field)
820397 278   }
H 279 }
280
281 /**
3e359e 282  * 操作成功后刷新
820397 283  */
3e359e 284 const refresh = () => {
H 285   // 重新获取详情
286   getDetail()
820397 287 }
3e359e 288
H 289 /** 当前的Tab */
290 const activeTab = ref('form')
820397 291
H 292 /** 初始化 */
293 const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
294 onMounted(async () => {
295   getDetail()
296   // 获得用户列表
297   userOptions.value = await UserApi.getSimpleUserList()
298 })
299 </script>
3e359e 300
H 301 <style lang="scss" scoped>
302 $wrap-padding-height: 20px;
303 $wrap-margin-height: 15px;
304 $button-height: 51px;
305 $process-header-height: 194px;
306
307 .processInstance-wrap-main {
308   height: calc(
309     100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
310   );
311   max-height: calc(
312     100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
313   );
314   overflow: auto;
315
316   .form-scroll-area {
317     height: calc(
318       100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
319         $process-header-height - 40px
320     );
321     max-height: calc(
322       100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
323         $process-header-height - 40px
324     );
325     overflow: auto;
326     display: flex;
327     flex-direction: column;
328
329     :deep(.box-card) {
330       height: 100%;
331       flex: 1;
332
333       .el-card__body {
334         height: 100%;
335         padding: 0;
336       }
337     }
338   }
339 }
340
341 .form-box {
342   :deep(.el-card) {
343     border: none;
344   }
345 }
346 </style>