React Flow

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

 

 

 

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 (
    <div style={{ width: '100vw', height: '100vh' }}>

      <ReactFlow
        nodes={nodes} // 节点
        edges={edges} // 连接线
        onNodesChange={onNodesChange} // 节点拖拽等改变
        onEdgesChange={onEdgesChange} // 连接线拖拽等改变
        onNodeClick={onNodeClick} // 点击节点
        onEdgeClick={onEdgeClick} // 点击连接线
        onConnect={onConnect} // 节点直接连接
        nodeTypes={nodeTypes} // 节点类型
        // edgeTypes={edgeTypes}
        fitView // 渲染节点数据
        style={rfStyle} // 背景色
        defaultEdgeOptions={defaultEdgeOptions} // 默认连接线样式
        onInit={setRfInstance} // 初始化保存的数据
        connectionMode={ConnectionMode.Loose}
      />
      {nodeShow ? (
        <UpdateNode info={nodeInfo} onChange={changeNode} />
      ) : (
          <UpdateEdge info={edgeInfo} onChange={changeEdge} />
        )}
      <Panel position='top-left'>
        <button onClick={onAdd}>add node</button>
        <button onClick={onSave}>save</button>
        <button onClick={onRestore}>restore</button>
        <button onClick={onDelete}>delete</button>
      </Panel>
      <MiniMap />
      <Controls />
    </div>
  );
}

export default function () {
  return (
    <ReactFlowProvider>
      <App1 />

    </ReactFlowProvider>
  );
}

 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<any>({});

  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 ? (
    <div className="updatenode__controls">
      <label>名称:</label>
      <Input
        placeholder=""
        value={nodeInfo.label}
        onChange={(evt) => setNodeName(evt.target.value)}
      />
      <label className="updatenode__bglabel">背景色:</label>
      <Input type="color" value={nodeInfo.nodeBg} onChange={(evt) => setNodeBg(evt.target.value)} />
      <div className="updatenode__checkboxwrapper">
        <label>是否隐藏:</label>
        {/* <Switch checked={nodeInfo.isHidden} onChange={setNodeHidden} /> */}
        <input type='checkbox' checked={nodeInfo.isHidden} onChange={(evt) => setNodeHidden(evt.target.checked)} />
      </div>
    </div>
  ) : (
      <></>
    );
};

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<any>({});
  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 ? (
    <div className="updatenode__controls">
      <label>连接线名称:</label>
      <Input
        placeholder=""
        value={edgeInfo.label}
        onChange={(evt) => setNodeName(evt.target.value)}
      />
      <label className="updatenode__bglabel">连接线颜色:</label>
      <Input type="color" value={edgeInfo.color} onChange={(evt) => setNodeBg(evt.target.value)} />
      <div className="updatenode__checkboxwrapper">
        <label>连接线类型:</label>
        <Select defaultValue="曲线 " value={edgeInfo.type} onChange={changeEdgeType}>
          {edgeTypes.map((item) => (
            <Option value={item.value} key={item.value}>
              {item.label}
            </Option>
          ))}
        </Select>
      </div>
      <div className="updatenode__checkboxwrapper">
        <label>是否隐藏:</label>
        <Switch checked={edgeInfo.isHidden} onChange={setEdgeHidden} />
      </div>
    </div>
  ) : (
      <></>
    );
};

5、ResizableNodeSelected.tsx

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

const ResizableNodeSelected = ({ data, selected }) => {
  return (
    <>
      <NodeResizer color="#F3A011" isVisible={selected} minWidth={80} minHeight={80} />
      <div
        style={{
          // width: 60,
          // height: 60,
          padding: 10,
          // display: "flex",
          // justifyContent: "center",
          // alignItems: "center",
          // fontSize: 2
        }}
      >
        {data.label}
      </div>
      <Handle style={{ opacity: 0 }} type="source" position={Position.Top} id='a' />
      <Handle style={{ opacity: 0 }} type="source" position={Position.Right} id='b' />
      <Handle style={{ opacity: 0 }} type="source" position={Position.Bottom} id='c' />
      <Handle style={{ opacity: 0 }} type="source" position={Position.Left} id='d' />
    </>
  );
};

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"
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/48087.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

50条必背JAVA知识点(二)

16.强制类型转换&#xff1a;将容量大的数据类型转换为容量小的数据类型&#xff0c;但可能造成精度降低或溢出。 17.字符串不能直接转换为基本类型&#xff0c;但通过基本类型对应的包装类则可以实现把字符串转换成基本类型。 18.计算机底层都以二进制补码的方式来存储数据。…

图数据库Neo4j学习三——cypher语法总结

1MATCH 1.1作用 MATCH是Cypher查询语言中用于从图数据库中检索数据的关键字。它的作用是在图中查找满足指定条件的节点和边&#xff0c;并返回这些节点和边的属性信息。 在MATCH语句中&#xff0c;通过节点标签和边类型来限定查找范围&#xff0c;然后通过WHERE语句来筛选符合…

解决VScode下载太慢的问题记录

最近突然想重新下载vscoded便携免安装版&#xff0c;发现下载很慢&#xff0c;于是乎查询一下&#xff0c;以便记录 下载地址 VScode官方网站&#xff1a; https://code.visualstudio.com/ 根据个人的需求选择下载&#xff0c;页面加载下载需要等一会&#xff0c; 然后就会…

kotlin高阶函数

kotlin高阶函数 函数式API:一个函数的入参数为Lambda表达式的函数就是函数式api 例子: public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {return filterTo(ArrayList<T>(), predicate) }上面这段函数: 首先这个函…

使用EM算法完成聚类任务

