首页 > 其他分享 >react-flow 流程图2.0

react-flow 流程图2.0

时间:2024-07-24 17:56:16浏览次数:14  
标签:node const flow react 2.0 data id

文件中需要下载的组件:


   npm install reactflow     (我的版本是npm install [email protected])
   npm install react-markdown (下面流程图中用到了 markdown) 版本7.1.0
    npm i antd   (版本 5.18.3)
    npm i axios   (版本1.7.2)
//marjdown 中用到的样式字体等
   npm i rehype-highlight
       npm i remark-gfm
       npm i rehype-raw
       npm i rehype-katex
       npm i remark-math
reactflow 官网
  https://reactflow.dev/

内置组件

  • <Background/>插件实现了一些基本的可定制背景模式。
  • <MiniMap/>插件在屏幕角落显示图形的小版本。
  • <Controls/>插件添加控件以缩放、居中和锁定视口。
  • <Panel/>插件可以轻松地将内容定位在视口顶部。
  • <NodeToolbar/>插件允许您渲染附加到节点的工具栏。
  • <NodeResizer/>插件可以很容易地为节点添加调整大小的功能。

需求:

  例1:最初获取到 第一个父节点  和父节点下面的选择下拉数据,根据点击的每个父节点的下拉信息后端返回子节点 。前端处理把子节点添加到整个数据中然后展示到页面上。

  小要求:每个节点中的内容用到了 markdown 的形式展示  且 每个父节点 点击获取数据时有加载效果。

      例2:如果你的需求是只获取一次全部的数据 展示出流程图这种会比较简单。

⭐️ 例1中复杂处理的地方:

  当父节点有子节点 子节点还有子节点的情况 再次点击父节点需要有删除节点的操作 。 需要删除子节点下面的子节点。而且每次点击父节点 需要删除上次获取到的子节点 然后再次加入新的子节点。

