!122 Merge remote-tracking branch 'yudao/dev' into dev

Merge pull request !122 from Jason/dev
dev
xingyu 2025-05-30 13:13:46 +00:00 committed by Gitee
commit 00d51604f1
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
7 changed files with 1388 additions and 5 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

@ -0,0 +1,174 @@
<script setup lang="ts">
import { toRefs, watch } from 'vue';
import { Plus, Trash2 } from '@vben/icons';
import {
Alert,
Button,
Col,
FormItem,
Input,
Row,
Select,
SelectOption,
} from 'ant-design-vue';
import { useFormFields } from '../../../helpers';
import HttpRequestParamSetting from './http-request-param-setting.vue';
defineOptions({ name: 'HttpRequestSetting' });
const props = defineProps({
setting: {
type: Object,
required: true,
},
responseEnable: {
type: Boolean,
required: true,
},
formItemPrefix: {
type: String,
required: true,
},
});
const emits = defineEmits(['update:setting']);
const { setting } = toRefs(props);
watch(
() => setting,
(val) => {
emits('update:setting', val);
},
);
/** 流程表单字段 */
const formFields = useFormFields();
/** 添加 HTTP 请求返回值设置项 */
const addHttpResponseSetting = (responseSetting: Record<string, string>[]) => {
responseSetting.push({
key: '',
value: '',
});
};
/** 删除 HTTP 请求返回值设置项 */
const deleteHttpResponseSetting = (
responseSetting: Record<string, string>[],
index: number,
) => {
responseSetting.splice(index, 1);
};
</script>
<template>
<FormItem>
<Alert
message="仅支持 POST 请求,以请求体方式接收参数"
type="warning"
show-icon
:closable="false"
/>
</FormItem>
<!-- 请求地址-->
<FormItem
label="请求地址"
:label-col="{ span: 24 }"
:wrapper-col="{ span: 24 }"
:name="[formItemPrefix, 'url']"
:rules="{
required: true,
message: '请求地址不能为空',
trigger: ['blur', 'change'],
}"
>
<Input v-model:value="setting.url" placeholder="请输入请求地址" />
</FormItem>
<!-- 请求头请求体设置-->
<HttpRequestParamSetting
:header="setting.header"
:body="setting.body"
:bind="formItemPrefix"
/>
<!-- 返回值设置-->
<div v-if="responseEnable">
<FormItem
label="返回值"
:label-col="{ span: 24 }"
:wrapper-col="{ span: 24 }"
>
<Alert
message="通过请求返回值, 可以修改流程表单的值"
type="warning"
show-icon
:closable="false"
/>
</FormItem>
<FormItem :wrapper-col="{ span: 24 }">
<Row
:gutter="8"
v-for="(item, index) in setting.response"
:key="index"
class="mb-2"
>
<Col :span="10">
<FormItem
:name="[formItemPrefix, 'response', index, 'key']"
:rules="{
required: true,
message: '表单字段不能为空',
trigger: ['blur', 'change'],
}"
>
<Select
v-model:value="item.key"
placeholder="请选择表单字段"
allow-clear
>
<SelectOption
v-for="(field, fIdx) in formFields"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="!field.required"
>
{{ field.title }}
</SelectOption>
</Select>
</FormItem>
</Col>
<Col :span="12">
<FormItem
:name="[formItemPrefix, 'response', index, 'value']"
:rules="{
required: true,
message: '请求返回字段不能为空',
trigger: ['blur', 'change'],
}"
>
<Input v-model:value="item.value" placeholder="请求返回字段" />
</FormItem>
</Col>
<Col :span="2">
<div class="flex h-[32px] items-center">
<Trash2
class="size-4 cursor-pointer text-red-500"
@click="deleteHttpResponseSetting(setting.response!, index)"
/>
</div>
</Col>
</Row>
<Button
type="link"
@click="addHttpResponseSetting(setting.response!)"
class="flex items-center"
>
<template #icon>
<Plus class="size-[18px]" />
</template>
添加一行
</Button>
</FormItem>
</div>
</template>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,697 @@
<script setup lang="ts">
import type { Rule } from 'ant-design-vue/es/form';
import type { SelectValue } from 'ant-design-vue/es/select';
import type {
FormTriggerSetting,
SimpleFlowNode,
TriggerSetting,
} from '../../consts';
import { computed, getCurrentInstance, onMounted, reactive, ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
import { IconifyIcon, Trash2 } from '@vben/icons';
import { cloneDeep } from '@vben/utils';
import {
Button,
Card,
Col,
Divider,
Form,
FormItem,
Input,
message,
Row,
Select,
SelectOption,
Tag,
} from 'ant-design-vue';
import {
DEFAULT_CONDITION_GROUP_VALUE,
NodeType,
TRIGGER_TYPES,
TriggerTypeEnum,
} from '../../consts';
import {
getConditionShowText,
useFormFields,
useFormFieldsAndStartUser,
useNodeName,
useWatchNode,
} from '../../helpers';
import ConditionDialog from './modules/condition-dialog.vue';
import HttpRequestSetting from './modules/http-request-setting.vue';
defineOptions({
name: 'TriggerNodeConfig',
});
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true,
},
});
const { proxy } = getCurrentInstance() as any;
//
const [Drawer, drawerApi] = useVbenDrawer({
header: true,
closable: true,
title: '',
onCancel() {
drawerApi.close();
},
onConfirm() {
saveConfig();
},
});
//
const currentNode = useWatchNode(props);
//
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(
NodeType.TRIGGER_NODE,
);
//
const formRef = ref(); // Ref
//
const formRules: Record<string, Rule[]> = reactive({
type: [{ required: true, message: '触发器类型不能为空', trigger: 'change' }],
'httpRequestSetting.url': [
{ required: true, message: '请求地址不能为空', trigger: 'blur' },
],
});
//
const configForm = ref<TriggerSetting>({
type: TriggerTypeEnum.HTTP_REQUEST,
httpRequestSetting: {
url: '',
header: [],
body: [],
response: [],
},
formSettings: [
{
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
updateFormFields: {},
deleteFields: [],
},
],
});
//
const formFields = useFormFields();
//
const optionalUpdateFormFields = computed(() => {
return formFields.map((field) => ({
title: field.title,
field: field.field,
disabled: false,
}));
});
let originalSetting: TriggerSetting | undefined;
/** 触发器类型改变了 */
const changeTriggerType = () => {
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
configForm.value.httpRequestSetting =
originalSetting?.type === TriggerTypeEnum.HTTP_REQUEST &&
originalSetting.httpRequestSetting
? originalSetting.httpRequestSetting
: {
url: '',
header: [],
body: [],
response: [],
};
configForm.value.formSettings = undefined;
return;
}
if (configForm.value.type === TriggerTypeEnum.HTTP_CALLBACK) {
configForm.value.httpRequestSetting =
originalSetting?.type === TriggerTypeEnum.HTTP_CALLBACK &&
originalSetting.httpRequestSetting
? originalSetting.httpRequestSetting
: {
url: '',
header: [],
body: [],
response: [],
};
configForm.value.formSettings = undefined;
return;
}
if (configForm.value.type === TriggerTypeEnum.FORM_UPDATE) {
configForm.value.formSettings =
originalSetting?.type === TriggerTypeEnum.FORM_UPDATE &&
originalSetting.formSettings
? originalSetting.formSettings
: [
{
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
updateFormFields: {},
deleteFields: [],
},
];
configForm.value.httpRequestSetting = undefined;
return;
}
if (configForm.value.type === TriggerTypeEnum.FORM_DELETE) {
configForm.value.formSettings =
originalSetting?.type === TriggerTypeEnum.FORM_DELETE &&
originalSetting.formSettings
? originalSetting.formSettings
: [
{
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
updateFormFields: undefined,
deleteFields: [],
},
];
configForm.value.httpRequestSetting = undefined;
}
};
/** 添加新的修改表单设置 */
const addFormSetting = () => {
configForm.value.formSettings!.push({
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
updateFormFields: {},
deleteFields: [],
});
};
/** 删除修改表单设置 */
const deleteFormSetting = (index: number) => {
configForm.value.formSettings!.splice(index, 1);
};
/** 添加条件配置 */
const addFormSettingCondition = (
index: number,
formSetting: FormTriggerSetting,
) => {
const conditionDialog = proxy.$refs[`condition-${index}`][0];
conditionDialog.open(formSetting);
};
/** 删除条件配置 */
const deleteFormSettingCondition = (formSetting: FormTriggerSetting) => {
formSetting.conditionType = undefined;
};
/** 打开条件配置弹窗 */
const openFormSettingCondition = (
index: number,
formSetting: FormTriggerSetting,
) => {
const conditionDialog = proxy.$refs[`condition-${index}`][0];
conditionDialog.open(formSetting);
};
/** 处理条件配置保存 */
const handleConditionUpdate = (index: number, condition: any) => {
if (configForm.value.formSettings![index]) {
configForm.value.formSettings![index].conditionType =
condition.conditionType;
configForm.value.formSettings![index].conditionExpression =
condition.conditionExpression;
configForm.value.formSettings![index].conditionGroups =
condition.conditionGroups;
}
};
//
const includeStartUserFormFields = useFormFieldsAndStartUser();
/** 条件配置展示 */
const showConditionText = (formSetting: FormTriggerSetting) => {
return getConditionShowText(
formSetting.conditionType,
formSetting.conditionExpression,
formSetting.conditionGroups,
includeStartUserFormFields,
);
};
/** 添加修改字段设置项 */
const addFormFieldSetting = (formSetting: FormTriggerSetting) => {
if (!formSetting) return;
if (!formSetting.updateFormFields) {
formSetting.updateFormFields = {};
}
formSetting.updateFormFields[''] = undefined;
};
/** 更新字段 KEY */
const updateFormFieldKey = (
formSetting: FormTriggerSetting,
oldKey: string,
newKey: SelectValue,
) => {
if (!formSetting?.updateFormFields || !newKey) return;
const value = formSetting.updateFormFields[oldKey];
delete formSetting.updateFormFields[oldKey];
formSetting.updateFormFields[String(newKey)] = value;
};
/** 删除修改字段设置项 */
const deleteFormFieldSetting = (
formSetting: FormTriggerSetting,
key: string,
) => {
if (!formSetting?.updateFormFields) return;
delete formSetting.updateFormFields[key];
};
/** 保存配置 */
const saveConfig = async () => {
if (!formRef.value) return false;
const valid = await formRef.value.validate();
if (!valid) return false;
const showText = getShowText();
if (!showText) return false;
currentNode.value.name = nodeName.value!;
currentNode.value.showText = showText;
switch (configForm.value.type) {
case TriggerTypeEnum.FORM_DELETE: {
configForm.value.httpRequestSetting = undefined;
//
configForm.value.formSettings?.forEach((setting) => {
setting.updateFormFields = undefined;
});
break;
}
case TriggerTypeEnum.FORM_UPDATE: {
configForm.value.httpRequestSetting = undefined;
//
configForm.value.formSettings?.forEach((setting) => {
setting.deleteFields = undefined;
});
break;
}
case TriggerTypeEnum.HTTP_REQUEST: {
configForm.value.formSettings = undefined;
break;
}
// No default
}
currentNode.value.triggerSetting = configForm.value;
drawerApi.close();
return true;
};
/** 获取节点展示内容 */
const getShowText = (): string => {
let showText = '';
switch (configForm.value.type) {
case TriggerTypeEnum.FORM_DELETE: {
for (const [index, setting] of configForm.value.formSettings!.entries()) {
if (!setting.deleteFields || setting.deleteFields.length === 0) {
message.warning(`请选择表单设置${index + 1}要删除的字段`);
return '';
}
}
showText = '删除表单数据';
break;
}
case TriggerTypeEnum.FORM_UPDATE: {
for (const [index, setting] of configForm.value.formSettings!.entries()) {
if (
!setting.updateFormFields ||
Object.keys(setting.updateFormFields).length === 0
) {
message.warning(`请添加表单设置${index + 1}的修改字段`);
return '';
}
}
showText = '修改表单数据';
break;
}
case TriggerTypeEnum.HTTP_CALLBACK:
case TriggerTypeEnum.HTTP_REQUEST: {
showText = `${configForm.value.httpRequestSetting?.url}`;
break;
}
// No default
}
return showText;
};
/** 显示触发器节点配置, 由父组件传过来 */
const showTriggerNodeConfig = (node: SimpleFlowNode) => {
nodeName.value = node.name;
originalSetting = node.triggerSetting
? cloneDeep(node.triggerSetting)
: undefined;
if (node.triggerSetting) {
configForm.value = {
type: node.triggerSetting.type,
httpRequestSetting: node.triggerSetting.httpRequestSetting || {
url: '',
header: [],
body: [],
response: [],
},
formSettings: node.triggerSetting.formSettings || [
{
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
updateFormFields: {},
deleteFields: [],
},
],
};
}
drawerApi.open();
};
//
defineExpose({ showTriggerNodeConfig });
onMounted(() => {
//
});
</script>
<template>
<Drawer class="w-[580px]">
<template #title>
<div class="config-header">
<Input
v-if="showInput"
type="text"
class="config-editable-input"
@blur="blurEvent()"
v-model:value="nodeName"
:placeholder="nodeName"
/>
<div v-else class="node-name">
{{ nodeName }}
<IconifyIcon class="ml-1" icon="ep:edit-pen" @click="clickIcon()" />
</div>
</div>
</template>
<div>
<Form
ref="formRef"
:model="configForm"
:label-col="{ span: 24 }"
:wrapper-col="{ span: 24 }"
:rules="formRules"
>
<FormItem label="触发器类型" name="type">
<Select v-model:value="configForm.type" @change="changeTriggerType">
<SelectOption
v-for="(item, index) in TRIGGER_TYPES"
:key="index"
:value="item.value"
:label="item.label"
>
{{ item.label }}
</SelectOption>
</Select>
</FormItem>
<!-- HTTP 请求触发器 -->
<div
v-if="
[
TriggerTypeEnum.HTTP_REQUEST,
TriggerTypeEnum.HTTP_CALLBACK,
].includes(configForm.type) && configForm.httpRequestSetting
"
>
<HttpRequestSetting
v-model:setting="configForm.httpRequestSetting"
:response-enable="configForm.type === TriggerTypeEnum.HTTP_REQUEST"
form-item-prefix="httpRequestSetting"
/>
</div>
<!-- 表单数据修改触发器 -->
<div v-if="configForm.type === TriggerTypeEnum.FORM_UPDATE">
<div
v-for="(formSetting, index) in configForm.formSettings"
:key="index"
>
<Card class="mt-4">
<template #title>
<div class="flex w-full items-center justify-between">
<span>修改表单设置 {{ index + 1 }}</span>
<Button
v-if="configForm.formSettings!.length > 1"
shape="circle"
class="flex items-center justify-center"
@click="deleteFormSetting(index)"
>
<template #icon>
<IconifyIcon icon="ep:close" />
</template>
</Button>
</div>
</template>
<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)"
>
{{ showConditionText(formSetting) }}
</Tag>
</div>
<Button
v-else
type="link"
class="flex items-center p-0"
@click="addFormSettingCondition(index, formSetting)"
>
<template #icon>
<IconifyIcon icon="ep:link" />
</template>
添加条件
</Button>
</Col>
</Row>
<Divider>修改表单字段设置</Divider>
<!-- 表单字段修改设置 -->
<Row
:gutter="8"
v-for="key in Object.keys(formSetting.updateFormFields || {})"
:key="key"
>
<Col :span="8">
<FormItem>
<Select
:value="key || undefined"
@change="
(newKey) => updateFormFieldKey(formSetting, key, newKey)
"
placeholder="请选择表单字段"
:disabled="key !== ''"
allow-clear
>
<SelectOption
v-for="(field, fIdx) in optionalUpdateFormFields"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="field.disabled"
>
{{ field.title }}
</SelectOption>
</Select>
</FormItem>
</Col>
<Col :span="4">
<FormItem>的值设置为</FormItem>
</Col>
<Col :span="10">
<FormItem
:name="['formSettings', index, 'updateFormFields', key]"
:rules="{
required: true,
message: '值不能为空',
trigger: 'blur',
}"
>
<Input
v-model:value="formSetting.updateFormFields![key]"
placeholder="请输入值"
allow-clear
:disabled="!key"
/>
</FormItem>
</Col>
<Col :span="2">
<div class="flex h-[32px] items-center">
<Trash2
class="size-4 cursor-pointer text-red-500"
@click="deleteFormFieldSetting(formSetting, key)"
/>
</div>
</Col>
</Row>
<!-- 添加表单字段按钮 -->
<Row>
<Col :span="24">
<Button
type="link"
class="flex items-center p-0"
@click="addFormFieldSetting(formSetting)"
>
<template #icon>
<IconifyIcon icon="ep:memo" />
</template>
添加修改字段
</Button>
</Col>
</Row>
</Card>
</div>
<!-- 添加新的设置 -->
<Row class="mt-6">
<Col :span="24">
<Button
class="flex items-center p-0"
type="link"
@click="addFormSetting"
>
<template #icon>
<IconifyIcon icon="ep:setting" />
</template>
添加设置
</Button>
</Col>
</Row>
</div>
<!-- 表单数据删除触发器 -->
<div v-if="configForm.type === TriggerTypeEnum.FORM_DELETE">
<div
v-for="(formSetting, index) in configForm.formSettings"
:key="index"
>
<Card class="mt-4">
<template #title>
<div class="flex w-full items-center justify-between">
<span>删除表单设置 {{ index + 1 }}</span>
<Button
v-if="configForm.formSettings!.length > 1"
shape="circle"
class="flex items-center justify-center"
@click="deleteFormSetting(index)"
>
<template #icon>
<IconifyIcon icon="ep:close" />
</template>
</Button>
</div>
</template>
<!-- 条件设置 -->
<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)"
>
{{ showConditionText(formSetting) }}
</Tag>
</div>
<Button
v-else
type="link"
class="flex items-center p-0"
@click="addFormSettingCondition(index, formSetting)"
>
<template #icon>
<IconifyIcon icon="ep:link" />
</template>
添加条件
</Button>
</Col>
</Row>
<Divider>删除表单字段设置</Divider>
<!-- 表单字段删除设置 -->
<div class="flex flex-wrap gap-2">
<Select
v-model:value="formSetting.deleteFields"
mode="multiple"
placeholder="请选择要删除的字段"
class="w-full"
>
<SelectOption
v-for="field in formFields"
:key="field.field"
:label="field.title"
:value="field.field"
>
{{ field.title }}
</SelectOption>
</Select>
</div>
</Card>
</div>
<!-- 添加新的设置 -->
<Row class="mt-6">
<Col :span="24">
<Button
class="flex items-center p-0"
type="link"
@click="addFormSetting"
>
<template #icon>
<IconifyIcon icon="ep:setting" />
</template>
添加设置
</Button>
</Col>
</Row>
</div>
</Form>
</div>
<template #footer>
<div class="flex justify-end">
<Button type="primary" @click="saveConfig"> </Button>
<Button class="ml-2" @click="drawerApi.close()"> </Button>
</div>
</template>
</Drawer>
</template>
<style lang="scss" scoped>
.config-editable-input {
&:focus {
outline: 0;
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgb(24 144 255 / 20%);
}
}
</style>

