潘志宝
6 天以前 f6ea543b3de9a770c1bf5db2baf3e8a5dc2c867a
提交 | 用户 | 时间
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                       <!-- 情况一:流程表单 -->
9259c2 52                       <el-col v-if="processDefinition?.formType === BpmModelFormType.NORMAL">
3e359e 53                         <form-create
H 54                           v-model="detailForm.value"
55                           v-model:api="fApi"
56                           :option="detailForm.option"
57                           :rule="detailForm.rule"
58                         />
59                       </el-col>
60                       <!-- 情况二:业务表单 -->
9259c2 61                       <div v-if="processDefinition?.formType === BpmModelFormType.CUSTOM">
3e359e 62                         <BusinessFormComponent :id="processInstance.businessKey" />
H 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"
9259c2 119             :normal-form="detailForm"
H 120             :normal-form-api="fApi"
121             :writable-fields="writableFields"
3e359e 122             @success="refresh"
H 123           />
124         </div>
125       </el-scrollbar>
126     </div>
820397 127   </ContentWrap>
H 128 </template>
129 <script lang="ts" setup>
3e359e 130 import { formatDate } from '@/utils/formatTime'
H 131 import { DICT_TYPE } from '@/utils/dict'
9259c2 132 import { BpmModelType, BpmModelFormType } from '@/utils/constants'
820397 133 import { setConfAndFields2 } from '@/utils/formCreate'
H 134 import { registerComponent } from '@/utils/routerHelper'
3e359e 135 import type { ApiAttrs } from '@form-create/element-ui/types/config'
H 136 import * as ProcessInstanceApi from '@/api/bpm/processInstance'
820397 137 import * as UserApi from '@/api/system/user'
3e359e 138 import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
H 139 import ProcessInstanceSimpleViewer from './ProcessInstanceSimpleViewer.vue'
140 import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
141 import ProcessInstanceOperationButton from './ProcessInstanceOperationButton.vue'
142 import ProcessInstanceTimeline from './ProcessInstanceTimeline.vue'
143 import { FieldPermissionType } from '@/components/SimpleProcessDesignerV2/src/consts'
144 import { TaskStatusEnum } from '@/api/bpm/task'
145 import runningSvg from '@/assets/svgs/bpm/running.svg'
146 import approveSvg from '@/assets/svgs/bpm/approve.svg'
147 import rejectSvg from '@/assets/svgs/bpm/reject.svg'
148 import cancelSvg from '@/assets/svgs/bpm/cancel.svg'
820397 149
H 150 defineOptions({ name: 'BpmProcessInstanceDetail' })
3e359e 151 const props = defineProps<{
H 152   id: string // 流程实例的编号
153   taskId?: string // 任务编号
154   activityId?: string //流程活动编号,用于抄送查看
155 }>()
820397 156 const message = useMessage() // 消息弹窗
H 157 const processInstanceLoading = ref(false) // 流程实例的加载中
158 const processInstance = ref<any>({}) // 流程实例
3e359e 159 const processDefinition = ref<any>({}) // 流程定义
H 160 const processModelView = ref<any>({}) // 流程模型视图
161 const operationButtonRef = ref() // 操作按钮组件 ref
162 const auditIconsMap = {
163   [TaskStatusEnum.RUNNING]: runningSvg,
164   [TaskStatusEnum.APPROVE]: approveSvg,
165   [TaskStatusEnum.REJECT]: rejectSvg,
166   [TaskStatusEnum.CANCEL]: cancelSvg
167 }
820397 168
H 169 // ========== 申请信息 ==========
170 const fApi = ref<ApiAttrs>() //
171 const detailForm = ref({
172   rule: [],
173   option: {},
174   value: {}
175 }) // 流程实例的表单详情
176
9259c2 177 const writableFields: Array<string> = [] // 表单可以编辑的字段
H 178
820397 179 /** 获得详情 */
H 180 const getDetail = () => {
3e359e 181   getApprovalDetail()
H 182
183   getProcessModelView()
820397 184 }
H 185
186 /** 加载流程实例 */
3e359e 187 const BusinessFormComponent = ref<any>(null) // 异步组件
H 188 /** 获取审批详情 */
189 const getApprovalDetail = async () => {
190   processInstanceLoading.value = true
820397 191   try {
3e359e 192     const param = {
H 193       processInstanceId: props.id,
194       activityId: props.activityId,
195       taskId: props.taskId
196     }
197     const data = await ProcessInstanceApi.getApprovalDetail(param)
820397 198     if (!data) {
3e359e 199       message.error('查询不到审批详情信息!')
H 200       return
201     }
202     if (!data.processDefinition || !data.processInstance) {
820397 203       message.error('查询不到流程信息!')
H 204       return
205     }
3e359e 206     processInstance.value = data.processInstance
H 207     processDefinition.value = data.processDefinition
820397 208
H 209     // 设置表单信息
9259c2 210     if (processDefinition.value.formType === BpmModelFormType.NORMAL) {
3e359e 211       // 获取表单字段权限
H 212       const formFieldsPermission = data.formFieldsPermission
9259c2 213       // 清空可编辑字段为空
H 214       writableFields.splice(0)
215       if (detailForm.value.rule?.length > 0) {
3e359e 216         // 避免刷新 form-create 显示不了
H 217         detailForm.value.value = processInstance.value.formVariables
218       } else {
219         setConfAndFields2(
220           detailForm,
221           processDefinition.value.formConf,
222           processDefinition.value.formFields,
223           processInstance.value.formVariables
224         )
225       }
820397 226       nextTick().then(() => {
H 227         fApi.value?.btn.show(false)
228         fApi.value?.resetBtn.show(false)
3e359e 229         //@ts-ignore
820397 230         fApi.value?.disabled(true)
3e359e 231         // 设置表单字段权限
H 232         if (formFieldsPermission) {
233           Object.keys(data.formFieldsPermission).forEach((item) => {
234             setFieldPermission(item, formFieldsPermission[item])
235           })
236         }
820397 237       })
H 238     } else {
239       // 注意:data.processDefinition.formCustomViewPath 是组件的全路径,例如说:/crm/contract/detail/index.vue
240       BusinessFormComponent.value = registerComponent(data.processDefinition.formCustomViewPath)
241     }
242
3e359e 243     // 获取审批节点,显示 Timeline 的数据
H 244     activityNodes.value = data.activityNodes
245
246     // 获取待办任务显示操作按钮
247     operationButtonRef.value?.loadTodoTask(data.todoTask)
820397 248   } finally {
H 249     processInstanceLoading.value = false
250   }
251 }
252
3e359e 253 /** 获取流程模型视图*/
H 254 const getProcessModelView = async () => {
255   if (BpmModelType.BPMN === processDefinition.value?.modelType) {
256     // 重置,解决 BPMN 流程图刷新不会重新渲染问题
257     processModelView.value = {
258       bpmnXml: ''
259     }
260   }
261   const data = await ProcessInstanceApi.getProcessInstanceBpmnModelView(props.id)
262   if (data) {
263     processModelView.value = data
264   }
265 }
820397 266
3e359e 267 // 审批节点信息
H 268 const activityNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([])
269 /**
270  * 设置表单权限
271  */
272 const setFieldPermission = (field: string, permission: string) => {
273   if (permission === FieldPermissionType.READ) {
274     //@ts-ignore
275     fApi.value?.disabled(true, field)
276   }
277   if (permission === FieldPermissionType.WRITE) {
278     //@ts-ignore
279     fApi.value?.disabled(false, field)
9259c2 280     // 加入可以编辑的字段
H 281     writableFields.push(field)
3e359e 282   }
H 283   if (permission === FieldPermissionType.NONE) {
284     //@ts-ignore
285     fApi.value?.hidden(true, field)
820397 286   }
H 287 }
288
289 /**
3e359e 290  * 操作成功后刷新
820397 291  */
3e359e 292 const refresh = () => {
H 293   // 重新获取详情
294   getDetail()
820397 295 }
3e359e 296
H 297 /** 当前的Tab */
298 const activeTab = ref('form')
820397 299
H 300 /** 初始化 */
301 const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
302 onMounted(async () => {
303   getDetail()
304   // 获得用户列表
305   userOptions.value = await UserApi.getSimpleUserList()
306 })
307 </script>
3e359e 308
H 309 <style lang="scss" scoped>
310 $wrap-padding-height: 20px;
311 $wrap-margin-height: 15px;
312 $button-height: 51px;
313 $process-header-height: 194px;
314
315 .processInstance-wrap-main {
316   height: calc(
317     100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
318   );
319   max-height: calc(
320     100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
321   );
322   overflow: auto;
323
324   .form-scroll-area {
9259c2 325     display: flex;
3e359e 326     height: calc(
H 327       100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
328         $process-header-height - 40px
329     );
330     max-height: calc(
331       100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
332         $process-header-height - 40px
333     );
334     overflow: auto;
335     flex-direction: column;
336
337     :deep(.box-card) {
338       height: 100%;
339       flex: 1;
340
341       .el-card__body {
342         height: 100%;
343         padding: 0;
344       }
345     }
346   }
347 }
348
349 .form-box {
350   :deep(.el-card) {
351     border: none;
352   }
353 }
354 </style>