|
@@ -0,0 +1,671 @@
|
|
|
+<template>
|
|
|
+ <div class="my-process-designer">
|
|
|
+ <div class="my-process-designer__header">
|
|
|
+ <slot name="control-header"></slot>
|
|
|
+ <template v-if="!$slots['control-header']">
|
|
|
+ <ElButtonGroup key="file-control">
|
|
|
+ <XButton preIcon="ep:folder-opened" title="打开文件" @click="refFile.click()" />
|
|
|
+ <el-tooltip effect="light" placement="bottom">
|
|
|
+ <template #content>
|
|
|
+ <XButton type="text" title="下载为XML文件" @click="downloadProcessAsXml()" />
|
|
|
+ <br />
|
|
|
+
|
|
|
+ <XButton type="text" title="下载为SVG文件" @click="downloadProcessAsSvg()" />
|
|
|
+ <br />
|
|
|
+
|
|
|
+ <XButton type="text" title="下载为BPMN文件" @click="downloadProcessAsBpmn()" />
|
|
|
+ </template>
|
|
|
+ <XButton title="下载文件" preIcon="ep:download" />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip effect="light">
|
|
|
+ <XButton preIcon="ep:view" title="浏览" />
|
|
|
+ <template #content>
|
|
|
+ <el-button type="text" @click="previewProcessXML">预览XML</el-button>
|
|
|
+ <br />
|
|
|
+ <el-button type="text" @click="previewProcessJson">预览JSON</el-button>
|
|
|
+ </template>
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip
|
|
|
+ v-if="props.simulation"
|
|
|
+ effect="light"
|
|
|
+ :content="simulationStatus ? '退出模拟' : '开启模拟'"
|
|
|
+ >
|
|
|
+ <XButton preIcon="ep:cpu" title="模拟" @click="processSimulation" />
|
|
|
+ </el-tooltip>
|
|
|
+ </ElButtonGroup>
|
|
|
+ <ElButtonGroup key="align-control">
|
|
|
+ <el-tooltip effect="light" content="向左对齐">
|
|
|
+ <!-- <el-button
|
|
|
+ class="align align-left"
|
|
|
+ icon="el-icon-s-data"
|
|
|
+ @click="elementsAlign('left')"
|
|
|
+ /> -->
|
|
|
+ <XButton
|
|
|
+ preIcon="fa:align-left"
|
|
|
+ class="align align-bottom"
|
|
|
+ @click="elementsAlign('left')"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip effect="light" content="向右对齐">
|
|
|
+ <!-- <el-button
|
|
|
+ class="align align-right"
|
|
|
+ icon="el-icon-s-data"
|
|
|
+ @click="elementsAlign('right')"
|
|
|
+ /> -->
|
|
|
+ <XButton
|
|
|
+ preIcon="fa:align-left"
|
|
|
+ class="align align-top"
|
|
|
+ @click="elementsAlign('right')"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip effect="light" content="向上对齐">
|
|
|
+ <!-- <el-button
|
|
|
+ class="align align-top"
|
|
|
+ icon="el-icon-s-data"
|
|
|
+ @click="elementsAlign('top')"
|
|
|
+ /> -->
|
|
|
+ <XButton
|
|
|
+ preIcon="fa:align-left"
|
|
|
+ class="align align-left"
|
|
|
+ @click="elementsAlign('top')"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip effect="light" content="向下对齐">
|
|
|
+ <!-- <el-button
|
|
|
+ class="align align-bottom"
|
|
|
+ icon="el-icon-s-data"
|
|
|
+ @click="elementsAlign('bottom')"
|
|
|
+ /> -->
|
|
|
+ <XButton
|
|
|
+ preIcon="fa:align-left"
|
|
|
+ class="align align-right"
|
|
|
+ @click="elementsAlign('bottom')"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip effect="light" content="水平居中">
|
|
|
+ <!-- <el-button
|
|
|
+ class="align align-center"
|
|
|
+ icon="el-icon-s-data"
|
|
|
+ @click="elementsAlign('center')"
|
|
|
+ /> -->
|
|
|
+ <!-- class="align align-center" -->
|
|
|
+ <XButton
|
|
|
+ preIcon="fa:align-left"
|
|
|
+ class="align align-center"
|
|
|
+ @click="elementsAlign('center')"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip effect="light" content="垂直居中">
|
|
|
+ <!-- <el-button
|
|
|
+ class="align align-middle"
|
|
|
+ icon="el-icon-s-data"
|
|
|
+ @click="elementsAlign('middle')"
|
|
|
+ /> -->
|
|
|
+ <XButton
|
|
|
+ preIcon="fa:align-left"
|
|
|
+ class="align align-middle"
|
|
|
+ @click="elementsAlign('middle')"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ </ElButtonGroup>
|
|
|
+ <ElButtonGroup key="scale-control">
|
|
|
+ <el-tooltip effect="light" content="缩小视图">
|
|
|
+ <!-- <el-button
|
|
|
+ :disabled="defaultZoom < 0.2"
|
|
|
+ icon="el-icon-zoom-out"
|
|
|
+ @click="processZoomOut()"
|
|
|
+ /> -->
|
|
|
+ <XButton
|
|
|
+ preIcon="ep:zoom-out"
|
|
|
+ @click="processZoomOut()"
|
|
|
+ :disabled="defaultZoom < 0.2"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-button>{{ Math.floor(defaultZoom * 10 * 10) + '%' }}</el-button>
|
|
|
+ <el-tooltip effect="light" content="放大视图">
|
|
|
+ <!-- <el-button
|
|
|
+ :disabled="defaultZoom > 4"
|
|
|
+ icon="el-icon-zoom-in"
|
|
|
+ @click="processZoomIn()"
|
|
|
+ /> -->
|
|
|
+ <XButton preIcon="ep:zoom-in" @click="processZoomIn()" :disabled="defaultZoom > 4" />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip effect="light" content="重置视图并居中">
|
|
|
+ <!-- <el-button icon="el-icon-c-scale-to-original" @click="processReZoom()" /> -->
|
|
|
+ <XButton preIcon="ep:scale-to-original" @click="processReZoom()" />
|
|
|
+ </el-tooltip>
|
|
|
+ </ElButtonGroup>
|
|
|
+ <ElButtonGroup key="stack-control">
|
|
|
+ <el-tooltip effect="light" content="撤销">
|
|
|
+ <!-- <el-button :disabled="!revocable" icon="el-icon-refresh-left" @click="processUndo()" /> -->
|
|
|
+ <XButton preIcon="ep:refresh-left" @click="processUndo()" :disabled="!revocable" />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip effect="light" content="恢复">
|
|
|
+ <!-- <el-button
|
|
|
+ :disabled="!recoverable"
|
|
|
+ icon="el-icon-refresh-right"
|
|
|
+ @click="processRedo()"
|
|
|
+ /> -->
|
|
|
+ <XButton preIcon="ep:refresh-right" @click="processRedo()" :disabled="!recoverable" />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip effect="light" content="重新绘制">
|
|
|
+ <!-- <el-button icon="el-icon-refresh" @click="processRestart" /> -->
|
|
|
+ <XButton preIcon="ep:refresh" @click="processRestart()" />
|
|
|
+ </el-tooltip>
|
|
|
+ </ElButtonGroup>
|
|
|
+ <XButton
|
|
|
+ preIcon="ep:plus"
|
|
|
+ title="保存模型"
|
|
|
+ @click="processSave"
|
|
|
+ :type="props.headerButtonType"
|
|
|
+ :disabled="simulationStatus"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ <!-- 用于打开本地文件-->
|
|
|
+ <input
|
|
|
+ type="file"
|
|
|
+ id="files"
|
|
|
+ ref="refFile"
|
|
|
+ style="display: none"
|
|
|
+ accept=".xml, .bpmn"
|
|
|
+ @change="importLocalFile"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="my-process-designer__container">
|
|
|
+ <div
|
|
|
+ class="my-process-designer__canvas"
|
|
|
+ ref="bpmnCanvas"
|
|
|
+ id="bpmnCanvas"
|
|
|
+ style="width: 1680px; height: 800px"
|
|
|
+ ></div>
|
|
|
+ <!-- <div id="js-properties-panel" class="panel"></div> -->
|
|
|
+ <!-- <div class="my-process-designer__canvas" ref="bpmn-canvas"></div> -->
|
|
|
+ </div>
|
|
|
+ <el-dialog
|
|
|
+ title="预览"
|
|
|
+ width="80%"
|
|
|
+ v-model="previewModelVisible"
|
|
|
+ append-to-body
|
|
|
+ destroy-on-close
|
|
|
+ >
|
|
|
+ <pre>
|
|
|
+ <!-- <code class="hljs" v-html="highlightedCode(previewType, previewResult)"></code> -->
|
|
|
+ {{ previewResult }}
|
|
|
+ </pre>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts" name="MyProcessDesigner">
|
|
|
+// import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
|
|
|
+// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'
|
|
|
+// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'
|
|
|
+// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
|
|
|
+// import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右侧框样式
|
|
|
+import { ElTooltip, ElButtonGroup, ElDialog, ElButton, ElMessage, ElMessageBox } from 'element-plus'
|
|
|
+import { computed, onBeforeMount, onBeforeUnmount, onMounted, provide, ref } from 'vue'
|
|
|
+import BpmnModeler from 'bpmn-js/lib/Modeler'
|
|
|
+import DefaultEmptyXML from './plugins/defaultEmpty'
|
|
|
+// 翻译方法
|
|
|
+import customTranslate from './plugins/translate/customTranslate'
|
|
|
+import translationsCN from './plugins/translate/zh'
|
|
|
+// 模拟流转流程
|
|
|
+import tokenSimulation from 'bpmn-js-token-simulation'
|
|
|
+// 标签解析构建器
|
|
|
+// import bpmnPropertiesProvider from "bpmn-js-properties-panel/lib/provider/bpmn";
|
|
|
+// import propertiesPanelModule from 'bpmn-js-properties-panel'
|
|
|
+// import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
|
|
|
+// 标签解析 Moddle
|
|
|
+import camundaModdleDescriptor from './plugins/descriptor/camundaDescriptor.json'
|
|
|
+import activitiModdleDescriptor from './plugins/descriptor/activitiDescriptor.json'
|
|
|
+import flowableModdleDescriptor from './plugins/descriptor/flowableDescriptor.json'
|
|
|
+// 标签解析 Extension
|
|
|
+import camundaModdleExtension from './plugins/extension-moddle/camunda'
|
|
|
+import activitiModdleExtension from './plugins/extension-moddle/activiti'
|
|
|
+import flowableModdleExtension from './plugins/extension-moddle/flowable'
|
|
|
+// 引入json转换与高亮
|
|
|
+// import xml2js from 'xml-js'
|
|
|
+import xml2js from 'fast-xml-parser'
|
|
|
+
|
|
|
+// 代码高亮插件
|
|
|
+// import hljs from 'highlight.js/lib/highlight'
|
|
|
+// import 'highlight.js/styles/github-gist.css'
|
|
|
+// hljs.registerLanguage('xml', 'highlight.js/lib/languages/xml')
|
|
|
+// hljs.registerLanguage('json', 'highlight.js/lib/languages/json')
|
|
|
+// const eventName = reactive({
|
|
|
+// name: ''
|
|
|
+// })
|
|
|
+const bpmnCanvas = ref()
|
|
|
+const refFile = ref()
|
|
|
+const emit = defineEmits([
|
|
|
+ 'destroy',
|
|
|
+ 'init-finished',
|
|
|
+ 'save',
|
|
|
+ 'commandStack-changed',
|
|
|
+ 'input',
|
|
|
+ 'change',
|
|
|
+ 'canvas-viewbox-changed',
|
|
|
+ // eventName.name
|
|
|
+ 'element-click'
|
|
|
+])
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ value: String, // xml 字符串
|
|
|
+ // valueWatch: true, // xml 字符串的 watch 状态
|
|
|
+ processId: String, // 流程 key 标识
|
|
|
+ processName: String, // 流程 name 名字
|
|
|
+ formId: Number, // 流程 form 表单编号
|
|
|
+ translations: Object, // 自定义的翻译文件
|
|
|
+ additionalModel: [Object, Array], // 自定义model
|
|
|
+ moddleExtension: Object, // 自定义moddle
|
|
|
+ onlyCustomizeAddi: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ onlyCustomizeModdle: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ simulation: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ keyboard: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ prefix: {
|
|
|
+ type: String,
|
|
|
+ default: 'camunda'
|
|
|
+ },
|
|
|
+ events: {
|
|
|
+ type: Array,
|
|
|
+ default: () => ['element.click']
|
|
|
+ },
|
|
|
+ headerButtonSize: {
|
|
|
+ type: String,
|
|
|
+ default: 'small',
|
|
|
+ validator: (value) => ['default', 'medium', 'small', 'mini'].indexOf(value) !== -1
|
|
|
+ },
|
|
|
+ headerButtonType: {
|
|
|
+ type: String,
|
|
|
+ default: 'primary',
|
|
|
+ validator: (value) =>
|
|
|
+ ['default', 'primary', 'success', 'warning', 'danger', 'info'].indexOf(value) !== -1
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+provide('configGlobal', props)
|
|
|
+let bpmnModeler = null
|
|
|
+const defaultZoom = ref(1)
|
|
|
+const previewModelVisible = ref(false)
|
|
|
+const simulationStatus = ref(false)
|
|
|
+const previewResult = ref('')
|
|
|
+const previewType = ref('xml')
|
|
|
+const recoverable = ref(false)
|
|
|
+const revocable = ref(false)
|
|
|
+const additionalModules = computed(() => {
|
|
|
+ console.log(props.additionalModel, 'additionalModel')
|
|
|
+ const Modules = []
|
|
|
+ // 仅保留用户自定义扩展模块
|
|
|
+ if (props.onlyCustomizeAddi) {
|
|
|
+ if (Object.prototype.toString.call(props.additionalModel) == '[object Array]') {
|
|
|
+ return props.additionalModel || []
|
|
|
+ }
|
|
|
+ return [props.additionalModel]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 插入用户自定义扩展模块
|
|
|
+ if (Object.prototype.toString.call(props.additionalModel) == '[object Array]') {
|
|
|
+ Modules.push(...props.additionalModel)
|
|
|
+ } else {
|
|
|
+ props.additionalModel && Modules.push(props.additionalModel)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 翻译模块
|
|
|
+ const TranslateModule = {
|
|
|
+ translate: ['value', customTranslate(props.translations || translationsCN)]
|
|
|
+ }
|
|
|
+ Modules.push(TranslateModule)
|
|
|
+
|
|
|
+ // 模拟流转模块
|
|
|
+ if (props.simulation) {
|
|
|
+ Modules.push(tokenSimulation)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据需要的流程类型设置扩展元素构建模块
|
|
|
+ // if (this.prefix === "bpmn") {
|
|
|
+ // Modules.push(bpmnModdleExtension);
|
|
|
+ // }
|
|
|
+ console.log(props.prefix, 'props.prefix ')
|
|
|
+ if (props.prefix === 'camunda') {
|
|
|
+ Modules.push(camundaModdleExtension)
|
|
|
+ }
|
|
|
+ if (props.prefix === 'flowable') {
|
|
|
+ Modules.push(flowableModdleExtension)
|
|
|
+ }
|
|
|
+ if (props.prefix === 'activiti') {
|
|
|
+ Modules.push(activitiModdleExtension)
|
|
|
+ }
|
|
|
+
|
|
|
+ return Modules
|
|
|
+})
|
|
|
+const moddleExtensions = computed(() => {
|
|
|
+ console.log(props.onlyCustomizeModdle, 'props.onlyCustomizeModdle')
|
|
|
+ console.log(props.moddleExtension, 'props.moddleExtension')
|
|
|
+ console.log(props.prefix, 'props.prefix')
|
|
|
+ const Extensions = {}
|
|
|
+ // 仅使用用户自定义模块
|
|
|
+ if (props.onlyCustomizeModdle) {
|
|
|
+ return props.moddleExtension || null
|
|
|
+ }
|
|
|
+
|
|
|
+ // 插入用户自定义模块
|
|
|
+ if (props.moddleExtension) {
|
|
|
+ for (let key in props.moddleExtension) {
|
|
|
+ Extensions[key] = props.moddleExtension[key]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据需要的 "流程类型" 设置 对应的解析文件
|
|
|
+ if (props.prefix === 'activiti') {
|
|
|
+ Extensions.activiti = activitiModdleDescriptor
|
|
|
+ }
|
|
|
+ if (props.prefix === 'flowable') {
|
|
|
+ Extensions.flowable = flowableModdleDescriptor
|
|
|
+ }
|
|
|
+ if (props.prefix === 'camunda') {
|
|
|
+ Extensions.camunda = camundaModdleDescriptor
|
|
|
+ }
|
|
|
+ return Extensions
|
|
|
+})
|
|
|
+console.log(additionalModules, 'additionalModules()')
|
|
|
+console.log(moddleExtensions, 'moddleExtensions()')
|
|
|
+const initBpmnModeler = () => {
|
|
|
+ if (bpmnModeler) return
|
|
|
+ let data = document.getElementById('bpmnCanvas')
|
|
|
+ console.log(data, 'data')
|
|
|
+ console.log(props.keyboard, 'props.keyboard')
|
|
|
+ console.log(additionalModules, 'additionalModules()')
|
|
|
+ console.log(moddleExtensions, 'moddleExtensions()')
|
|
|
+
|
|
|
+ bpmnModeler = new BpmnModeler({
|
|
|
+ // container: this.$refs['bpmn-canvas'],
|
|
|
+ // container: getCurrentInstance(),
|
|
|
+ // container: needClass,
|
|
|
+ // container: bpmnCanvas.value,
|
|
|
+ container: data,
|
|
|
+ // width: '100%',
|
|
|
+ // 添加控制板
|
|
|
+ // propertiesPanel: {
|
|
|
+ // parent: '#js-properties-panel'
|
|
|
+ // },
|
|
|
+ keyboard: props.keyboard ? { bindTo: document } : null,
|
|
|
+ // additionalModules: additionalModules.value,
|
|
|
+ additionalModules: additionalModules.value,
|
|
|
+ moddleExtensions: moddleExtensions.value
|
|
|
+
|
|
|
+ // additionalModules: [
|
|
|
+ // additionalModules.value
|
|
|
+ // propertiesPanelModule,
|
|
|
+ // propertiesProviderModule
|
|
|
+ // propertiesProviderModule
|
|
|
+ // ],
|
|
|
+ // moddleExtensions: { camunda: moddleExtensions.value }
|
|
|
+ })
|
|
|
+
|
|
|
+ // bpmnModeler.createDiagram()
|
|
|
+
|
|
|
+ console.log(bpmnModeler, 'bpmnModeler111111')
|
|
|
+ emit('init-finished', bpmnModeler)
|
|
|
+ initModelListeners()
|
|
|
+}
|
|
|
+
|
|
|
+const initModelListeners = () => {
|
|
|
+ const EventBus = bpmnModeler.get('eventBus')
|
|
|
+ console.log(EventBus, 'EventBus')
|
|
|
+ // 注册需要的监听事件, 将. 替换为 - , 避免解析异常
|
|
|
+ props.events.forEach((event) => {
|
|
|
+ EventBus.on(event, function (eventObj) {
|
|
|
+ let eventName = event.replace(/\./g, '-')
|
|
|
+ // eventName.name = eventName
|
|
|
+ let element = eventObj ? eventObj.element : null
|
|
|
+ console.log(eventName, 'eventName')
|
|
|
+ console.log(element, 'element')
|
|
|
+ emit('element-click', element, eventObj)
|
|
|
+ // emit(eventName, element, eventObj)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ // 监听图形改变返回xml
|
|
|
+ EventBus.on('commandStack.changed', async (event) => {
|
|
|
+ try {
|
|
|
+ recoverable.value = bpmnModeler.get('commandStack').canRedo()
|
|
|
+ revocable.value = bpmnModeler.get('commandStack').canUndo()
|
|
|
+ let { xml } = await bpmnModeler.saveXML({ format: true })
|
|
|
+ emit('commandStack-changed', event)
|
|
|
+ emit('input', xml)
|
|
|
+ emit('change', xml)
|
|
|
+ } catch (e) {
|
|
|
+ console.error(`[Process Designer Warn]: ${e.message || e}`)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ // 监听视图缩放变化
|
|
|
+ bpmnModeler.on('canvas.viewbox.changed', ({ viewbox }) => {
|
|
|
+ emit('canvas-viewbox-changed', { viewbox })
|
|
|
+ const { scale } = viewbox
|
|
|
+ defaultZoom.value = Math.floor(scale * 100) / 100
|
|
|
+ })
|
|
|
+}
|
|
|
+/* 创建新的流程图 */
|
|
|
+const createNewDiagram = async (xml) => {
|
|
|
+ console.log(xml, 'xml')
|
|
|
+ // 将字符串转换成图显示出来
|
|
|
+ let newId = props.processId || `Process_${new Date().getTime()}`
|
|
|
+ let newName = props.processName || `业务流程_${new Date().getTime()}`
|
|
|
+ let xmlString = xml || DefaultEmptyXML(newId, newName, props.prefix)
|
|
|
+ try {
|
|
|
+ // console.log(xmlString, 'xmlString')
|
|
|
+ // console.log(this.bpmnModeler.importXML);
|
|
|
+ let { warnings } = await bpmnModeler.importXML(xmlString)
|
|
|
+ console.log(warnings, 'warnings')
|
|
|
+ if (warnings && warnings.length) {
|
|
|
+ warnings.forEach((warn) => console.warn(warn))
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error(`[Process Designer Warn]: ${e?.message || e}`)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 下载流程图到本地
|
|
|
+const downloadProcess = async (type, name) => {
|
|
|
+ try {
|
|
|
+ // 按需要类型创建文件并下载
|
|
|
+ if (type === 'xml' || type === 'bpmn') {
|
|
|
+ const { err, xml } = await bpmnModeler.saveXML()
|
|
|
+ // 读取异常时抛出异常
|
|
|
+ if (err) {
|
|
|
+ console.error(`[Process Designer Warn ]: ${err.message || err}`)
|
|
|
+ }
|
|
|
+ let { href, filename } = setEncoded(type.toUpperCase(), name, xml)
|
|
|
+ downloadFunc(href, filename)
|
|
|
+ } else {
|
|
|
+ const { err, svg } = await bpmnModeler.saveSVG()
|
|
|
+ // 读取异常时抛出异常
|
|
|
+ if (err) {
|
|
|
+ return console.error(err)
|
|
|
+ }
|
|
|
+ let { href, filename } = setEncoded('SVG', name, svg)
|
|
|
+ downloadFunc(href, filename)
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error(`[Process Designer Warn ]: ${e.message || e}`)
|
|
|
+ }
|
|
|
+ // 文件下载方法
|
|
|
+ function downloadFunc(href, filename) {
|
|
|
+ if (href && filename) {
|
|
|
+ let a = document.createElement('a')
|
|
|
+ a.download = filename //指定下载的文件名
|
|
|
+ a.href = href // URL对象
|
|
|
+ a.click() // 模拟点击
|
|
|
+ URL.revokeObjectURL(a.href) // 释放URL 对象
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 根据所需类型进行转码并返回下载地址
|
|
|
+const setEncoded = (type, filename = 'diagram', data) => {
|
|
|
+ const encodedData = encodeURIComponent(data)
|
|
|
+ return {
|
|
|
+ filename: `${filename}.${type}`,
|
|
|
+ href: `data:application/${
|
|
|
+ type === 'svg' ? 'text/xml' : 'bpmn20-xml'
|
|
|
+ };charset=UTF-8,${encodedData}`,
|
|
|
+ data: data
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 加载本地文件
|
|
|
+const importLocalFile = () => {
|
|
|
+ const file = refFile.value.files[0]
|
|
|
+ const reader = new FileReader()
|
|
|
+ reader.readAsText(file)
|
|
|
+ reader.onload = function () {
|
|
|
+ let xmlStr = this.result
|
|
|
+ createNewDiagram(xmlStr)
|
|
|
+ }
|
|
|
+}
|
|
|
+/* ------------------------------------------------ refs methods ------------------------------------------------------ */
|
|
|
+const downloadProcessAsXml = () => {
|
|
|
+ downloadProcess('xml')
|
|
|
+}
|
|
|
+const downloadProcessAsBpmn = () => {
|
|
|
+ downloadProcess('bpmn')
|
|
|
+}
|
|
|
+const downloadProcessAsSvg = () => {
|
|
|
+ downloadProcess('svg')
|
|
|
+}
|
|
|
+const processSimulation = () => {
|
|
|
+ simulationStatus.value = !simulationStatus.value
|
|
|
+ console.log(bpmnModeler.get('toggleMode', 'strict'), "bpmnModeler.get('toggleMode')")
|
|
|
+ props.simulation && bpmnModeler.get('toggleMode', 'strict').toggleMode()
|
|
|
+}
|
|
|
+const processRedo = () => {
|
|
|
+ bpmnModeler.get('commandStack').redo()
|
|
|
+}
|
|
|
+const processUndo = () => {
|
|
|
+ bpmnModeler.get('commandStack').undo()
|
|
|
+}
|
|
|
+const processZoomIn = (zoomStep = 0.1) => {
|
|
|
+ let newZoom = Math.floor(defaultZoom.value * 100 + zoomStep * 100) / 100
|
|
|
+ if (newZoom > 4) {
|
|
|
+ throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4')
|
|
|
+ }
|
|
|
+ defaultZoom.value = newZoom
|
|
|
+ bpmnModeler.get('canvas').zoom(defaultZoom.value)
|
|
|
+}
|
|
|
+const processZoomOut = (zoomStep = 0.1) => {
|
|
|
+ let newZoom = Math.floor(defaultZoom.value * 100 - zoomStep * 100) / 100
|
|
|
+ if (newZoom < 0.2) {
|
|
|
+ throw new Error('[Process Designer Warn ]: The zoom ratio cannot be less than 0.2')
|
|
|
+ }
|
|
|
+ defaultZoom.value = newZoom
|
|
|
+ bpmnModeler.get('canvas').zoom(defaultZoom.value)
|
|
|
+}
|
|
|
+// const processZoomTo = (newZoom = 1) => {
|
|
|
+// if (newZoom < 0.2) {
|
|
|
+// throw new Error('[Process Designer Warn ]: The zoom ratio cannot be less than 0.2')
|
|
|
+// }
|
|
|
+// if (newZoom > 4) {
|
|
|
+// throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4')
|
|
|
+// }
|
|
|
+// defaultZoom = newZoom
|
|
|
+// bpmnModeler.get('canvas').zoom(newZoom)
|
|
|
+// }
|
|
|
+const processReZoom = () => {
|
|
|
+ defaultZoom.value = 1
|
|
|
+ bpmnModeler.get('canvas').zoom('fit-viewport', 'auto')
|
|
|
+}
|
|
|
+const processRestart = () => {
|
|
|
+ recoverable.value = false
|
|
|
+ revocable.value = false
|
|
|
+ createNewDiagram(null)
|
|
|
+}
|
|
|
+const elementsAlign = (align) => {
|
|
|
+ const Align = bpmnModeler.get('alignElements')
|
|
|
+ const Selection = bpmnModeler.get('selection')
|
|
|
+ const SelectedElements = Selection.get()
|
|
|
+ console.log(SelectedElements, 'SelectedElements')
|
|
|
+ if (!SelectedElements || SelectedElements.length <= 1) {
|
|
|
+ ElMessage.warning('请按住 Shift 键选择多个元素对齐')
|
|
|
+ // alert('请按住 Ctrl 键选择多个元素对齐')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ElMessageBox.confirm('自动对齐可能造成图形变形,是否继续?', '警告', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ Align.trigger(SelectedElements, align)
|
|
|
+ })
|
|
|
+}
|
|
|
+/*----------------------------- 方法结束 ---------------------------------*/
|
|
|
+const previewProcessXML = () => {
|
|
|
+ console.log(bpmnModeler.saveXML, 'bpmnModeler')
|
|
|
+ bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
|
|
|
+ console.log(xml, 'xml111111')
|
|
|
+ previewResult.value = xml
|
|
|
+ previewType.value = 'xml'
|
|
|
+ previewModelVisible.value = true
|
|
|
+ })
|
|
|
+}
|
|
|
+const previewProcessJson = () => {
|
|
|
+ bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
|
|
|
+ console.log(xml, 'xml')
|
|
|
+ const parser = new xml2js.XMLParser()
|
|
|
+ let jObj = parser.parse(xml)
|
|
|
+ // console.log(jObj, 'jObjjObjjObjjObjjObj')
|
|
|
+ // const builder = new xml2js.XMLBuilder(xml)
|
|
|
+ // const xmlContent = builder
|
|
|
+ // console.log(xmlContent, 'xmlContent')
|
|
|
+ // console.log(xml2js, 'convertconvertconvert')
|
|
|
+ previewResult.value = jObj
|
|
|
+ // previewResult.value = convert.xml2json(xml, {explicitArray : false},{ spaces: 2 })
|
|
|
+ previewType.value = 'json'
|
|
|
+ previewModelVisible.value = true
|
|
|
+ })
|
|
|
+}
|
|
|
+/* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
|
|
|
+const processSave = async () => {
|
|
|
+ const { err, xml } = await bpmnModeler.saveXML()
|
|
|
+ // 读取异常时抛出异常
|
|
|
+ if (err) {
|
|
|
+ // this.$modal.msgError('保存模型失败,请重试!')
|
|
|
+ alert('保存模型失败,请重试!')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 触发 save 事件
|
|
|
+ emit('save', xml)
|
|
|
+}
|
|
|
+/** 高亮显示 */
|
|
|
+// const highlightedCode = (previewType, previewResult) => {
|
|
|
+// console.log(previewType, 'previewType, previewResult')
|
|
|
+// console.log(previewResult, 'previewType, previewResult')
|
|
|
+// console.log(hljs.highlight, 'hljs.highlight')
|
|
|
+// const result = hljs.highlight(previewType, previewResult.value || '', true)
|
|
|
+// return result.value || ' '
|
|
|
+// }
|
|
|
+onBeforeMount(() => {
|
|
|
+ console.log(props, 'propspropspropsprops')
|
|
|
+})
|
|
|
+onMounted(() => {
|
|
|
+ initBpmnModeler()
|
|
|
+ createNewDiagram(props.value)
|
|
|
+})
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ // this.$once('hook:beforeDestroy', () => {
|
|
|
+ // })
|
|
|
+ if (bpmnModeler) bpmnModeler.destroy()
|
|
|
+ emit('destroy', bpmnModeler)
|
|
|
+ bpmnModeler = null
|
|
|
+})
|
|
|
+</script>
|