Jelajahi Sumber

fix: 制作地图钢笔

bill 1 tahun lalu
induk
melakukan
56eea75b29

+ 134 - 17
src/request/index.ts

@@ -193,27 +193,114 @@ export const relicsPolyginsFetch = () => {
     setTimeout(() => {
       resolve({
         lines: [
-          { id: "1", pointIds: ["2666", "2667"] },
-          { id: "2", pointIds: ["2667", "2669"] },
-          { id: "3", pointIds: ["2669", "2674"] },
-          { id: "4", pointIds: ["2674", "2675"] },
-          { id: "5", pointIds: ["2675", "2676"] },
-          { id: "6", pointIds: ["2676", "2673"] },
-          { id: "7", pointIds: ["2673", "2672"] },
-          { id: "8", pointIds: ["2672", "2670"] },
+          {
+            id: "1",
+            pointIds: ["2666", "2667"],
+          },
+          {
+            id: "2",
+            pointIds: ["2667", "2669"],
+          },
+          {
+            id: "3",
+            pointIds: ["2669", "2674"],
+          },
+          {
+            id: "4",
+            pointIds: ["2674", "2675"],
+          },
+          {
+            id: "5",
+            pointIds: ["2675", "2676"],
+          },
+          {
+            id: "6",
+            pointIds: ["2676", "2673"],
+          },
+          {
+            id: "7",
+            pointIds: ["2673", "2672"],
+          },
+          {
+            id: "8",
+            pointIds: ["2672", "2670"],
+          },
+          {
+            id: "9",
+            pointIds: ["2677", "2678"],
+          },
+          {
+            id: "10",
+            pointIds: ["2678", "2679"],
+          },
+          {
+            id: "11",
+            pointIds: ["2679", "2680"],
+          },
+          {
+            id: "12",
+            pointIds: ["2680", "2681"],
+          },
         ],
         polygons: [
-          { id: "1", lineIds: ["1", "2", "3", "4", "5", "6", "7", "8"] },
+          {
+            id: "1",
+            lineIds: ["1", "2", "3", "4", "5", "6", "7", "8"],
+          },
+          {
+            id: "2",
+            lineIds: ["9", "10", "11", "12"],
+          },
         ],
         points: [
-          { rtk: false, x: 115.949835199646, y: 30.0971239995873, id: "2666" },
-          { rtk: false, x: 115.949706558269, y: 30.0975243383135, id: "2667" },
-          { rtk: false, x: 115.950002555619, y: 30.0977552558535, id: "2668" },
-          { rtk: false, x: 115.949968744193, y: 30.097862045865, id: "2669" },
-          { rtk: true, x: 115.950063977564, y: 30.0978879318173, id: "2670" },
-          { rtk: true, x: 115.949964417593, y: 30.0978650571868, id: "2671" },
-          { rtk: true, x: 115.950300839723, y: 30.0976756336231, id: "2672" },
-          { rtk: true, x: 115.950437426448, y: 30.097269657442, id: "2673" },
+          {
+            rtk: false,
+            x: 115.949835199646,
+            y: 30.0971239995873,
+            id: "2666",
+          },
+          {
+            rtk: false,
+            x: 115.949706558269,
+            y: 30.0975243383135,
+            id: "2667",
+          },
+          {
+            rtk: false,
+            x: 115.950002555619,
+            y: 30.0977552558535,
+            id: "2668",
+          },
+          {
+            rtk: false,
+            x: 115.949968744193,
+            y: 30.097862045865,
+            id: "2669",
+          },
+          {
+            rtk: true,
+            x: 115.950063977564,
+            y: 30.0978879318173,
+            id: "2670",
+          },
+          {
+            rtk: true,
+            x: 115.949964417593,
+            y: 30.0978650571868,
+            id: "2671",
+          },
+          {
+            rtk: true,
+            x: 115.950300839723,
+            y: 30.0976756336231,
+            id: "2672",
+          },
+          {
+            rtk: true,
+            x: 115.950437426448,
+            y: 30.097269657442,
+            id: "2673",
+          },
           {
             rtk: false,
             x: 115.95025353664198,
@@ -232,6 +319,36 @@ export const relicsPolyginsFetch = () => {
             y: 30.097000962869746,
             id: "2676",
           },
+          {
+            rtk: false,
+            x: 115.95100324233627,
+            y: 30.09844929254667,
+            id: "2677",
+          },
+          {
+            rtk: false,
+            x: 115.95233898242569,
+            y: 30.098492207890907,
+            id: "2678",
+          },
+          {
+            rtk: false,
+            x: 115.95227997382736,
+            y: 30.097059908276954,
+            id: "2679",
+          },
+          {
+            rtk: false,
+            x: 115.95076720794296,
+            y: 30.0971403745474,
+            id: "2680",
+          },
+          {
+            rtk: false,
+            x: 115.95056336005783,
+            y: 30.098095240956702,
+            id: "2681",
+          },
         ],
       });
     }, 500);

+ 2 - 3
src/view/map/board/index.ts

@@ -21,11 +21,11 @@ export const createBoard = (container: HTMLDivElement, mapManage: Manage) => {
 
   board.bound.enableMove((newBound) => {
     mapManage.setBound(newBound);
-    return false;
+    return true;
   });
   board.bound.enableWheel((newBound) => {
     mapManage.setBound(newBound);
-    return false;
+    return true;
   });
 
   return {
@@ -40,7 +40,6 @@ export const createBoard = (container: HTMLDivElement, mapManage: Manage) => {
       map.removeEventListener("moveend", syncBound);
     },
     polygon() {
-      console.log(board.tree);
       return board.tree.children[0] as Polygons;
     },
     getPixelFromCoordinate(point: number[]) {

+ 0 - 52
src/view/map/board/line.ts

@@ -1,52 +0,0 @@
-import { PolygonsLineAttrib } from "@/request/type";
-import {
-  WholeLineLine,
-  WholeLineLineProps,
-  getWholeLinePolygonLinesRaw,
-} from "drawing-board";
-import { Line } from "konva/lib/shapes/Line";
-import { Polygons } from "./polygons";
-
-const actShapeFactory = (attrib: PolygonsLineAttrib, tree: any) => {
-  const polygons = tree.parent as Polygons;
-  const isActive = () => {
-    if (polygons.currentId.value) {
-      const lines = getWholeLinePolygonLinesRaw(
-        polygons.attrib,
-        polygons.currentId.value
-      );
-      if (lines.some(({ id }) => id === attrib.id)) {
-        return true;
-      }
-    }
-    return false;
-  };
-  const line = new Line({
-    strokeScaleEnabled: false,
-    strokeWidth: 5,
-    hitStrokeWidth: 20,
-  });
-  const common = () => {
-    line.stroke(isActive() ? "rgba(64, 158, 255, 1)" : "rgba(230, 162, 60, 1)");
-  };
-  return {
-    shape: line,
-    setData(data: number[]) {
-      line.points(data);
-      common();
-    },
-    common,
-  };
-};
-
-export class PYLine extends WholeLineLine<PolygonsLineAttrib> {
-  constructor(props: WholeLineLineProps<PolygonsLineAttrib>) {
-    super(props);
-    this.actShapeFactory = actShapeFactory;
-  }
-
-  mounted() {
-    super.mounted();
-    this.enableMouseAct(this.actShape);
-  }
-}

+ 0 - 176
src/view/map/board/point.ts

@@ -1,176 +0,0 @@
-import { PolygonsPointAttrib } from "@/request/type";
-import {
-  openEntityDrag,
-  getRealAbsoluteSize,
-  WholeLinePoint,
-  getWholeLinePolygonPoints,
-  WholeLinePointProps,
-} from "drawing-board";
-import { Path } from "konva/lib/shapes/Path";
-import { Group } from "konva/lib/Group";
-import { Circle } from "konva/lib/shapes/Circle";
-import { Label, Tag } from "konva/lib/shapes/Label";
-import { Text } from "konva/lib/shapes/Text";
-import { Polygons } from "./polygons";
-
-const actShapeFactory = (attrib: PolygonsPointAttrib, tree: any) => {
-  const polygons = tree.parent as Polygons;
-  const size = { width: 40, height: 40 };
-  const out = new Path({
-    data: `M22 44C32.6667 33.891 38 25.891 38 20C38 11.1634 30.8366 4 22 4C13.1634 4 6 11.1634 6 20C6 25.891 11.3333 33.891 22 44Z`,
-    strokeScaleEnabled: true,
-    stroke: "#ffffff",
-    strokeWidth: 1,
-  });
-  const inner = new Path({
-    fill: "#fff",
-    data: `M22 30C27.5228 30 32 25.5228 32 20C32 14.4772 27.5228 10 22 10C16.4772 10 12 14.4772 12 20C12 25.5228 16.4772 30 22 30Z`,
-  });
-  const rect = new Circle({
-    radius: Math.min(size.width, size.height) / 2,
-    fill: "rgba(0, 0, 0, 0)",
-    offset: { x: -size.width / 2, y: -size.height / 2 },
-  });
-  const index = new Text({
-    name: "text",
-    text: `1`,
-    fontFamily: "Calibri",
-    fontSize: 12,
-    padding: 5,
-    offsetY: -8,
-    fill: "#000",
-  });
-
-  const label = new Label({
-    visible: false,
-    opacity: 0.75,
-    name: "label",
-    offsetX: -size.width / 2,
-    offsetY: -6,
-  });
-
-  label.add(
-    new Tag({
-      name: "tag",
-      fill: "rgba(255, 255, 255, 0.8)",
-      pointerDirection: "down",
-      pointerWidth: 5,
-      pointerHeight: 5,
-      lineJoin: "round",
-      shadowColor: "black",
-      shadowBlur: 10,
-      shadowOffsetX: 10,
-      shadowOffsetY: 10,
-      shadowOpacity: 0.5,
-    }),
-    new Text({
-      name: "text",
-      text: `P${attrib.id}`,
-      fontFamily: "Calibri",
-      fontSize: 10,
-      padding: 5,
-      fill: "#000",
-    })
-  );
-
-  const offsetGroup = new Group();
-  offsetGroup.add(out, inner, rect, label, index);
-  offsetGroup.x(-size.width / 2);
-  offsetGroup.y(-size.height);
-
-  const group = new Group();
-  group.add(offsetGroup);
-
-  const activeNdx = () => {
-    if (polygons.currentId.value) {
-      const points = getWholeLinePolygonPoints(
-        polygons.attrib,
-        polygons.currentId.value
-      ).map(({ id }) => id);
-      const ndx = points.indexOf(attrib.id);
-      return ndx;
-    }
-    return -1;
-  };
-
-  const setStyle = () => {
-    let [width, height] = getRealAbsoluteSize(group, [1, 1]);
-    group.scale({ x: width, y: height });
-    const ndx = activeNdx();
-    if (~ndx) {
-      index.text((ndx + 1).toString()).visible(true);
-      index.offsetX(-rect.width() / 2 + index.width() / 2.5);
-    } else {
-      index.visible(false);
-    }
-  };
-
-  const commonStyle = () => {
-    out.fill(attrib.rtk ? "rgba(230, 162, 60, 1)" : "#409EFF");
-    label.visible(false);
-  };
-
-  const result = {
-    shape: group,
-    common: () => {
-      commonStyle();
-      polygons.bus.emit("hoverPoint", null);
-    },
-    hover: () => {
-      if (~activeNdx()) {
-        label.visible(true);
-      }
-      polygons.bus.emit("hoverPoint", tree.attrib);
-    },
-    setData(data: number[]) {
-      setStyle();
-      group.x(data[0]);
-      group.y(data[1]);
-    },
-    active() {
-      if (~activeNdx()) {
-        out.fill("#E6A23C");
-      } else {
-        commonStyle();
-      }
-      polygons.bus.emit("activePoint", tree.attrib);
-    },
-    draging() {
-      out.fill("#E6A23C");
-    },
-  };
-
-  if (attrib.rtk) {
-    return result;
-  } else {
-    return {
-      ...result,
-      draging() {
-        out.fill("#E6A23C");
-      },
-    };
-  }
-};
-
-export class PYPoint extends WholeLinePoint<PolygonsPointAttrib, Group> {
-  constructor(props: WholeLinePointProps<PolygonsPointAttrib>) {
-    super(props);
-    this.actShapeFactory = actShapeFactory;
-  }
-
-  mounted() {
-    super.mounted();
-    if (!this.attrib.rtk) {
-      openEntityDrag(this, {
-        readyHandler: (attrib) => {
-          return [attrib.x, attrib.y];
-        },
-        moveHandler: (pointAttrib, move) => {
-          pointAttrib.x = move[0];
-          pointAttrib.y = move[1];
-        },
-      });
-    }
-    this.enableMouseAct(this.actShape);
-  }
-}

+ 0 - 43
src/view/map/board/polygon.ts

@@ -1,43 +0,0 @@
-import {
-  WholeLinePolygon,
-  WholeLinePolygonAttrib,
-  WholeLinePolygonProps,
-} from "drawing-board";
-import { Group } from "konva/lib/Group";
-import { Line } from "konva/lib/shapes/Line";
-import { Polygons } from "./polygons";
-
-const actShapeFactory = (attrib: WholeLinePolygonAttrib, tree: any) => {
-  const polygons = tree.parent as Polygons;
-  const isActive = () => polygons.currentId.value === attrib.id;
-
-  const line = new Line({
-    closed: true,
-  });
-  const common = () => {
-    line.fill(
-      isActive() ? "rgba(64, 158, 255, 0.3)" : "rgba(230, 162, 60, 0.30)"
-    );
-  };
-
-  return {
-    shape: line,
-    setData(data: number[]) {
-      line.points(data);
-      common();
-    },
-    common,
-  };
-};
-
-export class PYPolygon extends WholeLinePolygon<WholeLinePolygonAttrib, Group> {
-  constructor(props: WholeLinePolygonProps<WholeLinePolygonAttrib>) {
-    super(props);
-    this.actShapeFactory = actShapeFactory;
-  }
-
-  mounted() {
-    super.mounted();
-    this.enableMouseAct(this.actShape);
-  }
-}

+ 212 - 50
src/view/map/board/polygons.ts

@@ -1,17 +1,23 @@
 import { PolygonsAttrib, PolygonsPointAttrib } from "@/request/type";
 import {
-  WholeLine,
   incEntitysFactoryGenerate,
   Attrib,
-  penWholeLinePoygonsEdit,
+  wholeLineStyle,
+  getRealAbsoluteSize,
+  PenEditWholeLine,
+  WholeLinePoint,
+  getWholeLinePolygonPoints,
+  shapeParentsEq,
+  openEntityDrag,
+  WholeLineInc,
 } from "drawing-board";
-import mitt from "mitt";
 import { Group } from "konva/lib/Group";
-import { Line } from "konva/lib/shapes/Line";
-import { PYLine } from "./line";
-import { PYPoint } from "./point";
+import { Path } from "konva/lib/shapes/Path";
+import { Circle } from "konva/lib/shapes/Circle";
+import { Label, Tag } from "konva/lib/shapes/Label";
+import { Text } from "konva/lib/shapes/Text";
 import { ref } from "vue";
-import { PYPolygon } from "./polygon";
+import mitt from "mitt";
 
 // 加点
 const getPolygonPoint = (position: number[]) => {
@@ -23,66 +29,222 @@ const getPolygonPoint = (position: number[]) => {
   return pointAttrib;
 };
 
-export class Polygons extends WholeLine<PolygonsAttrib & Attrib, Group, Line> {
-  currentId = ref<string>();
-  bus = mitt<{
-    hoverPoint: PolygonsPointAttrib | null;
-    activePoint: PolygonsPointAttrib;
-  }>();
+const pointActShapeFactory = (attrib: PolygonsPointAttrib, tree: any) => {
+  const polygons = tree.parent as Polygons;
+  const size = { width: 43, height: 44 };
+  const out = new Path({
+    data: `M22 44C32.6667 33.891 38 25.891 38 20C38 11.1634 30.8366 4 22 4C13.1634 4 6 11.1634 6 20C6 25.891 11.3333 33.891 22 44Z`,
+    strokeScaleEnabled: true,
+    stroke: "#ffffff",
+    strokeWidth: 1,
+  });
+  const inner = new Path({
+    fill: "#fff",
+    data: `M22 30C27.5228 30 32 25.5228 32 20C32 14.4772 27.5228 10 22 10C16.4772 10 12 14.4772 12 20C12 25.5228 16.4772 30 22 30Z`,
+  });
+  const rect = new Circle({
+    name: "anchor-move",
+    radius: Math.min(size.width, size.height) / 2,
+    fill: "rgba(0, 0, 0, 0)",
+    offset: { x: -size.width / 2, y: -size.height / 2 },
+  });
+  const wlp = wholeLineStyle.pointShapeFactory();
 
-  initIncFactory() {
-    this.incLinesFactory = incEntitysFactoryGenerate(PYLine, this, (line) =>
-      line.setConfig(this.attrib)
-    );
-    this.incPointsFactory = incEntitysFactoryGenerate(PYPoint, this);
+  wlp.shape.name("anchor-point");
 
-    this.incPolygonFactory = incEntitysFactoryGenerate(
-      PYPolygon,
-      this,
-      (py) => {
-        py.setConfig(this.attrib);
-      }
-    );
-  }
+  const index = new Text({
+    name: "text",
+    text: `1`,
+    fontFamily: "Calibri",
+    fontSize: 12,
+    padding: 5,
+    offsetY: -8,
+    fill: "#000",
+  });
+
+  const label = new Label({
+    visible: false,
+    opacity: 0.75,
+    name: "label",
+    offsetX: -size.width / 2,
+    offsetY: -6,
+  });
+
+  label.add(
+    new Tag({
+      name: "tag",
+      fill: "rgba(255, 255, 255, 0.8)",
+      pointerDirection: "down",
+      pointerWidth: 5,
+      pointerHeight: 5,
+      lineJoin: "round",
+      shadowColor: "black",
+      shadowBlur: 10,
+      shadowOffsetX: 10,
+      shadowOffsetY: 10,
+      shadowOpacity: 0.5,
+    }),
+    new Text({
+      name: "text",
+      text: `P${attrib.id}`,
+      fontFamily: "Calibri",
+      fontSize: 10,
+      padding: 5,
+      fill: "#000",
+    })
+  );
 
-  removePolygon(polygonId: string) {
-    const ndx = this.attrib.polygons.findIndex(({ id }) => id === polygonId);
+  const offsetGroup = new Group();
+  offsetGroup.add(out, inner, rect, label, index);
+  offsetGroup.x(-size.width / 2);
+  offsetGroup.y(-size.height);
+
+  const group = new Group();
+  group.add(offsetGroup, wlp.shape);
+
+  const activeNdx = () => {
+    if (polygons.editPolygonId.value) {
+      const points = getWholeLinePolygonPoints(
+        polygons.attrib,
+        polygons.editPolygonId.value
+      ).map(({ id }) => id);
+      const ndx = points.indexOf(attrib.id);
+      return ndx;
+    }
+    return -1;
+  };
+
+  const setStyle = () => {
+    let [width, height] = getRealAbsoluteSize(group, [1, 1]);
+    group.scale({ x: width, y: height });
+    const ndx = activeNdx();
     if (~ndx) {
-      this.attrib.polygons.splice(ndx, 1);
+      index.text((ndx + 1).toString()).visible(true);
+      index.offsetX(-rect.width() / 2 + index.width() / 2);
+    } else {
+      index.visible(false);
     }
+  };
+
+  const commonStyle = () => {
+    out.fill(attrib.rtk ? "rgba(230, 162, 60, 1)" : "#409EFF");
+    label.visible(false);
+    wlp.common();
+  };
+
+  const result = {
+    shape: group,
+    common: commonStyle,
+    hover: () => {
+      label.visible(true);
+    },
+    setData(data: number[]) {
+      setStyle();
+      group.x(data[0]);
+      group.y(data[1]);
+
+      label.visible(polygons.activePointId.value === attrib.id);
+    },
+    draging() {
+      if (polygons.editPolygonId.value && !attrib.rtk) {
+        out.fill("#e0403c");
+      }
+    },
+    active() {
+      polygons.activePointId.value = attrib.id;
+      polygons.bus.emit("clickPoint", attrib);
+    },
+  };
+
+  return result;
+};
+
+export class Polygons extends PenEditWholeLine<PolygonsAttrib & Attrib> {
+  bus = mitt<{ clickPoint: PolygonsPointAttrib }>();
+  activePointId = ref<string>();
+
+  dragAttach(inc: WholeLineInc<PolygonsAttrib & Attrib>) {
+    inc.pointEntityInc.adds.forEach((point) => {
+      openEntityDrag(point, {
+        readyHandler: (attrib) => {
+          return [attrib.x, attrib.y];
+        },
+        moveHandler: (pointAttrib, move) => {
+          if (this.editPolygonId.value && !pointAttrib.rtk) {
+            pointAttrib.x = move[0];
+            pointAttrib.y = move[1];
+          }
+        },
+      });
+      point.enableMouseAct(point.actShape);
+    });
+
+    inc.lineEntityInc.adds.forEach((line) => {
+      line.enableMouseAct(line.actShape);
+    });
+
+    inc.polygonEntityInc.adds.forEach((py) => {
+      py.enableMouseAct(py.actShape);
+    });
   }
 
-  private _leaveEditMode: (() => void) | void = void 0;
-  enterEditMode(polygonId?: string) {
-    const { continuous, end } = penWholeLinePoygonsEdit({
-      tree: this.container,
-      config: this.attrib,
-      polygonId,
+  initIncFactory() {
+    super.initIncFactory();
+    this.incPointsFactory = incEntitysFactoryGenerate(
+      WholeLinePoint<any>,
+      this,
+      (point) => {
+        point.actShapeFactory = pointActShapeFactory;
+      }
+    );
+  }
+
+  editPolygon(polygonId: string) {
+    super.enterEditMode({
+      polygonId: polygonId,
       pointAttribFactory: getPolygonPoint,
-      quotePoint: false,
-      canDelPoint: (p) => !p.rtk,
-      changePolygon: (pid) => {
-        this.currentId.value = pid;
+      canOper: (tree, operShape) => {
+        return (
+          !tree.name.includes(WholeLinePoint.namespace) ||
+          operShape.name() === "anchor-point"
+        );
       },
+      canDelPoint: (p) => !p.rtk,
+      quotePoint: false,
     });
-
-    this.container.stage.on("click.editPolygonsMode", continuous);
-    this._leaveEditMode = () => {
-      this.container.stage.off("click.editPolygonsMode", continuous);
-      end();
-    };
-  }
-  leaveEditMode() {
-    this._leaveEditMode && this._leaveEditMode();
+    return super.leaveEditMode;
   }
 
   mounted(): void {
     super.mounted();
-    this.enterEditMode("1");
+    let clearCursor: (() => void) | null = null;
+    this.container.stage.on("mousemove.anchor-move", (evt) => {
+      const isPoint = evt.target.name() === "anchor-move";
+      if (!isPoint) {
+        clearCursor && clearCursor();
+        clearCursor = null;
+        return;
+      }
+
+      if (this.editPolygonId.value) {
+        clearCursor = this.container.setCursor("move");
+      } else {
+        clearCursor = this.container.setCursor("pointer");
+      }
+    });
+
+    this.container.stage.on("click.anchor-move", (evt) => {
+      const point = shapeParentsEq(evt.target, (shape) =>
+        shape.id().startsWith(WholeLinePoint.namespace)
+      );
+      if (!point) {
+        this.activePointId.value = undefined;
+      }
+    });
   }
 
   destory(): void {
     super.destory();
-    this.leaveEditMode();
+    this.container.stage.off("mousemove.anchor-move click.anchor-move");
   }
 }

+ 205 - 224
src/view/map/board/storeData.json

@@ -1,230 +1,211 @@
 {
-  "rooms": [
+  "lines": [
+    {
+      "id": "1",
+      "pointIds": [
+        "2666",
+        "2667"
+      ]
+    },
     {
       "id": "2",
-      "points": [
-        {
-          "id": "1",
-          "x": 881,
-          "y": 492
-        },
-        {
-          "id": "2",
-          "x": 136,
-          "y": 237
-        },
-        {
-          "id": "8",
-          "x": 371,
-          "y": 699
-        },
-        {
-          "id": "18",
-          "x": 480,
-          "y": 511
-        },
-        {
-          "id": "19",
-          "x": 83,
-          "y": 647
-        },
-        {
-          "id": "3",
-          "x": 178,
-          "y": 803
-        },
-        {
-          "x": 675.8655056700748,
-          "y": 268.3909090194661,
-          "id": "20"
-        },
-        {
-          "x": 163.5307763543757,
-          "y": 608.0099861089675,
-          "id": "21"
-        },
-        {
-          "x": 579.4292704980454,
-          "y": 468.065825925416,
-          "id": "22"
-        },
-        {
-          "x": 579.1720366885647,
-          "y": 467.6217710319711,
-          "id": "23"
-        },
-        {
-          "x": 445.2292582546364,
-          "y": 325.58255334590945,
-          "id": "24"
-        }
-      ],
-      "polygons": [
-        {
-          "id": "1",
-          "lineIds": [
-            "1",
-            "2",
-            "3",
-            "4",
-            "5",
-            "6",
-            "7",
-            "8",
-            "9",
-            "10",
-            "11",
-            "12",
-            "13"
-          ]
-        },
-        {
-          "id": "2",
-          "lineIds": [
-            "14",
-            "15",
-            "16",
-            "17",
-            "18",
-            "19"
-          ]
-        }
-      ],
-      "lines": [
-        {
-          "id": "1",
-          "pointIds": [
-            "1",
-            "22"
-          ]
-        },
-        {
-          "id": "2",
-          "pointIds": [
-            "22",
-            "23"
-          ]
-        },
-        {
-          "id": "3",
-          "pointIds": [
-            "23",
-            "24"
-          ]
-        },
-        {
-          "id": "4",
-          "pointIds": [
-            "24",
-            "2"
-          ]
-        },
-        {
-          "id": "5",
-          "pointIds": [
-            "2",
-            "20"
-          ]
-        },
-        {
-          "id": "6",
-          "pointIds": [
-            "20",
-            "22"
-          ]
-        },
-        {
-          "id": "7",
-          "pointIds": [
-            "22",
-            "8"
-          ]
-        },
-        {
-          "id": "8",
-          "pointIds": [
-            "8",
-            "18"
-          ]
-        },
-        {
-          "id": "9",
-          "pointIds": [
-            "18",
-            "23"
-          ]
-        },
-        {
-          "id": "10",
-          "pointIds": [
-            "23",
-            "20"
-          ]
-        },
-        {
-          "id": "11",
-          "pointIds": [
-            "20",
-            "24"
-          ]
-        },
-        {
-          "id": "12",
-          "pointIds": [
-            "24",
-            "21"
-          ]
-        },
-        {
-          "id": "13",
-          "pointIds": [
-            "21",
-            "19"
-          ]
-        },
-        {
-          "id": "14",
-          "pointIds": [
-            "3",
-            "21"
-          ]
-        },
-        {
-          "id": "15",
-          "pointIds": [
-            "21",
-            "2"
-          ]
-        },
-        {
-          "id": "16",
-          "pointIds": [
-            "2",
-            "24"
-          ]
-        },
-        {
-          "id": "17",
-          "pointIds": [
-            "24",
-            "23"
-          ]
-        },
-        {
-          "id": "18",
-          "pointIds": [
-            "23",
-            "22"
-          ]
-        },
-        {
-          "id": "19",
-          "pointIds": [
-            "22",
-            "1"
-          ]
-        }
+      "pointIds": [
+        "2667",
+        "2669"
+      ]
+    },
+    {
+      "id": "3",
+      "pointIds": [
+        "2669",
+        "2674"
+      ]
+    },
+    {
+      "id": "4",
+      "pointIds": [
+        "2674",
+        "2675"
+      ]
+    },
+    {
+      "id": "5",
+      "pointIds": [
+        "2675",
+        "2676"
+      ]
+    },
+    {
+      "id": "6",
+      "pointIds": [
+        "2676",
+        "2673"
+      ]
+    },
+    {
+      "id": "7",
+      "pointIds": [
+        "2673",
+        "2672"
       ]
+    },
+    {
+      "id": "8",
+      "pointIds": [
+        "2672",
+        "2670"
+      ]
+    },
+    {
+      "id": "9",
+      "pointIds": [
+        "2677",
+        "2678"
+      ]
+    },
+    {
+      "id": "10",
+      "pointIds": [
+        "2678",
+        "2679"
+      ]
+    },
+    {
+      "id": "11",
+      "pointIds": [
+        "2679",
+        "2680"
+      ]
+    },
+    {
+      "id": "12",
+      "pointIds": [
+        "2680",
+        "2681"
+      ]
+    }
+  ],
+  "polygons": [
+    {
+      "id": "1",
+      "lineIds": [
+        "1",
+        "2",
+        "3",
+        "4",
+        "5",
+        "6",
+        "7",
+        "8"
+      ]
+    },
+    {
+      "id": "2",
+      "lineIds": [
+        "9",
+        "10",
+        "11",
+        "12"
+      ]
+    }
+  ],
+  "points": [
+    {
+      "rtk": false,
+      "x": 115.949835199646,
+      "y": 30.0971239995873,
+      "id": "2666"
+    },
+    {
+      "rtk": false,
+      "x": 115.949706558269,
+      "y": 30.0975243383135,
+      "id": "2667"
+    },
+    {
+      "rtk": false,
+      "x": 115.950002555619,
+      "y": 30.0977552558535,
+      "id": "2668"
+    },
+    {
+      "rtk": false,
+      "x": 115.949968744193,
+      "y": 30.097862045865,
+      "id": "2669"
+    },
+    {
+      "rtk": true,
+      "x": 115.950063977564,
+      "y": 30.0978879318173,
+      "id": "2670"
+    },
+    {
+      "rtk": true,
+      "x": 115.949964417593,
+      "y": 30.0978650571868,
+      "id": "2671"
+    },
+    {
+      "rtk": true,
+      "x": 115.950300839723,
+      "y": 30.0976756336231,
+      "id": "2672"
+    },
+    {
+      "rtk": true,
+      "x": 115.950437426448,
+      "y": 30.097269657442,
+      "id": "2673"
+    },
+    {
+      "rtk": false,
+      "x": 115.95025353664198,
+      "y": 30.097554052476422,
+      "id": "2674"
+    },
+    {
+      "rtk": false,
+      "x": 115.95021286828855,
+      "y": 30.09733715459145,
+      "id": "2675"
+    },
+    {
+      "rtk": false,
+      "x": 115.95031589478391,
+      "y": 30.097000962869746,
+      "id": "2676"
+    },
+    {
+      "rtk": false,
+      "x": 115.95100324233627,
+      "y": 30.09844929254667,
+      "id": "2677"
+    },
+    {
+      "rtk": false,
+      "x": 115.95233898242569,
+      "y": 30.098492207890907,
+      "id": "2678"
+    },
+    {
+      "rtk": false,
+      "x": 115.95227997382736,
+      "y": 30.097059908276954,
+      "id": "2679"
+    },
+    {
+      "rtk": false,
+      "x": 115.95076720794296,
+      "y": 30.0971403745474,
+      "id": "2680"
+    },
+    {
+      "rtk": false,
+      "x": 115.95056336005783,
+      "y": 30.098095240956702,
+      "id": "2681"
     }
-  ]
+  ],
+  "id": "141"
 }

+ 2 - 2
src/view/map/map-borad.vue

@@ -84,6 +84,7 @@ const flyScene = (scene: Scene) => {
 
 const flyScenePoint = (point: ScenePoint) => {
   flyPos(point.pos);
+  board.polygon().activePointId.value = point.id.toString();
 };
 
 watch(
@@ -103,8 +104,7 @@ watch(
         relicsPolyginsFetch().then((data) => {
           board.setData(data, router.currentRoute.value.params.relicsId as string);
           window.board = board;
-          console.log(board.polygon());
-          board.polygon().bus.on("activePoint", (bpoint) => {
+          board.polygon().bus.on("clickPoint", (bpoint) => {
             const point =
               bpoint.id &&
               scenePoints.value.find((point) => point.id.toString() === bpoint.id);

+ 1 - 1
src/view/map/openlayer/manage.ts

@@ -73,7 +73,7 @@ export class Manage {
     this.map.getView().fit(extent, {
       size: this.map.getSize(),
       padding: [0, 0, 0, 0], // 根据需要调整边距
-      maxZoom: 22, // 防止过度放大
+      maxZoom: 19.5, // 防止过度放大
     });
   }
 

+ 171 - 0
src/view/step-tree/StepTree.vue

@@ -0,0 +1,171 @@
+<template>
+  <svg
+    :viewBox="svgAttrib.viewBox.join(' ')"
+    v-if="svgAttrib"
+    xmlns="http://www.w3.org/2000/svg"
+    :style="{ width: svgAttrib.viewBox[2] + 'px', height: svgAttrib.viewBox[3] + 'px' }"
+  >
+    <Step
+      v-for="step in steps"
+      :key="step.id"
+      v-bind="getStepAttrib(step)"
+      @click="emit('stepClick', step)"
+      @click-host="(data) => emit('stepHostClick', data)"
+    />
+  </svg>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue'
+import { flatSteps, attachBoundAttrib, NStep, getTextBound } from './helper'
+import Step from './step.vue'
+
+const props = withDefaults(
+  defineProps<{
+    margin?: number[]
+    padding?: number[]
+    fontSize?: number
+    data: any
+    fontFamily?: string
+    hostFontSize?: number
+    hostMargin?: number[]
+    hostPadding?: number[]
+    customStepStyle?: (step: any) => {}
+    lineGap: number
+  }>(),
+  {
+    margin: () => [10, 10],
+    padding: () => [10, 10],
+    hostMargin: () => [2, 2],
+    hostPadding: () => [2, 2],
+    hostFontSize: 10,
+    lineGap: 5,
+    fontSize: 14,
+    fontFamily: 'sans-serif',
+  }
+)
+
+const emit = defineEmits<{
+  (e: 'stepClick', data: any): void
+  (e: 'stepHostClick', host: any): void
+}>()
+
+const getStepSize = (step: any) => {
+  const size = getTextBound(
+    step.displayName,
+    props.padding,
+    props.margin,
+    `${props.fontSize}px normal ${props.fontFamily}`
+  )
+
+  if (step.hosts?.length) {
+    const hostsGroup = []
+    const numGroup = 2
+    for (let i = 0; i < step.hosts.length; i += numGroup) {
+      hostsGroup.push(step.hosts.slice(i, i + numGroup))
+    }
+    let top = 0
+    const hostSizeGroup = hostsGroup.map((hosts) => {
+      let left = 0
+      const hostSize = hosts.reduce(
+        (t: any, host: any) => {
+          const size = getTextBound(
+            host.host,
+            props.hostPadding,
+            props.hostMargin,
+            `${props.hostFontSize}px normal ${props.fontFamily}`
+          )
+          t.width += size.width
+          t.height = Math.max(t.height, size.height)
+          host.bound = {
+            ...size,
+            left,
+            top,
+          }
+          left += size.width
+          return t
+        },
+        { width: 0, height: 0 }
+      )
+      top += hostSize.height
+      return hostSize
+    })
+
+    const hostSize = hostSizeGroup.reduceRight(
+      (t, hostSize) => {
+        t.width = Math.max(hostSize.width, t.width)
+        t.height += hostSize.height
+        return t
+      },
+      { width: 0, height: 0 }
+    )
+    step.hostSize = hostSize
+    size.width = Math.max(size.width, hostSize.width + (props.padding[1] + props.margin[1]) * 2)
+    size.height += hostSize.height
+  }
+
+  return size
+}
+
+const steps = computed(() => flatSteps(props.data))
+const bound = computed(() => attachBoundAttrib(steps.value, getStepSize))
+console.log('3323', bound.value)
+const svgAttrib = computed(() => {
+  if (!bound.value) return null
+  const { left, right, top, bottom } = bound.value
+  return {
+    viewBox: [left, top, right - left, bottom - top],
+  }
+})
+
+const lineGap = computed(() => Math.min(props.lineGap, props.margin[0]))
+
+const getStepLines = (step: NStep) => {
+  if (!step.parentIds.length) return []
+  const start = [step.bound.left + step.bound.width / 2, step.bound.top + props.margin[0]]
+  const points = []
+  for (let parentId of step.parentIds) {
+    const parent = steps.value.find((step) => step.id === parentId)!
+    const end = [
+      parent.bound.left + parent.bound.width / 2,
+      parent.bound.top + parent.bound.height - props.margin[0],
+    ]
+    const startLevelHeight = bound.value.levelHeights[step.level]
+    // const parentLevelHeight = bound.value.levelHeights[parent.level];
+    const offset = lineGap.value + (startLevelHeight - step.bound.height) / 2
+
+    points.push([...start, start[0], start[1] - offset, end[0], start[1] - offset, ...end])
+  }
+  return points
+}
+
+const defaultStyle = {
+  lineColor: '#000',
+  lineWidth: 1,
+  textColor: '#000',
+  rectBorderColor: '#000',
+  rectBgColor: '#ffff',
+  rectRadius: 2,
+  rectBorderWidth: 1,
+}
+
+const getStepAttrib = (step: NStep) => {
+  let style = defaultStyle
+  if (props.customStepStyle) {
+    style = { ...defaultStyle, ...props.customStepStyle(step.raw) }
+  }
+  return {
+    style,
+    step,
+    margin: props.margin,
+    padding: props.padding,
+    fontSize: props.fontSize,
+    hostMargin: props.hostMargin,
+    hostPadding: props.hostPadding,
+    hostFontSize: props.hostFontSize,
+    fontFamily: props.fontFamily,
+    lines: getStepLines(step),
+  }
+}
+</script>
+

+ 755 - 173
src/view/step-tree/example/data.ts

@@ -1,236 +1,818 @@
 export default [
   {
-    yamlversion: "v1.0",
-    subStepsParallel: "False", // false 串行,True并行,表示子步骤step1、step2串行执行
+    end_time: "",
+    start_time: "20240601085011",
+    status: "waiting",
     steps: [
       {
+        displayName: "Stop AP7 And MA7",
         name: "step1",
-        displayName: "Stop All Services",
-        subStepsParallel: "True",
         status: "success",
         steps: [
           {
-            name: "step1_1",
-            displayName: "Stop app1 Services",
-            type: "execution",
-            serviceType: "app1",
-            status: "stop",
-            serviceTypeParallel: "True",
-
-            steps: [
-              {
-                name: "step1_1_1",
-                displayName: "Stop app1 Services",
-                type: "execution",
-                serviceType: "app1",
-                status: "waiting",
-                serviceTypeParallel: "False",
-                steps: [
-                  {
-                    name: "step1_1_1_1",
-                    displayName: "Stop app1 Services",
-                    type: "execution",
-                    serviceType: "app1",
-                    status: "waiting",
-                    serviceTypeParallel: "False",
-                    hosts: [
-                      { host: "qladpaxasdasdasd1", status: "success" },
-                      { host: "qladpax1", status: "success" },
-                      { host: "qladpax1", status: "success" },
-                      { host: "qladpax1", status: "success" },
-                      { host: "qladpax1", status: "success" },
-                      { host: "qladpax1", status: "success" },
-                      { host: "qladpax2", status: "lose" },
-                      { host: "qladpax3", status: "wating" },
-                    ],
-                  },
-                  {
-                    name: "step1_1_1_2",
-                    displayName: "Stop app1 Services",
-                    type: "execution",
-                    serviceType: "app1",
-                    status: "waiting",
-                    serviceTypeParallel: "False",
-                    hosts: [
-                      { host: "qladpax1", status: "success" },
-                      { host: "qladpax2", status: "lose" },
-                      { host: "qladpax3", status: "wating" },
-                    ],
-                  },
-                ],
-              },
-              {
-                name: "step1_1_2",
-                displayName: "Stop app1 Services",
-                type: "execution",
-                serviceType: "app1",
-                status: "waiting",
-                serviceTypeParallel: "True",
-                hosts: [
-                  { host: "qladpax1", status: "success" },
-                  { host: "qladpax2", status: "lose" },
-                  { host: "qladpax3", status: "wating" },
-                ],
-              },
-              {
-                name: "step1_1_3",
-                displayName: "Stop app1 Services",
-                type: "execution",
-                serviceType: "app1",
-                status: "waiting",
-                serviceTypeParallel: "True",
-              },
-              {
-                name: "step1_1_4",
-                displayName: "Stop app1 Services",
-                type: "execution",
-                serviceType: "app1",
-                status: "waiting",
-                serviceTypeParallel: "True",
+            action: "stop",
+            displayName: "Stop AP7",
+            hosts: [
+              {
+                host: "qlaasap7",
+                status: "success",
               },
             ],
+            name: "step1_1",
+            serviceType: "AASAP_part7",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
           },
           {
+            action: "stop",
+            displayName: "Stop MA7",
+            hosts: [
+              {
+                host: "qlaasma7",
+                status: "success",
+              },
+            ],
             name: "step1_2",
-            displayName: "Stop app2_part1 Services",
-            type: "execution",
-            serviceType: "app2_part1",
-            status: "waiting",
-            serviceTypeParallel: "True",
-            steps: [
-              {
-                name: "step1_2_1",
-                displayName: "Stop app1 Services",
-                type: "execution",
-                serviceType: "app1",
-                status: "waiting",
-                serviceTypeParallel: "True",
-              },
-              {
-                name: "step1_2_2",
-                displayName: "Stop app1 Services",
-                type: "execution",
-                serviceType: "app1",
-                status: "waiting",
-                serviceTypeParallel: "True",
-                steps: [
-                  {
-                    name: "step1_2aaaa",
-                    displayName: "Stop app2_part1 Services",
-                    type: "execution",
-                    serviceType: "app2_part1",
-                    status: "waiting",
-                    serviceTypeParallel: "True",
-                    steps: [
-                      {
-                        name: "step1_2_1a",
-                        displayName: "Stop app1 Services",
-                        type: "execution",
-                        serviceType: "app1",
-                        status: "waiting",
-                        serviceTypeParallel: "True",
-                      },
-                      {
-                        name: "step1ccc",
-                        displayName: "Stop app1 Services",
-                        type: "execution",
-                        serviceType: "app1",
-                        status: "waiting",
-                        serviceTypeParallel: "True",
-                      },
-                    ],
-                  },
-                ],
-              },
-            ],
-          },
-        ],
-      },
-      {
-        // name: "step2step2step2step2step2step2step2step2step2stestep2step2step2step2step2step2step2step2step2stestep2step2step2step2step2step2step2step2step2stestep2step2step2step2step2step2step2step2step2ste",
+            serviceType: "AASMA_part7",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+        ],
+        subStepsParallel: true,
+      },
+      {
+        displayName: "step2",
         name: "step2",
-        displayName: "Stop All Services",
-        subStepsParallel: "True",
-        status: "waiting",
+        status: "success",
         steps: [
           {
+            action: "humanWaiting",
+            displayName:
+              "AP7 & MA7 application have closed, next step deploy application",
             name: "step2_1",
-            displayName: "Waiting",
+            status: "success",
+            type: "execution",
+          },
+        ],
+      },
+      {
+        displayName: "Deploy AP7 MA7 PW7 SC7 HT7 And MH7",
+        name: "step3",
+        status: "success",
+        steps: [
+          {
+            action: "deploy",
+            displayName: "Deploy AP7",
+            hosts: [
+              {
+                host: "qlaasap7",
+                status: "success",
+              },
+            ],
+            name: "step3_1",
+            serviceType: "AASAP_part7",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy MA7",
+            hosts: [
+              {
+                host: "qlaasma7",
+                status: "success",
+              },
+            ],
+            name: "step3_2",
+            serviceType: "AASMA_part7",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy PW7",
+            hosts: [
+              {
+                host: "qlaaspw7",
+                status: "success",
+              },
+            ],
+            name: "step3_3",
+            serviceType: "AASPW_part7",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy SC7",
+            hosts: [
+              {
+                host: "qlaassc7",
+                status: "success",
+              },
+            ],
+            name: "step3_4",
+            serviceType: "AASSC_part7",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy HT7",
+            hosts: [
+              {
+                host: "qlaasht7",
+                status: "success",
+              },
+            ],
+            name: "step3_5",
+            serviceType: "AASHT_part7",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy MH7",
+            hosts: [
+              {
+                host: "qlaasmh7",
+                status: "success",
+              },
+            ],
+            name: "step3_6",
+            serviceType: "AASMH_part7",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+        ],
+        subStepsParallel: true,
+      },
+      {
+        displayName: "step4",
+        name: "step4",
+        status: "success",
+        steps: [
+          {
+            action: "humanWaiting",
+            displayName: "Waiting for verify internal environment",
+            name: "step4_1",
+            status: "success",
+            type: "execution",
+          },
+        ],
+      },
+      {
+        displayName: "stop BM1 BM2 application",
+        name: "step5",
+        status: "success",
+        steps: [
+          {
+            action: "stop",
+            displayName: "Stop BM1",
+            hosts: [
+              {
+                host: "qlaasbm1",
+                status: "success",
+              },
+            ],
+            name: "step5_1",
+            serviceType: "AASBM_part1",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+          {
+            action: "stop",
+            displayName: "Stop BM2",
+            hosts: [
+              {
+                host: "qlaasbm2",
+                status: "success",
+              },
+            ],
+            name: "step5_2",
+            serviceType: "AASBM_part2",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+        ],
+        subStepsParallel: true,
+      },
+      {
+        displayName: "Deploy BM1 And BM2 application",
+        name: "step6",
+        status: "success",
+        steps: [
+          {
+            action: "deploy",
+            displayName: "Deploy BM1",
+            hosts: [
+              {
+                host: "qlaasbm1",
+                status: "success",
+              },
+            ],
+            name: "step6_1",
+            serviceType: "AASBM_part1",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy BM2",
+            hosts: [
+              {
+                host: "qlaasbm2",
+                status: "success",
+              },
+            ],
+            name: "step6_2",
+            serviceType: "AASBM_part2",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+        ],
+        subStepsParallel: true,
+      },
+      {
+        displayName: "step7",
+        name: "step7",
+        status: "success",
+        steps: [
+          {
+            action: "humanWaiting",
+            displayName: "Waiting for verify BM1 And BM2 application",
+            name: "step7_1",
+            status: "success",
+            type: "execution",
+          },
+        ],
+      },
+      {
+        displayName: "Stop HT1 And MH1 F5",
+        name: "step8",
+        status: "success",
+        steps: [
+          {
+            action: "stop",
+            displayName: "Stop HT1",
+            hosts: [
+              {
+                host: "qlaasht3",
+                status: "success",
+              },
+            ],
+            name: "step8_1",
+            serviceType: "AASHT_part1",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+          {
+            action: "stop",
+            displayName: "Stop MH1",
+            hosts: [
+              {
+                host: "qlaasmh1",
+                status: "success",
+              },
+            ],
+            name: "step8_2",
+            serviceType: "AASMH_part1",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+        ],
+        subStepsParallel: true,
+      },
+      {
+        displayName: "step9",
+        name: "step9",
+        status: "running",
+        steps: [
+          {
+            action: "humanWaiting",
+            displayName:
+              "Finish HT1 & MH1 closeF5, next step stop AP1 MA1 PW1 And SC1 application",
+            name: "step9_1",
+            status: "running",
+            type: "execution",
+          },
+        ],
+      },
+      {
+        displayName: "stop AP1 MA1 PW1 And SC1 application",
+        name: "step10",
+        status: "waiting",
+        steps: [
+          {
+            action: "stop",
+            displayName: "Stop AP1",
+            hosts: [
+              {
+                host: "qlaasap1",
+                status: "error",
+              },
+            ],
+            name: "step10_1",
+            serviceType: "AASAP_part1",
+            serviceTypeParallel: true,
+            status: "error",
+            type: "execution",
+          },
+          {
+            action: "stop",
+            displayName: "Stop MA1",
+            hosts: [
+              {
+                host: "qlaasma1",
+                status: "success",
+              },
+            ],
+            name: "step10_2",
+            serviceType: "AASMA_part1",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+          {
+            action: "stop",
+            displayName: "Stop PW1",
+            hosts: [
+              {
+                host: "qlaaspw1",
+                status: "success",
+              },
+            ],
+            name: "step10_3",
+            serviceType: "AASPW_part1",
+            serviceTypeParallel: true,
+            status: "success",
+            type: "execution",
+          },
+          {
+            action: "stop",
+            displayName: "Stop SC1",
+            hosts: [
+              {
+                host: "qlaassc1",
+                status: "success",
+              },
+            ],
+            name: "step10_4",
+            serviceType: "AASSC_part1",
+            serviceTypeParallel: true,
+            status: "success",
             type: "execution",
+          },
+        ],
+        subStepsParallel: true,
+      },
+      {
+        displayName: "step11",
+        name: "step11",
+        status: "waiting",
+        steps: [
+          {
+            action: "humanWaiting",
+            displayName:
+              "all application have closed, next step Deploy all application",
+            name: "step11_1",
             status: "waiting",
+            type: "execution",
           },
+        ],
+      },
+      {
+        displayName: "Deploy AP1 MA1 PW1 And SC1",
+        name: "step12",
+        status: "waiting",
+        steps: [
           {
-            name: "step2_2",
-            displayName: "Waiting",
+            action: "deploy",
+            displayName: "Deploy AP1",
+            hosts: [
+              {
+                host: "qlaasap1",
+                status: "waiting",
+              },
+            ],
+            name: "step12_1",
+            serviceType: "AASAP_part1",
+            serviceTypeParallel: true,
+            status: "waiting",
             type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy MA1",
+            hosts: [
+              {
+                host: "qlaasma1",
+                status: "waiting",
+              },
+            ],
+            name: "step12_2",
+            serviceType: "AASMA_part1",
+            serviceTypeParallel: true,
+            status: "waiting",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy PW1",
+            hosts: [
+              {
+                host: "qlaaspw1",
+                status: "waiting",
+              },
+            ],
+            name: "step12_3",
+            serviceType: "AASPW_part1",
+            serviceTypeParallel: true,
             status: "waiting",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy SC1",
+            hosts: [
+              {
+                host: "qlaassc1",
+                status: "waiting",
+              },
+            ],
+            name: "step12_4",
+            serviceType: "AASSC_part1",
+            serviceTypeParallel: true,
+            status: "waiting",
+            type: "execution",
           },
         ],
+        subStepsParallel: true,
       },
       {
-        name: "step3",
-        displayName: "deploy All Services",
-        subStepsParallel: "True",
+        displayName: "Deploy HT1 And MH1",
+        name: "step13",
         status: "waiting",
         steps: [
           {
-            name: "step3_1ssstep3",
-            displayName: "deploy app1 Services",
+            action: "deploy",
+            displayName: "Deploy HT1",
+            hosts: [
+              {
+                host: "qlaasht3",
+                status: "waiting",
+              },
+            ],
+            name: "step13_1",
+            serviceType: "AASHT_part1",
+            serviceTypeParallel: true,
+            status: "waiting",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy MH1",
+            hosts: [
+              {
+                host: "qlaasmh1",
+                status: "waiting",
+              },
+            ],
+            name: "step13_2",
+            serviceType: "AASMH_part1",
+            serviceTypeParallel: true,
+            status: "waiting",
             type: "execution",
-            serviceType: "app1",
+          },
+        ],
+        subStepsParallel: true,
+      },
+      {
+        displayName: "step14",
+        name: "step14",
+        status: "waiting",
+        steps: [
+          {
+            action: "humanWaiting",
+            displayName: "Waiting for external services",
+            name: "step14_1",
             status: "waiting",
-            serviceTypeParallel: "True",
+            type: "execution",
           },
+        ],
+      },
+      {
+        displayName: "StartF5 HT1 And MH1",
+        name: "step15",
+        status: "waiting",
+        steps: [
           {
-            name: "step3_1stes",
-            displayName: "deploy app1 Services",
+            action: "start",
+            displayName: "StartF5 HT1",
+            hosts: [
+              {
+                host: "qlaasht3",
+                status: "waiting",
+              },
+            ],
+            name: "step15_1",
+            serviceType: "AASHT_part1",
+            serviceTypeParallel: true,
+            status: "waiting",
             type: "execution",
-            serviceType: "app1",
+          },
+          {
+            action: "start",
+            displayName: "StartF5 MH1",
+            hosts: [
+              {
+                host: "qlaasmh1",
+                status: "waiting",
+              },
+            ],
+            name: "step15_2",
+            serviceType: "AASMH_part1",
+            serviceTypeParallel: true,
             status: "waiting",
-            serviceTypeParallel: "True",
+            type: "execution",
           },
+        ],
+        subStepsParallel: true,
+      },
+      {
+        displayName:
+          "Stop HT2 And MH2 closeF5, stop ap2 ma2 pw2 and sc2 application",
+        name: "step16",
+        status: "waiting",
+        steps: [
           {
-            name: "step3_1s",
-            displayName: "deploy app1 Services",
+            action: "stop",
+            displayName: "Stop HT2",
+            hosts: [
+              {
+                host: "qlaasht4",
+                status: "waiting",
+              },
+            ],
+            name: "step16_1",
+            serviceType: "AASHT_part2",
+            serviceTypeParallel: true,
+            status: "waiting",
             type: "execution",
-            serviceType: "app1",
+          },
+          {
+            action: "stop",
+            displayName: "Stop MH2",
+            hosts: [
+              {
+                host: "qlaasmh2",
+                status: "waiting",
+              },
+            ],
+            name: "step16_2",
+            serviceType: "AASMH_part2",
+            serviceTypeParallel: true,
             status: "waiting",
-            serviceTypeParallel: "True",
+            type: "execution",
           },
           {
-            name: "step3_2",
-            displayName: "deploy app2_part1 Services",
+            action: "stop",
+            displayName: "Stop AP2",
+            hosts: [
+              {
+                host: "qlaasap2",
+                status: "waiting",
+              },
+            ],
+            name: "step16_3",
+            serviceType: "AASAP_part2",
+            serviceTypeParallel: true,
+            status: "waiting",
             type: "execution",
-            serviceType: "app2_part1",
+          },
+          {
+            action: "stop",
+            displayName: "Stop MA2",
+            hosts: [
+              {
+                host: "qlaasma2",
+                status: "waiting",
+              },
+            ],
+            name: "step16_4",
+            serviceType: "AASMA_part2",
+            serviceTypeParallel: true,
             status: "waiting",
-            serviceTypeParallel: "True",
+            type: "execution",
+          },
+          {
+            action: "stop",
+            displayName: "Stop PW2",
+            hosts: [
+              {
+                host: "qlaaspw2",
+                status: "waiting",
+              },
+            ],
+            name: "step16_5",
+            serviceType: "AASPW_part2",
+            serviceTypeParallel: true,
+            status: "waiting",
+            type: "execution",
+          },
+          {
+            action: "stop",
+            displayName: "Stop SC2",
+            hosts: [
+              {
+                host: "qlaassc2",
+                status: "waiting",
+              },
+            ],
+            name: "step16_6",
+            serviceType: "AASSC_part2",
+            serviceTypeParallel: true,
+            status: "waiting",
+            type: "execution",
           },
         ],
+        subStepsParallel: true,
       },
       {
-        name: "step4",
-        displayName: "start All Services",
-        subStepsParallel: "False",
+        displayName: "step17",
+        name: "step17",
         status: "waiting",
         steps: [
           {
-            name: "step4_1",
-            displayName: "start app1 Services",
+            action: "humanWaiting",
+            displayName:
+              "Finish HT2 & MH2 closeF5, stop ap2 ma2 pw2 and sc2 application,next step Deploy AP2 MA2 PW2 And SC2",
+            name: "step17_1",
+            status: "waiting",
             type: "execution",
-            serviceType: "app1",
+          },
+        ],
+      },
+      {
+        displayName: "Deploy AP2 MA2 PW2 And SC2",
+        name: "step18",
+        status: "waiting",
+        steps: [
+          {
+            action: "deploy",
+            displayName: "Deploy AP2",
+            hosts: [
+              {
+                host: "qlaasap2",
+                status: "waiting",
+              },
+            ],
+            name: "step18_1",
+            serviceType: "AASAP_part2",
+            serviceTypeParallel: true,
             status: "waiting",
-            serviceTypeParallel: "True",
+            type: "execution",
           },
           {
-            name: "step4_2",
-            displayName: "start app2_part1 Services",
+            action: "deploy",
+            displayName: "Deploy MA2",
+            hosts: [
+              {
+                host: "qlaasma2",
+                status: "waiting",
+              },
+            ],
+            name: "step18_2",
+            serviceType: "AASMA_part2",
+            serviceTypeParallel: true,
+            status: "waiting",
             type: "execution",
-            serviceType: "app2_part1",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy PW2",
+            hosts: [
+              {
+                host: "qlaaspw2",
+                status: "waiting",
+              },
+            ],
+            name: "step18_3",
+            serviceType: "AASPW_part2",
+            serviceTypeParallel: true,
             status: "waiting",
-            serviceTypeParallel: "True",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy SC2",
+            hosts: [
+              {
+                host: "qlaassc2",
+                status: "waiting",
+              },
+            ],
+            name: "step18_4",
+            serviceType: "AASSC_part2",
+            serviceTypeParallel: true,
+            status: "waiting",
+            type: "execution",
+          },
+        ],
+        subStepsParallel: true,
+      },
+      {
+        displayName: "Deploy HT2 And MH2",
+        name: "step19",
+        status: "waiting",
+        steps: [
+          {
+            action: "deploy",
+            displayName: "Deploy HT2",
+            hosts: [
+              {
+                host: "qlaasht4",
+                status: "waiting",
+              },
+            ],
+            name: "step19_1",
+            serviceType: "AASHT_part2",
+            serviceTypeParallel: true,
+            status: "waiting",
+            type: "execution",
+          },
+          {
+            action: "deploy",
+            displayName: "Deploy MH2",
+            hosts: [
+              {
+                host: "qlaasmh2",
+                status: "waiting",
+              },
+            ],
+            name: "step19_2",
+            serviceType: "AASMH_part2",
+            serviceTypeParallel: true,
+            status: "waiting",
+            type: "execution",
           },
         ],
+        subStepsParallel: true,
+      },
+      {
+        displayName: "StartF5 HT2 And MH2",
+        name: "step20",
+        status: "waiting",
+        steps: [
+          {
+            action: "start",
+            displayName: "StartF5 HT2",
+            hosts: [
+              {
+                host: "qlaasht4",
+                status: "waiting",
+              },
+            ],
+            name: "step20_1",
+            serviceType: "AASHT_part2",
+            serviceTypeParallel: true,
+            status: "waiting",
+            type: "execution",
+          },
+          {
+            action: "start",
+            displayName: "StartF5 MH2",
+            hosts: [
+              {
+                host: "qlaasmh2",
+                status: "waiting",
+              },
+            ],
+            name: "step20_2",
+            serviceType: "AASMH_part2",
+            serviceTypeParallel: true,
+            status: "waiting",
+            type: "execution",
+          },
+        ],
+        subStepsParallel: true,
       },
     ],
+    subStepsParallel: false,
+    yamlversion: "v1.0",
   },
 ];

+ 156 - 19
src/view/step-tree/example/example.vue

@@ -1,13 +1,21 @@
 <template>
-  <div class="test">
+  <div class="status-box flex">
+    <!-- <span class="defualt">运行中</span> -->
+    <span class="waiting">等待中</span>
+    <span class="running">运行中</span>
+    <span class="succ">成功</span>
+    <span class="bf-suc">部分成功</span>
+    <span class="error">失败</span>
+  </div>
+  <div class="tree-cont-wrap" ref="treeWrapRef">
     <StepTree
       :data="treeData"
       :margin="[15, 15]"
       :padding="[10, 10]"
       :font-size="16"
-      :hostFontSize="10"
-      :hostMargin="[3, 3]"
-      :hostPadding="[3, 3]"
+      :hostFontSize="16"
+      :hostMargin="[10, 5]"
+      :hostPadding="[8, 8]"
       font-family="微软雅黑"
       :custom-step-style="customStepStyle"
       @step-click="stepClickHandler"
@@ -19,47 +27,176 @@
 
 <script setup>
 import data from "./data";
-import StepTree from "../step-tree.vue";
+import StepTree from "../StepTree.vue";
 
-const treeData = [{ name: "开始" }, ...data[0].steps, { name: "结束" }];
+import { ref, nextTick, toRefs } from "vue";
+import { useRouter } from "vue-router";
+const treeWrapRef = ref();
+const $router = useRouter();
+const treeData = [
+  { displayName: "开始", type: "startEnd" },
+  ...data[0].steps,
+  { displayName: "结束", type: "startEnd" },
+];
 
+// 每个step的样式
 const customStepStyle = (step) => {
-  if (step.action === "stop") {
+  console.log("xxxxx", step);
+  // 等待中,开始状态
+  if (step.status === "waiting") {
+    return {
+      lineColor: "#89beb2",
+      lineWidth: 1,
+      textColor: "#333",
+      rectBorderColor: "#333",
+      rectBgColor: "#89beb2",
+      rectRadius: 2,
+      rectBorderWidth: 1,
+    };
+  }
+  // 失败
+  else if (step.status === "error") {
+    return {
+      lineColor: "#89beb2",
+      lineWidth: 1,
+      textColor: "#333",
+      rectBorderColor: "#333",
+      rectBgColor: "red",
+      rectRadius: 2,
+      rectBorderWidth: 1,
+    };
+  }
+  // 成功
+  else if (step.status === "success") {
+    return {
+      lineColor: "#89beb2",
+      lineWidth: 1,
+      textColor: "#333",
+      rectBorderColor: "#333",
+      rectBgColor: "#30d567",
+      rectRadius: 2,
+      rectBorderWidth: 1,
+    };
+  }
+  // 部分成功
+  else if (step.status === "partsuccess") {
+    return {
+      lineColor: "#89beb2",
+      lineWidth: 1,
+      textColor: "#333",
+      rectBorderColor: "#333",
+      rectBgColor: "#d4f8c3",
+      rectRadius: 2,
+      rectBorderWidth: 1,
+    };
+  } else if (step.status === "running") {
     return {
-      lineColor: "red",
-      lineWidth: 3,
-      textColor: "red",
-      rectBorderColor: "red",
-      rectBorderColor: "red",
-      rectBgColor: "#ccc",
+      lineColor: "#89beb2",
+      lineWidth: 1,
+      textColor: "#333",
+      rectBorderColor: "#333",
+      rectBgColor: "#ecf752",
       rectRadius: 2,
       rectBorderWidth: 1,
     };
-  } else {
+  }
+  {
     return {
-      lineColor: "#000",
+      lineColor: "#89beb2",
       lineWidth: 1,
-      textColor: "#000",
-      rectBorderColor: "#000",
+      textColor: "#333",
+      rectBorderColor: "coral",
       rectBgColor: "#ffff",
       rectRadius: 2,
       rectBorderWidth: 1,
     };
   }
 };
+
+// hosts
+const customHostStyle = (step) => {
+  if (step.hosts?.length) {
+    step.hosts.forEach((item, idx) => {
+      if (item.status === "success") {
+        return {
+          lineColor: "#40dbd9",
+          lineWidth: 1,
+          textColor: "#ccc",
+          rectBorderColor: "#40dbd9",
+          rectBgColor: "#ffff",
+          rectRadius: 2,
+          rectBorderWidth: 1,
+        };
+      }
+    });
+  }
+};
+
 const stepClickHandler = (step) => {
   console.log(step);
+  if (step.raw.type === "startEnd") return;
+  let url = $router.resolve({
+    path: "/stepLogs",
+    params: {
+      key: 1,
+    },
+  }).href;
+  window.open(url, "_blank");
 };
 const stepHostClickHandler = (host) => {
   console.log(host);
 };
+nextTick(() => {
+  treeWrapRef.value.scrollLeft =
+    (treeWrapRef.value.scrollWidth - treeWrapRef.value.clientWidth) / 2;
+});
 </script>
 
 <style scoped>
-.test {
+.tree-cont-wrap {
   width: 100%;
   height: 100%;
-
+  margin: 0 auto;
   overflow: auto;
+  text-align: center;
+  /* display: flex;
+  justify-content: center;
+  align-items: center; */
+}
+
+.flex {
+  display: flex;
+}
+.status-box {
+  width: 400px;
+}
+.status-box span {
+  display: inline-block;
+  width: 120px;
+  height: 30px;
+  line-height: 30px;
+  color: #333;
+  text-align: center;
+  margin: 0 5px;
+}
+/* 运行中 */
+.running {
+  background: #ecf752;
+}
+/* 错误 */
+.error {
+  background: #ff4238;
+}
+/* 成功 */
+.succ {
+  background: #30d567;
+}
+/* 等待中 */
+.waiting {
+  background: #89beb2;
+}
+/* 部分成功 */
+.bf-suc {
+  background: #c6f9ae;
 }
 </style>

+ 19 - 8
src/view/step-tree/helper.ts

@@ -1,7 +1,7 @@
 export type NStep = {
   id: number;
   parentIds: number[];
-  name: string;
+  displayName: string;
   bound: { width: number; height: number; left: number; top: number };
   prevId: number;
   raw: any;
@@ -51,7 +51,7 @@ const _flatSteps = (
 
     const nstep = {
       id,
-      name: step.name,
+      displayName: step.displayName,
       parentIds: tempParentIds,
       prevId: tempPrevId,
       raw: step,
@@ -66,7 +66,9 @@ const _flatSteps = (
           [id],
           tempLevel + 1,
           step.subStepsParallel === "True" ||
-            step.serviceTypeParallel === "True",
+            step.serviceTypeParallel === "True" ||
+            step.subStepsParallel ||
+            step.serviceTypeParallel,
           nsteps
         )
       );
@@ -173,12 +175,21 @@ export const attachBoundAttrib = (
           parent.bound.left + (parent.bound.width - width) / 2
         );
       }
-      if (
-        left === parent.bound.left &&
-        parent.bound.width + parent.bound.left > left + treeBound.width
-      ) {
-        left += (parent.bound.width - treeBound.width) / 2;
+      const levelCount = steps.filter(
+        (qstep) => qstep.level === step.level
+      ).length;
+      const parentWidth = steps
+        .filter((qstep) => qstep.level === step.level - 1)
+        .reduce((s, pstep) => s + pstep.bound.width, 0);
+      if (levelCount === 1) {
+        left += parent.bound.left + (parentWidth - treeBound.width) / 2;
       }
+      // else if (
+      //   left === parent.bound.left &&
+      //   parent.bound.width + parent.bound.left > left + treeBound.width
+      // ) {
+      //   left += (parentWidth - treeBound.width) / 2;
+      // }
     }
 
     return left;

+ 50 - 36
src/view/step-tree/step.vue

@@ -7,6 +7,7 @@
     :fill="style.rectBgColor"
     :stroke="style.rectBorderColor"
     :stroke-width="style.rectBorderWidth"
+    style="cursor: pointer"
   >
   </rect>
 
@@ -19,10 +20,21 @@
       :height="hostBounds[index].height"
       :rx="style.rectRadius"
       :ry="style.rectRadius"
-      :fill="style.rectBgColor"
-      :stroke="style.rectBorderColor"
+      :fill="
+        _.status === 'success'
+          ? '#30d567'
+          : _.status === 'error'
+          ? '#ff4238'
+          : _.status === 'running'
+          ? '#ecf752'
+          : _.status === 'waiting'
+          ? '#89beb2'
+          : 'd4f8c3'
+      "
+      :stroke="'#333'"
       :stroke-width="style.rectBorderWidth"
       @click="emit('clickHost', step.raw.hosts[index])"
+      style="cursor: pointer"
     >
     </rect>
 
@@ -30,11 +42,12 @@
       v-for="(hostTex, i) in hostTextAttribs"
       @click="emit('clickHost', step.raw.hosts[i])"
       :font-family="fontFamily"
-      :fill="style.textColor"
+      :fill="'#333'"
       dominant-baseline="middle"
       :font-size="hostFontSize"
       text-anchor="middle"
       v-bind="hostTex"
+      style="cursor: pointer"
     >
       {{ step.raw.hosts[i].host }}
     </text>
@@ -47,8 +60,9 @@
     dominant-baseline="middle"
     text-anchor="middle"
     v-bind="textAttrib"
+    style="cursor: pointer"
   >
-    {{ step.name }}
+    {{ step.displayName }}
   </text>
 
   <template v-if="lines.length">
@@ -63,66 +77,66 @@
 </template>
 
 <script setup lang="ts">
-import { computed } from "vue";
-import { NStep } from "./helper";
+import { computed } from 'vue'
+import { NStep } from './helper'
 
 const props = defineProps<{
-  step: NStep;
-  margin: number[];
-  padding: number[];
-  fontSize: number;
-  fontFamily: string;
-  lines: number[][];
-  hostMargin: number[];
-  hostPadding: number[];
-  hostFontSize: number;
+  step: NStep
+  margin: number[]
+  padding: number[]
+  fontSize: number
+  fontFamily: string
+  lines: number[][]
+  hostMargin: number[]
+  hostPadding: number[]
+  hostFontSize: number
   style: {
-    lineColor: string;
-    lineWidth: number;
-    textColor: string;
-    rectBorderColor: string;
-    rectBorderWidth: number;
-    rectBgColor: string;
-    rectRadius: number;
-  };
-}>();
+    lineColor: string
+    lineWidth: number
+    textColor: string
+    rectBorderColor: string
+    rectBorderWidth: number
+    rectBgColor: string
+    rectRadius: number
+  }
+}>()
 
-const emit = defineEmits<{ (e: "click"): void; (e: "clickHost", host: any): void }>();
+const emit = defineEmits<{ (e: 'click'): void; (e: 'clickHost', host: any): void }>()
 
 const rectBound = computed(() => ({
   x: props.step.bound.left + props.margin[1],
   y: props.step.bound.top + props.margin[0],
   width: props.step.bound.width - props.margin[1] * 2,
   height: props.step.bound.height - props.margin[0] * 2,
-}));
+}))
 const textAttrib = computed(() => ({
   x: rectBound.value.x + rectBound.value.width / 2,
   y: rectBound.value.y + props.padding[0] + props.fontSize / 2,
-}));
+}))
 
 const hostBounds = computed(() => {
   let left =
     rectBound.value.x +
     props.padding[1] +
-    (rectBound.value.width - props.padding[1] * 2 - props.step.raw.hostSize.width) / 2;
-  let top = rectBound.value.y + rectBound.value.height - props.step.raw.hostSize.height;
+    (rectBound.value.width - props.padding[1] * 2 - props.step.raw.hostSize.width) / 2
+  let top = rectBound.value.y + rectBound.value.height - props.step.raw.hostSize.height
 
-  const hosts = props.step.raw.hosts;
+  const hosts = props.step.raw.hosts
   return hosts.map((host: any) => {
-    const x = left + host.bound.left + props.hostMargin[1];
-    const y = top + props.hostMargin[0] + host.bound.top;
+    const x = left + host.bound.left + props.hostMargin[1]
+    const y = top + props.hostMargin[0] + host.bound.top
     return {
       x,
       y,
       width: host.bound.width - props.hostMargin[1] * 2,
       height: host.bound.height - props.hostMargin[0] * 2,
-    };
-  });
-});
+    }
+  })
+})
 const hostTextAttribs = computed(() =>
   hostBounds.value.map((hostBound: any) => ({
     x: hostBound.x + hostBound.width / 2,
     y: hostBound.y + props.hostPadding[0] + props.hostFontSize / 2,
   }))
-);
+)
 </script>

+ 691 - 0
src/view/step-tree/views/data.ts

@@ -0,0 +1,691 @@
+export default [
+  {
+    "end_time": "",
+    "start_time": "20240601085011",
+    "status": "waiting",
+    "steps": [{
+      "displayName": "Stop AP7 And MA7",
+      "name": "step1",
+      "status": "success",
+      "steps": [{
+        "action": "stop",
+        "displayName": "Stop AP7",
+        "hosts": [{
+          "host": "qlaasap7",
+          "status": "success"
+        }],
+        "name": "step1_1",
+        "serviceType": "AASAP_part7",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop MA7",
+        "hosts": [{
+          "host": "qlaasma7",
+          "status": "success"
+        }],
+        "name": "step1_2",
+        "serviceType": "AASMA_part7",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "step2",
+      "name": "step2",
+      "status": "success",
+      "steps": [{
+        "action": "humanWaiting",
+        "displayName": "AP7 & MA7 application have closed, next step deploy application",
+        "name": "step2_1",
+        "status": "success",
+        "type": "execution"
+      }]
+    },
+    {
+      "displayName": "Deploy AP7 MA7 PW7 SC7 HT7 And MH7",
+      "name": "step3",
+      "status": "success",
+      "steps": [{
+        "action": "deploy",
+        "displayName": "Deploy AP7",
+        "hosts": [{
+          "host": "qlaasap7",
+          "status": "success"
+        }],
+        "name": "step3_1",
+        "serviceType": "AASAP_part7",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy MA7",
+        "hosts": [{
+          "host": "qlaasma7",
+          "status": "success"
+        }],
+        "name": "step3_2",
+        "serviceType": "AASMA_part7",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy PW7",
+        "hosts": [{
+          "host": "qlaaspw7",
+          "status": "success"
+        }],
+        "name": "step3_3",
+        "serviceType": "AASPW_part7",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy SC7",
+        "hosts": [{
+          "host": "qlaassc7",
+          "status": "success"
+        }],
+        "name": "step3_4",
+        "serviceType": "AASSC_part7",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy HT7",
+        "hosts": [{
+          "host": "qlaasht7",
+          "status": "success"
+        }],
+        "name": "step3_5",
+        "serviceType": "AASHT_part7",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy MH7",
+        "hosts": [{
+          "host": "qlaasmh7",
+          "status": "success"
+        }],
+        "name": "step3_6",
+        "serviceType": "AASMH_part7",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "step4",
+      "name": "step4",
+      "status": "success",
+      "steps": [{
+        "action": "humanWaiting",
+        "displayName": "Waiting for verify internal environment",
+        "name": "step4_1",
+        "status": "success",
+        "type": "execution"
+      }]
+    },
+    {
+      "displayName": "stop BM1 BM2 application",
+      "name": "step5",
+      "status": "success",
+      "steps": [{
+        "action": "stop",
+        "displayName": "Stop BM1",
+        "hosts": [{
+          "host": "qlaasbm1",
+          "status": "success"
+        }],
+        "name": "step5_1",
+        "serviceType": "AASBM_part1",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop BM2",
+        "hosts": [{
+          "host": "qlaasbm2",
+          "status": "success"
+        }],
+        "name": "step5_2",
+        "serviceType": "AASBM_part2",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "Deploy BM1 And BM2 application",
+      "name": "step6",
+      "status": "success",
+      "steps": [{
+        "action": "deploy",
+        "displayName": "Deploy BM1",
+        "hosts": [{
+          "host": "qlaasbm1",
+          "status": "success"
+        }],
+        "name": "step6_1",
+        "serviceType": "AASBM_part1",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy BM2",
+        "hosts": [{
+          "host": "qlaasbm2",
+          "status": "success"
+        }],
+        "name": "step6_2",
+        "serviceType": "AASBM_part2",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "step7",
+      "name": "step7",
+      "status": "success",
+      "steps": [{
+        "action": "humanWaiting",
+        "displayName": "Waiting for verify BM1 And BM2 application",
+        "name": "step7_1",
+        "status": "success",
+        "type": "execution"
+      }]
+    },
+    {
+      "displayName": "Stop HT1 And MH1 F5",
+      "name": "step8",
+      "status": "success",
+      "steps": [{
+        "action": "stop",
+        "displayName": "Stop HT1",
+        "hosts": [{
+          "host": "qlaasht3",
+          "status": "success"
+        }],
+        "name": "step8_1",
+        "serviceType": "AASHT_part1",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop MH1",
+        "hosts": [{
+          "host": "qlaasmh1",
+          "status": "success"
+        }],
+        "name": "step8_2",
+        "serviceType": "AASMH_part1",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "step9",
+      "name": "step9",
+      "status": "running",
+      "steps": [{
+        "action": "humanWaiting",
+        "displayName": "Finish HT1 & MH1 closeF5, next step stop AP1 MA1 PW1 And SC1 application",
+        "name": "step9_1",
+        "status": "running",
+        "type": "execution"
+      }]
+    },
+    {
+      "displayName": "stop AP1 MA1 PW1 And SC1 application",
+      "name": "step10",
+      "status": "waiting",
+      "steps": [{
+        "action": "stop",
+        "displayName": "Stop AP1",
+        "hosts": [{
+          "host": "qlaasap1",
+          "status": "error"
+        }],
+        "name": "step10_1",
+        "serviceType": "AASAP_part1",
+        "serviceTypeParallel": true,
+        "status": "error",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop MA1",
+        "hosts": [{
+          "host": "qlaasma1",
+          "status": "success"
+        }],
+        "name": "step10_2",
+        "serviceType": "AASMA_part1",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop PW1",
+        "hosts": [{
+          "host": "qlaaspw1",
+          "status": "success"
+        }],
+        "name": "step10_3",
+        "serviceType": "AASPW_part1",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop SC1",
+        "hosts": [{
+          "host": "qlaassc1",
+          "status": "success"
+        }],
+        "name": "step10_4",
+        "serviceType": "AASSC_part1",
+        "serviceTypeParallel": true,
+        "status": "success",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "step11",
+      "name": "step11",
+      "status": "waiting",
+      "steps": [{
+        "action": "humanWaiting",
+        "displayName": "all application have closed, next step Deploy all application",
+        "name": "step11_1",
+        "status": "waiting",
+        "type": "execution"
+      }]
+    },
+    {
+      "displayName": "Deploy AP1 MA1 PW1 And SC1",
+      "name": "step12",
+      "status": "waiting",
+      "steps": [{
+        "action": "deploy",
+        "displayName": "Deploy AP1",
+        "hosts": [{
+          "host": "qlaasap1",
+          "status": "waiting"
+        }],
+        "name": "step12_1",
+        "serviceType": "AASAP_part1",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy MA1",
+        "hosts": [{
+          "host": "qlaasma1",
+          "status": "waiting"
+        }],
+        "name": "step12_2",
+        "serviceType": "AASMA_part1",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy PW1",
+        "hosts": [{
+          "host": "qlaaspw1",
+          "status": "waiting"
+        }],
+        "name": "step12_3",
+        "serviceType": "AASPW_part1",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy SC1",
+        "hosts": [{
+          "host": "qlaassc1",
+          "status": "waiting"
+        }],
+        "name": "step12_4",
+        "serviceType": "AASSC_part1",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "Deploy HT1 And MH1",
+      "name": "step13",
+      "status": "waiting",
+      "steps": [{
+        "action": "deploy",
+        "displayName": "Deploy HT1",
+        "hosts": [{
+          "host": "qlaasht3",
+          "status": "waiting"
+        }],
+        "name": "step13_1",
+        "serviceType": "AASHT_part1",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy MH1",
+        "hosts": [{
+          "host": "qlaasmh1",
+          "status": "waiting"
+        }],
+        "name": "step13_2",
+        "serviceType": "AASMH_part1",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "step14",
+      "name": "step14",
+      "status": "waiting",
+      "steps": [{
+        "action": "humanWaiting",
+        "displayName": "Waiting for external services",
+        "name": "step14_1",
+        "status": "waiting",
+        "type": "execution"
+      }]
+    },
+    {
+      "displayName": "StartF5 HT1 And MH1",
+      "name": "step15",
+      "status": "waiting",
+      "steps": [{
+        "action": "start",
+        "displayName": "StartF5 HT1",
+        "hosts": [{
+          "host": "qlaasht3",
+          "status": "waiting"
+        }],
+        "name": "step15_1",
+        "serviceType": "AASHT_part1",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "start",
+        "displayName": "StartF5 MH1",
+        "hosts": [{
+          "host": "qlaasmh1",
+          "status": "waiting"
+        }],
+        "name": "step15_2",
+        "serviceType": "AASMH_part1",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "Stop HT2 And MH2 closeF5, stop ap2 ma2 pw2 and sc2 application",
+      "name": "step16",
+      "status": "waiting",
+      "steps": [{
+        "action": "stop",
+        "displayName": "Stop HT2",
+        "hosts": [{
+          "host": "qlaasht4",
+          "status": "waiting"
+        }],
+        "name": "step16_1",
+        "serviceType": "AASHT_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop MH2",
+        "hosts": [{
+          "host": "qlaasmh2",
+          "status": "waiting"
+        }],
+        "name": "step16_2",
+        "serviceType": "AASMH_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop AP2",
+        "hosts": [{
+          "host": "qlaasap2",
+          "status": "waiting"
+        }],
+        "name": "step16_3",
+        "serviceType": "AASAP_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop MA2",
+        "hosts": [{
+          "host": "qlaasma2",
+          "status": "waiting"
+        }],
+        "name": "step16_4",
+        "serviceType": "AASMA_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop PW2",
+        "hosts": [{
+          "host": "qlaaspw2",
+          "status": "waiting"
+        }],
+        "name": "step16_5",
+        "serviceType": "AASPW_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "stop",
+        "displayName": "Stop SC2",
+        "hosts": [{
+          "host": "qlaassc2",
+          "status": "waiting"
+        }],
+        "name": "step16_6",
+        "serviceType": "AASSC_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "step17",
+      "name": "step17",
+      "status": "waiting",
+      "steps": [{
+        "action": "humanWaiting",
+        "displayName": "Finish HT2 & MH2 closeF5, stop ap2 ma2 pw2 and sc2 application,next step Deploy AP2 MA2 PW2 And SC2",
+        "name": "step17_1",
+        "status": "waiting",
+        "type": "execution"
+      }]
+    },
+    {
+      "displayName": "Deploy AP2 MA2 PW2 And SC2",
+      "name": "step18",
+      "status": "waiting",
+      "steps": [{
+        "action": "deploy",
+        "displayName": "Deploy AP2",
+        "hosts": [{
+          "host": "qlaasap2",
+          "status": "waiting"
+        }],
+        "name": "step18_1",
+        "serviceType": "AASAP_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy MA2",
+        "hosts": [{
+          "host": "qlaasma2",
+          "status": "waiting"
+        }],
+        "name": "step18_2",
+        "serviceType": "AASMA_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy PW2",
+        "hosts": [{
+          "host": "qlaaspw2",
+          "status": "waiting"
+        }],
+        "name": "step18_3",
+        "serviceType": "AASPW_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy SC2",
+        "hosts": [{
+          "host": "qlaassc2",
+          "status": "waiting"
+        }],
+        "name": "step18_4",
+        "serviceType": "AASSC_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "Deploy HT2 And MH2",
+      "name": "step19",
+      "status": "waiting",
+      "steps": [{
+        "action": "deploy",
+        "displayName": "Deploy HT2",
+        "hosts": [{
+          "host": "qlaasht4",
+          "status": "waiting"
+        }],
+        "name": "step19_1",
+        "serviceType": "AASHT_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "deploy",
+        "displayName": "Deploy MH2",
+        "hosts": [{
+          "host": "qlaasmh2",
+          "status": "waiting"
+        }],
+        "name": "step19_2",
+        "serviceType": "AASMH_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    },
+    {
+      "displayName": "StartF5 HT2 And MH2",
+      "name": "step20",
+      "status": "waiting",
+      "steps": [{
+        "action": "start",
+        "displayName": "StartF5 HT2",
+        "hosts": [{
+          "host": "qlaasht4",
+          "status": "waiting"
+        }],
+        "name": "step20_1",
+        "serviceType": "AASHT_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      },
+      {
+        "action": "start",
+        "displayName": "StartF5 MH2",
+        "hosts": [{
+          "host": "qlaasmh2",
+          "status": "waiting"
+        }],
+        "name": "step20_2",
+        "serviceType": "AASMH_part2",
+        "serviceTypeParallel": true,
+        "status": "waiting",
+        "type": "execution"
+      }],
+      "subStepsParallel": true
+    }],
+    "subStepsParallel": false,
+    "yamlversion": "v1.0"
+  },
+];

+ 202 - 0
src/view/step-tree/views/flowChart.vue

@@ -0,0 +1,202 @@
+
+<template>
+  <div class="status-box flex">
+    <!-- <span class="defualt">运行中</span> -->
+    <span class="waiting">等待中</span>
+    <span class="running">运行中</span>
+    <span class="succ">成功</span>
+    <span class="bf-suc">部分成功</span>
+    <span class="error">失败</span>
+  </div>
+  <div class="tree-cont-wrap" ref="treeWrapRef">
+    <StepTree
+      :data="treeData"
+      :margin="[15, 15]"
+      :padding="[10, 10]"
+      :font-size="16"
+      :hostFontSize="16"
+      :hostMargin="[10, 5]"
+      :hostPadding="[8, 8]"
+      font-family="微软雅黑"
+      :custom-step-style="customStepStyle"
+      @step-click="stepClickHandler"
+      @step-host-click="stepHostClickHandler"
+      :line-gap="20"
+    />
+  </div>
+</template>
+
+<script setup>
+import data from './data'
+import StepTree from './StepTree.vue'
+
+import { ref, nextTick, toRefs } from 'vue'
+import { useRouter } from 'vue-router'
+const treeWrapRef = ref()
+const $router = useRouter()
+const treeData = [
+  { displayName: '开始', type: 'startEnd' },
+  ...data[0].steps,
+  { displayName: '结束', type: 'startEnd' },
+]
+
+// 每个step的样式
+const customStepStyle = (step) => {
+  console.log('xxxxx', step)
+  // 等待中,开始状态
+  if (step.status === 'waiting') {
+    return {
+      lineColor: '#89beb2',
+      lineWidth: 1,
+      textColor: '#333',
+      rectBorderColor: '#333',
+      rectBgColor: '#89beb2',
+      rectRadius: 2,
+      rectBorderWidth: 1,
+    }
+  }
+  // 失败
+  else if (step.status === 'error') {
+    return {
+      lineColor: '#89beb2',
+      lineWidth: 1,
+      textColor: '#333',
+      rectBorderColor: '#333',
+      rectBgColor: 'red',
+      rectRadius: 2,
+      rectBorderWidth: 1,
+    }
+  }
+  // 成功
+  else if (step.status === 'success') {
+    return {
+      lineColor: '#89beb2',
+      lineWidth: 1,
+      textColor: '#333',
+      rectBorderColor: '#333',
+      rectBgColor: '#30d567',
+      rectRadius: 2,
+      rectBorderWidth: 1,
+    }
+  }
+  // 部分成功
+  else if (step.status === 'partsuccess') {
+    return {
+      lineColor: '#89beb2',
+      lineWidth: 1,
+      textColor: '#333',
+      rectBorderColor: '#333',
+      rectBgColor: '#d4f8c3',
+      rectRadius: 2,
+      rectBorderWidth: 1,
+    }
+  } else if (step.status === 'running') {
+    return {
+      lineColor: '#89beb2',
+      lineWidth: 1,
+      textColor: '#333',
+      rectBorderColor: '#333',
+      rectBgColor: '#ecf752',
+      rectRadius: 2,
+      rectBorderWidth: 1,
+    }
+  }
+  {
+    return {
+      lineColor: '#89beb2',
+      lineWidth: 1,
+      textColor: '#333',
+      rectBorderColor: 'coral',
+      rectBgColor: '#ffff',
+      rectRadius: 2,
+      rectBorderWidth: 1,
+    }
+  }
+}
+
+// hosts
+const customHostStyle = (step) => {
+  if (step.hosts?.length) {
+    step.hosts.forEach((item, idx) => {
+      if (item.status === 'success') {
+        return {
+          lineColor: '#40dbd9',
+          lineWidth: 1,
+          textColor: '#ccc',
+          rectBorderColor: '#40dbd9',
+          rectBgColor: '#ffff',
+          rectRadius: 2,
+          rectBorderWidth: 1,
+        }
+      }
+    })
+  }
+}
+
+const stepClickHandler = (step) => {
+  console.log(step)
+  if (step.raw.type === 'startEnd') return
+  let url = $router.resolve({
+    path: '/stepLogs',
+    params: {
+      key: 1,
+    },
+  }).href
+  window.open(url, '_blank')
+}
+const stepHostClickHandler = (host) => {
+  console.log(host)
+}
+nextTick(() => {
+  treeWrapRef.value.scrollLeft = (treeWrapRef.value.scrollWidth - treeWrapRef.value.clientWidth) / 2
+})
+</script>
+
+<style scoped>
+.tree-cont-wrap {
+  width: 100%;
+  height: 100%;
+  margin: 0 auto;
+  overflow: auto;
+  text-align: center;
+  /* display: flex;
+  justify-content: center;
+  align-items: center; */
+}
+
+.flex {
+  display: flex;
+}
+.status-box {
+  width: 400px;
+}
+.status-box span {
+  display: inline-block;
+  width: 120px;
+  height: 30px;
+  line-height: 30px;
+  color: #333;
+  text-align: center;
+  margin: 0 5px;
+}
+/* 运行中 */
+.running {
+  background: #ecf752;
+}
+/* 错误 */
+.error {
+  background: #ff4238;
+}
+/* 成功 */
+.succ {
+  background: #30d567;
+}
+/* 等待中 */
+.waiting {
+  background: #89beb2;
+}
+/* 部分成功 */
+.bf-suc {
+  background: #c6f9ae;
+}
+</style>

+ 1 - 1
tsconfig.json

@@ -17,7 +17,7 @@
     "noEmit": true,
     "jsx": "preserve",
     /* Linting */
-    "strict": true,
+    "strict": false,
     "noUnusedLocals": true,
     "noUnusedParameters": true,
     "noFallthroughCasesInSwitch": true,