Files
frontend/apps/web-antd/src/components/simple-process-design/components/nodes-config/modules/condition.vue
2025-06-17 20:22:24 +08:00

329 lines
8.7 KiB
Vue

<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';
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
/** 切换条件配置方式 */
function changeConditionType() {
if (
condition.value.conditionType === ConditionType.RULE &&
!condition.value.conditionGroups
) {
condition.value.conditionGroups = cloneDeep(DEFAULT_CONDITION_GROUP_VALUE);
}
}
function deleteConditionGroup(conditions: any, index: number) {
conditions.splice(index, 1);
}
function deleteConditionRule(condition: any, index: number) {
condition.rules.splice(index, 1);
}
function addConditionRule(condition: any, index: number) {
const rule = {
opCode: '==',
leftSide: undefined,
rightSide: '',
};
condition.rules.splice(index + 1, 0, rule);
}
function addConditionGroup(conditions: any) {
const condition = {
and: true,
rules: [
{
opCode: '==',
leftSide: undefined,
rightSide: '',
},
],
};
conditions.push(condition);
}
async function validate() {
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-blue-500"
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="blue"
icon="lucide:circle-x"
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-8 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-6 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>