From 6951bd68a12d8473ca419ff1ef41f4f227e59358 Mon Sep 17 00:00:00 2001 From: XuZhiqiang Date: Wed, 17 Jun 2026 13:24:57 +0800 Subject: [PATCH] =?UTF-8?q?feat(@vben/web-antdv-next):=20BPM=E6=8B=92?= =?UTF-8?q?=E7=BB=9D=E5=8F=AF=E4=BB=A5=E6=B7=BB=E5=8A=A0=E9=99=84=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=8A=9F=E8=83=BD=E8=87=B3=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E8=A1=A8=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/api/bpm/processInstance/index.ts | 1 + .../detail/modules/operation-button.vue | 80 ++++++++++++++++++- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/apps/web-antdv-next/src/api/bpm/processInstance/index.ts b/apps/web-antdv-next/src/api/bpm/processInstance/index.ts index 7ee5421f8..f8442c35c 100644 --- a/apps/web-antdv-next/src/api/bpm/processInstance/index.ts +++ b/apps/web-antdv-next/src/api/bpm/processInstance/index.ts @@ -83,6 +83,7 @@ export namespace BpmProcessInstanceApi { reason: string; signPicUrl: string; status: number; + attachments?: string[]; } /** 抄送流程实例 */ diff --git a/apps/web-antdv-next/src/views/bpm/processInstance/detail/modules/operation-button.vue b/apps/web-antdv-next/src/views/bpm/processInstance/detail/modules/operation-button.vue index 423cd8daf..8afd72618 100644 --- a/apps/web-antdv-next/src/views/bpm/processInstance/detail/modules/operation-button.vue +++ b/apps/web-antdv-next/src/views/bpm/processInstance/detail/modules/operation-button.vue @@ -55,6 +55,7 @@ import { transferTask, } from '#/api/bpm/task'; import { setConfAndFields2 } from '#/components/form-create'; +import { FileUpload } from '#/components/upload'; import { $t } from '#/locales'; import Signature from './signature.vue'; @@ -120,6 +121,7 @@ const approveReasonForm: any = reactive({ reason: '', signPicUrl: '', nextAssignees: {}, + attachments: [], }); const approveReasonRule: Record = computed(() => { return { @@ -140,7 +142,8 @@ const approveReasonRule: Record = computed(() => { }); const rejectFormRef = ref(); -const rejectReasonForm = reactive({ +const rejectReasonForm = reactive<{ attachments: string[]; reason: string }>({ + attachments: [], reason: '', }); // 拒绝表单 const rejectReasonRule: any = computed(() => { @@ -290,6 +293,14 @@ function closePopover(type: string, formRef: any | FormInstance) { if (formRef) { formRef.resetFields(); } + if (type === 'approve') { + approveReasonForm.reason = ''; + approveReasonForm.attachments = []; + approveReasonForm.signPicUrl = ''; + } else if (type === 'reject') { + rejectReasonForm.reason = ''; + rejectReasonForm.attachments = []; + } if (popOverVisible.value[type]) popOverVisible.value[type] = false; nextAssigneesActivityNode.value = []; // 清理 Timeline 组件中的自定义审批人数据 @@ -401,6 +412,7 @@ async function handleAudit(pass: boolean, formRef: FormInstance | undefined) { const data = { id: runningTask.value.id, reason: approveReasonForm.reason, + attachments: approveReasonForm.attachments, variables, // 审批通过, 把修改的字段值赋于流程实例变量 nextAssignees: approveReasonForm.nextAssignees, // 下个自选节点选择的审批人信息 } as any; @@ -414,6 +426,9 @@ async function handleAudit(pass: boolean, formRef: FormInstance | undefined) { await formCreateApi.validate(); } await approveTask(data); + approveReasonForm.reason = ''; + approveReasonForm.attachments = []; + approveReasonForm.signPicUrl = ''; popOverVisible.value.approve = false; nextAssigneesActivityNode.value = []; // 清理 Timeline 组件中的自定义审批人数据 @@ -425,9 +440,12 @@ async function handleAudit(pass: boolean, formRef: FormInstance | undefined) { // 审批不通过数据 const data = { id: runningTask.value.id, + attachments: rejectReasonForm.attachments, reason: rejectReasonForm.reason, }; await rejectTask(data); + rejectReasonForm.reason = ''; + rejectReasonForm.attachments = []; popOverVisible.value.reject = false; message.success('审批不通过成功'); } @@ -748,6 +766,38 @@ function handleSignFinish(url: string) { approveFormRef.value?.validateFields(['signPicUrl']); } +/** 附件图片预览 */ +const imagePreviewOpen = ref(false); +const imagePreviewUrl = ref(''); + +/** 判断文件是否为图片类型 */ +function isImageUrl(url: string) { + return /\.(bmp|gif|jpe?g|png|svg|webp)$/i.test(url); +} + +/** 处理文件预览 */ +function handleFilePreview(file: any) { + if (!file?.url && !file?.response) { + message.warning('文件地址不存在,无法预览'); + return; + } + const url = file.url || file?.response?.url || file?.response; + if (!url) { + message.warning('文件地址不存在,无法预览'); + return; + } + if (isImageUrl(url)) { + imagePreviewUrl.value = url; + imagePreviewOpen.value = true; + } else { + window.open(url, '_blank'); + } +} + +function handleImagePreviewOpenChange(open: boolean) { + imagePreviewOpen.value = open; +} + /** 处理弹窗可见性 */ function handlePopoverVisible(visible: boolean) { if (!visible) { @@ -843,6 +893,15 @@ defineExpose({ loadTodoTask }); :rows="4" /> + + +