EM算法&#xff08;Expectation-Maximization Algorithm&#xff09;是一种基于迭代优化的聚类算法&#xff0c;用于在无监督的情况下将数据集分成几个不同的组或簇。EM算法是一种迭代算法&#xff0c;包含两个主要步骤&#xff1a;期望步骤&#xff08;E-step&#xff09;和最…

态势标绘专题介绍

介绍 这个专栏是专门针对基于Cesium来实现态势标绘的专题专栏,专栏主要实现了30余种态势几何形状的标绘和编辑、文本的标绘和编辑、图片的标绘和编辑以及简单模型的标绘,同时支持标绘结果的导出以及导入。包括最终编写成的一个完整的Vue3.2+TS+Cesium1.107.2的标绘组件。专栏…

Java JVM虚拟机内部体系结构

JVM(Java虚拟机)是一个抽象机器。 它是一个提供可以执行Java字节码的运行时环境的规范。JVM可用于许多硬件和软件平台(即JVM是平台相关的)。 什么是JVM&#xff1f; JVM(Java虚拟机)是&#xff1a; 指定Java虚拟机的工作的规范。 但实现提供程序是独立的选择算法。 其实现是由…

【Hive实战】Hive的压缩池与锁

文章目录 Hive的压缩池池的分配策略自动分配手动分配隐式分配 池的等待超时Labeled worker pools 标记的工作线程&#xff08;自定义线程池&#xff09;Default pool 默认池Worker allocation 工作线程的分配 锁Turn Off ConcurrencyDebuggingConfigurationhive.support.concur…

如何跳出Java中的多层嵌套循环?

在Java中&#xff0c;要跳出多层嵌套循环&#xff0c;可以使用带有标签的break语句。通过在外层循环前加上一个标签&#xff0c;然后在内层循环中使用break语句后跟标签名称&#xff0c;可以实现跳出多层循环的目的。 以下是使用标签和break语句跳出多层嵌套循环的示例代码&…

BUG:pm2启动verdaccio报错:Invalid or unexpected toke

输入命令&#xff1a; pm2 state verdaccio 问题描述&#xff1a; pm2 logs verdaccio报错翻译&#xff1a;数据格式错误 导致我呢提原因&#xff0c;没有找到运行文件&#xff0c; 发现问题&#xff1a;因为命令默认查找verdaccio是去系统盘查找。 解决方式 1&#xff1a;…

Hadoop_HDFS_常见的文件组织格式与压缩格式

参考资料 1. HDFS中的常用压缩算法及区别_大数据_王知无_InfoQ写作社区 2. orc格式和parquet格式对比-阿里云开发者社区 3.Hadoop 压缩格式 gzip/snappy/lzo/bzip2 比较与总结 | 海牛部落 高品质的 大数据技术社区 4. Hive中的文件存储格式TEXTFILE、SEQUENCEFILE、RCFILE…

【家庭公网IPv6】

家庭公网IPv6 这里有两个网站&#xff1a; 1、 IPV6版、多地Tcping、禁Ping版、tcp协议、tcping、端口延迟测试&#xff0c;在本机搭建好服务器后&#xff0c;可以用这个测试外网是否可以访问本机&#xff1b; 2、 IP查询ipw.cn&#xff0c;这个可以查询本机的网络是否IPv6访问…

Java面向对象 - 常用类——Object类

什么是Object类 Java中有一个比较特殊的类&#xff0c;就是 Object类&#xff0c;它是所有类的父类&#xff0c;如果一个类没有使用extends关键字明确标识继承另外一个类&#xff0c;那么这个类就默认继承 Object类。因此&#xff0c;Object 类是 Java 类层中的最高层类&#x…

c++11 标准模板(STL)(std::basic_filebuf)(七)

定义于头文件 <fstream> template< class CharT, class Traits std::char_traits<CharT> > class basic_filebuf : public std::basic_streambuf<CharT, Traits> std::basic_filebuf 是关联字符序列为文件的 std::basic_streambuf 。输入序…

基于 STM32+FPGA 的通用工业控制器设计(一)系统方案设计

本章首先介绍了现有 PLC 系统的概况&#xff0c;然后提出了本文设计的通用工业控制器的 整体方案架构&#xff0c;分析了硬件和软件上需要实现的功能&#xff0c;最后对各部分功能进行分析并提 出具体的实现方案。 2.1 PLC 系统简介 可编程逻辑控制器&#xff08; Progra…

RocketMQ, Dashboard, 控制台安装

文章说明 本文主要说明RocketMQ的控制台&#xff08;Dashboard&#xff09;的安装过程。工作中一直用的是别人装好的&#xff0c;这次终于自己亲手装了一遍。 由于每次都要启动三个应用&#xff0c;比较烦&#xff0c;于是我写了一键启动脚本&#xff0c;分享给大家。这个脚本…

7. Spring Boot 配置文件

目录 1. 配置文件作用 2. 配置文件格式 3. properties 配置文件说明 3.1 properties 基本语法 3.2 读取配置文件 3.3 缺点 4. yml 配置文件说明 4.1 properties 基本语法 4.2 读取配置文件 4.3 yml 配置不同的数据类型 布尔值 整数值 null 值 配置对象 配置集合 …

国产化 | 记一次基于达梦创建数据库模式思考过程

开篇 首先&#xff0c;我们先来了解一下达梦数据库中用户与模式的概念&#xff0c;以及用户与模式之间的关系。 用户&#xff1a;主要是用来登录连接数据库&#xff0c;以及操作数据库对象等等。 模式&#xff1a;数据库中相关对象的集合。 关系&#xff1a;用户&#xff0…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)六:后台主页功能实现下

一、本章内容 接上一章,继续实现后端主页内容,主要实现工具栏对应相关内容的实现,包括系统消息、系统公告、全屏切换、语言切换、主题切换等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 基于VUE3+Layui从头搭建通用后台管理系统合集…