From 0c009a44f7cd35489a56450d0aa2715b00b23c32 Mon Sep 17 00:00:00 2001
From: luxiaotao1123 <t1341870251@163.com>
Date: 星期六, 02 三月 2024 14:21:22 +0800
Subject: [PATCH] Merge branch 'master' of http://47.97.1.152:5880/r/zy-asrs-master

---
 zy-asrs-flow/src/pages/home/react-edge/edge1.less     |   12 
 zy-asrs-flow/src/pages/home/react-node/node1.jsx      |   16 
 zy-asrs-flow/package.json                             |    2 
 zy-asrs-flow/src/pages/home/index.jsx2                |  363 +++++++++++++++++
 zy-asrs-flow/src/pages/home/index.jsx                 |  108 +----
 zy-asrs-flow/src/components/Flow/GraphComponent.jsx   |  200 +++++++++
 zy-asrs-flow/src/pages/home/index.css                 |    5 
 zy-asrs-flow/src/pages/home/react-edge/edge1.jsx      |   22 +
 zy-asrs-flow/src/pages/home/index.jsx_old             |  256 ++++++++++++
 zy-asrs-flow/src/pages/home/react-node/node2.less     |   11 
 zy-asrs-flow/src/components/Flow/GraphConfig.jsx      |   74 +++
 zy-asrs-flow/src/pages/home/react-edge/dege2.jsx      |    9 
 zy-asrs-flow/src/pages/home/react-node/node2.jsx      |   15 
 zy-asrs-flow/src/components/Flow/StencilComponent.jsx |   92 ++++
 zy-asrs-flow/src/pages/home/react-edge/edge2.less     |   12 
 zy-asrs-flow/src/pages/home/index.less                |   20 
 zy-asrs-flow/src/pages/home/react-node/node1.less     |   11 
 zy-asrs-flow/src/config/setting.ts                    |    2 
 zy-asrs-flow/src/components/Flow/GraphTools.jsx       |   15 
 19 files changed, 1,159 insertions(+), 86 deletions(-)

diff --git a/zy-asrs-flow/package.json b/zy-asrs-flow/package.json
index 0925dc2..c8b677d 100644
--- a/zy-asrs-flow/package.json
+++ b/zy-asrs-flow/package.json
@@ -49,6 +49,8 @@
     "@ant-design/icons": "^5.3.0",
     "@ant-design/pro-components": "^2.6.48",
     "@antv/g6": "^4.8.24",
+    "@antv/x6": "^2.18.1",
+    "@antv/x6-plugin-stencil": "^2.1.5",
     "@antv/xflow": "^2.0.4",
     "@umijs/route-utils": "^2.2.2",
     "antd": "^5.13.2",
