React Flow

// 创建项目
npm create vite@latest my-react-flow-app -- --template react
// 安装插件
npm install reactflow
// 运行项目
npm run dev

React Flow_第1张图片 

 

React Flow_第2张图片

 

1、App.jsx

import { useCallback, useState } from 'react';
import ReactFlow,
{
  addEdge,
  ReactFlowProvider,
  MiniMap,
  Controls,
  useNodesState,
  useEdgesState,
  useReactFlow,
  MarkerType,
  Panel,
  ConnectionMode
} from 'reactflow';
import 'reactflow/dist/style.css';
import './index.css';

import UpdateNode from './components/nodeContent';
import UpdateEdge from './components/edgeContent';
import ResizableNodeSelected from './components/ResizableNodeSelected';
import {nodes as initialNodes1,edges as initialEdges1} from './components/data';

const nodeTypes = {
  ResizableNodeSelected,
};

const rfStyle = {
  backgroundColor: '#B8CEFF',
};

const initialNodes = [
  {
    id: '1',
    type: 'ResizableNodeSelected',
    position: { x: 100, y: 100 },
    data: { label: '1' },
    style: {
      background: "#F3A011",
      color: "white",
      border: '1px solid orange',
      borderRadius: '100%',
      width: 80,
      height: 80,
    },
  },
  {
    id: '2',
    type: 'ResizableNodeSelected',
    position: { x: 200, y: 300 },
    data: { label: '2' },
    style: {
      background: "#F3A011",
      color: "white",
      border: '1px solid orange',
      borderRadius: '100%',
      width: 80,
      height: 80,
    },
  },
  {
    id: '3',
    type: 'ResizableNodeSelected',
    position: { x: 100, y: 500 },
    data: { label: '3' },
    style: {
      background: "#F3A011",
      color: "white",
      border: '1px solid orange',
      borderRadius: '100%',
      width: 80,
      height: 80,
    },
  },
];
const initialEdges = [
  {
    id: 'e1-2',
    source: '1',
    target: '2',
    style: { stroke: "#116F97" },
    label: "连接1-2",
    sourceHandle: 'c',
    targetHandle: 'a',
  },
  {
    id: "e2-3",
    source: "2",
    target: "3",
    // labelStyle: { fill: "#116F97", fontWeight: 100 }, // 连接线名称样式
    style: { stroke: "#116F97" }, // 连接线颜色
    label: "连接2-3",
    sourceHandle: 'c',
    targetHandle: 'a',
  },
];

const flowKey = 'flow_test';
const localNodes = JSON.parse(localStorage.getItem(flowKey)).nodes;
const localEdges = JSON.parse(localStorage.getItem(flowKey)).edges;
let nodeId = 1;

function App1() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes1);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges1);
  const [nodeInfo, setNodeInfo] = useState({});
  const [edgeInfo, setEdgeInfo] = useState({});
  const [nodeShow, setNodeShow] = useState(true);
  const onConnect = useCallback(
    (connection) => setEdges((eds) => addEdge(connection, eds)),
    [setEdges]
  );

  // 保存
  const [rfInstance, setRfInstance] = useState({});
  const onSave = useCallback(() => {
    if (rfInstance) {
      const flow = rfInstance.toObject();
      localStorage.setItem(flowKey, JSON.stringify(flow));
      console.log(JSON.stringify(flow));
    }
  }, [rfInstance]
  );

  // 恢复
  const { setViewport } = useReactFlow();
  const onRestore = useCallback(() => {
    const restoreFlow = async () => {
      const flow = JSON.parse(localStorage.getItem(flowKey));
      if (flow) {
        const { x = 0, y = 0, zoom = 0 } = flow.viewport;
        setNodes(flow.nodes || []);
        setEdges(flow.edges || []);
        setViewport({ x, y, zoom });
      }
    };
    restoreFlow();
  }, [setNodes, setViewport]
  );

  // 清空
  const onDelete = useCallback(() => {
    const restoreFlow = async () => {
      setNodes([] || []);
      setEdges([] || []);
    };
    restoreFlow();
  }, [setNodes]
  );

  // 点击节点
  const onNodeClick = (e, node) => {
    setNodeInfo({
      ...node.data,
      id: node.id,
      nodeBg: node.style && node.style.background ? node.style.background : '#ffffff',
    });
    setNodeShow(true);
  };

  // 点击节点连接线
  const onEdgeClick = (e, edge) => {
    setEdgeInfo(edges.find((item) => edge.id === item.id));
    setNodeShow(false);
  };


  // 新增节点
  const reactFlowInstance = useReactFlow();
  const onAdd = useCallback(() => {
    const id = `${++nodeId}`;
    const newNode = {
      id,
      type: 'ResizableNodeSelected',
      position: {
        x: 100,
        y: 300,
        // x: Math.random() * 200,
        // y: Math.random() * 200,
      },
      data: {
        label: `Node ${id}`,
      },
      style: {
        background: "#F3A011",
        color: "white",
        border: '1px solid orange',
        borderRadius: '100%',
        width: 80,
        height: 80,

      },
    };
    reactFlowInstance.addNodes(newNode);
  }, []);

  // 改变节点内容
  const changeNode = (val) => {
    setNodes((nds) =>
      nds.map((item) => {
        if (item.id === val.id) {
          item.data = val;
          item.hidden = val.isHidden;
          item.style = { background: val.nodeBg, width: 80, height: 80, borderRadius: '100%', color: "white", fontSize: 2 };
        }
        return item;
      }),
    );
  };

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

  // 默认edge样式
  const defaultEdgeOptions = {
    style: {
      strokeWidth: 1,
      stroke: '#116F97'
    },
    type: 'default',
    markerEnd: {
      type: MarkerType.ArrowClosed,
      color: '#116F97'
    } // 连接线尾部的箭头
  }

  return (
    
{nodeShow ? ( ) : ( )}
); } export default function () { return ( ); }

 2、index.css

:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  /* line-height: 2; */
  font-weight: 400;

  /* color-scheme: light dark; */
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* -webkit-text-size-adjust: 100%; */
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}
a:hover {
  color: #535bf2;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
  min-width: 320px;
  min-height: 100vh;
}

h1 {
  font-size: 3.2em;
  line-height: 1.1;
}

button {
  border-radius: 8px;
  border: 1px solid transparent;
  padding: 0.6em 1.2em;
  font-size: 1em;
  font-weight: 500;
  font-family: inherit;
  background-color: #1a1a1a;
  cursor: pointer;
  transition: border-color 0.25s;
}
button:hover {
  border-color: #646cff;
}
button:focus,
button:focus-visible {
  outline: 4px auto -webkit-focus-ring-color;
}

@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }
  a:hover {
    color: #116F97;
  }
  button {
    background-color: #f9f9f9;
  }
}

/* edge颜色 */
.react-flow__handle{
  color: #116F97;
  background-color: #116F97;
  border:0;
  border-radius: 100%;
  min-width: 1px;
  min-height: 1px;
}
.react-flow__edge-textbg{
  fill:#3a94BB;
}
.react-flow__handle.connectionindicator{
  width: 1;
  height: 1;
}
.react-flow__node{
  width: 50;
  height: 50;
}


/* 4个连接点样式 */
.simple-floatingedges {
  flex-direction: column;
  display: flex;
  flex-grow: 1;
  height: 100%;
}
.simple-floatingedges .react-flow__handle {
  width: 8px;
  height: 8px;
  background-color: #bbb;
}
.simple-floatingedges .react-flow__handle-top {
  top: -5px;
}
.simple-floatingedges .react-flow__handle-bottom {
  bottom: -5px;
}
.simple-floatingedges .react-flow__handle-left {
  left: -5px;
}
.simple-floatingedges .react-flow__handle-right {
  right: -5px;
}
.simple-floatingedges .react-flow__node-custom {
  background: #fff;
  border: 1px solid #1a192b;
  border-radius: 3px;
  color: #222;
  font-size: 12px;
  padding: 10px;
  text-align: center;
  width: 150px;
}

