mapCanvas.vue 9.52 KB
<template>
  <div>
    <div style="position: absolute; z-index: 999; right: 10px; top: 20px">
      <el-button
        type="primary"
        size="mini"
        @click="handleMenuItemClick('export')"
        >导出</el-button
      >
      <el-button
        type="primary"
        size="mini"
        @click="handleMenuItemClick('import')"
        >导入</el-button
      >
    </div>
    <div
      class="right"
      id="container"
      :style="{ height: canvasHeight }"
      @mousedown.prevent
      @dblclick.prevent
    ></div>

    <map-context-menu
      ref="mapContextMenu"
      @attribute="attributeEdit"
      @control="controlEdit"
    />
    <map-attribute ref="mapAttribute"></map-attribute>
    <map-control ref="mapControl"></map-control>

    <div style="display: none">
      <input
        type="file"
        id="files"
        style="color: #fff"
        @change="savetemplate"
      />
    </div>
  </div>
</template>

<script>
import { formatTime, openConfirm, showMsg } from "@/utils/index.js";
import { Graph } from "@antv/x6";
import { Dnd } from "@antv/x6-plugin-dnd";
import { History } from "@antv/x6-plugin-history";
import { Selection } from "@antv/x6-plugin-selection";
import { getGUID } from "@/utils/index.js";
// import { cloneDeep } from "lodash";
import mapContextMenu from "./mapContextMenu.vue";
import mapAttribute from "./mapAttribute.vue";
import mapControl from "./mapControl.vue";
import mapTable from "./mapTable.vue";

