<template> <div class="baseTopo" id="baseTopo" ref="baseTopo"> <topoTooltip v-show="showTooltip" class="topoTooltip-box" ref="topoTooltip" @closeTooltip="showTooltip = false" :havItem="havItem" ></topoTooltip> <canvas id="persagy_topo" :width="canvasWidth" :height="canvasHeight" tabindex="0" ></canvas> </div> </template> <script> import { PTopoScene, PTopoParser, PTopoView, } from "@/components/editClass/persagy-edit"; import { SBaseEquipment } from "@/components/editClass/big-edit"; import topoTooltip from "./topoTooltip.vue"; import { mapState, mapMutations } from "vuex"; import base64ToFile from "@/utils/base64ToFile"; import { v1 as uuidv1 } from "uuid"; import bus from "@/bus/bus"; import axios from "axios"; import { saveGroup, readGroup, uploadGroup, getImageGroup, readPubGroup, } from "@/api/editer"; import { publishGraph } from "@/api/home"; import crypto from "crypto-js/"; export default { components: { topoTooltip }, data() { return { scene: null, //场景 view: null, //视图 canvasWidth: 700, //画布宽 canvasHeight: 700, //画布高 havItem: false, //右击是否选中item showTooltip: false, //是否显示tooltip topoContent: {}, // 读图后存储图所有数据 autoSave: null, // 自动保存定时器 }; }, computed: { ...mapState([ "editCmd", "legendObj", "graphId", "id", "isPub", "categoryId", "projectId", ]), }, mounted() { this.canvasWidth = this.$refs.baseTopo.offsetWidth; this.canvasHeight = this.$refs.baseTopo.offsetHeight - 10; this.scene = new PTopoScene(); this.view = new PTopoView("persagy_topo"); this.view.scene = this.scene; this.scene.clearCmdStatus = this.clearCmdStatus; // 初始化bus绑定事件 this.initBusEvent(); // 右键事件 this.scene.getItem = this.onContextMenu; this.scene.emitChoice = this.emitChoice; //左键事件 this.scene.vueOnMouseDown = this.vueOnMouseDown; // 屏蔽浏览器右键功能(防止与编辑器右键交互重合) document.getElementById("baseTopo").oncontextmenu = function (e) { return false; }; // 读取底图 this.readtopoMsg(); // 2分钟自动保存 this.autoSave = setInterval(() => { this.autoSaveTopo(); }, 120000); }, methods: { ...mapMutations([ "SETCHOICELEHEND", "SETLEGENDOBJ", "SETPROJECT", "SETCATEGROY", "SETISPUB", "ADDEQUIPITEM", "EDITEQUIPITEM", ]), // 恢复命令状态 clearCmdStatus() { this.SETCHOICELEHEND(""); this.SETLEGENDOBJ(null); }, // 右键获取item onContextMenu(item, [event]) { this.showTooltip = true; if (item) { this.havItem = true; } else { this.havItem = false; } const doms = document.getElementsByClassName("topoTooltip-box")[0]; doms.style.left = event.offsetX + "px"; doms.style.top = event.offsetY + "px"; }, // 左键事键 vueOnMouseDown(e) { // 关闭tooltip this.showTooltip = false; }, // 选中后的回调函数 emitChoice(itemList) { bus.$emit("emitChoice", itemList); }, //初始化bus绑定事件 initBusEvent() { // 改变样式 bus.$off("updateStyle"); bus.$on("updateStyle", (type, val) => { this.scene.updateStyle(type, val); }); // 撤销 bus.$off("topoUndo"); bus.$on("topoUndo", (val) => { this.scene.undo(); }); // 重做 bus.$off("topoRedo"); bus.$on("topoRedo", (val) => { this.scene.redo(); }); // 删除 bus.$off("deleteItem"); bus.$on("deleteItem", (val) => { this.scene.deleteItem([val]); this.EDITEQUIPITEM(); }); // 复制 bus.$off("copy"); bus.$on("copy", (val) => { this.scene.copy(); }); // 粘贴 bus.$off("paste"); bus.$on("paste", (val) => { this.scene.paste(); }); // 保存 bus.$off("saveTopo"); bus.$on("saveTopo", (val) => { this.saveTopoDraft(); }); // 设置实例置顶置底 bus.$off("setOrder"); bus.$on("setOrder", (val) => { this.scene.setOrder(val); }); // 设置实例status状态 bus.$off("setItemStatus"); bus.$on("setItemStatus", (val) => { this.scene.setItemStatus(); }); // 下载图片 bus.$off("saveTopoImg"); bus.$on("saveTopoImg", () => { // 隐藏选择器 this.scene.selectContainer.clear(); setTimeout(() => { this.view.saveImage(`${this.topoContent.name}.png`, "png"); }, 80); }); // 发布图片 bus.$off("issueTopo"); bus.$on("issueTopo", () => { this.saveTopoDraft().then(() => { this.issueDraft(); }); }); // 手动添加设备实例 bus.$off("addEquipment"); bus.$on("addEquipment", (val) => { this.addEquipmentList(val); }); // 更改设备信息点 bus.$off("changeEquipMsgPoint"); bus.$on("changeEquipMsgPoint", (val) => { this.scene.changeEquipMsgPoint(val); }); // 选中item bus.$off("chioceItem"); bus.$on("chioceItem", (item) => { this.scene.toggleItem(item); }); }, // 读取拓扑图 readtopoMsg() { const obj = { graphId: this.graphId, id: this.id, }; if (this.isPub == 1) { // 已发布 readPubGroup(obj).then((res) => { this.getDataSuc(res); }); } else { readGroup(obj).then((res) => { this.getDataSuc(res); }); } }, // 读图成功回调 getDataSuc(res) { if (res.result == "failure") return; this.SETCATEGROY(res.content); this.topoContent = res.content; const parse = new PTopoParser(); parse.parseData(res.content.elements); parse.markers.forEach((item) => { item.selectable = true; item.moveable = true; item.connect("finishCreated", this.scene, this.scene.finishCreated); item.connect("onContextMenu", this, this.scene.getItem); this.scene.addItem(item); }); parse.nodes.forEach((item) => { item.connect("finishCreated", this.scene, this.scene.finishCreated); item.connect("onContextMenu", this, this.scene.getItem); this.scene.addItem(item); // 如果为设备则存于vuex中便于联动 if (item instanceof SBaseEquipment) { this.ADDEQUIPITEM(item); } }); }, // 生成快照并保存草稿 saveTopoDraft() { const uuid = uuidv1(); return Promise.all([this.generateSnap(uuid), this.saveDraft(uuid)]).then( (vals) => { this.$message.success(`保存成功${vals[1].version}`); } ); }, // 生成快照 generateSnap(uuid) { // 隐藏选择器 this.scene.selectContainer.clear(); setTimeout(() => { // base64数据 const data = this.view.imageUrl("png"); // 根据base64生成file const file = base64ToFile(data); const reader = new FileReader(); const fileType = file.name.split("."); const imgType = fileType[fileType.length - 1]; return new Promise((resolve, reject) => { reader.onloadend = function () { // 这个事件在读取结束后,无论成功或者失败都会触发 if (reader.error) { console.log("reader error", reader.error); reject(reader.error); } else { // 构造 XMLHttpRequest 对象,发送文件 Binary 数据 const xhr = new XMLHttpRequest(); xhr.open( "POST", `/image-service/common/image_upload?systemId=dataPlatform&secret=9e0891a7a8c8e885&overwrite=true&key=${uuid}.${imgType}` ); xhr.send(reader.result); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { resolve(xhr); } } }; } }; reader.readAsArrayBuffer(file); }); }, 80); }, // 保存草稿 saveDraft(uuid) { const elements = this.scene.save(); console.log("elements", elements); const obj = { elements, name: this.topoContent.name, // 名称 categoryId: this.categoryId, // 图分类ID projectId: this.projectId, // 项目ID label: this.topoContent.label, buildingId: "1", // 建筑ID floorId: "1", // 楼层id note: "1", // 图说明 pic: `${uuid}.png`, graphId: this.graphId, id: this.id, log: { // 图操作日志 mark: "1", // 图的存盘标记 commandList: [ { command: "1", // 命令 desc: "1", // 描述 detail: "1", // 详情 }, ], }, }; console.log("elements", obj); return new Promise((resolve, reject) => { saveGroup(obj).then((res) => { // 如果是从已发布跳转过来 if (this.isPub == 1) { // 设置发布状态为 未发布 this.SETISPUB(0); const gid = res.entityList[0].graphId; const id = res.entityList[0].id; // 重设图id 与 id this.SETPROJECT({ graphId: gid, id: id }); // 修改url参数 this.$router.push({ name: "Editer", query: { graphId: gid, id: id, categoryName: encodeURI(this.categoryName), isPub: 0, }, }); } resolve(res.entityList[0]); }); }); }, // 自动保存 autoSaveTopo() { console.log(this.scene.undoStack.isChange); if (this.scene && this.scene.undoStack.isChange) { this.saveTopoDraft().then(() => { this.scene.undoStack.isChange = false; }); } }, // 发布草稿 issueDraft() { const pa = { graphId: this.graphId, id: this.id, }; publishGraph(pa).then((res) => { this.$message.success("发布成功"); }); }, // 手动添加设备 addEquipmentList(list) { const parse = new PTopoParser(); list.forEach((item, i) => { const x = (i + 1) * 100 + 300; const baseUrl = "/image-service/common/image_get?systemId=dataPlatform&key="; const url = baseUrl + item.url; let svg2Base = ""; let EquipHeight = this.canvasHeight - 100; let data = { /** 名称 */ name: "基础设备", /** 返回物理世界对象 ID 列表 */ attachObjectIds: [item.id], size: { width: 50, height: 50 }, /** 图标 (Image),线类型 (Line) */ type: "Image", /** 位置 */ pos: { x: x, y: 100 }, /** 由应用自己定义 */ properties: { type: "BaseEquipment", classCode: item.classCode, // 设备类型 localId: item.localId, // 本地编码 localName: item.localName, //本地名称 }, style: { default: { strokecolor: "#c0ccda", url: url, base64Url: "", }, }, }; parse.addNode(data); }); // 添加到 scence 中 parse.nodes.forEach((item) => { item.connect("finishCreated", this.scene, this.scene.finishCreated); item.connect("onContextMenu", this, this.scene.getItem); this.scene.addItem(item); // 如果为设备则存于vuex中便于联动 if (item instanceof SBaseEquipment) { this.ADDEQUIPITEM(item); } }); }, }, watch: { editCmd(val) { if (this.scene) { // 设置当前编辑状态 this.scene.editCmd = val; } }, legendObj: { handler: function (val, oldVal) { this.scene.legendObj = val; }, deep: true, }, }, created() { this.SETPROJECT(this.$route.query); this.SETISPUB(this.$route.query.isPub); this.categoryName = decodeURI(this.$route.query.categoryName); }, beforeDestroy() { clearInterval(this.autoSave); }, }; </script> <style lang="less" scoped> .baseTopo { width: 100%; height: 100%; position: relative; .topoTooltip-box { position: absolute; left: 0; top: 0; } } </style>