useBasicDataTransform.ts 5.01 KB
import { Ref, toRaw, unref } from 'vue';
import { BasicNodeBindData, NodeData } from '../types/node';
import { Elements, GraphNode } from '@vue-flow/core';
import { RuleChainType } from '../types/ruleNode';
import { allComponents } from '../packages';
import { RuleNodeTypeEnum } from '../packages/index.type';
import { buildUUID } from '/@/utils/uuid';
import { isNullOrUnDef } from '/@/utils/is';
import { useAddNodes } from './useAddNodes';
import { useAddEdges } from './useAddEdges';
import { RuleChainEntityType } from '../enum/entity';

export function useBasicDataTransform() {
  const nodeConfigMap = new Map<string, NodeData>();

  function initNodeConfigMap() {
    for (const key of Object.keys(allComponents)) {
      const category = allComponents[key as RuleNodeTypeEnum];
      for (const nodeConfig of category.components) {
        const { clazz } = nodeConfig;
        nodeConfigMap.set(clazz, { config: nodeConfig, category: category.category });
      }
    }
  }

  function mergeData(data: NodeData['data'], nodeData: NodeData, node: GraphNode) {
    const { x: layoutX, y: layoutY } = node.computedPosition;

    return {
      debugMode: !!data?.debugMode,
      name: data?.name,
      type: nodeData.config?.clazz,
      configuration: toRaw(unref(data?.configuration)),
      additionalInfo: {
        description: data?.description,
        layoutX,
        layoutY,
      },
    };
  }

  function deconstructionConnection(
    ruleChain: Ref<RuleChainType> | RuleChainType,
    inputNodeId: string
  ) {
    const { connections, nodes, firstNodeIndex } = unref(ruleChain);
    const indexMap = new Map<number, BasicNodeBindData>();
    const groupByConnections = new Map<string, string[]>();
    const SOURCE_HANDLE = '__handle-right';
    const TARGET_HANDLE = '__handle-left';
    const SEPARATOR = ',';
    const edges: Elements = [];
    const { getAddedgesParams } = useAddEdges();

    nodes.forEach((item, index) => indexMap.set(index, item));

    (connections || []).forEach((item) => {
      const { fromIndex, toIndex, type } = item;
      const key = `${fromIndex}${SEPARATOR}${toIndex}`;
      if (!groupByConnections.has(key)) groupByConnections.set(key, []);

      const types = groupByConnections.get(key)!;
      types.push(type);
    });

    for (const [key, types] of Array.from(groupByConnections.entries())) {
      const [fromIndex, toIndex] = key.split(SEPARATOR);

      const sourceNode = indexMap.get(Number(fromIndex));
      const targetNode = indexMap.get(Number(toIndex));
      const source = sourceNode?.id?.id || buildUUID();
      const target = targetNode?.id?.id || buildUUID();
      const sourceHandle = `${source}${SOURCE_HANDLE}`;
      const targetHandle = `${target}${TARGET_HANDLE}`;

      edges.push(
        getAddedgesParams(
          {
            source,
            target,
            sourceHandle,
            targetHandle,
          },
          { type: types }
        )
      );
    }

    if (!isNullOrUnDef(firstNodeIndex)) {
      const targetNode = indexMap.get(firstNodeIndex);
      const source = inputNodeId;
      const target = targetNode?.id?.id || buildUUID();
      const sourceHandle = `${source}$${SOURCE_HANDLE}`;
      const targetHandle = `${target}${TARGET_HANDLE}`;
      edges.push(
        getAddedgesParams(
          {
            source,
            target,
            sourceHandle,
            targetHandle,
          },
          {}
        )
      );
    }

    return edges;
  }

  function genNewNodeByData(node: BasicNodeBindData, config: NodeData) {
    const { additionalInfo, configuration, debugMode, name, id } = node;
    const { layoutX, layoutY, description } = additionalInfo || {};
    const { getAddNodesParams } = useAddNodes();

    const value = getAddNodesParams(
      { x: layoutX!, y: layoutY! },
      {
        ...config,
        data: {
          configuration,
          debugMode,
          description,
          name,
        },
        created: !!id?.id,
      },
      {
        id: id?.id || buildUUID(),
      }
    );

    if (!id?.id) node.id = { id: value.id, entityType: RuleChainEntityType.RULE_NODE };

    return value;
  }

  function deconstructionNode(nodes: RuleChainType['nodes']) {
    const addNodes: Elements = [];
    for (const node of unref(nodes)) {
      const { type } = node;
      if (!type) continue;
      const nodeConfig = nodeConfigMap.get(type);

      if (!nodeConfig) {
        throw `No component configuration of type '${type}' was found`;
      }

      const newNode = genNewNodeByData(node, nodeConfig);

      addNodes.push(newNode);
    }
    return addNodes;
  }

  function deconstructionData(
    ruleChain: RuleChainType | Ref<RuleChainType | undefined>,
    inputNodeId: string
  ) {
    if (!ruleChain || !unref(ruleChain)) return;
    ruleChain = toRaw(unref(ruleChain))!;

    const nodes = deconstructionNode(ruleChain?.nodes || []);

    const edges = deconstructionConnection(ruleChain!, inputNodeId);

    return {
      nodes,
      edges,
    };
  }

  initNodeConfigMap();

  return {
    mergeData,
    deconstructionData,
  };
}