View File

@ -0,0 +1,118 @@
<script setup lang="ts">
import type { SimpleFlowNode } from '../../consts';
import { inject, ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { Input } from 'ant-design-vue';
import { NODE_DEFAULT_TEXT, NodeType } from '../../consts';
import { useNodeName2, useTaskStatusClass, useWatchNode } from '../../helpers';
import TriggerNodeConfig from '../nodes-config/trigger-node-config.vue';
import NodeHandler from './node-handler.vue';
defineOptions({
name: 'TriggerNode',
});
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true,
},
});
//
const emits = defineEmits<{
'update:flowNode': [node: SimpleFlowNode | undefined];
}>();
//
const readonly = inject<Boolean>('readonly');
//
const currentNode = useWatchNode(props);
//
const { showInput, blurEvent, clickTitle } = useNodeName2(
currentNode,
NodeType.TRIGGER_NODE,
);
const nodeSetting = ref();
//
const openNodeConfig = () => {
if (readonly) {
return;
}
nodeSetting.value.showTriggerNodeConfig(currentNode.value);
};
//
const deleteNode = () => {
emits('update:flowNode', currentNode.value.childNode);
};
</script>
<template>
<div class="node-wrapper">
<div class="node-container">
<div
class="node-box"
:class="[
{ 'node-config-error': !currentNode.showText },
`${useTaskStatusClass(currentNode?.activityStatus)}`,
]"
>
<div class="node-title-container">
<div class="node-title-icon trigger-node">
<span class="iconfont icon-trigger"></span>
</div>
<Input
v-if="!readonly && showInput"
type="text"
class="editable-title-input"
@blur="blurEvent()"
v-model="currentNode.name"
:placeholder="currentNode.name"
/>
<div v-else class="node-title" @click="clickTitle">
{{ currentNode.name }}
</div>
</div>
<div class="node-content" @click="openNodeConfig">
<div
class="node-text"
:title="currentNode.showText"
v-if="currentNode.showText"
>
{{ currentNode.showText }}
</div>
<div class="node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(NodeType.TRIGGER_NODE) }}
</div>
<IconifyIcon v-if="!readonly" icon="ep:arrow-right-bold" />
</div>
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon">
<IconifyIcon
color="#0089ff"
icon="ep:circle-close-filled"
:size="18"
@click="deleteNode"
/>
</div>
</div>
</div>
<!-- 传递子节点给添加节点组件会在子节点前面添加节点 -->
<NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div>
<TriggerNodeConfig
v-if="!readonly && currentNode"
ref="nodeSetting"
:flow-node="currentNode"
/>
</div>
</template>
<style lang="scss" scoped></style>

View File

@ -6,6 +6,7 @@ import { useWatchNode } from '../helpers';
import CopyTaskNode from './nodes/copy-task-node.vue';
import EndEventNode from './nodes/end-event-node.vue';
import StartUserNode from './nodes/start-user-node.vue';
import TriggerNode from './nodes/trigger-node.vue';
import UserTaskNode from './nodes/user-task-node.vue';
defineOptions({ name: 'ProcessNodeTree' });
@ -117,11 +118,11 @@ const recursiveFindParentNode = (
@update:flow-node="handleModelValueUpdate"
/> -->
<!-- 触发器节点 -->
<!-- <TriggerNode
<TriggerNode
v-if="currentNode && currentNode.type === NodeType.TRIGGER_NODE"
:flow-node="currentNode"
@update:flow-node="handleModelValueUpdate"
/> -->
/>
<!-- 子流程节点 -->
<!-- <ChildProcessNode
v-if="currentNode && currentNode.type === NodeType.CHILD_PROCESS_NODE"

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: '',
},
],