Pārlūkot izejas kodu

feat: 添加节点库面板

liaojiaxing 12 stundas atpakaļ
vecāks
revīzija
5f0f55590f
21 mainītis faili ar 864 papildinājumiem un 129 dzēšanām
  1. 1 0
      apps/designer/src/components/mindMap/Text.tsx
  2. 8 1
      apps/designer/src/pages/mindmap/render/index.tsx
  3. 1 1
      apps/flowchart-designer/.umirc.ts
  4. 3 44
      apps/flowchart-designer/src/components/NodeMenu.tsx
  5. 2 2
      apps/flowchart-designer/src/components/SelectModal.tsx
  6. 4 2
      apps/flowchart-designer/src/components/nodes/Content.tsx
  7. 1 1
      apps/flowchart-designer/src/components/nodes/PopoverNode.tsx
  8. 317 67
      apps/flowchart-designer/src/components/nodes/index.ts
  9. 1 1
      apps/flowchart-designer/src/models/flowModel.ts
  10. 56 0
      apps/flowchart-designer/src/pages/designer/components/Libary/index.tsx
  11. 3 3
      apps/flowchart-designer/src/pages/designer/components/Toolbar/index.tsx
  12. 22 2
      apps/flowchart-designer/src/pages/designer/index.tsx
  13. 53 0
      apps/flowchart-designer/src/pages/designer/modals/action/index.tsx
  14. 45 0
      apps/flowchart-designer/src/pages/designer/modals/admision/index.tsx
  15. 20 0
      apps/flowchart-designer/src/pages/designer/modals/assessment/index.tsx
  16. 21 0
      apps/flowchart-designer/src/pages/designer/modals/autoHandle/index.tsx
  17. 48 0
      apps/flowchart-designer/src/pages/designer/modals/displayConfig/index.tsx
  18. 71 0
      apps/flowchart-designer/src/pages/designer/modals/general/index.tsx
  19. 134 5
      apps/flowchart-designer/src/pages/designer/modals/index.tsx
  20. 16 0
      apps/flowchart-designer/src/pages/designer/modals/processor/index.tsx
  21. 37 0
      apps/flowchart-designer/src/pages/designer/modals/skip/index.tsx

+ 1 - 0
apps/designer/src/components/mindMap/Text.tsx

@@ -148,6 +148,7 @@ export default function Text(props: {
           ...style,
           opacity: isEditing ? 0 : 1,
         }}
+        className="min-w-20px"
         onDoubleClick={() => handleSetEditing(true)}
       >
         {label}

+ 8 - 1
apps/designer/src/pages/mindmap/render/index.tsx

