You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Camunda Modeler新增Activity Diagram类型并解决解析错误?

如何在Camunda Modeler中新增自定义Activity Diagram图表类型

我正在基于Camunda Modeler开发,要新增一种Activity Diagram自定义图表类型(区别于默认的BPMN、DMN、CMMN),要求Camunda引擎能识别并在UI中展示。

期望能无报错运行以下代码:

const element = event.element;
if (is(element, 'bpmn:ManualTask')) {
  // 打开并渲染新的Activity Diagram
  const activityDiagramXML = `<activityDiagram xmlns="http://example.com/schema/activityDiagram">
    <metrics id="metrics1"/>
    <operation id="operation1"/>
  </activityDiagram>`;
  
  BPMNJS.importXML(activityDiagramXML, (err) => {
    if (!err) {
      console.log('Activity Diagram rendered');
    } else {
      console.error('Failed to render Activity Diagram', err);
    }
  });
}

但默认的importXML方法只支持<bpmn:Definitions>这类默认XML标签,核心代码如下:

BaseViewer.prototype.importXML = async function importXML(xml, bpmnDiagram) {

    const self = this;
  
    function ParseCompleteEvent(data) {
      return self.get('eventBus').createEvent(data);
    }
  
    let aggregatedWarnings = [];
    try {
  
      // 触发预解析监听器,允许修改XML
      xml = this._emit('import.parse.start', { xml: xml }) || xml;
  
      let parseResult;
      try {
        parseResult = await this._moddle.fromXML(xml, 'bpmn:Definitions');
      } catch (error) {
        this._emit('import.parse.complete', {
          error
        });
  
        throw error;
      }
  
      let definitions = parseResult.rootElement;
      const references = parseResult.references;
      const parseWarnings = parseResult.warnings;
      const elementsById = parseResult.elementsById;
  
      aggregatedWarnings = aggregatedWarnings.concat(parseWarnings);
  
      // 触发解析完成监听器,允许修改definitions
      definitions = this._emit('import.parse.complete', ParseCompleteEvent({
        error: null,
        definitions: definitions,
        elementsById: elementsById,
        references: references,
        warnings: aggregatedWarnings
      })) || definitions;
  
      const importResult = await this.importDefinitions(definitions, bpmnDiagram);
  
      aggregatedWarnings = aggregatedWarnings.concat(importResult.warnings);
  
      this._emit('import.done', { error: null, warnings: aggregatedWarnings });
  
      return { warnings: aggregatedWarnings };
    } catch (err) {
      let error = err;
      aggregatedWarnings = aggregatedWarnings.concat(error.warnings || []);
      addWarningsToError(error, aggregatedWarnings);
  
      error = checkValidationError(error);
  
      this._emit('import.done', { error, warnings: error.warnings });
  
      throw error;
    }
};

我已经编写了activityDiagramModdle.js和ActivityDiagramRenderer.js并完成注册,代码如下:

activityDiagramModdle.js

export default {
  name: 'ActivityDiagram',
  prefix: 'ad',
  uri: 'http://example.com/schema/activityDiagram',
  xml: {
    tagAlias: 'lowerCase'
  },
  types: [
    {
      name: 'ActivityDiagram',
      superClass: ['Element'],
      properties: [
        {
          name: 'metrics',
          isMany: true,
          type: 'ad:Metrics'
        },
        {
          name: 'operation',
          isMany: true,
          type: 'ad:Operation'
        }
      ]
    },
    {
      name: 'Metrics',
      superClass: ['Element'],
      properties: [
        {
          name: 'id',
          isAttr: true,
          type: 'String'
        }
      ]
    },
    {
      name: 'Operation',
      superClass: ['Element'],
      properties: [
        {
          name: 'id',
          isAttr: true,
          type: 'String'
        }
      ]
    }
  ]
};

ActivityDiagramRenderer.js

import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';

export default class ActivityDiagramRenderer extends BaseRenderer {
  constructor(eventBus, bpmnRenderer) {
    super(eventBus, 2000);

    this.bpmnRenderer = bpmnRenderer;
  }

  canRender(element) {
    return /^ad:/.test(element.type);
  }

  drawShape(parentNode, element) {
    const shape = this.bpmnRenderer.drawShape(parentNode, element);

    if (element.type === 'ad:Metrics') {
      shape.attr({
        fill: 'green'
      });
    } else if (element.type === 'ad:Operation') {
      shape.attr({
        fill: 'blue'
      });
    }

    return shape;
  }
}

ActivityDiagramRenderer.$inject = ['eventBus', 'bpmnRenderer'];

但尝试渲染新图表类型时,出现错误:failed to parse document as <bpmn:Definitions>,需要解决如何新增自定义图表类型并支持<activityDiagram>、<operation>等标签。


解决方案

1. 扩展importXML方法,支持动态根元素类型

默认importXML硬编码了仅解析<bpmn:Definitions>,需修改为根据XML根元素的命名空间动态匹配对应的Moddle类型:

