refactor: 重构 bpmnProcessDesigner designer 模块 ele => antd
This commit is contained in:
@@ -55,6 +55,7 @@
|
|||||||
"diagram-js": "^12.8.1",
|
"diagram-js": "^12.8.1",
|
||||||
"fast-xml-parser": "^4.5.3",
|
"fast-xml-parser": "^4.5.3",
|
||||||
"highlight.js": "catalog:",
|
"highlight.js": "catalog:",
|
||||||
|
"min-dash": "^4.2.3",
|
||||||
"pinia": "catalog:",
|
"pinia": "catalog:",
|
||||||
"steady-xml": "^0.1.0",
|
"steady-xml": "^0.1.0",
|
||||||
"tinymce": "catalog:",
|
"tinymce": "catalog:",
|
||||||
|
|||||||
@@ -1,258 +1,39 @@
|
|||||||
<template>
|
|
||||||
<div class="my-process-designer">
|
|
||||||
<div
|
|
||||||
class="my-process-designer__header"
|
|
||||||
style="z-index: 999; display: table-row-group"
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
<div style="color: #409eff">
|
|
||||||
<!-- <el-button link @click="downloadProcessAsXml()">下载为XML文件</el-button> -->
|
|
||||||
<XTextButton
|
|
||||||
title="下载为XML文件"
|
|
||||||
@click="downloadProcessAsXml()"
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<!-- <el-button link @click="downloadProcessAsSvg()">下载为SVG文件</el-button> -->
|
|
||||||
<XTextButton
|
|
||||||
title="下载为SVG文件"
|
|
||||||
@click="downloadProcessAsSvg()"
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<!-- <el-button link @click="downloadProcessAsBpmn()">下载为BPMN文件</el-button> -->
|
|
||||||
<XTextButton
|
|
||||||
title="下载为BPMN文件"
|
|
||||||
@click="downloadProcessAsBpmn()"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<XButton title="下载文件" preIcon="ep:download" />
|
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip effect="light">
|
|
||||||
<XButton preIcon="ep:view" title="浏览" />
|
|
||||||
<template #content>
|
|
||||||
<!-- <el-button link @click="previewProcessXML">预览XML</el-button> -->
|
|
||||||
<XTextButton title="预览XML" @click="previewProcessXML" />
|
|
||||||
<br />
|
|
||||||
<!-- <el-button link @click="previewProcessJson">预览JSON</el-button> -->
|
|
||||||
<XTextButton title="预览JSON" @click="previewProcessJson" />
|
|
||||||
</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>
|
|
||||||
</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>
|
|
||||||
<Dialog
|
|
||||||
title="预览"
|
|
||||||
v-model="previewModelVisible"
|
|
||||||
width="80%"
|
|
||||||
:scroll="true"
|
|
||||||
max-height="600px"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<pre><code v-dompurify-html="highlightedCode(previewResult)" class="hljs"></code></pre>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
|
// 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.css'
|
||||||
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.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/dist/assets/bpmn-font/css/bpmn-embedded.css'
|
||||||
// import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右侧框样式
|
// import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右侧框样式
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import {
|
||||||
import BpmnModeler from 'bpmn-js/lib/Modeler';
|
computed,
|
||||||
import DefaultEmptyXML from './plugins/defaultEmpty';
|
defineEmits,
|
||||||
// 翻译方法
|
defineOptions,
|
||||||
import customTranslate from './plugins/translate/customTranslate';
|
defineProps,
|
||||||
import translationsCN from './plugins/translate/zh';
|
onBeforeUnmount,
|
||||||
|
onMounted,
|
||||||
|
provide,
|
||||||
|
ref,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FolderOpenOutlined,
|
||||||
|
DownloadOutlined,
|
||||||
|
EyeOutlined,
|
||||||
|
ApiOutlined,
|
||||||
|
ZoomInOutlined,
|
||||||
|
ZoomOutOutlined,
|
||||||
|
UndoOutlined,
|
||||||
|
RedoOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
AlignLeftOutlined,
|
||||||
|
WarningOutlined,
|
||||||
|
} from '@vben/icons';
|
||||||
|
|
||||||
|
import { Button, ButtonGroup, Tooltip, Modal, message } from 'ant-design-vue';
|
||||||
// 模拟流转流程
|
// 模拟流转流程
|
||||||
|
// @ts-ignore
|
||||||
import tokenSimulation from 'bpmn-js-token-simulation';
|
import tokenSimulation from 'bpmn-js-token-simulation';
|
||||||
// 标签解析构建器
|
import BpmnModeler from 'bpmn-js/lib/Modeler';
|
||||||
// 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 { XmlNode, XmlNodeType, parseXmlString } from 'steady-xml';
|
|
||||||
// 代码高亮插件
|
// 代码高亮插件
|
||||||
// import hljs from 'highlight.js/lib/highlight'
|
// import hljs from 'highlight.js/lib/highlight'
|
||||||
// import 'highlight.js/styles/github-gist.css'
|
// import 'highlight.js/styles/github-gist.css'
|
||||||
@@ -262,6 +43,28 @@ import { XmlNode, XmlNodeType, parseXmlString } from 'steady-xml';
|
|||||||
// name: ''
|
// name: ''
|
||||||
// })
|
// })
|
||||||
import hljs from 'highlight.js'; // 导入代码高亮文件
|
import hljs from 'highlight.js'; // 导入代码高亮文件
|
||||||
|
// 引入json转换与高亮
|
||||||
|
// import xml2js from 'xml-js'
|
||||||
|
// import xml2js from 'fast-xml-parser'
|
||||||
|
import { parseXmlString, XmlNode } from 'steady-xml';
|
||||||
|
|
||||||
|
import DefaultEmptyXML from './plugins/defaultEmpty';
|
||||||
|
import activitiModdleDescriptor from './plugins/descriptor/activitiDescriptor.json';
|
||||||
|
// 标签解析构建器
|
||||||
|
// 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 flowableModdleDescriptor from './plugins/descriptor/flowableDescriptor.json';
|
||||||
|
import activitiModdleExtension from './plugins/extension-moddle/activiti';
|
||||||
|
// 标签解析 Extension
|
||||||
|
import camundaModdleExtension from './plugins/extension-moddle/camunda';
|
||||||
|
import flowableModdleExtension from './plugins/extension-moddle/flowable';
|
||||||
|
// 翻译方法
|
||||||
|
import customTranslate from './plugins/translate/customTranslate';
|
||||||
|
import translationsCN from './plugins/translate/zh';
|
||||||
|
|
||||||
import 'highlight.js/styles/github.css'; // 导入代码高亮样式
|
import 'highlight.js/styles/github.css'; // 导入代码高亮样式
|
||||||
|
|
||||||
defineOptions({ name: 'MyProcessDesigner' });
|
defineOptions({ name: 'MyProcessDesigner' });
|
||||||
@@ -291,6 +94,7 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
|
// eslint-disable-next-line vue/require-default-prop
|
||||||
additionalModel: [Object, Array], // 自定义model
|
additionalModel: [Object, Array], // 自定义model
|
||||||
moddleExtension: {
|
moddleExtension: {
|
||||||
// 自定义moddle
|
// 自定义moddle
|
||||||
@@ -376,7 +180,7 @@ const additionalModules = computed(() => {
|
|||||||
|
|
||||||
// 插入用户自定义扩展模块
|
// 插入用户自定义扩展模块
|
||||||
if (
|
if (
|
||||||
Object.prototype.toString.call(props.additionalModel) == '[object Array]'
|
Object.prototype.toString.call(props.additionalModel) === '[object Array]'
|
||||||
) {
|
) {
|
||||||
Modules.push(...(props.additionalModel as any[]));
|
Modules.push(...(props.additionalModel as any[]));
|
||||||
} else {
|
} else {
|
||||||
@@ -444,7 +248,7 @@ console.log(additionalModules, 'additionalModules()');
|
|||||||
console.log(moddleExtensions, 'moddleExtensions()');
|
console.log(moddleExtensions, 'moddleExtensions()');
|
||||||
const initBpmnModeler = () => {
|
const initBpmnModeler = () => {
|
||||||
if (bpmnModeler) return;
|
if (bpmnModeler) return;
|
||||||
let data = document.getElementById('bpmnCanvas');
|
const data: any = document.querySelector('#bpmnCanvas');
|
||||||
console.log(data, 'data');
|
console.log(data, 'data');
|
||||||
console.log(props.keyboard, 'props.keyboard');
|
console.log(props.keyboard, 'props.keyboard');
|
||||||
console.log(additionalModules, 'additionalModules()');
|
console.log(additionalModules, 'additionalModules()');
|
||||||
@@ -463,7 +267,7 @@ const initBpmnModeler = () => {
|
|||||||
// },
|
// },
|
||||||
keyboard: props.keyboard ? { bindTo: document } : null,
|
keyboard: props.keyboard ? { bindTo: document } : null,
|
||||||
// additionalModules: additionalModules.value,
|
// additionalModules: additionalModules.value,
|
||||||
additionalModules: additionalModules.value,
|
additionalModules: additionalModules.value as any[],
|
||||||
moddleExtensions: moddleExtensions.value,
|
moddleExtensions: moddleExtensions.value,
|
||||||
|
|
||||||
// additionalModules: [
|
// additionalModules: [
|
||||||
@@ -478,6 +282,7 @@ const initBpmnModeler = () => {
|
|||||||
// bpmnModeler.createDiagram()
|
// bpmnModeler.createDiagram()
|
||||||
|
|
||||||
// console.log(bpmnModeler, 'bpmnModeler111111')
|
// console.log(bpmnModeler, 'bpmnModeler111111')
|
||||||
|
// eslint-disable-next-line vue/custom-event-name-casing
|
||||||
emit('init-finished', bpmnModeler);
|
emit('init-finished', bpmnModeler);
|
||||||
initModelListeners();
|
initModelListeners();
|
||||||
};
|
};
|
||||||
@@ -487,10 +292,10 @@ const initModelListeners = () => {
|
|||||||
console.log(EventBus, 'EventBus');
|
console.log(EventBus, 'EventBus');
|
||||||
// 注册需要的监听事件, 将. 替换为 - , 避免解析异常
|
// 注册需要的监听事件, 将. 替换为 - , 避免解析异常
|
||||||
props.events.forEach((event: any) => {
|
props.events.forEach((event: any) => {
|
||||||
EventBus.on(event, function (eventObj) {
|
EventBus.on(event, (eventObj: any) => {
|
||||||
let eventName = event.replace(/\./g, '-');
|
const eventName = event.replaceAll('.', '-');
|
||||||
// eventName.name = eventName
|
// eventName.name = eventName
|
||||||
let element = eventObj ? eventObj.element : null;
|
const element = eventObj ? eventObj.element : null;
|
||||||
console.log(eventName, 'eventName');
|
console.log(eventName, 'eventName');
|
||||||
console.log(element, 'element');
|
console.log(element, 'element');
|
||||||
emit('element-click', element, eventObj);
|
emit('element-click', element, eventObj);
|
||||||
@@ -498,11 +303,12 @@ const initModelListeners = () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
// 监听图形改变返回xml
|
// 监听图形改变返回xml
|
||||||
EventBus.on('commandStack.changed', async (event) => {
|
EventBus.on('commandStack.changed', async (event: any) => {
|
||||||
try {
|
try {
|
||||||
recoverable.value = bpmnModeler.get('commandStack').canRedo();
|
recoverable.value = bpmnModeler.get('commandStack').canRedo();
|
||||||
revocable.value = bpmnModeler.get('commandStack').canUndo();
|
revocable.value = bpmnModeler.get('commandStack').canUndo();
|
||||||
let { xml } = await bpmnModeler.saveXML({ format: true });
|
const { xml } = await bpmnModeler.saveXML({ format: true });
|
||||||
|
// eslint-disable-next-line vue/custom-event-name-casing
|
||||||
emit('commandStack-changed', event);
|
emit('commandStack-changed', event);
|
||||||
emit('input', xml);
|
emit('input', xml);
|
||||||
emit('change', xml);
|
emit('change', xml);
|
||||||
@@ -512,26 +318,27 @@ const initModelListeners = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 监听视图缩放变化
|
// 监听视图缩放变化
|
||||||
bpmnModeler.on('canvas.viewbox.changed', ({ viewbox }) => {
|
bpmnModeler.on('canvas.viewbox.changed', ({ viewbox }: { viewbox: any }) => {
|
||||||
|
// eslint-disable-next-line vue/custom-event-name-casing
|
||||||
emit('canvas-viewbox-changed', { viewbox });
|
emit('canvas-viewbox-changed', { viewbox });
|
||||||
const { scale } = viewbox;
|
const { scale } = viewbox;
|
||||||
defaultZoom.value = Math.floor(scale * 100) / 100;
|
defaultZoom.value = Math.floor(scale * 100) / 100;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
/* 创建新的流程图 */
|
/* 创建新的流程图 */
|
||||||
const createNewDiagram = async (xml) => {
|
const createNewDiagram = async (xml: any) => {
|
||||||
console.log(xml, 'xml');
|
console.log(xml, 'xml');
|
||||||
// 将字符串转换成图显示出来
|
// 将字符串转换成图显示出来
|
||||||
let newId = props.processId || `Process_${new Date().getTime()}`;
|
const newId = props.processId || `Process_${Date.now()}`;
|
||||||
let newName = props.processName || `业务流程_${new Date().getTime()}`;
|
const newName = props.processName || `业务流程_${Date.now()}`;
|
||||||
let xmlString = xml || DefaultEmptyXML(newId, newName, props.prefix);
|
let xmlString = xml || DefaultEmptyXML(newId, newName, props.prefix);
|
||||||
try {
|
try {
|
||||||
// console.log(xmlString, 'xmlString')
|
// console.log(xmlString, 'xmlString')
|
||||||
// console.log(this.bpmnModeler.importXML);
|
// console.log(this.bpmnModeler.importXML);
|
||||||
let { warnings } = await bpmnModeler.importXML(xmlString);
|
const { warnings } = await bpmnModeler.importXML(xmlString);
|
||||||
console.log(warnings, 'warnings');
|
console.log(warnings, 'warnings');
|
||||||
if (warnings && warnings.length) {
|
if (warnings && warnings.length) {
|
||||||
warnings.forEach((warn) => console.warn(warn));
|
warnings.forEach((warn: any) => console.warn(warn));
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(`[Process Designer Warn]: ${e.message || e}`);
|
console.error(`[Process Designer Warn]: ${e.message || e}`);
|
||||||
@@ -539,7 +346,7 @@ const createNewDiagram = async (xml) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 下载流程图到本地
|
// 下载流程图到本地
|
||||||
const downloadProcess = async (type) => {
|
const downloadProcess = async (type: string) => {
|
||||||
try {
|
try {
|
||||||
// 按需要类型创建文件并下载
|
// 按需要类型创建文件并下载
|
||||||
if (type === 'xml' || type === 'bpmn') {
|
if (type === 'xml' || type === 'bpmn') {
|
||||||
@@ -563,7 +370,7 @@ const downloadProcess = async (type) => {
|
|||||||
console.error(`[Process Designer Warn ]: ${e.message || e}`);
|
console.error(`[Process Designer Warn ]: ${e.message || e}`);
|
||||||
}
|
}
|
||||||
// 文件下载方法
|
// 文件下载方法
|
||||||
function downloadFunc(href, filename) {
|
function downloadFunc(href: string, filename: string) {
|
||||||
if (href && filename) {
|
if (href && filename) {
|
||||||
let a = document.createElement('a');
|
let a = document.createElement('a');
|
||||||
a.download = filename; //指定下载的文件名
|
a.download = filename; //指定下载的文件名
|
||||||
@@ -575,7 +382,7 @@ const downloadProcess = async (type) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 根据所需类型进行转码并返回下载地址
|
// 根据所需类型进行转码并返回下载地址
|
||||||
const setEncoded = (type, data) => {
|
const setEncoded = (type: string, data: string) => {
|
||||||
const filename = 'diagram';
|
const filename = 'diagram';
|
||||||
const encodedData = encodeURIComponent(data);
|
const encodedData = encodeURIComponent(data);
|
||||||
return {
|
return {
|
||||||
@@ -651,27 +458,30 @@ const processRestart = () => {
|
|||||||
revocable.value = false;
|
revocable.value = false;
|
||||||
createNewDiagram(null);
|
createNewDiagram(null);
|
||||||
};
|
};
|
||||||
const elementsAlign = (align) => {
|
const elementsAlign = (align: string) => {
|
||||||
const Align = bpmnModeler.get('alignElements');
|
const Align = bpmnModeler.get('alignElements');
|
||||||
const Selection = bpmnModeler.get('selection');
|
const Selection = bpmnModeler.get('selection');
|
||||||
const SelectedElements = Selection.get();
|
const SelectedElements = Selection.get();
|
||||||
if (!SelectedElements || SelectedElements.length <= 1) {
|
if (!SelectedElements || SelectedElements.length <= 1) {
|
||||||
ElMessage.warning('请按住 Shift 键选择多个元素对齐');
|
message.warning('请按住 Shift 键选择多个元素对齐');
|
||||||
// alert('请按住 Ctrl 键选择多个元素对齐
|
// alert('请按住 Ctrl 键选择多个元素对齐
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ElMessageBox.confirm('自动对齐可能造成图形变形,是否继续?', '警告', {
|
Modal.confirm({
|
||||||
confirmButtonText: '确定',
|
title: '警告',
|
||||||
cancelButtonText: '取消',
|
content: '自动对齐可能造成图形变形,是否继续?',
|
||||||
type: 'warning',
|
okText: '确定',
|
||||||
}).then(() => {
|
cancelText: '取消',
|
||||||
Align.trigger(SelectedElements, align);
|
icon: WarningOutlined as any,
|
||||||
|
onOk() {
|
||||||
|
Align.trigger(SelectedElements, align);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
/*----------------------------- 方法结束 ---------------------------------*/
|
/*----------------------------- 方法结束 ---------------------------------*/
|
||||||
const previewProcessXML = () => {
|
const previewProcessXML = () => {
|
||||||
console.log(bpmnModeler.saveXML, 'bpmnModeler');
|
console.log(bpmnModeler.saveXML, 'bpmnModeler');
|
||||||
bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
|
bpmnModeler.saveXML({ format: true }).then(({ xml }: { xml: string }) => {
|
||||||
// console.log(xml, 'xml111111')
|
// console.log(xml, 'xml111111')
|
||||||
previewResult.value = xml;
|
previewResult.value = xml;
|
||||||
previewType.value = 'xml';
|
previewType.value = 'xml';
|
||||||
@@ -679,8 +489,8 @@ const previewProcessXML = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const previewProcessJson = () => {
|
const previewProcessJson = () => {
|
||||||
bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
|
bpmnModeler.saveXML({ format: true }).then(({ xml }: { xml: string }) => {
|
||||||
const rootNodes = new XmlNode(XmlNodeType.Root, parseXmlString(xml));
|
const rootNodes = new XmlNode('root' as any, parseXmlString(xml));
|
||||||
previewResult.value = rootNodes.parent?.toJSON() as unknown as string;
|
previewResult.value = rootNodes.parent?.toJSON() as unknown as string;
|
||||||
previewType.value = 'json';
|
previewType.value = 'json';
|
||||||
previewModelVisible.value = true;
|
previewModelVisible.value = true;
|
||||||
@@ -698,3 +508,172 @@ onBeforeUnmount(() => {
|
|||||||
bpmnModeler = null;
|
bpmnModeler = null;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="my-process-designer">
|
||||||
|
<div
|
||||||
|
class="my-process-designer__header"
|
||||||
|
style="z-index: 999; display: table-row-group"
|
||||||
|
>
|
||||||
|
<slot name="control-header"></slot>
|
||||||
|
<template v-if="!$slots['control-header']">
|
||||||
|
<ButtonGroup key="file-control">
|
||||||
|
<Button
|
||||||
|
:icon="FolderOpenOutlined"
|
||||||
|
title="打开文件"
|
||||||
|
@click="refFile.click()"
|
||||||
|
/>
|
||||||
|
<Tooltip placement="bottom">
|
||||||
|
<template #title>
|
||||||
|
<div>
|
||||||
|
<Button type="link" @click="downloadProcessAsXml()">
|
||||||
|
下载为XML文件
|
||||||
|
</Button>
|
||||||
|
<br />
|
||||||
|
<Button type="link" @click="downloadProcessAsSvg()">
|
||||||
|
下载为SVG文件
|
||||||
|
</Button>
|
||||||
|
<br />
|
||||||
|
<Button type="link" @click="downloadProcessAsBpmn()">
|
||||||
|
下载为BPMN文件
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<Button :icon="DownloadOutlined" title="下载文件" />
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip>
|
||||||
|
<template #title>
|
||||||
|
<Button type="link" @click="previewProcessXML">预览XML</Button>
|
||||||
|
<br />
|
||||||
|
<Button type="link" @click="previewProcessJson">预览JSON</Button>
|
||||||
|
</template>
|
||||||
|
<Button :icon="EyeOutlined" title="浏览" />
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip
|
||||||
|
v-if="props.simulation"
|
||||||
|
:title="simulationStatus ? '退出模拟' : '开启模拟'"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
:icon="ApiOutlined"
|
||||||
|
title="模拟"
|
||||||
|
@click="processSimulation"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</ButtonGroup>
|
||||||
|
<ButtonGroup key="align-control">
|
||||||
|
<Tooltip title="向左对齐">
|
||||||
|
<Button
|
||||||
|
:icon="AlignLeftOutlined"
|
||||||
|
class="align align-bottom"
|
||||||
|
@click="elementsAlign('left')"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="向右对齐">
|
||||||
|
<Button
|
||||||
|
:icon="AlignLeftOutlined"
|
||||||
|
class="align align-top"
|
||||||
|
@click="elementsAlign('right')"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="向上对齐">
|
||||||
|
<Button
|
||||||
|
:icon="AlignLeftOutlined"
|
||||||
|
class="align align-left"
|
||||||
|
@click="elementsAlign('top')"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="向下对齐">
|
||||||
|
<Button
|
||||||
|
:icon="AlignLeftOutlined"
|
||||||
|
class="align align-right"
|
||||||
|
@click="elementsAlign('bottom')"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="水平居中">
|
||||||
|
<Button
|
||||||
|
:icon="AlignLeftOutlined"
|
||||||
|
class="align align-center"
|
||||||
|
@click="elementsAlign('center')"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="垂直居中">
|
||||||
|
<Button
|
||||||
|
:icon="AlignLeftOutlined"
|
||||||
|
class="align align-middle"
|
||||||
|
@click="elementsAlign('middle')"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</ButtonGroup>
|
||||||
|
<ButtonGroup key="scale-control">
|
||||||
|
<Tooltip title="缩小视图">
|
||||||
|
<Button
|
||||||
|
:icon="ZoomOutOutlined"
|
||||||
|
@click="processZoomOut()"
|
||||||
|
:disabled="defaultZoom < 0.2"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Button>{{ Math.floor(defaultZoom * 10 * 10) + '%' }}</Button>
|
||||||
|
<Tooltip title="放大视图">
|
||||||
|
<Button
|
||||||
|
:icon="ZoomInOutlined"
|
||||||
|
@click="processZoomIn()"
|
||||||
|
:disabled="defaultZoom > 4"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="重置视图并居中">
|
||||||
|
<Button :icon="ReloadOutlined" @click="processReZoom()" />
|
||||||
|
</Tooltip>
|
||||||
|
</ButtonGroup>
|
||||||
|
<ButtonGroup key="stack-control">
|
||||||
|
<Tooltip title="撤销">
|
||||||
|
<Button
|
||||||
|
:icon="UndoOutlined"
|
||||||
|
@click="processUndo()"
|
||||||
|
:disabled="!revocable"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="恢复">
|
||||||
|
<Button
|
||||||
|
:icon="RedoOutlined"
|
||||||
|
@click="processRedo()"
|
||||||
|
:disabled="!recoverable"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="重新绘制">
|
||||||
|
<Button :icon="ReloadOutlined" @click="processRestart()" />
|
||||||
|
</Tooltip>
|
||||||
|
</ButtonGroup>
|
||||||
|
</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>
|
||||||
|
<Dialog
|
||||||
|
title="预览"
|
||||||
|
v-model:open="previewModelVisible"
|
||||||
|
width="80%"
|
||||||
|
:scroll="true"
|
||||||
|
style="max-height: 600px"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<pre><code v-dompurify-html="highlightedCode(previewResult)" class="hljs"></code></pre>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,184 +1,18 @@
|
|||||||
<template>
|
|
||||||
<div class="process-viewer">
|
|
||||||
<div style="height: 100%" ref="processCanvas" v-show="!isLoading"></div>
|
|
||||||
<!-- 自定义箭头样式,用于已完成状态下流程连线箭头 -->
|
|
||||||
<defs ref="customDefs">
|
|
||||||
<marker
|
|
||||||
id="sequenceflow-end-white-success"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
refX="11"
|
|
||||||
refY="10"
|
|
||||||
markerWidth="10"
|
|
||||||
markerHeight="10"
|
|
||||||
orient="auto"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
class="success-arrow"
|
|
||||||
d="M 1 5 L 11 10 L 1 15 Z"
|
|
||||||
style="
|
|
||||||
stroke-width: 1px;
|
|
||||||
stroke-linecap: round;
|
|
||||||
stroke-dasharray: 10000, 1;
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
id="conditional-flow-marker-white-success"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
refX="-1"
|
|
||||||
refY="10"
|
|
||||||
markerWidth="10"
|
|
||||||
markerHeight="10"
|
|
||||||
orient="auto"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
class="success-conditional"
|
|
||||||
d="M 0 10 L 8 6 L 16 10 L 8 14 Z"
|
|
||||||
style="
|
|
||||||
stroke-width: 1px;
|
|
||||||
stroke-linecap: round;
|
|
||||||
stroke-dasharray: 10000, 1;
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- 审批记录 -->
|
|
||||||
<el-dialog
|
|
||||||
:title="dialogTitle || '审批记录'"
|
|
||||||
v-model="dialogVisible"
|
|
||||||
width="1000px"
|
|
||||||
>
|
|
||||||
<el-row>
|
|
||||||
<el-table
|
|
||||||
:data="selectTasks"
|
|
||||||
size="small"
|
|
||||||
border
|
|
||||||
header-cell-class-name="table-header-gray"
|
|
||||||
>
|
|
||||||
<el-table-column
|
|
||||||
label="序号"
|
|
||||||
header-align="center"
|
|
||||||
align="center"
|
|
||||||
type="index"
|
|
||||||
width="50"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="审批人"
|
|
||||||
min-width="100"
|
|
||||||
align="center"
|
|
||||||
v-if="selectActivityType === 'bpmn:UserTask'"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
{{
|
|
||||||
scope.row.assigneeUser?.nickname ||
|
|
||||||
scope.row.ownerUser?.nickname
|
|
||||||
}}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="发起人"
|
|
||||||
prop="assigneeUser.nickname"
|
|
||||||
min-width="100"
|
|
||||||
align="center"
|
|
||||||
v-else
|
|
||||||
/>
|
|
||||||
<el-table-column label="部门" min-width="100" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
{{
|
|
||||||
scope.row.assigneeUser?.deptName ||
|
|
||||||
scope.row.ownerUser?.deptName
|
|
||||||
}}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="开始时间"
|
|
||||||
prop="createTime"
|
|
||||||
min-width="140"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="结束时间"
|
|
||||||
prop="endTime"
|
|
||||||
min-width="140"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="审批状态"
|
|
||||||
prop="status"
|
|
||||||
min-width="90"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag
|
|
||||||
:type="DICT_TYPE.BPM_TASK_STATUS"
|
|
||||||
:value="scope.row.status"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="审批建议"
|
|
||||||
prop="reason"
|
|
||||||
min-width="120"
|
|
||||||
v-if="selectActivityType === 'bpmn:UserTask'"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="耗时"
|
|
||||||
prop="durationInMillis"
|
|
||||||
width="100"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
{{ formatPast2(scope.row.durationInMillis) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-row>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- Zoom:放大、缩小 -->
|
|
||||||
<div style="position: absolute; top: 0; left: 0; width: 100%">
|
|
||||||
<el-row type="flex" justify="end">
|
|
||||||
<el-button-group key="scale-control" size="default">
|
|
||||||
<el-button
|
|
||||||
size="default"
|
|
||||||
:plain="true"
|
|
||||||
:disabled="defaultZoom <= 0.3"
|
|
||||||
:icon="ZoomOut"
|
|
||||||
@click="processZoomOut()"
|
|
||||||
/>
|
|
||||||
<el-button size="default" style="width: 90px">
|
|
||||||
{{ Math.floor(defaultZoom * 10 * 10) + '%' }}
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
size="default"
|
|
||||||
:plain="true"
|
|
||||||
:disabled="defaultZoom >= 3.9"
|
|
||||||
:icon="ZoomIn"
|
|
||||||
@click="processZoomIn()"
|
|
||||||
/>
|
|
||||||
<el-button
|
|
||||||
size="default"
|
|
||||||
:icon="ScaleToOriginal"
|
|
||||||
@click="processReZoom()"
|
|
||||||
/>
|
|
||||||
</el-button-group>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import '../theme/index.scss';
|
import { defineProps, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { BpmProcessInstanceStatus } from '@vben/constants';
|
||||||
|
import { UndoOutlined, ZoomInOutlined, ZoomOutOutlined } from '@vben/icons';
|
||||||
|
import { dateFormatter, formatPast2 } from '@vben/utils';
|
||||||
|
|
||||||
|
import { Button, ButtonGroup, Modal, Row, Table } from 'ant-design-vue';
|
||||||
import BpmnViewer from 'bpmn-js/lib/Viewer';
|
import BpmnViewer from 'bpmn-js/lib/Viewer';
|
||||||
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
|
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
|
||||||
import { ZoomOut, ZoomIn, ScaleToOriginal } from '@element-plus/icons-vue';
|
|
||||||
import { DICT_TYPE } from '@/utils/dict';
|
import { DictTag } from '#/components/dict-tag';
|
||||||
import { dateFormatter, formatPast2 } from '@/utils/formatTime';
|
import { DICT_TYPE } from '#/utils/dict';
|
||||||
import { BpmProcessInstanceStatus } from '@/utils/constants';
|
|
||||||
|
import '../theme/index.scss';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
xml: {
|
xml: {
|
||||||
@@ -188,11 +22,12 @@ const props = defineProps({
|
|||||||
view: {
|
view: {
|
||||||
type: Object,
|
type: Object,
|
||||||
require: true,
|
require: true,
|
||||||
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const processCanvas = ref();
|
const processCanvas = ref();
|
||||||
const bpmnViewer = ref<BpmnViewer | null>(null);
|
const bpmnViewer = ref<any | BpmnViewer>(null);
|
||||||
const customDefs = ref();
|
const customDefs = ref();
|
||||||
const defaultZoom = ref(1); // 默认缩放比例
|
const defaultZoom = ref(1); // 默认缩放比例
|
||||||
const isLoading = ref(false); // 是否加载中
|
const isLoading = ref(false); // 是否加载中
|
||||||
@@ -213,7 +48,7 @@ const processReZoom = () => {
|
|||||||
|
|
||||||
/** Zoom:放大 */
|
/** Zoom:放大 */
|
||||||
const processZoomIn = (zoomStep = 0.1) => {
|
const processZoomIn = (zoomStep = 0.1) => {
|
||||||
let newZoom = Math.floor(defaultZoom.value * 100 + zoomStep * 100) / 100;
|
const newZoom = Math.floor(defaultZoom.value * 100 + zoomStep * 100) / 100;
|
||||||
if (newZoom > 4) {
|
if (newZoom > 4) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'[Process Designer Warn ]: The zoom ratio cannot be greater than 4',
|
'[Process Designer Warn ]: The zoom ratio cannot be greater than 4',
|
||||||
@@ -225,7 +60,7 @@ const processZoomIn = (zoomStep = 0.1) => {
|
|||||||
|
|
||||||
/** Zoom:缩小 */
|
/** Zoom:缩小 */
|
||||||
const processZoomOut = (zoomStep = 0.1) => {
|
const processZoomOut = (zoomStep = 0.1) => {
|
||||||
let newZoom = Math.floor(defaultZoom.value * 100 - zoomStep * 100) / 100;
|
const newZoom = Math.floor(defaultZoom.value * 100 - zoomStep * 100) / 100;
|
||||||
if (newZoom < 0.2) {
|
if (newZoom < 0.2) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'[Process Designer Warn ]: The zoom ratio cannot be less than 0.2',
|
'[Process Designer Warn ]: The zoom ratio cannot be less than 0.2',
|
||||||
@@ -254,7 +89,7 @@ const addCustomDefs = () => {
|
|||||||
}
|
}
|
||||||
const canvas = bpmnViewer.value?.get('canvas');
|
const canvas = bpmnViewer.value?.get('canvas');
|
||||||
const svg = canvas?._svg;
|
const svg = canvas?._svg;
|
||||||
svg.appendChild(customDefs.value);
|
svg.append(customDefs.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 节点选中 */
|
/** 节点选中 */
|
||||||
@@ -301,14 +136,14 @@ const importXML = async (xml: string) => {
|
|||||||
clearViewer();
|
clearViewer();
|
||||||
|
|
||||||
// 初始化流程图
|
// 初始化流程图
|
||||||
if (xml != null && xml !== '') {
|
if (xml !== null && xml !== '') {
|
||||||
try {
|
try {
|
||||||
bpmnViewer.value = new BpmnViewer({
|
bpmnViewer.value = new BpmnViewer({
|
||||||
additionalModules: [MoveCanvasModule],
|
additionalModules: [MoveCanvasModule],
|
||||||
container: processCanvas.value,
|
container: processCanvas.value,
|
||||||
});
|
});
|
||||||
// 增加点击事件
|
// 增加点击事件
|
||||||
bpmnViewer.value.on('element.click', ({ element }) => {
|
bpmnViewer.value.on('element.click', ({ element }: { element: any }) => {
|
||||||
onSelectElement(element);
|
onSelectElement(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -317,7 +152,7 @@ const importXML = async (xml: string) => {
|
|||||||
await bpmnViewer.value.importXML(xml);
|
await bpmnViewer.value.importXML(xml);
|
||||||
// 自定义成功的箭头
|
// 自定义成功的箭头
|
||||||
addCustomDefs();
|
addCustomDefs();
|
||||||
} catch (e) {
|
} catch {
|
||||||
clearViewer();
|
clearViewer();
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
@@ -344,13 +179,13 @@ const setProcessStatus = (view: any) => {
|
|||||||
finishedSequenceFlowActivityIds,
|
finishedSequenceFlowActivityIds,
|
||||||
rejectedTaskActivityIds,
|
rejectedTaskActivityIds,
|
||||||
} = view;
|
} = view;
|
||||||
const canvas = bpmnViewer.value.get('canvas');
|
const canvas: any = bpmnViewer.value.get('canvas');
|
||||||
const elementRegistry = bpmnViewer.value.get('elementRegistry');
|
const elementRegistry: any = bpmnViewer.value.get('elementRegistry');
|
||||||
|
|
||||||
// 已完成节点
|
// 已完成节点
|
||||||
if (Array.isArray(finishedSequenceFlowActivityIds)) {
|
if (Array.isArray(finishedSequenceFlowActivityIds)) {
|
||||||
finishedSequenceFlowActivityIds.forEach((item: any) => {
|
finishedSequenceFlowActivityIds.forEach((item: any) => {
|
||||||
if (item != null) {
|
if (item !== null) {
|
||||||
canvas.addMarker(item, 'success');
|
canvas.addMarker(item, 'success');
|
||||||
const element = elementRegistry.get(item);
|
const element = elementRegistry.get(item);
|
||||||
const conditionExpression = element.businessObject.conditionExpression;
|
const conditionExpression = element.businessObject.conditionExpression;
|
||||||
@@ -376,7 +211,7 @@ const setProcessStatus = (view: any) => {
|
|||||||
// 被拒绝节点
|
// 被拒绝节点
|
||||||
if (Array.isArray(rejectedTaskActivityIds)) {
|
if (Array.isArray(rejectedTaskActivityIds)) {
|
||||||
rejectedTaskActivityIds.forEach((item: any) => {
|
rejectedTaskActivityIds.forEach((item: any) => {
|
||||||
if (item != null) {
|
if (item !== null) {
|
||||||
canvas.addMarker(item, 'danger');
|
canvas.addMarker(item, 'danger');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -429,3 +264,155 @@ onBeforeUnmount(() => {
|
|||||||
clearViewer();
|
clearViewer();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="process-viewer">
|
||||||
|
<div style="height: 100%" ref="processCanvas" v-show="!isLoading"></div>
|
||||||
|
<!-- 自定义箭头样式,用于已完成状态下流程连线箭头 -->
|
||||||
|
<defs ref="customDefs">
|
||||||
|
<marker
|
||||||
|
id="sequenceflow-end-white-success"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
refX="11"
|
||||||
|
refY="10"
|
||||||
|
markerWidth="10"
|
||||||
|
markerHeight="10"
|
||||||
|
orient="auto"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
class="success-arrow"
|
||||||
|
d="M 1 5 L 11 10 L 1 15 Z"
|
||||||
|
style="
|
||||||
|
stroke-width: 1px;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-dasharray: 10000, 1;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
id="conditional-flow-marker-white-success"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
refX="-1"
|
||||||
|
refY="10"
|
||||||
|
markerWidth="10"
|
||||||
|
markerHeight="10"
|
||||||
|
orient="auto"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
class="success-conditional"
|
||||||
|
d="M 0 10 L 8 6 L 16 10 L 8 14 Z"
|
||||||
|
style="
|
||||||
|
stroke-width: 1px;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-dasharray: 10000, 1;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- 审批记录 -->
|
||||||
|
<Modal
|
||||||
|
:title="dialogTitle || '审批记录'"
|
||||||
|
v-model:open="dialogVisible"
|
||||||
|
:width="1000"
|
||||||
|
>
|
||||||
|
<Row>
|
||||||
|
<Table :data-source="selectTasks" size="small" :bordered="true">
|
||||||
|
<Table.Column title="序号" align="center" width="50">
|
||||||
|
<template #default="{ index }">
|
||||||
|
{{ index + 1 }}
|
||||||
|
</template>
|
||||||
|
</Table.Column>
|
||||||
|
<Table.Column
|
||||||
|
title="审批人"
|
||||||
|
width="100"
|
||||||
|
align="center"
|
||||||
|
v-if="selectActivityType === 'bpmn:UserTask'"
|
||||||
|
>
|
||||||
|
<template #default="{ record }">
|
||||||
|
{{ record.assigneeUser?.nickname || record.ownerUser?.nickname }}
|
||||||
|
</template>
|
||||||
|
</Table.Column>
|
||||||
|
<Table.Column
|
||||||
|
title="发起人"
|
||||||
|
data-index="assigneeUser.nickname"
|
||||||
|
width="100"
|
||||||
|
align="center"
|
||||||
|
v-else
|
||||||
|
/>
|
||||||
|
<Table.Column title="部门" width="100" align="center">
|
||||||
|
<template #default="{ record }">
|
||||||
|
{{ record.assigneeUser?.deptName || record.ownerUser?.deptName }}
|
||||||
|
</template>
|
||||||
|
</Table.Column>
|
||||||
|
<Table.Column
|
||||||
|
:custom-render="({ text }) => dateFormatter(text)"
|
||||||
|
align="center"
|
||||||
|
title="开始时间"
|
||||||
|
data-index="createTime"
|
||||||
|
width="140"
|
||||||
|
/>
|
||||||
|
<Table.Column
|
||||||
|
:custom-render="({ text }) => dateFormatter(text)"
|
||||||
|
align="center"
|
||||||
|
title="结束时间"
|
||||||
|
data-index="endTime"
|
||||||
|
width="140"
|
||||||
|
/>
|
||||||
|
<Table.Column
|
||||||
|
align="center"
|
||||||
|
title="审批状态"
|
||||||
|
data-index="status"
|
||||||
|
width="90"
|
||||||
|
>
|
||||||
|
<template #default="{ record }">
|
||||||
|
<DictTag
|
||||||
|
:type="DICT_TYPE.BPM_TASK_STATUS"
|
||||||
|
:value="record.status"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Table.Column>
|
||||||
|
<Table.Column
|
||||||
|
align="center"
|
||||||
|
title="审批建议"
|
||||||
|
data-index="reason"
|
||||||
|
width="120"
|
||||||
|
v-if="selectActivityType === 'bpmn:UserTask'"
|
||||||
|
/>
|
||||||
|
<Table.Column
|
||||||
|
align="center"
|
||||||
|
title="耗时"
|
||||||
|
data-index="durationInMillis"
|
||||||
|
width="100"
|
||||||
|
>
|
||||||
|
<template #default="{ record }">
|
||||||
|
{{ formatPast2(record.durationInMillis) }}
|
||||||
|
</template>
|
||||||
|
</Table.Column>
|
||||||
|
</Table>
|
||||||
|
</Row>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<!-- Zoom:放大、缩小 -->
|
||||||
|
<div style="position: absolute; top: 0; left: 0; width: 100%">
|
||||||
|
<Row justify="end">
|
||||||
|
<ButtonGroup key="scale-control">
|
||||||
|
<Button
|
||||||
|
:disabled="defaultZoom <= 0.3"
|
||||||
|
:icon="ZoomOutOutlined"
|
||||||
|
@click="processZoomOut()"
|
||||||
|
/>
|
||||||
|
<Button style="width: 90px">
|
||||||
|
{{ `${Math.floor(defaultZoom * 10 * 10)}%` }}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
:disabled="defaultZoom >= 3.9"
|
||||||
|
:icon="ZoomInOutlined"
|
||||||
|
@click="processZoomIn()"
|
||||||
|
/>
|
||||||
|
<Button :icon="UndoOutlined" @click="processReZoom()" />
|
||||||
|
</ButtonGroup>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import MyProcessDesigner from './ProcessDesigner.vue';
|
import MyProcessDesigner from './ProcessDesigner.vue';
|
||||||
|
|
||||||
MyProcessDesigner.install = function (Vue) {
|
MyProcessDesigner.install = function (Vue: any) {
|
||||||
Vue.component(MyProcessDesigner.name, MyProcessDesigner);
|
Vue.component(MyProcessDesigner.name, MyProcessDesigner);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import MyProcessViewer from './ProcessViewer.vue';
|
import MyProcessViewer from './ProcessViewer.vue';
|
||||||
|
|
||||||
MyProcessViewer.install = function (Vue) {
|
MyProcessViewer.install = function (Vue: any) {
|
||||||
Vue.component(MyProcessViewer.name, MyProcessViewer);
|
Vue.component(MyProcessViewer.name, MyProcessViewer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"extends": "@vben/tsconfig/web-app.json",
|
"extends": "@vben/tsconfig/web-app.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"allowJs": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"#/*": ["./src/*"]
|
"#/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,35 @@ export const TMinimize = createIconifyIcon('tabler:arrows-minimize');
|
|||||||
export const AntdProfileOutlined = createIconifyIcon(
|
export const AntdProfileOutlined = createIconifyIcon(
|
||||||
'ant-design:profile-outlined',
|
'ant-design:profile-outlined',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const FolderOpenOutlined = createIconifyIcon(
|
||||||
|
'ant-design:folder-open-outlined',
|
||||||
|
);
|
||||||
|
|
||||||
|
export const DownloadOutlined = createIconifyIcon(
|
||||||
|
'ant-design:download-outlined',
|
||||||
|
);
|
||||||
|
|
||||||
|
export const EyeOutlined = createIconifyIcon('ant-design:eye-outlined');
|
||||||
|
|
||||||
|
export const ApiOutlined = createIconifyIcon('ant-design:api-outlined');
|
||||||
|
|
||||||
|
export const ZoomOutOutlined = createIconifyIcon(
|
||||||
|
'ant-design:zoom-out-outlined',
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ZoomInOutlined = createIconifyIcon('ant-design:zoom-in-outlined');
|
||||||
|
|
||||||
|
export const UndoOutlined = createIconifyIcon('ant-design:undo-outlined');
|
||||||
|
|
||||||
|
export const RedoOutlined = createIconifyIcon('ant-design:redo-outlined');
|
||||||
|
|
||||||
|
export const ReloadOutlined = createIconifyIcon('ant-design:reload-outlined');
|
||||||
|
|
||||||
|
export const AlignLeftOutlined = createIconifyIcon(
|
||||||
|
'ant-design:align-left-outlined',
|
||||||
|
);
|
||||||
|
|
||||||
|
export const WarningOutlined = createIconifyIcon('ant-design:warning-outlined');
|
||||||
|
|
||||||
export const RiDingding = createIconifyIcon('ri:dingding-fill');
|
export const RiDingding = createIconifyIcon('ri:dingding-fill');
|
||||||
|
|||||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -809,6 +809,9 @@ importers:
|
|||||||
highlight.js:
|
highlight.js:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 11.11.1
|
version: 11.11.1
|
||||||
|
min-dash:
|
||||||
|
specifier: ^4.2.3
|
||||||
|
version: 4.2.3
|
||||||
pinia:
|
pinia:
|
||||||
specifier: ^3.0.3
|
specifier: ^3.0.3
|
||||||
version: 3.0.3(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2))
|
version: 3.0.3(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2))
|
||||||
|
|||||||
Reference in New Issue
Block a user