提交 | 用户 | 时间
|
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> |