美文网首页
bpmn.js in angular10

bpmn.js in angular10

作者: 云上笔记 | 来源:发表于2022-04-27 15:31 被阅读0次

    Angular 中引入 bpmn.js

    最近的工作中有 workflow 相关的需求,初步讨论决定用 Camunda 实现。
    前端需要做的工作是提供一个页面查看所有的工作流列表,同时需要绘制工作流程并保存,效果图如下:


    图片.png

    1. 安装依赖

        npm install --save bpmn-js
        // package.json -- dependencies
        "diagram-js": "^8.2.1",
        "bpmn-js": "^9.0.3",
        "bpmn-js-properties-panel": "^0.46.0",
        "camunda-bpmn-moddle": "^6.1.2",
    

    2. 在 angular.json 中引入样式

                "styles": [
                  "src/theme.less",
                  "src/styles.scss",
                  "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/styles/properties.less"
                ],
    

    3. 创建组件

    // component
    import { AfterContentInit, Component,ElementRef, Input,OnChanges,OnDestroy,Output,ViewChild,SimpleChanges,OnInit} from'@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { from, Observable } from 'rxjs';
    // bpmn module
    import Modeler from 'bpmn-js/lib/Modeler.js';
    import propertiesPanelModule from 'bpmn-js-properties-panel';
    import CamundaPlatformPropertiesProviderModule from 'bpmn-js-properties-panel';
    import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda';
    import CamundaExtensionModule from 'camunda-bpmn-moddle/lib';
    import { default as camundaModdleDescriptor } from 'camunda-bpmn-moddle/resources/camunda.json';
    @Component({
      selector: 'app-diagram',
      templateUrl: './diagram.component.html',
      styleUrls: ['./diagram.component.scss']
    })
    export class DiagramComponent implements AfterContentInit, OnChanges, OnInit, OnDestroy {
      @ViewChild('canvas', { static: true }) private el: ElementRef;
      @Input() private diagramXML: string;
      private modeler: Modeler;
    
      constructor(private http: HttpClient) {
        this.modeler = new Modeler();
        this.modeler.on('import.done', ({ error }) => {
          if (!error) {
            this.modeler.get('canvas').zoom('fit-viewport');
          }
        });
      }
    
      ngOnChanges(changes: SimpleChanges) {
        if (changes.diagramXML) {
          this.diagramXML = changes.diagramXML.currentValue;
          if (this.diagramXML) {
            this.importDiagram(this.diagramXML);
          }
        }
      }
    
      ngOnInit() {
        this.modeler = new Modeler({
          container: '#canvas',
          width: '100%',
          height: '600px',
          propertiesPanel: {
            parent: '#properties-panel'
          },
          additionalModules: [
            // 右边工具栏
            propertiesPanelModule,
            // 左边工具栏以及节点
            propertiesProviderModule,
            CamundaPlatformPropertiesProviderModule,
            CamundaExtensionModule
          ],
          moddleExtensions: {
            camunda: camundaModdleDescriptor
          }
        });
        // 如果没有传入 xml 文件,则新建一个 workflow
        if (!this.diagramXML) {
          this.createWorkflow();
        }
    
      }
    
      ngAfterContentInit(): void {
        this.modeler.attachTo(this.el.nativeElement);
      }
    
      ngOnDestroy(): void {
        this.modeler.destroy();
      }
    
    
      /**
       * 创建空白工作流
       */
      private createWorkflow() {
        // 获取本地 bpmn 文件并导入
        // this.http.get('assets/bpmn/initial.bpmn', { responseType: 'text' }).subscribe(output => {
        //     this.importDiagram(output);
        //   });
        this.modeler.createDiagram();
      }
    
      public save() {
        return this.modeler.saveXML();
      }
    
      /**
       * 导出文件到本地
       */
      public saveDiagram() {
        this.modeler.saveXML({ format: true }).then(res => {
          const encodeXML = encodeURIComponent(res.xml);
          this.downloadToLocal(encodeXML);
        });
      }
    
    
      /**
       * 保存 bpmn文件到本地
       */
      private downloadToLocal(encodedData, name = 'diagram.bpmn') {
        // 创建虚拟a标签
        const eleLink = document.createElement('a');
        eleLink.download = name;
        eleLink.style.display = 'none';
        eleLink.href = 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData;
        // 触发点击
        document.body.appendChild(eleLink);
        eleLink.click();
        // 然后移除
        document.body.removeChild(eleLink);
      }
    
    
      /**
       * 导入 bpmn 文件
       */
      public beforeUpload = (file: NzUploadFile): boolean => {
        this.onFileChange(file);
        return false;
      }
    
      /**
       * 读取导入的 bpmn 文件并渲染
       */
      public onFileChange(file) {
        const reader = new FileReader();
        reader.readAsText(file);
        reader.onload = () => {
          const xmlData = reader.result;
          this.diagramXML = xmlData;
        };
        reader.onerror = () => {
          this.messageService.error('error is occured while reading file!');
        };
      }
    
    
      /**
       * modeler 中导入 xml 文件
       * @see https://github.com/bpmn-io/bpmn-js-callbacks-to-promises#importxml
       */
      private importDiagram(xml: string): Observable<{ warnings: Array<any> }> {
        return from(this.modeler.importXML(xml) as Promise<{ warnings: Array<any> }>);
      }
    
      /**
       * 获取服务器端存储的 bpmn 文件
       */
      // private loadUrl(url: string): Subscription {
      //   return (
      //     this.http.get(url, { responseType: 'text' }).pipe(
      //       switchMap((xml: string) => this.importDiagram(xml)),
      //       map(result => result.warnings),
      //     ).subscribe(
      //       (warnings) => {
      //         this.importDone.emit({
      //           type: 'success',
      //           warnings
      //         });
      //       },
      //       (err) => {
      //         this.importDone.emit({
      //           type: 'error',
      //           error: err
      //         });
      //       }
      //     )
      //   );
      // }
    }
    
    // html
    <div nz-row>
        <nz-upload class="mr-15" [nzShowUploadList]="false" [nzBeforeUpload]="beforeUpload" nzAccept=".bpmn">
            <button nz-button nzSize="small" nzType="primary">
                <i nz-icon nzType="upload"></i>import
            </button>
        </nz-upload>
        <button nz-button nzSize="small" nzType="primary" (click)="saveDiagram()">
            <i nz-icon nzType="download" nzTheme="outline"></i>export
        </button>
    </div>
    <div class="diagram-container">
        <div id="canvas" #canvas class="canvas"></div>
        <!-- 右侧属性栏 -->
        <div class="properties-panel" id="properties-panel"></div>
    </div>
    
    .diagram-container,
    .canvas {
        height: 100%;
        width: 100%;
    }
    
    :host .canvas ::ng-deep > .bjs-container {
        height: 100% !important;
    }
    
    .properties-panel {
        position: absolute;
        top: 0;
        bottom: 0;
        right: 0;
        width: 260px;
        z-index: 10;
        border-left: 1px solid #ccc;
        overflow: auto;
    }
    
    .properties-panel:empty {
        display: none;
    }
    
    .properties-panel > .djs-properties-panel {
        padding-bottom: 70px;
        min-height: 100%;
    }
    
    

    注意

    导入 JSON 文件时使用这种方式
    import { default as camundaModdleDescriptor } from 'camunda-bpmn-moddle/resources'
    需要在 tsconfig.base.json 文件中加入下面这两个配置项

      "compilerOptions": {
        "resolveJsonModule": true,
        "esModuleInterop": true,
      }
    

    相关文章

      网友评论

          本文标题:bpmn.js in angular10

          本文链接:https://www.haomeiwen.com/subject/wolhyrtx.html