export default {
  name: "mapCanvas",
  components: {
    mapContextMenu,
    mapAttribute,
    mapControl,
    mapTable,
  },
  props: {},
  data() {
    return {
      graph: null, //画布
    };
  },
  computed: {
    canvasHeight() {
      return `calc(100vh - 140px )`;
    },
  },
  created() {
    //开始监听
    window.addEventListener("keydown", this.handleKeyDown);
    window.addEventListener("resize", this.setHeight);
  },
  beforeDestroy() {
    //结束监听
    window.removeEventListener("keydown", this.handleKeyDown);
    window.removeEventListener("resize", this.setHeight);
  },
  mounted() {
    this.graph = new Graph({
      container: document.getElementById("container"),
      width: 1800,
      //   height: 580,
      background: {
        color: "#F2F7FA",
      },
      // 网格线设置
      grid: {
        visible: true,
        type: "doubleMesh",
        size: 10,
        args: [
          {
            color: "#eee", // 主网格线颜色
            thickness: 1, // 主网格线宽度
          },
          {
            color: "#ddd", // 次网格线颜色
            thickness: 1, // 次网格线宽度
            factor: 4, // 主次网格线间隔
          },
        ],
      },
      // 缩放与平移
      mousewheel: true, //使用滚轮控制缩放
      panning: {
        enabled: true,
        //触发键盘事件进行平移:'alt' | 'ctrl' | 'meta' | 'shift'
        modifiers: [],
        //触发鼠标事件进行平移:'leftMouseDown' | 'rightMouseDown' | 'mouseWheel'
        eventTypes: ["rightMouseDown"],
      },
    });

    this.graph.use(
      new History({
        enabled: true,
      })
    );

    this.graph.use(
      new Selection({
        enabled: true,
        multiple: true,
        rubberband: true,
        movable: true,
        showNodeSelectionBox: true,
      })
    );
    // this.graph.scale(1, 1);
    //   // 恢复初始位置
    // this.graph.translate(0, 0);
    // this.graph.centerContent()

    //双击事件
    this.graph.on("node:dblclick", ({ e, x, y, node, view }) => {
      this.$refs.mapAttribute.attributeEdit(node);
    });

    // this.graph.on("node:click", ({ node, e, x, y, view }) => {
    //   // console.log(node, x, y);
    //   node.attr({
    //     btn: {
    //       refX: "100%",
    //       refX2: -28,
    //       y: 4,
    //       width: 24,
    //       height: 18,
    //       rx: 10,
    //       ry: 10,
    //       fill: "rgba(255,255,0,0.01)",
    //       stroke: "red",
    //       cursor: "pointer",
    //       event: "node:delete",
    //     },
    //     btnText: {
    //       fontSize: 14,
    //       fill: "red",
    //       text: "x",
    //       refX: "100%",
    //       refX2: -19,
    //       y: 17,
    //       cursor: "pointer",
    //       pointerEvent: "none",
    //     },
    //     // image: {
    //     //   event: 'node:delete',
    //     //   xlinkHref: 'trash.png',
    //     //   width: 20,
    //     //   height: 20,
    //     // },
    //   });
    // });

    // 监听画布空白处右键事件
    this.graph.on("blank:contextmenu", ({ e }) => {
      e.preventDefault();
      this.$refs.mapContextMenu.showMenu(
        {
          x: e.clientX - 30,
          y: e.clientY - 100,
        },
        0,
        null
      );
    });

    // 监听节点右键事件
    this.graph.on("node:contextmenu", ({ e, node }) => {
      e.preventDefault();

      this.$refs.mapContextMenu.showMenu(
        {
          x: e.clientX - 30,
          y: e.clientY - 100,
        },
        1,
        node
      );
    });

    // 点击其他地方隐藏菜单
    document.addEventListener("click", () => {
      if (this.$refs.mapContextMenu) {
        this.$refs.mapContextMenu.hideMenu();
      }
    });

    this.graph.on("node:moved", ({ node, e, x, y, view }) => {
      // console.log("节点移动了", node, `移动到坐标 (${x}, ${y})`);
      // 你可以在这里添加自定义逻辑,例如更新节点数据、保存位置等
      if (node.store.data.shape == "Point") {
        // console.log(
        //   node.store.data.position.x + "" + node.store.data.position.y
        // );
        // console.log(node.store.data.data);
        var data = node.store.data.data;
        data.code =
          node.store.data.position.x + "" + node.store.data.position.y;
        node.prop("data", data);
        // console.log(data);
        node.prop("attrs/text", {
          text: node.store.data.position.x + "\n" + node.store.data.position.y,
          transform: "",
        });
      }
    });

    // 渲染节点和边
    this.graph.fromJSON({
      nodes: this.nodes,
      edges: this.edges,
    });
    // 实现画布内容居中
    this.graph.centerContent();
    this.$refs.mapControl.graph = this.graph;
    this.$refs.mapAttribute.graph = this.graph;
    this.$refs.mapContextMenu.graph = this.graph;
    this.$refs.mapContextMenu.reset();

    var mapData = sessionStorage.getItem("mapData");
    if (mapData) {
      this.graph.fromJSON(JSON.parse(mapData));
    }
  },
  methods: {
    //拖拽节点
    startDragToGraph(item, text, e) {
      const nodePoint = this.graph.createNode({
        attrs: {
          text: text,
        },
        position: {
          x: 0,
          y: 0,
        },
        size: {
          width: item.width,
          height: item.height,
        },
        angle: 0,
        shape: item.type,
        id: getGUID(),
        zIndex: 4,
        data: {
          code: "",
          name: "",
          type: "",
          status: "0",
          pallet: "0",
          auto: "1",
        },
      });
      const dnd = new Dnd({
        target: this.graph,
        // ☆拖拽结束时,验证节点是否可以放置到目标画布中。
        validateNode: (node, options) => {
          // console.log(node, options);
          // console.log("成功拖拽至目标画布");

          if (node.store.data.shape == "Point") {
            node.prop("data", {
              code:
                node.store.data.position.x + "" + node.store.data.position.y,
            });
            node.prop("attrs/text", {
              text:
                node.store.data.position.x + "\n" + node.store.data.position.y,
              transform: "",
            });
          }
        },
      });
      dnd.start(nodePoint, e);
    },

    //属性编辑
    attributeEdit(node) {
      this.$refs.mapAttribute.attributeEdit(node);
    },

    //管制区域
    controlEdit(node) {
      this.$refs.mapControl.controlEdit(node);
    },

    //删除按键
    handleKeyDown(event) {
      if (event.key == "Delete") {
        // console.log("ssss", this.graph.getSelectedCells());
        let isselect = this.graph.getSelectedCells();
        isselect.forEach((i) => {
          this.graph.removeNode(i.id);
        });
      }
      // 添加撤销和重做的快捷键
      if (event.ctrlKey && event.key === "z") {
        this.undo();
      }
      if (event.ctrlKey && event.key === "y") {
        this.redo();
      }
    },

    //设置画布高度
    setHeight() {
      document.getElementById("container").style.height =
        "calc(100vh - 140px )";
    },

    //撤销
    undo() {
      if (this.graph && this.graph.canUndo()) {
        try {
          this.graph.undo();
        } catch (error) {
          console.error("撤销操作出错:", error);
          showMsg("撤销操作出错,请稍后重试", "error");
        }
      }
    },

    //恢复
    redo() {
      if (this.graph && this.graph.canRedo()) {
        try {
          this.graph.redo();
        } catch (error) {
          console.error("恢复操作出错:", error);
          showMsg("恢复操作出错,请稍后重试", "error");
        }
      }
    },

    //右键菜单
    handleMenuItemClick(action) {
      if (action == "import") {
        document.getElementById("files").click();
      } else {
        this.$refs.mapContextMenu.handleMenuItemClick(action);
      }
    },

    //导出
    savetemplate() {
      this.$refs.mapContextMenu.savetemplate();
    },
  },
};
</script>

<style lang="scss" scoped>
.right {
  margin-left: 10px;
  margin-top: 10px;
  border: 1px solid #ebeef5;
}
</style>