@@ -42,7 +42,7 @@ export const renderMindMap = ({
   returnCells = false,
   showTool = true,
 }: RenderParams) => {
-  const cells: Cell[] = [];
+  let cells: Cell[] = [];
   topics.forEach((topic) => {
     // 遍历出层次结构
     const result: HierarchyResult = hierarchyMethodMap[structure]?.(
@@ -138,12 +138,19 @@ export const renderMindMap = ({
     traverse(result);
   });
 
+  // 根据id去重
+  cells = cells.filter(
+    (item, index, arr) => arr.findIndex((i) => i.id === item.id) === index
+  );
+
   // 数据为概要节点 返回节点数据
   if (returnCells) {
     return cells;
   }
 
   const oldCells = graph.getCells();
+
+  console.log("oldCells", oldCells, cells);
   // 移除不要的节点及对应的边
   oldCells.forEach((cell) => {
     if (!cells.find((item) => cell.id === item.id)) {

+ 1 - 1
apps/flowchart-designer/.umirc.ts

@@ -43,7 +43,7 @@ export default defineConfig({
   },
   history: { type: 'hash' },
   routes: [
-    { path: "/", component: "designer" },
+    { path: "/:id", component: "designer" },
     { path: "/*", component: '404' }
   ],
   npmClient: 'pnpm'

+ 3 - 44
apps/flowchart-designer/src/components/NodeMenu.tsx

@@ -1,25 +1,9 @@
 import { useState, useEffect } from "react";
 import { NodeType } from "@/enum";
-import { nodes, aiNode } from "@/components/nodes";
-
-import start from "@/assets/wf_icon_start.gif";
-import end from "@/assets/wf_icon_end.gif";
-import autohandle from "@/assets/wf_icon_autohandle.gif";
-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 { nodes, nodeItems } from "@/components/nodes";
 import { Graph, Node } from "@antv/x6";
 
-const items = [
-  { key: NodeType.START, name: "start-node", icon: start, text: "开始" },
-  { key: NodeType.PROCESS, name: "process-node", icon: handle, text: "处理" },
-  { key: NodeType.AUTO_PROCESS, name: "auto-process-node", icon: autohandle, text: "自动处理" },
-  { key: NodeType.DECISION, name: "decision-node", icon: or, text: "判断" },
-  { key: NodeType.AND, name: "and-node", icon: and, text: "与" },
-  { key: NodeType.LINK, name: "link-node", icon: judge, text: "连接" },
-  { key: NodeType.END, name: "end-node", icon: end, text: "结束" },
-];
+
 
 export default function NodeMenu(props: {
   graph?: Graph;
@@ -47,34 +31,9 @@ export default function NodeMenu(props: {
     props?.onChange?.(n);
   };
 
-  const handleAddAiNode = (item: any) => {
-    const { width = 100, height = 100 } = graph?.getGraphArea() || {};
-    const rect = graph?.getAllCellsBBox();
-    const y = rect ? rect.y + rect.height - 80 : height / 2 - 100;
-
-    const n = graph?.addNode({
-      shape: item?.name,
-      position: props?.position || {
-        x: width / 2 - 150,
-        y,
-      },
-      data: {
-        name: item.data.name,
-        icon: item.icon,
-        color: item.color,
-        style: item.style,
-        port: item.port,
-        type: item.data.type,
-        hidePort: item.data.hidePort,
-      },
-    });
-
-    props?.onChange?.(n);
-  };
-
   return (
     <div className="w-280px flex flex-wrap gap-[8px 12px]">
-      {items
+      {nodeItems
         .filter((item) => !hideNodes?.includes(item.name))
         .map((item) => {
           return (

+ 2 - 2
apps/flowchart-designer/src/components/SelectModal.tsx

@@ -25,8 +25,8 @@ export default function SelectModal({
   onSuccess: (data: any, params: any[]) => any[];
   onChange: (selectedRows: any[]) => void;
   defaultParams?: [params: any];
-  value: any;
-  disabled: boolean;
+  value?: any;
+  disabled?: boolean;
 }) {
   const [open, setOpen] = React.useState(false);
   const [searchKey, setSearchKey] = useState("");

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

@@ -6,6 +6,7 @@ import {
   DeleteFilled,
   DeleteOutlined,
 } from "@ant-design/icons";
+import { NodeType } from "@/enum";
 
 export default ({
   node,
@@ -49,7 +50,8 @@ export default ({
           />
         </Dropdown>
       </div>
-      {type ? (
+
+      {![NodeType.START, NodeType.END].includes(type) ? (
         <div className="flex items-center leading-100px px-20px">
           {typeof img === "string" ? (
             <img src={img} alt="logo" className="w-50px" />
@@ -69,7 +71,7 @@ export default ({
               img
             )}
           </div>
-          <div className="absolute w-full bottom--32px text-center text-#222">
+          <div className="absolute w-full bottom--32px text-center text-#222 max-w-[200px] truncate">
             {name}
           </div>
         </>

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

@@ -10,7 +10,7 @@ export default function PopoverNode({
   node: Node;
   graph: Graph;
 }) {
-  const { hideNodes = [] } = node.getData();
+  const { hideNodes = [] } = node.getData() || {};
   const handleChange = (addNode?: Node) => {
     node.prop("addedNode", { addNode });
   };

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

@@ -12,65 +12,73 @@ import PopoverNode from "./PopoverNode";
 import "./NoticeNode";
 import Custom from "./Custom";
 
+import start from "@/assets/wf_icon_start.gif";
+import end from "@/assets/wf_icon_end.gif";
+import autohandle from "@/assets/wf_icon_autohandle.gif";
+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";
+
 // 通用连接桩
 const ports = {
   groups: {
     top: {
-      position: 'top',
+      position: "top",
       attrs: {
         circle: {
           r: 4,
           magnet: true,
-          stroke: '#5F95FF',
+          stroke: "#5F95FF",
           strokeWidth: 1,
-          fill: '#fff',
+          fill: "#fff",
           style: {
-            visibility: 'hidden',
+            visibility: "hidden",
           },
         },
       },
     },
     right: {
-      position: 'right',
+      position: "right",
       attrs: {
         circle: {
           r: 4,
           magnet: true,
-          stroke: '#5F95FF',
+          stroke: "#5F95FF",
           strokeWidth: 1,
-          fill: '#fff',
+          fill: "#fff",
           style: {
-            visibility: 'hidden',
+            visibility: "hidden",
           },
         },
       },
     },
     bottom: {
-      position: 'bottom',
+      position: "bottom",
       attrs: {
         circle: {
           r: 4,
           magnet: true,
-          stroke: '#5F95FF',
+          stroke: "#5F95FF",
           strokeWidth: 1,
-          fill: '#fff',
+          fill: "#fff",
           style: {
-            visibility: 'hidden',
+            visibility: "hidden",
           },
         },
       },
     },
     left: {
-      position: 'left',
+      position: "left",
       attrs: {
         circle: {
           r: 4,
           magnet: true,
-          stroke: '#5F95FF',
+          stroke: "#5F95FF",
           strokeWidth: 1,
-          fill: '#fff',
+          fill: "#fff",
           style: {
-            visibility: 'hidden',
+            visibility: "hidden",
           },
         },
       },
@@ -78,81 +86,323 @@ const ports = {
   },
   items: [
     {
-      group: 'top',
+      group: "top",
     },
     {
-      group: 'right',
+      group: "right",
     },
     {
-      group: 'bottom',
+      group: "bottom",
     },
     {
-      group: 'left',
+      group: "left",
     },
   ],
-}
+};
 
 export const nodes = [
-  { 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: '连接'} }
+  {
+    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: "连接" },
+  },
+];
+
+export const nodeItems = [
+  { key: NodeType.START, name: "start-node", icon: start, text: "开始" },
+  { key: NodeType.PROCESS, name: "handle-node", icon: handle, text: "处理" },
+  {
+    key: NodeType.AUTO_PROCESS,
+    name: "autohandle-node",
+    icon: autohandle,
+    text: "自动处理",
+  },
+  { key: NodeType.DECISION, name: "decision-node", icon: or, text: "判断" },
+  { key: NodeType.AND, name: "and-node", icon: and, text: "与" },
+  { key: NodeType.LINK, name: "link-node", icon: judge, text: "连接" },
+  { key: NodeType.END, name: "end-node", icon: end, text: "结束" },
 ];
 
 export const aiNode = [
-  {port: {in: 1, out: 1}, style: {}, color: '#00b2b2', icon: 'icon-wentifenlei', name: 'ai-category', component: Custom, data: { name: '问题分类'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#00b2b2', icon: 'icon-xunhuan1', name: 'ai-loop', component: Custom, data: { name: '循环分支'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#ff811a', icon: 'icon-shiyongwendang', name: 'ai-doc', component: Custom, data: { name: '文档提取器'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#3071f3', icon: 'icon-http', name: 'ai-http', component: Custom, data: { name: 'HTTP'}},
-  {port: {in: 1, out: 0}, style: {borderTopRightRadius: 36, borderBottomRightRadius: 36}, color: '#d37d1d', icon: 'icon-flag', name: 'ai-end', component: Custom, data: { name: '结束'}},
-  {port: {in: 0, out: 1}, style: {borderTopLeftRadius: 36, borderBottomLeftRadius: 36}, color: '#5c62ff', icon: 'icon-message', name: 'ai-start', component: Custom, data: { name: '开始'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#00b2b2', icon: 'icon-daima', name: 'ai-code', component: Custom, data: { name: '代码'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#00b2b2', icon: 'icon-tiaojianfenzhi', name: 'ai-if', component: Custom, data: { name: '条件分支'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#00b2b2', icon: 'icon-xunhuan', name: 'ai-iteration', component: Custom, data: { name: '迭代'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#ff811a', icon: 'icon-data-update', name: 'ai-adddata', component: Custom, data: { name: '数据新增'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#ff811a', icon: 'icon-data-Inquire', name: 'ai-querydata', component: Custom, data: { name: '数据查询'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#ff811a', icon: 'icon-server-update-full', name: 'ai-updatedata', component: Custom, data: { name: '数据更新'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#ff811a', icon: 'icon-server-update', name: 'ai-deldata', component: Custom, data: { name: '数据删除'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#ff811a', icon: 'icon-book', name: 'ai-know', component: Custom, data: { name: '知识检索'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#ff811a', icon: 'icon-image', name: 'ai-ocr', component: Custom, data: { name: '图像识别'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#00b2b2', icon: 'icon-liebiaoguolvqi', name: 'ai-list', component: Custom, data: { name: '列表操作'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#ca60fe', icon: 'icon-flow', name: 'ai-flow', component: Custom, data: { name: '工作流'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#00b2b2', icon: 'icon-bianliangfuzhi', name: 'ai-equal', component: Custom, data: { name: '变量赋值'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#1d44d3', icon: 'icon-zhinengti-opsasda', name: 'ai-llm', component: Custom, data: { name: '逻辑处理模型LLM', type: 'model'}, width: 256, height: 100},
-  {port: {in: 1, out: 1}, style: {}, color: '#00b2b2', icon: 'icon-kaifangAImoxingku', name: 'ai-params', component: Custom, data: { name: '参数提取'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#00b2b2', icon: 'icon-canshutiqu', name: 'ai-var', component: Custom, data: { name: '变量聚合'}},
-  {port: {in: 1, out: 1}, style: {}, color: '#00b2b2', icon: 'icon-chajian1', name: 'ai-var', component: Custom, data: { name: '扩展插件', type: 'model'}, width: 256, height: 100},
-  {port: {in: 1, out: 1}, style: {}, color: '#4d6bfe', icon: 'icon-deepseek', name: 'ai-model', component: Custom, data: { name: 'DeepSeek R1', hidePort: true}},
-]
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#00b2b2",
+    icon: "icon-wentifenlei",
+    name: "ai-category",
+    component: Custom,
+    data: { name: "问题分类" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#00b2b2",
+    icon: "icon-xunhuan1",
+    name: "ai-loop",
+    component: Custom,
+    data: { name: "循环分支" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#ff811a",
+    icon: "icon-shiyongwendang",
+    name: "ai-doc",
+    component: Custom,
+    data: { name: "文档提取器" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#3071f3",
+    icon: "icon-http",
+    name: "ai-http",
+    component: Custom,
+    data: { name: "HTTP" },
+  },
+  {
+    port: { in: 1, out: 0 },
+    style: { borderTopRightRadius: 36, borderBottomRightRadius: 36 },
+    color: "#d37d1d",
+    icon: "icon-flag",
+    name: "ai-end",
+    component: Custom,
+    data: { name: "结束" },
+  },
+  {
+    port: { in: 0, out: 1 },
+    style: { borderTopLeftRadius: 36, borderBottomLeftRadius: 36 },
+    color: "#5c62ff",
+    icon: "icon-message",
+    name: "ai-start",
+    component: Custom,
+    data: { name: "开始" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#00b2b2",
+    icon: "icon-daima",
+    name: "ai-code",
+    component: Custom,
+    data: { name: "代码" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#00b2b2",
+    icon: "icon-tiaojianfenzhi",
+    name: "ai-if",
+    component: Custom,
+    data: { name: "条件分支" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#00b2b2",
+    icon: "icon-xunhuan",
+    name: "ai-iteration",
+    component: Custom,
+    data: { name: "迭代" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#ff811a",
+    icon: "icon-data-update",
+    name: "ai-adddata",
+    component: Custom,
+    data: { name: "数据新增" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#ff811a",
+    icon: "icon-data-Inquire",
+    name: "ai-querydata",
+    component: Custom,
+    data: { name: "数据查询" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#ff811a",
+    icon: "icon-server-update-full",
+    name: "ai-updatedata",
+    component: Custom,
+    data: { name: "数据更新" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#ff811a",
+    icon: "icon-server-update",
+    name: "ai-deldata",
+    component: Custom,
+    data: { name: "数据删除" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#ff811a",
+    icon: "icon-book",
+    name: "ai-know",
+    component: Custom,
+    data: { name: "知识检索" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#ff811a",
+    icon: "icon-image",
+    name: "ai-ocr",
+    component: Custom,
+    data: { name: "图像识别" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#00b2b2",
+    icon: "icon-liebiaoguolvqi",
+    name: "ai-list",
+    component: Custom,
+    data: { name: "列表操作" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#ca60fe",
+    icon: "icon-flow",
+    name: "ai-flow",
+    component: Custom,
+    data: { name: "工作流" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#00b2b2",
+    icon: "icon-bianliangfuzhi",
+    name: "ai-equal",
+    component: Custom,
+    data: { name: "变量赋值" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#1d44d3",
+    icon: "icon-zhinengti-opsasda",
+    name: "ai-llm",
+    component: Custom,
+    data: { name: "逻辑处理模型LLM", type: "model" },
+    width: 256,
+    height: 100,
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#00b2b2",
+    icon: "icon-kaifangAImoxingku",
+    name: "ai-params",
+    component: Custom,
+    data: { name: "参数提取" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#00b2b2",
+    icon: "icon-canshutiqu",
+    name: "ai-var",
+    component: Custom,
+    data: { name: "变量聚合" },
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#00b2b2",
+    icon: "icon-chajian1",
+    name: "ai-var",
+    component: Custom,
+    data: { name: "扩展插件", type: "model" },
+    width: 256,
+    height: 100,
+  },
+  {
+    port: { in: 1, out: 1 },
+    style: {},
+    color: "#4d6bfe",
+    icon: "icon-deepseek",
+    name: "ai-model",
+    component: Custom,
+    data: { name: "DeepSeek R1", hidePort: true },
+  },
+];
 
-aiNode.forEach((node) => {
-  register({
-    shape: node.name,
-    width: node?.width || 100,
-    height: node?.height || 100,
-    effect: ["data"],
-    component: node.component,
-    ports: {...ports}
-  });
-});
+// aiNode.forEach((node) => {
+//   register({
+//     shape: node.name,
+//     width: node?.width || 100,
+//     height: node?.height || 100,
+//     effect: ["data"],
+//     component: node.component,
+//     ports: {...ports}
+//   });
+// });
 
 nodes.forEach((node) => {
   register({
     shape: node.name,
-    width: 100,
+    width: ["start-node", "end-node"].includes(node.name) ? 100 : 256,
     height: 100,
     effect: ["data"],
+    data: {
+      ...(node.data || {}),
+      type: node.type,
+    },
     component: node.component,
-    ports: {...ports}
+    ports: { ...ports },
   });
 });
 
 register({
-  shape: 'menu-popover',
+  shape: "menu-popover",
   width: 304,
   height: 184,
-  component: PopoverNode
-});
+  component: PopoverNode,
+});

+ 1 - 1
apps/flowchart-designer/src/models/flowModel.ts

@@ -29,7 +29,7 @@ export default function flowModel() {
         maxScale: 2,
       },
       background: {
-        color: "#fbfcfe",
+        color: "#eee",
       },
       grid: {
         size: 20,

+ 56 - 0
apps/flowchart-designer/src/pages/designer/components/Libary/index.tsx

@@ -0,0 +1,56 @@
+import { useEffect, useRef } from "react";
+import { Dnd } from "@antv/x6-plugin-dnd";
+import { useModel } from "umi";
+import { nodeItems } from "@/components/nodes";
+
+export default function index() {
+  const containerRef = useRef(null);
+  const { graph } = useModel("flowModel");
+  const dnd = useRef<Dnd>();
+
+  useEffect(() => {
+    if (graph && containerRef.current) {
+      dnd.current = new Dnd({
+        target: graph,
+        scaled: false,
+        dndContainer: containerRef.current,
+      });
+    }
+  }, [graph, containerRef.current]);
+
+  const onStartDrag = (e: React.MouseEvent<HTMLDivElement, MouseEvent>, node: any) => {
+    if(!graph || !dnd.current) return;
+
+    const n = graph?.createNode({
+      shape: node.name,
+      data: {
+        type: node.key,
+        name: node.text
+      }
+    });
+
+    dnd.current?.start(n, e.nativeEvent);
+  }
+
+  return (
+    <div
+      ref={containerRef}
+      className="absolute left-32px top-32px z-2 flex gap-12px box-shadow rounded-12px"
+    >
+      <div className="w-60px bg-white rounded-12px">
+        {nodeItems.map((item) => {
+          return (
+            <div
+              key={item.key}
+              className="w-full h-60px flex flex-col items-center justify-center cursor-pointer hover:bg-gray-100"
+              onMouseDown={(e) => onStartDrag(e, item)}
+            >
+              <img src={item.icon} className="w-36px" />
+              <div className="text-12px text-gray-500">{item.text}</div>
+            </div>
+          );
+        })}
+      </div>
+    </div>
+  );
+}

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

@@ -148,8 +148,8 @@ export default function index() {
         <Button type="text" icon={<i className="iconfont icon-redo" />} />
       </div>
 
-      <div className="w-260px h-40px rounded-12px bg-#fff box-shadow-sm flex items-center justify-between px-12px">
-        <Popover
+      <div className="h-40px rounded-12px bg-#fff box-shadow-sm flex items-center justify-between px-12px">
+        {/* <Popover
           content={<NodeMenu graph={graph} />}
           trigger="click"
           placement="top"
@@ -158,7 +158,7 @@ export default function index() {
           <Button type="text" icon={<PlusCircleFilled />}>
             添加节点
           </Button>
-        </Popover>
+        </Popover> */}
         <Tooltip title="添加文本">
           <Button
             type="text"

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

@@ -3,27 +3,47 @@ import { useModel } from 'umi';
 import Header from "./components/Header";
 import ToolBar from "./components/Toolbar";
 import { useEvent } from "./useEvent";
+import ConfigModal from "./modals";
+import Libary from "./components/Libary";
+import { useParams } from 'umi';
+import { GetWorkFlow } from "@/api/node";
+import { useRequest } from 'umi';
+import { Spin } from 'antd';
 
 export default function index() {
   const ref = useRef<HTMLDivElement>(null);
   const { init, graph } = useModel('flowModel');
+  const params = useParams();
+
+  const { run, loading } = useRequest(GetWorkFlow, {
+    manual: true,
+    onSuccess: (res) => {
+      console.log("流程详情:", res);
+      
+    },
+  });
+
+  useEvent(graph);
 
   useEffect(() => {
     if (!ref.current) return;
     init(ref.current);
+    params?.id && run({ id: params.id });
   }, []);
 
-  useEvent(graph);
-
   return (
+    <Spin spinning={loading}>
     <div className='w-100vw h-100vh flex flex-col overflow-hidden'>
       <div className="w-full h-60px bg-#ffffff border-b border-#eaeaea border-b-solid box-shadow-sm">
         <Header />
       </div>
       <div className="flex-1 relative">
         <div className="w-full h-full" ref={ref}/>
+        <Libary />
         <ToolBar />
       </div>
+      <ConfigModal />
     </div>
+    </Spin>
   )
 }

+ 53 - 0
apps/flowchart-designer/src/pages/designer/modals/action/index.tsx

@@ -0,0 +1,53 @@
+import React from "react";
+import { Table, Button, Switch, TableColumnType, Space } from "antd";
+
+export default function Action() {
+  const columns: TableColumnType[] = [
+    { title: "按钮", dataIndex: "btn", render: (val, record) => <Button /> },
+    {
+      title: "是否启用",
+      dataIndex: "enable",
+      render: (val, record) => <Switch />,
+    },
+    {
+      title: "对应事件",
+      dataIndex: "enable",
+      render: (val, record) => <Switch />,
+    },
+    {
+      title: "跳转节点",
+      dataIndex: "enable",
+      render: (val, record) => <Switch />,
+    },
+    {
+      title: "跳转排除",
+      dataIndex: "enable",
+      render: (val, record) => <Switch />,
+    },
+    {
+      title: "设置变量",
+      dataIndex: "enable",
+      render: (val, record) => <Switch />,
+    },
+    {
+      title: "操作",
+      key: "action",
+      render: (val, record) => (
+        <Space size="small">
+          <Button type="text">编辑</Button>
+          <Button type="text" color="danger">
+            删除
+          </Button>
+        </Space>
+      ),
+    },
+  ];
+  return (
+    <div>
+      <div className="mb-12px">
+        <Button type="primary">+添加</Button>
+      </div>
+      <Table columns={columns} dataSource={[]} />
+    </div>
+  );
+}

+ 45 - 0
apps/flowchart-designer/src/pages/designer/modals/admision/index.tsx

@@ -0,0 +1,45 @@
+import React from "react";
+import { Switch, Input, Button, Form, Space } from "antd";
+
+export default function Admission() {
+  return (
+    <>
+      <Form>
+        <Form.Item label="是否启用" name="isEntryEnable">
+          <Switch />
+        </Form.Item>
+        <Form.Item label="" shouldUpdate>
+          {({ getFieldValue }) => {
+            const enabled = getFieldValue("isEntryEnable");
+            return (
+              <>
+                <Space className="w-full mb-12px" align="end" content="end">
+                  <Button disabled={!enabled}>+变量</Button>
+                  <Button disabled={!enabled}>+字段</Button>
+                  <Button disabled={!enabled} type="primary">
+                    查看
+                  </Button>
+                </Space>
+                <Input.TextArea
+                  disabled={!enabled}
+                  rows={5}
+                  placeholder="准入条件..."
+                />
+              </>
+            );
+          }}
+        </Form.Item>
+        <div className="mt-12px">
+          注意:
+          <br />
+          <p>1. 这里是SQL中WHERE的写法,支持MS SQL中的内置函数</p>
+          <p>2. 如果是比较日期,请注意是否要比较时分秒,否则需要做日期转换</p>
+          <p>
+            3.
+            如果节点准入条件不满足,此节点不参与处理,并会继续往后续节点进行流转
+          </p>
+        </div>
+      </Form>
+    </>
+  );
+}

+ 20 - 0
apps/flowchart-designer/src/pages/designer/modals/assessment/index.tsx

@@ -0,0 +1,20 @@
+import React from 'react'
+import { Form, Switch, InputNumber } from 'antd'
+
+export default function Assessment() {
+  return (
+    <Form>
+      <Form.Item label="是否启用" name="isEnable"> 
+        <Switch />
+      </Form.Item>
+      <Form.Item label="标准处理时间" tooltip="小时" shouldUpdate> 
+        {
+          ({getFieldValue}) => {
+            const isEnable = getFieldValue('isEnable')
+            return <InputNumber disabled={!isEnable} />
+          }
+        }
+      </Form.Item>
+    </Form>
+  )
+}

+ 21 - 0
apps/flowchart-designer/src/pages/designer/modals/autoHandle/index.tsx

@@ -0,0 +1,21 @@
+import React from "react";
+import { Select, Input, Button, Form } from "antd";
+
+export default function AutoHandle() {
+  const options = [
+    { value: "Sql", label: "Sql" },
+    { value: "Dll", label: "Dll" },
+    { value: "Intergration", label: "Intergration" },
+  ];
+  return (
+    <Form>
+      <Form.Item label="处理方式">
+        <Select placeholder="请选择" options={options} />
+      </Form.Item>
+      <Form.Item label="命令文本">
+        <Button className="mb-12px" type="primary" size="small">添加</Button>
+        <Input.TextArea placeholder="请输入" rows={5} />
+      </Form.Item>
+    </Form>
+  );
+}

+ 48 - 0
apps/flowchart-designer/src/pages/designer/modals/displayConfig/index.tsx

@@ -0,0 +1,48 @@
+import React from "react";
+import { Table, Button, Switch, TableColumnType, Space } from "antd";
+
+export default function DisplayConfig() {
+  const columns: TableColumnType[] = [
+    { title: "字段代码", dataIndex: "btn", render: (val, record) => <Button /> },
+    {
+      title: "字段名称",
+      dataIndex: "enable",
+      render: (val, record) => <Switch />,
+    },
+    {
+      title: "来源类型",
+      dataIndex: "enable",
+      render: (val, record) => <Switch />,
+    },
+    {
+      title: "内容",
+      dataIndex: "enable",
+      render: (val, record) => <Switch />,
+    },
+    {
+      title: "需求描述",
+      dataIndex: "enable",
+      render: (val, record) => <Switch />,
+    },
+    {
+      title: "操作",
+      key: "action",
+      render: (val, record) => (
+        <Space size="small">
+          <Button type="text">编辑</Button>
+          <Button type="text" color="danger">
+            删除
+          </Button>
+        </Space>
+      ),
+    },
+  ];
+  return (
+    <div>
+      <div className="mb-12px">
+        <Button type="primary">+字段</Button>
+      </div>
+      <Table columns={columns} dataSource={[]} />
+    </div>
+  );
+}

+ 71 - 0
apps/flowchart-designer/src/pages/designer/modals/general/index.tsx

@@ -0,0 +1,71 @@
+import React from "react";
+import { Form, Input, Switch, Button, FormInstance } from "antd";
+import SelectModal from "@/components/SelectModal";
+import { GetPageByNodeId } from "@/api/node";
+import LangInput from "@/components/LangInput";
+
+export default function General() {
+  const form = React.useRef<FormInstance>(null);
+
+  const handleSubmit = (values: any) => {};
+
+  return (
+    <Form
+      ref={form}
+      labelCol={{ span: 3 }}
+      wrapperCol={{ span: 16 }}
+      onFinish={handleSubmit}
+    >
+      <Form.Item label="节点代码">
+        <Input placeholder="请输入" />
+      </Form.Item>
+      <Form.Item label="节点名称">
+        <LangInput />
+      </Form.Item>
+      <Form.Item label="节点描述">
+        <Input.TextArea rows={3} placeholder="请输入..." />
+      </Form.Item>
+      <Form.Item label="关联页面">
+        <SelectModal
+          key={1}
+          api={GetPageByNodeId}
+          value={1}
+          onSuccess={(data) => {
+            return data?.result?.model || [];
+          }}
+          columns={[
+            { title: "名称", dataIndex: "name" },
+            { title: "描述", dataIndex: "memo" },
+          ]}
+          onChange={(rows) => {
+            const item = rows[0];
+          }}
+          defaultParams={[
+            {
+              currentPage: 1,
+              pageSize: 10,
+              orderByProperty: "Id",
+              Ascending: false,
+              filters: [
+                {
+                  name: "EnterpriseId",
+                },
+              ],
+            },
+          ]}
+        />
+      </Form.Item>
+      <Form.Item label="禁止撤回">
+        <Switch defaultChecked={false} />
+      </Form.Item>
+      <Form.Item label={null}>
+        <Button type="primary" htmlType="submit">
+          保存
+        </Button>
+        <Button style={{ marginLeft: 8 }} onClick={() => {}}>
+          关闭
+        </Button>
+      </Form.Item>
+    </Form>
+  );
+}

+ 134 - 5
apps/flowchart-designer/src/pages/designer/modals/index.tsx

@@ -1,9 +1,138 @@
-import React from 'react'
+import React, { useEffect, useMemo } from "react";
+import { Drawer, Tabs } from "antd";
+import { useModel } from "umi";
+import { NodeType } from "@/enum";
+import { create } from "zustand";
+
+import General from "./general";
+import Action from "./action";
+import Admission from "./admision";
+import Processor from "./processor";
+import SkipMode from "./skip";
+import Assessment from "./assessment";
+import DisplayConfig from "./displayConfig";
+import AutoHandle from "./autoHandle";
+
+const tabItems = [
+  { label: "常规", component: <General /> },
+  { label: "准入条件", component: <Admission /> },
+  { label: "动作", component: <Action /> },
+  { label: "处理人", component: <Processor /> },
+  { label: "跳过方式", component: <SkipMode /> },
+  { label: "考核", component: <Assessment /> },
+  { label: "展示配置", component: <DisplayConfig /> },
+  { label: "自动处理", component: <AutoHandle /> },
+];
+
+const configMap = {
+  [NodeType.START]: ["常规", "动作", "考核"],
+  [NodeType.PROCESS]: [
+    "常规",
+    "准入条件",
+    "动作",
+    "处理人",
+    "跳过方式",
+    "考核",
+    "展示配置",
+  ],
+  [NodeType.AUTO_PROCESS]: ["常规", "自动处理"],
+  [NodeType.DECISION]: ["常规"],
+  [NodeType.AND]: ["常规"],
+  [NodeType.LINK]: ["常规"],
+  [NodeType.END]: ["常规", "动作"],
+  [NodeType.TEXT]: [],
+};
+
+class configState {
+  id: string | null = null;
+  tabActiveKey: string = "常规";
+  type: NodeType | null = null;
+  updateState: (state: Partial<configState>) => void;
+  reset: () => void;
+  [key: string]: any;
+  constructor() {
+    this.updateState = (state: Partial<configState>) => {
+      Object.assign(this, state);
+    };
+    this.reset = () => {
+      Object.assign(this, getInitState());
+    };
+  }
+}
+const getInitState = () => {
+  const { updateState, reset, ...rest } = new configState();
+  return {
+    ...rest,
+  };
+};
+
+export const useConfigStore = create<configState>((set) => {
+  return {
+    ...getInitState(),
+    updateState: (para: any) => set((state) => ({ ...state, ...para })),
+    reset: () => set(getInitState()),
+  };
+});
 
 export default function ConfigModal() {
+  const [open, setOpen] = React.useState(false);
+  const tabActiveKey = useConfigStore((state) => state.tabActiveKey);
+  const type = useConfigStore((state) => state.type);
+  const updateState = useConfigStore((state) => state.updateState);
+  const { graph } = useModel("flowModel");
+
+  useEffect(() => {
+    graph?.on("node:dblclick", (args) => {
+      // 备注不需要配置
+      if (args.node.shape === "notice-node") return;
+      const data = args.node.getData() || {};
+      console.log
+      updateState({ id: args.node.id, ...data });
+      setOpen(true);
+    });
+  }, [graph]);
+
+  // 获取tab项
+  const items = useMemo(() => {
+    const arr = type !== null ? configMap[type] : configMap[NodeType.PROCESS];
+    console.log("type", type, useConfigStore.getState(), arr)
+    return tabItems
+      .filter((item) => arr.includes(item.label))
+      .map((item) => ({
+        key: item.label,
+        label: item.label,
+        children: item.component,
+      }));
+  }, [type]);
+
+  const changeActiveKey = (key: string) => {
+    useConfigStore.getState().updateState({ tabActiveKey: key });
+  };
+
+  const handleClose = () => {
+    setOpen(false);
+    setTimeout(() => {
+      useConfigStore.getState().reset();
+    }, 300);
+  };
+
   return (
-    <div>
-      
-    </div>
-  )
+    <Drawer
+      title="节点属性"
+      width={800}
+      open={open}
+      onClose={handleClose}
+      styles={{
+        body: {
+          paddingTop: 0,
+        },
+      }}
+    >
+      <Tabs
+        items={items}
+        activeKey={tabActiveKey}
+        onChange={changeActiveKey}
+      ></Tabs>
+    </Drawer>
+  );
 }

+ 16 - 0
apps/flowchart-designer/src/pages/designer/modals/processor/index.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import { Radio, Form } from 'antd'
+
+export default function Processor() {
+  return (
+    <Form>
+      <Form.Item label="处理方式">
+        <Radio.Group> 
+          <Radio value="1">任何一个</Radio>
+          <Radio value="2">并行处理</Radio>
+          <Radio value="3">串行处理</Radio>
+        </Radio.Group> 
+      </Form.Item>
+    </Form>
+  )
+}

+ 37 - 0
apps/flowchart-designer/src/pages/designer/modals/skip/index.tsx

@@ -0,0 +1,37 @@
+import React from "react";
+import { Table, Select, Switch } from "antd";
+
+export default function SkipMode() {
+  const columns = [
+    {
+      title: "类型",
+      dataIndex: "type",
+    },
+    {
+      title: "跳过方式",
+      dataIndex: "type",
+      render: (text: string) => <Switch />,
+    },
+    {
+      title: "配置",
+      dataIndex: "options",
+      render: (options: any[]) =>
+        options ? <Select options={options} /> : null,
+    },
+  ];
+
+  const data = [
+    { key: 1, type: "处理人与提交人相同跳过", options: "" },
+    { key: 2, type: "与前置节点处理人相同跳过", options: "" },
+    { key: 3, type: "与某一节点处理人相同跳过", options: "" },
+    { key: 4, type: "与前面任意节点处理人相同跳过", options: "" },
+    { key: 5, type: "无对应处理人跳过跳过", options: "" },
+  ];
+
+  return (
+    <div>
+      <h3>请选择跳过方式:</h3>
+      <Table columns={columns} dataSource={data} />
+    </div>
+  );
+}