bpmnjs Properties-panel拓展(ExtensionElements拓展篇)

接上文bpmnjs Properties-panel拓展(属性设置篇),继续记录下第三个拓展需求的实现。

需求简述

ExclusiveGateway标签的extensionElements标签中增加子标签<activiti:executionListener>子标签,可增加复数子标签。子标签中包含event属性和delegateExpression属性可进行设置,并实现name属性的自动生成。event属性默认设为start。
那么最终的结构可表示为:

- exclusiveGateway
	- extensionElements
		- activiti:exectionListener (event, name, delegateExpression)

json属性设置

activiti.json中增加要拓展的属性

      {
        "name": "executionListener",
        "superClass": [ "Element" ],
        "isMany": true,
        "properties": [
          {
            "name": "event",
            "isAttr":true,
            "type": "String"
          },{
            "name": "delegateExpression",
            "isAttr": true,
            "type": "String"
          }
        ]
      }

补充executionListener的具体定义,主要的是superClass,设为Element,这样会被归到extensionElements里。

ExtensionExecutionListener组件

用来构建单条的executionListener。
ExtensionExecutionListener.js

import { TextFieldEntry } from '@bpmn-io/properties-panel';

import { useService } from 'bpmn-js-properties-panel';

// 构建list中的一层,包括event设置和delegateExpression设置
export default function ExtensionExecutionListener(props) {

    const {
        idPrefix,
        element,
        executionListener
    } = props;

    const entries = [
        {
            // idPrefix是外部传进来的element id,所以增加一个后缀来进行区分
            id: idPrefix + '-event',
            component: event,
            idPrefix,
            executionListener
        },
        {
            id: idPrefix + '-delegateExpression',
            component: delegateExpression,
            idPrefix,
            executionListener
        }
    ];

    return entries;
}

// 设置event属性
function event(props) {

    // 要注意的是executionListener,需要它来进行属性的修改 
    const {
        idPrefix,
        element,
        executionListener
    } = props;

    const commandStack = useService('commandStack');
    const translate = useService('translate');
    const debounce = useService('debounceInput');

    const setValue = (value) => {
        commandStack.execute('element.updateModdleProperties', {
            element,
            moddleElement: executionListener,
            properties: {
                event: value
            }
        });
    };

    const getValue = (executionListener) => {
        return executionListener.event;
    };

    // 也可使用html拼接
    return TextFieldEntry({
        element: executionListener,
        id: idPrefix + '-event',
        label: translate('event'),
        getValue,
        setValue,
        debounce
    });
}

// 设置delegateExpression属性
function delegateExpression(props) {
    const {
        idPrefix,
        element,
        executionListener
    } = props;

    const commandStack = useService('commandStack');
    const translate = useService('translate');
    const debounce = useService('debounceInput');

    const setValue = (value) => {
        commandStack.execute('element.updateModdleProperties', {
            element,
            moddleElement: executionListener,
            properties: {
                delegateExpression: value
            }
        });
    };

    // exectionListener获取属性
    const getValue = (executionListener) => {
        return executionListener.delegateExpression;
    };

    // 也可使用html拼接
    return TextFieldEntry({
        element: executionListener,
        id: idPrefix + '-delegateExpression',
        label: translate('delegateExpression'),
        getValue,
        setValue,
        debounce
    });
}

主要是构建了包含event和delegateExpression的entries。需要注意的是,和前文bpmnjs Properties-panel拓展(属性设置篇)中不同,这里使用了commandStack命令栈去直接执行update,这种方法后面也会用到,好处是能将数个指令存到array中一起执行,比较方便写,不过错误提示会变成一坨,可能是我用的不对吧。

ExtensionExecutionListenerGroup

需要在ExtensionElements中塞进多个ExecutionListener,因此需要构建其组成的list。

import {
    getBusinessObject
} from 'bpmn-js/lib/util/ModelUtil';

import { without } from 'min-dash';
import ExtensionExecutionListener from './ExtensionExecutionListener';

import Ids from 'ids';

function nextId(prefix) {
    const ids = new Ids([32, 32, 1]);
    return ids.nextPrefixed(prefix);
}

function createElement(elementType, properties, parent, factory) {
    const element = factory.create(elementType, properties);

    if (parent) {
        element.$parent = parent;
    }

    return element;
}

首先是几个工具方法,getId随机构建不重复的id信息,createElement方法利用bpmnFactory来构建bpmnElement,是一种bpmnjs定义的结构体,用于后续插入整体的xml中。

// 获取element的extensionElment下的所有ExecutionListener
function getExtensionExecutionListeners(element) {
    const businessObject = getBusinessObject(element);
    // 不存在就算了
    if (!businessObject.extensionElements) {
        return null;
    }
    // 存在则使用filter找出所有的activiti:exectionListener
    return businessObject.extensionElements.values.filter(function (e) {
        return e.$instanceOf("activiti:executionListener");
    });
}

