feat: [BPM 工作流] Simple 模型 - 触发器节点条件设置

pull/122/head
jason 2025-05-30 21:06:41 +08:00
parent 9587a8cdcd
commit 688f7c9083
4 changed files with 410 additions and 15 deletions

View File

@ -0,0 +1,70 @@
<script setup lang="ts">
import type { ConditionGroup } from '../../../consts';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { cloneDeep } from '@vben/utils';
import { message } from 'ant-design-vue';
import { ConditionType, DEFAULT_CONDITION_GROUP_VALUE } from '../../../consts';
import Condition from './condition.vue';
defineOptions({ name: 'ConditionDialog' });
const emit = defineEmits<{
updateCondition: [condition: object];
}>();
const conditionData = ref<{
conditionExpression?: string;
conditionGroups?: ConditionGroup;
conditionType: ConditionType;
}>({
conditionType: ConditionType.RULE,
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
});
//
const conditionRef = ref();
const [Modal, modalApi] = useVbenModal({
title: '条件配置',
destroyOnClose: true,
draggable: true,
async onConfirm() {
//
if (!conditionRef.value) return;
const valid = await conditionRef.value.validate().catch(() => false);
if (!valid) {
message.warning('请完善条件规则');
return;
}
//
emit('updateCondition', conditionData.value);
modalApi.close();
},
onCancel() {
modalApi.close();
},
});
const open = (conditionObj: any | undefined) => {
if (conditionObj) {
conditionData.value.conditionType = conditionObj.conditionType;
conditionData.value.conditionExpression = conditionObj.conditionExpression;
conditionData.value.conditionGroups = conditionObj.conditionGroups;
}
modalApi.open();
};
defineExpose({ open });
</script>
<template>
<Modal class="w-1/2">
<Condition ref="conditionRef" v-model="conditionData" />
</Modal>
</template>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,323 @@
<script setup lang="ts">
import type { Rule } from 'ant-design-vue/es/form';
import type { Ref } from 'vue';
import { computed, inject, reactive, ref } from 'vue';
import { IconifyIcon, Plus, Trash2 } from '@vben/icons';
import { cloneDeep } from '@vben/utils';
import {
Card,
Col,
Form,
FormItem,
Input,
Radio,
RadioGroup,
Row,
Select,
SelectOption,
Space,
Switch,
Textarea,
Tooltip,
} from 'ant-design-vue';
import { BpmModelFormType } from '#/utils/constants';
import {
COMPARISON_OPERATORS,
CONDITION_CONFIG_TYPES,
ConditionType,
DEFAULT_CONDITION_GROUP_VALUE,
} from '../../../consts';
import { useFormFieldsAndStartUser } from '../../../helpers';
defineOptions({
name: 'Condition',
});
const props = defineProps({
modelValue: {
type: Object,
required: true,
},
});
const emit = defineEmits(['update:modelValue']);
const condition = computed({
get() {
return props.modelValue;
},
set(newValue) {
emit('update:modelValue', newValue);
},
});
const formType = inject<Ref<number>>('formType'); //
const conditionConfigTypes = computed(() => {
return CONDITION_CONFIG_TYPES.filter((item) => {
//
return !(
formType?.value === BpmModelFormType.CUSTOM &&
item.value === ConditionType.RULE
);
});
});
/** 条件规则可选择的表单字段 */
const fieldOptions = useFormFieldsAndStartUser();
//
const formRules: Record<string, Rule[]> = reactive({
conditionType: [
{ required: true, message: '配置方式不能为空', trigger: 'change' },
],
conditionExpression: [
{
required: true,
message: '条件表达式不能为空',
trigger: ['blur', 'change'],
},
],
});
const formRef = ref(); // Ref
/** 切换条件配置方式 */
const changeConditionType = () => {
if (
condition.value.conditionType === ConditionType.RULE &&
!condition.value.conditionGroups
) {
condition.value.conditionGroups = cloneDeep(DEFAULT_CONDITION_GROUP_VALUE);
}
};
const deleteConditionGroup = (conditions: any, index: number) => {
conditions.splice(index, 1);
};
const deleteConditionRule = (condition: any, index: number) => {
condition.rules.splice(index, 1);
};
const addConditionRule = (condition: any, index: number) => {
const rule = {
opCode: '==',
leftSide: undefined,
rightSide: '',
};
condition.rules.splice(index + 1, 0, rule);
};
const addConditionGroup = (conditions: any) => {
const condition = {
and: true,
rules: [
{
opCode: '==',
leftSide: undefined,
rightSide: '',
},
],
};
conditions.push(condition);
};
const validate = async () => {
if (!formRef.value) return false;
return await formRef.value.validate();
};
defineExpose({ validate });
</script>
<template>
<Form
ref="formRef"
:model="condition"
:rules="formRules"
:label-col="{ span: 24 }"
:wrapper-col="{ span: 24 }"
>
<FormItem label="配置方式" name="conditionType">
<RadioGroup
v-model:value="condition.conditionType"
@change="changeConditionType"
>
<Radio
v-for="(dict, indexConditionType) in conditionConfigTypes"
:key="indexConditionType"
:value="dict.value"
>
{{ dict.label }}
</Radio>
</RadioGroup>
</FormItem>
<FormItem
v-if="
condition.conditionType === ConditionType.RULE &&
condition.conditionGroups
"
>
<div class="mb-5 flex w-full justify-between">
<div class="flex items-center">
<div class="mr-4">条件组关系</div>
<Switch
v-model:checked="condition.conditionGroups.and"
checked-children="且"
un-checked-children="或"
/>
</div>
</div>
<Space direction="vertical" size="small" class="w-11/12 pl-1">
<template #split>
{{ condition.conditionGroups.and ? '且' : '或' }}
</template>
<Card
class="group relative w-full hover:border-[#1890ff]"
v-for="(equation, cIdx) in condition.conditionGroups.conditions"
:key="cIdx"
>
<div
class="absolute left-0 top-0 z-[1] flex cursor-pointer opacity-0 group-hover:opacity-100"
v-if="condition.conditionGroups.conditions.length > 1"
>
<IconifyIcon
color="#0089ff"
icon="ep:circle-close-filled"
class="size-4"
@click="
deleteConditionGroup(condition.conditionGroups.conditions, cIdx)
"
/>
</div>
<template #extra>
<div class="flex items-center justify-between">
<div>条件组</div>
<div class="flex">
<div class="mr-4">规则关系</div>
<Switch
v-model:checked="equation.and"
checked-children="且"
un-checked-children="或"
/>
</div>
</div>
</template>
<Row
:gutter="8"
class="mb-2"
v-for="(rule, rIdx) in equation.rules"
:key="rIdx"
>
<Col :span="8">
<FormItem
:name="[
'conditionGroups',
'conditions',
cIdx,
'rules',
rIdx,
'leftSide',
]"
:rules="{
required: true,
message: '左值不能为空',
trigger: 'change',
}"
>
<Select
v-model:value="rule.leftSide"
allow-clear
placeholder="请选择表单字段"
>
<SelectOption
v-for="(field, fIdx) in fieldOptions"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="!field.required"
>
<Tooltip
title="表单字段非必填时不能作为流程分支条件"
placement="right"
v-if="!field.required"
>
<span>{{ field.title }}</span>
</Tooltip>
<template v-else>{{ field.title }}</template>
</SelectOption>
</Select>
</FormItem>
</Col>
<Col :span="6">
<Select v-model:value="rule.opCode" placeholder="请选择操作符">
<SelectOption
v-for="operator in COMPARISON_OPERATORS"
:key="operator.value"
:label="operator.label"
:value="operator.value"
>
{{ operator.label }}
</SelectOption>
</Select>
</Col>
<Col :span="7">
<FormItem
:name="[
'conditionGroups',
'conditions',
cIdx,
'rules',
rIdx,
'rightSide',
]"
:rules="{
required: true,
message: '右值不能为空',
trigger: ['blur', 'change'],
}"
>
<Input
v-model:value="rule.rightSide"
placeholder="请输入右值"
/>
</FormItem>
</Col>
<Col :span="3">
<div class="flex h-[32px] items-center">
<Trash2
v-if="equation.rules.length > 1"
class="mr-2 size-4 cursor-pointer text-red-500"
@click="deleteConditionRule(equation, rIdx)"
/>
<Plus
class="size-4 cursor-pointer text-blue-500"
@click="addConditionRule(equation, rIdx)"
/>
</div>
</Col>
</Row>
</Card>
</Space>
<div title="添加条件组" class="mt-4 cursor-pointer">
<Plus
class="size-[24px] text-blue-500"
@click="addConditionGroup(condition.conditionGroups?.conditions)"
/>
</div>
</FormItem>
<FormItem
v-if="condition.conditionType === ConditionType.EXPRESSION"
label="条件表达式"
name="conditionExpression"
>
<Textarea
v-model:value="condition.conditionExpression"
placeholder="请输入条件表达式"
allow-clear
:auto-size="{ minRows: 3, maxRows: 6 }"
/>
</FormItem>
</Form>
</template>

