refactor: 重构 bpmnProcessDesigner 组件 ele => antd

This commit is contained in:
puhui999
2025-09-14 18:20:11 +08:00
parent 71dd9f2d88
commit 6ffd3dbd67
9 changed files with 418 additions and 203 deletions

View File

@@ -1,150 +1,22 @@
<template>
<div
class="process-panel__container"
:style="{ width: `${width}px`, maxHeight: '600px' }"
>
<Collapse v-model:activeKey="activeTab" v-if="isReady">
<CollapsePanel key="base" header="常规">
<template #title>
<Icon icon="ant-design:info-circle-filled" />
常规
</template>
<ElementBaseInfo
:id-edit-disabled="idEditDisabled"
:business-object="elementBusinessObject"
:type="elementType"
:model="model"
/>
</CollapsePanel>
<CollapsePanel
key="message"
v-if="elementType === 'Process'"
>
<template #title>
<Icon icon="ant-design:message-filled" />
消息与信号
</template>
<signal-and-massage />
</CollapsePanel>
<CollapsePanel
key="condition"
v-if="conditionFormVisible"
>
<template #title>
<Icon icon="ant-design:swap-right" />
流转条件
</template>
<flow-condition
:business-object="elementBusinessObject"
:type="elementType"
/>
</CollapsePanel>
<CollapsePanel key="form" v-if="formVisible">
<template #title>
<Icon icon="ant-design:unordered-list-outlined" />
表单
</template>
<element-form :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel
key="task"
v-if="isTaskCollapseItemShow(elementType)"
>
<template #title>
<Icon icon="ant-design:check-circle-filled" />
{{ getTaskCollapseItemName(elementType) }}
</template>
<element-task :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel
key="multiInstance"
v-if="elementType.indexOf('Task') !== -1"
>
<template #title>
<Icon icon="ant-design:question-circle-filled" />
多人审批方式
</template>
<element-multi-instance
:id="elementId"
:business-object="elementBusinessObject"
:type="elementType"
/>
</CollapsePanel>
<CollapsePanel key="listeners">
<template #title>
<Icon icon="ant-design:bell-filled" />
执行监听器
</template>
<element-listeners :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel
key="taskListeners"
v-if="elementType === 'UserTask'"
>
<template #title>
<Icon icon="ant-design:bell-filled" />
任务监听器
</template>
<user-task-listeners :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel key="extensions">
<template #title>
<Icon icon="ant-design:plus-circle-filled" />
扩展属性
</template>
<element-properties :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel key="other">
<template #title>
<Icon icon="ant-design:swap-right" />
其他
</template>
<element-other-config :id="elementId" />
</CollapsePanel>
<CollapsePanel key="customConfig">
<template #title>
<Icon icon="ant-design:tool-filled" />
自定义配置
</template>
<element-custom-config
:id="elementId"
:type="elementType"
:business-object="elementBusinessObject"
/>
</CollapsePanel>
<!-- 新增的时间事件配置项 -->
<CollapsePanel
key="timeEvent"
v-if="elementType === 'IntermediateCatchEvent'"
>
<template #title>
<Icon icon="ant-design:clock-circle-filled" />
时间事件
</template>
<TimeEventConfig
:businessObject="bpmnElement.value?.businessObject"
:key="elementId"
/>
</CollapsePanel>
</Collapse>
</div>
</template>
<script lang="ts" setup>
import { Collapse, CollapsePanel } from 'ant-design-vue';
import { nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue';
import { Icon } from '@vben/icons';
import { Collapse, CollapsePanel } from 'ant-design-vue';
import ElementBaseInfo from './base/ElementBaseInfo.vue';
import ElementOtherConfig from './other/ElementOtherConfig.vue';
import ElementTask from './task/ElementTask.vue';
import ElementMultiInstance from './multi-instance/ElementMultiInstance.vue';
import FlowCondition from './flow-condition/FlowCondition.vue';
import SignalAndMassage from './signal-message/SignalAndMessage.vue';
import ElementListeners from './listeners/ElementListeners.vue';
import ElementProperties from './properties/ElementProperties.vue';
// import ElementForm from './form/ElementForm.vue'
import UserTaskListeners from './listeners/UserTaskListeners.vue';
import ElementMultiInstance from './multi-instance/ElementMultiInstance.vue';
import ElementOtherConfig from './other/ElementOtherConfig.vue';
import ElementProperties from './properties/ElementProperties.vue';
import SignalAndMassage from './signal-message/SignalAndMessage.vue';
import { getTaskCollapseItemName, isTaskCollapseItemShow } from './task/data';
import ElementTask from './task/ElementTask.vue';
import TimeEventConfig from './time-event-config/TimeEventConfig.vue';
import { ref, watch, onMounted, onBeforeUnmount, provide, nextTick } from 'vue';
defineOptions({ name: 'MyPropertiesPanel' });
@@ -206,7 +78,7 @@ const initBpmnInstances = () => {
// 检查所有实例是否都存在
const allInstancesExist = Object.values(instances).every(
(instance) => instance,
Boolean,
);
if (allInstancesExist) {
const w = window as any;
@@ -296,7 +168,7 @@ const initFormOnChanged = (element) => {
type: ${activatedElement.businessObject.$type}
----------
`);
console.log('businessObject: ', activatedElement.businessObject);
console.log('businessObject:', activatedElement.businessObject);
bpmnInstances().bpmnElement = activatedElement;
bpmnElement.value = activatedElement;
elementId.value = activatedElement.id;
@@ -307,7 +179,7 @@ const initFormOnChanged = (element) => {
conditionFormVisible.value = !!(
elementType.value === 'SequenceFlow' &&
activatedElement.source &&
activatedElement.source.type.indexOf('StartEvent') === -1
!activatedElement.source.type.indexOf('StartEvent') === -1
);
formVisible.value =
elementType.value === 'UserTask' || elementType.value === 'StartEvent';
@@ -338,19 +210,30 @@ function updateNode() {
const element = elementRegistry.get(props.businessObject.id);
if (!element) return;
let timerDef = moddle.create('bpmn:TimerEventDefinition', {});
if (type.value === 'time') {
timerDef.timeDate = moddle.create('bpmn:FormalExpression', {
body: condition.value,
});
} else if (type.value === 'duration') {
timerDef.timeDuration = moddle.create('bpmn:FormalExpression', {
body: condition.value,
});
} else if (type.value === 'cycle') {
const timerDef = moddle.create('bpmn:TimerEventDefinition', {});
switch (type.value) {
case 'cycle': {
timerDef.timeCycle = moddle.create('bpmn:FormalExpression', {
body: condition.value,
});
break;
}
case 'duration': {
timerDef.timeDuration = moddle.create('bpmn:FormalExpression', {
body: condition.value,
});
break;
}
case 'time': {
timerDef.timeDate = moddle.create('bpmn:FormalExpression', {
body: condition.value,
});
break;
}
// No default
}
modeling.updateModdleProperties(element, element.businessObject, {
@@ -379,3 +262,335 @@ function syncFromBusinessObject() {
onMounted(syncFromBusinessObject);
watch(() => props.businessObject, syncFromBusinessObject, { deep: true });
</script>
<template>
<div
class="process-panel__container"
:style="{ width: `${width}px`, maxHeight: '600px' }"
>
<Collapse v-model:active-key="activeTab" v-if="isReady">
<CollapsePanel key="base" header="常规">
<template #title>
<Icon icon="ant-design:info-circle-filled" />
常规
</template>
<ElementBaseInfo
:id-edit-disabled="idEditDisabled"
:business-object="elementBusinessObject"
:type="elementType"
:model="model"
/>
</CollapsePanel>
<CollapsePanel
key="message"
v-if="elementType === 'Process'"
>
<template #title>
<Icon icon="ant-design:message-filled" />
消息与信号
</template>
<SignalAndMassage />
</CollapsePanel>
<CollapsePanel
key="condition"
v-if="conditionFormVisible"
>
<template #title>
<Icon icon="ant-design:swap-right" />
流转条件
</template>
<FlowCondition
:business-object="elementBusinessObject"
:type="elementType"
/>
</CollapsePanel>
<CollapsePanel key="form" v-if="formVisible">
<template #title>
<Icon icon="ant-design:unordered-list-outlined" />
表单
</template>
<element-form :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel
key="task"
v-if="isTaskCollapseItemShow(elementType)"
>
<template #title>
<Icon icon="ant-design:check-circle-filled" />
{{ getTaskCollapseItemName(elementType) }}
</template>
<ElementTask :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel
key="multiInstance"
v-if="elementType.includes('Task')"
>
<template #title>
<Icon icon="ant-design:question-circle-filled" />
多人审批方式
</template>
<ElementMultiInstance
:id="elementId"
:business-object="elementBusinessObject"
:type="elementType"
/>
</CollapsePanel>
<CollapsePanel key="listeners">
<template #title>
<Icon icon="ant-design:bell-filled" />
执行监听器
</template>
<ElementListeners :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel
key="taskListeners"
v-if="elementType === 'UserTask'"
>
<template #title>
<Icon icon="ant-design:bell-filled" />
任务监听器
</template>
<UserTaskListeners :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel key="extensions">
<template #title>
<Icon icon="ant-design:plus-circle-filled" />
扩展属性
</template>
<ElementProperties :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel key="other">
<template #title>
<Icon icon="ant-design:swap-right" />
其他
</template>
<ElementOtherConfig :id="elementId" />
</CollapsePanel>
<CollapsePanel key="customConfig">
<template #title>
<Icon icon="ant-design:tool-filled" />
自定义配置
</template>
<element-custom-config
:id="elementId"
:type="elementType"
:business-object="elementBusinessObject"
/>
</CollapsePanel>
<!-- 新增的时间事件配置项 -->
<CollapsePanel
key="timeEvent"
v-if="elementType === 'IntermediateCatchEvent'"
>
<template #title>
<Icon icon="ant-design:clock-circle-filled" />
时间事件
</template>
<TimeEventConfig
:business-object="bpmnElement.value?.businessObject"
:key="elementId"
/>
</CollapsePanel>
</Collapse>
</div>
</template>.includes('StartEvent')
);
formVisible.value =
elementType.value === 'UserTask' || elementType.value === 'StartEvent';
} catch (error) {
console.error('初始化表单数据失败:', error);
}
};
onBeforeUnmount(() => {
const w = window as any;
w.bpmnInstances = null;
isReady.value = false;
});
watch(
() => elementId.value,
() => {
activeTab.value = 'base';
},
);
function updateNode() {
const moddle = window.bpmnInstances?.moddle;
const modeling = window.bpmnInstances?.modeling;
const elementRegistry = window.bpmnInstances?.elementRegistry;
if (!moddle || !modeling || !elementRegistry) return;
const element = elementRegistry.get(props.businessObject.id);
if (!element) return;
const timerDef = moddle.create('bpmn:TimerEventDefinition', {});
switch (type.value) {
case 'time': {
timerDef.timeDate = moddle.create('bpmn:FormalExpression', {
body: condition.value,
});
break;
}
case 'duration': {
timerDef.timeDuration = moddle.create('bpmn:FormalExpression', {
body: condition.value,
});
break;
}
case 'cycle': {
timerDef.timeCycle = moddle.create('bpmn:FormalExpression', {
body: condition.value,
});
break;
}
// No default
}
modeling.updateModdleProperties(element, element.businessObject, {
eventDefinitions: [timerDef],
});
}
// 初始化和监听
function syncFromBusinessObject() {
if (props.businessObject) {
const timerDef = (props.businessObject.eventDefinitions || [])[0];
if (timerDef) {
if (timerDef.timeDate) {
type.value = 'time';
condition.value = timerDef.timeDate.body;
} else if (timerDef.timeDuration) {
type.value = 'duration';
condition.value = timerDef.timeDuration.body;
} else if (timerDef.timeCycle) {
type.value = 'cycle';
condition.value = timerDef.timeCycle.body;
}
}
}
}
onMounted(syncFromBusinessObject);
watch(() => props.businessObject, syncFromBusinessObject, { deep: true });
</script>
<template>
<div
class="process-panel__container"
:style="{ width: `${width}px`, maxHeight: '600px' }"
>
<Collapse v-model:active-key="activeTab" v-if="isReady">
<CollapsePanel key="base" header="常规">
<template #title>
<Icon icon="ant-design:info-circle-filled" />
常规
</template>
<ElementBaseInfo
:id-edit-disabled="idEditDisabled"
:business-object="elementBusinessObject"
:type="elementType"
:model="model"
/>
</CollapsePanel>
<CollapsePanel key="message" v-if="elementType === 'Process'">
<template #title>
<Icon icon="ant-design:message-filled" />
消息与信号
</template>
<SignalAndMassage />
</CollapsePanel>
<CollapsePanel key="condition" v-if="conditionFormVisible">
<template #title>
<Icon icon="ant-design:swap-right" />
流转条件
</template>
<FlowCondition
:business-object="elementBusinessObject"
:type="elementType"
/>
</CollapsePanel>
<CollapsePanel key="form" v-if="formVisible">
<template #title>
<Icon icon="ant-design:unordered-list-outlined" />
表单
</template>
<element-form :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel key="task" v-if="isTaskCollapseItemShow(elementType)">
<template #title>
<Icon icon="ant-design:check-circle-filled" />
{{ getTaskCollapseItemName(elementType) }}
</template>
<ElementTask :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel
key="multiInstance"
v-if="elementType.includes('Task')"
>
<template #title>
<Icon icon="ant-design:question-circle-filled" />
多人审批方式
</template>
<ElementMultiInstance
:id="elementId"
:business-object="elementBusinessObject"
:type="elementType"
/>
</CollapsePanel>
<CollapsePanel key="listeners">
<template #title>
<Icon icon="ant-design:bell-filled" />
执行监听器
</template>
<ElementListeners :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel key="taskListeners" v-if="elementType === 'UserTask'">
<template #title>
<Icon icon="ant-design:bell-filled" />
任务监听器
</template>
<UserTaskListeners :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel key="extensions">
<template #title>
<Icon icon="ant-design:plus-circle-filled" />
扩展属性
</template>
<ElementProperties :id="elementId" :type="elementType" />
</CollapsePanel>
<CollapsePanel key="other">
<template #title>
<Icon icon="ant-design:swap-right" />
其他
</template>
<ElementOtherConfig :id="elementId" />
</CollapsePanel>
<CollapsePanel key="customConfig">
<template #title>
<Icon icon="ant-design:tool-filled" />
自定义配置
</template>
<element-custom-config
:id="elementId"
:type="elementType"
:business-object="elementBusinessObject"
/>
</CollapsePanel>
<!-- 新增的时间事件配置项 -->
<CollapsePanel
key="timeEvent"
v-if="elementType === 'IntermediateCatchEvent'"
>
<template #title>
<Icon icon="ant-design:clock-circle-filled" />
时间事件
</template>
<TimeEventConfig
:business-object="bpmnElement.value?.businessObject"
:key="elementId"
/>
</CollapsePanel>
</Collapse>
</div>
</template>

View File

@@ -2,7 +2,6 @@
import { onBeforeUnmount, reactive, ref, toRaw, watch } from 'vue';
import { Form, FormItem, Input } from 'ant-design-vue';
import type { FormInstance, Rule } from 'ant-design-vue';
defineOptions({ name: 'ElementBaseInfo' });
@@ -24,7 +23,6 @@ interface Model {
[key: string]: any;
}
const formRef = ref<FormInstance>();
const needProps = ref<Record<string, any>>({});
const bpmnElement = ref<any>();
const elementBaseInfo = ref<BusinessObject>({} as any);

View File

@@ -1,7 +1,8 @@
<script lang="ts" setup>
import { defineOptions, defineProps, ref, watch } from 'vue';
import type { Component } from 'vue';
import { defineOptions, defineProps, ref, watch } from 'vue';
import { CustomConfigMap } from './data';
defineOptions({ name: 'ElementCustomConfig' });
@@ -38,7 +39,10 @@ watch(
val +=
props.businessObject.eventDefinitions[0]?.$type.split(':')[1] || '';
}
customConfigComponent.value = (CustomConfigMap as Record<string, { component: Component }>)[val]?.component;
// @ts-ignore
customConfigComponent.value = (
CustomConfigMap as Record<string, { component: Component }>
)[val]?.component;
}
},
{ immediate: true },

View File

@@ -11,12 +11,10 @@ import {
import {
Divider,
Form,
FormItem,
InputNumber,
Radio,
RadioGroup,
RadioButton,
RadioGroup,
Select,
SelectOption,
Switch,

View File

@@ -2,7 +2,6 @@
import { nextTick, onBeforeUnmount, ref, toRaw, watch } from 'vue';
import { Form, Input, Select } from 'ant-design-vue';
const { TextArea } = Input;
defineOptions({ name: 'FlowCondition' });
@@ -17,6 +16,8 @@ const props = defineProps({
},
});
const { TextArea } = Input;
const flowConditionForm = ref<any>({});
const bpmnElement = ref();
const bpmnElementSource = ref();

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { computed, inject, nextTick, onMounted, ref, toRaw, watch } from 'vue';
import { Form, FormItem, Select, Table, TableColumn, Button, Divider, Drawer, Input, Modal } from 'ant-design-vue';
import { Form, FormItem, Select } from 'ant-design-vue';
import { getFormSimpleList } from '#/api/bpm/form';
@@ -319,7 +319,9 @@ watch(
@change="_updateElementBusinessKey"
allow-clear
>
<Select.Option v-for="i in fieldList" :key="i.id" :value="i.id">{{ i.label }}</Select.Option>
<Select.Option v-for="i in fieldList" :key="i.id" :value="i.id">
{{ i.label }}
</Select.Option>
<Select.Option value=""></Select.Option>
</Select>
</FormItem>

View File

@@ -1,12 +1,7 @@
<script lang="ts" setup>
import { inject, nextTick, ref, watch } from 'vue';
import {
IconifyIcon,
MenuOutlined,
PlusOutlined,
SelectOutlined,
} from '@vben/icons';
import { IconifyIcon, PlusOutlined } from '@vben/icons';
import {
Button,

View File

@@ -71,8 +71,8 @@ const select = async (row: BpmProcessListenerApi.ProcessListener) => {
:pagination="false"
:scroll="{ x: 'max-content' }"
>
<Table.Column title="名字" align="center" dataIndex="name" />
<Table.Column title="类型" align="center" dataIndex="type">
<Table.Column title="名字" align="center" data-index="name" />
<Table.Column title="类型" align="center" data-index="type">
<template #default="{ record }">
<DictTag
:type="DICT_TYPE.BPM_PROCESS_LISTENER_TYPE"
@@ -80,8 +80,8 @@ const select = async (row: BpmProcessListenerApi.ProcessListener) => {
/>
</template>
</Table.Column>
<Table.Column title="事件" align="center" dataIndex="event" />
<Table.Column title="值类型" align="center" dataIndex="valueType">
<Table.Column title="事件" align="center" data-index="event" />
<Table.Column title="值类型" align="center" data-index="valueType">
<template #default="{ record }">
<DictTag
:type="DICT_TYPE.BPM_PROCESS_LISTENER_VALUE_TYPE"
@@ -89,7 +89,7 @@ const select = async (row: BpmProcessListenerApi.ProcessListener) => {
/>
</template>
</Table.Column>
<Table.Column title="值" align="center" dataIndex="value" />
<Table.Column title="值" align="center" data-index="value" />
<Table.Column title="操作" align="center">
<template #default="{ record }">
<Button type="primary" @click="select(record)"> 选择 </Button>

View File

@@ -1,36 +1,8 @@
<template>
<div class="panel-tab__content">
<Form :label-col="{ span: 9 }" :wrapper-col="{ span: 15 }">
<!-- add by 芋艿由于异步延续暂时用不到所以这里 display none -->
<Form.Item label="异步延续" style="display: none">
<Checkbox
v-model:checked="taskConfigForm.asyncBefore"
@change="changeTaskAsync"
>
异步前
</Checkbox>
<Checkbox
v-model:checked="taskConfigForm.asyncAfter"
@change="changeTaskAsync"
>
异步后
</Checkbox>
<Checkbox
v-model:checked="taskConfigForm.exclusive"
v-if="taskConfigForm.asyncAfter || taskConfigForm.asyncBefore"
@change="changeTaskAsync"
>
排除
</Checkbox>
</Form.Item>
<component :is="witchTaskComponent" v-bind="$props" />
</Form>
</div>
</template>
<script lang="ts" setup>
import { Form } from 'ant-design-vue';
import { ref, watch } from 'vue';
import { Form } from 'ant-design-vue';
import { installedComponent } from './data';
defineOptions({ name: 'ElementTaskConfig' });
@@ -81,3 +53,33 @@ watch(
{ immediate: true },
);
</script>
<template>
<div class="panel-tab__content">
<Form :label-col="{ span: 9 }" :wrapper-col="{ span: 15 }">
<!-- add by 芋艿由于异步延续暂时用不到所以这里 display none -->
<Form.Item label="异步延续" style="display: none">
<Checkbox
v-model:checked="taskConfigForm.asyncBefore"
@change="changeTaskAsync"
>
异步前
</Checkbox>
<Checkbox
v-model:checked="taskConfigForm.asyncAfter"
@change="changeTaskAsync"
>
异步后
</Checkbox>
<Checkbox
v-model:checked="taskConfigForm.exclusive"
v-if="taskConfigForm.asyncAfter || taskConfigForm.asyncBefore"
@change="changeTaskAsync"
>
排除
</Checkbox>
</Form.Item>
<component :is="witchTaskComponent" v-bind="$props" />
</Form>
</div>
</template>