工具方法,找executionListener。executionListener是在extensionElement下的,所以从那里利用filter找,返回是一个array或空。

// 关键方法,构建listener list
export default function ExtensionExecutionListenerGroup({ element, injector }) {

    // 防空
    const executionListeners = getExtensionExecutionListeners(element) || [];
    console.log(executionListeners);

    const bpmnFactory = injector.get('bpmnFactory'),
        commandStack = injector.get('commandStack');

    // 对list中的每个item进行构建
    const items = executionListeners.map((executionListener, index) => {
        // 按顺序给个id
        const id = element.id + '-executionListener-' + index;
        // 构建item
        return {
            id,
            label: executionListener.get('event') + '---' + executionListener.get('delegateExpression') || '',
            entries: ExtensionExecutionListener({
                idPrefix: id,
                element,
                executionListener
            }),
            autoFocusEntry: id + '-el',
            remove: removeFactory({ commandStack, element, executionListener })
        };
    });

    return {
        items,
        add: addFactory({ element, bpmnFactory, commandStack })
    };
}

主要是进行组件的构建。需要注意的是设置remove方法和add方法,分别是在可视化界面中进行组件增加和删除时调用的方法。

// 去除item时执行的方法,先获取extensionElement,之后进行without处理,最后更新
function removeFactory({ commandStack, element, executionListener }) {
    return function (event) {
        event.stopPropagation();

        const executionListeners = getExtensionExecutionListeners(element);
        
        if (!executionListeners) {
            return;
        }

        const businessObject = getBusinessObject(element);

        // 利用without将当前item对应的信息剔除
        console.log(executionListeners);
        const executionListenersAfter = without(executionListeners, executionListener);
        console.log(executionListenersAfter);

        // 更新剔除后的信息
        commandStack.execute('element.updateModdleProperties', {
            element,
            moddleElement: businessObject.get('extensionElements'),
            properties: {
                values: executionListenersAfter
            }
        });
        
    };
}

remove中主要工作就是先找到所有executionListener组成的array,之后通过without方法去除要删除的对象,最后进行update。

// 增加item时执行的方法
function addFactory({ element, bpmnFactory, commandStack }) {
    return function (event) {
        event.stopPropagation();

        // 存放处理命令,最后使用commandStack执行
        const commands = [];

        const businessObject = getBusinessObject(element);

        let extensionElements = businessObject.get('extensionElements');

        // extensionElements是bpmn自带的属性,不存在则先创建
        if (!extensionElements) {
            extensionElements = createElement(
                'bpmn:ExtensionElements',
                { values: [] },
                businessObject,
                bpmnFactory
            );

            commands.push({
                cmd: 'element.updateModdleProperties',
                context: {
                    element,
                    moddleElement: businessObject,
                    properties: { extensionElements }
                }
            });
        }

        // 构建exectionListener
        const newExecutionListener = createElement('activiti:executionListener', {
            name: nextId('ExecutionListener_'),
            event: 'start', // 这边其实可以改成下拉框
            delegateExpression: ''
        }, extensionElements, bpmnFactory);

        // 增加至extensionElements
        commands.push({
            cmd: 'element.updateModdleProperties',
            context: {
                element,
                moddleElement: extensionElements,
                properties: {
                    // 使新增的显示在下面
                    values: [newExecutionListener, ...extensionElements.get('values')]
                }
            }
        });

        commandStack.execute('properties-panel.multi-command-executor', commands);
    };
}

增加item主要分为三步,新增extensionElements,构建executionListener并给出随机id,update。

注册到可视化面板

ActivitiPropertiesProvider.js中将组件进行注册

      // 网关增加extensionElement:ExecutionListener
      if(is(element, 'bpmn:ExclusiveGateway')){
        groups.push(createExtensionExclusiveGateway(element, injector, translate));
      }

// 构建extensionElement下的exectionListener
function createExtensionExclusiveGateway(element, injector, translate){
  // 构建group list
  const elGroup = {
    id: 'ExtensionExectionListener',
    label: translate('对应监听实现类设置'),
    component: ListGroup,
    ...ExtensionExecutionListenerGroup({ element, injector })
  };

  return elGroup; 
}

实现效果

在这里插入图片描述
list增删成员
在这里插入图片描述xml成功修改

总结

实现了增加ExtensionElements中标签成员的需求,其实实现逻辑还是比较清晰的,构建组件,组件list,注册三步走。不过写的时候还是踩了不少坑,调了蛮久,还得多练啊。项目已上传至Github https://github.com/huiluczP/huiluczp-activiti-properties-panel-extension,感兴趣可以看一下。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值