375 lines
10 KiB
Vue
375 lines
10 KiB
Vue
<script setup lang="ts">
|
|
import type { User } from '#/api/core/user';
|
|
import type { FlowInfoResponse } from '#/api/workflow/instance/model';
|
|
import type { TaskInfo } from '#/api/workflow/task/model';
|
|
|
|
import { computed, onUnmounted, ref, watch } from 'vue';
|
|
|
|
import { Fallback, useVbenModal, VbenAvatar } from '@vben/common-ui';
|
|
import { DictEnum } from '@vben/constants';
|
|
import { getPopupContainer } from '@vben/utils';
|
|
|
|
import { useEventListener } from '@vueuse/core';
|
|
import {
|
|
Card,
|
|
Divider,
|
|
Dropdown,
|
|
Menu,
|
|
MenuItem,
|
|
Modal,
|
|
Popconfirm,
|
|
Skeleton,
|
|
Space,
|
|
TabPane,
|
|
Tabs,
|
|
} from 'ant-design-vue';
|
|
|
|
import { flowInfo } from '#/api/workflow/instance';
|
|
import {
|
|
getTaskByTaskId,
|
|
taskOperation,
|
|
terminationTask,
|
|
} from '#/api/workflow/task';
|
|
import { renderDict } from '#/utils/render';
|
|
|
|
import { approvalModal, approvalRejectionModal, ApprovalTimeline } from '.';
|
|
import userSelectModal from './user-select-modal.vue';
|
|
|
|
defineOptions({
|
|
name: 'ApprovalPanel',
|
|
inheritAttrs: false,
|
|
});
|
|
|
|
// eslint-disable-next-line no-use-before-define
|
|
const props = defineProps<{ task?: TaskInfo; type: ApprovalType }>();
|
|
|
|
const currentTask = ref<TaskInfo>();
|
|
/**
|
|
* 是否显示 加签/减签操作
|
|
*/
|
|
const showMultiActions = computed(() => {
|
|
if (!currentTask.value) {
|
|
return false;
|
|
}
|
|
if (Number(currentTask.value.nodeRatio) > 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
/**
|
|
* myself 我发起的
|
|
* readonly 只读 只用于查看
|
|
* approve 审批
|
|
*/
|
|
type ApprovalType = 'approve' | 'myself' | 'readonly';
|
|
const showFooter = computed(() => {
|
|
if (props.type === 'readonly') {
|
|
return false;
|
|
}
|
|
// 我发起的 && [已完成, 已作废] 不显示
|
|
if (
|
|
props.type === 'myself' &&
|
|
['finish', 'invalid'].includes(props.task?.flowStatus ?? '')
|
|
) {
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
const currentFlowInfo = ref<FlowInfoResponse>();
|
|
/**
|
|
* card的loading状态
|
|
*/
|
|
const loading = ref(false);
|
|
const iframeLoaded = ref(false);
|
|
useEventListener('message', (event) => {
|
|
/**
|
|
* iframe通信 加载完毕后才显示表单 解决卡顿问题
|
|
*/
|
|
if (event.data === 'mounted') {
|
|
iframeLoaded.value = true;
|
|
}
|
|
});
|
|
|
|
async function handleLoadInfo(task: TaskInfo | undefined) {
|
|
try {
|
|
if (!task) return null;
|
|
loading.value = true;
|
|
iframeLoaded.value = false;
|
|
const resp = await flowInfo(task.businessId);
|
|
currentFlowInfo.value = resp;
|
|
|
|
const taskResp = await getTaskByTaskId(props.task!.id);
|
|
currentTask.value = taskResp;
|
|
} catch (error) {
|
|
console.error(error);
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
watch(() => props.task, handleLoadInfo);
|
|
|
|
onUnmounted(() => (currentFlowInfo.value = undefined));
|
|
|
|
async function handleCancel() {
|
|
// await cancelProcessApply()
|
|
}
|
|
|
|
/**
|
|
* 审批驳回
|
|
*/
|
|
const [RejectionModal, rejectionModalApi] = useVbenModal({
|
|
connectedComponent: approvalRejectionModal,
|
|
});
|
|
function handleRejection() {
|
|
rejectionModalApi.setData({
|
|
taskId: props.task?.id,
|
|
instanceId: props.task?.instanceId,
|
|
});
|
|
rejectionModalApi.open();
|
|
}
|
|
|
|
/**
|
|
* 审批终止
|
|
*/
|
|
function handleTermination() {
|
|
Modal.confirm({
|
|
title: '审批终止',
|
|
content: '确定终止当前审批流程吗?',
|
|
centered: true,
|
|
okButtonProps: { danger: true },
|
|
onOk: async () => {
|
|
await terminationTask({ taskId: props.task!.id });
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 审批通过
|
|
*/
|
|
const [ApprovalModal, approvalModalApi] = useVbenModal({
|
|
connectedComponent: approvalModal,
|
|
});
|
|
function handleApproval() {
|
|
approvalModalApi.setData({ taskId: props.task?.id });
|
|
approvalModalApi.open();
|
|
}
|
|
|
|
/**
|
|
* TODO: 1提取公共函数 2原版是可以填写意见的(message参数)
|
|
*/
|
|
|
|
/**
|
|
* 委托
|
|
*/
|
|
const [DelegationModal, delegationModalApi] = useVbenModal({
|
|
connectedComponent: userSelectModal,
|
|
});
|
|
function handleDelegation(userList: User[]) {
|
|
if (userList.length === 0) return;
|
|
const current = userList[0];
|
|
Modal.confirm({
|
|
title: '委托',
|
|
content: `确定委托给${current?.nickName}吗?`,
|
|
centered: true,
|
|
onOk: async () => {
|
|
await taskOperation(
|
|
{ taskId: props.task!.id, userId: current!.userId },
|
|
'delegateTask',
|
|
);
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 转办
|
|
*/
|
|
const [TransferModal, transferModalApi] = useVbenModal({
|
|
connectedComponent: userSelectModal,
|
|
});
|
|
function handleTransfer(userList: User[]) {
|
|
if (userList.length === 0) return;
|
|
const current = userList[0];
|
|
Modal.confirm({
|
|
title: '转办',
|
|
content: `确定转办给${current?.nickName}吗?`,
|
|
centered: true,
|
|
onOk: async () => {
|
|
await taskOperation(
|
|
{ taskId: props.task!.id, userId: current!.userId },
|
|
'transferTask',
|
|
);
|
|
},
|
|
});
|
|
}
|
|
|
|
const [AddSignatureModal, addSignatureModalApi] = useVbenModal({
|
|
connectedComponent: userSelectModal,
|
|
});
|
|
function handleAddSignature(userList: User[]) {
|
|
if (userList.length === 0) return;
|
|
const userIds = userList.map((user) => user.userId);
|
|
Modal.confirm({
|
|
title: '提示',
|
|
content: '确认加签吗?',
|
|
centered: true,
|
|
onOk: async () => {
|
|
await taskOperation({ taskId: props.task!.id, userIds }, 'addSignature');
|
|
},
|
|
});
|
|
}
|
|
|
|
const [ReductionSignatureModal, reductionSignatureModalApi] = useVbenModal({
|
|
connectedComponent: userSelectModal,
|
|
});
|
|
function handleReductionSignature(userList: User[]) {
|
|
if (userList.length === 0) return;
|
|
const userIds = userList.map((user) => user.userId);
|
|
Modal.confirm({
|
|
title: '提示',
|
|
content: '确认加签吗?',
|
|
centered: true,
|
|
onOk: async () => {
|
|
await taskOperation(
|
|
{ taskId: props.task!.id, userIds },
|
|
'reductionSignature',
|
|
);
|
|
},
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Card
|
|
v-if="task"
|
|
:body-style="{ overflowY: 'auto', height: '100%' }"
|
|
:loading="loading"
|
|
:title="`编号: ${task.id}`"
|
|
class="thin-scrollbar flex-1 overflow-y-hidden"
|
|
size="small"
|
|
>
|
|
<template #extra>
|
|
<a-button size="small" @click="() => handleLoadInfo(task)">
|
|
<div class="flex items-center justify-center">
|
|
<span class="icon-[material-symbols--refresh] size-24px"></span>
|
|
</div>
|
|
</a-button>
|
|
</template>
|
|
<div class="flex flex-col gap-5 p-4">
|
|
<div class="flex flex-col gap-3">
|
|
<div class="flex items-center gap-2">
|
|
<div class="text-2xl font-bold">{{ task.flowName }}</div>
|
|
<div>
|
|
<component
|
|
:is="renderDict(task.flowStatus, DictEnum.WF_BUSINESS_STATUS)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<VbenAvatar
|
|
:alt="task.createByName"
|
|
class="bg-primary size-[24px] rounded-full text-white"
|
|
src=""
|
|
/>
|
|
<span>{{ task.createByName }}</span>
|
|
<div class="flex items-center opacity-50">
|
|
<span>XXXX有限公司</span>
|
|
<Divider type="vertical" />
|
|
<span>提交于: {{ task.createTime }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Tabs v-if="currentFlowInfo" class="flex-1">
|
|
<TabPane key="1" tab="审批详情">
|
|
<div class="h-fulloverflow-y-auto">
|
|
<!-- 约定${task.formPath}/frame 为内嵌表单 用于展示 需要在本地路由添加 -->
|
|
<iframe
|
|
v-show="iframeLoaded"
|
|
:src="`${task.formPath}/iframe?readonly=true&id=${task.businessId}`"
|
|
class="h-[300px] w-full"
|
|
></iframe>
|
|
<Skeleton v-show="!iframeLoaded" :paragraph="{ rows: 6 }" active />
|
|
<Divider />
|
|
<ApprovalTimeline :list="currentFlowInfo.list" />
|
|
</div>
|
|
</TabPane>
|
|
<TabPane key="2" tab="审批流程图">
|
|
<img
|
|
:src="`data:image/png;base64,${currentFlowInfo.image}`"
|
|
class="rounded-lg border"
|
|
/>
|
|
</TabPane>
|
|
</Tabs>
|
|
</div>
|
|
<!-- 固定底部 -->
|
|
<div class="h-[57px]"></div>
|
|
<div
|
|
v-if="showFooter"
|
|
class="border-t-solid bg-background absolute bottom-0 left-0 w-full border-t-[1px] p-3"
|
|
>
|
|
<div class="flex justify-end">
|
|
<Space v-if="type === 'myself'">
|
|
<Popconfirm
|
|
placement="topRight"
|
|
title="确定要撤销该申请吗?"
|
|
@confirm="handleCancel"
|
|
>
|
|
<a-button danger type="primary">撤销申请</a-button>
|
|
</Popconfirm>
|
|
</Space>
|
|
<Space v-if="type === 'approve'">
|
|
<a-button type="primary" @click="handleApproval">通过</a-button>
|
|
<a-button danger type="primary" @click="handleTermination">
|
|
终止
|
|
</a-button>
|
|
<a-button danger type="primary" @click="handleRejection">
|
|
驳回
|
|
</a-button>
|
|
<Dropdown
|
|
:get-popup-container="getPopupContainer"
|
|
placement="bottomRight"
|
|
>
|
|
<template #overlay>
|
|
<Menu>
|
|
<MenuItem key="1" @click="() => delegationModalApi.open()">
|
|
委托
|
|
</MenuItem>
|
|
<MenuItem key="2" @click="() => transferModalApi.open()">
|
|
转办
|
|
</MenuItem>
|
|
<MenuItem
|
|
v-if="showMultiActions"
|
|
key="3"
|
|
@click="() => addSignatureModalApi.open()"
|
|
>
|
|
加签
|
|
</MenuItem>
|
|
<MenuItem
|
|
v-if="showMultiActions"
|
|
key="4"
|
|
@click="() => reductionSignatureModalApi.open()"
|
|
>
|
|
减签
|
|
</MenuItem>
|
|
</Menu>
|
|
</template>
|
|
<a-button> 其他 </a-button>
|
|
</Dropdown>
|
|
<ApprovalModal />
|
|
<RejectionModal />
|
|
<DelegationModal mode="single" @finish="handleDelegation" />
|
|
<TransferModal mode="single" @finish="handleTransfer" />
|
|
<AddSignatureModal mode="multiple" @finish="handleAddSignature" />
|
|
<ReductionSignatureModal
|
|
mode="multiple"
|
|
@finish="handleReductionSignature"
|
|
/>
|
|
</Space>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
<Fallback v-else title="点击左侧选择" />
|
|
</template>
|