<script lang="ts" setup>
|
import type { BpmOALeaveApi } from '#/api/bpm/oa/leave';
|
import type { BpmProcessInstanceApi } from '#/api/bpm/processInstance';
|
|
import { computed, onMounted, ref, watch } from 'vue';
|
import { useRoute } from 'vue-router';
|
|
import { confirm, Page, useVbenForm } from '..\..\..\..\packages\effects\common-ui\src';
|
import { BpmCandidateStrategyEnum, BpmNodeIdEnum } from '..\..\..\..\packages\constants\src';
|
import { useTabs } from '..\..\..\..\packages\effects\hooks\src';
|
import { IconifyIcon } from '..\..\..\..\packages\icons\src';
|
|
import { Button, Card, Col, message, Row, Space } from 'ant-design-vue';
|
import dayjs from 'dayjs';
|
|
import { getProcessDefinition } from '#/api/bpm/definition';
|
import { createLeave, getLeave } from '#/api/bpm/oa/leave';
|
import { getApprovalDetail as getApprovalDetailApi } from '#/api/bpm/processInstance';
|
import { $t } from '#/locales';
|
import { router } from '#/router';
|
import ProcessInstanceTimeline from '#/views/bpm/processInstance/detail/modules/time-line.vue';
|
|
import { useFormSchema } from './data';
|
|
const { closeCurrentTab } = useTabs();
|
const { query } = useRoute();
|
|
const formLoading = ref(false); // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const processTimeLineLoading = ref(false); // 审批流的加载中
|
|
type LeaveCreateData = BpmOALeaveApi.Leave & {
|
startUserSelectAssignees?: Record<string, number[]>;
|
};
|
|
const processDefineKey = 'oa_leave'; // 流程定义 Key
|
const startUserSelectTasks = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]); // 发起人需要选择审批人的用户任务列表
|
const startUserSelectAssignees = ref<Record<string, number[]>>({}); // 发起人选择审批人的数据
|
const tempStartUserSelectAssignees = ref<Record<string, number[]>>({}); // 历史发起人选择审批人的数据,用于每次表单变更时,临时保存
|
const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]); // 审批节点信息
|
const processDefinitionId = ref('');
|
|
const formData = ref<BpmOALeaveApi.Leave>();
|
const getTitle = computed(() => {
|
return formData.value?.id
|
? '重新发起请假'
|
: $t('ui.actionTitle.create', ['请假']);
|
});
|
|
const [Form, formApi] = useVbenForm({
|
commonConfig: {
|
componentProps: {
|
class: 'w-full',
|
},
|
formItemClass: 'col-span-2',
|
labelWidth: 100,
|
},
|
layout: 'horizontal',
|
schema: useFormSchema(),
|
showDefaultActions: false,
|
});
|
|
/** 提交申请 */
|
async function onSubmit() {
|
// 1.1 表单校验
|
const { valid } = await formApi.validate();
|
if (!valid) {
|
return;
|
}
|
// 1.2 审批相关:校验指定审批人
|
if (startUserSelectTasks.value?.length > 0) {
|
for (const userTask of startUserSelectTasks.value) {
|
const assignees = startUserSelectAssignees.value[userTask.id];
|
if (Array.isArray(assignees) && assignees.length === 0) {
|
return message.warning(`请选择${userTask.name}的审批人`);
|
}
|
}
|
}
|
|
// 提交表单
|
const data = (await formApi.getValues()) as LeaveCreateData;
|
// 审批相关:设置指定审批人
|
if (startUserSelectTasks.value?.length > 0) {
|
data.startUserSelectAssignees = startUserSelectAssignees.value;
|
}
|
// 格式化开始时间和结束时间的值
|
const submitData: LeaveCreateData = {
|
...data,
|
startTime: Number(data.startTime),
|
endTime: Number(data.endTime),
|
};
|
try {
|
formLoading.value = true;
|
await createLeave(submitData);
|
// 关闭并提示
|
message.success($t('ui.actionMessage.operationSuccess'));
|
await closeCurrentTab();
|
await router.push({
|
name: 'BpmOALeave',
|
});
|
} finally {
|
formLoading.value = false;
|
}
|
}
|
|
/** 返回上一页 */
|
function onBack() {
|
confirm({
|
content: '确定要返回上一页吗?请先保存您填写的信息!',
|
icon: 'warning',
|
beforeClose({ isConfirm }) {
|
if (isConfirm) {
|
closeCurrentTab();
|
}
|
return Promise.resolve(true);
|
},
|
});
|
}
|
|
/** 审批相关:获取审批详情 */
|
async function getApprovalDetail() {
|
processTimeLineLoading.value = true;
|
try {
|
const data = await getApprovalDetailApi({
|
processDefinitionId: processDefinitionId.value,
|
// TODO 小北:可以支持 processDefinitionKey 查询
|
activityId: BpmNodeIdEnum.START_USER_NODE_ID,
|
processVariablesStr: JSON.stringify({
|
day: dayjs(formData.value?.startTime).diff(
|
dayjs(formData.value?.endTime),
|
'day',
|
),
|
}), // 解决 GET 无法传递对象的问题,后端 String 再转 JSON
|
});
|
if (!data) {
|
message.error('查询不到审批详情信息!');
|
return;
|
}
|
// 获取审批节点,显示 Timeline 的数据
|
activityNodes.value = data.activityNodes;
|
|
// 获取发起人自选的任务
|
startUserSelectTasks.value = data.activityNodes?.filter(
|
(node: BpmProcessInstanceApi.ApprovalNodeInfo) =>
|
BpmCandidateStrategyEnum.START_USER_SELECT === node.candidateStrategy,
|
);
|
// 恢复之前的选择审批人
|
if (startUserSelectTasks.value?.length > 0) {
|
for (const node of startUserSelectTasks.value) {
|
const tempAssignees = tempStartUserSelectAssignees.value[node.id];
|
startUserSelectAssignees.value[node.id] = tempAssignees?.length
|
? tempAssignees
|
: [];
|
}
|
}
|
} finally {
|
processTimeLineLoading.value = false;
|
}
|
}
|
|
/** 审批相关:选择发起人 */
|
function selectUserConfirm(id: string, userList: Array<{ id: number }>) {
|
startUserSelectAssignees.value[id] = userList.map((item) => item.id);
|
}
|
|
/** 获取请假数据,用于重新发起时自动填充 */
|
async function getDetail(id: number) {
|
try {
|
formLoading.value = true;
|
const data = await getLeave(id);
|
if (!data) {
|
message.error('重新发起请假失败,原因:请假数据不存在');
|
return;
|
}
|
formData.value = {
|
...formData.value,
|
id: data.id,
|
type: data.type,
|
reason: data.reason,
|
startTime: data.startTime,
|
endTime: data.endTime,
|
} as BpmOALeaveApi.Leave;
|
await formApi.setValues({
|
type: data.type,
|
reason: data.reason,
|
startTime: data.startTime,
|
endTime: data.endTime,
|
});
|
} finally {
|
formLoading.value = false;
|
}
|
}
|
|
/** 审批相关:预测流程节点会因为输入的参数值而产生新的预测结果值,所以需重新预测一次, formData.value可改成实际业务中的特定字段 */
|
watch(
|
formData.value as object,
|
(newValue, oldValue) => {
|
if (!oldValue) {
|
return;
|
}
|
if (newValue && Object.keys(newValue).length > 0) {
|
// 记录之前的节点审批人
|
tempStartUserSelectAssignees.value = startUserSelectAssignees.value;
|
startUserSelectAssignees.value = {};
|
// 加载最新的审批详情,主要用于节点预测
|
getApprovalDetail();
|
}
|
},
|
{
|
immediate: true,
|
},
|
);
|
|
/** 初始化 */
|
onMounted(async () => {
|
const processDefinitionDetail: any = await getProcessDefinition(
|
undefined,
|
processDefineKey,
|
);
|
if (!processDefinitionDetail) {
|
message.error('OA 请假的流程模型未配置,请检查!');
|
return;
|
}
|
processDefinitionId.value = processDefinitionDetail.id;
|
startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks;
|
|
// 如果是重新发起,则加载请假数据
|
if (query.id) {
|
await getDetail(Number(query.id));
|
}
|
|
await getApprovalDetail();
|
});
|
</script>
|
|
<template>
|
<Page>
|
<Row :gutter="16">
|
<Col :span="16">
|
<Card :title="getTitle" class="w-full" v-loading="formLoading">
|
<template #extra>
|
<Button type="default" @click="onBack">
|
<IconifyIcon icon="lucide:arrow-left" />
|
返回
|
</Button>
|
</template>
|
|
<Form />
|
<template #actions>
|
<Space warp :size="12" class="w-full px-6">
|
<Button type="primary" @click="onSubmit" :loading="formLoading">
|
提交
|
</Button>
|
</Space>
|
</template>
|
</Card>
|
</Col>
|
<Col :span="8">
|
<Card title="流程" class="w-full" v-loading="processTimeLineLoading">
|
<ProcessInstanceTimeline
|
:activity-nodes="activityNodes"
|
:show-status-icon="false"
|
@select-user-confirm="selectUserConfirm"
|
/>
|
</Card>
|
</Col>
|
</Row>
|
</Page>
|
</template>
|