Просмотр исходного кода

task-188 一体机流程配置添加 边上插入节点 功能

cc12458 6 месяцев назад
Родитель
Сommit
1ae6861329

+ 34 - 0
src/libs/logic-flow/index.ts

@@ -15,6 +15,8 @@ export { VLogicFlow };
 
 export type GraphData = LogicFlow.GraphConfigData;
 
+const Threshold = 100;
+
 export default function init(
   lf: LogicFlowInstance,
   config?: {
@@ -46,6 +48,38 @@ export default function init(
     }, 300);
   });
 
+  let insertNodeInPolylineTime = 0;
+  let delayInsertNodeInPolyline: ReturnType<typeof setTimeout>;
+  let cacheEdgePoint: string[] | void = void 0
+  listener('edge:delete', () => {
+    insertNodeInPolylineTime = Date.now();
+    cacheEdgePoint = [];
+    setTimeout(() => { cacheEdgePoint = void 0; }, Threshold * 2)
+  });
+  listener('edge:add', (event) => {
+    clearTimeout(delayInsertNodeInPolyline);
+    if (Array.isArray(cacheEdgePoint)) cacheEdgePoint.push(event.data.sourceNodeId, event.data.targetNodeId);
+    if (Date.now() - insertNodeInPolylineTime < Threshold)
+      delayInsertNodeInPolyline = setTimeout(() => {
+        if (Array.isArray(cacheEdgePoint) && cacheEdgePoint.length === 4 && cacheEdgePoint[1] === cacheEdgePoint[2]) {
+          const id = cacheEdgePoint[1];
+          lf.graphModel.eventCenter.emit('node:insert', { data: lf.getNodeModelById(id)?.getData() });
+        }
+      }, Threshold);
+  });
+  /**
+   * 拖拽节点插入边报错
+   */
+  let notConnectionId: string | void;
+  listener('connection:not-allowed', (event) => {
+    notConnectionId = event.data.id;
+    setTimeout(() => { notConnectionId = void 0 }, Threshold);
+    clearTimeout(delayInsertNodeInPolyline);
+  });
+  listener('node:dnd-add', (event) => {
+    if (event.data.id === notConnectionId) setTimeout(() => lf.graphModel.eventCenter.emit('node:delete', event as any), Threshold)
+  });
+
   /**
    * 增强功能
    * @description 渐进连线成功触发 [edge:proximity-connect] 事件

+ 33 - 9
src/pages/aio/flow-config/index.vue

@@ -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,6 +54,7 @@ let instance!: VLogicFlowInstance;
 let isUnmounted = false;
 const hasInstance = () => !isUnmounted && !!instance && !!instance.lf;
 // end
+let oldValue: FlowRequestData;
 const init = (lf: LogicFlowInstance): void => {
   instance = VLogicFlowInit(lf, {
     register: [{ category: 'node', type: 'FlowNode', view: FlowNodeView, model: FlowNodeViewModel }],
@@ -63,7 +64,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 +116,30 @@ 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,
+    });
   });
 };
 
@@ -220,7 +243,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 已销毁'));
@@ -268,9 +291,10 @@ const validate = (tips = true) => {
       [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 数据: ', );
   } catch (error: any) {
     if (tips) {
       notification.error({

+ 15 - 7
src/pages/aio/flow-config/nodes/FlowNode.model.ts

@@ -14,7 +14,7 @@ export default class FlowNodeModel extends RectNodeModel<FlowNodeProperties> {
     if (source === target) return { isAllPass: false, msg: `节点不能连接自身` };
 
     // 获取当前节点禁止直接连接
-    const forbidDirectTarget = source.getProperties().forbidDirectTarget ?? [];
+    const forbidDirectTarget = getProperties(source).forbidDirectTarget ?? [];
     if (forbidDirectTarget.includes(target.id))
       return {
         isAllPass: false,
@@ -22,7 +22,7 @@ export default class FlowNodeModel extends RectNodeModel<FlowNodeProperties> {
       };
 
     // 获取目标节点 forbidSource
-    let forbidSource = target.getProperties().forbidSource ?? [];
+    let forbidSource = getProperties(target).forbidSource ?? [];
     if (forbidSource.includes(source.id)) return { isAllPass: false, msg: `目标 [${target.text.value}] 节点不能在当前 [${source.text.value}] 节点之后` };
 
     // 获取当前节点的上游节点
@@ -47,9 +47,9 @@ export default class FlowNodeModel extends RectNodeModel<FlowNodeProperties> {
     do {
       const targetId = next.shift()!;
       visited.add(targetId);
-      const directNodes = target.graphModel.getNodeOutgoingNode(targetId);
+      const directNodes = this.graphModel.getNodeOutgoingNode(targetId);
       for (const directNode of directNodes) {
-        forbidSource = (directNode.getProperties().forbidSource as string[]) ?? [];
+        forbidSource = (getProperties(directNode).forbidSource as string[]) ?? [];
         if (forbidSource.includes(source.id))
           return {
             isAllPass: false,
@@ -72,7 +72,7 @@ export default class FlowNodeModel extends RectNodeModel<FlowNodeProperties> {
       };
 
     // 获取目标节点 onlySource
-    const onlySource = (this.getProperties().onlySource as string[]) ?? [];
+    const onlySource = (getProperties(this).onlySource as string[]) ?? [];
     if (onlySource.length && !onlySource.includes(args[0].id))
       return {
         isAllPass: false,
@@ -80,7 +80,7 @@ export default class FlowNodeModel extends RectNodeModel<FlowNodeProperties> {
       };
 
     // 当前节点是否开始
-    if (this.getProperties().start)
+    if (getProperties(this).start)
       return {
         isAllPass: false,
         msg: `当前 [${this.text.value}] 为开始节点不允许输入`,
@@ -100,7 +100,7 @@ export default class FlowNodeModel extends RectNodeModel<FlowNodeProperties> {
       };
 
     // 当前节点是否结束
-    if (this.getProperties().finish && args[0].id !== END_ID)
+    if (getProperties(this).finish && args[0].id !== END_ID)
       return {
         isAllPass: false,
         msg: `当前 [${this.text.value}] 为结束节点不允许输出`,
@@ -109,3 +109,11 @@ export default class FlowNodeModel extends RectNodeModel<FlowNodeProperties> {
     return this.isAllowConnected(this, args[0], args[1], args[2], args[3]);
   }
 }
+
+function getProperties(model: FlowNodeModel | { properties: FlowNodeProperties }) {
+  try {
+    return model instanceof FlowNodeModel ? model.getProperties() : model?.properties ?? {};
+  } catch {
+    return model?.properties ?? {};
+  }
+}

+ 2 - 2
src/pages/aio/flow-config/panel/StartPanel.vue

@@ -93,8 +93,8 @@ const updateHomeType = () => {
   <div>
     <a-form-item label="起始页">
       <a-radio-group v-model:value="model.homeType" @change="updateHomeType()">
-        <a-radio :value="1">预设:默认</a-radio>
-        <a-radio :value="2">预设:萧山</a-radio>
+        <a-radio :value="1">默认</a-radio>
+        <!--<a-radio :value="2">萧山</a-radio>-->
         <a-radio :value="99">
           <template v-if="model.homeType === 99">
             <div class="flex-none">自定义:</div>

+ 1 - 0
src/pages/aio/flow-config/tool.ts

@@ -66,6 +66,7 @@ export function toFlowRequestData(gather: Gather, data: Record<string, any>) {
       technicalSupporter: string;
       tabletDrainageImage: string;
     }),
+    timestamp: Date.now(),
   } as const;
 }