当前位置:网站首页>Build a BPMN modeling Web Service
Build a BPMN modeling Web Service
2022-06-10 20:23:00 【gzroy】
In my last blog post, I introduced how to use Camunda To arrange the workflow , Which USES Camunda Provided Modeler Software to model workflow . This software is based on bpmn.io Developed on the basis of open source software , Is a desktop version of the software . If we need to Web Services to provide , Then we can also base on bpmn.io To do some custom development , You can see bpmn.io · GitHub
Now I will build a Web Version of workflow modeling service , And realize Sinicization .
Project settings
Create a new folder , Run the following command
mkdir modeler-demo
cd modeler-demo
npm init -y
npm install webpack webpack-cli --save-dev
npm install copy-webpack-plugin --save-dev
npm install style-loader css-loader less-loader raw-loader --save-dev
npm install jquery --save-dev
npm install --save bpmn-js
npm install --save bpmn-js-properties-panel @bpmn-io/properties-panel
npm install --save camunda-bpmn-moddle
Create a new one webpack.config.js file , The contents are as follows :
var CopyPlugin = require('copy-webpack-plugin');
const path = require('path');
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.less$/i,
use: ['style-loader', 'css-loader', 'less-loader'],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
{
test: /\.bpmn$/i,
use: ['raw-loader'],
},
],
},
plugins: [
new CopyPlugin({
patterns: [
{ from: 'node_modules/bpmn-js/dist/assets', to: 'vendor/bpmn-js/assets' },
{ from: 'node_modules/bpmn-js-properties-panel/dist/assets', to: 'vendor/bpmn-js-properties-panel/assets' },
]
})
]
};Programming , load BPMN Module
Create a new src Catalog and a dist Catalog .
stay src Create a new app.js file , load bpmn Relevant modules and settings of .
import $ from 'jquery';
import './app.less';
import BpmnModeler from 'bpmn-js/lib/Modeler';
import diagramXML from './newDiagram.bpmn';
import {
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
CamundaPlatformPropertiesProviderModule
} from 'bpmn-js-properties-panel';
import CamundaBpmnModdle from 'camunda-bpmn-moddle/resources/camunda.json';
import customTranslate from './customTranslate/customTranslate';
var container = $('#js-drop-zone');
var customTranslateModule = {
translate: [ 'value', customTranslate ]
};
var modeler = new BpmnModeler({
container: '#js-canvas',
propertiesPanel: {
parent: '#js-properties-panel'
},
additionalModules: [
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
CamundaPlatformPropertiesProviderModule,
customTranslateModule
],
moddleExtensions: {
camunda: CamundaBpmnModdle
}
});
function createNewDiagram() {
openDiagram(diagramXML);
}
async function openDiagram(xml) {
try {
await modeler.importXML(xml);
container
.removeClass('with-error')
.addClass('with-diagram');
} catch (err) {
container
.removeClass('with-diagram')
.addClass('with-error');
container.find('.error pre').text(err.message);
console.error(err);
}
}
function registerFileDrop(container, callback) {
function handleFileSelect(e) {
e.stopPropagation();
e.preventDefault();
var files = e.dataTransfer.files;
var file = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var xml = e.target.result;
callback(xml);
};
reader.readAsText(file);
}
function handleDragOver(e) {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}
container.get(0).addEventListener('dragover', handleDragOver, false);
container.get(0).addEventListener('drop', handleFileSelect, false);
}
// file drag / drop ///
// check file api availability
if (!window.FileList || !window.FileReader) {
window.alert(
'Looks like you use an older browser that does not support drag and drop. ' +
'Try using Chrome, Firefox or the Internet Explorer > 10.');
} else {
registerFileDrop(container, openDiagram);
}
// bootstrap diagram functions
$(function() {
$('#js-create-diagram').click(function(e) {
e.stopPropagation();
e.preventDefault();
createNewDiagram();
});
var downloadLink = $('#js-download-diagram');
var downloadSvgLink = $('#js-download-svg');
$('.buttons a').click(function(e) {
if (!$(this).is('.active')) {
e.preventDefault();
e.stopPropagation();
}
});
function setEncoded(link, name, data) {
var encodedData = encodeURIComponent(data);
if (data) {
link.addClass('active').attr({
'href': 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData,
'download': name
});
} else {
link.removeClass('active');
}
}
var exportArtifacts = debounce(async function() {
try {
const { svg } = await modeler.saveSVG();
setEncoded(downloadSvgLink, 'diagram.svg', svg);
} catch (err) {
console.error('Error happened saving svg: ', err);
setEncoded(downloadSvgLink, 'diagram.svg', null);
}
try {
const { xml } = await modeler.saveXML({ format: true });
setEncoded(downloadLink, 'diagram.bpmn', xml);
} catch (err) {
console.error('Error happened saving XML: ', err);
setEncoded(downloadLink, 'diagram.bpmn', null);
}
}, 500);
modeler.on('commandStack.changed', exportArtifacts);
});
// helpers //
function debounce(fn, timeout) {
var timer;
return function() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, timeout);
};
}Create a new one app.less file , Including the definition of format :
* {
box-sizing: border-box;
}
body,
html {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 12px;
height: 100%;
max-height: 100%;
padding: 0;
margin: 0;
}
#js-properties-panel {
width: 400px;
}
a:link {
text-decoration: none;
}
.content {
position: relative;
width: 100%;
height: 100%;
display: flex;
> .message {
width: 100%;
height: 100%;
text-align: center;
display: table;
font-size: 16px;
color: #111;
.note {
vertical-align: middle;
text-align: center;
display: table-cell;
}
&.error {
.details {
max-width: 500px;
font-size: 12px;
margin: 20px auto;
text-align: left;
color: #BD2828;
}
pre {
border: solid 1px #BD2828;
background: #fefafa;
padding: 10px;
color: #BD2828;
}
}
}
&:not(.with-error) .error,
&.with-error .intro,
&.with-diagram .intro {
display: none;
}
.canvas {
width: 100%;
}
.canvas,
.properties-panel-parent {
display: none;
}
&.with-diagram {
.canvas,
.properties-panel-parent {
display: block;
}
}
}
.buttons {
position: fixed;
bottom: 20px;
left: 20px;
padding: 0;
margin: 0;
list-style: none;
> li {
display: inline-block;
margin-right: 10px;
> a {
background: #DDD;
border: solid 1px #666;
display: inline-block;
padding: 5px;
}
}
a {
opacity: 0.3;
}
a.active {
opacity: 1.0;
}
}
.properties-panel-parent {
border-left: 1px solid #ccc;
overflow: auto;
&:empty {
display: none;
}
> .djs-properties-panel {
padding-bottom: 70px;
min-height:100%;
}
}
In the above procedure , A customized translation component is loaded . We need to be in src Create a new one in the directory customTranslate The catalog of , Create a new customTranslate.js, As follows :
import translations from './translations';
export default function customTranslate(template, replacements) {
replacements = replacements || {};
// Translate
template = translations[template] || template;
// Replace
return template.replace(/{([^}]+)}/g, function(_, key) {
return replacements[key] || '{' + key + '}';
});
}Another new one translation.js file , It stores the corresponding translation information :
/**
* This is a sample file that should be replaced with the actual translation.
*
* Checkout https://github.com/bpmn-io/bpmn-js-i18n for a list of available
* translations and labels to translate.
*/
export default {
'Exclusive Gateway': 'Exklusives Gateway',
'Parallel Gateway': 'Paralleles Gateway',
'Inclusive Gateway': 'Inklusives Gateway',
'Complex Gateway': 'Komplexes Gateway',
'Service Task': ' Service tasks ',
'Event based Gateway': 'Ereignis-basiertes Gateway',
'Message Start Event': ' Message start event ',
'Timer Start Event': ' Timed start events ',
'Conditional Start Event': ' Conditional start event ',
'Signal Start Event': ' Signal start event ',
'Error Start Event': ' Error starting event ',
'Escalation Start Event': ' Upgrade start event ',
'Compensation Start Event': ' Compensation start event ',
'Message Start Event (non-interrupting)': ' Message start event ( Non interruptive )',
'Timer Start Event (non-interrupting)': ' Timed start events ( Non interruptive )',
'Conditional Start Event (non-interrupting)': ' Conditional start event ( Non interruptive )',
'Signal Start Event (non-interrupting)': ' Signal start event ( Non interruptive )',
'Escalation Start Event (non-interrupting)': ' Upgrade start event ( Non interruptive )',
//--------- Property panel --------
'General':' Universal ',
'Details':' details ',
'Process':' process ',
'Documentation':' file ',
'Version Tag':' Version label ',
'Category Value':' Category value ',
'Process Id':' process Id',
'Process Name':' process Name',
'Link Name':' Link name ',
'Element documentation':' Element document ',
'Process Documentation':' Process document ',
'Executable':' Executable ',
'Task Priority':' Task priority ',
'Startable':' But start ',
'Version tag':' Version label '
};stay dist Create a new directory index.html file , It needs to be filled with corresponding containers , Used to contain bpmn Corresponding components of .
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Getting Started</title>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/assets/diagram-js.css">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/assets/bpmn-js.css">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/assets/bpmn-font/css/bpmn-embedded.css">
<link rel="stylesheet" href="vendor/bpmn-js-properties-panel/assets/properties-panel.css" />
<style>
html, body, #canvas {
height: 100%;
padding: 0;
margin: 0;
}
</style>
</head>
<body>
<div class="content" id="js-drop-zone">
<div class="message intro">
<div class="note">
Drop BPMN diagram from your desktop or <a id="js-create-diagram" href>create a new diagram</a> to get started.
</div>
</div>
<div class="message error">
<div class="note">
<p>Ooops, we could not display the BPMN 2.0 diagram.</p>
<div class="details">
<span>cause of the problem</span>
<pre></pre>
</div>
</div>
</div>
<div class="canvas" id="js-canvas"></div>
<div class="properties-panel-parent" id="js-properties-panel"></div>
</div>
<ul class="buttons">
<li>
download
</li>
<li>
<a id="js-download-diagram" href title="download BPMN diagram">
BPMN diagram
</a>
</li>
<li>
<a id="js-download-svg" href title="download as SVG image">
SVG image
</a>
</li>
</ul>
<script src="bundle.js"></script>
</body>
</html>function npm run build To compile , And then in web Deploy in the server , visit dist In the catalog index.html file , You can model the workflow in the browser .
There is a small problem , In the display of the property panel ,Header and Group There is no Chinese translation . Search for GitHub bpmn Of issue, Someone inside gave a solution .
stay node_modules Under the table of contents , find bpmn-js-properties-panel->dist->index.esm.js file , Modify the class BpmnPropertiesProvider Is defined as follows :
class BpmnPropertiesProvider {
constructor(propertiesPanel, translate) {
BpmnPropertiesProvider.prototype.translate = translate
propertiesPanel.registerProvider(this);
}
getGroups(element) {
const translate = this.translate;
return groups => {
groups = groups.concat(getGroups(element));
forEach(groups, function(group){
group.label = translate(group.label);
})
return groups;
};
}
}
BpmnPropertiesProvider.$inject = ['propertiesPanel', 'translate'];modify const PanelHeaderProvider Inside getTypeLabel as follows
getTypeLabel: element => {
const elementTemplates = getTemplatesService();
if (elementTemplates) {
const template = getTemplate(element, elementTemplates);
if (template && template.name) {
return template.name;
}
}
const concreteType = getConcreteType(element);
const translate = useService('translate');
return translate(concreteType.replace(/(\B[A-Z])/g, ' $1').replace(/(\bNon Interrupting)/g, '($1)'));
}again npm run build that will do
Effect display
The operation effect is as follows :
bpmn_demo
边栏推荐
- 一个10年左右的老程序员说:简单CRUD功能进入无码开发时代1 之 增删改查接口信息
- Zabbix_ Principle Architecture - installation and deployment - Custom monitoring
- Rotated sorted array
- Complete knapsack problem and optimization tips
- 在阿里云国际上使用 OSS 和 CDN 部署静态网站
- How do big factories write data analysis reports?
- Zabbix_ Monitoring ssh/crond Service - wechat alarm
- Qualcomm qc2.0 fast charging intelligent identification IC fp6719
- 首批!青藤通过信通院CWPP能力评估检验
- 补水仪108K加湿器开发方案_单片机_NY8A051F_单片机开发设计开发
猜你喜欢

