ReactFlow流程图

TestCaseDetail

import { useCallback, useMemo, useRef, useState } from 'react';
import ReactFlow, { addEdge, Background, Controls, MarkerType, ReactFlowProvider } from 'reactflow';
import 'reactflow/dist/style.css';
import ConditionNode from './customNode/ConditionNode';
import EndNode from './customNode/EndNode';
import PreStepNode from './customNode/PreStepNode';
import SqlNode from './customNode/SqlNode';
import StartNode from './customNode/StartNode';
import SubProcessNode from './customNode/SubProcessNode';
import TestCaseNode from './customNode/TestCaseNode';
import './index.css';
import Sidebar from './Sidebar';

const TestCaseDetail = (props) => {
  const nodeTypes = useMemo(
    () => ({
      TestCaseNode: TestCaseNode,
      StartNode: StartNode,
      EndNode: EndNode,
      ConditionNode: ConditionNode,
      SubProcessNode: SubProcessNode,
      PreStepNode: PreStepNode,
      SqlNode: SqlNode,
    }),
    [],
  );
  const reactFlowWrapper = useRef(null);
  const [reactFlowInstance, setReactFlowInstance] = useState(null);

  // 添加连接线
  const onConnect = useCallback((params) => {
    const markerEnd = {
      type: MarkerType.ArrowClosed,
    };
    params.markerEnd = markerEnd;
    params.type = 'smoothstep';
    props.setEdges((eds) => addEdge(params, eds));
  }, []);

  // 节点拖拽时
  const onDragOver = useCallback((event) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = 'move';
  }, []);

  // 添加新节点
  const onDrop = useCallback(
    (event) => {
      event.preventDefault();
      const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
      const type = event.dataTransfer.getData('application/reactflow');
      // check if the dropped element is valid
      if (typeof type === 'undefined' || !type) {
        return;
      }
      const position = reactFlowInstance.project({
        x: event.clientX - reactFlowBounds.left,
        y: event.clientY - reactFlowBounds.top,
      });
      const newNode = {
        id: Date.now().toString(),
        type,
        position,
        data: { label: `${type} node` },
      };
      props.setNodes((nds) => nds.concat(newNode));
    },
    [reactFlowInstance],
  );

  // 点击节点
  const onNodeClick = (e, node) => {
    console.log('点击节点', node);
  };

  // 点击节点连接线
  const onEdgeClick = (e, edge) => {
    // console.log('点击连接线', edge);
  };

  // 改变节点内容
  const changeNode = (val) => {
    props.setNodes((nds) =>
      nds.map((item) => {
        if (item.id === val.id) {
          item.data = val;
          item.hidden = val.isHidden;
          item.style = { background: val.nodeBg };
        }
        return item;
      }),
    );
  };

  // 改变连接线内容
  const changeEdge = (val) => {
    props.setEdges((nds) =>
      nds.map((item) => {
        if (item.id === val.id) {
          item.label = val.label;
          item.type = val.type;
          item.style = { stroke: val.color };
        }
        return item;
      }),
    );
  };

  return (
    <div className="dndflow" style={{ height: '95%' }}>
      <ReactFlowProvider>
       	<Sidebar />
        <div className="reactflow-wrapper" ref={reactFlowWrapper}>
          <ReactFlow
            nodes={props.nodes}
            edges={props.edges}
            nodeTypes={nodeTypes}
            onNodesChange={props.onNodesChange}
            onEdgesChange={props.onEdgesChange}
            onNodeClick={onNodeClick}
            onEdgeClick={onEdgeClick}
            onConnect={onConnect}
            onInit={setReactFlowInstance}
            onDrop={onDrop}
            onDragOver={onDragOver}
            fitView
          >
            <Controls />
            <Background variant="dots" gap={12} size={1} />
          </ReactFlow>
        </div>
      </ReactFlowProvider>
    </div>
  );
};

export default TestCaseDetail;

Sidebar.jsx