整体效果图:  (官网的:https://reactflow.dev/examples/styling/turbo-flow)

首先在 useEffect中 根据后端获取得到第一个节点 和第一个节点的下拉数据 

 

 点击每个节点的下拉数据后 下拉框关闭 并且展示加载效果 再次获取到子节点展示到页面

 

 index.js 文件 (包裹流程图的文件)

import React from 'react';
import OverviewFlow from './overcierFlow';
import './index.css'
import './overflow.css'
class MindFlow extends React.Component { 
    render() {
        return (
            <div
                className='box'
                style={{ height: '100vh', width: '100%' }}>
                <OverviewFlow>
                </OverviewFlow>

            </div>
        );
    }
}

export default MindFlow;
overcierFlow.js 文件(流程图文件)  
  1 /* eslint-disable */
  2 import React, { useEffect, useCallback } from "react";
  3 import ReactFlow, {
  4   useNodesState,
  5   useEdgesState,
  6   Controls,
  7   MiniMap,
  8   getIncomers,
  9   getOutgoers,
 10   getConnectedEdges,
 11 } from "reactflow";
 12 import {
 13   nodes as initialNodes,
 14   edges as initialEdges,
 15 } from "./initial-elements";
 16 import CustomNode from "./ResizableNode";//自定义节点样式
 17 import TurboEdge from "./TurboEdge";//自定义连接线
 18  19 import axios from "axios";
 20 import "reactflow/dist/style.css";
 21 import "reactflow/dist/base.css";
 22 
 23 const nodeTypes = {
 24   custom: CustomNode, //注意:用到自定义节点的话必须每个数据的 type:custom ,如果添加其他自定义节点如 custom2:引入文件 数据的type 就是 custom2
 25 };
 26 const edgeTypes = {
 27   custom: TurboEdge, //注意:用到自定义的连线每个数据的 type:custom  如上一样
 28 };
 29 const defaultEdgeOptions = {
 30   type: "custom",
 31   markerEnd: "edge-circle",
 32 };
 33 
 34 const OverviewFlow = () => {
 35   const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
 36   const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
 37 
 38   //初始获取数据  如果需求是例2 只要下面就可以 就不需要onNodeClick函数中的代码
 39   useEffect(() => {
 40     axios({
 41       url: `/getConclusion`,
 42       method: "GET",
 43     }).then((res1) => {
 44       if (res1.data) {
 45         setNodes(res1.data.nodes);
 46         setEdges(res1.data.edges);
 47       }
 48     });
 49   }, []);
 50 
 51   if (!nodes?.length) {
 52     return null;
 53   }
 54 
 55   //页面中点击删除可以删除每条线
 56   const onNodesDelete = useCallback(
 57     (deleted) => {
 58       setEdges(
 59         deleted.reduce((acc, node) => {
 60           const incomers = getIncomers(node, nodes, edges);
 61           const outgoers = getOutgoers(node, nodes, edges);
 62           const connectedEdges = getConnectedEdges([node], edges);
 63 
 64           const remainingEdges = acc.filter(
 65             (edge) => !connectedEdges.includes(edge)
 66           );
 67 
 68           const createdEdges = incomers.flatMap(({ id: source }) =>
 69             outgoers.map(({ id: target }) => ({
 70               id: `${source}->${target}`,
 71               source,
 72               target,
 73             }))
 74           );
 75 
 76           return [...remainingEdges, ...createdEdges];
 77         }, edges)
 78       );
 79     },
 80     [nodes, edges]
 81   );
 82 
 83   //处理数据的方法
 84   const findarr = (a, b) => {
 85     let arr = a.filter((item) => b.some((v) => v.source === item.source));
 86     return arr;
 87   };
 88 
 89   const findtarget = (a, b) => {
 90     if (a && b) {
 91       return a.filter((item) => !b.some((v) => v.target === item.id));
 92     } else {
 93       return [];
 94     }
 95   };
 96   const findsource = (a, b) => {
 97     if (a && b) {
 98       return a.filter((item) => !b.some((v) => v.source === item.source));
 99     } else {
100       return [];
101     }
102   };
103   const findnodes = (a, b) => {
104     if (a && b) {
105       return a.filter((item) => !b.some((v) => v.target === item.id));
106     } else {
107       return [];
108     }
109   };
110   const findedges = (a, b) => {
111     if (a && b) {
112       return a.filter((item) => !b.some((v) => v.id === item.id));
113     } else {
114       return [];
115     }
116   };
117   const debounce = (fn, delay) => {
118     let timer;
119     return (...args) => {
120       if (timer) {
121         clearTimeout(timer);
122       }
123       timer = setTimeout(() => {
124         fn(...args);
125       }, delay);
126     };
127   };
128 
129   const _ResizeObserver = window.ResizeObserver;
130   window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
131     /**
132      * @constructor
133      * @param {Function} callback - 回调函数,将在每次滚动时被调用。该函数接受一个参数:event(包含滚动事件的信息)。
134      * 该函数应该返回一个布尔值,表示是否应该继续触发事件。如果返回false,则不会再次触发事件。
135      * 该函数可以选择性地使用preventDefault()来防止默认行为。
136      * @description 构造函数,创建一个DebouncedScrollEvent对象实例。
137      */
138     constructor(callback) {
139       callback = debounce(callback, 10);
140       super(callback);
141     }
142   };
143 
144   const findAllData = (node, edge) => {
145     if (findarr(edges, edge).length !== 0) {
146       if (edge) {
147         //如果要添加的 edge 的起点source 比总和的 edges 的最后一个的起点短 删除edges里面长的所有source
148         //删除 nodes 中 id 和要删除的长的source一样的 id
149         if (edge[0].source.length < edges[edges.length - 1].source.length) {
150           let findeag = edges.filter((item) => {
151             return item.source.length > edge[0].source.length;
152           });
153           let findegds = findsource(edges, findeag);
154           let findnode = findnodes(nodes, findeag);
155           setEdges(findegds);
156           setNodes(findnode);
157         } else {
158           //删掉之前的线 和 nodes中id 和删除线的target相同的
159           const listall = findtarget(nodes, findarr(edges, edge)).concat(node);
160           listall.forEach((item) => {
161             item.data.loading = false;
162           });
163           //删除两个 id 重复的后合并edge
164           setEdges(findedges(edges, edge).concat(edge));
165           setNodes(listall);
166         }
167       }
168     } else {
169       const listall = nodes.concat(node);
170       listall.forEach((item) => {
171         item.data.loading = false;
172       });
173       setEdges(edges.concat(edge));
174       setNodes(listall);
175     }
176     Array.from(document.getElementsByClassName("ant-spin-dot-holder")).forEach(
177       (item) => {
178         item.style.display = "none";
179       }
180     );
181     Array.from(document.getElementsByClassName("loadingTitle")).forEach(
182       (item) => {
183         item.style.display = "none";
184       }
185     );
186   };
187 
188   // 点击节点,将节点初始配置传入nodes
189   const onNodeClick = (e, node) => {
190     if (node.flags) {
191       if (node.data.select) {
192         nodes.forEach((item) => {
193           if (item.id === node.id) {
194             item.data.loading = true;
195           }
196         });
197         setNodes(nodes);
198         const obj = node;
199         obj.level = node.data.level;
200         obj.select = node.data.select;
201         delete node.data["select"];
202         delete node.data["weekly_template"];
203         204         axios({
205           url: `/getNodes`,
206           method: "post",
207           data: obj,
208         })
209           .then((res) => {
210             if (res.data) {
211               findAllData(res.data.nodes, res.data.edges);
212             }
213           })
214           .finally(() => {});
215       }
216     }
217   };
218 
219   return (
220     <ReactFlow
221       nodes={nodes}
222       edges={edges}
223       nodeTypes={nodeTypes}
224       edgeTypes={edgeTypes}
225       onNodeClick={onNodeClick}
226       onNodesChange={onNodesChange}
227       onNodesDelete={onNodesDelete}
228       onEdgesChange={onEdgesChange}
229       defaultEdgeOptions={defaultEdgeOptions} 
230     >
231       <Controls />
232       <MiniMap />
233       <svg>
234         <defs>
235           <linearGradient id="edge-gradient">
236             <stop offset="0%" stopColor="#ae53ba" />
237             <stop offset="100%" stopColor="#2a8af6" />
238           </linearGradient>
239 
240           <marker
241             id="edge-circle"
242             viewBox="-5 -5 10 10"
243             refX="0"
244             refY="0"
245             markerUnits="strokeWidth"
246             markerWidth="10"
247             markerHeight="10"
248             orient="auto"
249           >
250             <circle stroke="#2a8af6" strokeOpacity="0.75" r="2" cx="0" cy="0" />
251           </marker>
252         </defs>
253       </svg> 
254     </ReactFlow>
255   );
256 };
257  
258 export default OverviewFlow;

 

