update 优化bpmn位置
This commit is contained in:
parent
73db6deec3
commit
945eec5418
@ -1,4 +1,4 @@
|
||||
import zh from '@/components/BpmnDesign/assets/lang/zh';
|
||||
import zh from '../../lang/zh';
|
||||
|
||||
const customTranslate = (template: any, replacements: any) => {
|
||||
replacements = replacements || {};
|
@ -1,4 +1,4 @@
|
||||
import showConfig from '@/components/BpmnDesign/assets/showConfig';
|
||||
import showConfig from '../assets/showConfig';
|
||||
import { ModdleElement } from 'bpmn';
|
||||
import useModelerStore from '@/store/modules/modeler';
|
||||
import { MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums';
|
498
src/bpmn/index.vue
Normal file
498
src/bpmn/index.vue
Normal file
@ -0,0 +1,498 @@
|
||||
<template>
|
||||
<div class="containers-bpmn">
|
||||
<!-- dark模式下 连接线的箭头样式 -->
|
||||
<svg width="0" height="0" style="position: absolute">
|
||||
<defs>
|
||||
<marker id="markerArrow-dark-mode" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto">
|
||||
<path d="M 1 5 L 11 10 L 1 15 Z" class="arrow-dark" />
|
||||
</marker>
|
||||
</defs>
|
||||
</svg>
|
||||
<div v-loading="loading" class="app-containers-bpmn">
|
||||
<el-container class="h-full">
|
||||
<el-container style="align-items: stretch">
|
||||
<el-header>
|
||||
<div class="process-toolbar">
|
||||
<el-space wrap :size="10">
|
||||
<el-button size="small" type="primary" @click="saveXml">保 存</el-button>
|
||||
<el-dropdown size="small">
|
||||
<el-button size="small" type="primary"> 预 览 </el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Document" @click="previewXML">XML预览</el-dropdown-item>
|
||||
<el-dropdown-item icon="View" @click="previewSVG"> SVG预览</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<el-dropdown size="small">
|
||||
<el-button size="small" type="primary"> 下 载 </el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Download" @click="downloadXML">下载XML</el-dropdown-item>
|
||||
<el-dropdown-item icon="Download" @click="downloadSVG"> 下载SVG</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-tooltip effect="dark" content="新建" placement="bottom">
|
||||
<el-button size="small" icon="CirclePlus" @click="newDiagram" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
|
||||
<el-button size="small" icon="Rank" @click="fitViewport" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="放大" placement="bottom">
|
||||
<el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="缩小" placement="bottom">
|
||||
<el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="后退" placement="bottom">
|
||||
<el-button size="small" icon="Back" @click="bpmnModeler.get('commandStack').undo()" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="前进" placement="bottom">
|
||||
<el-button size="small" icon="Right" @click="bpmnModeler.get('commandStack').redo()" />
|
||||
</el-tooltip>
|
||||
</el-space>
|
||||
</div>
|
||||
</el-header>
|
||||
<div ref="canvas" class="canvas" />
|
||||
</el-container>
|
||||
<div :class="{ 'process-panel': true, 'hide': panelFlag }">
|
||||
<div class="process-panel-bar" @click="panelBarClick">
|
||||
<div class="open-bar">
|
||||
<el-link type="default" :underline="false">
|
||||
<svg-icon class-name="open-bar" :icon-class="panelFlag ? 'caret-back' : 'caret-forward'"></svg-icon>
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<transition enter-active-class="animate__animated animate__fadeIn">
|
||||
<div v-show="showPanel" v-if="bpmnModeler" class="panel-content">
|
||||
<PropertyPanel :modeler="bpmnModeler" />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</el-container>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-dialog v-model="perviewXMLShow" title="XML预览" width="80%" append-to-body>
|
||||
<highlightjs :code="xmlStr" language="XML" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div>
|
||||
<el-dialog v-model="perviewSVGShow" title="SVG预览" width="80%" append-to-body>
|
||||
<div style="text-align: center" v-html="svgData" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="BpmnDesign">
|
||||
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 './assets/style/index.scss';
|
||||
import { Canvas, Modeler } from 'bpmn';
|
||||
import PropertyPanel from './panel/index.vue';
|
||||
import BpmnModeler from 'bpmn-js/lib/Modeler.js';
|
||||
import defaultXML from './assets/defaultXML';
|
||||
import flowableModdle from './assets/moddle/flowable';
|
||||
import Modules from './assets/module/index';
|
||||
import useModelerStore from '@/store/modules/modeler';
|
||||
import useDialog from '@/hooks/useDialog';
|
||||
|
||||
const emit = defineEmits(['closeCallBack', 'saveCallBack']);
|
||||
|
||||
const { visible, title, openDialog, closeDialog } = useDialog({
|
||||
title: '编辑流程'
|
||||
});
|
||||
const modelerStore = useModelerStore();
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const panelFlag = ref(false);
|
||||
const showPanel = ref(true);
|
||||
const canvas = ref<HTMLDivElement>();
|
||||
const panel = ref<HTMLDivElement>();
|
||||
const bpmnModeler = ref<Modeler>();
|
||||
const zoom = ref(1);
|
||||
const perviewXMLShow = ref(false);
|
||||
const perviewSVGShow = ref(false);
|
||||
const xmlStr = ref('');
|
||||
const svgData = ref('');
|
||||
const loading = ref(false);
|
||||
|
||||
const panelBarClick = () => {
|
||||
// 延迟执行,否则会导致面板收起时,属性面板不显示
|
||||
panelFlag.value = !panelFlag.value;
|
||||
setTimeout(() => {
|
||||
showPanel.value = !panelFlag.value;
|
||||
}, 100);
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化Canvas
|
||||
*/
|
||||
const initCanvas = () => {
|
||||
bpmnModeler.value = new BpmnModeler({
|
||||
container: canvas.value,
|
||||
// 键盘
|
||||
keyboard: {
|
||||
bindTo: window // 或者window,注意与外部表单的键盘监听事件是否冲突
|
||||
},
|
||||
propertiesPanel: {
|
||||
parent: panel.value
|
||||
},
|
||||
additionalModules: Modules,
|
||||
moddleExtensions: {
|
||||
flowable: flowableModdle
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化Model
|
||||
*/
|
||||
const initModel = () => {
|
||||
if (modelerStore.getModeler()) {
|
||||
modelerStore.getModeler().destroy();
|
||||
modelerStore.setModeler(undefined);
|
||||
}
|
||||
modelerStore.setModeler(bpmnModeler.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* 新建
|
||||
*/
|
||||
const newDiagram = async () => {
|
||||
await proxy?.$modal.confirm('是否确认新建');
|
||||
initDiagram();
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
const initDiagram = (xml?: string) => {
|
||||
if (!xml) xml = defaultXML;
|
||||
bpmnModeler.value.importXML(xml);
|
||||
};
|
||||
|
||||
/**
|
||||
* 自适应屏幕
|
||||
*/
|
||||
const fitViewport = () => {
|
||||
zoom.value = bpmnModeler.value.get<Canvas>('canvas').zoom('fit-viewport');
|
||||
const bbox = document.querySelector<SVGGElement>('.app-containers-bpmn .viewport').getBBox();
|
||||
const currentViewBox = bpmnModeler.value.get<Canvas>('canvas').viewbox();
|
||||
const elementMid = {
|
||||
x: bbox.x + bbox.width / 2 - 65,
|
||||
y: bbox.y + bbox.height / 2
|
||||
};
|
||||
bpmnModeler.value.get<Canvas>('canvas').viewbox({
|
||||
x: elementMid.x - currentViewBox.width / 2,
|
||||
y: elementMid.y - currentViewBox.height / 2,
|
||||
width: currentViewBox.width,
|
||||
height: currentViewBox.height
|
||||
});
|
||||
zoom.value = (bbox.width / currentViewBox.width) * 1.8;
|
||||
};
|
||||
/**
|
||||
* 放大或者缩小
|
||||
* @param zoomIn true 放大 | false 缩小
|
||||
*/
|
||||
const zoomViewport = (zoomIn = true) => {
|
||||
zoom.value = bpmnModeler.value.get<Canvas>('canvas').zoom();
|
||||
zoom.value += zoomIn ? 0.1 : -0.1;
|
||||
bpmnModeler.value.get<Canvas>('canvas').zoom(zoom.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载XML
|
||||
*/
|
||||
const downloadXML = async () => {
|
||||
try {
|
||||
const { xml } = await bpmnModeler.value.saveXML({ format: true });
|
||||
downloadFile(`${getProcessElement().name}.bpmn20.xml`, xml, 'application/xml');
|
||||
} catch (e) {
|
||||
proxy?.$modal.msgError(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载SVG
|
||||
*/
|
||||
const downloadSVG = async () => {
|
||||
try {
|
||||
const { svg } = await bpmnModeler.value.saveSVG();
|
||||
downloadFile(getProcessElement().name, svg, 'image/svg+xml');
|
||||
} catch (e) {
|
||||
proxy?.$modal.msgError(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* XML预览
|
||||
*/
|
||||
const previewXML = async () => {
|
||||
try {
|
||||
const { xml } = await bpmnModeler.value.saveXML({ format: true });
|
||||
xmlStr.value = xml;
|
||||
perviewXMLShow.value = true;
|
||||
} catch (e) {
|
||||
proxy?.$modal.msgError(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SVG预览
|
||||
*/
|
||||
const previewSVG = async () => {
|
||||
try {
|
||||
const { svg } = await bpmnModeler.value.saveSVG();
|
||||
svgData.value = svg;
|
||||
perviewSVGShow.value = true;
|
||||
} catch (e) {
|
||||
proxy?.$modal.msgError(e);
|
||||
}
|
||||
};
|
||||
|
||||
const curNodeInfo = reactive({
|
||||
curType: '', // 任务类型 用户任务
|
||||
curNode: '',
|
||||
expValue: '' //多用户和部门角色实现
|
||||
});
|
||||
|
||||
const downloadFile = (fileName: string, data: any, type: string) => {
|
||||
const a = document.createElement('a');
|
||||
const url = window.URL.createObjectURL(new Blob([data], { type: type }));
|
||||
a.href = url;
|
||||
a.download = fileName;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
const getProcessElement = () => {
|
||||
const rootElements = bpmnModeler.value?.getDefinitions().rootElements;
|
||||
for (let i = 0; i < rootElements.length; i++) {
|
||||
if (rootElements[i].$type === 'bpmn:Process') return rootElements[i];
|
||||
}
|
||||
};
|
||||
|
||||
const getProcess = () => {
|
||||
const element = getProcessElement();
|
||||
return {
|
||||
id: element.id,
|
||||
name: element.name
|
||||
};
|
||||
};
|
||||
|
||||
const saveXml = async () => {
|
||||
const { xml } = await bpmnModeler.value.saveXML({ format: true });
|
||||
const { svg } = await bpmnModeler.value.saveSVG();
|
||||
const process = getProcess();
|
||||
let data = {
|
||||
xml: xml,
|
||||
svg: svg,
|
||||
key: process.id,
|
||||
name: process.name,
|
||||
loading: loading
|
||||
};
|
||||
emit('saveCallBack', data);
|
||||
};
|
||||
|
||||
const open = (xml?: string) => {
|
||||
openDialog();
|
||||
nextTick(() => {
|
||||
initDiagram(xml);
|
||||
});
|
||||
};
|
||||
const close = () => {
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initCanvas();
|
||||
initModel();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 对外暴露子组件方法
|
||||
*/
|
||||
defineExpose({
|
||||
initDiagram,
|
||||
saveXml,
|
||||
open,
|
||||
close
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/** 夜间模式 线条的颜色 */
|
||||
$stroke-color-dark: white;
|
||||
$bpmn-font-size: 12px;
|
||||
/** 日间模式 字体颜色 */
|
||||
$bpmn-font-color-dark: white;
|
||||
/** 夜间模式 字体颜色 */
|
||||
$bpmn-font-color-light: #222;
|
||||
|
||||
/* 背景网格 */
|
||||
@mixin djs-container {
|
||||
background-image: linear-gradient(90deg, hsl(0deg 0% 78.4% / 15%) 10%, transparent 0), linear-gradient(hsl(0deg 0% 78.4% / 15%) 10%, transparent 0) !important;
|
||||
background-size: 10px 10px !important;
|
||||
}
|
||||
|
||||
html[class='light'] {
|
||||
/** 从左侧拖动时的背景图 */
|
||||
svg.new-parent {
|
||||
@include djs-container;
|
||||
}
|
||||
|
||||
/** 双击编辑元素时样式保持一致 */
|
||||
div.djs-direct-editing-parent {
|
||||
border-radius: 10px;
|
||||
background-color: transparent !important;
|
||||
color: $bpmn-font-color-light;
|
||||
}
|
||||
|
||||
g.djs-visual {
|
||||
.djs-label {
|
||||
fill: $bpmn-font-color-light !important;
|
||||
font-size: $bpmn-font-size !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[class='dark'] {
|
||||
/** dark模式下 连接线的箭头样式 */
|
||||
.arrow-dark {
|
||||
stroke-width: 1px;
|
||||
stroke-linecap: round;
|
||||
stroke: $stroke-color-dark;
|
||||
fill: $stroke-color-dark;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
/** 从左侧拖动时的背景图 */
|
||||
svg.new-parent {
|
||||
background-color: black !important;
|
||||
@include djs-container;
|
||||
}
|
||||
|
||||
/** 双击编辑元素时样式保持一致 */
|
||||
div.djs-direct-editing-parent {
|
||||
border-radius: 10px;
|
||||
background-color: transparent !important;
|
||||
color: $bpmn-font-color-dark;
|
||||
}
|
||||
|
||||
/** 元素相关设置 */
|
||||
g.djs-visual {
|
||||
/** 元素边框 需要去除文字(.djs-label) */
|
||||
& > *:first-child:not(.djs-label) {
|
||||
stroke: $stroke-color-dark !important;
|
||||
}
|
||||
|
||||
/** 字体颜色 */
|
||||
.djs-label {
|
||||
fill: $bpmn-font-color-dark !important;
|
||||
font-size: $bpmn-font-size !important;
|
||||
}
|
||||
|
||||
/* 连接线样式 */
|
||||
path[data-corner-radius] {
|
||||
stroke: $stroke-color-dark !important;
|
||||
marker-end: url('#markerArrow-dark-mode') !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.containers-bpmn {
|
||||
height: 100%;
|
||||
.app-containers-bpmn {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@include djs-container;
|
||||
}
|
||||
.el-header {
|
||||
height: 35px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.process-panel {
|
||||
transition: width 0.25s ease-in;
|
||||
.process-panel-bar {
|
||||
width: 34px;
|
||||
height: 40px;
|
||||
.open-bar {
|
||||
width: 34px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
// 收起面板样式
|
||||
&.hide {
|
||||
width: 34px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
.process-panel-bar {
|
||||
width: 34px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
text-align: left;
|
||||
line-height: 34px;
|
||||
}
|
||||
.process-panel-bar:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pre {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
max-height: calc(80vh - 32px);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
.hljs {
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
padding: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.open-bar {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
.process-panel {
|
||||
box-sizing: border-box;
|
||||
padding: 0 8px 0 8px;
|
||||
border-left: 1px solid #eeeeee;
|
||||
box-shadow: #cccccc 0 0 8px;
|
||||
max-height: 100%;
|
||||
width: 25%;
|
||||
height: calc(100vh - 100px);
|
||||
.el-collapse {
|
||||
height: calc(100vh - 182px);
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// 任务栏 透明度
|
||||
//:deep(.djs-palette) {
|
||||
// opacity: 0.3;
|
||||
// transition: all 1s;
|
||||
//}
|
||||
//
|
||||
//:deep(.djs-palette:hover) {
|
||||
// opacity: 1;
|
||||
// transition: all 1s;
|
||||
//}
|
||||
</style>
|
@ -39,11 +39,11 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import useParseElement from '@/components/BpmnDesign/hooks/useParseElement';
|
||||
import usePanel from '@/components/BpmnDesign/hooks/usePanel';
|
||||
import useParseElement from '../hooks/useParseElement';
|
||||
import usePanel from '../hooks/usePanel';
|
||||
import { Modeler, ModdleElement } from 'bpmn';
|
||||
import { GatewayPanel } from 'bpmnDesign';
|
||||
import ExecutionListener from '@/components/BpmnDesign/panel/property/ExecutionListener.vue';
|
||||
import ExecutionListener from './property/ExecutionListener.vue';
|
||||
|
||||
interface PropType {
|
||||
element: ModdleElement;
|
@ -39,8 +39,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import useParseElement from '@/components/BpmnDesign/hooks/useParseElement';
|
||||
import usePanel from '@/components/BpmnDesign/hooks/usePanel';
|
||||
import useParseElement from '../hooks/useParseElement';
|
||||
import usePanel from '../hooks/usePanel';
|
||||
import ExecutionListener from './property/ExecutionListener.vue';
|
||||
import { ModdleElement } from 'bpmn';
|
||||
import { ParticipantPanel } from 'bpmnDesign';
|
||||
|
@ -41,8 +41,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import ExecutionListener from './property/ExecutionListener.vue';
|
||||
import useParseElement from '@/components/BpmnDesign/hooks/useParseElement';
|
||||
import usePanel from '@/components/BpmnDesign/hooks/usePanel';
|
||||
import useParseElement from '../hooks/useParseElement';
|
||||
import usePanel from '../hooks/usePanel';
|
||||
import { Modeler, ModdleElement } from 'bpmn';
|
||||
import { ProcessPanel } from 'bpmnDesign';
|
||||
|
@ -45,11 +45,12 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import useParseElement from '@/components/BpmnDesign/hooks/useParseElement';
|
||||
import usePanel from '@/components/BpmnDesign/hooks/usePanel';
|
||||
import useParseElement from '../hooks/useParseElement';
|
||||
import useModelerStore from '@/store/modules/modeler';
|
||||
import usePanel from '../hooks/usePanel';
|
||||
import ExecutionListener from './property/ExecutionListener.vue';
|
||||
import { Modeler, ModdleElement } from 'bpmn';
|
||||
import { SequenceFlowPanel } from 'bpmnDesign';
|
||||
import useModelerStore from '@/store/modules/modeler';
|
||||
|
||||
interface PropType {
|
||||
element: ModdleElement;
|
@ -39,8 +39,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import useParseElement from '@/components/BpmnDesign/hooks/useParseElement';
|
||||
import usePanel from '@/components/BpmnDesign/hooks/usePanel';
|
||||
import ExecutionListener from './property/ExecutionListener.vue';
|
||||
import useParseElement from '../hooks/useParseElement';
|
||||
import usePanel from '../hooks/usePanel';
|
||||
import { Modeler, ModdleElement } from 'bpmn';
|
||||
import { StartEndPanel } from 'bpmnDesign';
|
||||
|
@ -108,8 +108,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import useParseElement from '@/components/BpmnDesign/hooks/useParseElement';
|
||||
import usePanel from '@/components/BpmnDesign/hooks/usePanel';
|
||||
import ExecutionListener from './property/ExecutionListener.vue';
|
||||
import useParseElement from '../hooks/useParseElement';
|
||||
import usePanel from '../hooks/usePanel';
|
||||
import { ModdleElement } from 'bpmn';
|
||||
import { SubProcessPanel } from 'bpmnDesign';
|
||||
import { MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums';
|
@ -21,9 +21,14 @@
|
||||
<el-form-item v-if="showConfig.skipExpression" prop="skipExpression" label="跳过表达式">
|
||||
<el-input v-model="formData.skipExpression" @change="skipExpressionChange"> </el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="formKey" label="表单地址" v-loading="formManageListLoading">
|
||||
<el-select @change="formKeyChange" v-model="formData.formKey" clearable filterable placeholder="请选择表单" style="width: 260px" >
|
||||
<el-option v-for="item in formManageList" :key="item.id" :label="item.formTypeName+':'+item.formName" :value="item.formType+':'+item.id" />
|
||||
<el-form-item v-loading="formManageListLoading" prop="formKey" label="表单地址">
|
||||
<el-select v-model="formData.formKey" clearable filterable placeholder="请选择表单" style="width: 260px" @change="formKeyChange">
|
||||
<el-option
|
||||
v-for="item in formManageList"
|
||||
:key="item.id"
|
||||
:label="item.formTypeName + ':' + item.formName"
|
||||
:value="item.formType + ':' + item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
@ -231,11 +236,13 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import useParseElement from '@/components/BpmnDesign/hooks/useParseElement';
|
||||
import usePanel from '@/components/BpmnDesign/hooks/usePanel';
|
||||
import useParseElement from '../hooks/useParseElement';
|
||||
import usePanel from '../hooks/usePanel';
|
||||
import UserSelect from '@/components/UserSelect';
|
||||
import RoleSelect from '@/components/RoleSelect';
|
||||
import DueDate from '@/components/BpmnDesign/panel/property/DueDate.vue';
|
||||
import ExecutionListener from './property/ExecutionListener.vue';
|
||||
import TaskListener from './property/TaskListener.vue';
|
||||
import DueDate from './property/DueDate.vue';
|
||||
import { ModdleElement } from 'bpmn';
|
||||
import { TaskPanel } from 'bpmnDesign';
|
||||
import { AllocationTypeEnum, MultiInstanceTypeEnum, SpecifyDescEnum } from '@/enums/bpmn/IndexEnums';
|
||||
@ -464,11 +471,11 @@ const SpecifyDesc = [
|
||||
];
|
||||
|
||||
const listFormManage = async () => {
|
||||
formManageListLoading.value = true
|
||||
formManageListLoading.value = true;
|
||||
const res = await selectListFormManage();
|
||||
formManageList.value = res.data;
|
||||
formManageListLoading.value = false
|
||||
}
|
||||
formManageListLoading.value = false;
|
||||
};
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
listFormManage();
|
@ -66,7 +66,10 @@
|
||||
<el-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="typeSelect.filter(e=>e.value === formData.type)[0]?typeSelect.filter(e=>e.value === formData.type)[0]?.label:'表达式'" prop="className">
|
||||
<el-form-item
|
||||
:label="typeSelect.filter((e) => e.value === formData.type)[0] ? typeSelect.filter((e) => e.value === formData.type)[0]?.label : '表达式'"
|
||||
prop="className"
|
||||
>
|
||||
<el-input v-model="formData.className" type="text"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -90,7 +93,7 @@ import { VxeTableEvents, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
|
||||
import { ExecutionListenerVO } from 'bpmnDesign';
|
||||
import { Moddle, Modeler, ModdleElement } from 'bpmn';
|
||||
|
||||
import usePanel from '@/components/BpmnDesign/hooks/usePanel';
|
||||
import usePanel from '../../hooks/usePanel';
|
||||
import useDialog from '@/hooks/useDialog';
|
||||
import useModelerStore from '@/store/modules/modeler';
|
||||
|
@ -67,7 +67,10 @@
|
||||
<el-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="typeSelect.filter(e=>e.value === formData.type)[0]?typeSelect.filter(e=>e.value === formData.type)[0]?.label:'表达式'" prop="className">
|
||||
<el-form-item
|
||||
:label="typeSelect.filter((e) => e.value === formData.type)[0] ? typeSelect.filter((e) => e.value === formData.type)[0]?.label : '表达式'"
|
||||
prop="className"
|
||||
>
|
||||
<el-input v-model="formData.className" type="text"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -91,7 +94,7 @@ import { VxeTableEvents, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
|
||||
import { TaskListenerVO } from 'bpmnDesign';
|
||||
import { ModdleElement } from 'bpmn';
|
||||
|
||||
import usePanel from '@/components/BpmnDesign/hooks/usePanel';
|
||||
import usePanel from '../../hooks/usePanel';
|
||||
import useDialog from '@/hooks/useDialog';
|
||||
import useModelerStore from '@/store/modules/modeler';
|
||||
|
@ -1,498 +1,71 @@
|
||||
<template>
|
||||
<div class="containers-bpmn">
|
||||
<!-- dark模式下 连接线的箭头样式 -->
|
||||
<svg width="0" height="0" style="position: absolute">
|
||||
<defs>
|
||||
<marker id="markerArrow-dark-mode" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto">
|
||||
<path d="M 1 5 L 11 10 L 1 15 Z" class="arrow-dark" />
|
||||
</marker>
|
||||
</defs>
|
||||
</svg>
|
||||
<div v-loading="loading" class="app-containers-bpmn">
|
||||
<el-container class="h-full">
|
||||
<el-container style="align-items: stretch">
|
||||
<el-header>
|
||||
<div class="process-toolbar">
|
||||
<el-space wrap :size="10">
|
||||
<el-button size="small" type="primary" @click="saveXml">保 存</el-button>
|
||||
<el-dropdown size="small">
|
||||
<el-button size="small" type="primary"> 预 览 </el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Document" @click="previewXML">XML预览</el-dropdown-item>
|
||||
<el-dropdown-item icon="View" @click="previewSVG"> SVG预览</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<el-dropdown size="small">
|
||||
<el-button size="small" type="primary"> 下 载 </el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Download" @click="downloadXML">下载XML</el-dropdown-item>
|
||||
<el-dropdown-item icon="Download" @click="downloadSVG"> 下载SVG</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-tooltip effect="dark" content="新建" placement="bottom">
|
||||
<el-button size="small" icon="CirclePlus" @click="newDiagram" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
|
||||
<el-button size="small" icon="Rank" @click="fitViewport" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="放大" placement="bottom">
|
||||
<el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="缩小" placement="bottom">
|
||||
<el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="后退" placement="bottom">
|
||||
<el-button size="small" icon="Back" @click="bpmnModeler.get('commandStack').undo()" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="前进" placement="bottom">
|
||||
<el-button size="small" icon="Right" @click="bpmnModeler.get('commandStack').redo()" />
|
||||
</el-tooltip>
|
||||
</el-space>
|
||||
<div class="design">
|
||||
<el-dialog v-model="visible" width="100%" fullscreen :title="title">
|
||||
<div class="modeler">
|
||||
<bpmn-design ref="bpmnDesignRef" @save-call-back="saveCallBack"></bpmn-design>
|
||||
</div>
|
||||
</el-header>
|
||||
<div ref="canvas" class="canvas" />
|
||||
</el-container>
|
||||
<div :class="{ 'process-panel': true, 'hide': panelFlag }">
|
||||
<div class="process-panel-bar" @click="panelBarClick">
|
||||
<div class="open-bar">
|
||||
<el-link type="default" :underline="false">
|
||||
<svg-icon class-name="open-bar" :icon-class="panelFlag ? 'caret-back' : 'caret-forward'"></svg-icon>
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<transition enter-active-class="animate__animated animate__fadeIn">
|
||||
<div v-show="showPanel" v-if="bpmnModeler" class="panel-content">
|
||||
<PropertyPanel :modeler="bpmnModeler" />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</el-container>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-dialog v-model="perviewXMLShow" title="XML预览" width="80%" append-to-body>
|
||||
<highlightjs :code="xmlStr" language="XML" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div>
|
||||
<el-dialog v-model="perviewSVGShow" title="SVG预览" width="80%" append-to-body>
|
||||
<div style="text-align: center" v-html="svgData" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="BpmnDesign">
|
||||
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 './assets/style/index.scss';
|
||||
import { Canvas, Modeler } from 'bpmn';
|
||||
import PropertyPanel from './panel/index.vue';
|
||||
import BpmnModeler from 'bpmn-js/lib/Modeler.js';
|
||||
import defaultXML from '@/components/BpmnDesign/assets/defaultXML';
|
||||
import flowableModdle from '@/components/BpmnDesign/assets/moddle/flowable';
|
||||
import Modules from './assets/module/index';
|
||||
import useModelerStore from '@/store/modules/modeler';
|
||||
import useDialog from '@/hooks/useDialog';
|
||||
|
||||
const emit = defineEmits(['closeCallBack', 'saveCallBack']);
|
||||
|
||||
const { visible, title, openDialog, closeDialog } = useDialog({
|
||||
title: '编辑流程'
|
||||
});
|
||||
const modelerStore = useModelerStore();
|
||||
<script lang="ts" setup name="Design">
|
||||
import { getInfo, editModelXml } from '@/api/workflow/model';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const panelFlag = ref(false);
|
||||
const showPanel = ref(true);
|
||||
const canvas = ref<HTMLDivElement>();
|
||||
const panel = ref<HTMLDivElement>();
|
||||
const bpmnModeler = ref<Modeler>();
|
||||
const zoom = ref(1);
|
||||
const perviewXMLShow = ref(false);
|
||||
const perviewSVGShow = ref(false);
|
||||
const xmlStr = ref('');
|
||||
const svgData = ref('');
|
||||
const loading = ref(false);
|
||||
|
||||
const panelBarClick = () => {
|
||||
// 延迟执行,否则会导致面板收起时,属性面板不显示
|
||||
panelFlag.value = !panelFlag.value;
|
||||
setTimeout(() => {
|
||||
showPanel.value = !panelFlag.value;
|
||||
}, 100);
|
||||
import { ModelForm } from '@/api/workflow/model/types';
|
||||
import BpmnDesign from '@/bpmn/index.vue';
|
||||
import useDialog from '@/hooks/useDialog';
|
||||
const bpmnDesignRef = ref<InstanceType<typeof BpmnDesign>>();
|
||||
const modelForm = ref<ModelForm>();
|
||||
const emit = defineEmits(['closeCallBack']);
|
||||
const { visible, title } = useDialog({
|
||||
title: '编辑流程'
|
||||
});
|
||||
const modelId = ref('');
|
||||
const open = async (id) => {
|
||||
visible.value = true;
|
||||
modelId.value = id;
|
||||
const { data } = await getInfo(id);
|
||||
modelForm.value = data;
|
||||
bpmnDesignRef.value.initDiagram(modelForm.value.xml);
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化Canvas
|
||||
*/
|
||||
const initCanvas = () => {
|
||||
bpmnModeler.value = new BpmnModeler({
|
||||
container: canvas.value,
|
||||
// 键盘
|
||||
keyboard: {
|
||||
bindTo: window // 或者window,注意与外部表单的键盘监听事件是否冲突
|
||||
},
|
||||
propertiesPanel: {
|
||||
parent: panel.value
|
||||
},
|
||||
additionalModules: Modules,
|
||||
moddleExtensions: {
|
||||
flowable: flowableModdle
|
||||
//保存模型
|
||||
const saveCallBack = async (data) => {
|
||||
await proxy?.$modal.confirm('是否确认保存?');
|
||||
data.loading.value = true;
|
||||
modelForm.value.id = modelId.value;
|
||||
modelForm.value.xml = data.xml;
|
||||
modelForm.value.svg = data.svg;
|
||||
modelForm.value.key = data.key;
|
||||
modelForm.value.name = data.name;
|
||||
editModelXml(modelForm.value).then((res) => {
|
||||
if (res.code === 200) {
|
||||
visible.value = false;
|
||||
proxy?.$modal.msgSuccess('保存成功');
|
||||
emit('closeCallBack', data);
|
||||
}
|
||||
});
|
||||
data.loading.value = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化Model
|
||||
*/
|
||||
const initModel = () => {
|
||||
if (modelerStore.getModeler()) {
|
||||
modelerStore.getModeler().destroy();
|
||||
modelerStore.setModeler(undefined);
|
||||
}
|
||||
modelerStore.setModeler(bpmnModeler.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* 新建
|
||||
*/
|
||||
const newDiagram = async () => {
|
||||
await proxy?.$modal.confirm('是否确认新建');
|
||||
initDiagram();
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
const initDiagram = (xml?: string) => {
|
||||
if (!xml) xml = defaultXML;
|
||||
bpmnModeler.value.importXML(xml);
|
||||
};
|
||||
|
||||
/**
|
||||
* 自适应屏幕
|
||||
*/
|
||||
const fitViewport = () => {
|
||||
zoom.value = bpmnModeler.value.get<Canvas>('canvas').zoom('fit-viewport');
|
||||
const bbox = document.querySelector<SVGGElement>('.app-containers-bpmn .viewport').getBBox();
|
||||
const currentViewBox = bpmnModeler.value.get<Canvas>('canvas').viewbox();
|
||||
const elementMid = {
|
||||
x: bbox.x + bbox.width / 2 - 65,
|
||||
y: bbox.y + bbox.height / 2
|
||||
};
|
||||
bpmnModeler.value.get<Canvas>('canvas').viewbox({
|
||||
x: elementMid.x - currentViewBox.width / 2,
|
||||
y: elementMid.y - currentViewBox.height / 2,
|
||||
width: currentViewBox.width,
|
||||
height: currentViewBox.height
|
||||
});
|
||||
zoom.value = (bbox.width / currentViewBox.width) * 1.8;
|
||||
};
|
||||
/**
|
||||
* 放大或者缩小
|
||||
* @param zoomIn true 放大 | false 缩小
|
||||
*/
|
||||
const zoomViewport = (zoomIn = true) => {
|
||||
zoom.value = bpmnModeler.value.get<Canvas>('canvas').zoom();
|
||||
zoom.value += zoomIn ? 0.1 : -0.1;
|
||||
bpmnModeler.value.get<Canvas>('canvas').zoom(zoom.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载XML
|
||||
*/
|
||||
const downloadXML = async () => {
|
||||
try {
|
||||
const { xml } = await bpmnModeler.value.saveXML({ format: true });
|
||||
downloadFile(`${getProcessElement().name}.bpmn20.xml`, xml, 'application/xml');
|
||||
} catch (e) {
|
||||
proxy?.$modal.msgError(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载SVG
|
||||
*/
|
||||
const downloadSVG = async () => {
|
||||
try {
|
||||
const { svg } = await bpmnModeler.value.saveSVG();
|
||||
downloadFile(getProcessElement().name, svg, 'image/svg+xml');
|
||||
} catch (e) {
|
||||
proxy?.$modal.msgError(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* XML预览
|
||||
*/
|
||||
const previewXML = async () => {
|
||||
try {
|
||||
const { xml } = await bpmnModeler.value.saveXML({ format: true });
|
||||
xmlStr.value = xml;
|
||||
perviewXMLShow.value = true;
|
||||
} catch (e) {
|
||||
proxy?.$modal.msgError(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SVG预览
|
||||
*/
|
||||
const previewSVG = async () => {
|
||||
try {
|
||||
const { svg } = await bpmnModeler.value.saveSVG();
|
||||
svgData.value = svg;
|
||||
perviewSVGShow.value = true;
|
||||
} catch (e) {
|
||||
proxy?.$modal.msgError(e);
|
||||
}
|
||||
};
|
||||
|
||||
const curNodeInfo = reactive({
|
||||
curType: '', // 任务类型 用户任务
|
||||
curNode: '',
|
||||
expValue: '' //多用户和部门角色实现
|
||||
});
|
||||
|
||||
const downloadFile = (fileName: string, data: any, type: string) => {
|
||||
const a = document.createElement('a');
|
||||
const url = window.URL.createObjectURL(new Blob([data], { type: type }));
|
||||
a.href = url;
|
||||
a.download = fileName;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
const getProcessElement = () => {
|
||||
const rootElements = bpmnModeler.value?.getDefinitions().rootElements;
|
||||
for (let i = 0; i < rootElements.length; i++) {
|
||||
if (rootElements[i].$type === 'bpmn:Process') return rootElements[i];
|
||||
}
|
||||
};
|
||||
|
||||
const getProcess = () => {
|
||||
const element = getProcessElement();
|
||||
return {
|
||||
id: element.id,
|
||||
name: element.name
|
||||
};
|
||||
};
|
||||
|
||||
const saveXml = async () => {
|
||||
const { xml } = await bpmnModeler.value.saveXML({ format: true });
|
||||
const { svg } = await bpmnModeler.value.saveSVG();
|
||||
const process = getProcess();
|
||||
let data = {
|
||||
xml: xml,
|
||||
svg: svg,
|
||||
key: process.id,
|
||||
name: process.name,
|
||||
loading: loading
|
||||
};
|
||||
emit('saveCallBack', data);
|
||||
};
|
||||
|
||||
const open = (xml?: string) => {
|
||||
openDialog();
|
||||
nextTick(() => {
|
||||
initDiagram(xml);
|
||||
});
|
||||
};
|
||||
const close = () => {
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initCanvas();
|
||||
initModel();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 对外暴露子组件方法
|
||||
*/
|
||||
defineExpose({
|
||||
initDiagram,
|
||||
saveXml,
|
||||
open,
|
||||
close
|
||||
open
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/** 夜间模式 线条的颜色 */
|
||||
$stroke-color-dark: white;
|
||||
$bpmn-font-size: 12px;
|
||||
/** 日间模式 字体颜色 */
|
||||
$bpmn-font-color-dark: white;
|
||||
/** 夜间模式 字体颜色 */
|
||||
$bpmn-font-color-light: #222;
|
||||
|
||||
/* 背景网格 */
|
||||
@mixin djs-container {
|
||||
background-image: linear-gradient(90deg, hsl(0deg 0% 78.4% / 15%) 10%, transparent 0), linear-gradient(hsl(0deg 0% 78.4% / 15%) 10%, transparent 0) !important;
|
||||
background-size: 10px 10px !important;
|
||||
<style lang="scss" scoped>
|
||||
.design {
|
||||
:deep(.el-dialog .el-dialog__body) {
|
||||
max-height: 100% !important;
|
||||
min-height: calc(100vh - 80px);
|
||||
padding: 10px 0 10px 0 !important;
|
||||
}
|
||||
|
||||
html[class='light'] {
|
||||
/** 从左侧拖动时的背景图 */
|
||||
svg.new-parent {
|
||||
@include djs-container;
|
||||
}
|
||||
|
||||
/** 双击编辑元素时样式保持一致 */
|
||||
div.djs-direct-editing-parent {
|
||||
border-radius: 10px;
|
||||
background-color: transparent !important;
|
||||
color: $bpmn-font-color-light;
|
||||
}
|
||||
|
||||
g.djs-visual {
|
||||
.djs-label {
|
||||
fill: $bpmn-font-color-light !important;
|
||||
font-size: $bpmn-font-size !important;
|
||||
:deep(.el-dialog__header) {
|
||||
padding: 0 0 5px 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[class='dark'] {
|
||||
/** dark模式下 连接线的箭头样式 */
|
||||
.arrow-dark {
|
||||
stroke-width: 1px;
|
||||
stroke-linecap: round;
|
||||
stroke: $stroke-color-dark;
|
||||
fill: $stroke-color-dark;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
/** 从左侧拖动时的背景图 */
|
||||
svg.new-parent {
|
||||
background-color: black !important;
|
||||
@include djs-container;
|
||||
}
|
||||
|
||||
/** 双击编辑元素时样式保持一致 */
|
||||
div.djs-direct-editing-parent {
|
||||
border-radius: 10px;
|
||||
background-color: transparent !important;
|
||||
color: $bpmn-font-color-dark;
|
||||
}
|
||||
|
||||
/** 元素相关设置 */
|
||||
g.djs-visual {
|
||||
/** 元素边框 需要去除文字(.djs-label) */
|
||||
& > *:first-child:not(.djs-label) {
|
||||
stroke: $stroke-color-dark !important;
|
||||
}
|
||||
|
||||
/** 字体颜色 */
|
||||
.djs-label {
|
||||
fill: $bpmn-font-color-dark !important;
|
||||
font-size: $bpmn-font-size !important;
|
||||
}
|
||||
|
||||
/* 连接线样式 */
|
||||
path[data-corner-radius] {
|
||||
stroke: $stroke-color-dark !important;
|
||||
marker-end: url('#markerArrow-dark-mode') !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.containers-bpmn {
|
||||
height: 100%;
|
||||
.app-containers-bpmn {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@include djs-container;
|
||||
}
|
||||
.el-header {
|
||||
height: 35px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.process-panel {
|
||||
transition: width 0.25s ease-in;
|
||||
.process-panel-bar {
|
||||
width: 34px;
|
||||
height: 40px;
|
||||
.open-bar {
|
||||
width: 34px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
// 收起面板样式
|
||||
&.hide {
|
||||
width: 34px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
.process-panel-bar {
|
||||
width: 34px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
text-align: left;
|
||||
line-height: 34px;
|
||||
}
|
||||
.process-panel-bar:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pre {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
max-height: calc(80vh - 32px);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
.hljs {
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
padding: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.open-bar {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
.process-panel {
|
||||
box-sizing: border-box;
|
||||
padding: 0 8px 0 8px;
|
||||
border-left: 1px solid #eeeeee;
|
||||
box-shadow: #cccccc 0 0 8px;
|
||||
max-height: 100%;
|
||||
width: 25%;
|
||||
height: calc(100vh - 80px);
|
||||
.el-collapse {
|
||||
height: calc(100vh - 162px);
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// 任务栏 透明度
|
||||
//:deep(.djs-palette) {
|
||||
// opacity: 0.3;
|
||||
// transition: all 1s;
|
||||
//}
|
||||
//
|
||||
//:deep(.djs-palette:hover) {
|
||||
// opacity: 1;
|
||||
// transition: all 1s;
|
||||
//}
|
||||
</style>
|
||||
|
@ -1,67 +0,0 @@
|
||||
<template>
|
||||
<div class="design">
|
||||
<el-dialog v-model="visible" width="100%" fullscreen :title="title">
|
||||
<div class="modeler">
|
||||
<bpmn-design ref="bpmnDesignRef" @save-call-back="saveCallBack"></bpmn-design>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="Design">
|
||||
import { getInfo, editModelXml } from '@/api/workflow/model';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
import { ModelForm } from '@/api/workflow/model/types';
|
||||
import BpmnDesign from '@/components/BpmnDesign';
|
||||
import useDialog from '@/hooks/useDialog';
|
||||
const bpmnDesignRef = ref<InstanceType<typeof BpmnDesign>>();
|
||||
const modelForm = ref<ModelForm>();
|
||||
const emit = defineEmits(['closeCallBack']);
|
||||
const { visible, title } = useDialog({
|
||||
title: '编辑流程'
|
||||
});
|
||||
const modelId = ref('');
|
||||
const open = async (id) => {
|
||||
visible.value = true;
|
||||
modelId.value = id;
|
||||
const { data } = await getInfo(id);
|
||||
modelForm.value = data;
|
||||
bpmnDesignRef.value.initDiagram(modelForm.value.xml);
|
||||
};
|
||||
//保存模型
|
||||
const saveCallBack = async (data) => {
|
||||
await proxy?.$modal.confirm('是否确认保存?');
|
||||
data.loading.value = true;
|
||||
modelForm.value.id = modelId.value;
|
||||
modelForm.value.xml = data.xml;
|
||||
modelForm.value.svg = data.svg;
|
||||
modelForm.value.key = data.key;
|
||||
modelForm.value.name = data.name;
|
||||
editModelXml(modelForm.value).then((res) => {
|
||||
if (res.code === 200) {
|
||||
visible.value = false;
|
||||
proxy?.$modal.msgSuccess('保存成功');
|
||||
emit('closeCallBack', data);
|
||||
}
|
||||
});
|
||||
data.loading.value = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* 对外暴露子组件方法
|
||||
*/
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.design {
|
||||
:deep(.el-dialog .el-dialog__body) {
|
||||
max-height: 100% !important;
|
||||
min-height: calc(100vh - 50px);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -138,7 +138,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="Model">
|
||||
import Design from './design.vue';
|
||||
import Design from '../../../components/BpmnDesign/index.vue';
|
||||
import { listModel, addModel, delModel, modelDeploy, getInfo, update } from '@/api/workflow/model';
|
||||
import { ModelQuery, ModelForm, ModelVO } from '@/api/workflow/model/types';
|
||||
import { listCategory } from '@/api/workflow/category';
|
||||
|
Loading…
x
Reference in New Issue
Block a user