BaseViewer.prototype.importXML = async function importXML(xml, options = {}) {
  const self = this;

  function ParseCompleteEvent(data) {
    return self.get('eventBus').createEvent(data);
  }

  let aggregatedWarnings = [];
  try {
    // 预解析XML获取根元素信息
    const parser = new DOMParser();
    const doc = parser.parseFromString(xml, 'text/xml');
    const rootElement = doc.documentElement;
    const rootNs = rootElement.namespaceURI;
    const rootLocalName = rootElement.localName;

    // 匹配根元素对应的Moddle类型
    let rootType;
    if (rootNs === 'http://example.com/schema/activityDiagram') {
      rootType = 'ad:ActivityDiagram';
    } else if (rootNs === 'http://www.omg.org/spec/BPMN/20100524/MODEL') {
      rootType = 'bpmn:Definitions';
    } else {
      throw new Error(`Unsupported root element namespace: ${rootNs}`);
    }

    // 触发预解析事件
    xml = this._emit('import.parse.start', { xml: xml }) || xml;

    let parseResult;
    try {
      // 动态传入根类型解析XML
      parseResult = await this._moddle.fromXML(xml, rootType);
    } catch (error) {
      this._emit('import.parse.complete', { error });
      throw error;
    }

    let rootElementModel = parseResult.rootElement;
    const references = parseResult.references;
    const parseWarnings = parseResult.warnings;
    const elementsById = parseResult.elementsById;

    aggregatedWarnings = aggregatedWarnings.concat(parseWarnings);

    // 触发解析完成事件,传递通用根元素
    rootElementModel = this._emit('import.parse.complete', ParseCompleteEvent({
      error: null,
      rootElement: rootElementModel,
      elementsById: elementsById,
      references: references,
      warnings: aggregatedWarnings
    })) || rootElementModel;

    // 调用通用根元素导入方法
    const importResult = await this.importRootElement(rootElementModel, options);

    aggregatedWarnings = aggregatedWarnings.concat(importResult.warnings);

    this._emit('import.done', { error: null, warnings: aggregatedWarnings });

    return { warnings: aggregatedWarnings };
  } catch (err) {
    let error = err;
    aggregatedWarnings = aggregatedWarnings.concat(error.warnings || []);
    addWarningsToError(error, aggregatedWarnings);

    error = checkValidationError(error);

    this._emit('import.done', { error, warnings: error.warnings });

    throw error;
  }
};

2. 新增通用根元素导入方法

原importDefinitions仅适配BPMN,需新增importRootElement处理不同类型的根元素:

BaseViewer.prototype.importRootElement = async function importRootElement(rootElement, options) {
  const self = this;
  const elementFactory = this.get('elementFactory');
  const canvas = this.get('canvas');

  let warnings = [];

  // 处理Activity Diagram逻辑
  if (rootElement.$type === 'ad:ActivityDiagram') {
    // 创建画布根节点
    const diagramRoot = elementFactory.createRoot({
      id: 'activity-diagram-root',
      type: 'ad:ActivityDiagram'
    });
    canvas.setRootElement(diagramRoot);

    // 渲染Metrics元素
    rootElement.metrics.forEach((metric, index) => {
      const shape = elementFactory.createShape({
        id: metric.id,
        type: 'ad:Metrics',
        x: 100,
        y: 100 + (index * 120),
        width: 80,
        height: 80
      });
      canvas.addShape(shape, diagramRoot);
    });

    // 渲染Operation元素
    rootElement.operation.forEach((op, index) => {
      const shape = elementFactory.createShape({
        id: op.id,
        type: 'ad:Operation',
        x: 300,
        y: 100 + (index * 120),
        width: 80,
        height: 80
      });
      canvas.addShape(shape, diagramRoot);
    });
  } else if (rootElement.$type === 'bpmn:Definitions') {
    // 复用原有BPMN解析逻辑
    warnings = (await this.importDefinitions(rootElement, options)).warnings;
  }

  return { warnings };
};

3. 确认Moddle扩展正确注册

在插件配置中确保activityDiagramModdle被注册到Moddle扩展:

import activityDiagramModdle from './activityDiagramModdle';
import ActivityDiagramRenderer from './ActivityDiagramRenderer';

export default {
  __init__: ['activityDiagramRenderer'],
  activityDiagramRenderer: ['type', ActivityDiagramRenderer],
  moddleExtensions: {
    ad: activityDiagramModdle
  }
};

4. 调整XML结构(可选)

为符合Moddle规范,可给Activity Diagram XML添加命名空间前缀:

<ad:activityDiagram xmlns:ad="http://example.com/schema/activityDiagram">
  <ad:metrics id="metrics1"/>
  <ad:operation id="operation1"/>
</ad:activityDiagram>

内容的提问来源于stack exchange,提问作者Lucas Gentry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 18:42:02