initial-elements.js文件 流程图的数据
export const nodes = [
    {
        id: "root",
        type: "custom",    //type:custom 就是和上面文件的自定义对上了
        data: {
            label: '',
            loading: true,
        },
        position: { x: 15, y: 10 },//-113px, -130.5
        flags: true,
    },
    // {
    //     id: "hangye",
    //     type: "custom",
    //     data: { label: "行业",show:true },
    //     position: { x: 0, y: -55 },
    //   flags:true,
    //   style: {
    //     borderRadius: '5px',
    //         width:100,
    //         height:50
    //   }
    // },
    // {
    //     id: "chanpinxian",
    //     type: "custom",
    //     data: { label: "产品线",show:true },
    //     position: { x: 0, y: -60 },
    //   flags:true,
    //   style: {
    //     borderRadius: '5px',
    //         width:100,
    //         height:50
    //   }
    // },
    // {
    //     id: "duan",
    //     type: "custom",
    //     data: { label: "端" ,show:true},
    //     position: { x: 0, y: -80 ,},
    //   flags:true,
    //   style: {
    //     borderRadius: '5px',
    //         width:100,
    //         height:50
    //   }
    // },
    //  {
    //     id: "horizontal-2",
    //   type:'custom', 
    //     data: { label: "端内" },  
    //     position: { x: 300, y: -50 },
    //   flags:true
    // },
    // {
    //     id: "horizontal-3",
    //   type:'custom',

    //     // sourcePosition: "right",
    //     // targetPosition: "left",
    //     data: { label: "端外" },  
    //     position: { x: 300, y: 0 }
    // },
    // {
    //     id: "horizontal-0",
    //   type:'custom',
    //     // sourcePosition: "right",
    //     // targetPosition: "left",
    //     data: { label: "PC" },  
    //     position: { x: 300, y: 50 }
    // },
    //  {
    //     id: "hzhuong",
    //   type:'custom', 
    //     data: { label: "主动" },  
    //     position: { x: 400, y: -100 }
    // },
    // {
    //     id: "jifa-3",
    //   type:'custom',

    //     // sourcePosition: "right",
    //     // targetPosition: "left",
    //     data: { label: "激发" },  
    //     position: { x: 400, y: -50 }
    // },
    // {
    //     id: "diaodong-0",
    //   type:'custom',
    //     // sourcePosition: "right",
    //     // targetPosition: "left",
    //     data: { label: "调动运营" },  
    //     position: { x: 400, y: 0 }
    // },
];