/* node与wdge编辑样式 */
.dndflow {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  height: 70vh;
}
.react-flow__attribution {
  display: none;
}
.dndflow aside {
  padding: 15px 10px;
  font-size: 12px;
  background: #fcfcfc;
  border-right: 1px solid #eee;
}
.dndflow aside .description {
  margin-bottom: 10px;
}
.dndflow .dndnode {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 20px;
  margin-bottom: 10px;
  padding: 4px;
  border: 1px solid #1a192b;
  border-radius: 2px;
  cursor: grab;
}
.dndflow .dndnode.input {
  border-color: #0041d0;
}
.dndflow .dndnode.output {
  border-color: #ff0072;
}
.dndflow .reactflow-wrapper {
  flex-grow: 1;
  height: 100%;
}
.dndflow .selectall {
  margin-top: 10px;
}
@media screen and (min-width: 768px) {
  .dndflow {
    flex-direction: row;
  }

  .dndflow aside {
    width: 20%;
    max-width: 250px;
  }
}
.my_handle {
  z-index: 99;
}
.nodeContent {
  position: relative;
  color: #222;
  font-size: 12px;
  line-height: 10px;
  text-align: center;
  background-color: #fff;
  border: 1px solid #1a192b;
  border-radius: 3px;
}
.nodeStyle {
  width: 110px;
  height: 30px;
  line-height: 10px;
}
.updatenode__controls {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 4;
  padding: 16px;
  font-size: 12px;
  background-color: #fff;
}
.updatenode__controls label {
  display: block;
}
.updatenode__bglabel {
  margin-top: 10px;
}
.updatenode__checkboxwrapper {
  display: flex;
  align-items: center;
  margin-top: 10px;
}


3、nodeContent.tsx

import React, { useState, useEffect } from 'react';
import { Input, Switch } from 'antd';

export type nodeProps = {
  info: any;
  onChange: (val: any) => void;
};

export default ({ info, onChange }: nodeProps) => {
  const [nodeInfo, setNodeInfo] = useState({});

  useEffect(() => {
    if (info.id) {
      if (!info.isHidden) {
        info.isHidden = false;
      }
      setNodeInfo(info);
    }
  }, [info.id]);

  // 改变名称
  const setNodeName = (value: string) => {
    setNodeInfo({
      ...nodeInfo,
      label: value,
    });
    onChange({
      ...nodeInfo,
      label: value,
    });
  };

  // 改变背景色
  const setNodeBg = (value: string) => {
    setNodeInfo({
      ...nodeInfo,
      nodeBg: value,
    });
    onChange({
      ...nodeInfo,
      nodeBg: value,
    });
  };

  // 是否隐藏
  const setNodeHidden = (value: boolean) => {
    setNodeInfo({
      ...nodeInfo,
      isHidden: value,
    });
    onChange({
      ...nodeInfo,
      isHidden: value,
    });
  };

  return nodeInfo.id ? (
    
setNodeName(evt.target.value)} /> setNodeBg(evt.target.value)} />
{/* */} setNodeHidden(evt.target.checked)} />
) : ( <> ); };

4、edgeContent.tsx

import React, { useState, useEffect } from 'react';
import { Input, Select, Switch } from 'antd';

const { Option } = Select;

export type edgeProps = {
  info: any;
  onChange: (val: any) => void;
};

export default ({ info, onChange }: edgeProps) => {
  const [edgeInfo, setEdgeInfo] = useState({});
  const edgeTypes = [
    { label: '曲线', value: 'default' },
    { label: '直线', value: 'straight' },
    { label: '直角线', value: 'step' },
    { label: '圆滑直角线', value: 'smoothstep' },
  ];

  useEffect(() => {
    if (info.id) {
      if (info.style) {
        info.color = info.style.stroke;
      }
      if (!info.isHidden) {
        info.isHidden = false;
      }
      setEdgeInfo(info);
    }
  }, [info.id]);

  // 改变名称
  const setNodeName = (value: string) => {
    setEdgeInfo({
      ...edgeInfo,
      label: value,
    });
    onChange({
      ...edgeInfo,
      label: value,
    });
  };

  // 改变颜色
  const setNodeBg = (value: string) => {
    setEdgeInfo({
      ...edgeInfo,
      color: value,
    });
    onChange({
      ...edgeInfo,
      color: value,
    });
  };

  // 改变类型
  const changeEdgeType = (value: string) => {
    setEdgeInfo({
      ...edgeInfo,
      type: value,
    });
    onChange({
      ...edgeInfo,
      type: value,
    });
  };

  // 是否隐藏
  const setEdgeHidden = (value: boolean) => {
    setEdgeInfo({
      ...edgeInfo,
      isHidden: value,
    });
    onChange({
      ...edgeInfo,
      isHidden: value,
    });
  };

  return edgeInfo.id ? (
    
setNodeName(evt.target.value)} /> setNodeBg(evt.target.value)} />
) : ( <> ); };

