From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/views/bpm/processInstance/detail/index.vue | 363 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 363 insertions(+), 0 deletions(-)
diff --git a/src/views/bpm/processInstance/detail/index.vue b/src/views/bpm/processInstance/detail/index.vue
new file mode 100644
index 0000000..aec1473
--- /dev/null
+++ b/src/views/bpm/processInstance/detail/index.vue
@@ -0,0 +1,363 @@
+<template>
+ <ContentWrap :bodyStyle="{ padding: '10px 20px 0' }" class="position-relative">
+ <div class="processInstance-wrap-main">
+ <el-scrollbar>
+ <img
+ class="position-absolute right-20px"
+ width="150"
+ :src="auditIconsMap[processInstance.status]"
+ alt=""
+ />
+ <div class="flex">
+ <div class="text-#878c93 h-15px">缂栧彿锛歿{ id }}</div>
+ <Icon icon="ep:printer" class="ml-15px cursor-pointer" @click="handlePrint" />
+ </div>
+ <el-divider class="!my-8px" />
+ <div class="flex items-center gap-5 mb-10px h-40px">
+ <div class="text-26px font-bold mb-5px">{{ processInstance.name }}</div>
+ <dict-tag
+ v-if="processInstance.status"
+ :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS"
+ :value="processInstance.status"
+ />
+ </div>
+
+ <div class="flex items-center gap-5 mb-10px text-13px h-35px">
+ <div
+ class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600"
+ >
+ <el-avatar
+ :size="28"
+ v-if="processInstance?.startUser?.avatar"
+ :src="processInstance?.startUser?.avatar"
+ />
+ <el-avatar :size="28" v-else-if="processInstance?.startUser?.nickname">
+ {{ processInstance?.startUser?.nickname.substring(0, 1) }}
+ </el-avatar>
+ {{ processInstance?.startUser?.nickname }}
+ </div>
+ <div class="text-#878c93"> {{ formatDate(processInstance.startTime) }} 鎻愪氦 </div>
+ </div>
+
+ <el-tabs v-model="activeTab">
+ <!-- 琛ㄥ崟淇℃伅 -->
+ <el-tab-pane label="瀹℃壒璇︽儏" name="form">
+ <div class="form-scroll-area">
+ <el-scrollbar>
+ <el-row>
+ <el-col :span="17" class="!flex !flex-col formCol">
+ <!-- 琛ㄥ崟淇℃伅 -->
+ <div
+ v-loading="processInstanceLoading"
+ class="form-box flex flex-col mb-30px flex-1"
+ >
+ <!-- 鎯呭喌涓�锛氭祦绋嬭〃鍗� -->
+ <el-col v-if="processDefinition?.formType === BpmModelFormType.NORMAL">
+ <form-create
+ v-model="detailForm.value"
+ v-model:api="fApi"
+ :option="detailForm.option"
+ :rule="detailForm.rule"
+ />
+ </el-col>
+ <!-- 鎯呭喌浜岋細涓氬姟琛ㄥ崟 -->
+ <div v-if="processDefinition?.formType === BpmModelFormType.CUSTOM">
+ <BusinessFormComponent :id="processInstance.businessKey" />
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="7">
+ <!-- 瀹℃壒璁板綍鏃堕棿绾� -->
+ <ProcessInstanceTimeline :activity-nodes="activityNodes" />
+ </el-col>
+ </el-row>
+ </el-scrollbar>
+ </div>
+ </el-tab-pane>
+
+ <!-- 娴佺▼鍥� -->
+ <el-tab-pane label="娴佺▼鍥�" name="diagram">
+ <div class="form-scroll-area">
+ <ProcessInstanceSimpleViewer
+ v-show="
+ processDefinition.modelType && processDefinition.modelType === BpmModelType.SIMPLE
+ "
+ :loading="processInstanceLoading"
+ :model-view="processModelView"
+ />
+ <ProcessInstanceBpmnViewer
+ v-show="
+ processDefinition.modelType && processDefinition.modelType === BpmModelType.BPMN
+ "
+ :loading="processInstanceLoading"
+ :model-view="processModelView"
+ />
+ </div>
+ </el-tab-pane>
+
+ <!-- 娴佽浆璁板綍 -->
+ <el-tab-pane label="娴佽浆璁板綍" name="record">
+ <div class="form-scroll-area">
+ <el-scrollbar>
+ <ProcessInstanceTaskList :loading="processInstanceLoading" :id="id" />
+ </el-scrollbar>
+ </div>
+ </el-tab-pane>
+
+ <!-- 娴佽浆璇勮 TODO 寰呭紑鍙� -->
+ <el-tab-pane label="娴佽浆璇勮" name="comment" v-if="false">
+ <div class="form-scroll-area">
+ <el-scrollbar> 娴佽浆璇勮 </el-scrollbar>
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+
+ <div class="b-t-solid border-t-1px border-[var(--el-border-color)]">
+ <!-- 鎿嶄綔鏍忔寜閽� -->
+ <ProcessInstanceOperationButton
+ ref="operationButtonRef"
+ :process-instance="processInstance"
+ :process-definition="processDefinition"
+ :userOptions="userOptions"
+ :normal-form="detailForm"
+ :normal-form-api="fApi"
+ :writable-fields="writableFields"
+ @success="refresh"
+ />
+ </div>
+ </el-scrollbar>
+ </div>
+ </ContentWrap>
+
+ <!-- 鎵撳嵃棰勮寮圭獥 -->
+ <PrintDialog ref="printRef" />
+</template>
+<script lang="ts" setup>
+import { formatDate } from '@/utils/formatTime'
+import { DICT_TYPE } from '@/utils/dict'
+import { BpmModelType, BpmModelFormType } from '@/utils/constants'
+import { setConfAndFields2 } from '@/utils/formCreate'
+import { registerComponent } from '@/utils/routerHelper'
+import type { ApiAttrs } from '@form-create/element-ui/types/config'
+import * as ProcessInstanceApi from '@/api/bpm/processInstance'
+import * as UserApi from '@/api/system/user'
+import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
+import ProcessInstanceSimpleViewer from './ProcessInstanceSimpleViewer.vue'
+import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
+import ProcessInstanceOperationButton from './ProcessInstanceOperationButton.vue'
+import ProcessInstanceTimeline from './ProcessInstanceTimeline.vue'
+import { FieldPermissionType } from '@/components/SimpleProcessDesignerV2/src/consts'
+import { TaskStatusEnum } from '@/api/bpm/task'
+import runningSvg from '@/assets/svgs/bpm/running.svg'
+import approveSvg from '@/assets/svgs/bpm/approve.svg'
+import rejectSvg from '@/assets/svgs/bpm/reject.svg'
+import cancelSvg from '@/assets/svgs/bpm/cancel.svg'
+import PrintDialog from './PrintDialog.vue'
+
+defineOptions({ name: 'BpmProcessInstanceDetail' })
+const props = defineProps<{
+ id: string // 娴佺▼瀹炰緥鐨勭紪鍙�
+ taskId?: string // 浠诲姟缂栧彿
+ activityId?: string //娴佺▼娲诲姩缂栧彿锛岀敤浜庢妱閫佹煡鐪�
+}>()
+const message = useMessage() // 娑堟伅寮圭獥
+const processInstanceLoading = ref(false) // 娴佺▼瀹炰緥鐨勫姞杞戒腑
+const processInstance = ref<any>({}) // 娴佺▼瀹炰緥
+const processDefinition = ref<any>({}) // 娴佺▼瀹氫箟
+const processModelView = ref<any>({}) // 娴佺▼妯″瀷瑙嗗浘
+const operationButtonRef = ref() // 鎿嶄綔鎸夐挳缁勪欢 ref
+const auditIconsMap = {
+ [TaskStatusEnum.RUNNING]: runningSvg,
+ [TaskStatusEnum.APPROVE]: approveSvg,
+ [TaskStatusEnum.REJECT]: rejectSvg,
+ [TaskStatusEnum.CANCEL]: cancelSvg
+}
+
+// ========== 鐢宠淇℃伅 ==========
+const fApi = ref<ApiAttrs>() //
+const detailForm = ref({
+ rule: [],
+ option: {},
+ value: {}
+}) // 娴佺▼瀹炰緥鐨勮〃鍗曡鎯�
+
+const writableFields: Array<string> = [] // 琛ㄥ崟鍙互缂栬緫鐨勫瓧娈�
+
+/** 鑾峰緱璇︽儏 */
+const getDetail = () => {
+ // 鑾峰緱瀹℃壒璇︽儏
+ getApprovalDetail()
+ // 鑾峰緱娴佺▼妯″瀷瑙嗗浘
+ getProcessModelView()
+}
+
+/** 鍔犺浇娴佺▼瀹炰緥 */
+const BusinessFormComponent = ref<any>(null) // 寮傛缁勪欢
+/** 鑾峰彇瀹℃壒璇︽儏 */
+const activityNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) // 瀹℃壒鑺傜偣淇℃伅
+const getApprovalDetail = async () => {
+ processInstanceLoading.value = true
+ try {
+ const param = {
+ processInstanceId: props.id,
+ activityId: props.activityId,
+ taskId: props.taskId
+ }
+ const data = await ProcessInstanceApi.getApprovalDetail(param)
+ if (!data) {
+ message.error('鏌ヨ涓嶅埌瀹℃壒璇︽儏淇℃伅锛�')
+ return
+ }
+ if (!data.processDefinition || !data.processInstance) {
+ message.error('鏌ヨ涓嶅埌娴佺▼淇℃伅锛�')
+ return
+ }
+ processInstance.value = data.processInstance
+ processDefinition.value = data.processDefinition
+
+ // 璁剧疆琛ㄥ崟淇℃伅
+ if (processDefinition.value.formType === BpmModelFormType.NORMAL) {
+ // 鑾峰彇琛ㄥ崟瀛楁鏉冮檺
+ const formFieldsPermission = data.formFieldsPermission
+ // 娓呯┖鍙紪杈戝瓧娈典负绌�
+ writableFields.splice(0)
+ if (detailForm.value.rule?.length > 0) {
+ // 閬垮厤鍒锋柊 form-create 鏄剧ず涓嶄簡
+ detailForm.value.value = processInstance.value.formVariables
+ } else {
+ setConfAndFields2(
+ detailForm,
+ processDefinition.value.formConf,
+ processDefinition.value.formFields,
+ processInstance.value.formVariables
+ )
+ }
+ nextTick().then(() => {
+ fApi.value?.btn.show(false)
+ fApi.value?.resetBtn.show(false)
+ //@ts-ignore
+ fApi.value?.disabled(true)
+ // 璁剧疆琛ㄥ崟瀛楁鏉冮檺
+ if (formFieldsPermission) {
+ Object.keys(data.formFieldsPermission).forEach((item) => {
+ setFieldPermission(item, formFieldsPermission[item])
+ })
+ }
+ })
+ } else {
+ // 娉ㄦ剰锛歞ata.processDefinition.formCustomViewPath 鏄粍浠剁殑鍏ㄨ矾寰勶紝渚嬪璇达細/crm/contract/detail/index.vue
+ BusinessFormComponent.value = registerComponent(data.processDefinition.formCustomViewPath)
+ }
+
+ // 鑾峰彇瀹℃壒鑺傜偣锛屾樉绀� Timeline 鐨勬暟鎹�
+ activityNodes.value = data.activityNodes
+
+ // 鑾峰彇寰呭姙浠诲姟鏄剧ず鎿嶄綔鎸夐挳
+ operationButtonRef.value?.loadTodoTask(data.todoTask)
+ } finally {
+ processInstanceLoading.value = false
+ }
+}
+
+/** 鑾峰彇娴佺▼妯″瀷瑙嗗浘*/
+const getProcessModelView = async () => {
+ if (BpmModelType.BPMN === processDefinition.value?.modelType) {
+ // 閲嶇疆锛岃В鍐� BPMN 娴佺▼鍥惧埛鏂颁笉浼氶噸鏂版覆鏌撻棶棰�
+ processModelView.value = {
+ bpmnXml: ''
+ }
+ }
+ const data = await ProcessInstanceApi.getProcessInstanceBpmnModelView(props.id)
+ if (data) {
+ processModelView.value = data
+ }
+}
+
+/** 璁剧疆琛ㄥ崟鏉冮檺 */
+const setFieldPermission = (field: string, permission: string) => {
+ if (permission === FieldPermissionType.READ) {
+ //@ts-ignore
+ fApi.value?.disabled(true, field)
+ }
+ if (permission === FieldPermissionType.WRITE) {
+ //@ts-ignore
+ fApi.value?.disabled(false, field)
+ // 鍔犲叆鍙互缂栬緫鐨勫瓧娈�
+ writableFields.push(field)
+ }
+ if (permission === FieldPermissionType.NONE) {
+ //@ts-ignore
+ fApi.value?.hidden(true, field)
+ }
+}
+
+/** 鎿嶄綔鎴愬姛鍚庡埛鏂� */
+const refresh = () => {
+ // 閲嶆柊鑾峰彇璇︽儏
+ getDetail()
+}
+
+/** 澶勭悊鎵撳嵃 */
+const printRef = ref()
+const handlePrint = async () => {
+ printRef.value.open(props.id)
+}
+
+/** 褰撳墠鐨� Tab */
+const activeTab = ref('form')
+
+/** 鍒濆鍖� */
+const userOptions = ref<UserApi.UserVO[]>([]) // 鐢ㄦ埛鍒楄〃
+onMounted(async () => {
+ getDetail()
+ // 鑾峰緱鐢ㄦ埛鍒楄〃
+ userOptions.value = await UserApi.getSimpleUserList()
+})
+</script>
+
+<style lang="scss" scoped>
+$wrap-padding-height: 20px;
+$wrap-margin-height: 15px;
+$button-height: 51px;
+$process-header-height: 194px;
+
+.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 {
+ display: flex;
+ 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;
+ flex-direction: column;
+
+ :deep(.box-card) {
+ height: 100%;
+ flex: 1;
+
+ .el-card__body {
+ height: 100%;
+ padding: 0;
+ }
+ }
+ }
+}
+
+.form-box {
+ :deep(.el-card) {
+ border: none;
+ }
+}
+</style>
--
Gitblit v1.8.0