export const edges = [
    // {
    //     id: "horizontal-e1-2",
    //     source: "root",
    //     type: "smoothstep",
    //     target: "horizontal-2",
    //     label: '中间字段'
    //     // animated: true
    // },
    // {
    //   id: "horizontal-e1-0",
    //     source: "root",
    //     type: "smoothstep",
    //     target: "horizontal-0"
    // },
    // {
    //     id: "horizontal-e1-3",
    //     source: "root",
    //     type: "smoothstep",
    //     target: "horizontal-3",
    //     // animated: true
    // },  
    // {
    //   id: "duannei-1",
    //     source: "horizontal-2",
    //     type: "smoothstep",
    //     target: "hzhuong",
    //     // animated: true
    // },
    // {
    //   id: "duannei-2",
    //     source: "horizontal-2",
    //     type: "smoothstep",
    //     target: "jifa-3",
    //     // animated: true
    // },
    // {
    //   id: "duannei-3",
    //     source: "horizontal-2",
    //     type: "smoothstep",
    //     target: "diaodong-0",
    //     // animated: true
    // },
];

 
 
ResizableNode.js自定义节点文件
import React, { memo, useState, useEffect, useRef } from "react";
import { SearchOutlined } from "@ant-design/icons";
import { Popconfirm, Button } from "antd";
import { Handle } from "reactflow";
import { Spin } from "antd";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
import remarkGfm from "remark-gfm";
import "katex/dist/katex.min.css";
import "highlight.js/styles/github.css";
import "./index.css";
import "./overflow.css";
export default memo(({ data, id, isConnectable }) => {
  const [open, setOpen] = useState(false);
  const [loadTitle, setLoadTitle] = useState(null);
  const tooltipContainerRef = useRef(null);
  const getPopupContainer = (triggerNode) => {
    // 这里返回你希望 Tooltip 弹出层挂载到的 DOM 元素  f
    return tooltipContainerRef.current;
  };

  useEffect(() => {
    setOpen(false);
  }, []);

  const visivleChange = (visible) => {
    setOpen(visible); //这里使用的HOOKs
    data.select = "";
  };

  const changeDrawer = (obj, key, label) => {
    setLoadTitle(label);
    data.select = obj;
    data.select.key = key;
  };

  const resicaBox = () => {
    return (
      <div
        className="ResicabelNode gradient"
        ref={tooltipContainerRef}
        id="root"
      >
        <div className="inner">
          <Handle
            type="target"
            position={data.show ? "top" : "left"}
            className="my_handle"
            onConnect={(params) => console.log("handle onConnect", params)}
            isConnectable={isConnectable}
          />

          <div className="nodeContent" style={data.style}>
            <div className="nodelabel">
              <ReactMarkdown
                components={{
                  // Map `h1` (`# heading`) to use `h2`s.
                  h1: "h2",
                  // Rewrite `em`s (`*like so*`) to `i` with a red foreground color.
                  em: ({ node, ...props }) => (
                    <i style={{ color: "red" }} {...props} />
                  ),
                }}
                children={data.label}
                remarkPlugins={[remarkGfm]}
                rehypePlugins={[rehypeRaw]}
              />
              {/* {data.desc && <div className="subline">{data.desc}</div>} */}
              {data.desc && (
                <ReactMarkdown
                  components={{
                    // Map `h1` (`# heading`) to use `h2`s.
                    h1: "h2",
                    // Rewrite `em`s (`*like so*`) to `i` with a red foreground color.
                    em: ({ node, ...props }) => (
                      <i style={{ color: "red" }} {...props} />
                    ),
                  }}
                  children={data.desc}
                  remarkPlugins={[remarkGfm]}
                  rehypePlugins={[rehypeRaw]}
                />
              )}
              {data.loading ? (
                <div className="loadingbox">
                  <p className="loadingTitle">
                    {loadTitle && loadTitle + "加载中"}
                  </p>
                  <Spin></Spin>
                </div>
              ) : (
                ""
              )}
            </div>
          </div>
          <Handle
            type="source"
            position={data.show ? "bottom" : "right"}
            id="a"
            className="my_handle"
            isConnectable={isConnectable}
          />
        </div>
      </div>
    );
  };

  return (
    <>
      {data.list ? (
        <Popconfirm
          title={
            <div className="popcon">
              {data.list.map((item) => {
                return (
                  <div key={item.key}>
                    <p>{item.label}:</p>
                    {item.children &&
                      item.children.map((nitem) => {
                        return (
                          <div
                            className="popcon_btn"
                            key={nitem.id}
                            onClick={() => {
                              changeDrawer(nitem, item.key, item.label);
                            }}
                          >
                            <Button
                              onClick={() => {
                                setOpen(false);
                              }}
                              type="primary"
                            >
                              {nitem.title}
                            </Button>
                          </div>
                        );
                      })}
                  </div>
                );
              })}
            </div>
          }
          cancelText=""
          okText=""
          open={open}
          onOpenChange={visivleChange}
        >
          {resicaBox()}
        </Popconfirm>
      ) : (
        resicaBox()
      )}
    </>
  );
});
TurboEdge.js 文件 自定义线
 1 import React from 'react';
 2 import {  getBezierPath } from 'reactflow';
 3 
 4 export default function CustomEdge({
 5   id,
 6   sourceX,
 7   sourceY,
 8   targetX,
 9   targetY,
10   sourcePosition,
11   targetPosition,
12   style = {},
13   markerEnd,
14 }) {
15   const xEqual = sourceX === targetX;
16   const yEqual = sourceY === targetY;
17 
18   const [edgePath] = getBezierPath({
19     // we need this little hack in order to display the gradient for a straight line
20     sourceX: xEqual ? sourceX + 0.0001 : sourceX,
21     sourceY: yEqual ? sourceY + 0.0001 : sourceY,
22     sourcePosition,
23     targetX,
24     targetY,
25     targetPosition,
26   });
27 
28   return (
29     <>
30       <path
31         id={id}
32         style={style}
33         className="react-flow__edge-path"
34         d={edgePath}
35         markerEnd={markerEnd}
36       />
37     </>
38   );
39 }

