<template>
  <div class="wrapper">
    <div class="container">
      <!-- 创建一个canvas画布 npmn-js是通过canvas实现绘图的，并设置ref让vue获取到element -->
      <div class="bpmn-container">
        <div class="bpmn-canvas" ref="canvas"></div>
        <!-- 工具栏显示的地方 -->
        <div class="bpmn-js-properties-panel" id="js-properties-panel"></div>
      </div>

      <!-- 把操作按钮写在这里面 -->
      <div class="action">
        <!-- 打开本地xml文件 -->
        <el-upload action class="upload-demo" :before-upload="openBpmn">
          <el-button icon="el-icon-folder-opened"></el-button>
        </el-upload>
        <!-- 重置操作 -->
        <el-button
          class="new"
          icon="el-icon-circle-plus"
          @click="newDiagram"
        ></el-button>
        <!-- 导出文件 -->
        <el-button icon="el-icon-download" @click="downloadBpmn"></el-button>
        <!-- 导出图片 -->
        <el-button icon="el-icon-picture" @click="downloadSvg"></el-button>
        <a hidden ref="downloadLink"></a>
      </div>
    </div>
  </div>
</template>
<script>
// 引入相关的依赖
import BpmnModeler from "bpmn-js/lib/Modeler";
import xmlStr from "./mock/xmlStr"; // 这里是直接引用了xml字符串
// 工具栏相关
import propertiesPanelModule from "bpmn-js-properties-panel";
import propertiesProviderModule from "bpmn-js-properties-panel/lib/provider/camunda";
import camundaModdleDescriptor from "camunda-bpmn-moddle/resources/camunda";

// 自定义panel
// import propertiesProviderModule from "@/views/system/bpmn/components/properties-panel-extension/provider/authority/index";
// import authorityModdleDescriptor from "@/views/system/bpmn/components/properties-panel-extension/descriptors/authority.json";
// 网格插件
import GridLineModule from "diagram-js-grid-bg";
import ContextPadModule from "diagram-js-context-pad"; //上下文优化
// 汉化
import customTranslate from "./mock/customTranslate";

import minimapModule from "diagram-js-minimap"; //导入小地图
import "./mock/minimap.css"; //自定义小地图样式

export default {
  name: "",
  components: {},
  created() {},
  mounted() {
    this.init();
  },
  data() {
    return {
      // bpmn建模器
      bpmnModeler: null,
      container: null,
      canvas: null,
    };
  },
  methods: {
    // 初始化相关方法
    init() {
      // 获取到属性ref为“canvas”的dom节点
      const canvas = this.$refs.canvas;
      // 将汉化包装成一个模块
      const customTranslateModule = {
        translate: ["value", customTranslate],
      };
      // 建模
      this.bpmnModeler = new BpmnModeler({
        container: canvas,
        // 加入工具栏支持
        propertiesPanel: {
          parent: "#js-properties-panel",
        },
        additionalModules: [
          // 工具栏模块
          propertiesPanelModule,
          propertiesProviderModule,
          // 汉化模块
          customTranslateModule,
          GridLineModule, //网格
          minimapModule, //小地图
        ],
        moddleExtensions: {
          camunda: camundaModdleDescriptor,
          // authority: authorityModdleDescriptor,
        },
        contextPad: {
          beauty: true,
        },
      });
      this.createNewDiagram(xmlStr);
    },
    async createNewDiagram(xmlStr) {
      try {
        await this.bpmnModeler.importXML(xmlStr);
        this.success();
      } catch (err) {
        this.$Message.error("打开模型出错,请确认该模型符合Bpmn2.0规范");
      }
    },
    success() {
      console.log(this.bpmnModeler);
      console.log("创建成功!");
    },
    newDiagram() {
      this.createNewDiagram(xmlStr);
    },

    // 文件操作相关方法
    downloadBpmn() {
      this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
        if (!err) {
          // 获取文件名
          const name = `${this.getFilename(xml)}.bpmn`;
          // 将文件名以及数据交给下载方法
          this.download({ name: name, data: xml });
        }
      });
    },
    downloadSvg() {
      this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
        if (!err) {
          // 获取文件名
          const name = `${this.getFilename(xml)}.svg`;

          // 从建模器画布中提取svg图形标签
          let context = "";
          const djsGroupAll = this.$refs.canvas.querySelectorAll(".djs-group");
          for (let item of djsGroupAll) {
            context += item.innerHTML;
          }
          // 获取svg的基本数据，长宽高
          const viewport = this.$refs.canvas
            .querySelector(".viewport")
            .getBBox();

          // 将标签和数据拼接成一个完整正常的svg图形
          const svg = `
            <svg
              xmlns="http://www.w3.org/2000/svg"
              xmlns:xlink="http://www.w3.org/1999/xlink"
              width="${viewport.width}"
              height="${viewport.height}"
              viewBox="${viewport.x} ${viewport.y} ${viewport.width} ${viewport.height}"
              version="1.1"
              >
              ${context}
            </svg>
          `;
          // 将文件名以及数据交给下载方法
          this.download({ name: name, data: svg });
        }
      });
    },

    openBpmn(file) {
      const reader = new FileReader();
      // 读取File对象中的文本信息，编码格式为UTF-8
      reader.readAsText(file, "utf-8");
      reader.onload = () => {
        //读取完毕后将文本信息导入到Bpmn建模器
        this.createNewDiagram(reader.result);
      };
      return false;
    },

    getFilename(xml) {
      let start = xml.indexOf("process");
      let filename = xml.substr(start, xml.indexOf(">"));
      filename = filename.substr(filename.indexOf("id") + 4);
      filename = filename.substr(0, filename.indexOf('"'));
      return filename;
    },

    download({ name = "diagram.bpmn", data }) {
      // 这里就获取到了之前设置的隐藏链接
      const downloadLink = this.$refs.downloadLink;
      // 把数据转换为URI，下载要用到的
      const encodedData = encodeURIComponent(data);

      if (data) {
        // 将数据给到链接
        downloadLink.href =
          "data:application/bpmn20-xml;charset=UTF-8," + encodedData;
        // 设置文件名
        downloadLink.download = name;
        // 触发点击事件开始下载
        downloadLink.click();
      }
    },
  },
};
</script>

<style scoped lang="scss">
.wrapper {
  height: calc(100vh - 173px);
}
.container {
  position: relative;
  height: 100%;
}
.bpmn-container {
  width: 100%;
  height: 100%;
  display: flex;
}

.bpmn-canvas {
  width: calc(100% - 300px);
  height: 100%;
}

.bpmn-js-properties-panel {
  width: 320px;
  height: inherit;
  overflow-y: auto;
}

.action {
  position: fixed;
  top: 135px;
  left: 50%;
  transform: translate(-50%);
  display: flex;
}
.upload-demo {
  margin-right: 10px;
}
</style>