// 画布侧边栏
const Sidebar = () => {
  const onDragStart = (event, nodeType) => {
    event.dataTransfer.setData('application/reactflow', nodeType);
    event.dataTransfer.effectAllowed = 'move';
  };
  return (
    <aside style={{ height: '90%' }}>
      <div
        style={{
          backgroundColor: 'white',
          color: 'black',
          textAlign: 'center',
          borderColor: 'black',
          borderStyle: 'solid',
          borderBottomLeftRadius: '2em',
          borderBottomRightRadius: '2em',
          borderTopLeftRadius: '2em',
          borderTopRightRadius: '2em',
        }}
        className="dndnode input"
        onDragStart={(event) => {
          event.stopPropagation();
          onDragStart(event, 'StartNode');
        }}
        draggable
      >
        开始
      </div>
      <div
        style={{ borderStyle: 'dashed' }}
        className="dndnode testcase"
        onDragStart={(event) => {
          event.stopPropagation();
          onDragStart(event, 'PreStepNode');
        }}
        draggable
      >
        前置步骤
      </div>
      <div
        className="dndnode testcase"
        onDragStart={(event) => {
          event.stopPropagation();
          onDragStart(event, 'TestCaseNode');
        }}
        draggable
      >
        用例节点
      </div>
      <div
        style={{
          backgroundColor: 'white',
          color: 'black',
          textAlign: 'center',
          borderColor: 'black',
          borderStyle: 'solid',
          borderWidth: 1,
          borderLeftWidth: 8,
          borderRightWidth: 8,
        }}
        className="dndnode testcase"
        onDragStart={(event) => {
          event.stopPropagation();
          onDragStart(event, 'SubProcessNode');
        }}
        draggable
      >
        子流程
      </div>
      <div
        style={{
          backgroundColor: 'white',
          color: 'black',
          textAlign: 'center',
          borderColor: 'black',
          borderStyle: 'solid',
          borderWidth: 4,
          borderLeftWidth: 1,
          borderRightWidth: 1,
        }}
        className="dndnode testcase"
        onDragStart={(event) => {
          event.stopPropagation();
          onDragStart(event, 'SqlNode');
        }}
        draggable
      >
        SQL
      </div>
      <div
        style={{
          backgroundColor: 'gray',
          color: 'black',
          textAlign: 'center',
          borderColor: 'gray',
          borderStyle: 'solid',
          borderWidth: 1,
          clipPath: 'polygon(50% 0, 100% 50%, 50% 100%, 0 50%)',
          transition: '1s clip-path',
        }}
        className="dndnode testcase"
        onDragStart={(event) => {
          event.stopPropagation();
          onDragStart(event, 'ConditionNode');
        }}
        draggable
      >
        条件节点
      </div>
      <div
        style={{
          backgroundColor: 'white',
          color: 'black',
          textAlign: 'center',
          borderColor: 'black',
          borderStyle: 'solid',
          borderBottomLeftRadius: '2em',
          borderBottomRightRadius: '2em',
          borderTopLeftRadius: '2em',
          borderTopRightRadius: '2em',
        }}
        className="dndnode output"
        onDragStart={(event) => {
          event.stopPropagation();
          onDragStart(event, 'EndNode');
        }}
        draggable
      >
        结束
      </div>
    </aside>
  );
};

export default Sidebar;

customNode

import { Button } from 'antd';
import { Handle, Position } from 'reactflow';

const ConditionNode = ({ data }) => {
  const label = data.label === undefined ? '新用例节点' : data.label;
  const color = data.color === undefined ? 'white' : data.color;
  const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;

  return (
    <div>
      <Button
        style={{
          height: 40,
          width: 250,
          backgroundColor: color,
          color: 'black',
          textAlign: 'center',
          borderColor: borderColor,
          borderStyle: 'solid',
          borderWidth: 3,
          clipPath: 'polygon(50% 0, 100% 50%, 50% 100%, 0 50%)',
          transition: '1s clip-path',
        }}
      >
        {label}
      </Button>
      <Handle type="target" position={Position.Top} id="1" />
      <Handle type="target" position={Position.Left} id="2" />
      <Handle type="target" position={Position.Right} id="3" />
      <Handle type="source" position={Position.Bottom} id="4" />
    </div>
  );
};

export default ConditionNode;

import { Button } from 'antd';
import { Handle, Position } from 'reactflow';

const EndNode = ({ data }) => {
  const color = data.color === undefined ? 'white' : data.color;
  const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;

  return (
    <div>
      <Button
        style={{
          height: 40,
          width: 150,
          backgroundColor: color,
          color: 'black',
          textAlign: 'center',
          borderColor: borderColor,
          borderStyle: 'solid',
          borderBottomLeftRadius: '2em',
          borderBottomRightRadius: '2em',
          borderTopLeftRadius: '2em',
          borderTopRightRadius: '2em',
        }}
      >
        结束
      </Button>
      <Handle type="target" position={Position.Top} id="1" />
    </div>
  );
};