overflow.css

  1 /* eslint-disable */
  2 .react-flow__node {
  3     display: flex;
  4     width: 220px;
  5     height: auto;
  6     border-radius: var(--node-border-radius);
  7     box-shadow: var(--node-box-shadow);
  8     letter-spacing: -.2px;
  9     font-weight: 500;
 10     font-family: 'Fira Mono', Monospace;
 11 }
 12 
 13 .ResicabelNode {
 14     position: relative;
 15     display: flex;
 16     overflow: hidden;
 17     flex-grow: 1;
 18     padding: 2px;
 19     width: 100%;
 20     height: 100%;
 21     border-radius: var(--node-border-radius);
 22 }
 23 
 24 .box {
 25     display: 'flex';
 26     align-items: 'center';
 27     justify-content: 'center';
 28 }
 29 
 30 .gradient::before {
 31     position: absolute;
 32     top: 50%;
 33     left: 50%;
 34     padding-bottom: calc(100% * 1.41421356237);
 35     width: calc(100% * 1.41421356237);
 36     border-radius: 100%;
 37     background: conic-gradient(from -160deg at 50% 50%, #e92a67 0deg, #a853ba 120deg, #2a8af6 240deg, #e92a67 360deg);
 38     content: '';
 39     transform: translate(-50%, -50%);
 40 }
 41 
 42 .wrapper.gradient::before {
 43     z-index: -1;
 44     background: conic-gradient(from -160deg at 50% 50%, #e92a67 0deg, #a853ba 120deg, #2a8af6 240deg, rgba(42, 138, 246, 0) 360deg);
 45     content: '';
 46     transform: translate(-50%, -50%) rotate(0deg);
 47     animation: spinner 4s linear infinite;
 48 }
 49 
 50 @keyframes spinner {
 51     100% {
 52         transform: translate(-50%, -50%) rotate(-360deg);
 53     }
 54 }
 55 
 56 .inner {
 57     position: relative;
 58     display: flex;
 59     flex-direction: column;
 60     flex-grow: 1;
 61     justify-content: center;
 62     padding: 0px 9px;
 63     border-radius: var(--node-border-radius);
 64     background: var(--bg-color);
 65 }
 66 
 67 .cloud {
 68     position: absolute;
 69     top: 0;
 70     right: 0;
 71     z-index: 1;
 72     display: flex;
 73     overflow: hidden;
 74     padding: 2px;
 75     width: 30px;
 76     height: 30px;
 77     border-radius: 100%;
 78     box-shadow: var(--node-box-shadow);
 79     transform: translate(50%, -50%);
 80     transform-origin: center center;
 81 }
 82 
 83 .nodelabel {
 84     margin-bottom: 2px;
 85     /* width:120px; */
 86     font-size: 12px;
 87     line-height: 1;
 88 }
 89 
 90 .subline {
 91     margin-top: -6px;
 92     color: #777;
 93     font-size: 10px;
 94 }
 95 
 96 .circle {
 97     width: 2em;
 98     height: 2em;
 99     border-radius: 50%;
100     background: #ebf2fd;
101     box-shadow: .1em .125em 0 0 rgb(15 28 63 / 13%);
102     text-align: center;
103     font-weight: bold;
104     line-height: 2em;
105 }
106 
107 .popcon_btn {
108     margin: 10px 0;
109 }
110 
111 .ant-popconfirm-message-icon {
112     display: none;
113 }
114 
115 .ant-popover-buttons {
116     display: none;
117 }
118 
119 .ant-popover-inner-content {
120     padding: 7px 10px;
121 }
122 
123 .ant-popconfirm-buttons {
124     display: none;
125 }
126 
127 .react-flow {
128     background-color: var(--bg-color);
129     color: var(--text-color);
130     --bg-color: rgb(17, 17, 17);
131     --text-color: rgb(243, 244, 246);
132     --node-border-radius: 10px;
133     --node-box-shadow:
134         10px 0 15px rgba(42, 138, 246, .3),
135         -10px 0 15px rgba(233, 42, 103, .3);
136 }
137 
138 .react-flow__node-turbo {
139     display: flex;
140     min-width: 150px;
141     height: 70px;
142     border-radius: var(--node-border-radius);
143     box-shadow: var(--node-box-shadow);
144     letter-spacing: -.2px;
145     font-weight: 500;
146     font-family: 'Fira Mono', Monospace;
147 }
148 
149 .react-flow__node-turbo .wrapper {
150     position: relative;
151     display: flex;
152     overflow: hidden;
153     flex-grow: 1;
154     padding: 2px;
155     border-radius: var(--node-border-radius);
156 }
157 
158 /* .react-flow__node-turbo.selected .wrapper.gradient::before {
159     z-index: -1;
160     background:
161     conic-gradient(from -160deg at 50% 50%, #e92a67 0deg, #a853ba 120deg, #2a8af6 240deg, rgba(42, 138, 246, 0)360deg);
162     content: '';
163     transform: translate(-50%, -50%) rotate(0deg);
164     animation: spinner 4s linear infinite;
165 } */
166 
167 @keyframes spinner {
168     100% {
169         transform: translate(-50%, -50%) rotate(-360deg);
170     }
171 }
172 
173 .react-flow__node-turbo .inner {
174     position: relative;
175     display: flex;
176     flex-direction: column;
177     flex-grow: 1;
178     justify-content: center;
179     padding: 16px 20px;
180     border-radius: var(--node-border-radius);
181     background: var(--bg-color);
182 }
183 
184 .react-flow__node-turbo .icon {
185     margin-right: 8px;
186 }
187 
188 .react-flow__node-turbo .title {
189     margin-bottom: 2px;
190     font-size: 16px;
191     line-height: 1;
192 }
193 
194 .react-flow__node-turbo .subline {
195     color: #777;
196     font-size: 12px;
197 }
198 
199 .react-flow__node-turbo .cloud {
200     position: absolute;
201     top: 0;
202     right: 0;
203     z-index: 1;
204     display: flex;
205     overflow: hidden;
206     padding: 2px;
207     width: 30px;
208     height: 30px;
209     border-radius: 100%;
210     box-shadow: var(--node-box-shadow);
211     transform: translate(50%, -50%);
212     transform-origin: center center;
213 }
214 
215 .react-flow__node-turbo .cloud div {
216     position: relative;
217     display: flex;
218     align-items: center;
219     flex-grow: 1;
220     justify-content: center;
221     border-radius: 100%;
222     background-color: var(--bg-color);
223 }
224 
225 .react-flow__handle {
226     opacity: 0;
227 }
228 
229 .react-flow__handle.source {
230     right: -10px;
231 }
232 
233 .react-flow__handle.target {
234     left: -10px;
235 }
236 
237 .react-flow__node:focus {
238     outline: none;
239 }
240 
241 .react-flow__edge .react-flow__edge-path {
242     stroke: url(#edge-gradient);
243     stroke-width: 2;
244     stroke-opacity: .75;
245 }
246 
247 .react-flow__controls button {
248     border: 1px solid #95679e;
249     border-bottom: none;
250     background-color: var(--bg-color);
251     color: var(--text-color);
252 }
253 
254 .react-flow__controls button:hover {
255     background-color: rgb(37, 37, 37);
256 }
257 
258 .react-flow__controls button:first-child {
259     border-radius: 5px 5px 0 0;
260 }
261 
262 .react-flow__controls button:last-child {
263     border-bottom: 1px solid #95679e;
264     border-radius: 0 0 5px 5px;
265 }
266 
267 .react-flow__controls button path {
268     fill: var(--text-color);
269 }
270 
271 .react-flow__attribution {
272     background: rgba(200, 200, 200, .2);
273     display: none;
274 }
275 
276 .react-flow__attribution a {
277     color: #95679e;
278 }
279 
280 .loadingbox {
281     display: flex;
282     justify-content: space-evenly;
283     align-items: center;
284 }
285 /* eslint-enable */

index.css

 

.react-flow__container {
    top: -30px !important;
}

.anticon .anticon-exclamation-circle {
    display: none;
}

.ant-popover-inner-content .ant-popover-buttons {
    display: none;
}

.ant-popover-message-title {
    display: none;
}

.a-Page-body .homeHeader {
    overflow: auto !important;
}

.react-flow__attribution.left {
    display: none;
}

.react-flow {
    overflow: auto;
    display: block;
}

.react-flow__edge-textbg {
    fill: #e2e6f3 !important;
}

 

 

 

标签:node,const,flow,react,2.0,data,id
From: https://www.cnblogs.com/qing1224/p/18321410

相关文章

  • 等保测评2.0标准下,企业安全合规新路径!全面剖析等保测评2.0要点,企业安全策略新视角!
    网络疆域,纷繁复杂,安全之虞,如影随形。及至等保测评2.0之令出,犹如春风化雨,润物无声,而企业安全合规之路,豁然开朗。云计算、大数据、物联网、移动互联网等新兴技术的广泛应用,为企业带来了新的安全挑战,为了应对这些挑战,国家发布了网络安全等级保护基本要求的第二代标准(简称“等保2.......
  • Airflow 2.7.2 触发badld编写的代码块 Triggerer的异步线程
    我正在运行Airflow版本2.7.2,并且我不断地在触发器日志中看到该问题。Triggerer'sasyncthreadwasblockedfor0.50seconds,likelybyabadly-writtentrigger.SetPYTHONASYNCIODEBUG=1togetmoreinformationonoverrunningcoroutines.我的触发器运行函数是......
  • 如何在tensorflow中使用gpu 1而不是gpu 0?
    我安装了TensorFlow,我的笔记本电脑有2个GPU,GPU0(集成显卡)和GPU1(专用显卡)。当我想使用TensorFlow时,我的可用GPU是1并且是GPU0如何将GPU0更改为GPU0?我的物理设备只有GPU0,没有GPU0你遇到的问题很有趣!看起来你的系统把集成显卡识别为了GPU1,而把独立显卡识别为了......
  • 代码随想录算法训练营第四天 | Leetcode 24 两两交换链表中的节点 Leetcode 19 删除链
    前言今天链表的内容突出一个注意细节,判空条件,头节点是否为空等等。采用虚拟头节点可以方便链表进行更改,还需要学会使用临时变量。 Leetcode24两两交换链表中的节点题目链接:https://leetcode.cn/problems/swap-nodes-in-pairs/代码随想录题解:代码随想录(programmercarl.......
  • React中引入使用本地图片
    1、css样式中,可以写成如下:.login{ //映射路径background:url('@images/img-login.png');background-size:100%100%;}.box{ //相对路径background:url('../assets/images/box.png');background-size:100%100%;}2、在jsx文件中importLoginLogo1......
  • React 18【实用教程】(2024最新版)
    搭建开发环境含@配置,react-developer-tools和ReduxDevTools下载安装https://blog.csdn.net/weixin_41192489/article/details/138523829JSX语法https://blog.csdn.net/weixin_41192489/article/details/138649165组件父子组件传值、兄弟组件传值、越层组件......
  • 从图像到万象,InternVL 2.0 书生·万象多模态大模型发布!
    从图像到万象,InternVL2.0书生·万象多模态大模型发布!OpenGVLab​目录收起代码开源/模型使用方法:76B大模型,司南评测优于GPT-4O8B端侧小模型,消费级单卡可部署试用Demo:正文开始!1.多模态大模型的开始,AGI的黎明三年演进,多模态先行者的实至名归之路2.更少资源、更高......
  • 学习React(添加交互)
    添加交互        界面上的控件会根据用户的输入而更新。例如,点击按钮切换轮播图的展示。在React中,随时间变化的数据被称为状态(state)。你可以向任何组件添加状态,并按需进行更新。在本章节中,你将学习如何编写处理交互的组件,更新它们的状态,并根据时间变化显示不同的效......
  • React中函数组件中闭包陷阱如何产生,如何解决?
    在什么情况下会产生闭包陷阱?在React中,当使用useState和useEffect以及useCallback时,我们必须得注意闭包陷阱,避免出现一些意外的行为什么是闭包陷阱?闭包是指一个函数可以访问其词法作用域之外的变量。闭包主要发生的集中情况?在useState中的闭包陷阱在useEffect中的闭......
  • react + Ant表单数据回调不更新
    刚学react,拿项目练手,有个问题恶心死了,折腾了好久,百度上也没找到很合适的,觉得有必要分享一下,不为别的,以后自己遇到了,再复习一下,方案不是最优解,但能解决问题业务描述   就是点击编辑,新增的时候,数据发生变化,我获取到了,第一次更新成功,后面就只会有第一次的值代码描述   ......