Browse Source

feat: 添加括号图

liaojiaxing 6 months ago
parent
commit
b2afdea5df

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

@@ -28,7 +28,7 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
     tags,
     extraModules,
     remark,
-    herf,
+    href,
     children,
     type,
     collapsed,
@@ -62,16 +62,16 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
     }
   }, [type]);
 
-  const showHerfConfig = () => {
+  const showHrefConfig = () => {
     setShowPopover(true);
     setPopoverContent(
       <LinkForm
-        title={herf?.title}
-        value={herf?.value}
+        title={href?.title}
+        value={href?.value}
         onCancel={() => setShowPopover(false)}
         onConfirm={(data) => {
           setShowPopover(false);
-          node.setData({ herf: data });
+          node.setData({ href: data });
         }}
       />
     );
@@ -79,7 +79,7 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
 
   // @ts-ignore 绑定一个外部调用方法
   node.extendAttr = {
-    showHerfConfig,
+    showHrefConfig,
   };
 
   useEffect(() => {
@@ -166,7 +166,7 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
 
   const handleDeleteHeft = () => {
     node.setData({
-      herf: undefined,
+      href: undefined,
     }, {
       deep: false
     });
@@ -243,10 +243,10 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
               className="flex items-center color-#fff m-l-8px"
               ref={remarkRef}
             >
-              {herf && (
+              {href && (
                 <Link
-                  link={herf}
-                  onEdit={showHerfConfig}
+                  link={href}
+                  onEdit={showHrefConfig}
                   onDelete={handleDeleteHeft}
                 />
               )}

+ 2 - 2
apps/designer/src/pages/mindmap/components/Config/Structure.tsx

@@ -164,12 +164,12 @@ const structures = [
   },
   {
     name: "左侧括号图",
-    icon: "icon-zuokuohaotu",
+    icon: "icon-youkuohaotu",
     type: StructureType.leftBracket,
   },
   {
     name: "右侧括号图",
-    icon: "icon-youkuohaotu",
+    icon: "icon-zuokuohaotu",
     type: StructureType.rightBracket,
   },
 ];

+ 71 - 4
apps/designer/src/pages/mindmap/edge.ts

@@ -195,6 +195,42 @@ Graph.registerConnector(
   true
 );
 
+// 括号
+Graph.registerConnector(
+  "bracket-branch-connector",
+  function (sourcePoint, targetPoint, routerPoints, options) {
+    const midX =
+      sourcePoint.x +
+      (sourcePoint.x < targetPoint.x ? 20 : -20);
+    const midY = sourcePoint.y;
+    const pathData = `
+     M ${sourcePoint.x} ${sourcePoint.y}
+     Q ${midX} ${midY} ${midX} ${sourcePoint.y + (sourcePoint.y < targetPoint.y ? 20 : -20)}
+     L ${midX} ${targetPoint.y + (targetPoint.y < sourcePoint.y ? 20 : targetPoint.y === sourcePoint.y ? 0 : -20)}
+     Q ${midX} ${targetPoint.y} ${midX + (targetPoint.x < sourcePoint.x ? -20 : 20)} ${targetPoint.y}
+    `;
+    return options.raw ? Path.parse(pathData) : pathData;
+  },
+  true
+);
+Graph.registerConnector(
+  "bracket-sub-connector",
+  function (sourcePoint, targetPoint, routerPoints, options) {
+    const midX =
+      sourcePoint.x +
+      (sourcePoint.x < targetPoint.x ? 10 : -10);
+    const midY = sourcePoint.y;
+    const pathData = `
+     M ${sourcePoint.x} ${sourcePoint.y}
+     Q ${midX} ${midY} ${midX} ${sourcePoint.y + (sourcePoint.y < targetPoint.y ? 10 : -10)}
+     L ${midX} ${targetPoint.y + (targetPoint.y < sourcePoint.y ? 10 : targetPoint.y === sourcePoint.y ? 0 : -10)}
+     Q ${midX} ${targetPoint.y} ${midX + (targetPoint.x < sourcePoint.x ? -10 : 10)} ${targetPoint.y}
+    `;
+    return options.raw ? Path.parse(pathData) : pathData;
+  },
+  true
+);
+
 const getConnector = (
   structure: StructureType,
   theme: string,
@@ -207,6 +243,9 @@ const getConnector = (
   if (structure === StructureType.organization) {
     return `organization-${type}-connector`;
   }
+  if ([StructureType.leftBracket, StructureType.rightBracket].includes(structure)) {
+    return `bracket-${type}-connector`;
+  }
   const themeObj = getTheme(theme);
   if (type === TopicType.branch) {
     return `${themeObj.branchConnector}-${type}-connector`;
@@ -257,10 +296,24 @@ const getSourceAnchor = (type: TopicType, structure: StructureType) => {
         name: "bottom",
       };
     }
-    case StructureType.leftBracket:
+    case StructureType.leftBracket: {
+      return {
+        name: "left",
+        args: {
+          dx: -10,
+        },
+      };
+    }
+    case StructureType.rightBracket: {
+      return {
+        name: "right",
+        args: {
+          dx: 10,
+        },
+      };
+    }
     case StructureType.leftFishbone:
     case StructureType.leftTreeShape:
-    case StructureType.rightBracket:
     case StructureType.rightFishbone:
     case StructureType.rightTreeShape:
       return {
@@ -298,10 +351,24 @@ const getTargetAnchor = (type: TopicType, structure: StructureType) => {
         name: "top",
       };
     }
-    case StructureType.leftBracket:
+    case StructureType.leftBracket: {
+      return {
+        name: "right",
+        args: {
+          dx: 10,
+        },
+      };
+    }
+    case StructureType.rightBracket: {
+      return {
+        name: "left",
+        args: {
+          dx: 10,
+        },
+      };
+    }
     case StructureType.leftFishbone:
     case StructureType.leftTreeShape:
-    case StructureType.rightBracket:
     case StructureType.rightFishbone:
     case StructureType.rightTreeShape:
       return {

+ 32 - 1
apps/designer/src/pages/mindmap/hierarchy.ts

@@ -3,6 +3,7 @@ import { MindMapProjectInfo, TopicItem } from "@/types";
 import Hierarchy from "@antv/hierarchy";
 import { getTreeStructure } from "@/utils/hierarchy/tree";
 import { getOrganization } from "@/utils/hierarchy/organization";
+import { getFishbone } from "@/utils/hierarchy/fishbone";
 
 // 思维导图结构实现
 export const hierarchyMethodMap: Record<
@@ -88,5 +89,35 @@ export const hierarchyMethodMap: Record<
   // 组织结构
   [StructureType.organization]: <T>(topic: TopicItem, pageSetting: MindMapProjectInfo['pageSetting']): T => {
     return getOrganization<T>(topic, pageSetting);
-  }
+  },
+  // 左侧鱼骨图
+  [StructureType.leftFishbone]: <T>(topic: TopicItem, pageSetting: MindMapProjectInfo['pageSetting']): T => {
+    if(topic.type === TopicType.main) {
+      return getFishbone<T>(topic, 'left')
+    } else {
+      return hierarchyMethodMap[StructureType.left](topic, pageSetting);
+    }
+  },
+  // 左侧鱼骨图
+  [StructureType.rightFishbone]: <T>(topic: TopicItem, pageSetting: MindMapProjectInfo['pageSetting']): T => {
+    if(topic.type === TopicType.main) {
+      return getFishbone<T>(topic, 'right')
+    } else {
+      return hierarchyMethodMap[StructureType.left](topic, pageSetting);
+    }
+  },
+  // 右侧括号图
+  [StructureType.rightBracket]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    return hierarchyMethodMap[StructureType.right](topic, pageSetting);
+  },
+  // 左侧括号图
+  [StructureType.leftBracket]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    return hierarchyMethodMap[StructureType.left](topic, pageSetting);
+  },
 };

+ 15 - 11
apps/designer/src/pages/mindmap/mindMap.tsx

@@ -1,4 +1,4 @@
-import { TopicType } from "@/enum";
+import { StructureType, TopicType } from "@/enum";
 import { MindMapProjectInfo, TopicItem, HierarchyResult } from "@/types";
 import { Graph, Cell, Node } from "@antv/x6";
 import TopicComponent from "@/components/mindMap/Topic";
@@ -67,16 +67,20 @@ export const renderMindMap = (graph: Graph, setMindProjectInfo: () => void) => {
         }
 
         if (children) {
-          children.forEach((item: HierarchyResult) => {
-            const edge = createEdge(
-              graph,
-              id,
-              item,
-              projectInfo.structure,
-              projectInfo.theme
-            );
-            cells.push(edge);
-            node.addChild(edge);
+          children.forEach((item: HierarchyResult, index) => {
+            const isBracket = [StructureType.leftBracket, StructureType.rightBracket].includes(projectInfo.structure)
+            // 括号图不绘制中间连线
+            if(!isBracket || (index === 0 || index === children.length - 1)) {
+              const edge = createEdge(
+                graph,
+                id,
+                item,
+                projectInfo.structure,
+                projectInfo.theme
+              );
+              cells.push(edge);
+              node.addChild(edge);
+            }
             // 递归遍历
             traverse(item, node);
           });

+ 1 - 1
apps/designer/src/types.d.ts

@@ -105,7 +105,7 @@ export type TopicItem = {
   /**
    * 链接
    */
-  herf?: {
+  href?: {
     title: string;
     value: string;
   };

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

@@ -362,10 +362,10 @@ const topicMenuData: MenuItem[] = [
         handler: mindmapMenuHander.addRemark,
       },
       {
-        key: "herf",
+        key: "href",
         label: "链接",
         fastKey: "Ctrl+K",
-        handler: mindmapMenuHander.addHerf,
+        handler: mindmapMenuHander.addHref,
       },
       // {
       //   key: "topicLink",

+ 56 - 0
apps/designer/src/utils/hierarchy/fishbone.ts

@@ -0,0 +1,56 @@
+import { TopicType } from "@/enum";
+import { MindMapProjectInfo, TopicItem, HierarchyResult } from "@/types";
+
+const DEFAULT_NODE_WIDTH = 100;
+const DEFAULT_NODE_HEIGHT = 30;
+const HORIZONTAL_SPACING = 50;
+const VERTICAL_SPACING = 50;
+
+function traverseHierarchy(
+  node: TopicItem,
+  x: number,
+  y: number,
+  direction: 'left' | 'right',
+  depth: number = 0
+): HierarchyResult {
+  // 计算节点和子节点的尺寸及位置
+  const width = node.fixedWidth ? node.width : DEFAULT_NODE_WIDTH;
+  const height = node.fixedWidth ? node.height : DEFAULT_NODE_HEIGHT;
+
+  let totalWidth = width;
+  let totalHeight = height;
+  let children: HierarchyResult[] = [];
+
+  if (node.children && node.children.length > 0) {
+    const childSpacing = (direction === 'left' ? -1 : 1) * (width / 2 + HORIZONTAL_SPACING);
+    let currentY = y + height + VERTICAL_SPACING;
+
+    children = node.children.map((childNode, index) => {
+      const childX = x + childSpacing * (index + 1); // 根据方向和索引调整子节点的 x 坐标
+      return traverseHierarchy(childNode, childX, currentY, direction, depth + 1);
+    });
+
+    // 计算子节点的总宽度和总高度
+    totalWidth = Math.max(...children.map(child => child.totalWidth), width);
+    totalHeight += VERTICAL_SPACING + children.map(child => child.totalHeight).reduce((a, b) => a + b, 0);
+  }
+
+  return {
+    id: node.id,
+    x,
+    y,
+    data: node,
+    width,
+    height,
+    children,
+    totalWidth,
+    totalHeight
+  };
+}
+
+export function getFishbone<T>(
+  root: TopicItem,
+  direction: 'left' | 'right' = 'left'
+): T {
+  return traverseHierarchy(root, 0, 0, direction) as T;
+}

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

@@ -398,10 +398,10 @@ export const mindmapMenuHander = {
     tool.graph?.extendAttr?.setRightToolbarActive("remark");
     selectTopic(tool.graph, tool.cell.data);
   },
-  addHerf(tool: ContextMenuTool) {
+  addHref(tool: ContextMenuTool) {
     console.log(tool.cell)
     // @ts-ignore
-    tool.cell?.extendAttr?.showHerfConfig?.();
+    tool.cell?.extendAttr?.showHrefConfig?.();
   },
   addTopicLink(tool: ContextMenuTool) {
     // todo