提交 | 用户 | 时间
|
820397
|
1 |
<template> |
H |
2 |
<div class="my-process-designer"> |
|
3 |
<div class="my-process-designer__header" style="z-index: 999; display: table-row-group"> |
|
4 |
<slot name="control-header"></slot> |
|
5 |
<template v-if="!$slots['control-header']"> |
|
6 |
<ElButtonGroup key="file-control"> |
|
7 |
<XButton preIcon="ep:folder-opened" title="打开文件" @click="refFile.click()" /> |
|
8 |
<el-tooltip effect="light" placement="bottom"> |
|
9 |
<template #content> |
|
10 |
<div style="color: #409eff"> |
|
11 |
<!-- <el-button link @click="downloadProcessAsXml()">下载为XML文件</el-button> --> |
|
12 |
<XTextButton title="下载为XML文件" @click="downloadProcessAsXml()" /> |
|
13 |
<br /> |
|
14 |
|
|
15 |
<!-- <el-button link @click="downloadProcessAsSvg()">下载为SVG文件</el-button> --> |
|
16 |
<XTextButton title="下载为SVG文件" @click="downloadProcessAsSvg()" /> |
|
17 |
<br /> |
|
18 |
|
|
19 |
<!-- <el-button link @click="downloadProcessAsBpmn()">下载为BPMN文件</el-button> --> |
|
20 |
<XTextButton title="下载为BPMN文件" @click="downloadProcessAsBpmn()" /> |
|
21 |
</div> |
|
22 |
</template> |
|
23 |
<XButton title="下载文件" preIcon="ep:download" /> |
|
24 |
</el-tooltip> |
|
25 |
<el-tooltip effect="light"> |
|
26 |
<XButton preIcon="ep:view" title="浏览" /> |
|
27 |
<template #content> |
|
28 |
<!-- <el-button link @click="previewProcessXML">预览XML</el-button> --> |
|
29 |
<XTextButton title="预览XML" @click="previewProcessXML" /> |
|
30 |
<br /> |
|
31 |
<!-- <el-button link @click="previewProcessJson">预览JSON</el-button> --> |
|
32 |
<XTextButton title="预览JSON" @click="previewProcessJson" /> |
|
33 |
</template> |
|
34 |
</el-tooltip> |
|
35 |
<el-tooltip |
|
36 |
v-if="props.simulation" |
|
37 |
effect="light" |
|
38 |
:content="simulationStatus ? '退出模拟' : '开启模拟'" |
|
39 |
> |
|
40 |
<XButton preIcon="ep:cpu" title="模拟" @click="processSimulation" /> |
|
41 |
</el-tooltip> |
|
42 |
</ElButtonGroup> |
|
43 |
<ElButtonGroup key="align-control"> |
|
44 |
<el-tooltip effect="light" content="向左对齐"> |
|
45 |
<!-- <el-button |
|
46 |
class="align align-left" |
|
47 |
icon="el-icon-s-data" |
|
48 |
@click="elementsAlign('left')" |
|
49 |
/> --> |
|
50 |
<XButton |
|
51 |
preIcon="fa:align-left" |
|
52 |
class="align align-bottom" |
|
53 |
@click="elementsAlign('left')" |
|
54 |
/> |
|
55 |
</el-tooltip> |
|
56 |
<el-tooltip effect="light" content="向右对齐"> |
|
57 |
<!-- <el-button |
|
58 |
class="align align-right" |
|
59 |
icon="el-icon-s-data" |
|
60 |
@click="elementsAlign('right')" |
|
61 |
/> --> |
|
62 |
<XButton |
|
63 |
preIcon="fa:align-left" |
|
64 |
class="align align-top" |
|
65 |
@click="elementsAlign('right')" |
|
66 |
/> |
|
67 |
</el-tooltip> |
|
68 |
<el-tooltip effect="light" content="向上对齐"> |
|
69 |
<!-- <el-button |
|
70 |
class="align align-top" |
|
71 |
icon="el-icon-s-data" |
|
72 |
@click="elementsAlign('top')" |
|
73 |
/> --> |
|
74 |
<XButton |
|
75 |
preIcon="fa:align-left" |
|
76 |
class="align align-left" |
|
77 |
@click="elementsAlign('top')" |
|
78 |
/> |
|
79 |
</el-tooltip> |
|
80 |
<el-tooltip effect="light" content="向下对齐"> |
|
81 |
<!-- <el-button |
|
82 |
class="align align-bottom" |
|
83 |
icon="el-icon-s-data" |
|
84 |
@click="elementsAlign('bottom')" |
|
85 |
/> --> |
|
86 |
<XButton |
|
87 |
preIcon="fa:align-left" |
|
88 |
class="align align-right" |
|
89 |
@click="elementsAlign('bottom')" |
|
90 |
/> |
|
91 |
</el-tooltip> |
|
92 |
<el-tooltip effect="light" content="水平居中"> |
|
93 |
<!-- <el-button |
|
94 |
class="align align-center" |
|
95 |
icon="el-icon-s-data" |
|
96 |
@click="elementsAlign('center')" |
|
97 |
/> --> |
|
98 |
<!-- class="align align-center" --> |
|
99 |
<XButton |
|
100 |
preIcon="fa:align-left" |
|
101 |
class="align align-center" |
|
102 |
@click="elementsAlign('center')" |
|
103 |
/> |
|
104 |
</el-tooltip> |
|
105 |
<el-tooltip effect="light" content="垂直居中"> |
|
106 |
<!-- <el-button |
|
107 |
class="align align-middle" |
|
108 |
icon="el-icon-s-data" |
|
109 |
@click="elementsAlign('middle')" |
|
110 |
/> --> |
|
111 |
<XButton |
|
112 |
preIcon="fa:align-left" |
|
113 |
class="align align-middle" |
|
114 |
@click="elementsAlign('middle')" |
|
115 |
/> |
|
116 |
</el-tooltip> |
|
117 |
</ElButtonGroup> |
|
118 |
<ElButtonGroup key="scale-control"> |
|
119 |
<el-tooltip effect="light" content="缩小视图"> |
|
120 |
<!-- <el-button |
|
121 |
:disabled="defaultZoom < 0.2" |
|
122 |
icon="el-icon-zoom-out" |
|
123 |
@click="processZoomOut()" |
|
124 |
/> --> |
|
125 |
<XButton |
|
126 |
preIcon="ep:zoom-out" |
|
127 |
@click="processZoomOut()" |
|
128 |
:disabled="defaultZoom < 0.2" |
|
129 |
/> |
|
130 |
</el-tooltip> |
|
131 |
<el-button>{{ Math.floor(defaultZoom * 10 * 10) + '%' }}</el-button> |
|
132 |
<el-tooltip effect="light" content="放大视图"> |
|
133 |
<!-- <el-button |
|
134 |
:disabled="defaultZoom > 4" |
|
135 |
icon="el-icon-zoom-in" |
|
136 |
@click="processZoomIn()" |
|
137 |
/> --> |
|
138 |
<XButton preIcon="ep:zoom-in" @click="processZoomIn()" :disabled="defaultZoom > 4" /> |
|
139 |
</el-tooltip> |
|
140 |
<el-tooltip effect="light" content="重置视图并居中"> |
|
141 |
<!-- <el-button icon="el-icon-c-scale-to-original" @click="processReZoom()" /> --> |
|
142 |
<XButton preIcon="ep:scale-to-original" @click="processReZoom()" /> |
|
143 |
</el-tooltip> |
|
144 |
</ElButtonGroup> |
|
145 |
<ElButtonGroup key="stack-control"> |
|
146 |
<el-tooltip effect="light" content="撤销"> |
|
147 |
<!-- <el-button :disabled="!revocable" icon="el-icon-refresh-left" @click="processUndo()" /> --> |
|
148 |
<XButton preIcon="ep:refresh-left" @click="processUndo()" :disabled="!revocable" /> |
|
149 |
</el-tooltip> |
|
150 |
<el-tooltip effect="light" content="恢复"> |
|
151 |
<!-- <el-button |
|
152 |
:disabled="!recoverable" |
|
153 |
icon="el-icon-refresh-right" |
|
154 |
@click="processRedo()" |
|
155 |
/> --> |
|
156 |
<XButton preIcon="ep:refresh-right" @click="processRedo()" :disabled="!recoverable" /> |
|
157 |
</el-tooltip> |
|
158 |
<el-tooltip effect="light" content="重新绘制"> |
|
159 |
<!-- <el-button icon="el-icon-refresh" @click="processRestart" /> --> |
|
160 |
<XButton preIcon="ep:refresh" @click="processRestart()" /> |
|
161 |
</el-tooltip> |
|
162 |
</ElButtonGroup> |
|
163 |
<XButton |
|
164 |
preIcon="ep:plus" |
|
165 |
title="保存模型" |
|
166 |
@click="processSave" |
|
167 |
:type="props.headerButtonType" |
|
168 |
:disabled="simulationStatus" |
|
169 |
/> |
|
170 |
</template> |
|
171 |
<!-- 用于打开本地文件--> |
|
172 |
<input |
|
173 |
type="file" |
|
174 |
id="files" |
|
175 |
ref="refFile" |
|
176 |
style="display: none" |
|
177 |
accept=".xml, .bpmn" |
|
178 |
@change="importLocalFile" |
|
179 |
/> |
|
180 |
</div> |
|
181 |
<div class="my-process-designer__container"> |
|
182 |
<div |
|
183 |
class="my-process-designer__canvas" |
|
184 |
ref="bpmnCanvas" |
|
185 |
id="bpmnCanvas" |
|
186 |
style="width: 1680px; height: 800px" |
|
187 |
></div> |
|
188 |
<!-- <div id="js-properties-panel" class="panel"></div> --> |
|
189 |
<!-- <div class="my-process-designer__canvas" ref="bpmn-canvas"></div> --> |
|
190 |
</div> |
|
191 |
<Dialog |
|
192 |
title="预览" |
|
193 |
v-model="previewModelVisible" |
|
194 |
width="80%" |
|
195 |
:scroll="true" |
|
196 |
max-height="600px" |
|
197 |
> |
|
198 |
<!-- append-to-body --> |
|
199 |
<div v-highlight> |
|
200 |
<code class="hljs"> |
|
201 |
<!-- 高亮代码块 --> |
|
202 |
{{ previewResult }} |
|
203 |
</code> |
|
204 |
</div> |
|
205 |
</Dialog> |
|
206 |
</div> |
|
207 |
</template> |
|
208 |
|
|
209 |
<script lang="ts" setup> |
|
210 |
// import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式 |
|
211 |
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css' |
|
212 |
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css' |
|
213 |
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css' |
|
214 |
// import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右侧框样式 |
|
215 |
import { ElMessage, ElMessageBox } from 'element-plus' |
|
216 |
import BpmnModeler from 'bpmn-js/lib/Modeler' |
|
217 |
import DefaultEmptyXML from './plugins/defaultEmpty' |
|
218 |
// 翻译方法 |
|
219 |
import customTranslate from './plugins/translate/customTranslate' |
|
220 |
import translationsCN from './plugins/translate/zh' |
|
221 |
// 模拟流转流程 |
|
222 |
import tokenSimulation from 'bpmn-js-token-simulation' |
|
223 |
// 标签解析构建器 |
|
224 |
// import bpmnPropertiesProvider from "bpmn-js-properties-panel/lib/provider/bpmn"; |
|
225 |
// import propertiesPanelModule from 'bpmn-js-properties-panel' |
|
226 |
// import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda' |
|
227 |
// 标签解析 Moddle |
|
228 |
import camundaModdleDescriptor from './plugins/descriptor/camundaDescriptor.json' |
|
229 |
import activitiModdleDescriptor from './plugins/descriptor/activitiDescriptor.json' |
|
230 |
import flowableModdleDescriptor from './plugins/descriptor/flowableDescriptor.json' |
|
231 |
// 标签解析 Extension |
|
232 |
import camundaModdleExtension from './plugins/extension-moddle/camunda' |
|
233 |
import activitiModdleExtension from './plugins/extension-moddle/activiti' |
|
234 |
import flowableModdleExtension from './plugins/extension-moddle/flowable' |
|
235 |
// 引入json转换与高亮 |
|
236 |
// import xml2js from 'xml-js' |
|
237 |
// import xml2js from 'fast-xml-parser' |
|
238 |
import { XmlNode, XmlNodeType, parseXmlString } from 'steady-xml' |
|
239 |
// 代码高亮插件 |
|
240 |
// import hljs from 'highlight.js/lib/highlight' |
|
241 |
// import 'highlight.js/styles/github-gist.css' |
|
242 |
// hljs.registerLanguage('xml', 'highlight.js/lib/languages/xml') |
|
243 |
// hljs.registerLanguage('json', 'highlight.js/lib/languages/json') |
|
244 |
// const eventName = reactive({ |
|
245 |
// name: '' |
|
246 |
// }) |
|
247 |
|
|
248 |
defineOptions({ name: 'MyProcessDesigner' }) |
|
249 |
|
|
250 |
const bpmnCanvas = ref() |
|
251 |
const refFile = ref() |
|
252 |
const emit = defineEmits([ |
|
253 |
'destroy', |
|
254 |
'init-finished', |
|
255 |
'save', |
|
256 |
'commandStack-changed', |
|
257 |
'input', |
|
258 |
'change', |
|
259 |
'canvas-viewbox-changed', |
|
260 |
// eventName.name |
|
261 |
'element-click' |
|
262 |
]) |
|
263 |
|
|
264 |
const props = defineProps({ |
|
265 |
value: String, // xml 字符串 |
|
266 |
// valueWatch: true, // xml 字符串的 watch 状态 |
|
267 |
processId: String, // 流程 key 标识 |
|
268 |
processName: String, // 流程 name 名字 |
|
269 |
formId: Number, // 流程 form 表单编号 |
|
270 |
translations: { |
|
271 |
// 自定义的翻译文件 |
|
272 |
type: Object, |
|
273 |
default: () => {} |
|
274 |
}, |
|
275 |
additionalModel: [Object, Array], // 自定义model |
|
276 |
moddleExtension: { |
|
277 |
// 自定义moddle |
|
278 |
type: Object, |
|
279 |
default: () => {} |
|
280 |
}, |
|
281 |
onlyCustomizeAddi: { |
|
282 |
type: Boolean, |
|
283 |
default: false |
|
284 |
}, |
|
285 |
onlyCustomizeModdle: { |
|
286 |
type: Boolean, |
|
287 |
default: false |
|
288 |
}, |
|
289 |
simulation: { |
|
290 |
type: Boolean, |
|
291 |
default: true |
|
292 |
}, |
|
293 |
keyboard: { |
|
294 |
type: Boolean, |
|
295 |
default: true |
|
296 |
}, |
|
297 |
prefix: { |
|
298 |
type: String, |
|
299 |
default: 'camunda' |
|
300 |
}, |
|
301 |
events: { |
|
302 |
type: Array, |
|
303 |
default: () => ['element.click'] |
|
304 |
}, |
|
305 |
headerButtonSize: { |
|
306 |
type: String, |
|
307 |
default: 'small', |
|
308 |
validator: (value: string) => ['default', 'medium', 'small', 'mini'].indexOf(value) !== -1 |
|
309 |
}, |
|
310 |
headerButtonType: { |
|
311 |
type: String, |
|
312 |
default: 'primary', |
|
313 |
validator: (value: string) => |
|
314 |
['default', 'primary', 'success', 'warning', 'danger', 'info'].indexOf(value) !== -1 |
|
315 |
} |
|
316 |
}) |
|
317 |
|
|
318 |
provide('configGlobal', props) |
|
319 |
let bpmnModeler: any = null |
|
320 |
const defaultZoom = ref(1) |
|
321 |
const previewModelVisible = ref(false) |
|
322 |
const simulationStatus = ref(false) |
|
323 |
const previewResult = ref('') |
|
324 |
const previewType = ref('xml') |
|
325 |
const recoverable = ref(false) |
|
326 |
const revocable = ref(false) |
|
327 |
const additionalModules = computed(() => { |
|
328 |
console.log(props.additionalModel, 'additionalModel') |
|
329 |
const Modules: any[] = [] |
|
330 |
// 仅保留用户自定义扩展模块 |
|
331 |
if (props.onlyCustomizeAddi) { |
|
332 |
if (Object.prototype.toString.call(props.additionalModel) == '[object Array]') { |
|
333 |
return props.additionalModel || [] |
|
334 |
} |
|
335 |
return [props.additionalModel] |
|
336 |
} |
|
337 |
|
|
338 |
// 插入用户自定义扩展模块 |
|
339 |
if (Object.prototype.toString.call(props.additionalModel) == '[object Array]') { |
|
340 |
Modules.push(...(props.additionalModel as any[])) |
|
341 |
} else { |
|
342 |
props.additionalModel && Modules.push(props.additionalModel) |
|
343 |
} |
|
344 |
|
|
345 |
// 翻译模块 |
|
346 |
const TranslateModule = { |
|
347 |
translate: ['value', customTranslate(props.translations || translationsCN)] |
|
348 |
} |
|
349 |
Modules.push(TranslateModule) |
|
350 |
|
|
351 |
// 模拟流转模块 |
|
352 |
if (props.simulation) { |
|
353 |
Modules.push(tokenSimulation) |
|
354 |
} |
|
355 |
|
|
356 |
// 根据需要的流程类型设置扩展元素构建模块 |
|
357 |
// if (this.prefix === "bpmn") { |
|
358 |
// Modules.push(bpmnModdleExtension); |
|
359 |
// } |
|
360 |
console.log(props.prefix, 'props.prefix ') |
|
361 |
if (props.prefix === 'camunda') { |
|
362 |
Modules.push(camundaModdleExtension) |
|
363 |
} |
|
364 |
if (props.prefix === 'flowable') { |
|
365 |
Modules.push(flowableModdleExtension) |
|
366 |
} |
|
367 |
if (props.prefix === 'activiti') { |
|
368 |
Modules.push(activitiModdleExtension) |
|
369 |
} |
|
370 |
|
|
371 |
return Modules |
|
372 |
}) |
|
373 |
const moddleExtensions = computed(() => { |
|
374 |
console.log(props.onlyCustomizeModdle, 'props.onlyCustomizeModdle') |
|
375 |
console.log(props.moddleExtension, 'props.moddleExtension') |
|
376 |
console.log(props.prefix, 'props.prefix') |
|
377 |
const Extensions: any = {} |
|
378 |
// 仅使用用户自定义模块 |
|
379 |
if (props.onlyCustomizeModdle) { |
|
380 |
return props.moddleExtension || null |
|
381 |
} |
|
382 |
|
|
383 |
// 插入用户自定义模块 |
|
384 |
if (props.moddleExtension) { |
|
385 |
for (let key in props.moddleExtension) { |
|
386 |
Extensions[key] = props.moddleExtension[key] |
|
387 |
} |
|
388 |
} |
|
389 |
|
|
390 |
// 根据需要的 "流程类型" 设置 对应的解析文件 |
|
391 |
if (props.prefix === 'activiti') { |
|
392 |
Extensions.activiti = activitiModdleDescriptor |
|
393 |
} |
|
394 |
if (props.prefix === 'flowable') { |
|
395 |
Extensions.flowable = flowableModdleDescriptor |
|
396 |
} |
|
397 |
if (props.prefix === 'camunda') { |
|
398 |
Extensions.camunda = camundaModdleDescriptor |
|
399 |
} |
|
400 |
return Extensions |
|
401 |
}) |
|
402 |
console.log(additionalModules, 'additionalModules()') |
|
403 |
console.log(moddleExtensions, 'moddleExtensions()') |
|
404 |
const initBpmnModeler = () => { |
|
405 |
if (bpmnModeler) return |
|
406 |
let data = document.getElementById('bpmnCanvas') |
|
407 |
console.log(data, 'data') |
|
408 |
console.log(props.keyboard, 'props.keyboard') |
|
409 |
console.log(additionalModules, 'additionalModules()') |
|
410 |
console.log(moddleExtensions, 'moddleExtensions()') |
|
411 |
|
|
412 |
bpmnModeler = new BpmnModeler({ |
|
413 |
// container: this.$refs['bpmn-canvas'], |
|
414 |
// container: getCurrentInstance(), |
|
415 |
// container: needClass, |
|
416 |
// container: bpmnCanvas.value, |
|
417 |
container: data, |
|
418 |
// width: '100%', |
|
419 |
// 添加控制板 |
|
420 |
// propertiesPanel: { |
|
421 |
// parent: '#js-properties-panel' |
|
422 |
// }, |
|
423 |
keyboard: props.keyboard ? { bindTo: document } : null, |
|
424 |
// additionalModules: additionalModules.value, |
|
425 |
additionalModules: additionalModules.value, |
|
426 |
moddleExtensions: moddleExtensions.value |
|
427 |
|
|
428 |
// additionalModules: [ |
|
429 |
// additionalModules.value |
|
430 |
// propertiesPanelModule, |
|
431 |
// propertiesProviderModule |
|
432 |
// propertiesProviderModule |
|
433 |
// ], |
|
434 |
// moddleExtensions: { camunda: moddleExtensions.value } |
|
435 |
}) |
|
436 |
|
|
437 |
// bpmnModeler.createDiagram() |
|
438 |
|
|
439 |
// console.log(bpmnModeler, 'bpmnModeler111111') |
|
440 |
emit('init-finished', bpmnModeler) |
|
441 |
initModelListeners() |
|
442 |
} |
|
443 |
|
|
444 |
const initModelListeners = () => { |
|
445 |
const EventBus = bpmnModeler.get('eventBus') |
|
446 |
console.log(EventBus, 'EventBus') |
|
447 |
// 注册需要的监听事件, 将. 替换为 - , 避免解析异常 |
|
448 |
props.events.forEach((event: any) => { |
|
449 |
EventBus.on(event, function (eventObj) { |
|
450 |
let eventName = event.replace(/\./g, '-') |
|
451 |
// eventName.name = eventName |
|
452 |
let element = eventObj ? eventObj.element : null |
|
453 |
console.log(eventName, 'eventName') |
|
454 |
console.log(element, 'element') |
|
455 |
emit('element-click', element, eventObj) |
|
456 |
// emit(eventName, element, eventObj) |
|
457 |
}) |
|
458 |
}) |
|
459 |
// 监听图形改变返回xml |
|
460 |
EventBus.on('commandStack.changed', async (event) => { |
|
461 |
try { |
|
462 |
recoverable.value = bpmnModeler.get('commandStack').canRedo() |
|
463 |
revocable.value = bpmnModeler.get('commandStack').canUndo() |
|
464 |
let { xml } = await bpmnModeler.saveXML({ format: true }) |
|
465 |
emit('commandStack-changed', event) |
|
466 |
emit('input', xml) |
|
467 |
emit('change', xml) |
|
468 |
} catch (e: any) { |
|
469 |
console.error(`[Process Designer Warn]: ${e.message || e}`) |
|
470 |
} |
|
471 |
}) |
|
472 |
// 监听视图缩放变化 |
|
473 |
bpmnModeler.on('canvas.viewbox.changed', ({ viewbox }) => { |
|
474 |
emit('canvas-viewbox-changed', { viewbox }) |
|
475 |
const { scale } = viewbox |
|
476 |
defaultZoom.value = Math.floor(scale * 100) / 100 |
|
477 |
}) |
|
478 |
} |
|
479 |
/* 创建新的流程图 */ |
|
480 |
const createNewDiagram = async (xml) => { |
|
481 |
console.log(xml, 'xml') |
|
482 |
// 将字符串转换成图显示出来 |
|
483 |
let newId = props.processId || `Process_${new Date().getTime()}` |
|
484 |
let newName = props.processName || `业务流程_${new Date().getTime()}` |
|
485 |
let xmlString = xml || DefaultEmptyXML(newId, newName, props.prefix) |
|
486 |
try { |
|
487 |
// console.log(xmlString, 'xmlString') |
|
488 |
// console.log(this.bpmnModeler.importXML); |
|
489 |
let { warnings } = await bpmnModeler.importXML(xmlString) |
|
490 |
console.log(warnings, 'warnings') |
|
491 |
if (warnings && warnings.length) { |
|
492 |
warnings.forEach((warn) => console.warn(warn)) |
|
493 |
} |
|
494 |
} catch (e: any) { |
|
495 |
console.error(`[Process Designer Warn]: ${e.message || e}`) |
|
496 |
} |
|
497 |
} |
|
498 |
|
|
499 |
// 下载流程图到本地 |
|
500 |
const downloadProcess = async (type) => { |
|
501 |
try { |
|
502 |
// 按需要类型创建文件并下载 |
|
503 |
if (type === 'xml' || type === 'bpmn') { |
|
504 |
const { err, xml } = await bpmnModeler.saveXML() |
|
505 |
// 读取异常时抛出异常 |
|
506 |
if (err) { |
|
507 |
console.error(`[Process Designer Warn ]: ${err.message || err}`) |
|
508 |
} |
|
509 |
let { href, filename } = setEncoded(type.toUpperCase(), xml) |
|
510 |
downloadFunc(href, filename) |
|
511 |
} else { |
|
512 |
const { err, svg } = await bpmnModeler.saveSVG() |
|
513 |
// 读取异常时抛出异常 |
|
514 |
if (err) { |
|
515 |
return console.error(err) |
|
516 |
} |
|
517 |
let { href, filename } = setEncoded('SVG', svg) |
|
518 |
downloadFunc(href, filename) |
|
519 |
} |
|
520 |
} catch (e: any) { |
|
521 |
console.error(`[Process Designer Warn ]: ${e.message || e}`) |
|
522 |
} |
|
523 |
// 文件下载方法 |
|
524 |
function downloadFunc(href, filename) { |
|
525 |
if (href && filename) { |
|
526 |
let a = document.createElement('a') |
|
527 |
a.download = filename //指定下载的文件名 |
|
528 |
a.href = href // URL对象 |
|
529 |
a.click() // 模拟点击 |
|
530 |
URL.revokeObjectURL(a.href) // 释放URL 对象 |
|
531 |
} |
|
532 |
} |
|
533 |
} |
|
534 |
|
|
535 |
// 根据所需类型进行转码并返回下载地址 |
|
536 |
const setEncoded = (type, data) => { |
|
537 |
const filename = 'diagram' |
|
538 |
const encodedData = encodeURIComponent(data) |
|
539 |
return { |
|
540 |
filename: `${filename}.${type}`, |
|
541 |
href: `data:application/${ |
|
542 |
type === 'svg' ? 'text/xml' : 'bpmn20-xml' |
|
543 |
};charset=UTF-8,${encodedData}`, |
|
544 |
data: data |
|
545 |
} |
|
546 |
} |
|
547 |
|
|
548 |
// 加载本地文件 |
|
549 |
const importLocalFile = () => { |
|
550 |
const file = refFile.value.files[0] |
|
551 |
const reader = new FileReader() |
|
552 |
reader.readAsText(file) |
|
553 |
reader.onload = function () { |
|
554 |
let xmlStr = this.result |
|
555 |
createNewDiagram(xmlStr) |
|
556 |
} |
|
557 |
} |
|
558 |
/* ------------------------------------------------ refs methods ------------------------------------------------------ */ |
|
559 |
const downloadProcessAsXml = () => { |
|
560 |
downloadProcess('xml') |
|
561 |
} |
|
562 |
const downloadProcessAsBpmn = () => { |
|
563 |
downloadProcess('bpmn') |
|
564 |
} |
|
565 |
const downloadProcessAsSvg = () => { |
|
566 |
downloadProcess('svg') |
|
567 |
} |
|
568 |
const processSimulation = () => { |
|
569 |
simulationStatus.value = !simulationStatus.value |
|
570 |
console.log(bpmnModeler.get('toggleMode', 'strict'), "bpmnModeler.get('toggleMode')") |
|
571 |
props.simulation && bpmnModeler.get('toggleMode', 'strict').toggleMode() |
|
572 |
} |
|
573 |
const processRedo = () => { |
|
574 |
bpmnModeler.get('commandStack').redo() |
|
575 |
} |
|
576 |
const processUndo = () => { |
|
577 |
bpmnModeler.get('commandStack').undo() |
|
578 |
} |
|
579 |
const processZoomIn = (zoomStep = 0.1) => { |
|
580 |
let newZoom = Math.floor(defaultZoom.value * 100 + zoomStep * 100) / 100 |
|
581 |
if (newZoom > 4) { |
|
582 |
throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4') |
|
583 |
} |
|
584 |
defaultZoom.value = newZoom |
|
585 |
bpmnModeler.get('canvas').zoom(defaultZoom.value) |
|
586 |
} |
|
587 |
const processZoomOut = (zoomStep = 0.1) => { |
|
588 |
let newZoom = Math.floor(defaultZoom.value * 100 - zoomStep * 100) / 100 |
|
589 |
if (newZoom < 0.2) { |
|
590 |
throw new Error('[Process Designer Warn ]: The zoom ratio cannot be less than 0.2') |
|
591 |
} |
|
592 |
defaultZoom.value = newZoom |
|
593 |
bpmnModeler.get('canvas').zoom(defaultZoom.value) |
|
594 |
} |
|
595 |
// const processZoomTo = (newZoom = 1) => { |
|
596 |
// if (newZoom < 0.2) { |
|
597 |
// throw new Error('[Process Designer Warn ]: The zoom ratio cannot be less than 0.2') |
|
598 |
// } |
|
599 |
// if (newZoom > 4) { |
|
600 |
// throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4') |
|
601 |
// } |
|
602 |
// defaultZoom = newZoom |
|
603 |
// bpmnModeler.get('canvas').zoom(newZoom) |
|
604 |
// } |
|
605 |
const processReZoom = () => { |
|
606 |
defaultZoom.value = 1 |
|
607 |
bpmnModeler.get('canvas').zoom('fit-viewport', 'auto') |
|
608 |
} |
|
609 |
const processRestart = () => { |
|
610 |
recoverable.value = false |
|
611 |
revocable.value = false |
|
612 |
createNewDiagram(null) |
|
613 |
} |
|
614 |
const elementsAlign = (align) => { |
|
615 |
const Align = bpmnModeler.get('alignElements') |
|
616 |
const Selection = bpmnModeler.get('selection') |
|
617 |
const SelectedElements = Selection.get() |
|
618 |
if (!SelectedElements || SelectedElements.length <= 1) { |
|
619 |
ElMessage.warning('请按住 Shift 键选择多个元素对齐') |
|
620 |
// alert('请按住 Ctrl 键选择多个元素对齐 |
|
621 |
return |
|
622 |
} |
|
623 |
ElMessageBox.confirm('自动对齐可能造成图形变形,是否继续?', '警告', { |
|
624 |
confirmButtonText: '确定', |
|
625 |
cancelButtonText: '取消', |
|
626 |
type: 'warning' |
|
627 |
}).then(() => { |
|
628 |
Align.trigger(SelectedElements, align) |
|
629 |
}) |
|
630 |
} |
|
631 |
/*----------------------------- 方法结束 ---------------------------------*/ |
|
632 |
const previewProcessXML = () => { |
|
633 |
console.log(bpmnModeler.saveXML, 'bpmnModeler') |
|
634 |
bpmnModeler.saveXML({ format: true }).then(({ xml }) => { |
|
635 |
// console.log(xml, 'xml111111') |
|
636 |
previewResult.value = xml |
|
637 |
previewType.value = 'xml' |
|
638 |
previewModelVisible.value = true |
|
639 |
}) |
|
640 |
} |
|
641 |
const previewProcessJson = () => { |
|
642 |
bpmnModeler.saveXML({ format: true }).then(({ xml }) => { |
|
643 |
// console.log(xml, 'xml') |
|
644 |
|
|
645 |
// const rootNode = parseXmlString(xml) |
|
646 |
// console.log(rootNode, 'rootNoderootNode') |
|
647 |
const rootNodes = new XmlNode(XmlNodeType.Root, parseXmlString(xml)) |
|
648 |
// console.log(rootNodes, 'rootNodesrootNodesrootNodes') |
|
649 |
// console.log(rootNodes.parent.toJsObject(), 'rootNodes.toJSON()') |
|
650 |
// console.log(JSON.stringify(rootNodes.parent.toJsObject()), 'rootNodes.toJSON()') |
|
651 |
// console.log(JSON.stringify(rootNodes.parent.toJSON()), 'rootNodes.toJSON()') |
|
652 |
|
|
653 |
// const parser = new xml2js.XMLParser() |
|
654 |
// let jObj = parser.parse(xml) |
|
655 |
// console.log(jObj, 'jObjjObjjObjjObjjObj') |
|
656 |
// const builder = new xml2js.XMLBuilder(xml) |
|
657 |
// const xmlContent = builder |
|
658 |
// console.log(xmlContent, 'xmlContent') |
|
659 |
// console.log(xml2js, 'convertconvertconvert') |
|
660 |
previewResult.value = rootNodes.parent?.toJSON() as unknown as string |
|
661 |
// previewResult.value = jObj |
|
662 |
// previewResult.value = convert.xml2json(xml, {explicitArray : false},{ spaces: 2 }) |
|
663 |
previewType.value = 'json' |
|
664 |
previewModelVisible.value = true |
|
665 |
}) |
|
666 |
} |
3e359e
|
667 |
/* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */ |
820397
|
668 |
const processSave = async () => { |
H |
669 |
// console.log(bpmnModeler, 'bpmnModelerbpmnModelerbpmnModelerbpmnModeler') |
|
670 |
const { err, xml } = await bpmnModeler.saveXML() |
|
671 |
// console.log(err, 'errerrerrerrerr') |
|
672 |
// console.log(xml, 'xmlxmlxmlxmlxml') |
|
673 |
// 读取异常时抛出异常 |
|
674 |
if (err) { |
|
675 |
// this.$modal.msgError('保存模型失败,请重试!') |
|
676 |
alert('保存模型失败,请重试!') |
|
677 |
return |
|
678 |
} |
|
679 |
// 触发 save 事件 |
|
680 |
emit('save', xml) |
|
681 |
} |
|
682 |
/** 高亮显示 */ |
|
683 |
// const highlightedCode = (previewType, previewResult) => { |
|
684 |
// console.log(previewType, 'previewType, previewResult') |
|
685 |
// console.log(previewResult, 'previewType, previewResult') |
|
686 |
// console.log(hljs.highlight, 'hljs.highlight') |
|
687 |
// const result = hljs.highlight(previewType, previewResult.value || '', true) |
|
688 |
// return result.value || ' ' |
|
689 |
// } |
|
690 |
onBeforeMount(() => { |
|
691 |
console.log(props, 'propspropspropsprops') |
|
692 |
}) |
|
693 |
onMounted(() => { |
|
694 |
initBpmnModeler() |
|
695 |
createNewDiagram(props.value) |
|
696 |
}) |
|
697 |
onBeforeUnmount(() => { |
|
698 |
// this.$once('hook:beforeDestroy', () => { |
|
699 |
// }) |
|
700 |
if (bpmnModeler) bpmnModeler.destroy() |
|
701 |
emit('destroy', bpmnModeler) |
|
702 |
bpmnModeler = null |
|
703 |
}) |
|
704 |
</script> |