View File

@ -38,10 +38,11 @@ import {
import {
getConditionShowText,
useFormFields,
useFormFieldsAndStartUser,
useNodeName,
useWatchNode,
} from '../../helpers';
// TODO import ConditionDialog from './modules/condition-dialog.vue';
import ConditionDialog from './modules/condition-dialog.vue';
import HttpRequestSetting from './modules/http-request-setting.vue';
defineOptions({
@ -215,8 +216,7 @@ const openFormSettingCondition = (
conditionDialog.open(formSetting);
};
/** 处理条件配置保存 */
/* 使
const _handleConditionUpdate = (index: number, condition: any) => {
const handleConditionUpdate = (index: number, condition: any) => {
if (configForm.value.formSettings![index]) {
configForm.value.formSettings![index].conditionType =
condition.conditionType;
@ -226,14 +226,15 @@ const _handleConditionUpdate = (index: number, condition: any) => {
condition.conditionGroups;
}
};
*/
//
const includeStartUserFormFields = useFormFieldsAndStartUser();
/** 条件配置展示 */
const showConditionText = (formSetting: FormTriggerSetting) => {
return getConditionShowText(
formSetting.conditionType,
formSetting.conditionExpression,
formSetting.conditionGroups,
formFields,
includeStartUserFormFields,
);
};
@ -457,17 +458,17 @@ onMounted(() => {
</div>
</template>
<!-- TODO 条件设置 -->
<!-- <ConditionDialog
<ConditionDialog
:ref="`condition-${index}`"
@update-condition="(val) => handleConditionUpdate(index, val)"
/> -->
/>
<Row>
<Col :span="24">
<div class="cursor-pointer" v-if="formSetting.conditionType">
<Tag
color="success"
closable
class="text-sm"
@close="deleteFormSettingCondition(formSetting)"
@click="openFormSettingCondition(index, formSetting)"
>
@ -605,17 +606,18 @@ onMounted(() => {
</div>
</template>
<!-- TODO 条件设置 -->
<!-- <ConditionDialog
<!-- 条件设置 -->
<ConditionDialog
:ref="`condition-${index}`"
@update-condition="(val) => handleConditionUpdate(index, val)"
/> -->
/>
<Row>
<Col :span="24">
<div class="cursor-pointer" v-if="formSetting.conditionType">
<Tag
color="warning"
color="success"
closable
class="text-sm"
@close="deleteFormSettingCondition(formSetting)"
@click="openFormSettingCondition(index, formSetting)"
>

View File

@ -475,9 +475,9 @@ export type ListenerHandler = {
*
*/
export type ConditionRule = {
leftSide: string;
leftSide: string | undefined;
opCode: string;
rightSide: string;
rightSide: string | undefined;
};
/**
@ -725,7 +725,7 @@ export const DEFAULT_CONDITION_GROUP_VALUE = {
rules: [
{
opCode: '==',
leftSide: '',
leftSide: undefined,
rightSide: '',
},
],