5、ResizableNodeSelected.tsx

import { memo } from 'react';
import { Handle, Position, NodeResizer } from 'reactflow';

const ResizableNodeSelected = ({ data, selected }) => {
  return (
    <>
      
      
{data.label}
); }; export default memo(ResizableNodeSelected);

6、data.js

export const nodes = [
    { "width": 80, "height": 80, "id": "13", "type": "ResizableNodeSelected", "position": { "x": 181.99158953145331, "y": 472.7199877834713 }, "data": { "label": "服务实例JVM堆大小", "id": "13", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 2 }, "selected": false, "positionAbsolute": { "x": 181.99158953145331, "y": 472.7199877834713 }, "dragging": false, "hidden": false },
    { "width": 80, "height": 80, "id": "12", "type": "ResizableNodeSelected", "position": { "x": 458.51664737488375, "y": 497.7400344424826 }, "data": { "label": "服务实例JVM线程数", "id": "12", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 2 }, "selected": false, "positionAbsolute": { "x": 458.51664737488375, "y": 497.7400344424826 }, "dragging": false, "hidden": false },
    { "width": 80, "height": 80, "id": "11", "type": "ResizableNodeSelected", "position": { "x": 456.86503312460417, "y": 278.4940093032253 }, "data": { "label": "应用服务平均响应时长", "id": "11", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 2 }, "selected": false, "positionAbsolute": { "x": 456.86503312460417, "y": 278.4940093032253 }, "dragging": false, "hidden": false },
    { "width": 80, "height": 80, "id": "10", "type": "ResizableNodeSelected", "position": { "x": 188.70901987307826, "y": 316.5335073980088 }, "data": { "label": "存储I/O负载", "id": "10", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 2 }, "selected": false, "positionAbsolute": { "x": 188.70901987307826, "y": 316.5335073980088 }, "dragging": false, "hidden": false },
    { "width": 80, "height": 80, "id": "9", "type": "ResizableNodeSelected", "position": { "x": 86.9908194969212, "y": 56.769326529302944 }, "data": { "label": "服务端点平均响应时长", "id": "9", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 2 }, "selected": false, "positionAbsolute": { "x": 86.9908194969212, "y": 56.769326529302944 }, "dragging": false, "hidden": false },
    { "width": 80, "height": 80, "id": "8", "type": "ResizableNodeSelected", "position": { "x": 461.40008223448365, "y": 92.49854876752454 }, "data": { "label": "服务实例", "id": "8", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 2 }, "selected": false, "positionAbsolute": { "x": 461.40008223448365, "y": 92.49854876752454 }, "dragging": false, "hidden": false },
    { "width": 80, "height": 80, "id": "7", "type": "ResizableNodeSelected", "position": { "x": -87.30759005365732, "y": 133.76253323256137 }, "data": { "label": "端点链路(动态模型)平均响应时长", "id": "7", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 4 }, "selected": false, "dragging": false, "hidden": false, "positionAbsolute": { "x": -87.30759005365732, "y": 133.76253323256137 } },
    { "width": 80, "height": 80, "id": "6", "type": "ResizableNodeSelected", "position": { "x": -11.910201399135396, "y": 485.8445794117532 }, "data": { "label": "主机I/O负载", "id": "6", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 4 }, "selected": false, "dragging": false, "hidden": false, "positionAbsolute": { "x": -11.910201399135396, "y": 485.8445794117532 }, "resizing": false },
    { "width": 80, "height": 80, "id": "5", "type": "ResizableNodeSelected", "position": { "x": -10, "y": 280.5 }, "data": { "label": "Mysql实例慢查询", "id": "5", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 4 }, "selected": false, "dragging": false, "hidden": false, "positionAbsolute": { "x": -10, "y": 280.5 } },
    { "width": 80, "height": 80, "id": "4", "type": "ResizableNodeSelected", "position": { "x": -282.5, "y": 453 }, "data": { "label": "主机内存使用率", "id": "4", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 4 }, "selected": false, "dragging": false, "hidden": false, "positionAbsolute": { "x": -282.5, "y": 453 } },
    { "width": 80, "height": 80, "id": "3", "type": "ResizableNodeSelected", "position": { "x": -275, "y": 283 }, "data": { "label": "主机CPU使用率", "id": "3", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 4 }, "selected": false, "positionAbsolute": { "x": -275, "y": 283 }, "dragging": false, "hidden": false },
    { "width": 80, "height": 80, "id": "2", "type": "ResizableNodeSelected", "position": { "x": -271, "y": 133.5 }, "data": { "label": "消息中间件堆积数", "id": "2", "nodeBg": "#F3A011", "isHidden": false }, "style": { "background": "#F3A011", "width": 80, "height": 80, "borderRadius": "100%", "color": "white", "fontSize": 4 }, "selected": false, "dragging": false, "hidden": false, "positionAbsolute": { "x": -271, "y": 133.5 } }
];

