ソースを参照

feat: 添加鱼骨图

liaojiaxing 7 ヶ月 前
コミット
48a8dbf092

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

@@ -36,12 +36,12 @@ const structures = [
   },
   {
     name: "左侧鱼骨图",
-    icon: "icon-siweidaotubianjiqi-3",
+    icon: "icon-siweidaotubianjiqi-4",
     type: StructureType.leftFishbone,
   },
   {
     name: "右侧鱼骨图",
-    icon: "icon-siweidaotubianjiqi-4",
+    icon: "icon-siweidaotubianjiqi-3",
     type: StructureType.rightFishbone,
   },
   {

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

@@ -17,7 +17,7 @@ InsertCss(`
   }
 `)
 export default function index() {
-  const { rightToobarActive, selectedCell } = useModel("mindMapModel");
+  const { rightToobarActive, selectedCell, rightToolbarActive } = useModel("mindMapModel");
   const [activeKey, setActiveKey] = useState("1");
   useEffect(() => {
     if(selectedCell.find(cell => cell.isNode())) {
@@ -27,7 +27,10 @@ export default function index() {
     }
   }, [selectedCell]);
   return (
-    <div>
+    <div className="relative">
+      <div className="absolute w-16px h-16px right-10px top-10px cursor-pointer z-99" onClick={() => rightToolbarActive("")}>
+        <i className="iconfont icon-cuowu-1 color-#666 hover:color-#333"/>
+      </div>
       {/* 样式 */}
       {rightToobarActive === "style" && (
         <>

+ 310 - 26
apps/designer/src/pages/mindmap/edge.ts

@@ -2,6 +2,7 @@ import { StructureType, MindmapConnectorType, TopicType } from "@/enum";
 import { TopicItem } from "@/types";
 import { Edge, Graph, Path } from "@antv/x6";
 import { getTheme } from "./theme";
+import { caclculateX } from "@/utils/hierarchy/rightFishbone";
 
 // 曲线
 Graph.registerConnector(
@@ -199,9 +200,7 @@ Graph.registerConnector(
 Graph.registerConnector(
   "bracket-branch-connector",
   function (sourcePoint, targetPoint, routerPoints, options) {
-    const midX =
-      sourcePoint.x +
-      (sourcePoint.x < targetPoint.x ? 20 : -20);
+    const midX = sourcePoint.x + (sourcePoint.x < targetPoint.x ? 20 : -20);
     const midY = sourcePoint.y;
     const pathData = `
      M ${sourcePoint.x} ${sourcePoint.y}
@@ -216,9 +215,7 @@ Graph.registerConnector(
 Graph.registerConnector(
   "bracket-sub-connector",
   function (sourcePoint, targetPoint, routerPoints, options) {
-    const midX =
-      sourcePoint.x +
-      (sourcePoint.x < targetPoint.x ? 10 : -10);
+    const midX = sourcePoint.x + (sourcePoint.x < targetPoint.x ? 10 : -10);
     const midY = sourcePoint.y;
     const pathData = `
      M ${sourcePoint.x} ${sourcePoint.y}
@@ -231,21 +228,274 @@ Graph.registerConnector(
   true
 );
 
+// 一个子项的括号
+Graph.registerConnector(
+  "bracket-one-branch-connector",
+  function (sourcePoint, targetPoint, routerPoints, options, edgeView) {
+    const targetCell = edgeView.targetView?.cell;
+    let height = 0;
+    if (targetCell?.isNode()) {
+      height = targetCell.getBBox().height;
+    }
+    const targetTop = targetPoint.y - height / 2 - 3;
+    const targetBottom = targetPoint.y + height / 2 + 3;
+    const offset = 10;
+    const midX =
+      sourcePoint.x + (sourcePoint.x < targetPoint.x ? offset : -offset);
+    const midY = sourcePoint.y;
+    const pathData = `
+     M ${sourcePoint.x} ${sourcePoint.y}
+     Q ${midX} ${midY} ${midX} ${sourcePoint.y - offset}
+     L ${midX} ${targetTop + offset}
+     Q ${midX} ${targetTop} ${midX + offset} ${targetTop}
+     M ${sourcePoint.x} ${sourcePoint.y}
+     Q ${midX} ${midY} ${midX} ${sourcePoint.y + offset}
+     L ${midX} ${targetBottom - offset}
+     Q ${midX} ${targetBottom} ${midX + offset} ${targetBottom}
+    `;
+    return options.raw ? Path.parse(pathData) : pathData;
+  },
+  true
+);
+Graph.registerConnector(
+  "bracket-one-sub-connector",
+  function (sourcePoint, targetPoint, routerPoints, options, edgeView) {
+    const targetCell = edgeView.targetView?.cell;
+    let height = 0;
+    if (targetCell?.isNode()) {
+      height = targetCell.getBBox().height;
+    }
+    const targetTop = targetPoint.y - height / 2 + 3;
+    const targetBottom = targetPoint.y + height / 2 - 3;
+    const offset = 5;
+    const midX =
+      sourcePoint.x + (sourcePoint.x < targetPoint.x ? offset : -offset);
+    const midY = sourcePoint.y;
+    const pathData = `
+     M ${sourcePoint.x} ${sourcePoint.y}
+     Q ${midX} ${midY} ${midX} ${sourcePoint.y - offset}
+     L ${midX} ${targetTop + offset}
+     Q ${midX} ${targetTop} ${midX + offset} ${targetTop}
+     M ${sourcePoint.x} ${sourcePoint.y}
+     Q ${midX} ${midY} ${midX} ${sourcePoint.y + offset}
+     L ${midX} ${targetBottom - offset}
+     Q ${midX} ${targetBottom} ${midX + offset} ${targetBottom}
+    `;
+    return options.raw ? Path.parse(pathData) : pathData;
+  },
+  true
+);
+
+// 树形图
+Graph.registerConnector(
+  "tree-shape-connector",
+  function (sourcePoint, targetPoint, routerPoints, options) {
+    const pathData = `
+     M ${sourcePoint.x} ${sourcePoint.y}
+     L ${sourcePoint.x} ${targetPoint.y}
+     L ${targetPoint.x} ${targetPoint.y}
+    `;
+    return options.raw ? Path.parse(pathData) : pathData;
+  },
+  true
+);
+
+// 右侧鱼骨图
+Graph.registerConnector(
+  "fishbone-branch-right-connector",
+  function (sourcePoint, targetPoint, routerPoints, options, edgeView) {
+    const direction = sourcePoint.y > targetPoint.y ? "up" : "down";
+    const node = edgeView.targetView?.cell;
+    let targetY = targetPoint.y;
+    if (node?.isNode() && direction === "down") {
+      targetY -= node.size().height;
+    }
+    const midX = caclculateX({ x: targetPoint.x, y: targetY }, sourcePoint.y, "right", direction);
+    let pathData = "";
+    if (direction === "up") {
+      pathData = `
+     M ${targetPoint.x} ${targetPoint.y}
+     L ${midX} ${sourcePoint.y}
+     L ${sourcePoint.x} ${sourcePoint.y}
+    `;
+    } else {
+      pathData = `
+      M ${targetPoint.x} ${targetY}
+      L ${midX} ${sourcePoint.y}
+      L ${sourcePoint.x} ${sourcePoint.y}
+     `;
+    }
+
+    return options.raw ? Path.parse(pathData) : pathData;
+  },
+  true
+);
+Graph.registerConnector(
+  "fishbone-sub-right-connector",
+  function (sourcePoint, targetPoint, routerPoints, options, edgeView) {
+    const sourceNode = edgeView.sourceView?.cell;
+    const direction = sourcePoint.y < targetPoint.y ? "up" : "down";
+    let midX = caclculateX(sourcePoint, targetPoint.y, "right", direction);
+    if (direction === "down" && sourceNode?.isNode()) {
+      midX = caclculateX(
+        { x: sourcePoint.x, y: sourcePoint.y - sourceNode.size().height },
+        targetPoint.y,
+        "right",
+        direction
+      );
+    }
+
+    let pathData = "";
+    if (sourceNode?.isNode() && sourceNode.data.type === TopicType.sub) {
+      const size = sourceNode.size();
+      const sourceP = {
+        x: sourcePoint.x + size.width / 2,
+        y: sourcePoint.y - size.height / 2,
+      };
+      pathData = `
+       M ${sourceP.x} ${sourceP.y}
+       L ${sourceP.x + 10} ${sourceP.y}
+       L ${sourceP.x + 10} ${targetPoint.y}
+       L ${targetPoint.x} ${targetPoint.y}
+      `;
+    } else {
+      pathData = `
+        M ${midX} ${targetPoint.y}
+        L ${targetPoint.x} ${targetPoint.y}
+        `;
+    }
+    return options.raw ? Path.parse(pathData) : pathData;
+  },
+  true
+);
+
+// 左侧鱼骨图
+Graph.registerConnector(
+  "fishbone-branch-left-connector",
+  function (sourcePoint, targetPoint, routerPoints, options, edgeView) {
+    const direction = sourcePoint.y > targetPoint.y ? "up" : "down";
+    const node = edgeView.targetView?.cell;
+    let targetY = targetPoint.y;
+    if (node?.isNode() && direction === "down") {
+      targetY -= node.size().height;
+    }
+    const midX = caclculateX({ x: targetPoint.x, y: targetY }, sourcePoint.y, "left", direction);
+    let pathData = "";
+    if (direction === "up") {
+      pathData = `
+     M ${targetPoint.x} ${targetPoint.y}
+     L ${midX} ${sourcePoint.y}
+     L ${sourcePoint.x} ${sourcePoint.y}
+    `;
+    } else {
+      pathData = `
+      M ${targetPoint.x} ${targetY}
+      L ${midX} ${sourcePoint.y}
+      L ${sourcePoint.x} ${sourcePoint.y}
+     `;
+    }
+
+    return options.raw ? Path.parse(pathData) : pathData;
+  },
+  true
+);
+Graph.registerConnector(
+  "fishbone-sub-left-connector",
+  function (sourcePoint, targetPoint, routerPoints, options, edgeView) {
+    const sourceNode = edgeView.sourceView?.cell;
+
+    let pathData = "";
+    // 子主题到子主题
+    if (sourceNode?.isNode() && sourceNode.data.type === TopicType.sub) {
+      const size = sourceNode.size();
+      const sourceP = {
+        x: sourcePoint.x - size.width,
+        y: sourcePoint.y,
+      };
+      pathData = `
+       M ${sourceP.x} ${sourceP.y}
+       L ${sourceP.x - 10} ${sourceP.y}
+       L ${sourceP.x - 10} ${targetPoint.y}
+       L ${targetPoint.x} ${targetPoint.y}
+      `;
+    } else {
+      // 分支子主题到子主题
+      const direction = sourcePoint.y < targetPoint.y ? "up" : "down";
+      const size = sourceNode?.isNode() ? sourceNode.size() : { width: 0, height: 0};
+      let midX = caclculateX({
+        x: sourcePoint.x - size.width / 2,
+        y: sourcePoint.y + size.height / 2,
+      }, targetPoint.y, "left", direction);
+      if (direction === "down") {
+        midX = caclculateX(
+          { 
+            x: sourcePoint.x - size.width / 2,
+            y: sourcePoint.y - size.height / 2,
+           },
+          targetPoint.y,
+          "left",
+          direction
+        );
+      }
+      pathData = `
+        M ${midX} ${targetPoint.y}
+        L ${targetPoint.x} ${targetPoint.y}
+        `;
+    }
+    return options.raw ? Path.parse(pathData) : pathData;
+  },
+  true
+);
 const getConnector = (
   structure: StructureType,
   theme: string,
-  type: TopicType
+  type: TopicType,
+  options?: Record<string, any>
 ) => {
-  // TODO根据结构处理连接线
+  // 树状结构图
   if (structure === StructureType.tree) {
     return `tree-${type}-connector`;
   }
+  // 组织结构图
   if (structure === StructureType.organization) {
     return `organization-${type}-connector`;
   }
-  if ([StructureType.leftBracket, StructureType.rightBracket].includes(structure)) {
+  // 时间图
+  if (
+    [
+      StructureType.upwardTime,
+      StructureType.downwardTime,
+      StructureType.horizontalTime,
+    ].includes(structure)
+  ) {
+    return `poly-${type}-connector`;
+  }
+  // 括号图
+  if (
+    [StructureType.leftBracket, StructureType.rightBracket].includes(structure)
+  ) {
+    if (options?.onlyOneChild) {
+      return `bracket-one-${type}-connector`;
+    }
     return `bracket-${type}-connector`;
   }
+  // 树形图
+  if (
+    [
+      StructureType.leftTreeShape,
+      StructureType.rightTreeShape,
+      StructureType.treeShape,
+    ].includes(structure)
+  ) {
+    return `tree-shape-connector`;
+  }
+  // 鱼骨
+  if (structure === StructureType.leftFishbone) {
+    return `fishbone-${type}-left-connector`;
+  }
+  if (structure === StructureType.rightFishbone) {
+    return `fishbone-${type}-right-connector`;
+  }
   const themeObj = getTheme(theme);
   if (type === TopicType.branch) {
     return `${themeObj.branchConnector}-${type}-connector`;
@@ -254,7 +504,11 @@ const getConnector = (
   }
 };
 
-const getSourceAnchor = (type: TopicType, structure: StructureType) => {
+const getSourceAnchor = (
+  type: TopicType,
+  structure: StructureType,
+  options?: Record<string, any>
+) => {
   switch (structure) {
     case StructureType.left: {
       return type === TopicType.branch
@@ -312,20 +566,38 @@ const getSourceAnchor = (type: TopicType, structure: StructureType) => {
         },
       };
     }
-    case StructureType.leftFishbone:
-    case StructureType.leftTreeShape:
-    case StructureType.rightFishbone:
-    case StructureType.rightTreeShape:
+    case StructureType.upwardTime:
+    case StructureType.downwardTime:
+    case StructureType.horizontalTime: {
       return {
         name: "center",
       };
+    }
+    case StructureType.leftFishbone: {
+      return {
+        name: type === TopicType.branch ? "left" : "right",
+      };
+    }
+    case StructureType.rightFishbone:
+      return {
+        name: type === TopicType.branch ? "right" : "bottom",
+      };
+    case StructureType.leftTreeShape:
+    case StructureType.rightTreeShape:
+    case StructureType.treeShape: {
+      return "bottom";
+    }
   }
   return {
     name: "center",
   };
 };
 
-const getTargetAnchor = (type: TopicType, structure: StructureType) => {
+const getTargetAnchor = (
+  type: TopicType,
+  structure: StructureType,
+  options?: Record<string, any>
+) => {
   switch (structure) {
     case StructureType.left: {
       return {
@@ -367,16 +639,27 @@ const getTargetAnchor = (type: TopicType, structure: StructureType) => {
         },
       };
     }
-    case StructureType.leftFishbone:
-    case StructureType.leftTreeShape:
+    case StructureType.upwardTime:
+    case StructureType.downwardTime:
+    case StructureType.horizontalTime: {
+      return {
+        name: "left",
+      };
+    }
+    case StructureType.leftFishbone: {
+      return {
+        name: type === TopicType.branch ? "bottom" : "right",
+      };
+    }
     case StructureType.rightFishbone:
-    case StructureType.rightTreeShape:
       return {
-        name: "center",
-        args: {
-          dx: "25%",
-        },
+        name: type === TopicType.branch ? "bottom" : "left",
       };
+    case StructureType.leftTreeShape:
+    case StructureType.rightTreeShape:
+    case StructureType.treeShape: {
+      return "left";
+    }
   }
   return {
     name: "center",
@@ -400,22 +683,23 @@ export const createEdge = (
   sourceId: string,
   item: { id: string; data: TopicItem },
   structure: StructureType,
-  theme: string
+  theme: string,
+  options: Record<string, any> = {}
 ): Edge => {
   return graph.createEdge({
     id: item.id + "-edge",
     inherit: "edge",
     connector: {
-      name: getConnector(structure, theme, item.data.type),
+      name: getConnector(structure, theme, item.data.type, options),
     },
     zIndex: 0,
     source: {
       cell: sourceId,
-      anchor: getSourceAnchor(item.data.type, structure),
+      anchor: getSourceAnchor(item.data.type, structure, options),
     },
     target: {
       cell: item.id,
-      anchor: getTargetAnchor(item.data.type, structure),
+      anchor: getTargetAnchor(item.data.type, structure, options),
     },
     attrs: {
       line: {

+ 93 - 19
apps/designer/src/pages/mindmap/hierarchy.ts

@@ -1,9 +1,12 @@
 import { StructureType, TopicType } from "@/enum";
 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";
+import { getTreeDiagramHierarchy } from "@/utils/hierarchy/treeDiagram";
+import { getOrganizationHierarchy } from "@/utils/hierarchy/organization";
+import { getRightFishboneHierarchy } from "@/utils/hierarchy/rightFishbone";
+import { getLeftFishboneHierarchy } from "@/utils/hierarchy/leftFishbone";
+import { getTimeHierarchy } from "@/utils/hierarchy/time";
+import { getTreeHierarchy } from "@/utils/hierarchy/tree";
 
 // 思维导图结构实现
 export const hierarchyMethodMap: Record<
@@ -72,36 +75,58 @@ export const hierarchyMethodMap: Record<
     if (topic.type !== TopicType.main) {
       return hierarchyMethodMap[StructureType.left](topic, pageSetting);
     } else {
-      const rightChildren = (topic.children || []).filter((item, index) => index % 2 === 0);
-      const leftChildren = (topic.children || []).filter((item, index) => index % 2 === 1);
-      const left = hierarchyMethodMap[StructureType.left]({...topic, children: leftChildren}, pageSetting);
-      const right = hierarchyMethodMap[StructureType.right]({...topic, children: rightChildren}, pageSetting);
+      const rightChildren = (topic.children || []).filter(
+        (item, index) => index % 2 === 0
+      );
+      const leftChildren = (topic.children || []).filter(
+        (item, index) => index % 2 === 1
+      );
+      const left = hierarchyMethodMap[StructureType.left](
+        { ...topic, children: leftChildren },
+        pageSetting
+      );
+      const right = hierarchyMethodMap[StructureType.right](
+        { ...topic, children: rightChildren },
+        pageSetting
+      );
       return {
         ...right,
-        children: [...right.children, ...left.children]
-      }
+        children: [...right.children, ...left.children],
+      };
     }
   },
   // 树状结构
-  [StructureType.tree]: <T>(topic: TopicItem, pageSetting: MindMapProjectInfo['pageSetting']): T => {
-    return getTreeStructure<T>(topic, pageSetting);
+  [StructureType.tree]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    return getTreeDiagramHierarchy<T>(topic, pageSetting);
   },
   // 组织结构
-  [StructureType.organization]: <T>(topic: TopicItem, pageSetting: MindMapProjectInfo['pageSetting']): T => {
-    return getOrganization<T>(topic, pageSetting);
+  [StructureType.organization]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    return getOrganizationHierarchy<T>(topic, pageSetting);
   },
   // 左侧鱼骨图
-  [StructureType.leftFishbone]: <T>(topic: TopicItem, pageSetting: MindMapProjectInfo['pageSetting']): T => {
-    if(topic.type === TopicType.main) {
-      return getFishbone<T>(topic, 'left')
+  [StructureType.leftFishbone]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    if (topic.type === TopicType.main) {
+      return getLeftFishboneHierarchy<T>(topic);
     } 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')
+  [StructureType.rightFishbone]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    if (topic.type === TopicType.main) {
+      return getRightFishboneHierarchy<T>(topic);
     } else {
       return hierarchyMethodMap[StructureType.left](topic, pageSetting);
     }
@@ -120,4 +145,53 @@ export const hierarchyMethodMap: Record<
   ): T => {
     return hierarchyMethodMap[StructureType.left](topic, pageSetting);
   },
+  // 向上时间轴
+  [StructureType.upwardTime]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    if (topic.type !== TopicType.main) {
+      return hierarchyMethodMap[StructureType.right](topic, pageSetting);
+    }
+    return getTimeHierarchy<T>(topic, pageSetting, "up");
+  },
+  // 向下时间轴
+  [StructureType.downwardTime]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    if (topic.type !== TopicType.main) {
+      return hierarchyMethodMap[StructureType.right](topic, pageSetting);
+    }
+    return getTimeHierarchy<T>(topic, pageSetting, "down");
+  },
+  // 横向时间轴
+  [StructureType.horizontalTime]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    if (topic.type !== TopicType.main) {
+      return hierarchyMethodMap[StructureType.right](topic, pageSetting);
+    }
+    return getTimeHierarchy<T>(topic, pageSetting, "updown");
+  },
+  // 树形
+  [StructureType.rightTreeShape]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    return getTreeHierarchy<T>(topic, pageSetting, "right");
+  },
+  [StructureType.leftTreeShape]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    return getTreeHierarchy<T>(topic, pageSetting, "left");
+  },
+  [StructureType.treeShape]: <T>(
+    topic: TopicItem,
+    pageSetting: MindMapProjectInfo["pageSetting"]
+  ): T => {
+    return getTreeHierarchy<T>(topic, pageSetting, "leftright");
+  },
 };

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

@@ -76,7 +76,10 @@ export const renderMindMap = (graph: Graph, setMindProjectInfo: () => void) => {
                 id,
                 item,
                 projectInfo.structure,
-                projectInfo.theme
+                projectInfo.theme,
+                {
+                  onlyOneChild: children.length === 1
+                }
               );
               cells.push(edge);
               node.addChild(edge);

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

@@ -1,56 +0,0 @@
-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;
-}

+ 163 - 0
apps/designer/src/utils/hierarchy/leftFishbone.ts

@@ -0,0 +1,163 @@
+import { TopicItem, HierarchyResult } from "@/types";
+import { caclculateX } from "@/utils/hierarchy/rightFishbone";
+
+// branch subtopic
+const B2SH = 20;
+const S2SH = 20;
+const B2SV = 20;
+const S2SV = 20;
+function firstWalk(node: TopicItem): HierarchyResult {
+  const root: HierarchyResult = {
+    x: 0,
+    y: 0,
+    totalHeight: 0,
+    totalWidth: 0,
+    width: node.width,
+    height: node.height,
+    id: node.id,
+    data: node,
+    children: [],
+  };
+
+  root.children = node.collapsed
+    ? []
+    : node.children?.map((child) => firstWalk(child)) || [];
+
+  return root;
+}
+
+// 计算每个分支主题位置
+function secondWalk(root: HierarchyResult) {
+  let startUpX = -B2SH;
+  let startDownX = -B2SH;
+  let startY = root.height / 2;
+  root.children.forEach((branch, i) => {
+    // 上排骨架处理
+    if(i%2 === 0) {
+      const x =  Math.min(startUpX, startDownX);
+      const childPosition = thirdWalk(branch, "up", {
+        x,
+        y: startY,
+      });
+  
+      branch.x = childPosition.endX - branch.width / 2;
+      // 本身大于子主题的宽
+      if (branch.width >= childPosition.totalWidth) {
+        branch.x = x - branch.width;
+      }
+      branch.y = childPosition.endY - branch.height;
+      branch.totalHeight = branch.height + childPosition.totalHeight;
+      branch.totalWidth = Math.max(branch.width, childPosition.totalWidth);
+      startUpX = x - branch.totalWidth - S2SH;
+    } else {
+      const previous = root.children[i - 1];
+      const x = caclculateX({
+        x: previous.x + previous.width / 2,
+        y: previous.y + previous.height,
+      }, startY, 'left', 'up') - S2SH;
+      const childPosition = thirdWalk(branch, "down", {
+        x,
+        y: startY,
+      });
+  
+      branch.x = childPosition.endX - branch.width / 2;
+      // 本身大于子主题的宽
+      if (branch.width >= childPosition.totalWidth) {
+        branch.x = x - S2SH;
+      }
+  
+      branch.y = childPosition.endY + branch.height;
+      branch.totalHeight = branch.height + childPosition.totalHeight;
+      branch.totalWidth = Math.max(branch.width, childPosition.totalWidth);
+      startDownX = x - branch.totalWidth - S2SH;
+    }
+  });
+}
+
+// 计算一级子主题的宽高位置
+function thirdWalk(
+  branch: HierarchyResult,
+  direction: "up" | "down",
+  position: { x: number; y: number }
+) {
+  let totalHeight = 0;
+  let totalWidth = 0;
+  // 记录最终分支主题的位置
+  let startY = position.y + (direction === "up" ? -2*B2SV : +B2SV);
+
+  for (let i = branch.children.length - 1; i >= 0; i--) {
+    const child = branch.children[i];
+    const childSize = fourthWalk(child);
+    // 根据上下,设置子节点Y的位置
+    if(direction === "up") {
+      child.y = startY - childSize.totalHeight / 2;
+      startY += -childSize.totalHeight - S2SV;
+    }
+    if(direction === "down") {
+      child.y = startY + childSize.totalHeight / 2;
+      startY += childSize.totalHeight + S2SV;
+    }
+    // 计算出在斜线上x的位置 最后的结果加上偏移量
+    const x = caclculateX(position, child.y, 'left', direction);
+    child.x = x - child.width - S2SH;
+    // 总的宽度 = 子节点到最开始的位置的距离中最大的一个
+    const w = child.x - childSize.totalWidth - S2SH;
+    totalWidth = Math.max(totalWidth, -w);
+    totalHeight += childSize.totalHeight + B2SV;
+
+    fivethWalk(child);
+  }
+
+  return {
+    endX: caclculateX(position, startY, 'left', direction),
+    endY: startY,
+    totalHeight: Math.max(branch.height, totalHeight),
+    totalWidth: Math.max(branch.width, totalWidth),
+  };
+}
+
+// 获取子主题的宽高
+function fourthWalk(node: HierarchyResult) {
+  let totalHeight = 0;
+  let totalWidth = node.width;
+
+  node.children.forEach((child: HierarchyResult, i) => {
+    const childrenSize = fourthWalk(child);
+    totalHeight += childrenSize.totalHeight;
+    if(i !== node.children.length - 1) {
+      totalHeight += S2SV;
+    }
+    totalWidth = Math.max(totalWidth, node.width + childrenSize.totalWidth);
+  });
+
+  node.totalHeight = Math.max(node.height, totalHeight);
+  node.totalWidth = totalWidth;
+  return {
+    totalWidth: node.totalWidth,
+    totalHeight: node.totalHeight,
+  };
+}
+
+// 设置子子主题位置
+function fivethWalk(node: HierarchyResult) {
+  let startY = node.y + node.height / 2 - node.totalHeight / 2;
+  const startX = node.x - S2SH;
+  node.children?.forEach((child) => {
+    child.x = startX - child.width;
+    child.y = startY + child.totalHeight / 2 - child.height / 2;
+    startY += child.totalHeight + S2SV;
+    if (child.children?.length) {
+      fivethWalk(child);
+    }
+  });
+}
+
+export const getLeftFishboneHierarchy = <K>(
+  topic: TopicItem,
+): K => {
+  const root = firstWalk(topic);
+  secondWalk(root);
+
+  console.log(root);
+  return root as K;
+};

+ 2 - 2
apps/designer/src/utils/hierarchy/organization.ts

@@ -14,7 +14,7 @@ function firstWalk(node: TopicItem): HierarchyResult {
     children: [],
   };
 
-  root.children = node.children?.map((child) => firstWalk(child)) || [];
+  root.children = node.collapsed ? [] : node.children?.map((child) => firstWalk(child)) || [];
 
   return root;
 }
@@ -63,7 +63,7 @@ function thirdWalk(node: HierarchyResult, pageSetting: MindMapProjectInfo['pageS
 }
 
 
-export const getOrganization = <K>(
+export const getOrganizationHierarchy = <K>(
   topic: TopicItem,
   pageSetting: MindMapProjectInfo["pageSetting"]
 ): K => {

+ 185 - 0
apps/designer/src/utils/hierarchy/rightFishbone.ts

@@ -0,0 +1,185 @@
+import { TopicType } from "@/enum";
+import { MindMapProjectInfo, TopicItem, HierarchyResult } from "@/types";
+
+// branch subtopic
+const B2SH = 20;
+const S2SH = 20;
+const B2SV = 20;
+const S2SV = 20;
+
+// 计算X值
+export function caclculateX(
+  start: { x: number; y: number },
+  y: number,
+  type: "left" | "right",
+  direction: "up" | "down"
+) {
+  let angle = -60;
+  if (
+    (direction === "down" && type === "right") ||
+    (direction === "up" && type === "left")
+  ) {
+    angle = 60;
+  }
+
+  const theta = angle * (Math.PI / 180);
+  const slope = Math.tan(theta);
+  return start.x + (y - start.y) / slope;
+}
+
+function firstWalk(node: TopicItem): HierarchyResult {
+  const root: HierarchyResult = {
+    x: 0,
+    y: 0,
+    totalHeight: 0,
+    totalWidth: 0,
+    width: node.width,
+    height: node.height,
+    id: node.id,
+    data: node,
+    children: [],
+  };
+
+  root.children = node.collapsed
+    ? []
+    : node.children?.map((child) => firstWalk(child)) || [];
+
+  return root;
+}
+
+// 计算每个分支主题位置
+function secondWalk(root: HierarchyResult) {
+  let startUpX = root.width + B2SH;
+  let startDownX = root.width + B2SH;
+  let startY = root.height / 2;
+  let upTimesX = startUpX;
+  root.children.forEach((branch, i) => {
+    // 上排骨架处理
+    if (i % 2 === 0) {
+      const x = Math.max(startUpX, startDownX);
+      const childPosition = thirdWalk(branch, "up", {
+        x,
+        y: startY,
+      });
+      upTimesX = x;
+
+      branch.x = childPosition.endX - branch.width / 2;
+      // 本身大于子主题的宽
+      if (branch.width >= childPosition.totalWidth) {
+        branch.x = x;
+        upTimesX = caclculateX(
+          { x: branch.x + branch.width / 2, y: childPosition.endY },
+          startY,
+          "right",
+          "up"
+        );
+      }
+      branch.y = childPosition.endY - branch.height;
+      branch.totalHeight = branch.height + childPosition.totalHeight;
+      branch.totalWidth = Math.max(branch.width, childPosition.totalWidth);
+      startUpX = upTimesX + branch.totalWidth + S2SH;
+    } else {
+      const childPosition = thirdWalk(branch, "down", {
+        x: upTimesX + 40,
+        y: startY,
+      });
+
+      branch.x = childPosition.endX - branch.width / 2;
+      // 本身大于子主题的宽
+      if (branch.width >= childPosition.totalWidth) {
+        branch.x = upTimesX + S2SH;
+      }
+
+      branch.y = childPosition.endY;
+      branch.totalHeight = branch.height + childPosition.totalHeight;
+      branch.totalWidth = Math.max(branch.width, childPosition.totalWidth);
+      startDownX = upTimesX + branch.totalWidth + S2SH;
+    }
+  });
+}
+
+// 计算一级子主题的宽高位置
+function thirdWalk(
+  branch: HierarchyResult,
+  direction: "up" | "down",
+  position: { x: number; y: number }
+) {
+  let totalHeight = 0;
+  let totalWidth = 0;
+  // 记录最终分支主题的位置
+  let startY = position.y + (direction === "up" ? -B2SV : +B2SV);
+
+  for (let i = branch.children.length - 1; i >= 0; i--) {
+    const child = branch.children[i];
+    const childSize = fourthWalk(child);
+    // 根据上下,设置子节点Y的位置
+    if (direction === "up") {
+      child.y = startY - childSize.totalHeight / 2 - child.height / 2;
+      startY = startY - childSize.totalHeight - S2SV;
+    }
+    if (direction === "down") {
+      child.y = startY + childSize.totalHeight / 2 - child.height / 2;
+      startY = startY + childSize.totalHeight + S2SV;
+    }
+    // 计算出在斜线上x的位置 最后的结果加上偏移量
+    const x = caclculateX(position, child.y, "right", direction);
+    child.x = x + S2SH;
+    // 总的宽度 = 子节点到最开始的位置的距离中最大的一个
+    const w = child.x + childSize.totalWidth - position.x;
+    totalWidth = Math.max(totalWidth, w);
+    totalHeight += childSize.totalHeight + B2SV;
+
+    fivethWalk(child);
+  }
+
+  return {
+    endX: caclculateX(position, startY, "right", direction),
+    endY: startY,
+    totalHeight: Math.max(branch.height, totalHeight),
+    totalWidth: Math.max(branch.width, totalWidth),
+  };
+}
+
+// 获取子主题的宽高
+function fourthWalk(node: HierarchyResult) {
+  let totalHeight = 0;
+  let totalWidth = node.width;
+
+  node.children.forEach((child: HierarchyResult, i) => {
+    const childrenSize = fourthWalk(child);
+    totalHeight += childrenSize.totalHeight;
+    if (i !== node.children.length - 1) {
+      totalHeight += S2SV;
+    }
+    totalWidth = Math.max(totalWidth, node.width + childrenSize.totalWidth);
+  });
+
+  node.totalHeight = Math.max(node.height, totalHeight);
+  node.totalWidth = totalWidth;
+  return {
+    totalWidth: node.totalWidth,
+    totalHeight: node.totalHeight,
+  };
+}
+
+// 设置子子主题位置
+function fivethWalk(node: HierarchyResult) {
+  let startY = node.y + node.height / 2 - node.totalHeight / 2;
+  const startX = node.x + node.width + S2SH;
+  node.children?.forEach((child) => {
+    child.x = startX;
+    child.y = startY + child.totalHeight / 2 - child.height / 2;
+    startY += child.totalHeight + S2SV;
+    if (child.children?.length) {
+      fivethWalk(child);
+    }
+  });
+}
+
+export const getRightFishboneHierarchy = <K>(topic: TopicItem): K => {
+  const root = firstWalk(topic);
+  secondWalk(root);
+
+  console.log(root);
+  return root as K;
+};

+ 133 - 0
apps/designer/src/utils/hierarchy/time.ts

@@ -0,0 +1,133 @@
+import { TopicItem, MindMapProjectInfo, HierarchyResult } from "@/types"
+import { TopicType } from "@/enum"
+
+// branch subtopic
+const B2SH = 20;
+const S2SH = 20;
+const B2SV = 20;
+const S2SV = 20;
+
+function firstWalk(node: TopicItem): HierarchyResult {
+  const root: HierarchyResult = {
+    x: 0,
+    y: 0,
+    totalHeight: 0,
+    totalWidth: 0,
+    width: node.width,
+    height: node.height,
+    id: node.id,
+    data: node,
+    children: [],
+  };
+
+  root.children = node.collapsed ? [] : node.children?.map((child) => firstWalk(child)) || [];
+
+  return root;
+}
+
+// 设置宽高
+function secondWalk(
+  node: HierarchyResult,
+  pageSetting: MindMapProjectInfo["pageSetting"]
+) {
+  let totalHeight = node.data.type === TopicType.branch ? node.height : 0;
+  let totalWidth = node.width;
+  if (node.children?.length) {
+    node.children.forEach((child: HierarchyResult, index) => {
+      const childSize = secondWalk(child, pageSetting);
+      
+      if(node.data.type === TopicType.branch) {
+        // 分支主题高度 = 分支主题高度 + 子主题或自身高的数据 + 间隔
+        totalHeight += childSize.totalHeight + B2SV;
+      } else {
+        // 子主题高度 = 子子主题高度之和
+        totalHeight += childSize.totalHeight + (index !== node.children.length - 1 ? S2SV : 0);
+      }
+      
+      if(node.data.type === TopicType.branch) {
+        // 分支主题宽度 = 分支主题的一半 + 子主题到分支主题中心的距离 + 子主题的宽度
+        totalWidth = Math.max(totalWidth, (node.width / 2) + B2SH + childSize.totalWidth);
+      } else {
+        totalWidth = Math.max(totalWidth, node.width + childSize.totalWidth + S2SH);
+      }
+    });
+  }
+  node.totalHeight = Math.max(node.height, totalHeight);
+  node.totalWidth =  totalWidth;
+
+  return node;
+}
+
+// 分支主题位置
+function thirdWalk(root: HierarchyResult) {
+  let startX = root.width + 20;
+  const centerY = root.height / 2;
+  root.children.forEach((child) => {
+    child.x = startX;
+    child.y = root.y + centerY - child.height / 2;
+    startX += child.totalWidth + 30;
+  });
+}
+
+// 中间处理
+function fourthWalk(branch: HierarchyResult, index: number, type: 'up' | 'down' | 'updown') {
+  let startX = branch.x + branch.width / 2 + B2SH;
+  let startY = branch.y;
+
+ for(let i = 0; i < branch.children.length; i++) {
+  if(type === 'down' || (type === 'updown' && index % 2 === 1)) {
+    startY += 20;
+    fivethWalk(branch.children, startX, startY, 'down');
+  } else {
+    startY -= 10;
+    fivethWalk(branch.children, startX, startY, 'up');
+  }
+ }
+}
+
+// 设置一级子主题位置
+function fivethWalk(nodes: HierarchyResult[], x: number, y: number, type: 'up' | 'down') {
+  let startY = y;
+  for(let i = 0; i < nodes.length; i++) {
+    // 根据朝向 设置渲染方向
+    const node = nodes[type === 'down' ? i : nodes.length - i - 1];
+    node.x = x;
+    node.y = startY + (type === 'up' ? -(node.totalHeight/2) - node.height/2 : node.totalHeight / 2 + node.height/2);
+    startY += type === 'up' ? -node.totalHeight - S2SV : node.totalHeight + S2SV;
+    if(node.children?.length) {
+      sixthWalk(node);
+    }
+  }
+}
+
+// 其他子级位置
+function sixthWalk(node: HierarchyResult) {
+  let startY = node.y + node.height / 2 - node.totalHeight / 2;
+  const x = node.x + node.width + S2SH;
+  node.children.forEach((child) => {
+    child.x = x;
+    child.y = startY;
+    startY += child.totalHeight + S2SV;
+    if(child.children?.length) {
+      sixthWalk(child);
+    }
+  });
+}
+export const getTimeHierarchy = <T>(
+  topic: TopicItem,
+  pageSetting: MindMapProjectInfo["pageSetting"],
+  type: 'up' | 'down' | 'updown' = 'updown',
+): T => {
+  // 处理结构
+  const root = firstWalk(topic);
+  // 处理宽高
+  secondWalk(root, pageSetting);
+  // 处理分支主题位置
+  thirdWalk(root);
+  // 处理子主题位置
+  root.children.forEach((child, index) => {
+    fourthWalk(child, index, type);
+  });
+  console.log(root)
+  return root as T;
+}

+ 87 - 47
apps/designer/src/utils/hierarchy/tree.ts

@@ -1,7 +1,13 @@
-import { TopicType } from "@/enum";
-import { MindMapProjectInfo, TopicItem, HierarchyResult } from "@/types";
+import { TopicItem, MindMapProjectInfo, HierarchyResult } from "@/types"
+import { TopicType } from "@/enum"
+import { e } from "unocss";
+
+// branch subtopic
+const B2SH = 20;
+const S2SH = 20;
+const B2SV = 20;
+const S2SV = 20;
 
-const DISTANCE = 20; // 子节点相对于父节点的偏差
 function firstWalk(node: TopicItem): HierarchyResult {
   const root: HierarchyResult = {
     x: 0,
@@ -15,7 +21,7 @@ function firstWalk(node: TopicItem): HierarchyResult {
     children: [],
   };
 
-  root.children = node.children?.map((child) => firstWalk(child)) || [];
+  root.children = node.collapsed ? [] : node.children?.map((child) => firstWalk(child)) || [];
 
   return root;
 }
@@ -26,61 +32,95 @@ function secondWalk(
   pageSetting: MindMapProjectInfo["pageSetting"]
 ) {
   let totalHeight = node.height;
-  let totalWidth = node.data.type === TopicType.main ? 0 : node.width;
+  let totalWidth = node.width;
   if (node.children?.length) {
-    node.children.forEach((c: HierarchyResult, index) => {
-      const childSize = secondWalk(c, pageSetting);
-      // const offsetY = node.data.type === TopicType.main ? pageSetting.branchY :  pageSetting.subTopicY;
-      // 总的高度 = 自身高度 + 子节点的高度 + 节点之间的距离
-      totalHeight += childSize.height + pageSetting.branchY;
+    node.children.forEach((child: HierarchyResult, index) => {
+      const childSize = secondWalk(child, pageSetting);
       
-      if (node.data.type === TopicType.main) {
-        const offsetX = index < node.children.length - 1 ? pageSetting.branchX : 0;
-        totalWidth += childSize.width + offsetX;
+      if(node.data.type === TopicType.branch) {
+        // 分支主题高度 = 分支主题高度 + 子主题或自身高的数据 + 间隔
+        totalHeight += childSize.totalHeight + B2SV;
       } else {
-        // 总的宽度 = 子节点的宽度与自身宽度取最大值
-        totalWidth = Math.max(DISTANCE + childSize.width, totalWidth);
+        // 子主题高度 = 子子主题高度之和
+        totalHeight += childSize.totalHeight + S2SV;
       }
+
+      // 分支主题宽度 = 分支主题的一半 + 子主题到分支主题中心的距离 + 子主题的宽度
+      totalWidth = Math.max(totalWidth, (node.width / 2) + B2SH + childSize.totalWidth);
     });
   }
   node.totalHeight = totalHeight;
-  node.totalWidth = totalWidth;
-
-  return {
-    height: totalHeight,
-    width: Math.max(totalWidth, node.width),
-  };
-}
+  node.totalWidth =  totalWidth;
 
-function thirdWalk(node: HierarchyResult, pageSetting: MindMapProjectInfo['pageSetting']) {
-  let startX = node.width / 2 - node.totalWidth / 2;
-  node.children.forEach((child) => {
-    child.y = node.y + node.height + pageSetting.branchY;
-    child.x = startX;
-    startX += child.totalWidth + pageSetting.branchX;
-    fourthWalk(child, pageSetting);
-  });
+  return node;
 }
 
-function fourthWalk(node: HierarchyResult, pageSetting: MindMapProjectInfo['pageSetting']) {
-  let startY = node.y + node.height + pageSetting.branchY;
-  node.children.forEach((child) => {
-    child.y = startY;
-    child.x = node.x + DISTANCE;
-    startY += child.totalHeight + pageSetting.branchY;
-    if(child.children.length) {
-      fourthWalk(child, pageSetting);
-    }
-  });
+// 分支主题位置
+function thirdWalk(root: HierarchyResult, type: 'left' | 'right' | 'leftright') {
+  let startX = root.x + root.width / 2;
+  let startY = root.y + root.height + B2SV;
+  if(type === 'leftright') {
+    let leftStartY = startY + root.children[0]?.height || 0 + S2SV;
+    let rightStartY = startY;
+    root.children.forEach((child, index) => {
+      child.x = index % 2 === 1 ? startX - child.width - B2SH : startX + B2SH;
+      const previous = root.children[index - 1];
+      if(index % 2 === 0) {
+        if(previous && rightStartY <= previous.y + previous.height) {
+          rightStartY = previous.y + previous.height + S2SV;
+        }
+        child.y = rightStartY;
+        rightStartY += child.totalHeight + S2SV;
+      } else {
+        if(previous && leftStartY <= previous.y + previous.height) {
+          leftStartY = previous.y + previous.height + S2SV;
+        }
+        child.y = leftStartY;
+        leftStartY += child.totalHeight + S2SV;
+      }
+      if(child.children?.length) {
+        thirdWalk(child, index % 2 === 1 ? 'left' : 'right');
+      }
+    });
+  } else {
+    root.children.forEach((child) => {
+      child.x = type === 'left' ? startX - child.width - B2SH : startX + B2SH;
+      child.y = startY;
+      startY += child.totalHeight + S2SV;
+      if(child.children?.length) {
+        thirdWalk(child, type);
+      }
+    });
+  }
 }
 
-export const getTreeStructure = <K>(
+export const getTreeHierarchy = <T>(
   topic: TopicItem,
-  pageSetting: MindMapProjectInfo["pageSetting"]
-): K => {
+  pageSetting: MindMapProjectInfo["pageSetting"],
+  type: 'left' | 'right' | 'leftright' = 'leftright',
+): T => {
+  // 处理结构
   const root = firstWalk(topic);
+  // 处理宽高
   secondWalk(root, pageSetting);
-  thirdWalk(root, pageSetting);
-  console.log(root);
-  return root as K;
-};
+  // 根据左右分别处理
+  const leftRoot = {
+    ...root,
+    children: root.children.filter((child, index) => index % 2 === 1),
+  }
+  const rightRoot = {
+    ...root,
+    children: root.children.filter((child, index) => index % 2 === 0),
+  }
+  if(type === 'leftright') {
+    root.children = [...(leftRoot.children || []), ...(rightRoot.children || [])];
+    thirdWalk(root, 'leftright');
+  } else if(type === 'left') {
+    thirdWalk(root, 'left');
+  } else if(type === 'right') {
+    thirdWalk(root, 'right');
+  }
+
+  console.log(root)
+  return root as T;
+}

+ 86 - 0
apps/designer/src/utils/hierarchy/treeDiagram.ts

@@ -0,0 +1,86 @@
+import { TopicType } from "@/enum";
+import { MindMapProjectInfo, TopicItem, HierarchyResult } from "@/types";
+
+const DISTANCE = 20; // 子节点相对于父节点的偏差
+function firstWalk(node: TopicItem): HierarchyResult {
+  const root: HierarchyResult = {
+    x: 0,
+    y: 0,
+    totalHeight: 0,
+    totalWidth: 0,
+    width: node.width,
+    height: node.height,
+    id: node.id,
+    data: node,
+    children: [],
+  };
+
+  root.children = node.collapsed ? [] : node.children?.map((child) => firstWalk(child)) || [];
+
+  return root;
+}
+
+// 设置宽高
+function secondWalk(
+  node: HierarchyResult,
+  pageSetting: MindMapProjectInfo["pageSetting"]
+) {
+  let totalHeight = node.height;
+  let totalWidth = node.data.type === TopicType.main ? 0 : node.width;
+  if (node.children?.length) {
+    node.children.forEach((c: HierarchyResult, index) => {
+      const childSize = secondWalk(c, pageSetting);
+      // const offsetY = node.data.type === TopicType.main ? pageSetting.branchY :  pageSetting.subTopicY;
+      // 总的高度 = 自身高度 + 子节点的高度 + 节点之间的距离
+      totalHeight += childSize.height + pageSetting.branchY;
+      
+      if (node.data.type === TopicType.main) {
+        const offsetX = index < node.children.length - 1 ? pageSetting.branchX : 0;
+        totalWidth += childSize.width + offsetX;
+      } else {
+        // 总的宽度 = 子节点的宽度与自身宽度取最大值
+        totalWidth = Math.max(DISTANCE + childSize.width, totalWidth);
+      }
+    });
+  }
+  node.totalHeight = totalHeight;
+  node.totalWidth = totalWidth;
+
+  return {
+    height: totalHeight,
+    width: Math.max(totalWidth, node.width),
+  };
+}
+
+function thirdWalk(node: HierarchyResult, pageSetting: MindMapProjectInfo['pageSetting']) {
+  let startX = node.width / 2 - node.totalWidth / 2;
+  node.children.forEach((child) => {
+    child.y = node.y + node.height + pageSetting.branchY;
+    child.x = startX;
+    startX += child.totalWidth + pageSetting.branchX;
+    fourthWalk(child, pageSetting);
+  });
+}
+
+function fourthWalk(node: HierarchyResult, pageSetting: MindMapProjectInfo['pageSetting']) {
+  let startY = node.y + node.height + pageSetting.branchY;
+  node.children.forEach((child) => {
+    child.y = startY;
+    child.x = node.x + DISTANCE;
+    startY += child.totalHeight + pageSetting.branchY;
+    if(child.children.length) {
+      fourthWalk(child, pageSetting);
+    }
+  });
+}
+
+export const getTreeDiagramHierarchy = <K>(
+  topic: TopicItem,
+  pageSetting: MindMapProjectInfo["pageSetting"]
+): K => {
+  const root = firstWalk(topic);
+  secondWalk(root, pageSetting);
+  thirdWalk(root, pageSetting);
+  console.log(root);
+  return root as K;
+};