feat: [BPM 工作流] Simple 模型 - 流程设计校验

pull/125/head
jason 2025-06-04 09:48:48 +08:00
parent 9ceb1cc63c
commit db10830bbb
5 changed files with 69 additions and 71 deletions

View File

@ -11,9 +11,10 @@ import type { SystemUserApi } from '#/api/system/user';
import { inject, onMounted, provide, ref, watch } from 'vue'; import { inject, onMounted, provide, ref, watch } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { handleTree } from '@vben/utils'; import { handleTree } from '@vben/utils';
import { Button, Modal } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
import { getFormDetail } from '#/api/bpm/form'; import { getFormDetail } from '#/api/bpm/form';
import { getUserGroupSimpleList } from '#/api/bpm/userGroup'; import { getUserGroupSimpleList } from '#/api/bpm/userGroup';
@ -112,8 +113,11 @@ provide('tasks', []);
provide('processInstance', {}); provide('processInstance', {});
const processNodeTree = ref<SimpleFlowNode | undefined>(); const processNodeTree = ref<SimpleFlowNode | undefined>();
provide('processNodeTree', processNodeTree); provide('processNodeTree', processNodeTree);
const errorDialogVisible = ref(false);
const errorNodes: SimpleFlowNode[] = []; //
const [ErrorModal, errorModalApi] = useVbenModal({
fullscreenButton: false,
});
// //
const updateModel = () => { const updateModel = () => {
@ -122,6 +126,7 @@ const updateModel = () => {
name: '发起人', name: '发起人',
type: NodeType.START_USER_NODE, type: NodeType.START_USER_NODE,
id: NodeId.START_USER_NODE_ID, id: NodeId.START_USER_NODE_ID,
showText: '默认配置',
childNode: { childNode: {
id: NodeId.END_EVENT_NODE_ID, id: NodeId.END_EVENT_NODE_ID,
name: '结束', name: '结束',
@ -151,7 +156,7 @@ const saveSimpleFlowModel = async (
/** /**
* 校验节点设置 暂时以 showText 为空 未节点错误配置 * 校验节点设置 暂时以 showText 为空 未节点错误配置
*/ */
// eslint-disable-next-line unused-imports/no-unused-vars, no-unused-vars
const validateNode = ( const validateNode = (
node: SimpleFlowNode | undefined, node: SimpleFlowNode | undefined,
errorNodes: SimpleFlowNode[], errorNodes: SimpleFlowNode[],
@ -161,34 +166,23 @@ const validateNode = (
if (type === NodeType.END_EVENT_NODE) { if (type === NodeType.END_EVENT_NODE) {
return; return;
} }
if (type === NodeType.START_USER_NODE) {
//
validateNode(node.childNode, errorNodes);
}
if (
type === NodeType.USER_TASK_NODE ||
type === NodeType.COPY_TASK_NODE ||
type === NodeType.CONDITION_NODE
) {
if (!showText) {
errorNodes.push(node);
}
validateNode(node.childNode, errorNodes);
}
if ( if (
type === NodeType.CONDITION_BRANCH_NODE || type === NodeType.CONDITION_BRANCH_NODE ||
type === NodeType.PARALLEL_BRANCH_NODE || type === NodeType.PARALLEL_BRANCH_NODE ||
type === NodeType.INCLUSIVE_BRANCH_NODE type === NodeType.INCLUSIVE_BRANCH_NODE
) { ) {
// // 1. ,
// 1.
conditionNodes?.forEach((item) => { conditionNodes?.forEach((item) => {
validateNode(item, errorNodes); validateNode(item, errorNodes);
}); });
// 2. // 2.
validateNode(node.childNode, errorNodes); validateNode(node.childNode, errorNodes);
} else {
if (!showText) {
errorNodes.push(node);
}
validateNode(node.childNode, errorNodes);
} }
} }
}; };
@ -220,38 +214,40 @@ onMounted(async () => {
} }
}); });
const simpleProcessModelRef = ref(); const validate = async () => {
const errorNodes: SimpleFlowNode[] = [];
defineExpose({}); validateNode(processNodeTree.value, errorNodes);
if (errorNodes.length === 0) {
return true;
} else {
//
errorModalApi.setData(errorNodes);
errorModalApi.open();
return false;
}
};
defineExpose({ validate });
</script> </script>
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<SimpleProcessModel <SimpleProcessModel
ref="simpleProcessModelRef"
v-if="processNodeTree" v-if="processNodeTree"
:flow-node="processNodeTree" :flow-node="processNodeTree"
:readonly="false" :readonly="false"
@save="saveSimpleFlowModel" @save="saveSimpleFlowModel"
/> />
<Modal <ErrorModal title="流程设计校验不通过" class="w-[600px]">
v-model="errorDialogVisible" <div class="mb-2 text-base">以下节点配置不完善请修改相关配置</div>
title="保存失败"
width="400"
:fullscreen="false"
>
<div class="mb-2">以下节点内容不完善请修改后保存</div>
<div <div
class="b-rounded-1 line-height-normal mb-3 bg-gray-100 p-2" class="mb-3 rounded-md bg-gray-100 p-2 text-sm"
v-for="(item, index) in errorNodes" v-for="(item, index) in errorModalApi.getData()"
:key="index" :key="index"
> >
{{ item.name }} : {{ NODE_DEFAULT_TEXT.get(item.type) }} {{ item.name }} : {{ NODE_DEFAULT_TEXT.get(item.type) }}
</div> </div>
<template #footer> <template #footer>
<Button type="primary" @click="errorDialogVisible = false"> <Button type="primary" @click="errorModalApi.close()"></Button>
知道了
</Button>
</template> </template>
</Modal> </ErrorModal>
</div> </div>
</template> </template>

View File

@ -648,7 +648,8 @@
width: 80px; width: 80px;
height: 36px; height: 36px;
color: #212121; color: #212121;
border: 2px solid #fafafa; background-color: #fff;
border: 2px solid transparent;
border-radius: 30px; border-radius: 30px;
box-shadow: 0 1px 5px 0 rgb(10 30 65 / 8%); box-shadow: 0 1px 5px 0 rgb(10 30 65 / 8%);

View File

@ -216,7 +216,8 @@ const validateAllSteps = async () => {
await validateBasic(); await validateBasic();
} catch { } catch {
currentStep.value = 0; currentStep.value = 0;
throw new Error('请完善基本信息'); message.warning('请完善基本信息');
return false;
} }
// //
@ -224,25 +225,19 @@ const validateAllSteps = async () => {
await validateForm(); await validateForm();
} catch { } catch {
currentStep.value = 1; currentStep.value = 1;
throw new Error('请完善自定义表单信息'); message.warning('请完善自定义表单信息');
return false;
} }
// TODO //
try { try {
await validateProcess(); await validateProcess();
} catch { } catch {
currentStep.value = 2; currentStep.value = 2;
throw new Error('请设计流程'); return false;
} }
// // TODO
try {
await validateProcess();
} catch {
currentStep.value = 2;
throw new Error('请设计流程');
}
return true; return true;
}; };
@ -251,7 +246,10 @@ const validateAllSteps = async () => {
const handleSave = async () => { const handleSave = async () => {
try { try {
// //
await validateAllSteps(); const result = await validateAllSteps();
if (!result) {
return;
}
// //
const modelData = { const modelData = {
@ -297,7 +295,7 @@ const handleSave = async () => {
} }
} catch (error: any) { } catch (error: any) {
console.error('保存失败:', error); console.error('保存失败:', error);
message.warning(error.message || '请完善所有步骤的必填信息'); // message.warning(error.msg || '');
} }
}; };
@ -347,23 +345,13 @@ const handleStepClick = async (index: number) => {
if (index !== 2) { if (index !== 2) {
await validateProcess(); await validateProcess();
} }
// //
currentStep.value = index; currentStep.value = index;
//
if (index === 2) {
// TODO
// await nextTick();
// //
// await new Promise((resolve) => setTimeout(resolve, 200));
// if (processDesignRef.value?.refresh) {
// await processDesignRef.value.refresh();
// }
}
} catch (error) { } catch (error) {
console.error('步骤切换失败:', error); console.error('步骤切换失败:', error);
message.warning('请先完善当前步骤必填信息'); if (currentStep.value !== 2) {
message.warning('请先完善当前步骤必填信息');
}
} }
}; };

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { computed, inject, nextTick } from 'vue'; import { computed, inject, nextTick, ref } from 'vue';
import { BpmModelType } from '#/utils'; import { BpmModelType } from '#/utils';
@ -13,12 +13,21 @@ const modelData = defineModel<any>();
const processData = inject('processData') as Ref; const processData = inject('processData') as Ref;
const simpleDesign = ref();
/** 表单校验 */ /** 表单校验 */
const validate = async () => { const validate = async () => {
// //
if (!processData.value) { if (!processData.value) {
throw new Error('请设计流程'); throw new Error('请设计流程');
} }
if (modelData.value.type === BpmModelType.SIMPLE) {
//
const validateResult = await simpleDesign.value?.validateConfig();
if (!validateResult) {
throw new Error('请完善设计配置');
}
}
return true; return true;
}; };
/** 处理设计器保存成功 */ /** 处理设计器保存成功 */
@ -41,9 +50,7 @@ const handleDesignSuccess = async (data?: any) => {
const showDesigner = computed(() => { const showDesigner = computed(() => {
return Boolean(modelData.value?.key && modelData.value?.name); return Boolean(modelData.value?.key && modelData.value?.name);
}); });
defineExpose({ defineExpose({ validate });
validate,
});
</script> </script>
<template> <template>
<div class="h-full"> <div class="h-full">
@ -61,6 +68,7 @@ defineExpose({
:start-user-ids="modelData.startUserIds" :start-user-ids="modelData.startUserIds"
:start-dept-ids="modelData.startDeptIds" :start-dept-ids="modelData.startDeptIds"
@success="handleDesignSuccess" @success="handleDesignSuccess"
ref="simpleDesign"
/> />
</template> </template>
</div> </div>

View File

@ -17,12 +17,17 @@ defineProps<{
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
const designerRef = ref(); const designerRef = ref();
// /** 保存成功回调 */
const handleSuccess = (data?: any) => { const handleSuccess = (data?: any) => {
if (data) { if (data) {
emit('success', data); emit('success', data);
} }
}; };
/** 设计器配置校验 */
const validateConfig = async () => {
return await designerRef.value.validate();
};
defineExpose({ validateConfig });
</script> </script>
<template> <template>
<ContentWrap :body-style="{ padding: '20px 16px' }"> <ContentWrap :body-style="{ padding: '20px 16px' }">