diff --git a/zy-asrs-flow/src/components/Flow/GraphComponent.jsx b/zy-asrs-flow/src/components/Flow/GraphComponent.jsx
new file mode 100644
index 0000000..c673a67
--- /dev/null
+++ b/zy-asrs-flow/src/components/Flow/GraphComponent.jsx
@@ -0,0 +1,200 @@
+import React, { useRef, useEffect } from "react";
+import { Graph, Shape } from "@antv/x6";
+import { Snapline } from '@antv/x6-plugin-snapline';
+import { Selection } from '@antv/x6-plugin-selection';
+import { Keyboard } from '@antv/x6-plugin-keyboard';
+import { Clipboard } from '@antv/x6-plugin-clipboard';
+import { Stencil } from '@antv/x6-plugin-stencil';
+import { History } from '@antv/x6-plugin-history'
+import { commonGraphPorts, commonGraphAttrs } from "./GraphConfig";
+
+
+export const GraphComponent = React.forwardRef((props, ref) => {
+    const container = useRef(null);
+    const stencilContainer = useRef(null);
+
+    useEffect(() => {
+        const graph = initGrap();
+        initBind(graph);
+        initStencil(graph);
+
+        return () => graph.dispose();
+    })
+
+    function initGrap() {
+        ref.current = new Graph({
+            container: container.current,
+            // width: document.documentElement.clientWidth,
+            // height: document.documentElement.clientHeight,
+            width: 200,
+            height: 500,
+            // grid: 1,
+            connecting: {
+                snap: true,
+                createEdge() {
+                    return new Shape.Edge({
+                        attrs: {
+                            line: {
+                                stroke: '#a0a0a0',
+                                strokeWidth: 1,
+                                targetMarker: {
+                                    name: 'classic',
+                                    size: 8,
+                                },
+                            },
+                        },
+                    })
+                },
+                connector: 'smooth',
+                allowMulti: true,
+                allowPort: true,
+            }
+        });
+
+        const graph = ref.current;
+
+        const rect = graph.addNode({
+            x: 60,
+            y: 60,
+            width: 120,
+            height: 40,
+            label: '璁㈠崟绠$悊',
+            ports: commonGraphPorts,
+            attrs: commonGraphAttrs,
+        });
+
+        const rect2 = graph.addNode({
+            x: 240,
+            y: 240,
+            width: 120,
+            height: 40,
+            label: '搴撳瓨绠$悊',
+            ports: commonGraphPorts,
+            attrs: commonGraphAttrs,
+        });
+
+        graph.use(
+            new Snapline({
+                enabled: true,
+            }),
+        )
+
+        graph.use(
+            new Selection({
+                enabled: true,
+                multiple: true,
+                rubberband: true,
+                movable: true,
+                showNodeSelectionBox: true,
+            }),
+        )
+
+        graph.use(
+            new History({
+                enabled: true,
+            }),
+        )
+
+        props.initHandle();//閫氱煡鐖剁粍浠跺垵濮嬪寲瀹屾垚
+        return graph;
+    }
+
+    function initStencil(graph) {
+        const stencil = new Stencil({
+            title: '鍏ㄩ儴',
+            target: graph,
+            search(cell, keyword) {
+                return cell.label.indexOf(keyword) !== -1
+            },
+            placeholder: '鎼滅储缁勪欢',
+            notFoundText: '缁勪欢涓嶅瓨鍦�',
+            collapsable: true,
+            stencilGraphHeight: 0,
+            groups: [
+                {
+                    name: 'group1',
+                    title: '甯哥敤缁勪欢',
+                }
+            ],
+        })
+
+        stencilContainer.current.appendChild(stencil.container)
+
+
+
+        const n1 = graph.createNode({
+            shape: "rect",
+            width: 80,
+            height: 40,
+            label: "榛樿缁勪欢",
+            attrs: commonGraphAttrs,
+        })
+
+        const n2 = graph.createNode({
+            shape: "rect",
+            width: 80,
+            height: 40,
+            label: "娴嬭瘯缁勪欢",
+            attrs: commonGraphAttrs,
+        })
+
+        stencil.load([n1, n2], 'group1')
+    }
+
+    function initBind(graph) {
+        graph.use(
+            new Clipboard({
+                enabled: true,
+            }),
+        )
+
+        graph.use(
+            new Keyboard({
+                enabled: true,
+                global: true,
+            })
+        )
+
+        graph.bindKey('ctrl+c', () => {
+            const cells = graph.getSelectedCells()
+            if (cells.length) {
+                graph.copy(cells)
+            }
+            return false
+        })
+
+        graph.bindKey('ctrl+v', () => {
+            if (!graph.isClipboardEmpty()) {
+                const cells = graph.paste({ offset: 32 })
+                graph.cleanSelection()
+                graph.select(cells)
+            }
+            return false
+        })
+
+        graph.bindKey('ctrl+z', () => {
+            graph.undo()
+            return false
+        })
+
+        graph.bindKey('ctrl+y', () => {
+            graph.redo()
+            return false
+        })
+
+        graph.bindKey('del', (e) => {
+            let nodeSelected = graph.getSelectedCells();  // 鑾峰彇閫変腑鐨勫厓绱�
+            nodeSelected.forEach(cell => {
+                graph.removeCell(cell)
+            })
+            return false
+        })
+    }
+
+    return (
+        <>
+            <div className="app-stencil" ref={stencilContainer} />
+            <div className="app-content" ref={container} />
+        </>
+    );
+})
diff --git a/zy-asrs-flow/src/components/Flow/GraphConfig.jsx b/zy-asrs-flow/src/components/Flow/GraphConfig.jsx
new file mode 100644
index 0000000..b2c9136
--- /dev/null
+++ b/zy-asrs-flow/src/components/Flow/GraphConfig.jsx
@@ -0,0 +1,74 @@
+import React, { useRef, useEffect } from "react";
+
+const commonGraphPorts = {
+    groups: {
+        top: {
+            position: 'top',
+            attrs: {
+                circle: {
+                    magnet: true,
+                    stroke: '#8f8f8f',
+                    r: 5,
+                },
+            },
+        },
+        bottom: {
+            position: 'bottom',
+            attrs: {
+                circle: {
+                    magnet: true,
+                    stroke: '#8f8f8f',
+                    r: 5,
+                },
+            },
+        },
+        left: {
+            position: 'left',
+            attrs: {
+                circle: {
+                    magnet: true,
+                    stroke: '#8f8f8f',
+                    r: 5,
+                },
+            },
+        },
+        right: {
+            position: 'right',
+            attrs: {
+                circle: {
+                    magnet: true,
+                    stroke: '#8f8f8f',
+                    r: 5,
+                },
+            },
+        },
+    },
+    items: [
+        {
+            id: 'port1',
+            group: 'top',
+        },
+        {
+            id: 'port2',
+            group: 'bottom',
+        },
+        {
+            id: 'port3',
+            group: 'left',
+        },
+        {
+            id: 'port4',
+            group: 'right',
+        },
+    ],
+}
+
+const commonGraphAttrs = {
+    body: {
+        fill: '#efefef',
+        stroke: '#4d4d4d',
+        strokeWidth: 2,
+    },
+}
+
+export { commonGraphPorts, commonGraphAttrs }
\ No newline at end of file
diff --git a/zy-asrs-flow/src/components/Flow/GraphTools.jsx b/zy-asrs-flow/src/components/Flow/GraphTools.jsx
new file mode 100644
index 0000000..11f4f30
--- /dev/null
+++ b/zy-asrs-flow/src/components/Flow/GraphTools.jsx
@@ -0,0 +1,15 @@
+import React, { useRef, useEffect } from "react";
+
+export const GraphTools = ({ graphRef,isReady }) => {
+
+    const exportData = () => {
+        const graph = graphRef.current;
+        if (graph) {
+            const data = graph.toJSON();
+            console.log(data);
+            // 杩欓噷浣犲彲浠ュ皢鏁版嵁鍙戦�佸埌鏈嶅姟鍣ㄦ垨淇濆瓨鍒版湰鍦�
+        }
+    }
+
+    return <button onClick={exportData}>瀵煎嚭鏁版嵁</button>;
+}
\ No newline at end of file
diff --git a/zy-asrs-flow/src/components/Flow/StencilComponent.jsx b/zy-asrs-flow/src/components/Flow/StencilComponent.jsx
new file mode 100644
index 0000000..bc6f7a7
--- /dev/null
+++ b/zy-asrs-flow/src/components/Flow/StencilComponent.jsx
@@ -0,0 +1,92 @@
+import React, { useRef, useEffect } from "react";
+import { Stencil } from '@antv/x6-plugin-stencil';
+
+export const StencilComponent = (({ graphRef, isReady, stencilContainer }) => {
+
+    useEffect(() => {
+        if (isReady) {
+            const graph = graphRef.current;
+
+            const stencil = new Stencil({
+                title: 'Stencil',
+                target: graphRef.current,
+                search(cell, keyword) {
+                    return cell.shape.indexOf(keyword) !== -1
+                },
+                placeholder: 'Search by shape name',
+                notFoundText: 'Not Found',
+                collapsable: true,
+                stencilGraphHeight: 0,
+                groups: [
+                    {
+                        name: 'group1',
+                        title: 'Group(Collapsable)',
+                    },
+                    {
+                        name: 'group2',
+                        title: 'Group',
+                        collapsable: false,
+                    },
+                ],
+            })
+
+            stencilContainer.current.appendChild(stencil.container)
+
+            const commonAttrs = {
+                body: {
+                    fill: '#fff',
+                    stroke: '#8f8f8f',
+                    strokeWidth: 1,
+                },
+            }
+
+            const n1 = graph.createNode({
+                shape: 'rect',
+                x: 40,
+                y: 40,
+                width: 80,
+                height: 40,
+                label: 'rect',
+                attrs: commonAttrs,
+            })
+
+            const n2 = graph.createNode({
+                shape: 'circle',
+                x: 180,
+                y: 40,
+                width: 40,
+                height: 40,
+                label: 'circle',
+                attrs: commonAttrs,
+            })
+
+            const n3 = graph.createNode({
+                shape: 'ellipse',
+                x: 280,
+                y: 40,
+                width: 80,
+                height: 40,
+                label: 'ellipse',
+                attrs: commonAttrs,
+            })
+
+            const n4 = graph.createNode({
+                shape: 'path',
+                x: 420,
+                y: 40,
+                width: 40,
+                height: 40,
+                // https://www.svgrepo.com/svg/13653/like
+                path: 'M24.85,10.126c2.018-4.783,6.628-8.125,11.99-8.125c7.223,0,12.425,6.179,13.079,13.543c0,0,0.353,1.828-0.424,5.119c-1.058,4.482-3.545,8.464-6.898,11.503L24.85,48L7.402,32.165c-3.353-3.038-5.84-7.021-6.898-11.503c-0.777-3.291-0.424-5.119-0.424-5.119C0.734,8.179,5.936,2,13.159,2C18.522,2,22.832,5.343,24.85,10.126z',
+                attrs: commonAttrs,
+                label: 'path123',
+            })
+
+            stencil.load([n1, n2], 'group1')
+            stencil.load([n3, n4], 'group2')
+        }
+
+    })
+
+    return <div className="app-stencil" ref={stencilContainer} />
+})
\ No newline at end of file
diff --git a/zy-asrs-flow/src/config/setting.ts b/zy-asrs-flow/src/config/setting.ts
index 9038b15..32a86f4 100644
--- a/zy-asrs-flow/src/config/setting.ts
+++ b/zy-asrs-flow/src/config/setting.ts
@@ -1,5 +1,5 @@
 // 鎺ュ彛鍦板潃
