You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
flow-playform-react/docs/add-loop-node-template.md

3.9 KiB

新增 LOOP 类节点模板(基于当前重构版本)

这份模板用于你现在这套 Flow CoreloopFactory + nodeRegistry + useFlowCallbacks)下,新增“成对节点/分支类”能力。

适用场景:像 LOOP_START/LOOP_END 这种需要一次创建多个节点和连边的节点族。


1. 推荐做法(先看)

不要在 useFlowCallbacks 里直接手写大段节点/边构造。按现在的模式:

  1. src/utils/flow/ 新增工厂(如 xxxFactory.ts
  2. 暴露:
    • 节点对(或节点组)构造函数
    • 组内连接边构造函数
    • 外部插入连接边构造函数(可选)
  3. useFlowCallbacks 只负责调用工厂 + setNodes/setEdges + snapshot

2. 工厂模板

路径示例src/utils/flow/retryFactory.ts

import { Edge } from '@xyflow/react';

export const createRetryNodePair = (position: { x: number; y: number }) => {
  const retryStartNode: any = {
    id: `RETRY_START-${Date.now()}`,
    type: 'RETRY',
    position: { x: position.x, y: position.y },
    data: {
      title: '重试开始',
      type: 'RETRY_START',
      parameters: {
        apiIns: [{ name: 'start', desc: '', dataType: '', defaultValue: '' }],
        apiOuts: [{ name: 'done', desc: '', dataType: '', defaultValue: '' }],
        dataIns: [],
        dataOuts: [],
      },
      component: {},
    },
  };

  const retryEndNode: any = {
    id: `RETRY_END-${Date.now()}`,
    type: 'RETRY',
    position: { x: position.x + 400, y: position.y },
    data: {
      title: '重试结束',
      type: 'RETRY_END',
      parameters: {
        apiIns: [
          { name: 'continue', desc: '', dataType: '', defaultValue: '' },
        ],
        apiOuts: [{ name: 'break', desc: '', dataType: '', defaultValue: '' }],
        dataIns: [],
        dataOuts: [],
      },
      component: {
        type: 'RETRY_END',
        customDef: JSON.stringify({ retryStartNodeId: retryStartNode.id }),
        retryStartNodeId: retryStartNode.id,
      },
    },
  };

  retryStartNode.data.component = {
    type: 'RETRY_START',
    customDef: JSON.stringify({ retryEndNodeId: retryEndNode.id }),
  };

  return { retryStartNode, retryEndNode };
};

export const createRetryGroupEdge = (
  retryStartId: string,
  retryEndId: string
): Edge => ({
  id: `${retryStartId}-${retryEndId}-group`,
  source: retryStartId,
  target: retryEndId,
  sourceHandle: `${retryStartId}-group`,
  targetHandle: `${retryEndId}-group`,
  type: 'custom',
});

3. useFlowCallbacks 接入模板

import {
  createRetryNodePair,
  createRetryGroupEdge,
} from '@/utils/flow/retryFactory';
import { ensureNodeTypeRegistered } from '@/utils/flow/nodeRegistry';
import { dispatchFlowSnapshotAsync } from '@/utils/flow/snapshot';

const addRetryNodeWithStartEnd = useCallback(
  (position: { x: number; y: number }) => {
    const { retryStartNode, retryEndNode } = createRetryNodePair(position);
    const groupEdge = createRetryGroupEdge(retryStartNode.id, retryEndNode.id);

    ensureNodeTypeRegistered('RETRY', '重试');

    setNodes((nds) => {
      const newNodes = [...nds, retryStartNode, retryEndNode];
      dispatchFlowSnapshotAsync({
        nodes: [...newNodes],
        edges: [...edges, groupEdge],
      });
      return newNodes;
    });

    setEdges((eds) => [...eds, groupEdge]);
  },
  [edges]
);

4. 类型映射模板

文件src/utils/flow/nodeRegistry.ts

case 'RETRY':
  return LoopNode; // 或你自己的专用节点组件

早期可复用 LoopNode/LocalNode,后续再拆专用展示组件。


5. 新增“边上插入该类节点”的模板(可选)

如需在边上插入节点组,建议像 LOOP 一样提供工厂函数:

  • createXxxNodePair(position)
  • createXxxGroupEdge(startId, endId)
  • createXxxInsertConnectionEdges({ sourceId, sourceHandle, targetId, targetHandle, ... })

这样 addNodeOnEdge 里只拼装调用,不再手写边结构。