提交 | 用户 | 时间
|
3e359e
|
1 |
<template> |
H |
2 |
<ContentWrap :bodyStyle="{ padding: '10px 20px 0' }"> |
|
3 |
<div class="processInstance-wrap-main"> |
|
4 |
<el-scrollbar> |
|
5 |
<div class="text-#878c93 h-15px">流程:{{ selectProcessDefinition.name }}</div> |
|
6 |
<el-divider class="!my-8px" /> |
|
7 |
|
|
8 |
<!-- 中间主要内容 tab 栏 --> |
|
9 |
<el-tabs v-model="activeTab"> |
|
10 |
<!-- 表单信息 --> |
|
11 |
<el-tab-pane label="表单填写" name="form"> |
|
12 |
<div class="form-scroll-area"> |
|
13 |
<el-scrollbar> |
|
14 |
<el-row> |
|
15 |
<el-col :span="17"> |
|
16 |
<form-create |
|
17 |
:rule="detailForm.rule" |
|
18 |
v-model:api="fApi" |
|
19 |
v-model="detailForm.value" |
|
20 |
:option="detailForm.option" |
|
21 |
@submit="submitForm" |
|
22 |
/> |
|
23 |
</el-col> |
|
24 |
|
|
25 |
<el-col :span="6" :offset="1"> |
|
26 |
<!-- 流程时间线 --> |
|
27 |
<ProcessInstanceTimeline |
|
28 |
ref="timelineRef" |
|
29 |
:activity-nodes="activityNodes" |
|
30 |
:show-status-icon="false" |
|
31 |
@select-user-confirm="selectUserConfirm" |
|
32 |
/> |
|
33 |
</el-col> |
|
34 |
</el-row> |
|
35 |
</el-scrollbar> |
|
36 |
</div> |
|
37 |
</el-tab-pane> |
|
38 |
<!-- 流程图 --> |
|
39 |
<el-tab-pane label="流程图" name="diagram"> |
|
40 |
<div class="form-scroll-area"> |
|
41 |
<!-- BPMN 流程图预览 --> |
|
42 |
<ProcessInstanceBpmnViewer |
|
43 |
:bpmn-xml="bpmnXML" |
|
44 |
v-if="BpmModelType.BPMN === selectProcessDefinition.modelType" |
|
45 |
/> |
|
46 |
|
|
47 |
<!-- Simple 流程图预览 --> |
|
48 |
<ProcessInstanceSimpleViewer |
|
49 |
:simple-json="simpleJson" |
|
50 |
v-if="BpmModelType.SIMPLE === selectProcessDefinition.modelType" |
|
51 |
/> |
|
52 |
</div> |
|
53 |
</el-tab-pane> |
|
54 |
</el-tabs> |
|
55 |
|
|
56 |
<!-- 底部操作栏 --> |
|
57 |
<div class="b-t-solid border-t-1px border-[var(--el-border-color)]"> |
|
58 |
<!-- 操作栏按钮 --> |
|
59 |
<div |
|
60 |
v-if="activeTab === 'form'" |
|
61 |
class="h-50px bottom-10 text-14px flex items-center color-#32373c dark:color-#fff font-bold btn-container" |
|
62 |
> |
|
63 |
<el-button plain type="success" @click="submitForm"> |
|
64 |
<Icon icon="ep:select" /> 发起 |
|
65 |
</el-button> |
|
66 |
<el-button plain type="danger" @click="handleCancel"> |
|
67 |
<Icon icon="ep:close" /> 取消 |
|
68 |
</el-button> |
|
69 |
</div> |
|
70 |
</div> |
|
71 |
</el-scrollbar> |
|
72 |
</div> |
|
73 |
</ContentWrap> |
|
74 |
</template> |
|
75 |
<script lang="ts" setup> |
|
76 |
import { decodeFields, setConfAndFields2 } from '@/utils/formCreate' |
|
77 |
import { BpmModelType } from '@/utils/constants' |
|
78 |
import { CandidateStrategy } from '@/components/SimpleProcessDesignerV2/src/consts' |
|
79 |
import ProcessInstanceBpmnViewer from '../detail/ProcessInstanceBpmnViewer.vue' |
|
80 |
import ProcessInstanceSimpleViewer from '../detail/ProcessInstanceSimpleViewer.vue' |
|
81 |
import ProcessInstanceTimeline from '../detail/ProcessInstanceTimeline.vue' |
|
82 |
import type { ApiAttrs } from '@form-create/element-ui/types/config' |
|
83 |
import { useTagsViewStore } from '@/store/modules/tagsView' |
|
84 |
import * as ProcessInstanceApi from '@/api/bpm/processInstance' |
|
85 |
import * as DefinitionApi from '@/api/bpm/definition' |
|
86 |
import { ApprovalNodeInfo } from '@/api/bpm/processInstance' |
|
87 |
|
|
88 |
defineOptions({ name: 'ProcessDefinitionDetail' }) |
|
89 |
const props = defineProps<{ |
|
90 |
selectProcessDefinition: any |
|
91 |
}>() |
|
92 |
const emit = defineEmits(['cancel']) |
|
93 |
|
|
94 |
const { push, currentRoute } = useRouter() // 路由 |
|
95 |
const message = useMessage() // 消息弹窗 |
|
96 |
const { delView } = useTagsViewStore() // 视图操作 |
|
97 |
|
|
98 |
const detailForm: any = ref({ |
|
99 |
rule: [], |
|
100 |
option: {}, |
|
101 |
value: {} |
|
102 |
}) // 流程表单详情 |
|
103 |
const fApi = ref<ApiAttrs>() |
|
104 |
// 指定审批人 |
|
105 |
const startUserSelectTasks: any = ref([]) // 发起人需要选择审批人或抄送人的任务列表 |
|
106 |
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据 |
|
107 |
const bpmnXML: any = ref(null) // BPMN 数据 |
|
108 |
const simpleJson = ref<string | undefined>() // Simple 设计器数据 json 格式 |
|
109 |
|
|
110 |
const activeTab = ref('form') // 当前的 Tab |
|
111 |
const activityNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) // 审批节点信息 |
|
112 |
|
|
113 |
/** 设置表单信息、获取流程图数据 **/ |
|
114 |
const initProcessInfo = async (row: any, formVariables?: any) => { |
|
115 |
// 重置指定审批人 |
|
116 |
startUserSelectTasks.value = [] |
|
117 |
startUserSelectAssignees.value = {} |
|
118 |
|
|
119 |
// 情况一:流程表单 |
|
120 |
if (row.formType == 10) { |
|
121 |
// 设置表单 |
|
122 |
// 注意:需要从 formVariables 中,移除不在 row.formFields 的值。 |
|
123 |
// 原因是:后端返回的 formVariables 里面,会有一些非表单的信息。例如说,某个流程节点的审批人。 |
|
124 |
// 这样,就可能导致一个流程被审批不通过后,重新发起时,会直接后端报错!!! |
|
125 |
const allowedFields = decodeFields(row.formFields).map((fieldObj: any) => fieldObj.field) |
|
126 |
for (const key in formVariables) { |
|
127 |
if (!allowedFields.includes(key)) { |
|
128 |
delete formVariables[key] |
|
129 |
} |
|
130 |
} |
|
131 |
setConfAndFields2(detailForm, row.formConf, row.formFields, formVariables) |
|
132 |
await nextTick() |
|
133 |
fApi.value?.btn.show(false) // 隐藏提交按钮 |
|
134 |
// 获取流程审批信息 |
|
135 |
await getApprovalDetail(row) |
|
136 |
|
|
137 |
// 加载流程图 |
|
138 |
const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id) |
|
139 |
if (processDefinitionDetail) { |
|
140 |
bpmnXML.value = processDefinitionDetail.bpmnXml |
|
141 |
simpleJson.value = processDefinitionDetail.simpleModel |
|
142 |
} |
|
143 |
// 情况二:业务表单 |
|
144 |
} else if (row.formCustomCreatePath) { |
|
145 |
await push({ |
|
146 |
path: row.formCustomCreatePath |
|
147 |
}) |
|
148 |
// 这里暂时无需加载流程图,因为跳出到另外个 Tab; |
|
149 |
} |
|
150 |
} |
|
151 |
|
|
152 |
/** 获取审批详情 */ |
|
153 |
const getApprovalDetail = async (row: any) => { |
|
154 |
try { |
|
155 |
const data = await ProcessInstanceApi.getApprovalDetail({ processDefinitionId: row.id }) |
|
156 |
if (!data) { |
|
157 |
message.error('查询不到审批详情信息!') |
|
158 |
return |
|
159 |
} |
|
160 |
|
|
161 |
// 获取发起人自选的任务 |
|
162 |
startUserSelectTasks.value = data.activityNodes?.filter( |
|
163 |
(node: ApprovalNodeInfo) => CandidateStrategy.START_USER_SELECT === node.candidateStrategy |
|
164 |
) |
|
165 |
if (startUserSelectTasks.value?.length > 0) { |
|
166 |
for (const node of startUserSelectTasks.value) { |
|
167 |
startUserSelectAssignees.value[node.id] = [] |
|
168 |
} |
|
169 |
} |
|
170 |
|
|
171 |
// 获取审批节点,显示 Timeline 的数据 |
|
172 |
activityNodes.value = data.activityNodes |
|
173 |
} finally { |
|
174 |
} |
|
175 |
} |
|
176 |
|
|
177 |
/** 提交按钮 */ |
|
178 |
const submitForm = async () => { |
|
179 |
if (!fApi.value || !props.selectProcessDefinition) { |
|
180 |
return |
|
181 |
} |
|
182 |
// 如果有指定审批人,需要校验 |
|
183 |
if (startUserSelectTasks.value?.length > 0) { |
|
184 |
for (const userTask of startUserSelectTasks.value) { |
|
185 |
if ( |
|
186 |
Array.isArray(startUserSelectAssignees.value[userTask.id]) && |
|
187 |
startUserSelectAssignees.value[userTask.id].length === 0 |
|
188 |
) |
|
189 |
return message.warning(`请选择${userTask.name}的候选人`) |
|
190 |
} |
|
191 |
} |
|
192 |
|
|
193 |
// 提交请求 |
|
194 |
fApi.value.btn.loading(true) |
|
195 |
try { |
|
196 |
await ProcessInstanceApi.createProcessInstance({ |
|
197 |
processDefinitionId: props.selectProcessDefinition.id, |
|
198 |
variables: detailForm.value.value, |
|
199 |
startUserSelectAssignees: startUserSelectAssignees.value |
|
200 |
}) |
|
201 |
// 提示 |
|
202 |
message.success('发起流程成功') |
|
203 |
// 跳转回去 |
|
204 |
delView(unref(currentRoute)) |
|
205 |
await push({ |
|
206 |
name: 'BpmProcessInstanceMy' |
|
207 |
}) |
|
208 |
} finally { |
|
209 |
fApi.value.btn.loading(false) |
|
210 |
} |
|
211 |
} |
|
212 |
|
|
213 |
/** 取消发起审批 */ |
|
214 |
const handleCancel = () => { |
|
215 |
emit('cancel') |
|
216 |
} |
|
217 |
|
|
218 |
/** 选择发起人 */ |
|
219 |
const selectUserConfirm = (id: string, userList: any[]) => { |
|
220 |
startUserSelectAssignees.value[id] = userList?.map((item: any) => item.id) |
|
221 |
} |
|
222 |
|
|
223 |
defineExpose({ initProcessInfo }) |
|
224 |
</script> |
|
225 |
|
|
226 |
<style lang="scss" scoped> |
|
227 |
$wrap-padding-height: 20px; |
|
228 |
$wrap-margin-height: 15px; |
|
229 |
$button-height: 51px; |
|
230 |
$process-header-height: 105px; |
|
231 |
|
|
232 |
.processInstance-wrap-main { |
|
233 |
height: calc( |
|
234 |
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px |
|
235 |
); |
|
236 |
max-height: calc( |
|
237 |
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px |
|
238 |
); |
|
239 |
overflow: auto; |
|
240 |
|
|
241 |
.form-scroll-area { |
|
242 |
height: calc( |
|
243 |
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px - |
|
244 |
$process-header-height - 40px |
|
245 |
); |
|
246 |
max-height: calc( |
|
247 |
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px - |
|
248 |
$process-header-height - 40px |
|
249 |
); |
|
250 |
overflow: auto; |
|
251 |
} |
|
252 |
} |
|
253 |
|
|
254 |
.form-box { |
|
255 |
:deep(.el-card) { |
|
256 |
border: none; |
|
257 |
} |
|
258 |
} |
|
259 |
</style> |