-export const API_BASE_URL: string = 'http://172.16.0.219:9090/wcs';
+export const API_BASE_URL: string = 'http://127.0.0.1:9090/wcs';
 
 // 椤圭洰鍚嶇О
 export const PROJECT_NAME: string = 'admin';
diff --git a/zy-asrs-flow/src/pages/home/index.css b/zy-asrs-flow/src/pages/home/index.css
index d3d4eaa..c08b77c 100644
--- a/zy-asrs-flow/src/pages/home/index.css
+++ b/zy-asrs-flow/src/pages/home/index.css
@@ -7,4 +7,9 @@
         background-color: rgba(255, 255, 255, 0.9);
         padding: 10px 8px;
         box-shadow: rgb(174, 174, 174) 0px 0px 10px;
+      }
+
+      .modalInput {
+        margin-top: 30px;
+        margin-bottom: 30px;
       }
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/index.jsx b/zy-asrs-flow/src/pages/home/index.jsx
index 92634cc..7ca20ba 100644
--- a/zy-asrs-flow/src/pages/home/index.jsx
+++ b/zy-asrs-flow/src/pages/home/index.jsx
@@ -1,91 +1,29 @@
-import React, { useEffect, useState } from 'react';
-import ReactDOM from 'react-dom';
-import { data } from './data';
-import G6 from '@antv/g6';
-import "./index.css"
+import React, { useEffect, useRef, useState } from "react";
+import { Graph, Shape } from "@antv/x6";
+import { GraphComponent } from "../../components/Flow/GraphComponent";
+import { GraphTools } from "../../components/Flow/GraphTools";
+import './index.less';
 