export default EndNode;

import { Button } from 'antd';
import { Handle, Position } from 'reactflow';
// 前置步骤
const PreStepNode = ({ data }) => {
  const label = data.label === undefined ? '前置步骤节点' : data.label;
  const color = data.color === undefined ? 'white' : data.color;
  const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;

  return (
    <div>
      <Button
        style={{
          height: 40,
          width: 250,
          backgroundColor: color,
          color: 'black',
          textAlign: 'center',
          borderColor: borderColor,
          borderStyle: 'dashed',
        }}
      >
        {label}
      </Button>
      <Handle type="target" position={Position.Top} id="1" />
      <Handle type="source" position={Position.Left} id="2" />
      <Handle type="source" position={Position.Right} id="3" />
      <Handle type="source" position={Position.Bottom} id="4" />
    </div>
  );
};

export default PreStepNode;

import { Button } from 'antd';
import { Handle, Position } from 'reactflow';

// SQL节点
const SqlNode = ({ data }) => {
  const label = data.label === undefined ? 'SQL' : data.label;
  const color = data.color === undefined ? 'white' : data.color;
  const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;

  return (
    <div>
      <Button
        style={{
          height: 40,
          width: 250,
          backgroundColor: color,
          color: 'black',
          textAlign: 'center',
          borderColor: borderColor,
          borderStyle: 'solid',
          borderWidth: 4,
          borderLeftWidth: 1,
          borderRightWidth: 1,
        }}
      >
        {label}
      </Button>
      <Handle type="target" position={Position.Top} id="1" />
      <Handle type="source" position={Position.Left} id="2" />
      <Handle type="source" position={Position.Right} id="3" />
      <Handle type="source" position={Position.Bottom} id="4" />
    </div>
  );
};

export default SqlNode;

import { Button } from 'antd';
import { Handle, Position } from 'reactflow';

const StartNode = ({ data }) => {
  const color = data.color === undefined ? 'white' : data.color;
  const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;

  return (
    <div>
      <Button
        style={{
          height: 40,
          width: 150,
          backgroundColor: color,
          color: 'black',
          textAlign: 'center',
          borderColor: borderColor,
          borderStyle: 'solid',
          borderBottomLeftRadius: '2em',
          borderBottomRightRadius: '2em',
          borderTopLeftRadius: '2em',
          borderTopRightRadius: '2em',
        }}
      >
        开始
      </Button>
      <Handle type="source" position={Position.Bottom} id="4" />
    </div>
  );
};

export default StartNode;

import { Button } from 'antd';
import { Handle, Position } from 'reactflow';

// 子流程
const SubProcessNode = ({ data }) => {
  const label = data.label === undefined ? '子流程' : data.label;
  const color = data.color === undefined ? 'white' : data.color;
  const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;

  return (
    <div>
      <Button
        style={{
          height: 40,
          width: 250,
          backgroundColor: color,
          color: 'black',
          textAlign: 'center',
          borderColor: borderColor,
          borderStyle: 'solid',
          borderWidth: 2,
          borderLeftWidth: 8,
          borderRightWidth: 8,
        }}
      >
        {label}
      </Button>
      <Handle type="target" position={Position.Top} id="1" />
      <Handle type="source" position={Position.Left} id="2" />
      <Handle type="source" position={Position.Right} id="3" />
      <Handle type="source" position={Position.Bottom} id="4" />
    </div>
  );
};

export default SubProcessNode;

import { Button } from 'antd';
import { Handle, Position } from 'reactflow';

const TestCaseNode = ({ data }) => {
  const label = data.label === undefined ? '新用例节点' : data.label;
  const color = data.color === undefined ? 'white' : data.color;
  const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;

  return (
    <div>
      <Button
        style={{
          height: 40,
          width: 250,
          backgroundColor: color,
          color: 'black',
          textAlign: 'center',
          borderColor: borderColor,
          borderStyle: 'solid',
        }}
      >
        {label}
      </Button>
      <Handle type="target" position={Position.Top} id="1" />
      <Handle type="source" position={Position.Left} id="2" />
      <Handle type="source" position={Position.Right} id="3" />
      <Handle type="source" position={Position.Bottom} id="4" />
    </div>
  );
};

export default TestCaseNode;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值