<template>
|
<ContentWrap :bodyStyle="{ padding: '10px 20px 0' }">
|
<div class="processInstance-wrap-main">
|
<el-scrollbar>
|
<div class="text-#878c93 h-15px">流程:{{ selectProcessDefinition.name }}</div>
|
<el-divider class="!my-8px" />
|
|
<!-- 中间主要内容 tab 栏 -->
|
<el-tabs v-model="activeTab">
|
<!-- 表单信息 -->
|
<el-tab-pane label="表单填写" name="form">
|
<div class="form-scroll-area">
|
<el-scrollbar>
|
<el-row>
|
<el-col :span="17">
|
<form-create
|
:rule="detailForm.rule"
|
v-model:api="fApi"
|
v-model="detailForm.value"
|
:option="detailForm.option"
|
@submit="submitForm"
|
/>
|
</el-col>
|
|
<el-col :span="6" :offset="1">
|
<!-- 流程时间线 -->
|
<ProcessInstanceTimeline
|
ref="timelineRef"
|
:activity-nodes="activityNodes"
|
:show-status-icon="false"
|
@select-user-confirm="selectUserConfirm"
|
/>
|
</el-col>
|
</el-row>
|
</el-scrollbar>
|
</div>
|
</el-tab-pane>
|
<!-- 流程图 -->
|
<el-tab-pane label="流程图" name="diagram">
|
<div class="form-scroll-area">
|
<!-- BPMN 流程图预览 -->
|
<ProcessInstanceBpmnViewer
|
:bpmn-xml="bpmnXML"
|
v-if="BpmModelType.BPMN === selectProcessDefinition.modelType"
|
/>
|
|
<!-- Simple 流程图预览 -->
|
<ProcessInstanceSimpleViewer
|
:simple-json="simpleJson"
|
v-if="BpmModelType.SIMPLE === selectProcessDefinition.modelType"
|
/>
|
</div>
|
</el-tab-pane>
|
</el-tabs>
|
|
<!-- 底部操作栏 -->
|
<div class="b-t-solid border-t-1px border-[var(--el-border-color)]">
|
<!-- 操作栏按钮 -->
|
<div
|
v-if="activeTab === 'form'"
|
class="h-50px bottom-10 text-14px flex items-center color-#32373c dark:color-#fff font-bold btn-container"
|
>
|
<el-button plain type="success" @click="submitForm">
|
<Icon icon="ep:select" /> 发起
|
</el-button>
|
<el-button plain type="danger" @click="handleCancel">
|
<Icon icon="ep:close" /> 取消
|
</el-button>
|
</div>
|
</div>
|
</el-scrollbar>
|
</div>
|
</ContentWrap>
|
</template>
|
<script lang="ts" setup>
|
import { decodeFields, setConfAndFields2 } from '@/utils/formCreate'
|
import { BpmModelType } from '@/utils/constants'
|
import { CandidateStrategy } from '@/components/SimpleProcessDesignerV2/src/consts'
|
import ProcessInstanceBpmnViewer from '../detail/ProcessInstanceBpmnViewer.vue'
|
import ProcessInstanceSimpleViewer from '../detail/ProcessInstanceSimpleViewer.vue'
|
import ProcessInstanceTimeline from '../detail/ProcessInstanceTimeline.vue'
|
import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
import { useTagsViewStore } from '@/store/modules/tagsView'
|
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
import * as DefinitionApi from '@/api/bpm/definition'
|
import { ApprovalNodeInfo } from '@/api/bpm/processInstance'
|
|
defineOptions({ name: 'ProcessDefinitionDetail' })
|
const props = defineProps<{
|
selectProcessDefinition: any
|
}>()
|
const emit = defineEmits(['cancel'])
|
|
const { push, currentRoute } = useRouter() // 路由
|
const message = useMessage() // 消息弹窗
|
const { delView } = useTagsViewStore() // 视图操作
|
|
const detailForm: any = ref({
|
rule: [],
|
option: {},
|
value: {}
|
}) // 流程表单详情
|
const fApi = ref<ApiAttrs>()
|
// 指定审批人
|
const startUserSelectTasks: any = ref([]) // 发起人需要选择审批人或抄送人的任务列表
|
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
|
const bpmnXML: any = ref(null) // BPMN 数据
|
const simpleJson = ref<string | undefined>() // Simple 设计器数据 json 格式
|
|
const activeTab = ref('form') // 当前的 Tab
|
const activityNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) // 审批节点信息
|
|
/** 设置表单信息、获取流程图数据 **/
|
const initProcessInfo = async (row: any, formVariables?: any) => {
|
// 重置指定审批人
|
startUserSelectTasks.value = []
|
startUserSelectAssignees.value = {}
|
|
// 情况一:流程表单
|
if (row.formType == 10) {
|
// 设置表单
|
// 注意:需要从 formVariables 中,移除不在 row.formFields 的值。
|
// 原因是:后端返回的 formVariables 里面,会有一些非表单的信息。例如说,某个流程节点的审批人。
|
// 这样,就可能导致一个流程被审批不通过后,重新发起时,会直接后端报错!!!
|
const allowedFields = decodeFields(row.formFields).map((fieldObj: any) => fieldObj.field)
|
for (const key in formVariables) {
|
if (!allowedFields.includes(key)) {
|
delete formVariables[key]
|
}
|
}
|
setConfAndFields2(detailForm, row.formConf, row.formFields, formVariables)
|
await nextTick()
|
fApi.value?.btn.show(false) // 隐藏提交按钮
|
// 获取流程审批信息
|
await getApprovalDetail(row)
|
|
// 加载流程图
|
const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id)
|
if (processDefinitionDetail) {
|
bpmnXML.value = processDefinitionDetail.bpmnXml
|
simpleJson.value = processDefinitionDetail.simpleModel
|
}
|
// 情况二:业务表单
|
} else if (row.formCustomCreatePath) {
|
await push({
|
path: row.formCustomCreatePath
|
})
|
// 这里暂时无需加载流程图,因为跳出到另外个 Tab;
|
}
|
}
|
|
/** 获取审批详情 */
|
const getApprovalDetail = async (row: any) => {
|
try {
|
const data = await ProcessInstanceApi.getApprovalDetail({ processDefinitionId: row.id })
|
if (!data) {
|
message.error('查询不到审批详情信息!')
|
return
|
}
|
|
// 获取发起人自选的任务
|
startUserSelectTasks.value = data.activityNodes?.filter(
|
(node: ApprovalNodeInfo) => CandidateStrategy.START_USER_SELECT === node.candidateStrategy
|
)
|
if (startUserSelectTasks.value?.length > 0) {
|
for (const node of startUserSelectTasks.value) {
|
startUserSelectAssignees.value[node.id] = []
|
}
|
}
|
|
// 获取审批节点,显示 Timeline 的数据
|
activityNodes.value = data.activityNodes
|
} finally {
|
}
|
}
|
|
/** 提交按钮 */
|
const submitForm = async () => {
|
if (!fApi.value || !props.selectProcessDefinition) {
|
return
|
}
|
// 如果有指定审批人,需要校验
|
if (startUserSelectTasks.value?.length > 0) {
|
for (const userTask of startUserSelectTasks.value) {
|
if (
|
Array.isArray(startUserSelectAssignees.value[userTask.id]) &&
|
startUserSelectAssignees.value[userTask.id].length === 0
|
)
|
return message.warning(`请选择${userTask.name}的候选人`)
|
}
|
}
|
|
// 提交请求
|
fApi.value.btn.loading(true)
|
try {
|
await ProcessInstanceApi.createProcessInstance({
|
processDefinitionId: props.selectProcessDefinition.id,
|
variables: detailForm.value.value,
|
startUserSelectAssignees: startUserSelectAssignees.value
|
})
|
// 提示
|
message.success('发起流程成功')
|
// 跳转回去
|
delView(unref(currentRoute))
|
await push({
|
name: 'BpmProcessInstanceMy'
|
})
|
} finally {
|
fApi.value.btn.loading(false)
|
}
|
}
|
|
/** 取消发起审批 */
|
const handleCancel = () => {
|
emit('cancel')
|
}
|
|
/** 选择发起人 */
|
const selectUserConfirm = (id: string, userList: any[]) => {
|
startUserSelectAssignees.value[id] = userList?.map((item: any) => item.id)
|
}
|
|
defineExpose({ initProcessInfo })
|
</script>
|
|
<style lang="scss" scoped>
|
$wrap-padding-height: 20px;
|
$wrap-margin-height: 15px;
|
$button-height: 51px;
|
$process-header-height: 105px;
|
|
.processInstance-wrap-main {
|
height: calc(
|
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
|
);
|
max-height: calc(
|
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
|
);
|
overflow: auto;
|
|
.form-scroll-area {
|
height: calc(
|
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
|
$process-header-height - 40px
|
);
|
max-height: calc(
|
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
|
$process-header-height - 40px
|
);
|
overflow: auto;
|
}
|
}
|
|
.form-box {
|
:deep(.el-card) {
|
border: none;
|
}
|
}
|
</style>
|