瀏覽代碼

perf: 优化AI对话,移除ai创作,整理思维导图工具

liaojiaxing 1 周之前
父節點
當前提交
40b897e2b3
共有 30 個文件被更改,包括 448 次插入163 次删除
  1. 1 1
      apps/designer/.umirc.ts
  2. 154 102
      apps/designer/src/components/ai/AIChat.tsx
  3. 17 6
      apps/designer/src/components/ai/MarkdownViewer.tsx
  4. 98 0
      apps/designer/src/components/ai/RenderGraph.tsx
  5. 59 0
      apps/designer/src/components/ai/markdown.less
  6. 1 1
      apps/designer/src/components/mindMap/Topic.tsx
  7. 2 2
      apps/designer/src/events/mindMapEvent.ts
  8. 7 2
      apps/designer/src/hooks/useChat.ts
  9. 4 1
      apps/designer/src/models/appModel.ts
  10. 1 1
      apps/designer/src/models/mindMapModel.ts
  11. 22 10
      apps/designer/src/pages/flow/components/Config/index.tsx
  12. 1 1
      apps/designer/src/pages/flow/components/Content/index.tsx
  13. 21 4
      apps/designer/src/pages/flow/components/ToolBar/index.tsx
  14. 1 1
      apps/designer/src/pages/mindmap/components/Config/Theme.tsx
  15. 34 19
      apps/designer/src/pages/mindmap/components/Config/index.tsx
  16. 1 1
      apps/designer/src/pages/mindmap/components/HeaderToolbar/index.tsx
  17. 15 4
      apps/designer/src/pages/mindmap/components/RightToolbar/index.tsx
  18. 1 1
      apps/designer/src/pages/mindmap/index.tsx
  19. 1 1
      apps/designer/src/utils/contentMenu.tsx
  20. 0 0
      apps/designer/src/utils/mindmap/catologue-to-tree.ts
  21. 0 0
      apps/designer/src/utils/mindmap/edge.ts
  22. 0 0
      apps/designer/src/utils/mindmap/hierarchy.ts
  23. 6 4
      apps/designer/src/pages/mindmap/mindMap.tsx
  24. 0 0
      apps/designer/src/utils/mindmap/theme/index.ts
  25. 0 0
      apps/designer/src/utils/mindmap/theme/theme1.ts
  26. 0 0
      apps/designer/src/utils/mindmap/theme/theme2.ts
  27. 0 0
      apps/designer/src/utils/mindmap/theme/theme3.ts
  28. 0 0
      apps/designer/src/utils/mindmap/theme/theme4.ts
  29. 0 0
      apps/designer/src/utils/mindmap/theme/theme5.ts
  30. 1 1
      apps/designer/src/utils/mindmapHander.tsx

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

