美文网首页
Activiti Explorer定制

Activiti Explorer定制

作者: 架构路上的一亩三分地 | 来源:发表于2022-03-04 15:06 被阅读0次

    为了简化流程引擎配置,支持用户自定义配置,针对Activit Explorer进行定制化

    简化侧边节点,重分类,重命名

    简化前

    编辑stencilset.json,在stencils数组中,移除不需要的节点,比如

    {
          "type": "node",
          "id": "StartErrorEvent",
          "title": "异常事件",
          "description": "A start event that catches a thrown BPMN error",
          "view": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg\n   xmlns=\"http://www.w3.org/2000/svg\"\n   xmlns:oryx=\"http://www.b3mn.org/oryx\"\n   width=\"40\"\n   height=\"40\"\n   version=\"1.0\">\n  <defs></defs>\n  <oryx:magnets>\n  \t<oryx:magnet oryx:cx=\"16\" oryx:cy=\"16\" oryx:default=\"yes\" />\n  </oryx:magnets>\n  <oryx:docker oryx:cx=\"16\" oryx:cy=\"16\" />\n  <g pointer-events=\"fill\">\n    <circle id=\"bg_frame\" cx=\"16\" cy=\"16\" r=\"15\" stroke=\"#585858\" fill=\"#ffffff\" stroke-width=\"1\"/>\n    \n    <path\n         stroke=\"#585858\"\n         style=\"fill:none;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10\"\n         d=\"M 22.820839,11.171502 L 19.36734,24.58992 L 13.54138,14.281819 L 9.3386512,20.071607 L 13.048949,6.8323057 L 18.996148,16.132659 L 22.820839,11.171502 z\"\n         id=\"errorPolygon\" />\n\t<text font-size=\"11\" \n\t\tid=\"text_name\" \n\t\tx=\"16\" y=\"33\" \n\t\toryx:align=\"top center\" \n\t\tstroke=\"#373e48\"\n\t></text>\n  </g>\n</svg>",
          "icon": "startevent/error.png",
          "groups": [
            "启动事件"
          ],
          "propertyPackages": [
            "overrideidpackage",
            "namepackage",
            "documentationpackage",
            "executionlistenerspackage",
            "errorrefpackage"
          ],
          "hiddenPropertyPackages": [],
          "roles": [
            "sequence_start",
            "Startevents_all",
            "StartEventsMorph",
            "all"
          ]
        },
    

    同时,可以修改groups和title,进行自定义分组和命名

    简化后

    简化节点属性

    简化前

    编辑stencilset.json,找到对应的节点,比如"id": "UserTask",将不必要的properties从propertyPackages移动到hiddenPropertyPackages,例:

    改动前

        {
          "type": "node",
          "id": "UserTask",
          "title": "用户活动",
          "description": "分配给特定人的任务 ",
          "view": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg\n   xmlns=\"http://www.w3.org/2000/svg\"\n   xmlns:svg=\"http://www.w3.org/2000/svg\"\n   xmlns:oryx=\"http://www.b3mn.org/oryx\"\n   xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\n   width=\"102\"\n   height=\"82\"\n   version=\"1.0\">\n  <defs></defs>\n  <oryx:magnets>\n  \t<oryx:magnet oryx:cx=\"1\" oryx:cy=\"20\" oryx:anchors=\"left\" />\n  \t<oryx:magnet oryx:cx=\"1\" oryx:cy=\"40\" oryx:anchors=\"left\" />\n  \t<oryx:magnet oryx:cx=\"1\" oryx:cy=\"60\" oryx:anchors=\"left\" />\n  \t\n  \t<oryx:magnet oryx:cx=\"25\" oryx:cy=\"79\" oryx:anchors=\"bottom\" />\n  \t<oryx:magnet oryx:cx=\"50\" oryx:cy=\"79\" oryx:anchors=\"bottom\" />\n  \t<oryx:magnet oryx:cx=\"75\" oryx:cy=\"79\" oryx:anchors=\"bottom\" />\n  \t\n  \t<oryx:magnet oryx:cx=\"99\" oryx:cy=\"20\" oryx:anchors=\"right\" />\n  \t<oryx:magnet oryx:cx=\"99\" oryx:cy=\"40\" oryx:anchors=\"right\" />\n  \t<oryx:magnet oryx:cx=\"99\" oryx:cy=\"60\" oryx:anchors=\"right\" />\n  \t\n  \t<oryx:magnet oryx:cx=\"25\" oryx:cy=\"1\" oryx:anchors=\"top\" />\n  \t<oryx:magnet oryx:cx=\"50\" oryx:cy=\"1\" oryx:anchors=\"top\" />\n  \t<oryx:magnet oryx:cx=\"75\" oryx:cy=\"1\" oryx:anchors=\"top\" />\n  \t\n  \t<oryx:magnet oryx:cx=\"50\" oryx:cy=\"40\" oryx:default=\"yes\" />\n  </oryx:magnets>\n  <g pointer-events=\"fill\" oryx:minimumSize=\"50 40\">\n\t<rect id=\"text_frame\" oryx:anchors=\"bottom top right left\" x=\"1\" y=\"1\" width=\"94\" height=\"79\" rx=\"10\" ry=\"10\" stroke=\"none\" stroke-width=\"0\" fill=\"none\" />\n\t<rect id=\"bg_frame\" oryx:resize=\"vertical horizontal\" x=\"0\" y=\"0\" width=\"100\" height=\"80\" rx=\"10\" ry=\"10\" stroke=\"#bbbbbb\" stroke-width=\"1\" fill=\"#f9f9f9\" />\n\t\t<text \n\t\t\tfont-size=\"12\" \n\t\t\tid=\"text_name\" \n\t\t\tx=\"50\" \n\t\t\ty=\"40\" \n\t\t\toryx:align=\"middle center\"\n\t\t\toryx:fittoelem=\"text_frame\"\n\t\t\tstroke=\"#373e48\">\n\t\t</text>\n\t\n\t<g id=\"userTask\" transform=\"translate(3,3)\">\n\t\t<path oryx:anchors=\"top left\"\n       \t\tstyle=\"fill:#d1b575;stroke:none;\"\n       \t\t d=\"m 1,17 16,0 0,-1.7778 -5.333332,-3.5555 0,-1.7778 c 1.244444,0 1.244444,-2.3111 1.244444,-2.3111 l 0,-3.0222 C 12.555557,0.8221 9.0000001,1.0001 9.0000001,1.0001 c 0,0 -3.5555556,-0.178 -3.9111111,3.5555 l 0,3.0222 c 0,0 0,2.3111 1.2444443,2.3111 l 0,1.7778 L 1,15.2222 1,17 17,17\" \n         />\n\t\t\n\t</g>\n  \n\t<g id=\"parallel\">\n\t\t<path oryx:anchors=\"bottom\" fill=\"none\" stroke=\"#bbbbbb\" d=\"M46 70 v8 M50 70 v8 M54 70 v8\" stroke-width=\"2\" />\n\t</g>\n\t\n\t<g id=\"sequential\">\n\t\t<path oryx:anchors=\"bottom\" fill=\"none\" stroke=\"#bbbbbb\" stroke-width=\"2\" d=\"M46,76h10M46,72h10 M46,68h10\"/>\n\t</g>\n\t\n\n\t<g id=\"compensation\">\n\t\t<path oryx:anchors=\"bottom\" fill=\"none\" stroke=\"#bbbbbb\" d=\"M 62 74 L 66 70 L 66 78 L 62 74 L 62 70 L 58 74 L 62 78 L 62 74\" stroke-width=\"1\" />\n\t</g>\n  </g>\n</svg>",
          "icon": "activity/list/type.user.png",
          "groups": [
            "活动列表"
          ],
          "propertyPackages": [
            "tasklistenerspackage",
            "overrideidpackage",
            "namepackage",
            "documentationpackage",
            "asynchronousdefinitionpackage",
            "exclusivedefinitionpackage",
            "executionlistenerspackage",
            "multiinstance_typepackage",
            "multiinstance_cardinalitypackage",
            "multiinstance_collectionpackage",
            "multiinstance_variablepackage",
            "multiinstance_conditionpackage",
            "isforcompensationpackage",
            "usertaskassignmentpackage",
            "formkeydefinitionpackage",
            "duedatedefinitionpackage",
            "prioritydefinitionpackage",
            "formpropertiespackage"
          ],
          "hiddenPropertyPackages": [],
          "roles": [
            "Activity",
            "sequence_start",
            "sequence_end",
            "ActivitiesMorph",
            "all"
          ]
        },
    

    改动后

        {
          "type": "node",
          "id": "UserTask",
          "title": "人工任务",
          "description": "分配给特定人的任务 ",
          "view": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg\n   xmlns=\"http://www.w3.org/2000/svg\"\n   xmlns:svg=\"http://www.w3.org/2000/svg\"\n   xmlns:oryx=\"http://www.b3mn.org/oryx\"\n   xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\n   width=\"102\"\n   height=\"82\"\n   version=\"1.0\">\n  <defs></defs>\n  <oryx:magnets>\n  \t<oryx:magnet oryx:cx=\"1\" oryx:cy=\"20\" oryx:anchors=\"left\" />\n  \t<oryx:magnet oryx:cx=\"1\" oryx:cy=\"40\" oryx:anchors=\"left\" />\n  \t<oryx:magnet oryx:cx=\"1\" oryx:cy=\"60\" oryx:anchors=\"left\" />\n  \t\n  \t<oryx:magnet oryx:cx=\"25\" oryx:cy=\"79\" oryx:anchors=\"bottom\" />\n  \t<oryx:magnet oryx:cx=\"50\" oryx:cy=\"79\" oryx:anchors=\"bottom\" />\n  \t<oryx:magnet oryx:cx=\"75\" oryx:cy=\"79\" oryx:anchors=\"bottom\" />\n  \t\n  \t<oryx:magnet oryx:cx=\"99\" oryx:cy=\"20\" oryx:anchors=\"right\" />\n  \t<oryx:magnet oryx:cx=\"99\" oryx:cy=\"40\" oryx:anchors=\"right\" />\n  \t<oryx:magnet oryx:cx=\"99\" oryx:cy=\"60\" oryx:anchors=\"right\" />\n  \t\n  \t<oryx:magnet oryx:cx=\"25\" oryx:cy=\"1\" oryx:anchors=\"top\" />\n  \t<oryx:magnet oryx:cx=\"50\" oryx:cy=\"1\" oryx:anchors=\"top\" />\n  \t<oryx:magnet oryx:cx=\"75\" oryx:cy=\"1\" oryx:anchors=\"top\" />\n  \t\n  \t<oryx:magnet oryx:cx=\"50\" oryx:cy=\"40\" oryx:default=\"yes\" />\n  </oryx:magnets>\n  <g pointer-events=\"fill\" oryx:minimumSize=\"50 40\">\n\t<rect id=\"text_frame\" oryx:anchors=\"bottom top right left\" x=\"1\" y=\"1\" width=\"94\" height=\"79\" rx=\"10\" ry=\"10\" stroke=\"none\" stroke-width=\"0\" fill=\"none\" />\n\t<rect id=\"bg_frame\" oryx:resize=\"vertical horizontal\" x=\"0\" y=\"0\" width=\"100\" height=\"80\" rx=\"10\" ry=\"10\" stroke=\"#bbbbbb\" stroke-width=\"1\" fill=\"#f9f9f9\" />\n\t\t<text \n\t\t\tfont-size=\"12\" \n\t\t\tid=\"text_name\" \n\t\t\tx=\"50\" \n\t\t\ty=\"40\" \n\t\t\toryx:align=\"middle center\"\n\t\t\toryx:fittoelem=\"text_frame\"\n\t\t\tstroke=\"#373e48\">\n\t\t</text>\n\t\n\t<g id=\"userTask\" transform=\"translate(3,3)\">\n\t\t<path oryx:anchors=\"top left\"\n       \t\tstyle=\"fill:#d1b575;stroke:none;\"\n       \t\t d=\"m 1,17 16,0 0,-1.7778 -5.333332,-3.5555 0,-1.7778 c 1.244444,0 1.244444,-2.3111 1.244444,-2.3111 l 0,-3.0222 C 12.555557,0.8221 9.0000001,1.0001 9.0000001,1.0001 c 0,0 -3.5555556,-0.178 -3.9111111,3.5555 l 0,3.0222 c 0,0 0,2.3111 1.2444443,2.3111 l 0,1.7778 L 1,15.2222 1,17 17,17\" \n         />\n\t\t\n\t</g>\n  \n\t<g id=\"parallel\">\n\t\t<path oryx:anchors=\"bottom\" fill=\"none\" stroke=\"#bbbbbb\" d=\"M46 70 v8 M50 70 v8 M54 70 v8\" stroke-width=\"2\" />\n\t</g>\n\t\n\t<g id=\"sequential\">\n\t\t<path oryx:anchors=\"bottom\" fill=\"none\" stroke=\"#bbbbbb\" stroke-width=\"2\" d=\"M46,76h10M46,72h10 M46,68h10\"/>\n\t</g>\n\t\n\n\t<g id=\"compensation\">\n\t\t<path oryx:anchors=\"bottom\" fill=\"none\" stroke=\"#bbbbbb\" d=\"M 62 74 L 66 70 L 66 78 L 62 74 L 62 70 L 58 74 L 62 78 L 62 74\" stroke-width=\"1\" />\n\t</g>\n  </g>\n</svg>",
          "icon": "activity/list/type.user.png",
          "groups": [
            "任务"
          ],
          "propertyPackages": [
            "namepackage",
            "approvalwaypackage",
            "approvalrulepackage",
            "autocopypackage",
            "isskipnobodypackage",
            "isskipRepeatpackage",
            "executionlistenerspackage",
            "multiinstance_typepackage",
            "isforcompensationpackage"
          ],
          "hiddenPropertyPackages": [
            "tasklistenerspackage",
            "overrideidpackage",
            "documentationpackage",
            "asynchronousdefinitionpackage",
            "exclusivedefinitionpackage",
            "multiinstance_cardinalitypackage",
            "multiinstance_collectionpackage",
            "multiinstance_variablepackage",
            "multiinstance_conditionpackage",
            "usertaskassignmentpackage",
            "formkeydefinitionpackage",
            "duedatedefinitionpackage",
            "prioritydefinitionpackage",
            "formpropertiespackage"
          ],
          "roles": [
            "Activity",
            "sequence_start",
            "sequence_end",
            "ActivitiesMorph",
            "all"
          ]
        },
    
    简化后

    增加自定义属性

    上面的任务任务节点中,其中approvalwaypackage,approvalrulepackage, autocopypackage为自定义属性,在节点的propertyPackages进行引用外,还需要单独定义

    {
          "name": "approvalwaypackage",
          "properties": [
            {
              "id": "approvalway",
              "type": "kisbpm-multiinstance-approvalway",
              "title": "审批方式",
              "value": "",
              "description": "一票通过或拒绝,全部通过",
              "popular": true,
              "refToView": "multiinstance"
            }
          ]
        },
        {
          "name": "approvalrulepackage",
          "properties": [
            {
              "id": "approvalrule",
              "type": "String",
              "title": "审批规则",
              "value": "",
              "description": "指定岗位,指定人,或者部门负责人",
              "popular": true
            }
          ]
        },
        {
          "name": "autocopypackage",
          "properties": [
            {
              "id": "autocopy",
              "type": "multiplecomplex",
              "title": "自动抄送",
              "value": "",
              "description": "指定岗位,指定人,或者部门负责人",
              "popular": true
            }
          ]
        },
    

    其中
    1、approvalrule指定"type": "String",最为简单,效果如下


    String类型自定义属性

    2、approvalway指定"type": "kisbpm-multiinstance-approvalway","refToView": "multiinstance",表示为下拉列表,其中这里的kisbpm-multiinstance-approvalway需要额外在两处进行修改,效果如下


    下拉类型自定义属性

    Activiti explorer前端使用的是AngularJS

    2-1、properties.js 定义显示和写入模板
    增加

    "kisbpm-multiinstance-approvalway" : {
            "readModeTemplateUrl": "editor-app/configuration/properties/multiinstance-property-display-approvalway.html",
            "writeModeTemplateUrl": "editor-app/configuration/properties/multiinstance-property-write-approvalway.html"
        },
    

    2-2、增加定义的显示和写入模板, AngularJS语法
    multiinstance-property-display-approvalway.html

    <span ng-if="!property.noValue">
        <select ng-model="property.value">
            <option value="oneTicket">一票通过或拒绝</option>
            <option value="allTicket">全部通过</option>
        </select></span>
    <span ng-if="property.noValue" translate>PROPERTY.EMPTY</span>
    

    multiinstance-property-write-approvalway.html

    <div ng-controller="KisBpmMultiInstanceCtrl">
        <select ng-model="property.value" ng-change="multiInstanceChanged()">
            <option value="oneTicket">一票通过或拒绝</option>
            <option value="allTicket">全部通过</option>
        </select>
    </div>
    

    3、autocopypackage指定"type": "multiplecomplex",需要额外增加读、写模板,并在properties.js中定义模板指向,效果如下

    复杂多选类型自定义属性1 复杂多选类型自定义属性2

    3-1、properties.js定义模板

    "oryx-autocopy-multiplecomplex": {
            "readModeTemplateUrl": "editor-app/configuration/properties/auto-copy-display-template.html",
            "writeModeTemplateUrl": "editor-app/configuration/properties/auto-copy-write-template.html"
        },
    

    3-2、实现模板
    auto-copy-display-template.html

    <span ng-if="!property.noValue">{{'PROPERTY.AUTOCOPY.DISPLAY' | translate:property.value.autocopies}}</span>
    <span ng-if="property.noValue" translate>PROPERTY.AUTOCOPY.EMPTY</span>
    

    auto-copy-write-template.html

    <!-- Just need to instantiate the controller, and it will take care of showing the modal dialog -->
    <span ng-controller="KisBpmAutoCopyCtrl">
    </span>
    

    auto-copy-popup.html

    
    <div class="modal" ng-controller="KisBpmAutoCopyPopupCtrl">
        <div class="modal-dialog modal-wide">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-click="close()">&times;</button>
                    <h2>{{'PROPERTY.PROPERTY.EDIT.TITLE' | translate:property}}</h2>
                </div>
                <div class="modal-body">
    
                    <div class="row row-no-gutter">
                        <div class="col-xs-6">
                            <div ng-if="translationsRetrieved" class="kis-listener-grid" ng-grid="gridOptions"></div>
                            <div class="pull-right">
                                <div class="btn-group">
                                    <a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.MOVE.UP | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveListenerUp()"><i class="glyphicon glyphicon-arrow-up"></i></a>
                                    <a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.MOVE.DOWN | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveListenerDown()"><i class="glyphicon glyphicon-arrow-down"></i></a>
                                </div>
                                <div class="btn-group">
                                    <a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.ADD | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="addNewListener()"><i class="glyphicon glyphicon-plus"></i></a>
                                    <a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.REMOVE | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="removeListener()"><i class="glyphicon glyphicon-minus"></i></a>
                                </div>
                            </div>
                        </div>
    
                        <div class="col-xs-6">
                            <div ng-show="selectedListeners.length > 0">
    
                                <div class="form-group">
                                    <label for="typeField">{{'PROPERTY.AUTOCOPY.TYPE' | translate}}</label>
                                    <select id="typeField" class="form-control" ng-model="selectedListeners[0].type">
                                        <option value="station">指定岗位</option>
                                        <option value="person">指定人</option>
                                        <option value="deptHeader">部门负责人</option>
                                    </select>
                                </div>
    
                                <div class="form-group">
                                    <label for="assignerField">{{'PROPERTY.AUTOCOPY.ASSIGNER' | translate}}</label>
                                    <select id="assignerField" class="form-control" ng-model="selectedListeners[0].assigner">
                                        <option>张三</option>
                                        <option>李四</option>
                                    </select>
                                </div>
    
                                <div class="form-group">
                                    <label for="settingField">{{'PROPERTY.AUTOCOPY.OTHERSETTING' | translate}}</label>
                                    <select id="settingField" class="form-control" ng-model="selectedListeners[0].othersetting">
                                        <option>为空时自动寻找上级</option>
                                        <option>为空时不寻找上级</option>
                                    </select>
                                </div>
    
                            </div>
                            <div ng-show="selectedListeners.length == 0" class="muted no-property-selected" translate>PROPERTY.AUTOCOPY.UNSELECTED</div>
                        </div>
                    </div>
                </div>
                <div class="modal-footer">
                    <button ng-click="cancel()" class="btn btn-primary" translate>ACTION.CANCEL</button>
                    <button ng-click="save()" class="btn btn-primary" translate>ACTION.SAVE</button>
                </div>
            </div>
        </div>
    </div>
    

    实现properties-auto-copy-controller.js,并在modler.html中进行引用

    /*
     * Activiti Modeler component part of the Activiti project
     * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
     * 
     * This library is free software; you can redistribute it and/or
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
     * version 2.1 of the License, or (at your option) any later version.
     *
     * This library is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * Lesser General Public License for more details.
    
     * You should have received a copy of the GNU Lesser General Public
     * License along with this library; if not, write to the Free Software
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     */
    
    /*
     * Auto copy
     */
    
    var KisBpmAutoCopyCtrl = [ '$scope', '$modal', '$timeout', '$translate', function($scope, $modal, $timeout, $translate) {
    
        // Config for the modal window
        var opts = {
            template:  'editor-app/configuration/properties/auto-copy-popup.html?version=' + Date.now(),
            scope: $scope
        };
    
        // Open the dialog
        $modal(opts);
    }];
    
    var KisBpmAutoCopyPopupCtrl = [ '$scope', '$q', '$translate', function($scope, $q, $translate) {
    
        // Put json representing form properties on scope
        if ($scope.property.value !== undefined && $scope.property.value !== null
            && $scope.property.value.autocopies !== undefined
            && $scope.property.value.autocopies !== null) {
    
            if ($scope.property.value.autocopies.constructor == String)
            {
                $scope.autocopies = JSON.parse($scope.property.value.autocopies);
            }
            else
            {
                // Note that we clone the json object rather then setting it directly,
                // this to cope with the fact that the user can click the cancel button and no changes should have happened
                $scope.autocopies = angular.copy($scope.property.value.autocopies);
            }
        } else {
            $scope.autocopies = [];
        }
    
        // Array to contain selected properties (yes - we only can select one, but ng-grid isn't smart enough)
        $scope.selectedListeners = [];
        $scope.selectedFields = [];
        $scope.translationsRetrieved = false;
    
        $scope.labels = {};
    
        var typePromise = $translate('PROPERTY.AUTOCOPY.TYPE');
        var assignerPromise = $translate('PROPERTY.AUTOCOPY.ASSIGNER');
        var othersettingPromise = $translate('PROPERTY.AUTOCOPY.OTHERSETTING');
    
        $q.all([typePromise, assignerPromise, othersettingPromise]).then(function(results) {
            $scope.labels.typeLabel = results[0];
            $scope.labels.assignerLabel = results[1];
            $scope.labels.othersettingLabel = results[2];
            $scope.translationsRetrieved = true;
    
            // Config for grid
            $scope.gridOptions = {
                data: 'autocopies',
                enableRowReordering: true,
                headerRowHeight: 28,
                multiSelect: false,
                keepLastSelected : false,
                selectedItems: $scope.selectedListeners,
                afterSelectionChange: function (rowItem, event) {
                    $scope.selectedFields.length = 0;
                    if ($scope.selectedListeners.length > 0)
                    {
                        var fields = $scope.selectedListeners[0].fields;
                    }
                },
                columnDefs: [{ field: 'type', displayName: $scope.labels.typeLabel },
                    { field: 'assigner', displayName: $scope.labels.assignerLabel},
                    { field: 'othersetting', displayName: $scope.labels.othersettingLabel }]
            };
    
            this.typeItems = [
                { text: "指定岗位", value: 'station' },
                { text: "指定人", value: 'person' },
                { text: "部门负责人", value: 'deptHeader' },
            ];
    
            // Config for field grid
            $scope.gridFieldOptions = {
                data: 'selectedListeners[0].fields',
                enableRowReordering: true,
                headerRowHeight: 28,
                multiSelect: false,
                keepLastSelected : false,
                selectedItems: $scope.selectedFields,
                columnDefs: [{ field: 'type', displayName: $scope.labels.typeLabel,
                    editorType: DropList,
                    editorSettings: {items: this.typeItems}},
                    { field: 'assigner', displayName: $scope.labels.assignerLabel},
                    { field: 'othersetting', displayName: $scope.labels.othersettingLabel }]
            };
        });
        
        $scope.listenerDetailsChanged = function() {
        };
    
        // Click handler for add button
        $scope.addNewListener = function() {
            $scope.autocopies.push({ type : 'station',
                assigner : '',
                othersetting : ''});
        };
    
        // Click handler for remove button
        $scope.removeListener = function() {
            if ($scope.selectedListeners.length > 0) {
                var index = $scope.autocopies.indexOf($scope.selectedListeners[0]);
                $scope.gridOptions.selectItem(index, false);
                $scope.autocopies.splice(index, 1);
    
                $scope.selectedListeners.length = 0;
                if (index < $scope.autocopies.length) {
                    $scope.gridOptions.selectItem(index + 1, true);
                } else if ($scope.autocopies.length > 0) {
                    $scope.gridOptions.selectItem(index - 1, true);
                }
            }
        };
    
        // Click handler for up button
        $scope.moveListenerUp = function() {
            if ($scope.selectedListeners.length > 0) {
                var index = $scope.autocopies.indexOf($scope.selectedListeners[0]);
                if (index != 0) { // If it's the first, no moving up of course
                    // Reason for funny way of swapping, see https://github.com/angular-ui/ng-grid/issues/272
                    var temp = $scope.autocopies[index];
                    $scope.autocopies.splice(index, 1);
                    $timeout(function(){
                        $scope.autocopies.splice(index + -1, 0, temp);
                    }, 100);
    
                }
            }
        };
    
        // Click handler for down button
        $scope.moveListenerDown = function() {
            if ($scope.selectedListeners.length > 0) {
                var index = $scope.autocopies.indexOf($scope.selectedListeners[0]);
                if (index != $scope.autocopies.length - 1) { // If it's the last element, no moving down of course
                    // Reason for funny way of swapping, see https://github.com/angular-ui/ng-grid/issues/272
                    var temp = $scope.autocopies[index];
                    $scope.autocopies.splice(index, 1);
                    $timeout(function(){
                        $scope.autocopies.splice(index + 1, 0, temp);
                    }, 100);
    
                }
            }
        };
        
        $scope.fieldDetailsChanged = function() {
        };
    
        // Click handler for add button
        $scope.addNewField = function() {
            if ($scope.selectedListeners.length > 0)
            {
                if ($scope.selectedListeners[0].fields == undefined)
                {
                    $scope.selectedListeners[0].fields = [];
                }
                $scope.selectedListeners[0].fields.push({
                    type : '',
                    assigner : '',
                    othersetting: ''});
            }
        };
    
        // Click handler for remove button
        $scope.removeField = function() {
            if ($scope.selectedFields.length > 0) {
                var index = $scope.selectedListeners[0].fields.indexOf($scope.selectedFields[0]);
                $scope.gridFieldOptions.selectItem(index, false);
                $scope.selectedListeners[0].fields.splice(index, 1);
    
                $scope.selectedFields.length = 0;
                if (index < $scope.selectedListeners[0].fields.length) {
                    $scope.gridFieldOptions.selectItem(index + 1, true);
                } else if ($scope.selectedListeners[0].fields.length > 0) {
                    $scope.gridFieldOptions.selectItem(index - 1, true);
                }
            }
        };
    
        // Click handler for up button
        $scope.moveFieldUp = function() {
            if ($scope.selectedFields.length > 0) {
                var index = $scope.selectedListeners[0].fields.indexOf($scope.selectedFields[0]);
                if (index != 0) { // If it's the first, no moving up of course
                    // Reason for funny way of swapping, see https://github.com/angular-ui/ng-grid/issues/272
                    var temp = $scope.selectedListeners[0].fields[index];
                    $scope.selectedListeners[0].fields.splice(index, 1);
                    $timeout(function(){
                        $scope.selectedListeners[0].fields.splice(index + -1, 0, temp);
                    }, 100);
    
                }
            }
        };
    
        // Click handler for down button
        $scope.moveFieldDown = function() {
            if ($scope.selectedFields.length > 0) {
                var index = $scope.selectedListeners[0].fields.indexOf($scope.selectedFields[0]);
                if (index != $scope.selectedListeners[0].fields.length - 1) { // If it's the last element, no moving down of course
                    // Reason for funny way of swapping, see https://github.com/angular-ui/ng-grid/issues/272
                    var temp = $scope.selectedListeners[0].fields[index];
                    $scope.selectedListeners[0].fields.splice(index, 1);
                    $timeout(function(){
                        $scope.selectedListeners[0].fields.splice(index + 1, 0, temp);
                    }, 100);
    
                }
            }
        };
    
        // Click handler for save button
        $scope.save = function() {
    
            if ($scope.autocopies.length > 0) {
                $scope.property.value = {};
                $scope.property.value.autocopies = $scope.autocopies;
            } else {
                $scope.property.value = null;
            }
    
            $scope.updatePropertyInModel($scope.property);
            $scope.close();
        };
    
        $scope.cancel = function() {
            $scope.$hide();
            $scope.property.mode = 'read';
        };
    
        // Close button handler
        $scope.close = function() {
            $scope.$hide();
            $scope.property.mode = 'read';
        };
    
    }];
    

    modeler.html

    <script src="editor-app/configuration/properties-auto-copy-controller.js" type="text/javascript"></script>
    

    多语言定义在en.json中,这里直接修改了,也可以增加zh_cn.json,然后切换语言

        "PROPERTY.AUTOCOPY.DISPLAY" : "{{length}}自动抄送",
        "PROPERTY.AUTOCOPY.EMPTY" : "没有配置自动抄送",
        "PROPERTY.AUTOCOPY.TYPE" : "审批规则",
        "PROPERTY.AUTOCOPY.ASSIGNER" : "审批人",
        "PROPERTY.AUTOCOPY.OTHERSETTING" : "其他设置",
        "PROPERTY.AUTOCOPY.UNSELECTED" : "没有配置自动抄送",
        "PROPERTY.AUTOCOPY.FIELDS.TYPE" : "审批规则",
        "PROPERTY.AUTOCOPY.FIELDS.ASSIGNER" : "审批人",
        "PROPERTY.AUTOCOPY.FIELDS.OTHERSETTING" : "其他设置",
        "PROPERTY.AUTOCOPY.FIELDS.EMPTY" : "没有选择字段"
    

    除了以上前端工作,自定义属性,还需要客制化流程图加载过程,以读取自定义属性

    CustomUserTaskJsonConverter.java

    public class CustomUserTaskJsonConverter extends UserTaskJsonConverter {
         @Override
         protected FlowElement convertJsonToElement(JsonNode elementNode, JsonNode modelNode, Map<String, JsonNode> shapeMap) {
             FlowElement flowElement = super.convertJsonToElement(elementNode, modelNode, shapeMap);
             UserTask userTask = (UserTask) flowElement;
             List<CustomProperty> customProperties = new ArrayList<>();
             
             //添加扩展属性 审批方式
             String approvalWay = this.getPropertyValueAsString(CommonConstants.APPROVAL_WAY, elementNode);
             if (StringUtils.isNotEmpty(approvalWay)) {
                 CustomProperty customProperty = new CustomProperty();
                 customProperty.setName(CommonConstants.APPROVAL_WAY);
                 customProperty.setSimpleValue(approvalWay);
                 customProperties.add(customProperty);
             }
    
             //添加扩展属性 审批规则 
             String approvalRule = this.getPropertyValueAsString(CommonConstants.APPROVAL_RULE, elementNode);
             if (StringUtils.isNotEmpty(approvalRule)) {
                 CustomProperty customProperty = new CustomProperty();
                 customProperty.setName(CommonConstants.APPROVAL_RULE);
                 customProperty.setSimpleValue(approvalRule);
                 customProperties.add(customProperty);
             }
    
             //添加扩展属性 自动抄送
             JsonNode autoCopy = this.getProperty(CommonConstants.AUTO_COPY, elementNode);
             if (ObjectUtil.isNotEmpty(autoCopy)) {
                 CustomProperty customProperty = new CustomProperty();
                 customProperty.setName(CommonConstants.AUTO_COPY);
                 customProperties.add(customProperty);
             }
    
             userTask.setCustomProperties(customProperties);
             return userTask;
    
         }
     }
    

    ModelServiceImpl.java

    public class ModelServiceImpl implements ModelService {
    
    @Override
        @Transactional(rollbackFor = Exception.class)
        public Boolean deploy(String modelId) {
            try {
                // 获取模型
                Model model = repositoryService.getModel(modelId);
                ObjectNode objectNode = (ObjectNode) new ObjectMapper()
                        .readTree(repositoryService.getModelEditorSource(model.getId()));
    
                //从json 文件解析并添加自定义扩展属性到模型文件中
                CustomBpmnJsonConverter.getConvertersToBpmnMap().put("UserTask", CustomUserTaskJsonConverter.class);
    
                BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(objectNode);
            }
            ...
    }
    

    待实现

    • 指定岗位时,岗位下拉列表自动获取
    • 指定人时,人员下拉列表自动获取
    • 选择不同审批规则时,下拉获取不同的审批人列表
    • 根据设置的审批方式实现任务监听,审批通过/拒绝时更新任务节点和流程实例状态
    • 根据设置的审批规则,进入节点时,动态获取审批候选人
    • 根据设置的自动抄送规则,节点审批通过/拒绝时,自动触发抄送

    相关文章

      网友评论

          本文标题:Activiti Explorer定制

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