|
|
@@ -4,11 +4,11 @@ import { h } from 'vue';
|
|
|
import { tryOnUnmounted, useParentElement } from '@vueuse/core';
|
|
|
import { VxeUI } from 'vxe-pc-ui';
|
|
|
|
|
|
-import { notification } from 'ant-design-vue';
|
|
|
+import { Button, notification } from 'ant-design-vue';
|
|
|
import { ArrowDownOutlined, ArrowRightOutlined, CloseOutlined, FullscreenExitOutlined, MenuOutlined, SearchOutlined, ToolOutlined } from '@ant-design/icons-vue';
|
|
|
|
|
|
import type { CallbackArgs } from '@logicflow/core';
|
|
|
-import { ProximityConnect } from '@logicflow/extension';
|
|
|
+import { ProximityConnect, InsertNodeInPolyline } from '@logicflow/extension';
|
|
|
import { Dagre } from '@logicflow/layout';
|
|
|
import VLogicFlowInit, { type LogicFlowInstance, type LogicFlowOptions, VLogicFlow, type VLogicFlowInstance } from '@/libs/logic-flow';
|
|
|
|
|
|
@@ -33,7 +33,7 @@ const options = reactive<LogicFlowOptions>({
|
|
|
snapToGrid: true,
|
|
|
snapline: false,
|
|
|
textEdit: false,
|
|
|
- plugins: [Dagre, ProximityConnect],
|
|
|
+ plugins: [Dagre, ProximityConnect, InsertNodeInPolyline],
|
|
|
pluginsOptions: {
|
|
|
proximityConnect: {
|
|
|
enable: true, // 插件是否启用
|
|
|
@@ -54,7 +54,10 @@ let instance!: VLogicFlowInstance;
|
|
|
let isUnmounted = false;
|
|
|
const hasInstance = () => !isUnmounted && !!instance && !!instance.lf;
|
|
|
// end
|
|
|
+let oldValue: FlowRequestData;
|
|
|
+const maxPanelHeight = ref(0);
|
|
|
const init = (lf: LogicFlowInstance): void => {
|
|
|
+ maxPanelHeight.value = lf.container.getBoundingClientRect().height - 80;
|
|
|
instance = VLogicFlowInit(lf, {
|
|
|
register: [{ category: 'node', type: 'FlowNode', view: FlowNodeView, model: FlowNodeViewModel }],
|
|
|
});
|
|
|
@@ -63,7 +66,7 @@ const init = (lf: LogicFlowInstance): void => {
|
|
|
watchPostEffect(() => {
|
|
|
const value = requestData.value;
|
|
|
console.log('[AioFlowConfig] 接收 request-data 数据', value);
|
|
|
- update(value);
|
|
|
+ if (!oldValue || oldValue?.timestamp !== value?.timestamp) update(value);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
@@ -115,8 +118,51 @@ const init = (lf: LogicFlowInstance): void => {
|
|
|
if (index > -1) nodes.value.splice(index, 1);
|
|
|
});
|
|
|
// @ts-ignore
|
|
|
- instance.listener('edge:proximity-connect', () => {
|
|
|
+ /*instance.listener('edge:proximity-connect', () => {
|
|
|
updateLayout();
|
|
|
+ });*/
|
|
|
+ // @ts-ignore
|
|
|
+ instance.listener('node:insert', (event: CallbackArgs<'data'>) => {
|
|
|
+ notification.success({
|
|
|
+ key: 'connection:node:insert',
|
|
|
+ message: '插入节点成功',
|
|
|
+ description: () =>
|
|
|
+ h(
|
|
|
+ Button,
|
|
|
+ {
|
|
|
+ type: 'primary',
|
|
|
+ size: 'small',
|
|
|
+ onClick: () => {
|
|
|
+ validate().catch();
|
|
|
+ notification.close('connection:node:insert');
|
|
|
+ },
|
|
|
+ },
|
|
|
+ { default: () => '检测连接' }
|
|
|
+ ),
|
|
|
+ top: '12px',
|
|
|
+ getContainer: () => (el.value as HTMLElement) ?? document.body,
|
|
|
+ });
|
|
|
+ });
|
|
|
+ // @ts-ignore
|
|
|
+ instance.listener('connection:quick', (event: any) => {
|
|
|
+ const add = (id: string) => {
|
|
|
+ for (const group of nodeGroup.value) {
|
|
|
+ const node = group.find((node) => node.id === id);
|
|
|
+ if (node) return instance.lf.addNode(node);
|
|
|
+ }
|
|
|
+ return void 0;
|
|
|
+ };
|
|
|
+ const { sourceNodeId, targetNodeId } = event;
|
|
|
+ const source = instance.lf.getNodeModelById(sourceNodeId) ?? add(sourceNodeId);
|
|
|
+ const target = instance.lf.getNodeModelById(targetNodeId) ?? add(targetNodeId);
|
|
|
+ if (source && target) {
|
|
|
+ const sourceEdge = instance.lf.getNodeOutgoingEdge(sourceNodeId);
|
|
|
+ const targetEdge = instance.lf.getNodeIncomingEdge(targetNodeId);
|
|
|
+ for (const edge of sourceEdge) instance.lf.deleteEdge(edge.id);
|
|
|
+ for (const edge of targetEdge) instance.lf.deleteEdge(edge.id);
|
|
|
+ instance.lf.addEdge({ sourceNodeId, targetNodeId });
|
|
|
+ updateLayout();
|
|
|
+ }
|
|
|
});
|
|
|
};
|
|
|
|
|
|
@@ -220,7 +266,7 @@ const update = (data?: FlowRequestData) => {
|
|
|
|
|
|
instance.lf.renderRawData(graph);
|
|
|
updateLayout('TB');
|
|
|
- if (graph.nodes && graph.nodes.length > 2) updateLayout('center');
|
|
|
+ if (graph.nodes && graph.nodes.length > 2) setTimeout(() => updateLayout('center'), 100);
|
|
|
};
|
|
|
const validate = (tips = true) => {
|
|
|
if (!hasInstance()) return Promise.reject(new Error('LogicFlow 已销毁'));
|
|
|
@@ -259,18 +305,19 @@ const validate = (tips = true) => {
|
|
|
|
|
|
const start = instance.lf.getNodeModelById(Node.ID_Start);
|
|
|
|
|
|
- let gather;
|
|
|
+ let gather: Gather;
|
|
|
const { promise, resolve, reject } = withResolvers<{ gather: Gather; data?: FlowRequestData; message?: string }>();
|
|
|
try {
|
|
|
- gather = map(start);
|
|
|
+ gather = map(start).sort((g1, g2) => g1.level - g2.level || +(g1.targetNodeId === Node.ID_Back) - +(g2.targetNodeId === Node.ID_Back));
|
|
|
|
|
|
const data: Record<string, any> = {
|
|
|
[Node.ID_Start]: instance.lf.getNodeModelById(Node.ID_Start)?.getProperties().requestData,
|
|
|
};
|
|
|
for (const group of nodeGroup.value) for (const node of group) if (node.id) data[node.id] = node.properties?.requestData;
|
|
|
- requestData.value = toFlowRequestData(gather, data);
|
|
|
- resolve({ gather, data: requestData.value });
|
|
|
- console.log('[AioFlowConfig] 更新 request-data 数据: ', requestData.value);
|
|
|
+ oldValue = toFlowRequestData(gather, data);
|
|
|
+ requestData.value = oldValue;
|
|
|
+ resolve({ gather, data: oldValue });
|
|
|
+ console.log('[AioFlowConfig] 更新 request-data 数据: ', oldValue);
|
|
|
} catch (error: any) {
|
|
|
if (tips) {
|
|
|
notification.error({
|
|
|
@@ -286,9 +333,12 @@ const validate = (tips = true) => {
|
|
|
}
|
|
|
|
|
|
if (Array.isArray(gather) && gather.length) {
|
|
|
+ const last = gather.at(-1);
|
|
|
+ const notOpenEdgeId = last.targetNodeId === Node.ID_Back ? last.edgeId : void 0;
|
|
|
for (const { edgeId } of gather) {
|
|
|
instance.lf.setProperties(edgeId, { isAnimation: true });
|
|
|
- instance.lf.openEdgeAnimation(edgeId);
|
|
|
+ if (notOpenEdgeId === edgeId) instance.lf.closeEdgeAnimation(notOpenEdgeId);
|
|
|
+ else instance.lf.openEdgeAnimation(edgeId);
|
|
|
}
|
|
|
} else {
|
|
|
for (const edge of instance.lf.getGraphRawData().edges) {
|
|
|
@@ -326,7 +376,7 @@ defineExpose({
|
|
|
</template>
|
|
|
</a-button>
|
|
|
<template #overlay>
|
|
|
- <a-card size="small" style="width: 370px">
|
|
|
+ <a-card size="small" style="width: 370px; overflow-y: auto;" :style="{ maxHeight: maxPanelHeight + 'px' }">
|
|
|
<div class="flex justify-between m-y-2" v-for="(group, g) in nodeGroup" :key="g">
|
|
|
<FlowNodeComponent
|
|
|
:class="{ selected: dragPanelNodeId === node.id, disabled: getPanelNodeDisabled(node) }"
|
|
|
@@ -338,6 +388,12 @@ defineExpose({
|
|
|
@mousedown="startDragPanelNode(node, $event)"
|
|
|
/>
|
|
|
</div>
|
|
|
+ <a-space direction="vertical" class="tips-wrapper">
|
|
|
+ <div>添加流程:从左侧拖入</div>
|
|
|
+ <div>编辑流程:单击</div>
|
|
|
+ <div>删除流程和连线:双击</div>
|
|
|
+ <div>连接流程:从上流节点中拖拽锚点到下流节点以连接</div>
|
|
|
+ </a-space>
|
|
|
</a-card>
|
|
|
</template>
|
|
|
</a-dropdown>
|
|
|
@@ -379,6 +435,12 @@ defineExpose({
|
|
|
left: 24px;
|
|
|
z-index: 1;
|
|
|
}
|
|
|
+.tips-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ padding: 4px;
|
|
|
+ color: rgba(0, 0, 0, 0.8);
|
|
|
+ border: 1px #bbbbbb dashed;
|
|
|
+}
|
|
|
.ant-float-btn-group {
|
|
|
position: absolute !important;
|
|
|
bottom: 24px;
|