Microsoft Word 教程「5」,如何在 Word 中更改页边距、创建新闻稿栏?

传音 Infinix 新机现身谷歌产品库,TECNO CAMON 19 预装 Android 13

Tutoriel Microsoft Word "5", comment changer les marges de page et créer une barre de nouvelles en word?

PDU session flow

hidden danger? Limited meaning? Can't stop the real cooking Mini kitchenware hot 618

First batch! Sinomenine has passed CWPP capability assessment and inspection of Xintong Institute

国庆期间给大家推荐一个可能会成为2019最佳的CRUD工具

【技术碎片】重名文件 加后缀重命名过滤实现

SBC芯片35584数据手册预调节器翻译

刷脸认证如何实现人脸又快又准完成校验?
随机推荐
Zabbix_ Principle Architecture - installation and deployment - Custom monitoring
The annual salary of testers in large factories ranges from 300000 to 8K a month. Roast complained that the salary was too low, but he was ridiculed by netizens?
torch.nn.Parameter的简单理解//未完待续,待我看懂这段
Solving Bob's survival problem by trilogy routine
Fs4100 lithium battery charging management IC input 12V to 8.4v charging IC
Microsoft Word 教程「5」,如何在 Word 中更改頁邊距、創建新聞稿欄?
【FAQ】运动健康服务REST API接口使用过程中常见问题和解决方法总结
身份识别与访问管理(IAM)
HW blue team intermediate interview reply
localhost和127.0.0.1的区别?
首批!青藤通过信通院CWPP能力评估检验
4.35v lithium battery charging IC
vulnhub-The Planets: Earth
Zabbix_原理架构-安装部署-自定义监控
批量检测不同url的指定端口(py脚本)
Performance test plan (plan) template
C (pointer 02)
nodejs: 官方文档3 dgram Stream
为什么网页样式属性,有的需要加冒号“:”,有的不用?
How to add aggregation hotspots in VR panorama? How to add a content module?