集成 bpmn js

This commit is contained in:
tanyp 2025-03-22 23:11:36 +08:00
parent 4504304d4c
commit 3b8b8475e8
13 changed files with 3333 additions and 14 deletions

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,9 @@
"dependencies": {
"amis": "^6.0.0",
"axios": "^1.3.4",
"bpmn-js": "^18.4.0",
"bpmn-js-properties-panel": "^5.35.0",
"camunda-bpmn-moddle": "^7.0.1",
"copy-to-clipboard": "^3.3.3",
"element-plus": "^2.6.3",
"vue": "^3.4.21",

View File

@ -0,0 +1,75 @@
import request from '@/utils/request'
// 分页
export function page(params:any){
return new Promise((resolve, reject) => {
request({
url: '/tansci/system/workflow/page',
method: 'get',
params: params
}).then((res:any) => {
resolve(res.data)
}).catch((e:any) => {
reject(e)
})
})
}
// 详情
export function details(params:any){
return new Promise((resolve, reject) => {
request({
url: '/tansci/system/workflow/details',
method: 'get',
params: params
}).then((res:any) => {
resolve(res.data)
}).catch((e:any) => {
reject(e)
})
})
}
// 添加
export function save(data:any){
return new Promise((resolve, reject) => {
request({
url: '/tansci/system/workflow/save',
method: 'post',
data: data
}).then((res:any) => {
resolve(res.data)
}).catch((e:any) => {
reject(e)
})
})
}
// 删除
export function del(id:String){
return new Promise((resolve, reject) => {
request({
url: '/tansci/system/workflow/delete/' + id,
method: 'get'
}).then((res:any) => {
resolve(res.data)
}).catch((e:any) => {
reject(e)
})
})
}
// 修改
export function update(data:any){
return new Promise((resolve, reject) => {
request({
url: '/tansci/system/workflow/update',
method: 'post',
data: data
}).then((res:any) => {
resolve(res.data)
}).catch((e:any) => {
reject(e)
})
})
}

View File

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd">
<collaboration id="sid-c0e745ff-361e-4afb-8c8d-2a1fc32b1424">
<participant id="sid-87F4C1D6-25E1-4A45-9DA7-AD945993D06F" name="Customer" processRef="sid-C3803939-0872-457F-8336-EAE484DC4A04" />
</collaboration>
<process id="sid-C3803939-0872-457F-8336-EAE484DC4A04" name="Customer" processType="None" isClosed="false" isExecutable="false">
<extensionElements />
<laneSet id="sid-b167d0d7-e761-4636-9200-76b7f0e8e83a">
<lane id="sid-57E4FE0D-18E4-478D-BC5D-B15164E93254">
<flowNodeRef>sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26</flowNodeRef>
<flowNodeRef>sid-E49425CF-8287-4798-B622-D2A7D78EF00B</flowNodeRef>
<flowNodeRef>sid-D7F237E8-56D0-4283-A3CE-4F0EFE446138</flowNodeRef>
<flowNodeRef>sid-E433566C-2289-4BEB-A19C-1697048900D2</flowNodeRef>
<flowNodeRef>sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9</flowNodeRef>
<flowNodeRef>SCAN_OK</flowNodeRef>
</lane>
</laneSet>
<task id="sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26" name="Scan QR code">
<incoming>sid-4DC479E5-5C20-4948-BCFC-9EC5E2F66D8D</incoming>
<outgoing>sid-EE8A7BA0-5D66-4F8B-80E3-CC2751B3856A</outgoing>
</task>
<task id="sid-E49425CF-8287-4798-B622-D2A7D78EF00B" name="Open product information in mobile app">
<incoming>sid-8B820AF5-DC5C-4618-B854-E08B71FB55CB</incoming>
<outgoing>sid-57EB1F24-BD94-479A-BF1F-57F1EAA19C6C</outgoing>
</task>
<startEvent id="sid-D7F237E8-56D0-4283-A3CE-4F0EFE446138" name="Notices&#10;QR code">
<outgoing>sid-7B791A11-2F2E-4D80-AFB3-91A02CF2B4FD</outgoing>
</startEvent>
<endEvent id="sid-E433566C-2289-4BEB-A19C-1697048900D2" name="Is informed">
<incoming>sid-57EB1F24-BD94-479A-BF1F-57F1EAA19C6C</incoming>
</endEvent>
<exclusiveGateway id="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9">
<incoming>sid-7B791A11-2F2E-4D80-AFB3-91A02CF2B4FD</incoming>
<incoming>sid-337A23B9-A923-4CCE-B613-3E247B773CCE</incoming>
<outgoing>sid-4DC479E5-5C20-4948-BCFC-9EC5E2F66D8D</outgoing>
</exclusiveGateway>
<exclusiveGateway id="SCAN_OK" name="Scan successful?&#10;">
<incoming>sid-EE8A7BA0-5D66-4F8B-80E3-CC2751B3856A</incoming>
<outgoing>sid-8B820AF5-DC5C-4618-B854-E08B71FB55CB</outgoing>
<outgoing>sid-337A23B9-A923-4CCE-B613-3E247B773CCE</outgoing>
</exclusiveGateway>
<sequenceFlow id="sid-337A23B9-A923-4CCE-B613-3E247B773CCE" name="Yes" sourceRef="SCAN_OK" targetRef="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9" />
<sequenceFlow id="sid-4DC479E5-5C20-4948-BCFC-9EC5E2F66D8D" sourceRef="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9" targetRef="sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26" />
<sequenceFlow id="sid-8B820AF5-DC5C-4618-B854-E08B71FB55CB" name="No" sourceRef="SCAN_OK" targetRef="sid-E49425CF-8287-4798-B622-D2A7D78EF00B" />
<sequenceFlow id="sid-57EB1F24-BD94-479A-BF1F-57F1EAA19C6C" sourceRef="sid-E49425CF-8287-4798-B622-D2A7D78EF00B" targetRef="sid-E433566C-2289-4BEB-A19C-1697048900D2" />
<sequenceFlow id="sid-EE8A7BA0-5D66-4F8B-80E3-CC2751B3856A" sourceRef="sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26" targetRef="SCAN_OK" />
<sequenceFlow id="sid-7B791A11-2F2E-4D80-AFB3-91A02CF2B4FD" sourceRef="sid-D7F237E8-56D0-4283-A3CE-4F0EFE446138" targetRef="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9" />
</process>
<bpmndi:BPMNDiagram id="sid-74620812-92c4-44e5-949c-aa47393d3830">
<bpmndi:BPMNPlane id="sid-cdcae759-2af7-4a6d-bd02-53f3352a731d" bpmnElement="sid-c0e745ff-361e-4afb-8c8d-2a1fc32b1424">
<bpmndi:BPMNShape id="sid-87F4C1D6-25E1-4A45-9DA7-AD945993D06F_gui" bpmnElement="sid-87F4C1D6-25E1-4A45-9DA7-AD945993D06F" isHorizontal="true">
<omgdc:Bounds x="83" y="105" width="933" height="250" />
<bpmndi:BPMNLabel labelStyle="sid-84cb49fd-2f7c-44fb-8950-83c3fa153d3b">
<omgdc:Bounds x="47.49999999999999" y="170.42857360839844" width="12.000000000000014" height="59.142852783203125" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="sid-57E4FE0D-18E4-478D-BC5D-B15164E93254_gui" bpmnElement="sid-57E4FE0D-18E4-478D-BC5D-B15164E93254" isHorizontal="true">
<omgdc:Bounds x="113" y="105" width="903" height="250" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26_gui" bpmnElement="sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26">
<omgdc:Bounds x="393" y="170" width="100" height="80" />
<bpmndi:BPMNLabel labelStyle="sid-84cb49fd-2f7c-44fb-8950-83c3fa153d3b">
<omgdc:Bounds x="360.5" y="172" width="84" height="12" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="sid-E49425CF-8287-4798-B622-D2A7D78EF00B_gui" bpmnElement="sid-E49425CF-8287-4798-B622-D2A7D78EF00B">
<omgdc:Bounds x="728" y="170" width="100" height="80" />
<bpmndi:BPMNLabel labelStyle="sid-84cb49fd-2f7c-44fb-8950-83c3fa153d3b">
<omgdc:Bounds x="695.9285736083984" y="162" width="83.14285278320312" height="36" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="sid-EE8A7BA0-5D66-4F8B-80E3-CC2751B3856A_gui" bpmnElement="sid-EE8A7BA0-5D66-4F8B-80E3-CC2751B3856A">
<omgdi:waypoint x="493" y="210" />
<omgdi:waypoint x="585" y="210" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="494" y="185" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="sid-8B820AF5-DC5C-4618-B854-E08B71FB55CB_gui" bpmnElement="sid-8B820AF5-DC5C-4618-B854-E08B71FB55CB">
<omgdi:waypoint x="635" y="210" />
<omgdi:waypoint x="728" y="210" />
<bpmndi:BPMNLabel labelStyle="sid-e0502d32-f8d1-41cf-9c4a-cbb49fecf581">
<omgdc:Bounds x="642" y="185" width="16" height="12" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="sid-7B791A11-2F2E-4D80-AFB3-91A02CF2B4FD_gui" bpmnElement="sid-7B791A11-2F2E-4D80-AFB3-91A02CF2B4FD">
<omgdi:waypoint x="223" y="210" />
<omgdi:waypoint x="275" y="210" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="204" y="185" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="sid-4DC479E5-5C20-4948-BCFC-9EC5E2F66D8D_gui" bpmnElement="sid-4DC479E5-5C20-4948-BCFC-9EC5E2F66D8D">
<omgdi:waypoint x="325" y="210" />
<omgdi:waypoint x="393" y="210" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="314" y="185" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="sid-57EB1F24-BD94-479A-BF1F-57F1EAA19C6C_gui" bpmnElement="sid-57EB1F24-BD94-479A-BF1F-57F1EAA19C6C">
<omgdi:waypoint x="828" y="210" />
<omgdi:waypoint x="901" y="210" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="820" y="185" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="sid-337A23B9-A923-4CCE-B613-3E247B773CCE_gui" bpmnElement="sid-337A23B9-A923-4CCE-B613-3E247B773CCE">
<omgdi:waypoint x="611" y="234" />
<omgdi:waypoint x="610.5" y="299" />
<omgdi:waypoint x="300.5" y="299" />
<omgdi:waypoint x="301" y="234" />
<bpmndi:BPMNLabel labelStyle="sid-e0502d32-f8d1-41cf-9c4a-cbb49fecf581">
<omgdc:Bounds x="585" y="236" width="21" height="12" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="StartEvent_0l6sgn0_di" bpmnElement="sid-D7F237E8-56D0-4283-A3CE-4F0EFE446138">
<omgdc:Bounds x="187" y="192" width="36" height="36" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="182" y="229" width="46" height="24" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="EndEvent_0xwuvv5_di" bpmnElement="sid-E433566C-2289-4BEB-A19C-1697048900D2">
<omgdc:Bounds x="901" y="192" width="36" height="36" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="892" y="231" width="56" height="12" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="ExclusiveGateway_1g0eih2_di" bpmnElement="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9" isMarkerVisible="true">
<omgdc:Bounds x="275" y="185" width="50" height="50" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="210" y="160" width="90" height="12" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="ExclusiveGateway_0vci1x5_di" bpmnElement="SCAN_OK" isMarkerVisible="true">
<omgdc:Bounds x="585" y="185" width="50" height="50" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="568" y="157" width="88" height="24" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
<bpmndi:BPMNLabelStyle id="sid-e0502d32-f8d1-41cf-9c4a-cbb49fecf581">
<omgdc:Font name="Arial" size="11" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false" />
</bpmndi:BPMNLabelStyle>
<bpmndi:BPMNLabelStyle id="sid-84cb49fd-2f7c-44fb-8950-83c3fa153d3b">
<omgdc:Font name="Arial" size="12" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false" />
</bpmndi:BPMNLabelStyle>
</bpmndi:BPMNDiagram>
</definitions>

View File

@ -0,0 +1,192 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import BpmnModeler from "bpmn-js/lib/Modeler.js"
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-embedded.css"
import "bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css"
import {
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
CamundaPlatformPropertiesProviderModule
} from "bpmn-js-properties-panel"
import camundaBpmnModdle from "camunda-bpmn-moddle/resources/camunda.json"
import translate from './translate/translate.js'
const bpmnCanvas = ref();
const bpmnProperties = ref();
const bpmnModeler = ref();
const props = defineProps({
xmlStr: {
type: String,
default: null
},
isPanel:{
type: Boolean,
default: true
}
})
const state = reactive({
scale: 1.0
})
defineExpose({
bpmnModeler
})
onMounted(()=>{
onModelerInit()
})
function onModelerInit() {
bpmnModeler.value = new BpmnModeler({
//
container: bpmnCanvas.value,
//
propertiesPanel: {
parent: bpmnProperties.value,
},
additionalModules: [
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
CamundaPlatformPropertiesProviderModule,
{translate: ["value", translate]},
],
moddleExtensions: {
camunda: camundaBpmnModdle,
},
})
let xmlStr:string = props.xmlStr;
if(!props.xmlStr){
xmlStr = `xml <?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="sid-38422fae-e03e-43a3-bef4-bd33b32041b2" targetNamespace="http://bpmn.io/bpmn" exporter="bpmn-js (https://demo.bpmn.io)" exporterVersion="5.1.2">
<process id="Process" name="默认模板" isExecutable="true" camunda:versionTag="0.0.1">
</process>
<bpmndi:BPMNDiagram id="BpmnDiagram_1">
<bpmndi:BPMNPlane id="BpmnPlane_1" bpmnElement="Process">
<bpmndi:BPMNShape id="StartEvent_1y45yut_di" bpmnElement="StartEvent">
<omgdc:Bounds x="152" y="102" width="36" height="36" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="160" y="145" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>`
}
bpmnModeler.value.importXML(xmlStr).then(() => {
console.log("success");
bpmnModeler.value.get("canvas").zoom("fit-viewport");
}).catch((error:any) => {
console.log(error);
});
}
// XML
async function onSaveXml() {
const { xml } = await bpmnModeler.value.saveXML({ format: true });
}
// SVG
async function onSaveSvg() {
const { svg } = await bpmnModeler.value.saveSVG();
}
//
function onHandlerZoom(radio:any) {
bpmnModeler.value.get("commandStack").undo();
const newScale = !radio ? 1.0 : state.scale + radio;
bpmnModeler.value.get('canvas').zoom(newScale);
state.scale = newScale;
}
// 退
function onHandleUndo() {
bpmnModeler.value.get("commandStack").undo();
}
//
function onHandleRedo() {
bpmnModeler.value.get("commandStack").redo();
}
//
async function onHandleDownload() {
const { xml } = await bpmnModeler.value.saveXML({ format: true });
const dataTrack = "bpmn";
const a = document.createElement("a");
const name = `diagram.${dataTrack}`;
a.setAttribute("href",`data:application/bpmn20-xml;charset=UTF-8,${encodeURIComponent(xml)}`);
a.setAttribute("target", "_blank");
a.setAttribute("dataTrack", `diagram:download-${dataTrack}`);
a.setAttribute("download", name);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
//
function onHandleOnchangeFile(file:any) {
const reader = new FileReader();
reader.readAsText(file.raw, "utf-8");
reader.onload = async (event) => {
await bpmnModeler.value.importXML(event.target.result);
}
}
</script>
<template>
<div class="bpmn-container">
<div class="bpmn-header">
<el-button-group size="small">
<el-tooltip effect="light" content="还原"><el-button icon="Refresh" @click="onHandlerZoom(0)"></el-button></el-tooltip>
<el-tooltip effect="light" content="放大"><el-button icon="CirclePlus" @click="onHandlerZoom(0.1)"></el-button></el-tooltip>
<el-tooltip effect="light" content="缩小"><el-button icon="Remove" @click="onHandlerZoom(-0.1)"></el-button></el-tooltip>
<el-tooltip effect="light" content="撤销"><el-button icon="RefreshLeft" @click="onHandleUndo"></el-button></el-tooltip>
<el-tooltip effect="light" content="恢复"><el-button icon="RefreshRight" @click="onHandleRedo"></el-button></el-tooltip>
<el-tooltip effect="light" content="下载"><el-button icon="Download" @click="onHandleDownload"></el-button></el-tooltip>
<el-tooltip effect="light" content="导入">
<el-upload style="display: inline-block" :file-list="fileList" action="" :auto-upload="false" :show-file-list="false" :on-change="onHandleOnchangeFile">
<el-button icon="Upload"></el-button>
</el-upload>
</el-tooltip>
</el-button-group>
</div>
<div class="bpmn-mall">
<!-- 内容区 -->
<div id="container" ref="bpmnCanvas"></div>
<!-- 右侧控制区 -->
<div id="properties" ref="bpmnProperties" v-if="props.isPanel" class="bpmn-panel"></div>
</div>
</div>
</template>
<style lang="scss">
@import './properties-panel.css';
.bpmn-container{
background-color: white;
position: relative;
height: 85vh;
.bpmn-header{
padding-bottom: 0.4rem;
}
.bpmn-mall {
// background-image: linear-gradient(90deg,hsla(0,0%,78.4%,.15) 10%,transparent 0),linear-gradient(hsla(0,0%,78.4%,.15) 10%,transparent 0);
// background-size: 10px 10px;
background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') repeat !important;
width:100%;
height:100%;
display: grid;
}
.bpmn-panel {
width: 260px;
position: absolute;
right: 1px;
border: 1px solid rgba(0,0,0,0.1);
}
/* 右下角logo */
.bjs-powered-by {
display: none;
}
}
</style>

View File

@ -0,0 +1,56 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import BpmnViewer from "bpmn-js/lib/Viewer.js"
const bpmnViewer = ref();
const viewerCanvas = ref();
const props = defineProps({
xmlStr: {
type: String,
default: null
}
})
onMounted(()=>{
onViewerInit()
})
function onViewerInit() {
console.log(1111111)
//
if(bpmnViewer && bpmnViewer.value){
bpmnViewer && bpmnViewer.value.destroy();
viewerCanvas.value.innerHTML = "";
}
//
bpmnViewer.value = new BpmnViewer({
container: viewerCanvas.value,
height: 520,
})
let xmlStr:string = props.xmlStr;
bpmnViewer.value.importXML(xmlStr).then(() => {
console.log("success");
bpmnViewer.value.get("canvas").zoom("fit-viewport");
}).catch((error:any) => {
console.log(error);
});
}
</script>
<template>
<div class="bpmn-viewer-container">
<div id="container" ref="viewerCanvas"></div>
</div>
</template>
<style lang="scss">
.bpmn-viewer-container{
background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') repeat !important;
width:100%;
height:100%;
.bjs-powered-by {
display: none;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
import translations from "./zh";
export default function customTranslate(template:any, replacements:any) {
replacements = replacements || {};
template = translations[template] || template;
return template.replace(/{([^}]+)}/g, function(_, key) {
let str = replacements[key];
if (
translations[replacements[key]] !== null &&
translations[replacements[key]] !== "undefined"
) {
str = translations[replacements[key]];
}
return str || "{" + key + "}";
});
}

View File

@ -0,0 +1,251 @@
export default {
// Labels
"Activate the global connect tool": "激活全局连接工具",
"Append {type}": "追加 {type}",
"Append EndEvent": "追加 结束事件 ",
"Append Task": "追加 任务",
"Append Gateway": "追加 网关",
"Append Intermediate/Boundary Event": "追加 中间/边界 事件",
"Add Lane above": "在上面添加道",
"Divide into two Lanes": "分割成两个道",
"Divide into three Lanes": "分割成三个道",
"Add Lane below": "在下面添加道",
"Append compensation activity": "追加补偿活动",
"Change type": "修改类型",
"Connect using Association": "使用关联连接",
"Connect using Sequence/MessageFlow or Association": "使用顺序/消息流或者关联连接",
"Connect using DataInputAssociation": "使用数据输入关联连接",
"Remove": "移除",
"Activate the hand tool": "激活抓手工具",
"Activate the lasso tool": "激活套索工具",
"Activate the create/remove space tool": "激活创建/删除空间工具",
"Create expanded SubProcess": "创建扩展子过程",
"Create IntermediateThrowEvent/BoundaryEvent": "创建中间抛出事件/边界事件",
"Create Pool/Participant": "创建池/参与者",
"Parallel Multi Instance": "并行多重事件",
"Sequential Multi Instance": "时序多重事件",
"DataObjectReference": "数据对象参考",
"DataStoreReference": "数据存储参考",
"Loop": "循环",
"Ad-hoc": "即席",
"Create {type}": "创建 {type}",
"Create Task": "创建任务",
"Create StartEvent": "创建开始事件",
"Create EndEvent": "创建结束事件",
"Create Group": "创建组",
"Task": "任务",
"Send Task": "发送任务",
"Receive Task": "接收任务",
"User Task": "用户任务",
"Manual Task": "手工任务",
"Business Rule Task": "业务规则任务",
"Service Task": "服务任务",
"Script Task": "脚本任务",
"Call Activity": "调用活动",
"Sub Process (collapsed)": "子流程(折叠的)",
"Sub Process (expanded)": "子流程(展开的)",
"Start Event": "开始事件",
"StartEvent": "开始事件",
"Intermediate Throw Event": "中间事件",
"End Event": "结束事件",
"EndEvent": "结束事件",
"Create Gateway": "创建网关",
"GateWay": "网关",
"Create Intermediate/Boundary Event": "创建中间/边界事件",
"Message Start Event": "消息开始事件",
"Timer Start Event": "定时开始事件",
"Conditional Start Event": "条件开始事件",
"Signal Start Event": "信号开始事件",
"Error Start Event": "错误开始事件",
"Escalation Start Event": "升级开始事件",
"Compensation Start Event": "补偿开始事件",
"Message Start Event (non-interrupting)": "消息开始事件(非中断)",
"Timer Start Event (non-interrupting)": "定时开始事件(非中断)",
"Conditional Start Event (non-interrupting)": "条件开始事件(非中断)",
"Signal Start Event (non-interrupting)": "信号开始事件(非中断)",
"Escalation Start Event (non-interrupting)": "升级开始事件(非中断)",
"Message Intermediate Catch Event": "消息中间捕获事件",
"Message Intermediate Throw Event": "消息中间抛出事件",
"Timer Intermediate Catch Event": "定时中间捕获事件",
"Escalation Intermediate Throw Event": "升级中间抛出事件",
"Conditional Intermediate Catch Event": "条件中间捕获事件",
"Link Intermediate Catch Event": "链接中间捕获事件",
"Link Intermediate Throw Event": "链接中间抛出事件",
"Compensation Intermediate Throw Event": "补偿中间抛出事件",
"Signal Intermediate Catch Event": "信号中间捕获事件",
"Signal Intermediate Throw Event": "信号中间抛出事件",
"Message End Event": "消息结束事件",
"Escalation End Event": "定时结束事件",
"Error End Event": "错误结束事件",
"Cancel End Event": "取消结束事件",
"Compensation End Event": "补偿结束事件",
"Signal End Event": "信号结束事件",
"Terminate End Event": "终止结束事件",
"Message Boundary Event": "消息边界事件",
"Message Boundary Event (non-interrupting)": "消息边界事件(非中断)",
"Timer Boundary Event": "定时边界事件",
"Timer Boundary Event (non-interrupting)": "定时边界事件(非中断)",
"Escalation Boundary Event": "升级边界事件",
"Escalation Boundary Event (non-interrupting)": "升级边界事件(非中断)",
"Conditional Boundary Event": "条件边界事件",
"Conditional Boundary Event (non-interrupting)": "条件边界事件(非中断)",
"Error Boundary Event": "错误边界事件",
"Cancel Boundary Event": "取消边界事件",
"Signal Boundary Event": "信号边界事件",
"Signal Boundary Event (non-interrupting)": "信号边界事件(非中断)",
"Compensation Boundary Event": "补偿边界事件",
"Exclusive Gateway": "互斥网关",
"Parallel Gateway": "并行网关",
"Inclusive Gateway": "相容网关",
"Complex Gateway": "复杂网关",
"Event based Gateway": "事件网关",
"Transaction": "转运",
"Sub Process": "子流程",
"Event Sub Process": "事件子流程",
"Collapsed Pool": "折叠池",
"Expanded Pool": "展开池",
// Errors
"no parent for {element} in {parent}": "在{parent}里,{element}没有父类",
"no shape type specified": "没有指定的形状类型",
"flow elements must be children of pools/participants": "流元素必须是池/参与者的子类",
"out of bounds release": "out of bounds release",
"more than {count} child lanes": "子道大于{count} ",
"element required": "元素不能为空",
"diagram not part of bpmn:Definitions": "流程图不符合bpmn规范",
"no diagram to display": "没有可展示的流程图",
"no process or collaboration to display": "没有可展示的流程/协作",
"element {element} referenced by {referenced}#{property} not yet drawn": "由{referenced}#{property}引用的{element}元素仍未绘制",
"already rendered {element}": "{element} 已被渲染",
"failed to import {element}": "导入{element}失败",
//属性面板的参数
"Id": "编号",
"Name": "名称",
"General": "常规",
"Details": "详情",
"Message Name": "消息名称",
"Message": "消息",
"Initiator": "创建者",
"Asynchronous Continuations": "持续异步",
"Asynchronous Before": "异步前",
"Asynchronous After": "异步后",
"Job Configuration": "工作配置",
"Exclusive": "排除",
"Job Priority": "工作优先级",
"Retry Time Cycle": "重试时间周期",
"Documentation": "文档",
"Element Documentation": "元素文档",
"History Configuration": "历史配置",
"History Time To Live": "历史的生存时间",
"Forms": "表单",
"Form Key": "表单key",
"Form Fields": "表单字段",
"Business Key": "业务key",
"Form Field": "表单字段",
"ID": "编号",
"Type": "类型",
"Label": "名称",
"Default Value": "默认值",
"Validation": "校验",
"Add Constraint": "添加约束",
"Config": "配置",
"Properties": "属性",
"Add Property": "添加属性",
"Value": "值",
"Add": "添加",
"Values": "值",
"Add Value": "添加值",
"Listeners": "监听器",
"Execution Listener": "执行监听",
"Event Type": "事件类型",
"Listener Type": "监听器类型",
"Java Class": "Java类",
"Expression": "表达式",
"Must provide a value": "必须提供一个值",
"Delegate Expression": "代理表达式",
"Script": "脚本",
"Script Format": "脚本格式",
"Script Type": "脚本类型",
"Inline Script": "内联脚本",
"External Script": "外部脚本",
"Resource": "资源",
"Field Injection": "字段注入",
"Extensions": "扩展",
"Input/Output": "输入/输出",
"Input Parameters": "输入参数",
"Output Parameters": "输出参数",
"Parameters": "参数",
"Output Parameter": "输出参数",
"Timer Definition Type": "定时器定义类型",
"Timer Definition": "定时器定义",
"Date": "日期",
"Duration": "持续",
"Cycle": "循环",
"Signal": "信号",
"Signal Name": "信号名称",
"Escalation": "升级",
"Error": "错误",
"Link Name": "链接名称",
"Condition": "条件名称",
"Variable Name": "变量名称",
"Variable Event": "变量事件",
"Specify more than one variable change event as a comma separated list.": "多个变量事件以逗号隔开",
"Wait for Completion": "等待完成",
"Activity Ref": "活动参考",
"Version Tag": "版本标签",
"Executable": "可执行文件",
"External Task Configuration": "扩展任务配置",
"Task Priority": "任务优先级",
"External": "外部",
"Connector": "连接器",
"Must configure Connector": "必须配置连接器",
"Connector Id": "连接器编号",
"Implementation": "实现方式",
"Field Injections": "字段注入",
"Fields": "字段",
"Result Variable": "结果变量",
"Topic": "主题",
"Configure Connector": "配置连接器",
"Input Parameter": "输入参数",
"Assignee": "代理人",
"Candidate Users": "候选用户",
"Candidate Groups": "候选组",
"Due Date": "到期时间",
"Follow Up Date": "跟踪日期",
"Priority": "优先级",
"The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)": "跟踪日期必须符合EL表达式 ${someDate} ,或者一个ISO标准日期2015-06-26T09:54:00",
"The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)": "跟踪日期必须符合EL表达式 ${someDate} ,或者一个ISO标准日期2015-06-26T09:54:00",
"Variables": "变量",
"Candidate Starter Configuration": "候选开始配置",
"Task Listener": "任务监听器",
"Candidate Starter Groups": "候选开始组",
"Candidate Starter Users": "候选开始用户",
"Tasklist Configuration": "任务列表配置",
"Startable": "启动",
"Specify more than one group as a comma separated list.": "指定多个组,用逗号分隔",
"Specify more than one user as a comma separated list.": "指定多个用户,用逗号分隔",
"This maps to the process definition key.": "这会映射为流程定义的键",
"CallActivity Type": "调用活动类型",
"Condition Type": "条件类型",
"Create UserTask": "创建用户任务",
"Create CallActivity": "创建调用活动",
"Called Element": "调用元素",
"Create DataObjectReference": "创建数据对象引用",
"Create DataStoreReference": "创建数据存储引用",
"Multi Instance": "多实例",
"Loop Cardinality": "实例数量",
"Collection": "任务参与人列表",
"Element Variable": "元素变量",
"Completion Condition": "完成条件",
"Open minimap": "打开小地图",
"Close minimap": "关闭小地图",
"History cleanup": "历史记录清理",
"Tasklist": "任务列表",
"Candidate starter": "候选人启动器",
"External task": "外部任务",
"Job execution": "任务执行",
"Execution listeners": "执行侦听器",
"Extension properties": "扩展属性",
"Time to live":"存活时间",
"Candidate starter groups":"候选人启动小组",
"Candidate starter users":"候选初学者用户",
}

View File

@ -32,7 +32,6 @@ export default[
children: [
{
path: 'index',
name: 'index',
icon: 'HomeFilled',
meta: { title: "首页" },
isShow: false,

View File

@ -0,0 +1,157 @@
<script setup lang="ts">
import {onMounted, reactive, ref} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import type {FormInstance} from 'element-plus'
import Table from '@/components/Table.vue'
import BpmnViewer from "@/components/bpmn/BpmnViewer.vue"
import {useRouter} from 'vue-router'
import {page,update,del} from '@/api/work/workflow'
const router = useRouter()
const searchForm = reactive({
name: null
})
const table = reactive({
loading: false,
page: {
current: 1,
size: 10,
total: 1,
},
operation:{
isShow: true,
width: '240'
},
tableTitle: [
{prop:'name',label:'流程名称'},
{prop:'status',label:'是否禁用',type:'switch',
option:{
activeValue:0,activeColor:'#13ce66',activeText:'启用',
inactiveValue:1,inactiveColor:'#ff4949',inactiveText:'禁用',
inlinePrompt: false
}
},
{prop:'updateTime',label:'更新时间'},
{prop:'remarks',label:'描述'}
],
tableData:[],
})
onMounted(()=>{
onPage()
})
function onPage(){
table.loading = true;
page(Object.assign(table.page, searchForm)).then((res:any)=>{
if(res){
table.loading = false;
table.tableData = res.result.records;
table.page.current = res.result.current;
table.page.size = res.result.size;
table.page.total = res.result.total;
}
})
}
function onSizeChange(e){
table.page.size = e;
onPage();
}
function onCurrentChange(e){
table.page.current = e;
onPage();
}
function onRefresh(){
searchForm.name = null
onPage();
}
function onSearch(){
onPage();
}
function onSwitchChange(row:any){
update({
id: row.id,
status: row.status
}).then(res=>{
if(res){
ElMessage.success('操作成功!');
onPage();
}
});
}
//
function onAdd(){
router.push({
path: '/work/WorkflowAdd',
query: { id: null }
});
}
//
function onEdit(val:any){
router.push({
path: '/work/WorkflowAdd',
query: { id: val.column.row.id }
});
}
//
function onDelete(val:any){
ElMessageBox.confirm('此操作将永久删除, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
del(val.column.row.id).then(res=>{
if(res){
ElMessage.success('删除成功!');
onPage();
}
})
}).catch(e=>{
console.log(e)
})
}
//
const viewer = reactive({
viewerVisible: false,
xmlStr: ''
})
function onViewer(val:any){
viewer.xmlStr = val.column.row.content;
viewer.viewerVisible = true;
}
</script>
<template>
<div class="workflow-container">
<Table :data="table.tableData" :column="table.tableTitle" :operation="table.operation" :page="table.page" :loading="table.loading"
@onSizeChange="onSizeChange" @onCurrentChange="onCurrentChange" @onSwitchChange="onSwitchChange">
<template #search>
<div><el-button v-permission="'workflow:save'" type="primary" @click="onAdd()">添加</el-button></div>
<div><el-input v-model="searchForm.name" placeholder="请输入名称"></el-input></div>
<div><el-button @click="onRefresh" icon="RefreshRight" circle></el-button></div>
<div><el-button @click="onSearch" type="primary" icon="Search">查询</el-button></div>
</template>
<template #column="scope">
<el-button @click="onViewer(scope)" type='primary' link style="color:var(--query); padding:0;">预览</el-button>
<el-button @click="onEdit(scope)" v-permission="'workflow:update'" type='primary' link style="color:var(--edit); padding:0;">编辑</el-button>
<el-button @click="onDelete(scope)" v-permission="'workflow:delete'" type='primary' link style="color:var(--delete); padding:0;">删除</el-button>
</template>
</Table>
<el-dialog v-model="viewer.viewerVisible" title="预览">
<BpmnViewer :xmlStr="viewer.xmlStr" />
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
.workflow-container{
}
</style>

View File

@ -0,0 +1,113 @@
<script setup lang="ts">
import {onMounted, reactive, ref} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import type {FormInstance} from 'element-plus'
import BpmnModeler from "@/components/bpmn/BpmnModeler.vue"
import { useRoute, useRouter } from 'vue-router'
import { details, save, update } from '@/api/work/workflow'
const route = useRoute()
const router = useRouter()
const formRef = ref<FormInstance>();
const saveXml = ref(null);
const form = reactive({
loading: false,
workflowForm:{
id: '',
name: '',
content: null,
status: null,
remarks: '',
}
})
onMounted(()=>{
onDetails()
})
function onDetails(){
if(!route.query.id){
return false;
}
form.loading = true;
details({id: route.query.id}).then((res:any) =>{
form.workflowForm = {
id: res.result.id,
name: res.result.name,
content: res.result.content,
status: res.result.status,
remarks: res.result.remarks,
}
form.loading = false;
})
}
function goBack(){
router.push({path: '/work/Workflow'});
}
async function onSubmit(formEl: FormInstance | undefined) {
if (!formEl) return;
await formEl.validate((valid)=>{
if(valid){
const bpmnModeler = saveXml.value.bpmnModeler;
if(!bpmnModeler){
ElMessage.warning("流程图不能为空!")
return false;
}
bpmnModeler.saveXML({ format: true }).then((res:any) =>{
if(!res.xml){
ElMessage.warning("流程图不能为空!")
return false;
}
form.workflowForm.content = res.xml;
if(!form.workflowForm.id){
save(form.workflowForm).then(res=>{
if(res){
ElMessage.success("添加成功!");
router.push({path: '/work/Workflow'});
}
})
} else {
update(form.workflowForm).then(res=>{
if(res){
ElMessage.success("更新成功!");
router.push({path: '/work/Workflow'});
}
})
}
})
}
})
}
</script>
<template>
<div class="workflow-add-container">
<el-form :model="form.workflowForm" ref="formRef" :rules="rules" label-position="left" status-icon>
<el-row :gutter="20">
<el-col :span="1">
<el-button @click="goBack()">返回</el-button>
</el-col>
<el-col :span="7">
<el-form-item label="流程名称" prop="name" :rules="[{required: true,message:'请输入名称',trigger: 'blur'}]">
<el-input v-model="form.workflowForm.name" placeholder="请输入名称" style="width: 100%"/>
</el-form-item>
</el-col>
<el-col :span="16">
<el-button type="primary" @click="onSubmit(formRef)">提交</el-button>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<BpmnModeler v-if="!form.loading" :xmlStr="form.workflowForm.content" ref="saveXml"/>
</el-col>
</el-row>
</el-form>
</div>
</template>
<style lang="scss" scoped>
.workflow-add-container{
}
</style>