因爲之前公司的angular項目中需要用到bpmn,網上教程不多,我是參照下面這個博主寫的教程才學會使用的:他寫的特別棒
BPMN-JS與Angular集成(1)
BPMN-JS與Angular集成(2)
BPMN-JS與Angular集成(3)
我這篇博客只能算自己總結學習筆記,在angular中引入bpmn的步驟:
按照下面的步驟操作,會出現左側的工具欄和右側的屬性欄
1、 首先在項目中使用兩個命令:
npm install bpmn-js
npm install --save bpmn-js-properties-panel
2、在angular.json文件中的styles ,添加bpmn-js-properties-panel樣式 :
"styles": [
"src/styles.less",
"node_modules/bpmn-js/dist/assets/diagram-js.css",
"node_modules/bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css",
"node_modules/bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css"
],
3、在需要用到bpmn的頁面HTML寫下一下代碼:
<div class="content with-diagram" id="js-drop-zone" style="height: 1600px;">
<div class="canvas" id="js-canvas"></div>
<div class="properties-panel-parent" id="js-properties-panel">
</div>
</div>
<div class="modeler">
<div id="canvas"></div>
</div>
<ul class="buttons">
<li>
<a id="js-download-diagram" href title="download BPMN diagram" class="active" (click)="saveDiagram($event)">
BPMN diagram
</a>
</li>
<li>
<a id="js-download-svg" href title="download as SVG image" class="active" (click)="saveSVG($event)">
SVG image
</a>
</li>
<li *ngIf="saveHref">
<a id="download" [href]="saveHref" [download]="saveName" class="active">下載</a>
</li>
</ul>
TS文件如下:
注意:在ts文件裏面有一句: const url = ‘…/…/…/assets/bpmn/newDiagram.bpmn’;
這個是存放的空白bpmn文件,根據自己存放書寫位置
注意:這個bpmn文件一定一定要放在assets下面
import propertiesPanelModule from 'bpmn-js-properties-panel';
import propertiesProvider from 'bpmn-js-properties-panel/lib/provider/bpmn';
import BpmnModeler from 'bpmn-js/lib/Modeler.js';
import{HttpClient}from '@angular/common/http';
import { DomSanitizer } from '@angular/platform-browser';
constructor(
private https: HttpClient,
private sanitizer: DomSanitizer
) { }
ngOnInit() :void{
this.initBpmn();
}
private modeler:any;
saveHref;//下載是否出現
saveName = '';
initBpmn(){
this.modeler = new BpmnModeler({
container: '#js-canvas',
width: '100%',
height: '100%',
propertiesPanel: {
parent: '#js-properties-panel'
},
additionalModules: [
propertiesProvider,
propertiesPanelModule,
],
});
this.load();
}
load(): void {
const url = '../../../assets/bpmn/newDiagram.bpmn';
this.https.get(url, {
headers: {observe: 'response'}, responseType: 'text'
}).subscribe(
(x: any) => {
this.modeler.importXML(x, this.handleError);
},
this.handleError
);
}
handleError(err: any) {
if (err) {
console.warn('Ups, error: ', err);
}else {
console.log('rendered:')
}
}
save(): void {
this.modeler.saveXML((err: any, xml: any) => console.log('Result of saving XML: ', err, xml));
}
// 保存爲XML
saveDiagram(e) {
this.modeler.saveXML({format: true}, (err, xml) => {
if (err) {
console.error(err);
} else {
this.setEncoded(xml, 'bpmn.xml');
}
});
e.preventDefault();
e.stopPropagation();
}
// 保存爲svg
saveSVG(e) {
this.modeler.saveSVG((err, svg) => {
if (err) {
console.error(err);
} else {
this.setEncoded(svg, 'bpmn.svg');
}
});
e.preventDefault();
e.stopPropagation();
}
setEncoded(data, name) {
const encodedData = encodeURIComponent(data);
if (data) {
this.saveHref = this.sanitizer.bypassSecurityTrustResourceUrl('data:application/bpmn20-xml;charset=UTF-8,' + encodedData);
this.saveName = name;
}
}
bpmn文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="LeaveProcess" name="BPMN業務流程建模與標註" isExecutable="true">
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_LeaveProcess">
<bpmndi:BPMNPlane bpmnElement="LeaveProcess" id="BPMNPlane_LeaveProcess">
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
這個時候可能頁面無法顯示,原因是需要給bpmn一個寬和高:
樣式文件如下:
* {
box-sizing: border-box;
}
body,
html {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 12px;
height: 100%;
padding: 0;
margin: 0;
}
a:link {
text-decoration: none;
}
.content{
display: flex;
}
.content>div {
width: 100%;
// width:200px;
height: 100%;
}
.content>.message {
text-align: center;
display: table;
font-size: 16px;
color: #111;
}
.content>.message .note {
vertical-align: middle;
text-align: center;
display: table-cell;
}
.content .error .details {
max-width: 500px;
font-size: 12px;
margin: 20px auto;
text-align: left;
}
.content .error pre {
border: solid 1px #CCC;
background: #EEE;
padding: 10px;
}
.content:not(.with-error) .error,
.content.with-error .intro,
.content.with-diagram .intro {
display: none;
// height: 1000px;
}
.content .canvas,
.content.with-error .canvas {
visibility: hidden;
}
.content.with-diagram .canvas {
visibility: visible;
// height: 1000px;
}
.buttons {
position: fixed;
bottom: 20px;
padding: 0;
margin: 0;
list-style: none;
}
.buttons>li {
display: inline-block;
margin-right: 10px;
}
.buttons>li>a {
background: #DDD;
border: solid 1px #666;
display: inline-block;
padding: 5px;
}
.buttons a {
opacity: 0.3;
}
.buttons a.active {
opacity: 1.0;
}
我沒有按照上面這個css樣式寫,自己重新寫了一份,是下面這個樣式,我覺得比較好看也比較好操作。ts文件也做了小小的改動
修改後的HTML文件和樣式文件、ts文件如下:
HTML頁面
<nz-select nzAllowClear nzPlaceHolder="下載類型" (ngModelChange)="saveUpload($event)" [(ngModel)]="selectedValue">
<nz-option nzValue="xml" nzLabel="XML下載"></nz-option>
<nz-option nzValue="svg" nzLabel="SVG下載"></nz-option>
</nz-select>
<a *ngIf="isdownload" id="download" [href]="saveHref" class="active" [download]="saveName">下載</a>
<div nz-row class="content with-diagram" id="js-drop-zone" style="height:800px;">
<div nz-col nzSpan="18" class="canvas" id="js-canvas"></div>
<div nz-col nzSpan="6" class="properties-panel-parent" id="js-properties-panel"></div>
</div>
樣式
* {
box-sizing: border-box;
}
body,
html {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 12px;
height: 100%;
padding: 0;
margin: 0;
}
a:link {
text-decoration: none;
}
.content>.message {
text-align: center;
display: table;
font-size: 16px;
color: #111;
}
.content>.message .note {
vertical-align: middle;
text-align: center;
display: table-cell;
}
.content .error .details {
max-width: 500px;
font-size: 12px;
margin: 20px auto;
text-align: left;
}
.content .error pre {
border: solid 1px #CCC;
background: #EEE;
padding: 10px;
}
.content:not(.with-error) .error,
.content.with-error .intro,
.content.with-diagram .intro {
display: flex; //控制屬性欄與右側有沒有間隙
height: 1000px;
}
.content .canvas,
.content.with-error .canvas {
visibility: hidden;
}
.content.with-diagram .canvas {
visibility: inherit;
height: 1000px;
}
// 控制左側工具欄的寬度
:host ::ng-deep .djs-palette.open {
width: 100px !important;
}
// 下拉框的寬度
nz-select {
margin: 8px;
width: 120px;
}
import { Component, OnInit } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';
import propertiesPanelModule from 'bpmn-js-properties-panel';
import propertiesProvider from 'bpmn-js-properties-panel/lib/provider/bpmn';
import BpmnModeler from 'bpmn-js/lib/Modeler.js';
@Component({
selector: 'app-system-bpmn',
templateUrl: './bpmn.component.html',
styleUrls: ['./bpmn.component.less']
})
export class SystemBpmnComponent implements OnInit {
constructor(
private https: HttpClient,
private sanitizer: DomSanitizer) { }
ngOnInit() {
this.initBpmn();
}
private modeler: any;
saveHref;//下載是否出現
saveName = '';
isdownload = false;
selectedValue = null;
saveXml;
initBpmn() {
this.modeler = new BpmnModeler({
container: '#js-canvas',//js-canvas左側工具欄的id
width: '100%',//畫板的大小
height: '100%',
propertiesPanel: {
parent: '#js-properties-panel'
},
additionalModules: [
propertiesProvider,//左側工具欄
propertiesPanelModule,//右側屬性欄
],
});
this.load();
}
load(): void {
const url = '../../../../assets/system-bpmn/blank/blank.bpmn';
this.https.get(url, {
headers: { observe: 'response' },
responseType: 'text'
}).subscribe(
(x: any) => {
this.modeler.importXML(x, this.handleError);
},
this.handleError
);
}
handleError(err: any) {
if (err) {
console.warn('Ups, error: ', err);
} else {
console.log('rendered:')
}
}
save(): void {
this.modeler.saveXML((err: any, xml: any) =>
this.saveXml = xml);
}
// 保存爲XML
saveDiagram(e) {
this.modeler.saveXML({ format: true }, (err, xml) => {
if (err) {
console.error(err);
} else {
this.setEncoded(xml, 'bpmn.xml');
}
});
}
// e.preventDefault();//取消事件的默認動作
// e.stopPropagation();//不再派發事件。
// 保存爲svg
saveSVG(e) {
this.modeler.saveSVG((err, svg) => {
if (err) {
console.error(err);
} else {
this.setEncoded(svg, 'bpmn.svg');
}
});
// e.preventDefault();//取消事件的默認動作
// e.stopPropagation();//不再派發事件。
}
setEncoded(data, name) {
// data是xml name是bpmn.xml
const encodedData = encodeURIComponent(data);
if (data) {
this.saveHref = this.sanitizer.bypassSecurityTrustResourceUrl('data:application/bpmn20-xml;charset=UTF-8,' + encodedData);
this.saveName = name;
}
}
saveUpload(e) {
console.log(e)
if (e == 'xml') {
this.isdownload = true;
this.modeler.saveXML({ format: true }, (err, xml) => {
if (err) {
console.error(err);
} else {
this.setEncoded(xml, 'bpmn.xml');
}
});
// e.preventDefault();//取消事件的默認動作
// e.stopPropagation();//不再派發事件。
}
else if (e == 'svg') {
this.isdownload = true;
this.modeler.saveSVG((err, svg) => {
if (err) {
console.error(err);
} else {
this.setEncoded(svg, 'bpmn.svg');
}
});
// e.preventDefault();//取消事件的默認動作
// e.stopPropagation();//不再派發事件。
}
else {
this.isdownload = false;
}
}
}
如果內容有什麼問題,歡迎大家指出,順便給我點個讚唄!