@@ -9,7 +9,7 @@ export default defineConfig({
     '/favicon.ico'
   ],
   styles: [
-    '//at.alicdn.com/t/c/font_4676747_ild1695qz8.css'
+    '//at.alicdn.com/t/c/font_4676747_kbfv8otb8de.css'
   ],
   metas: [
     { name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' }

+ 154 - 102
apps/designer/src/components/ai/AIChat.tsx

@@ -1,66 +1,22 @@
-import React, { useEffect, useMemo, useRef, useState } from "react";
+import { ReactNode, useEffect, useMemo, useRef, useState } from "react";
 import {
   PlusOutlined,
   CloseOutlined,
   FieldTimeOutlined,
-  UserOutlined,
-  SendOutlined,
-  LoadingOutlined,
-  EditOutlined,
-  DeleteOutlined,
 } from "@ant-design/icons";
-import {
-  Button,
-  Tooltip,
-  Input,
-  Form,
-  Dropdown,
-  Typography,
-  Space,
-  Spin,
-} from "antd";
+import { Button, Tooltip, Dropdown, Typography, Space, Spin } from "antd";
 import { GetProp } from "antd";
-import { Bubble, Sender } from "@ant-design/x";
+import { Bubble, Sender, Suggestion, Prompts } from "@ant-design/x";
 import MarkdownViewer from "./MarkdownViewer";
 import { useChat } from "@/hooks/useChat";
+import RenderGraph from "./RenderGraph";
 
-// bubbles角色配置
-const roles: GetProp<typeof Bubble.List, "roles"> = {
-  assistant: {
-    placement: "start",
-    avatar: undefined,
-    loadingRender: () => (
-      <Space>
-        <Spin size="small" />
-        思考中...
-      </Space>
-    ),
-    messageRender: (content) => {
-      return typeof content === "string" ? (
-        <Typography className={content?.includes("```") ? "w-full" : ""}>
-          <MarkdownViewer content={content} />
-        </Typography>
-      ) : (
-        content
-      );
-    },
-    header: <span className="text-12px text-#666">数据模型助手</span>,
-  },
-  user: {
-    placement: "start",
-    avatar: undefined,
-    messageRender: (content) => {
-      return <div style={{ whiteSpace: "pre-wrap" }}>{content}</div>;
-    },
-    styles: {
-      content: {
-        backgroundColor: "#d2e1fb",
-      },
-    },
-  },
-};
-
-export default function AIChat(props: { onClose?: () => void }) {
+export default function AIChat(props: {
+  onClose?: () => void;
+  onInsert?: (data: any) => void;
+  graphType?: "flow" | "mindmap";
+}) {
+  const { onClose, onInsert, graphType } = props;
   const [chatStarted, setChatStarted] = useState(false);
   const [inputVal, setInputVal] = useState("");
   const contentRef = useRef<HTMLDivElement>(null);
@@ -80,41 +36,129 @@ export default function AIChat(props: { onClose?: () => void }) {
     };
   }, []);
 
-  const { loading, onRequest, cancel, messages, setMessages, addConversation, conversationList } =
-    useChat({
-      app_name: "system_design",
-      onSuccess: (msg) => {
-        setMessages((messages) => {
-          const arr = [...messages];
-          // const query = arr[messages.length - 2].content as string;
-          arr[messages.length - 1].status = "done";
-          arr[messages.length - 1].footer = <></>;
-          return arr;
-        });
+  const {
+    loading,
+    onRequest,
+    cancel,
+    messages,
+    setMessages,
+    addConversation,
+    conversationList,
+    changeConversation,
+  } = useChat({
+    app_name: "system_design",
+    onSuccess: (msg) => {
+      setMessages((messages) => {
+        const arr = [...messages];
+        // const query = arr[messages.length - 2].content as string;
+        arr[messages.length - 1].status = "done";
+        arr[messages.length - 1].footer = <></>;
+        return arr;
+      });
+    },
+    onUpdate: (msg) => {
+      setMessages((messages) => {
+        const arr = [...messages];
+        arr[messages.length - 1].content += msg.answer;
+        arr[messages.length - 1].id = msg.message_id;
+        arr[messages.length - 1].loading = false;
+        arr[messages.length - 1].status = "loading";
+        return arr;
+      });
+    },
+    onError: (error) => {
+      // message.error(error.message);
+      setMessages((messages) => {
+        const arr = [...messages];
+        arr[messages.length - 1].content = (
+          <Typography.Text type="danger">{error.message}</Typography.Text>
+        );
+        arr[messages.length - 1].status = "error";
+        arr[messages.length - 1].loading = false;
+        return arr;
+      });
+    },
+  });
+
+  const CustomRender = (props: { language: string; code: string }) => {
+    const { language, code } = props;
+    const children = useMemo(
+      () => (
+        <RenderGraph
+          language={language}
+          json={code}
+          onInsertFlow={onInsert}
+          type={graphType}
+        ></RenderGraph>
+      ),
+      [language, code]
+    );
+
+    return <>{children}</>;
+  };
+
+  const CustomMessage = (props: { content: string | ReactNode }) => {
+    const { content } = props;
+    const children = useMemo(
+      () =>
+        typeof content === "string" ? (
+          <Typography className={content?.includes("```") ? "w-full" : ""}>
+            <MarkdownViewer
+              content={content}
+              renderCode={(language, code) => (
+                <CustomRender language={language} code={code} />
+              )}
+            />
+          </Typography>
+        ) : (
+          content
+        ),
+      [content]
+    );
+
+    return children;
+  };
+
+  // bubbles角色配置
+  const roles: GetProp<typeof Bubble.List, "roles"> = {
+    assistant: {
+      placement: "start",
+      avatar: undefined,
+      loadingRender: () => (
+        <Space>
+          <Spin size="small" />
+          思考中...
+        </Space>
+      ),
+      messageRender: (content) => <CustomMessage content={content} />,
+      header: (
+        <span className="text-12px text-#666">
+          <svg className="icon h-16px! w-16px!" aria-hidden="true">
+            <use xlinkHref="#icon-AI1"></use>
+          </svg>
+          <span className="ml-4px">数据模型助手</span>
+        </span>
+      ),
+      styles: {
+        content: {
+          borderRadius: "0px 8px 8px 8px",
+        },
       },
-      onUpdate: (msg) => {
-        setMessages((messages) => {
-          const arr = [...messages];
-          arr[messages.length - 1].content += msg.answer;
-          arr[messages.length - 1].id = msg.message_id;
-          arr[messages.length - 1].loading = false;
-          arr[messages.length - 1].status = "loading";
-          return arr;
-        });
+    },
+    user: {
+      placement: "end",
+      avatar: undefined,
+      messageRender: (content) => {
+        return <div style={{ whiteSpace: "pre-wrap" }}>{content}</div>;
       },
-      onError: (error) => {
-        // message.error(error.message);
-        setMessages((messages) => {
-          const arr = [...messages];
-          arr[messages.length - 1].content = (
-            <Typography.Text type="danger">{error.message}</Typography.Text>
-          );
-          arr[messages.length - 1].status = "error";
-          arr[messages.length - 1].loading = false;
-          return arr;
-        });
+      styles: {
+        content: {
+          backgroundColor: "#d2e1fb",
+          borderRadius: "8px 0 8px 8px",
+        },
       },
-    });
+    },
+  };
 
   // 处理提交
   const onSubmit = (val: string) => {
@@ -155,14 +199,29 @@ export default function AIChat(props: { onClose?: () => void }) {
   };
 
   const historyItems = useMemo(() => {
-    return (conversationList || []).map(item => {
+    return (conversationList || []).map((item) => {
       return {
         key: item.id,
-        label: item.label
-      }
-    })
+        label: item.label,
+        onClick: () => {
+          console.log("changeConversation", item);
+          changeConversation(item.sessionId);
+        },
+      };
+    });
   }, [conversationList]);
 
+  const bubble = useMemo(() => {
+    return (
+      <Bubble.List
+        style={{ maxHeight: contentHeight, padding: "0 10px" }}
+        autoScroll
+        items={messages}
+        roles={roles}
+      />
+    );
+  }, [messages]);
+
   return (
     <div
       className="flex-1 h-full flex flex-col"
@@ -171,7 +230,7 @@ export default function AIChat(props: { onClose?: () => void }) {
       }}
     >
       <div className="chat-head w-full h-40px px-10px color-#333 flex items-center justify-between">
-      <span className="text-14px">
+        <span className="text-14px">
           <svg className="icon h-32px w-32px mr-4px" aria-hidden="true">
             <use xlinkHref="#icon-AI1"></use>
           </svg>
@@ -205,13 +264,13 @@ export default function AIChat(props: { onClose?: () => void }) {
             type="text"
             size="small"
             icon={<CloseOutlined />}
-            onClick={() => props.onClose?.()}
+            onClick={() => onClose?.()}
           ></Button>
         </span>
       </div>
 
       <div
-        className="chat-content flex-1 px-10px overflow-hidden mt-12px"
+        className="chat-content flex-1 overflow-hidden mt-12px"
         ref={contentRef}
         style={{ height: contentHeight }}
       >
@@ -226,20 +285,13 @@ export default function AIChat(props: { onClose?: () => void }) {
             <p className="text-center">我是AI助手,有什么可以帮您的吗?</p>
           </>
         ) : (
-          <div className="overflow-y-auto h-full">
-            <Bubble.List
-              style={{ maxHeight: contentHeight }}
-              autoScroll
-              items={messages}
-              roles={roles}
-            />
-          </div>
+          <div className="overflow-y-auto h-full">{bubble}</div>
         )}
       </div>
 
       <div className="px-12px py-16px">
         <Sender
-          placeholder="你想咨询什么?"
+          placeholder="开始探索..."
           loading={loading}
           value={inputVal}
           onChange={setInputVal}

+ 17 - 6
apps/designer/src/components/ai/MarkdownViewer.tsx

@@ -2,11 +2,13 @@ import ReactMarkdown from "react-markdown";
 import remarkGfm from "remark-gfm";
 import rehypeRaw from "rehype-raw";
 import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
-import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
-import { useState } from "react";
+import { materialDark } from "react-syntax-highlighter/dist/esm/styles/prism";
+import { useState, useMemo } from "react";
+import "./markdown.less";
 
 interface MarkdownViewerProps {
   content: string;
+  renderCode?: (language: string, code: string) => React.ReactNode | undefined;
 }
 
 const CodeHeader: React.FC<{
@@ -30,9 +32,15 @@ const CodeHeader: React.FC<{
   </div>
 );
 
-const MarkdownViewer: React.FC<MarkdownViewerProps> = ({ content }) => {
+const MarkdownViewer: React.FC<MarkdownViewerProps> = ({ content, renderCode }) => {
   const [copiedIndex, setCopiedIndex] = useState<number | null>(null);
 
+  const filterContent = useMemo(() => {
+    return content
+      .replaceAll("<think>", `<div class="think"><div class="think-title"><i class="iconfont icon-sikao" style="color: #df9e20;"></i> <span>思考过程</span></div>`)
+      .replaceAll("</think>", "</div>");
+  }, [content]);
+
   const handleCopy = (code: string, index: number) => {
     navigator.clipboard.writeText(code);
     setCopiedIndex(index);
@@ -49,7 +57,10 @@ const MarkdownViewer: React.FC<MarkdownViewerProps> = ({ content }) => {
           const match = /language-(\w+)/.exec(className || "");
           const code = String(children).replace(/\n$/, "");
           const language = match ? match[1] : "";
-
+          const customRender = renderCode?.(language, code);
+          if (customRender) {
+            return customRender;
+          }
           if (match) {
             return (
               <div className="rounded-md overflow-hidden mb-4">
@@ -62,7 +73,7 @@ const MarkdownViewer: React.FC<MarkdownViewerProps> = ({ content }) => {
                 />
                 <div className="max-w-full overflow-x-auto">
                   <SyntaxHighlighter
-                    style={vscDarkPlus}
+                    style={materialDark}
                     language={language}
                     PreTag="div"
                     {...props}
@@ -86,7 +97,7 @@ const MarkdownViewer: React.FC<MarkdownViewerProps> = ({ content }) => {
         },
       }}
     >
-      {content}
+      {filterContent}
     </ReactMarkdown>
   );
 };

+ 98 - 0
apps/designer/src/components/ai/RenderGraph.tsx

@@ -0,0 +1,98 @@
+import { useEffect, useRef, useMemo } from "react";
+import { Button, message, Tooltip } from "antd";
+import { Graph } from "@antv/x6";
+import { handleParseAIData } from "@/utils";
+import { getFlowNodeMetaByAi } from "@/utils/flow";
+import { getDefaultDataByTheme } from "@/config/data";
+import { PlusOutlined } from "@ant-design/icons";
+
+export default function RenderGraph(props: {
+  language: string;
+  json: string;
+  type?: "flow" | "mindmap";
+  onInsertFlow?: (data: any) => void;
+  showInsert?: boolean;
+}) {
+  const { language, json, onInsertFlow, showInsert = true, type = "flow" } = props;
+  const containerRef = useRef<HTMLDivElement>(null);
+  const graphRef = useRef<Graph>();
+  const [messageApi, contextHolder] = message.useMessage();
+  const parseData = useRef();
+
+  useEffect(() => {
+    graphRef.current = new Graph({
+      container: containerRef.current!,
+      width: containerRef.current!.clientWidth,
+      height: containerRef.current!.clientHeight,
+      autoResize: true,
+      panning: true,
+      mousewheel: { enabled: true },
+      interacting: {
+        edgeLabelMovable: false,
+        edgeMovable: false,
+        nodeMovable: false,
+        arrowheadMovable: false,
+      },
+      background: {
+        color: "#f5f5f5",
+      },
+    });
+  }, []);
+
+  useEffect(() => {
+    handleParseAIData({
+      content: json,
+      message: {
+        key: "ai-creator",
+        instance: messageApi,
+      },
+      onSuccess: (data) => {
+        parseData.current = data;
+        // graphRef.current?.fromJSON(data);
+        const defaultDataByTheme = getDefaultDataByTheme("1");
+
+        const list = (Array.isArray(data) ? data : [data]).map((item) =>
+          getFlowNodeMetaByAi(item, defaultDataByTheme)
+        );
+
+        list.forEach((cell) => {
+          if (cell.shape !== "edge") {
+            graphRef.current?.addNode(cell);
+          }
+          if (cell.shape === "edge") {
+            graphRef.current?.addEdge(cell);
+          }
+        });
+        graphRef.current?.zoomToFit({
+          padding: 20,
+        });
+      },
+      onError: (err) => {
+        messageApi.error(err.message);
+      },
+    });
+  }, [language, json]);
+
+  return (
+    <div className="w-full">
+      {contextHolder}
+      <div className="text-14px">以为您生成以下内容:</div>
+      <div className="w-full h-300px max-h-300px rounded-4px" ref={containerRef}/>
+      <div className="flex mt-8px">
+        {showInsert && type === "flow" && (
+          <Tooltip title="添加到当前画布">
+            <Button
+              variant="text"
+              size="small"
+              color="primary"
+              icon={<PlusOutlined />}
+              onClick={() => onInsertFlow?.(parseData.current)}
+            >
+              加入画布
+            </Button>
+          </Tooltip>
+        )}
+      </div>
+    </div>
+  );
+}

+ 59 - 0
apps/designer/src/components/ai/markdown.less

@@ -0,0 +1,59 @@
+.markdown-body {
+  table {
+    width: 100%;
+    border-collapse: collapse;
+    border-spacing: 0;
+    margin-bottom: 20px;
+    font-size: 14px;
+    line-height: 1.42857143;
+    color: #333;
+    border: solid 1px #ddd;
+    th {
+      padding: 8px;
+      line-height: 1.42857143;
+      vertical-align: top;
+      border-top: 1px solid #ddd;
+      border-right: 1px solid #ddd;
+      word-break: break-all;
+    }
+    td {
+      padding: 8px;
+      line-height: 1.42857143;
+      vertical-align: top;
+      border-top: 1px solid #ddd;
+      border-right: 1px solid #ddd;
+      word-break: break-all;
+    }
+    th {
+      text-align: left;
+    }
+    th {
+      background-color: #f9f9f9;
+    }
+    tr:nth-child(2n) {
+      // background-color: #f9f9f9;
+    }
+  }
+
+  .think {
+    padding-left: 20px;
+    border-left: solid 2px #999;
+    color: #666;
+    font-size: 12px;
+  }
+
+  .think-title {
+    font-size: 14px;
+    color: #333;
+  }
+
+  img, video {
+    max-width: 100%;
+  }
+
+  pre {
+    padding: 0;
+    background: none;
+    border: none;
+  }
+}

+ 1 - 1
apps/designer/src/components/mindMap/Topic.tsx

@@ -5,7 +5,7 @@ import { useSizeHook, useShapeProps } from "@/hooks";
 import { useEffect, useMemo, useRef, useState } from "react";
 import { PlusOutlined } from "@ant-design/icons";
 import { TopicType } from "@/enum";
-import { addTopic } from "@/pages/mindmap/mindMap";
+import { addTopic } from "@/utils/mindmap";
 
 import Link from "./Link";
 import ExtraModule from "./ExtraModule";

+ 2 - 2
apps/designer/src/events/mindMapEvent.ts

@@ -1,11 +1,11 @@
 import { Graph, Cell, Node, Edge, EventArgs } from "@antv/x6";
 import { BorderSize, StructureType, TopicType } from "@/enum";
-import { addTopic, updateTopic } from "@/pages/mindmap/mindMap";
+import { addTopic, updateTopic } from "@/utils/mindmap";
 import { cellStyle, MindMapProjectInfo, TopicItem } from "@/types";
 import { Dnd } from "@antv/x6-plugin-dnd";
 import { selectTopic } from "@/utils/mindmapHander";
 import { uuid } from "@repo/utils";
-import { getTheme } from "@/pages/mindmap/theme";
+import { getTheme } from "@/utils/mindmap/theme";
 import { traverseNode } from "@/utils/mindmapHander";
 import { EditMindMapElement, AddMindMapElement } from "@/api/systemDesigner";
 import { debounce, isEqual } from "lodash-es";

+ 7 - 2
apps/designer/src/hooks/useChat.ts

@@ -192,6 +192,8 @@ export function useChat({ app_name, onSuccess, onUpdate, onError }: ChatProps) {
     }
   };
 
+  const baseUrl = process.env.NODE_ENV === "production" ? "" : "/api";
+
   /**
    * 封装智能体
    */
@@ -199,15 +201,18 @@ export function useChat({ app_name, onSuccess, onUpdate, onError }: ChatProps) {
     request: async (message, { onError, onSuccess, onUpdate }) => {
       abortController.current = new AbortController();
       const signal = abortController.current.signal;
+      const enterpriseCode = sessionStorage.getItem("enterpriseCode");
+      const token = localStorage.getItem("token_" + enterpriseCode) || '';
+
       try {
         setLoading(true);
         const response = await fetch(
-          "https://design.shalu.com/api/ai/chat-message",
+          baseUrl + "/api/ai/chat-message",
           {
             method: "POST",
             body: JSON.stringify(message),
             headers: {
-              Authorization: localStorage.getItem("token_a") || "",
+              Authorization: token,
               "Content-Type": "application/json",
             },
             signal,

+ 4 - 1
apps/designer/src/models/appModel.ts

@@ -44,6 +44,7 @@ export default function appModel() {
   const [rightPanelTabActiveKey, setRightPanelTabActiveKey] = useState("1");
   // 流程图右侧面板宽度
   const [flowRightPanelWidth, setFlowRightPanelWidth] = useState(280);
+  const [rightPanelOriginalWidth, setRightOriginalPanelWidth] = useState(280);
   // 思维导图右侧宽度
   const [mindmapRightPanelWidth, setMindMapRightPanelWidth] = useState(280);
   const graphRef = useRef<Graph>();
@@ -197,6 +198,8 @@ export default function appModel() {
     flowRightPanelWidth,
     setFlowRightPanelWidth,
     mindmapRightPanelWidth,
-    setMindMapRightPanelWidth
+    setMindMapRightPanelWidth,
+    rightPanelOriginalWidth,
+    setRightOriginalPanelWidth
   }
 }

+ 1 - 1
apps/designer/src/models/mindMapModel.ts

@@ -10,7 +10,7 @@ import { Clipboard } from "@antv/x6-plugin-clipboard";
 import { Export } from "@antv/x6-plugin-export";
 import { MindMapProjectInfo } from "@/types";
 import { bindMindMapEvents } from "@/events/mindMapEvent";
-import { renderMindMap } from "@/pages/mindmap/mindMap";
+import { renderMindMap } from "@/utils/mindmap";
 import { TopicType } from "@/enum";
 import { isEqual, cloneDeep } from "lodash-es";
 import { bindMindmapKeys } from "@/utils/fastKey";

+ 22 - 10
apps/designer/src/pages/flow/components/Config/index.tsx

@@ -28,6 +28,7 @@ export default function Config() {
     activeAI,
     setActiveAI,
     setFlowRightPanelWidth,
+    rightPanelOriginalWidth,
   } = useModel("appModel");
 
   const { dragging, handleMouseDown } = useDragResize(setFlowRightPanelWidth);
@@ -43,6 +44,11 @@ export default function Config() {
     });
   };
 
+  const handleCloseAI = () => {
+    setActiveAI(undefined);
+    setFlowRightPanelWidth(rightPanelOriginalWidth);
+  };
+
   const tabItems = [
     {
       key: "1",
@@ -103,20 +109,26 @@ export default function Config() {
       ></div>
       <div className="flex-1 overflow-hidden">
         {activeAI === "chat" && (
-          <AIChat onClose={() => setActiveAI(undefined)} />
+          <AIChat
+            graphType="flow"
+            onClose={handleCloseAI}
+            onInsert={handleAddNodeByAi}
+          />
         )}
         {activeAI === "creator" && (
-          <AICreator type="flow" onClose={() => setActiveAI(undefined)} onChange={handleAddNodeByAi}/>
-        )}
-        {activeAI === undefined && (
-          <Tabs
-            centered
-            items={tabItems}
-            activeKey={rightPanelTabActiveKey}
-            onChange={(key) => setRightPanelTabActiveKey(key)}
-            className="w-full"
+          <AICreator
+            type="flow"
+            onClose={handleCloseAI}
+            onChange={handleAddNodeByAi}
           />
         )}
+        <Tabs
+          centered
+          items={tabItems}
+          activeKey={rightPanelTabActiveKey}
+          onChange={(key) => setRightPanelTabActiveKey(key)}
+          className="w-full"
+        />
       </div>
     </div>
   );

+ 1 - 1
apps/designer/src/pages/flow/components/Content/index.tsx

@@ -91,7 +91,7 @@ export default function Content() {
         allowMulti: true,
         highlight: false,
         anchor: "center",
-        connectionPoint: "anchor",
+        // connectionPoint: "anchor", // mermaid导入可能存在异常
         snap: {
           radius: 20,
         },

+ 21 - 4
apps/designer/src/pages/flow/components/ToolBar/index.tsx

@@ -45,6 +45,9 @@ export default function ToolBar() {
     pageState,
     activeAI,
     setActiveAI,
+    setFlowRightPanelWidth,
+    setRightOriginalPanelWidth,
+    flowRightPanelWidth,
   } = useModel("appModel");
   const { canRedo, canUndo, onRedo, onUndo, selectedCell, graph } =
     useModel("flowchartModel");
@@ -776,7 +779,7 @@ export default function ToolBar() {
         <MermaidModal ref={mermaidModelRef} onChange={handleInsertMermaid} />
         <div>
           <Tooltip placement="bottom" title="AI助手">
-            <Dropdown
+            {/* <Dropdown
               menu={{
                 items: [
                   {
@@ -789,6 +792,10 @@ export default function ToolBar() {
                     ),
                     onClick: () => {
                       setActiveAI("chat");
+                      setRightOriginalPanelWidth(flowRightPanelWidth);
+                      if(flowRightPanelWidth <= 400) {
+                        setFlowRightPanelWidth(400)
+                      }
                     },
                   },
                   {
@@ -801,11 +808,15 @@ export default function ToolBar() {
                     ),
                     onClick: () => {
                       setActiveAI("creator");
+                      setRightOriginalPanelWidth(flowRightPanelWidth);
+                      if(flowRightPanelWidth <= 400) {
+                        setFlowRightPanelWidth(400)
+                      }
                     },
                   },
                 ],
               }}
-            >
+            > */}
             <Button
               type="text"
               icon={
@@ -815,9 +826,15 @@ export default function ToolBar() {
               }
               className={activeAI ? "active" : ""}
               style={{ marginRight: 16 }}
-              // onClick={() => setActiveAI("creator")}
+              onClick={() => {
+                setActiveAI("chat");
+                setRightOriginalPanelWidth(flowRightPanelWidth);
+                if(flowRightPanelWidth <= 450) {
+                  setFlowRightPanelWidth(450)
+                }
+              }}
             />
-            </Dropdown>
+            {/* </Dropdown> */}
           </Tooltip>
           <Tooltip placement="bottom" title="替换">
             <Button

+ 1 - 1
apps/designer/src/pages/mindmap/components/Config/Theme.tsx

@@ -1,7 +1,7 @@
 import { Tabs } from "antd";
 import React from "react";
 import { useModel } from "umi";
-import { changeTheme } from "../../theme";
+import { changeTheme } from "../../../../utils/mindmap/theme";
 
 const themes = [
   {

+ 34 - 19
apps/designer/src/pages/mindmap/components/Config/index.tsx

@@ -15,7 +15,7 @@ import NodeAttrs from "@/components/NodeAttrs";
 import AICreator from "./AiCreator";
 import AIChat from "@/components/ai/AIChat";
 
-import { buildTopic } from "../../mindMap";
+import { buildTopic } from "@/utils/mindmap";
 import { TopicType } from "@/enum";
 import { Node } from "@antv/x6";
 import { TopicItem } from "@/types";
@@ -35,7 +35,8 @@ export default function index() {
     mindProjectInfo,
     setMindProjectInfo,
   } = useModel("mindMapModel");
-  const { setMindMapRightPanelWidth } = useModel("appModel");
+  const { setMindMapRightPanelWidth, rightPanelOriginalWidth } =
+    useModel("appModel");
   const [activeKey, setActiveKey] = useState("1");
   const { dragging, handleMouseDown } = useDragResize(
     setMindMapRightPanelWidth
@@ -62,12 +63,18 @@ export default function index() {
 
   const handleCloseAI = () => {
     rightToolbarActive("");
+    setMindMapRightPanelWidth(rightPanelOriginalWidth);
   };
 
   // 记录ai新增的节点id
   const aiAddNodeIds = useRef<string[]>([]);
   // 获取转换数据
-  const getConvertData = (data: any[], parentId: string, type: TopicType, parentTopic: TopicItem) => {
+  const getConvertData = (
+    data: any[],
+    parentId: string,
+    type: TopicType,
+    parentTopic: TopicItem
+  ) => {
     return data.map((item) => {
       const topic = buildTopic(
         type,
@@ -78,7 +85,12 @@ export default function index() {
       topic.parentId = parentId;
       aiAddNodeIds.current.push(topic.id);
       if (item.children) {
-        topic.children = getConvertData(item.children, topic.id, TopicType.sub, topic);
+        topic.children = getConvertData(
+          item.children,
+          topic.id,
+          TopicType.sub,
+          topic
+        );
       }
 
       return topic;
@@ -90,29 +102,30 @@ export default function index() {
     aiAddNodeIds.current = [];
     if (Array.isArray(data)) {
       let currentTopic = graph?.getSelectedCells()?.[0]?.data;
-      if(!currentTopic){
+      if (!currentTopic) {
         currentTopic = mindProjectInfo?.topics?.find(
           (topic) => topic.type === TopicType.main
         );
       }
 
-      const type = currentTopic?.type === TopicType.main ? TopicType.branch : TopicType.sub;
+      const type =
+        currentTopic?.type === TopicType.main
+          ? TopicType.branch
+          : TopicType.sub;
       // 获取AI转换数据
       const result = getConvertData(data, currentTopic?.id, type, currentTopic);
 
       // 往节点下添加
-      mindProjectInfo && setMindProjectInfo({
-        ...mindProjectInfo,
-        topics: traverseNode(mindProjectInfo.topics, (topic) => {
-          if (topic.id === currentTopic?.id) {
-            topic.children = [
-              ...(topic.children || []),
-              ...result
-            ];
-          }
-          return topic
-        })
-      });
+      mindProjectInfo &&
+        setMindProjectInfo({
+          ...mindProjectInfo,
+          topics: traverseNode(mindProjectInfo.topics, (topic) => {
+            if (topic.id === currentTopic?.id) {
+              topic.children = [...(topic.children || []), ...result];
+            }
+            return topic;
+          }),
+        });
 
       setTimeout(() => {
         graph &&
@@ -191,7 +204,9 @@ export default function index() {
         {/* 备注 */}
         {rightToobarActive === "remark" && <Remark />}
         {/* AI对话 */}
-        {rightToobarActive === "ai-chat" && <AIChat onClose={handleCloseAI}/>}
+        {rightToobarActive === "ai-chat" && (
+          <AIChat onClose={handleCloseAI} onInsert={handleAiCreated} />
+        )}
         {/* AI创作 */}
         {rightToobarActive === "ai-creator" && (
           <AICreator

+ 1 - 1
apps/designer/src/pages/mindmap/components/HeaderToolbar/index.tsx

@@ -3,7 +3,7 @@ import { Button, Input, Dropdown, Tooltip, MenuProps, Divider, message } from "a
 import { LeftOutlined, MenuOutlined } from "@ant-design/icons";
 import logo from "@/assets/logo.png";
 import { useModel, Icon } from "umi";
-import { addTopic } from "../../mindMap";
+import { addTopic } from "@/utils/mindmap";
 import { TopicType, GraphType } from "@/enum";
 import { selectTopic, addBorder, addSummary } from "@/utils/mindmapHander";
 import { createNew } from "@/utils";

+ 15 - 4
apps/designer/src/pages/mindmap/components/RightToolbar/index.tsx

@@ -11,6 +11,11 @@ import { insertImage } from "@/utils/mindmapHander";
 export default function index() {
   const { rightToobarActive, rightToolbarActive, selectedCell } =
     useModel("mindMapModel");
+  const {
+    setRightOriginalPanelWidth,
+    mindmapRightPanelWidth,
+    setMindMapRightPanelWidth,
+  } = useModel("appModel");
 
   const handleAddImage = () => {
     insertImage(selectedCell.find((node) => node.isNode()));
@@ -23,11 +28,17 @@ export default function index() {
           <Button
             type="text"
             icon={<i className="iconfont icon-AIduihua text-[#2984fd]" />}
-            className={rightToobarActive === "ai-chat" ? "active" : ""}
-            onClick={() => rightToolbarActive("ai-chat")}
+            // className={rightToobarActive === "ai-chat" ? "active" : ""}
+            onClick={() => {
+              rightToolbarActive("ai-chat");
+              setRightOriginalPanelWidth(mindmapRightPanelWidth);
+              if (mindmapRightPanelWidth <= 450) {
+                setMindMapRightPanelWidth(450);
+              }
+            }}
           />
         </Tooltip>
-        <Tooltip placement="bottom" title="AI创作">
+        {/* <Tooltip placement="bottom" title="AI创作">
           <Button
             type="text"
             icon={<svg className="icon h-32px w-32px" aria-hidden="true">
@@ -36,7 +47,7 @@ export default function index() {
             className={rightToobarActive === "ai-creator" ? "active" : ""}
             onClick={() => rightToolbarActive("ai-creator")}
           />
-        </Tooltip>
+        </Tooltip> */}
       </div>
 
       <div className="bg-white shadow-md rounded-4px flex flex-col p-8px">

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

@@ -11,7 +11,7 @@ import SvgComponent from "./components/SvgComponent";
 import { FlowchartMindMapInfo } from "@/api/systemDesigner";
 import { defaultProject, topicData } from "@/config/data";
 import { TopicItem } from "@/types";
-import { buildTopic } from "./mindMap";
+import { buildTopic } from "@/utils/mindmap";
 import { TopicType } from "@/enum";
 import HistoryPanel from "@/components/HistoryPanel";
 

+ 1 - 1
apps/designer/src/utils/contentMenu.tsx

@@ -408,7 +408,7 @@ const topicMenuData: MenuItem[] = [
       },
       {
         key: "allSameLevel",
-        label: "选择所有分支统计主题",
+        label: "选择所有分支同级主题",
         handler: mindmapMenuHander.chooseAllSameLevel,
       },
     ],

apps/designer/src/pages/mindmap/catologue-to-tree.ts → apps/designer/src/utils/mindmap/catologue-to-tree.ts


apps/designer/src/pages/mindmap/edge.ts → apps/designer/src/utils/mindmap/edge.ts


apps/designer/src/pages/mindmap/hierarchy.ts → apps/designer/src/utils/mindmap/hierarchy.ts


+ 6 - 4
apps/designer/src/pages/mindmap/mindMap.tsx

@@ -6,7 +6,7 @@ import TopicBorder from "@/components/mindMap/Border";
 import SummaryBorder from "@/components/mindMap/SummaryBorder";
 import { topicData } from "@/config/data";
 import { uuid } from "@repo/utils";
-import { hierarchyMethodMap } from "@/pages/mindmap/hierarchy";
+import { hierarchyMethodMap } from "@/utils/mindmap/hierarchy";
 import { createEdge } from "./edge";
 import { getTheme } from "./theme";
 import { topicMenu } from "@/utils/contentMenu";
@@ -23,6 +23,7 @@ type RenderParams = {
   graph: Graph;
   setMindProjectInfo: (info: MindMapProjectInfo) => void;
   returnCells?: boolean;
+  showTool?: boolean;
 }
 
 /**
@@ -36,7 +37,8 @@ export const renderMindMap = ({
   theme,
   graph,
   setMindProjectInfo,
-  returnCells = false
+  returnCells = false,
+  showTool = true
 }: RenderParams) => {
   const cells: Cell[] = [];
   topics.forEach((topic) => {
@@ -73,14 +75,14 @@ export const renderMindMap = ({
           id,
           x: offsetX + x,
           y: offsetY + y,
-          tools: [
+          tools: showTool ? [
             {
               name: "contextmenu",
               args: {
                 menu: topicMenu,
               },
             },
-          ],
+          ] : [],
         });
         // 渲染边框
         if (data.border) {

apps/designer/src/pages/mindmap/theme/index.ts → apps/designer/src/utils/mindmap/theme/index.ts


apps/designer/src/pages/mindmap/theme/theme1.ts → apps/designer/src/utils/mindmap/theme/theme1.ts


apps/designer/src/pages/mindmap/theme/theme2.ts → apps/designer/src/utils/mindmap/theme/theme2.ts


apps/designer/src/pages/mindmap/theme/theme3.ts → apps/designer/src/utils/mindmap/theme/theme3.ts


apps/designer/src/pages/mindmap/theme/theme4.ts → apps/designer/src/utils/mindmap/theme/theme4.ts


apps/designer/src/pages/mindmap/theme/theme5.ts → apps/designer/src/utils/mindmap/theme/theme5.ts


+ 1 - 1
apps/designer/src/utils/mindmapHander.tsx

@@ -1,5 +1,5 @@
 import { BorderSize, TopicType } from "@/enum";
-import { addTopic, buildTopic } from "@/pages/mindmap/mindMap";
+import { addTopic, buildTopic } from "@/utils/mindmap";
 import { HierarchyResult, MindMapProjectInfo, TopicItem } from "@/types";
 import { Cell, Graph, Node, Edge } from "@antv/x6";
 import { message } from "antd";