-export default function() {
-  const ref = React.useRef(null);
-  let graph = null;
+export default function () {
+    const graphRef = useRef(null);
+    const [ready, setReady] = useState(false);
 
-  useEffect(() => {
-    if (!graph) {
-      graph = new G6.Graph({
-        container: ReactDOM.findDOMNode(ref.current),
-        width: document.documentElement.clientWidth,
-        height: document.documentElement.clientHeight,
-        modes: {
-          default: [
-            'drag-canvas', 
-            'zoom-canvas',
-            'drag-node',
-          ],
-          edit: ['click-select']
-        },
-        layout: {
-          type: 'dagre',
-          direction: 'LR',
-        },
-        defaultNode: {
-          shape: 'node',
-          type: 'rect',
-          labelCfg: {
-            style: {
-              fill: '#000000A6',
-              fontSize: 14,
-            },
-          },
-          style: {
-            // 浠呭湪 keyShape 涓婄敓鏁�
-            fill: 'lightblue',
-            stroke: '#888',
-            lineWidth: 1,
-            radius: 7,
-          },
-          linkPoints: {
-            top: true,
-            bottom: true,
-            left: true,
-            right: true,
-            // ... 鍥涗釜鍦嗙殑鏍峰紡鍙互鍦ㄨ繖閲屾寚瀹�
-          },
-        },
-        defaultEdge: {
-          shape: 'polyline',
-        },
-        nodeStateStyles: {
-          // 鍚勭姸鎬佷笅鐨勬牱寮忥紝骞抽摵鐨勯厤缃」浠呭湪 keyShape 涓婄敓鏁堛�傞渶瑕佸湪鍏朵粬 shape 鏍峰紡涓婂搷搴旂姸鎬佸彉鍖栧垯鍐欐硶涓嶅悓锛屽弬瑙佷笂鏂囨彁鍒扮殑 閰嶇疆鐘舵�佹牱寮� 閾炬帴
-          hover: {
-            fillOpacity: 0.1,
-            lineWidth: 1,
-          },
-        },
-      });
+    const initHandle = () => {
+        setReady(true);
     }
-    graph.data(data);
-    graph.render();
-    
-    // 鐩戝惉榧犳爣杩涘叆鑺傜偣浜嬩欢
-    graph.on('node:mouseenter', (evt) => {
-      const node = evt.item;
-      // 婵�娲昏鑺傜偣鐨� hover 鐘舵��
-      graph.setItemState(node, 'hover', true);
-    });
 
-    // 鐩戝惉榧犳爣绂诲紑鑺傜偣浜嬩欢
-    graph.on('node:mouseleave', (evt) => {
-      const node = evt.item;
-      // 鍏抽棴璇ヨ妭鐐圭殑 hover 鐘舵��
-      graph.setItemState(node, 'hover', false);
-    });
+    useEffect(() => {
+        if (ready) {
+            // 浣犻渶瑕佸湪loading鐘舵�佹敼鍙樺悗鎵ц鐨勪唬鐮�
+            console.log('graphRef is ready:', graphRef.current);
+        }
+    }, [ready]);
 
-    graph.on('node:click', (evt) => {
-      const node = evt.item;
-      const model = node.getModel();
-      // 鍦ㄨ繖閲屾墦寮�妯℃�佹鎴栬�呯紪杈戣〃鏍硷紝浼犲叆model浠ヤ究鐢ㄤ簬鍒濆鍖栫紪杈戝唴瀹�
-      showModal(model);
-    });
-  }, []);
-
-  return <div ref={ref}></div>;
+    return (
+        
+        <div className="stencil-app">
+            <GraphTools isReady={ready} graphRef={graphRef} />
+            <GraphComponent ref={graphRef} initHandle={initHandle} />
+        </div>
+    );
 }
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/index.jsx2 b/zy-asrs-flow/src/pages/home/index.jsx2
new file mode 100644
index 0000000..f1596f5
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/index.jsx2
@@ -0,0 +1,363 @@
+import React, { useRef, useEffect } from "react";
+import { Graph, Shape } from "@antv/x6";
+import { Snapline } from '@antv/x6-plugin-snapline';
+import { Selection } from '@antv/x6-plugin-selection';
+import { Keyboard } from '@antv/x6-plugin-keyboard';
+import { Clipboard } from '@antv/x6-plugin-clipboard';
+import { Stencil } from '@antv/x6-plugin-stencil';
+import './index.less';
+
+
+export default function () {
+    const container = useRef(null);
+    const stencilContainer = useRef(null);
+    const graphRef = useRef(null);
+
+    useEffect(() => {
+        graphRef.current = new Graph({
+            container: container.current,
+            // width: document.documentElement.clientWidth,
+            // height: document.documentElement.clientHeight,
+            // width: 200,
+            // height: 300,
+            // grid: 1,
+            connecting: {
+                snap: true,
+                createEdge() {
+                    return new Shape.Edge({
+                        attrs: {
+                            line: {
+                                stroke: '#a0a0a0',
+                                strokeWidth: 1,
+                                targetMarker: {
+                                    name: 'classic',
+                                    size: 8,
+                                },
+                            },
+                        },
+                    })
+                },
+                connector: 'smooth',
+                allowMulti: true,
+                allowPort: true,
+            },
+        });
+
+        const graph = graphRef.current;
+
+        // // 寮�鍚妭鐐瑰彲浠ヨ鎷栧姩浜や簰
+        // graph.on('cell:mouseenter', ({ cell }) => {
+        //     if (cell.isNode()) {
+        //         cell.addTools([
+        //             {
+        //                 name: 'boundary',
+        //             },
+        //         ])
+        //     }
+        // })
+
+        // graph.on('node:mouseleave', ({ cell }) => {
+        //     cell.removeTools()
+        // })
+
+        const rect = graph.addNode({
+            x: 60,
+            y: 60,
+            width: 120,
+            height: 40,
+            label: '璁㈠崟绠$悊',
+            ports: {
+                groups: {
+                    top: {
+                        position: 'top',
+                        attrs: {
+                            circle: {
+                                magnet: true,
+                                stroke: '#8f8f8f',
+                                r: 5,
+                            },
+                        },
+                    },
+                    bottom: {
+                        position: 'bottom',
+                        attrs: {
+                            circle: {
+                                magnet: true,
+                                stroke: '#8f8f8f',
+                                r: 5,
+                            },
+                        },
+                    },
+                    left: {
+                        position: 'left',
+                        attrs: {
+                            circle: {
+                                magnet: true,
+                                stroke: '#8f8f8f',
+                                r: 5,
+                            },
+                        },
+                    },
+                    right: {
+                        position: 'right',
+                        attrs: {
+                            circle: {
+                                magnet: true,
+                                stroke: '#8f8f8f',
+                                r: 5,
+                            },
+                        },
+                    },
+                },
+                items: [
+                    {
+                        id: 'port1',
+                        group: 'top',
+                    },
+                    {
+                        id: 'port2',
+                        group: 'bottom',
+                    },
+                    {
+                        id: 'port3',
+                        group: 'left',
+                    },
+                    {
+                        id: 'port4',
+                        group: 'right',
+                    },
+                ],
+            },
+            attrs: {
+                body: {
+                    fill: '#efefef',
+                    stroke: '#4d4d4d',
+                    strokeWidth: 2,
+                },
+            },
+        });
+
+        const rect2 = graph.addNode({
+            x: 240,
+            y: 240,
+            width: 120,
+            height: 40,
+            label: '搴撳瓨绠$悊',
+            ports: {
+                groups: {
+                    top: {
+                        position: 'top',
+                        attrs: {
+                            circle: {
+                                magnet: true,
+                                stroke: '#8f8f8f',
+                                r: 5,
+                            },
+                        },
+                    },
+                    bottom: {
+                        position: 'bottom',
+                        attrs: {
+                            circle: {
+                                magnet: true,
+                                stroke: '#8f8f8f',
+                                r: 5,
+                            },
+                        },
+                    },
+                    left: {
+                        position: 'left',
+                        attrs: {
+                            circle: {
+                                magnet: true,
+                                stroke: '#8f8f8f',
+                                r: 5,
+                            },
+                        },
+                    },
+                    right: {
+                        position: 'right',
+                        attrs: {
+                            circle: {
+                                magnet: true,
+                                stroke: '#8f8f8f',
+                                r: 5,
+                            },
+                        },
+                    },
+                },
+                items: [
+                    {
+                        id: 'port1',
+                        group: 'top',
+                    },
+                    {
+                        id: 'port2',
+                        group: 'bottom',
+                    },
+                    {
+                        id: 'port3',
+                        group: 'left',
+                    },
+                    {
+                        id: 'port4',
+                        group: 'right',
+                    },
+                ],
+            },
+            attrs: {
+                body: {
+                    fill: '#efefef',
+                    stroke: '#4d4d4d',
+                    strokeWidth: 2,
+                },
+            },
+        });
+
+        // graph.addEdge({
+        //     source: { cell: rect.id },
+        //     target: { cell: rect2.id },
+        // });
+
+        graph.use(
+            new Snapline({
+                enabled: true,
+            }),
+        )
+
+        graph.use(
+            new Selection({
+                enabled: true,
+                multiple: true,
+                rubberband: true,
+                movable: true,
+                showNodeSelectionBox: true,
+            }),
+        )
+
+        graph.use(
+            new Clipboard({
+                enabled: true,
+            }),
+        )
+
+        graph.use(
+            new Keyboard({
+                enabled: true,
+                global: true,
+            })
+        )
+
+        graph.bindKey('ctrl+c', () => {
+            const cells = graph.getSelectedCells()
+            if (cells.length) {
+                graph.copy(cells)
+            }
+            return false
+        })
+
+        graph.bindKey('ctrl+v', () => {
+            if (!graph.isClipboardEmpty()) {
+                const cells = graph.paste({ offset: 32 })
+                graph.cleanSelection()
+                graph.select(cells)
+            }
+            return false
+        })
+
+        const stencil = new Stencil({
+            title: 'Stencil',
+            target: graph,
+            search(cell, keyword) {
+                return cell.shape.indexOf(keyword) !== -1
+            },
+            placeholder: 'Search by shape name',
+            notFoundText: 'Not Found',
+            collapsable: true,
+            stencilGraphHeight: 0,
+            groups: [
+                {
+                    name: 'group1',
+                    title: 'Group(Collapsable)',
+                },
+                {
+                    name: 'group2',
+                    title: 'Group',
+                    collapsable: false,
+                },
+            ],
+        })
+
+        stencilContainer.current.appendChild(stencil.container)
+
+        const commonAttrs = {
+            body: {
+                fill: '#fff',
+                stroke: '#8f8f8f',
+                strokeWidth: 1,
+            },
+        }
+
+        const n1 = graph.createNode({
+            shape: 'rect',
+            x: 40,
+            y: 40,
+            width: 80,
+            height: 40,
+            label: 'rect',
+            attrs: commonAttrs,
+        })
+
+        const n2 = graph.createNode({
+            shape: 'circle',
+            x: 180,
+            y: 40,
+            width: 40,
+            height: 40,
+            label: 'circle',
+            attrs: commonAttrs,
+        })
+
+        const n3 = graph.createNode({
+            shape: 'ellipse',
+            x: 280,
+            y: 40,
+            width: 80,
+            height: 40,
+            label: 'ellipse',
+            attrs: commonAttrs,
+        })
+
+        const n4 = graph.createNode({
+            shape: 'path',
+            x: 420,
+            y: 40,
+            width: 40,
+            height: 40,
+            // https://www.svgrepo.com/svg/13653/like
+            path: 'M24.85,10.126c2.018-4.783,6.628-8.125,11.99-8.125c7.223,0,12.425,6.179,13.079,13.543c0,0,0.353,1.828-0.424,5.119c-1.058,4.482-3.545,8.464-6.898,11.503L24.85,48L7.402,32.165c-3.353-3.038-5.84-7.021-6.898-11.503c-0.777-3.291-0.424-5.119-0.424-5.119C0.734,8.179,5.936,2,13.159,2C18.522,2,22.832,5.343,24.85,10.126z',
+            attrs: commonAttrs,
+            label: 'path',
+        })
+
+        stencil.load([n1, n2], 'group1')
+        stencil.load([n3, n4], 'group2')
+
+        return () => graph.dispose();
+    }, []);
+
+    const exportData = () => {
+        if (graphRef.current) {
+            const data = graphRef.current.toJSON();
+            console.log(data);
+        }
+    }
+
+    return (
+        <div className="stencil-app">
+            <button onClick={exportData}>瀵煎嚭鏁版嵁</button>
+            <div className="app-stencil" ref={stencilContainer} />
+            <div className="app-content" ref={container} />
+        </div>
+    );
+}
diff --git a/zy-asrs-flow/src/pages/home/index.jsx_old b/zy-asrs-flow/src/pages/home/index.jsx_old
new file mode 100644
index 0000000..5ba00fe
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/index.jsx_old
@@ -0,0 +1,256 @@
+import React, { useEffect, useState,useRef } from 'react';
+import ReactDOM from 'react-dom';
+import { data } from './data';
+import G6 from '@antv/g6';
+import "./index.css"
+import { Modal, Input } from 'antd';
+
+export default function() {
+  const ref = useRef(null);
+  const graphRef = useRef(null);
+
+  useEffect(() => {
+    if (!graphRef.current) {
+      // // 娉ㄥ唽鑷畾涔夎涓�
+      // G6.registerBehavior('drag-line', {
+      //   getEvents() {
+      //     return {
+      //       'edge:mousedown': 'onDragStart',
+      //       'canvas:mousemove': 'onDrag',
+      //       'canvas:mouseup': 'onDragEnd',
+      //     }
+      //   },
+      //   onDragStart(ev) {
+      //     const edge = ev.item;
+      //     this.edge = edge;
+      //     this.dragging = true;
+      //     console.log(ev)
+      //   },
+      //   onDrag(ev) {
+      //     if (!this.dragging) {
+      //       return;
+      //     }
+      //     const { x, y } = ev;
+
+      //     // 鏇存柊杈圭殑 target锛堢粓鐐癸級
+      //     this.edge.update({
+      //       target: { x, y },
+      //     });
+      //   },
+      //   onDragEnd() {
+      //     this.edge = null;
+      //     this.dragging = false;
+      //   }
+      // });
+
+      G6.registerBehavior('click-add-edge', {
+        getEvents() {
+          return {
+            'node:click': 'onClick',
+            mousemove: 'onMousemove',
+            'edge:click': 'onEdgeClick' // 鐐瑰嚮绌虹櫧澶勶紝鍙栨秷杈�
+          };
+        },
+        onClick(ev) {
+          const node = ev.item;
+          const graph = this.graph;
+          const point = {
+            x: ev.x,
+            y: ev.y
+          };
+          const model = node.getModel();
+          if (this.addingEdge && this.edge) {
+            graph.updateItem(this.edge, {
+              target: model.id
+            });
+            // graph.setItemState(this.edge, 'selected', true);
+            this.edge = null;
+            this.addingEdge = false;
+          } else {
+            this.edge = graph.addItem('edge', {
+              source: model.id,
+              target: point
+            });
+            this.addingEdge = true;
+          }
+        },
+        onMousemove(ev) {
+          const point = {
+            x: ev.x,
+            y: ev.y
+          };
+          if (this.addingEdge && this.edge) {
+            this.graph.updateItem(this.edge, {
+              target: point
+            });
+          }
+        },
+        onEdgeClick(ev) {
+          const currentEdge = ev.item;
+          // 鎷栨嫿杩囩▼涓紝鐐瑰嚮浼氱偣鍑诲埌鏂板鐨勮竟涓�
+          if (this.addingEdge && this.edge == currentEdge) {
+            graph.removeItem(this.edge);
+            this.edge = null;
+            this.addingEdge = false;
+          }
+        }
+      });
+
+
+      graphRef.current = new G6.Graph({
+        container: ReactDOM.findDOMNode(ref.current),
+        width: document.documentElement.clientWidth,
+        height: document.documentElement.clientHeight,
+        modes: {
+          default: [
+            'drag-canvas', 
+            'zoom-canvas',
+            'drag-node',
+            'drag-line',
+            'click-add-edge'
+          ],
+          addEdge: ['click-add-edge', 'click-select']
+        },
+        layout: {
+          type: 'dagre',
+          direction: 'LR',
+        },
+        defaultNode: {
+          shape: 'node',
+          type: 'rect',
+          labelCfg: {
+            style: {
+              fill: '#000000A6',
+              fontSize: 14,
+            },
+          },
+          style: {
+            // 浠呭湪 keyShape 涓婄敓鏁�
+            fill: 'lightblue',
+            stroke: '#888',
+            lineWidth: 1,
+            radius: 7,
+          },
+          // linkPoints: {
+          //   top: true,
+          //   bottom: true,
+          //   left: true,
+          //   right: true,
+          //   // ... 鍥涗釜鍦嗙殑鏍峰紡鍙互鍦ㄨ繖閲屾寚瀹�
+          // },
+        },
+        defaultEdge: {
+          shape: 'polyline',
+        },
+        nodeStateStyles: {
+          // 鍚勭姸鎬佷笅鐨勬牱寮忥紝骞抽摵鐨勯厤缃」浠呭湪 keyShape 涓婄敓鏁堛�傞渶瑕佸湪鍏朵粬 shape 鏍峰紡涓婂搷搴旂姸鎬佸彉鍖栧垯鍐欐硶涓嶅悓锛屽弬瑙佷笂鏂囨彁鍒扮殑 閰嶇疆鐘舵�佹牱寮� 閾炬帴
+          hover: {
+            fillOpacity: 0.1,
+            lineWidth: 1,
+          },
+        },
+      });
+    }
+
+    const graph = graphRef.current;
+
+    graph.data(data);
+    graph.render();
+
+    graph.setMode("edit")
+    
+    // 鐩戝惉榧犳爣杩涘叆鑺傜偣浜嬩欢
+    graph.on('node:mouseenter', (evt) => {
+      const node = evt.item;
+      // 婵�娲昏鑺傜偣鐨� hover 鐘舵��
+      graph.setItemState(node, 'hover', true);
+    });
+
+    // 鐩戝惉榧犳爣绂诲紑鑺傜偣浜嬩欢
+    graph.on('node:mouseleave', (evt) => {
+      const node = evt.item;
+      // 鍏抽棴璇ヨ妭鐐圭殑 hover 鐘舵��
+      graph.setItemState(node, 'hover', false);
+    });
+
+    graph.on('node:dblclick', (evt) => {
+      const node = evt.item;
+      const model = node.getModel();
+      // 鍦ㄦ澶勬墽琛屾樉绀轰竴涓ā鎬佹鎴栬緭鍏ユ鐨勬搷浣滐紝灏唌odel.id浼犲叆鐢ㄦ潵纭畾鍝釜鑺傜偣锛宮odel.label浼犲叆鐢ㄦ潵鏄剧ず褰撳墠鑺傜偣鏂囧瓧
+      showModal(model.id, model.label);
+    });
+
+    // 娣诲姞琛屼负
+    graph.on('edge:click', (evt) => {
+      const { item } = evt;
+  
+      // 鑾峰彇杈圭殑妯″瀷鏁版嵁
+      const model = item.getModel();
+      
+      // 鍒囨崲閫変腑鐘舵��
+      if (model.style?.stroke === '#f00') {
+        // 濡傛灉杈瑰浜庨�変腑鐘舵�侊紝鍒欏彇娑堥�変腑
+        graph.updateItem(item, {
+          style: {
+            ...model.style,
+            stroke: '#000',
+          },
+        });
+      } else {
+        // 濡傛灉杈规湭琚�変腑锛屽垯閫変腑褰撳墠杈�
+        graph.updateItem(item, {
+          style: {
+            ...model.style,
+            stroke: '#f00',
+          },
+        });
+      }
+    });
+  }, []);
+
+  const [modalVisible, setModalVisible] = useState(false);
+  const [currentNode, setCurrentNode] = useState(null);
+  const [currentLabel, setCurrentLabel] = useState('');
+
+  const showModal = (id, label) => {
+    setCurrentNode(id);        // 璁剧疆褰撳墠鑺傜偣
+    setCurrentLabel(label);    // 璁剧疆褰撳墠鑺傜偣鏂囧瓧
+    setModalVisible(true);     // 鎵撳紑妯℃�佹
+  };
+
+  const updateLabel = (nodeId, newLabel) => {
+    if(graphRef.current){
+      const node = graphRef.current.findById(nodeId);
+      graphRef.current.update(node, {
+        label: newLabel,
+      });
+      graphRef.current.refresh();
+    }
+  };
+
+  const handleOk = () => {
+    // 鐐瑰嚮纭畾鍚庢洿鏂拌妭鐐�
+    updateLabel(currentNode, currentLabel);
+    setModalVisible(false);
+  };
+
+  const handleCancel = () => {
+    // 鍏抽棴妯℃�佹
+    setModalVisible(false);
+  };
+
+  return (
+    <>
+      <div ref={ref} />  {/* g6鍥捐〃鐨勫鍣� */}
+      <Modal
+        open={modalVisible}
+        onOk={handleOk}
+        onCancel={handleCancel}
+      >
+        <div className='modalInput'>
+        <Input value={currentLabel} onChange={e => setCurrentLabel(e.target.value)} />
+        </div>
+      </Modal>
+    </>
+  );
+}
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/index.less b/zy-asrs-flow/src/pages/home/index.less
new file mode 100644
index 0000000..b6596ec
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/index.less
@@ -0,0 +1,20 @@
+.stencil-app {
+    display: flex;
+    padding: 0;
+    font-family: sans-serif;
+  
+    .app-stencil {
+      position: relative;
+      width: 300px;
+      border: 1px solid #f0f0f0;
+    }
+  
+    .app-content {
+      flex: 1;
+      height: 380px;
+      margin-right: 8px;
+      margin-left: 8px;
+      box-shadow: 0 0 10px 1px #e9e9e9;
+    }
+  }
+  
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/react-edge/dege2.jsx b/zy-asrs-flow/src/pages/home/react-edge/dege2.jsx
new file mode 100644
index 0000000..67d4e17
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/react-edge/dege2.jsx
@@ -0,0 +1,9 @@
+import React from 'react'
+import { NsGraph } from '@antv/xflow'
+import { useAppContext } from '@antv/xflow'
+import './edge2.less'
+
+const Edge2 = props => {
+  return <div className="edge2-container">React2</div>
+}
+export default Edge2
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/react-edge/edge1.jsx b/zy-asrs-flow/src/pages/home/react-edge/edge1.jsx
new file mode 100644
index 0000000..e934e66
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/react-edge/edge1.jsx
@@ -0,0 +1,22 @@
+import React from 'react'
+import { NsGraph } from '@antv/xflow'
+import { useAppContext } from '@antv/xflow'
+import { Tooltip } from 'antd'
+import './edge1.less'
+
+const Edge1 = props => {
+  const ctx = useAppContext()
+  // console.log('edge useAppContext', ctx);
+  // console.log('edge props:', props);
+  return (
+    <div className="edge1-container">
+      <Tooltip
+        title="杩欐槸杩炵嚎涓婃覆鏌撶殑React鍐呭"
+        // defaultVisible={true}
+      >
+        <div>hover鎴�</div>
+      </Tooltip>
+    </div>
+  )
+}
+export default Edge1
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/react-edge/edge1.less b/zy-asrs-flow/src/pages/home/react-edge/edge1.less
new file mode 100644
index 0000000..0893e2b
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/react-edge/edge1.less
@@ -0,0 +1,12 @@
+.edge1-container {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    height: 100%;
+    font-size: 14px;
+    background-color: #fff;
+    border: 1px solid #690;
+    border-radius: 4px;
+    cursor: pointer;
+  }
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/react-edge/edge2.less b/zy-asrs-flow/src/pages/home/react-edge/edge2.less
new file mode 100644
index 0000000..041d057
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/react-edge/edge2.less
@@ -0,0 +1,12 @@
+.edge2-container {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    height: 100%;
+    font-size: 14px;
+    background-color: #fff;
+    border: 1px solid #690;
+    border-radius: 4px;
+    cursor: pointer;
+  }
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/react-node/node1.jsx b/zy-asrs-flow/src/pages/home/react-node/node1.jsx
new file mode 100644
index 0000000..06965f9
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/react-node/node1.jsx
@@ -0,0 +1,16 @@
+import { NsGraph } from '@antv/xflow'
+import React from 'react'
+import './node1.less'
+
+const Node1 = props => {
+  /**
+   * 1. 鑺傜偣鐨勬暟鎹�佷綅缃俊鎭�氳繃props鍙�
+   * 2. 褰撹妭鐐硅瑙﹀彂鏇存柊鏃�, props杩斿洖鐨勬暟鎹篃浼氬姩鎬佹洿鏂�, 瑙﹀彂鑺傜偣閲嶆柊娓叉煋
+   */
+  return (
+    <div className="node1-container">
+      <div>{'React鑺傜偣1'}</div>
+    </div>
+  )
+}
+export default Node1
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/react-node/node1.less b/zy-asrs-flow/src/pages/home/react-node/node1.less
new file mode 100644
index 0000000..609ef41
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/react-node/node1.less
@@ -0,0 +1,11 @@
+.node1-container {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    height: 100%;
+    font-weight: 600;
+    background-color: #fff;
+    border: 1px solid #873bf4;
+    border-radius: 4px;
+  }
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/react-node/node2.jsx b/zy-asrs-flow/src/pages/home/react-node/node2.jsx
new file mode 100644
index 0000000..77aa261
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/react-node/node2.jsx
@@ -0,0 +1,15 @@
+import { NsGraph } from '@antv/xflow'
+import React from 'react'
+import { useAppContext } from '@antv/xflow'
+import './node2.less'
+
+const Node2 = props => {
+  const ctx = useAppContext()
+
+  return (
+    <div className="node2-container">
+      <div>{'React鑺傜偣2'}</div>
+    </div>
+  )
+}
+export default Node2
\ No newline at end of file
diff --git a/zy-asrs-flow/src/pages/home/react-node/node2.less b/zy-asrs-flow/src/pages/home/react-node/node2.less
new file mode 100644
index 0000000..6e85bb8
--- /dev/null
+++ b/zy-asrs-flow/src/pages/home/react-node/node2.less
@@ -0,0 +1,11 @@
+.node2-container {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    height: 100%;
+    font-weight: 600;
+    background-color: #fff;
+    border: 1px solid #dd4a68;
+    border-radius: 4px;
+  }
\ No newline at end of file

--
Gitblit v1.9.1