export const edges = [
    { "style": { "stroke": "#116F97" }, "type": "smoothstep", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "4", "sourceHandle": "a", "target": "5", "targetHandle": "d", "id": "reactflow__edge-4a-5d", "selected": false, "hidden": true },
    { "style": { "stroke": "#116F97" }, "type": "straight", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "4", "sourceHandle": "b", "target": "5", "targetHandle": "c", "id": "reactflow__edge-4b-5c", "selected": false, "hidden": true },
    { "style": { "stroke": "#116F97" }, "type": "default", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "5", "sourceHandle": "d", "target": "4", "targetHandle": "a", "id": "reactflow__edge-5d-4a", "selected": false, "hidden": true },
    { "style": { "stroke": "#116F97" }, "type": "default", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "2", "sourceHandle": "b", "target": "7", "targetHandle": "d", "id": "reactflow__edge-2b-7d", "selected": false, "label": "模型间接关系", "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "default", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "5", "sourceHandle": "d", "target": "3", "targetHandle": "b", "id": "reactflow__edge-5d-3b", "selected": false, "label": "模型直接关系", "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "default", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "6", "sourceHandle": "a", "target": "5", "targetHandle": "c", "id": "reactflow__edge-6a-5c", "selected": false, "label": "模型直接关系", "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "smoothstep", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "5", "sourceHandle": "a", "target": "7", "targetHandle": "c", "id": "reactflow__edge-5a-7c", "selected": false, "label": "模型直接关系", "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "smoothstep", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "4", "sourceHandle": "a", "target": "5", "targetHandle": "c", "id": "reactflow__edge-4a-5c", "selected": false, "label": "模型直接关系", "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "step", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "5", "sourceHandle": "b", "target": "7", "targetHandle": "b", "id": "reactflow__edge-5b-7b", "selected": false, "label": "模型直接关系", "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "smoothstep", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "7", "sourceHandle": "b", "target": "9", "targetHandle": "d", "id": "reactflow__edge-7b-9d", "selected": false, "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "smoothstep", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "10", "sourceHandle": "d", "target": "5", "targetHandle": "b", "id": "reactflow__edge-10d-5b", "selected": false, "label": "模型直接关系", "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "smoothstep", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "8", "sourceHandle": "c", "target": "11", "targetHandle": "a", "id": "reactflow__edge-8c-11a", "selected": false, "label": "模型直接关系", "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "straight", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "12", "sourceHandle": "a", "target": "11", "targetHandle": "c", "id": "reactflow__edge-12a-11c", "selected": false, "label": "模型直接关系", "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "smoothstep", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "13", "sourceHandle": "b", "target": "11", "targetHandle": "d", "id": "reactflow__edge-13b-11d", "selected": false, "label": "模型直接关系", "hidden": false },
    { "style": { "stroke": "#116F97" }, "type": "smoothstep", "markerEnd": { "type": "arrowclosed", "color": "#116F97" }, "source": "9", "sourceHandle": "b", "target": "11", "targetHandle": "d", "id": "reactflow__edge-9b-11d", "selected": false, "label": "模型直接关系", "hidden": false }
]

// "viewport": { "x": 654.5507940552135, "y": -54.945769269730704, "zoom": 1.6908994642667994 } }

7、package.json

{
  "name": "my-react-flow-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "antd": "^5.7.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "reactflow": "^11.7.4"
  },
  "devDependencies": {
    "@types/react": "^18.2.14",
    "@types/react-dom": "^18.2.6",
    "@vitejs/plugin-react": "^4.0.1",
    "eslint": "^8.44.0",
    "eslint-plugin-react": "^7.32.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.1",
    "vite": "^4.4.0"
  }
}

你可能感兴趣的:(react.js,前端,前端框架)