如何为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
相关产品推荐
相关产品推荐

