Browse Source

feat: 新增节点点击添加其他节点功能

liaojiaxing 2 months ago
parent
commit
cc20dae821

+ 4 - 5
apps/flowchart-designer/src/pages/designer/components/Nodes/index.tsx

@@ -1,7 +1,6 @@
 import { useState, useEffect } from "react";
 import { NodeType } from "@/enum";
 import { nodes } from "@/components/nodes";
-import { useModel } from "umi";
 
 import start from "@/assets/wf_icon_start.gif";
 import end from "@/assets/wf_icon_end.gif";
@@ -10,6 +9,7 @@ import and from "@/assets/wf_icon_and.gif";
 import handle from "@/assets/wf_icon_handle.gif";
 import judge from "@/assets/wf_icon_judge.gif";
 import or from "@/assets/wf_icon_or.gif";
+import { Graph } from "@antv/x6";
 
 const items = [
   { key: NodeType.START, icon: start, text: "开始" },
@@ -21,9 +21,8 @@ const items = [
   { key: NodeType.END, icon: end, text: "结束" },
 ];
 
-export default function index() {
-  const { graph } = useModel("flowModel");
-
+export default function NodeMenu(props: { graph?: Graph}) {
+  const { graph } = props;
   const handleAddNode = (type: NodeType) => { 
     const node = nodes.find(item => item.type === type);
     const { width = 100, height = 100} = graph?.getGraphArea() || {};
@@ -36,7 +35,7 @@ export default function index() {
         x: width / 2 - 150,
         y,
       },
-      data: {},
+      data: node?.data,
     });
   };
 

+ 2 - 1
apps/flowchart-designer/src/components/nodes/Content.tsx

@@ -15,6 +15,7 @@ export default ({
   items: MenuProps["items"],
   headerStyle: React.CSSProperties
 }) => {
+  const {name} = node.getData();
   const [editing, setEditing] = useState(false);
 
   return (
@@ -36,7 +37,7 @@ export default ({
             className="text-16px color-#383743 flex-1"
             onDoubleClick={() => setEditing(true)}
           >
-            开始节点
+            { name }
           </span>
         )}
         <Dropdown menu={{ items }} overlayStyle={{ width: 120 }}>

+ 1 - 0
apps/flowchart-designer/src/components/nodes/Handle.tsx

@@ -46,6 +46,7 @@ export default ({ node, graph }: { node: Node; graph: Graph }) => {
         }}
       />
 
+      <Port out={false} hovered={hovered} style={{ left: -7, cursor: "crosshair" }} />
       <Port hovered={hovered} style={{ right: -7, cursor: "crosshair" }} />
     </div>
   );

+ 39 - 20
apps/flowchart-designer/src/components/nodes/Port.tsx

@@ -1,18 +1,19 @@
 import { PlusOutlined } from "@ant-design/icons";
+import { Graph, Node } from "@antv/x6";
 import React from "react";
+import { Dropdown, Popover } from "antd";
+import NodeMenu from "../NodeMenu";
 
 export default function Port(props: {
   hovered: boolean;
   out?: boolean;
   style?: React.CSSProperties;
+  node?: Node,
+  graph?: Graph
 }) {
-  const { hovered, style = {}, out = true } = props;
+  const { hovered, style = {}, out = true, node, graph } = props;
   const [canAdd, setCanAdd] = React.useState(false);
 
-  const handleSetCanAdd = (value: boolean) => {
-    out && setCanAdd(value);
-  };
-
   const extraStyle = React.useMemo(() => {
     if (out && canAdd) {
       return {
@@ -22,22 +23,40 @@ export default function Port(props: {
     return {};
   }, [canAdd]);
 
+  const handleSetCanAdd = (value: boolean) => {
+    out && setCanAdd(value);
+    node?.setData({ lock: !!value });
+    graph?.togglePanning(!value);
+  };
+
+  const handleClickPort = (e: React.MouseEvent<HTMLDivElement>) => {
+    
+  };
+
   return (
-    <div
-      className="node-port flex items-center justify-center"
-      style={{
-        transform: hovered ? "scale(1.5) translateY(-50%)" : "scale(1) translateY(-50%)",
-        opacity: hovered ? 1 : 0.5,
-        ...style,
-        ...extraStyle
-      }}
-      onMouseEnter={() => handleSetCanAdd(true)}
-      onMouseLeave={() => handleSetCanAdd(false)}
+    <Popover
+      content={<NodeMenu graph={graph} />}
+      trigger="click"
+      placement="right"
+      arrow={false}
     >
-      { out && <PlusOutlined
-        className="transform scale-0 transition duration-300 color-#fff text-8px"
-        style={{ transform: canAdd ? "scale(1)" : "scale(0)" }}
-      />}
-    </div>
+      <div
+        className="node-port flex items-center justify-center"
+        style={{
+          transform: hovered ? "scale(1.5) translateY(-50%)" : "scale(1) translateY(-50%)",
+          opacity: hovered ? 1 : 0.5,
+          ...style,
+          ...extraStyle
+        }}
+        onMouseEnter={() => handleSetCanAdd(true)}
+        onMouseLeave={() => handleSetCanAdd(false)}
+        onClick={handleClickPort}
+      >
+        { out && <PlusOutlined
+          className="transform scale-0 transition duration-300 color-#fff text-8px"
+          style={{ transform: canAdd ? "scale(1)" : "scale(0)" }}
+        />}
+      </div>
+    </Popover>
   );
 }

+ 2 - 2
apps/flowchart-designer/src/components/nodes/Start.tsx

@@ -42,11 +42,11 @@ export default ({ node, graph }: { node: Node; graph: Graph }) => {
         items={items}
         headerStyle={{
           backgroundImage:
-            "linear-gradient(to bottom, rgba(191, 131, 1, 0.2), #ffffff)",
+            "linear-gradient(to bottom, rgba(82, 196, 26, 0.2), #ffffff)",
         }}
       />
 
-      <Port hovered={hovered} style={{ right: -7, cursor: "crosshair" }} />
+      <Port hovered={hovered} style={{ right: -7, cursor: "crosshair" }} node={node} graph={graph} />
     </div>
   );
 };

+ 7 - 7
apps/flowchart-designer/src/components/nodes/index.ts

@@ -90,13 +90,13 @@ const ports = {
 }
 
 export const nodes = [
-  { name: "start-node", component: Start, type: NodeType.START },
-  { name: "and-node", component: And, type: NodeType.AND },
-  { name: "autohandle-node", component: AutoHandle, type: NodeType.AUTO_PROCESS },
-  { name: "decision-node", component: Decision, type: NodeType.DECISION },
-  { name: "end-node", component: End, type: NodeType.END },
-  { name: "handle-node", component: Handle, type: NodeType.PROCESS },
-  { name: "link-node", component: Link, type: NodeType.LINK },
+  { name: "start-node", component: Start, type: NodeType.START, data: { name: '开始'} },
+  { name: "and-node", component: And, type: NodeType.AND, data: { name: '与'} },
+  { name: "autohandle-node", component: AutoHandle, type: NodeType.AUTO_PROCESS, data: { name: '自动处理'} },
+  { name: "decision-node", component: Decision, type: NodeType.DECISION, data: { name: '判断'} },
+  { name: "end-node", component: End, type: NodeType.END, data: { name: '结束'} },
+  { name: "handle-node", component: Handle, type: NodeType.PROCESS, data: { name: '处理'} },
+  { name: "link-node", component: Link, type: NodeType.LINK, data: { name: '连接'} },
 ];
 
 nodes.forEach((node) => {

+ 26 - 0
apps/flowchart-designer/src/models/flowModel.ts

@@ -20,6 +20,7 @@ export default function flowModel() {
       width: container.clientWidth,
       height: container.clientHeight,
       autoResize: true,
+      // 鼠标滚轮缩放
       mousewheel: {
         enabled: true,
         zoomAtMousePosition: true,
@@ -36,9 +37,34 @@ export default function flowModel() {
         visible: true,
         args: { background: true },
       },
+      // 节点连线交互
+      interacting: {
+        edgeLabelMovable: false,
+        edgeMovable: true,
+        nodeMovable: (view) => {
+          // 节点上锁状态无法移动
+          const data = view.cell.getData<{ lock: boolean }>();
+          return !data || !data?.lock;
+        },
+        arrowheadMovable: true,
+        vertexMovable: true,
+        vertexAddable: true,
+        vertexDeletable: true,
+        useEdgeTools: true,
+        magnetConnectable: true,
+        stopDelegateOnDragging: true,
+        toolsAddable: true,
+      },
+      // 连线配置
+      connecting: {
+        snap: true
+      }
     });
 
     instance.use(new Transform());
+    instance.use(new Snapline({
+      enabled: true
+    }));
     instance.use(
       new Selection({
         enabled: true,

+ 2 - 2
apps/flowchart-designer/src/pages/designer/components/Toolbar/index.tsx

@@ -8,7 +8,7 @@ import {
   ZoomOutOutlined,
 } from "@ant-design/icons";
 import { Button, Divider, Dropdown, Tooltip, Popover } from "antd";
-import Nodes from "../Nodes";
+import NodeMenu from "@/components/NodeMenu";
 import type { MenuProps } from "antd/lib";
 import { useRef, useEffect } from "react";
 import { MiniMap } from "@antv/x6-plugin-minimap";
@@ -74,7 +74,7 @@ export default function index() {
 
       <div className="w-260px h-40px rounded-12px bg-#fff box-shadow-sm flex items-center justify-between px-12px">
         <Popover
-          content={<Nodes />}
+          content={<NodeMenu graph={graph} />}
           trigger="click"
           placement="top"
           arrow={false}