Vue+bpmn.js自定义流程图之render(三)

本文介绍如何通过重写BPMN.js的Renderer模块来自定义流程图元素样式,包括自定义元素图标、尺寸及位置,并展示了具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、回顾

经过前面 palette 模块的自定义,左侧工具栏的样式已经出来了,但是 rendercontextPad 这两个部分还是原来的样式。同样的,这两部分也需要重写对应的方法来实现自定义。
在这里插入图片描述

二、代码

1.目录

同样在plugins文件目录下新建render文件夹。创建一个index.js入口文件和render.js自定义render文件(用来覆盖默认render),再创建一个util.js文件,用来存放render的自定义配置(元素大小和自定义元素图片路径)。

2.代码实现

1.render.js文件

render.js

import inherits from 'inherits'
import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer'
import {
    isObject,
    assign,
    forEach
} from 'min-dash';
import {
    append as svgAppend,
    create as svgCreate,
    classes as svgClasses
} from 'tiny-svg'

import {
    customElements,
    customConfig,
    hasLabelElements
} from './util'


export default function CustomRenderer(eventBus, styles, textRenderer) {

    //删除双击事件
    delete eventBus._listeners['element.dblclick']
    BaseRenderer.call(this, eventBus, 2000)
    var computeStyle = styles.computeStyle
    function renderLabel(parentGfx, label, options) {
        options = assign({
            size: {
                x: 0,
                y: 20,
                width: 40,
            }
        }, options);

        var text = textRenderer.createText(label || '', options);
        svgClasses(text).add('djs-label');
        svgAppend(parentGfx, text);

        return text;
    }
    //计算文字宽度
    String.prototype.pxWidth = function (font) {
        // re-use canvas object for better performance
        var canvas = String.prototype.pxWidth.canvas || (String.prototype.pxWidth.canvas = document.createElement("canvas")),
            context = canvas.getContext("2d");

        font && (context.font = font);
        var metrics = context.measureText(this);

        return metrics.width;
    }
  //绘制自定义元素
    this.drawCustomElements = function (parentNode, element) {
        const {
            type,

        } = element // 获取到类型

        if (type !== 'label') {
            if (customElements.includes(type)) { // or customConfig[type]
                const {
                    url,
                    attr,
                    defaultName
                } = customConfig[type]
                //可以设置节点默认值
                // if (!element.businessObject.name && defaultName) {
                //     element.businessObject.name = defaultName
                // }

                const customIcon = svgCreate('image', {
                    ...attr,
                    href: url
                })
                element['width'] = attr.width //  直接修改了元素的宽高
                element['height'] = attr.height
                svgAppend(parentNode, customIcon)
                // 判断是否有name属性来决定是否要渲染出label
                // if (!hasLabelElements.includes(type) && element.businessObject.name) {
                //当类型为UserTask时,自定义绘制label标签(标签会在shape里面)
                if (type === 'bpmn:UserTask' && element.businessObject.name) {
                    const textWidth = element.businessObject.name.pxWidth('normal 13px')
                    const text = svgCreate('text', {
                        x: attr.x + (attr.width / 2 - textWidth / 1.6),
                        y: attr.y + attr.height + 15,
                        fontSize: "13px",
                        fill: "#000"
                    })
                    text.innerHTML = element.businessObject.name
                    svgAppend(parentNode, text)
                }
                // renderLabel(parentNode, element.label)

                return customIcon
            }
            const shape = this.bpmnRenderer.drawShape(parentNode, element)
            return shape
        }
    }
}

inherits(CustomRenderer, BaseRenderer)

CustomRenderer.$inject = ['eventBus', 'styles', 'textRenderer']

CustomRenderer.prototype.canRender = function (element) {
    // ignore labels
    return true
    // return !element.labelTarget;
}

CustomRenderer.prototype.drawShape = function (p, element) {
    const {
        type,
    } = element

    if (customElements.includes(type)) {
        return this.drawCustomElements(p, element)
    }
}

CustomRenderer.prototype.getShapePath = function (shape) {

}

上面代码做的事情:
重写 renderer类,同时覆盖了其原型上的 drawShape方法

通过drawShape来绘制各种类型的元素,先判断哪些元素类型需要自定义绘制,然后通过drawCustomElements方法绘制自定义元素。

2.util.js文件

util.js

//  数组的作用就是用来放哪些类型是需要我们自定义的, 从而在渲染的时候就可以与不需要自定义的元素作区分.
const startNode = require('/public/bpmn_imgs/startNode.png')
const endNode = require('/public/bpmn_imgs/endNode.png')
const taskNode = require('/public/bpmn_imgs/taskNode.png')
const gatewayNode = require('/public/bpmn_imgs/gatewayNode.png')
const customElements = ['bpmn:StartEvent', 'bpmn:ExclusiveGateway', 'bpmn:UserTask', 'bpmn:EndEvent'] //需要自定义的元素类型
const hasLabelElements = ['bpmn:StartEvent', 'bpmn:ExclusiveGateway', 'bpmn:UserTask', 'bpmn:EndEvent'] // 一开始就有label标签的元素类型
const customConfig = { // 自定义元素的配置
    'bpmn:StartEvent': {
        url: startNode,
        defaultName: '开始',
        attr: {
            x: 0,
            y: 0,
            width: 40,
            height: 40
        }
    },
    'bpmn:EndEvent': {
        url: endNode,
        defaultName: '结束',
        attr: {
            x: 0,
            y: 0,
            width: 40,
            height: 40
        }
    },
    'bpmn:UserTask': {
        url: taskNode,
        attr: {
            x: 0,
            y: 0,
            width: 40,
            height: 40
        }
    },
    'bpmn:ExclusiveGateway': {
        url: gatewayNode,
        attr: {
            x: 0,
            y: 0,
            width: 40,
            height: 40
        }
    },

}

export {
    customElements,
    hasLabelElements,
    customConfig
}

util.js 导出了一些自定义元素的配置 customConfig 中配置了需要自定义的元素的图片url和宽、高、坐标。

3.index.js文件

index.js

import CustomRenderer from './render'

export default {
    __init__: ["CustomRenderer"],
    CustomRenderer: ["type", CustomRenderer]
};

三、效果

render这个模块的类重写完过后,应该看到绘制在画板上的元素变成自定义的了

在这里插入图片描述

但是contextPad这个部分还是原生的样式,这个模块同样也需要重写。到这里我们应该清楚了我们就是通过重写bpmn的几个方法来达到自定义的效果,所以搞懂方法里的参数和作用也很重要,有兴趣的自己打印出来里面的参数研究一下,会更加清晰代码到底做了什么。

<think>嗯,用户想找一个适用于Vue2的流程图依赖包。首先,我得回想一下Vue2生态中常用的库。Vue2虽然现在有Vue3了,但很多项目还在用,所以应该有支持的库。 第一个想到的是bpmn-js,它是一个BPMN 2.0的渲染工具包,和Vue结合应该没问题。不过它主要面向业务流程模型,可能功能比较专业,用户是否需要这样的呢?用户可能想要更通用的流程图工具。 然后,JointJS,这个库功能强大,支持多种图表类型,包括流程图。不过可能需要自己封装成Vue组件,用户是否愿意做这些额外工作呢?可能适合需要高度定制的场景。 GoJS也不错,商业库,功能丰富,但需要付费授权。如果用户的项目是商业用途,可能需要考虑费用问题,但试用期或许可以先用。 Vue-flowchart,这个名字听起来像是专门为Vue设计的,不过得确认是否支持Vue2。查一下文档,可能有些版本兼容性问题,比如Vue3的库可能需要降级或者有Vue2分支。 另外,G6和X6来自AntV,这两个是阿里系的图表库,可能对Vue支持友好,但可能需要自己封装。G6更侧重关系数据,X6更适合流程图,但需要确认Vue2的适配情况。 可能还有其他库,比如Rete.js,用于可视化编程,但不确定是否适合普通流程图。还有mermaid.js,文本生成流程图,但集成到Vue2可能需要组件封装。 用户可能需要考虑的因素包括:是否需要免费开源、功能的复杂度、是否愿意自己封装、是否需要交互功能(如拖拽、连线)。得把这些点整理出来,帮助用户选择。 比如,如果用户需要简单易用,可能推荐vue-flowchart,但得确认是否支持Vue2。如果功能要求高,可以选择bpmn-js或JointJS,但学习曲线可能陡峭。商业项目可以考虑GoJS,但注意许可问题。 可能遗漏了一些库,需要再检查一下。比如,Vue2-Diagram,或者基于D3的封装库。另外,Element UI之类的UI框架是否有流程图组件?好像没有,所以还是得依赖专门的图表库。 总结下来,推荐几个主流选项,分别说明优缺点,帮助用户根据需求做决定。</think>以下是一些适用于Vue2的流程图依赖包推荐及分析,根据功能特点和适用场景分类整理: --- ### 一、BPMN-JS + Vue2 **特点** - 专业级BPMN 2.0流程图工具 - 支持复杂业务流程建模 - 需要手动封装为Vue组件 **代码示例** ```javascript // 安装 npm install bpmn-js // Vue组件封装 <template> <div ref="container"></div> </template> <script> import BpmnJS from 'bpmn-js'; export default { mounted() { const viewer = new BpmnJS({ container: this.$refs.container }); viewer.importXML(xmlContent); } }; </script> ``` --- ### 二、JointJS + Vue2 **特点** - 支持流程图、UML等多样化图表 - 高自由度,但需自行实现交互逻辑 - 适合需要深度定制的场景 **集成方式** ```javascript // 安装 npm install jointjs // 封装为Vue组件(伪代码) <template> <div ref="paper"></div> </template> <script> import { dia } from 'jointjs'; export default { mounted() { const graph = new dia.Graph(); new dia.Paper({ el: this.$ref.paper, model: graph }); // 添加节点和连线... } }; </script> ``` --- ### Vue-Flowchart (社区方案) **特点** - 轻量级流程图库 - 需确认版本兼容性(部分分支支持Vue2) - 基础拖拽/连线功能 **示例** ```javascript // 安装旧版本(如存在) npm install vue-flowchart@legacy // 使用 <flowchart :nodes="nodes" :connections="connections" /> ``` --- ### 四、GoJS (商业授权) **特点** - 企业级交互式流程图 - 提供官方Vue适配器 - 免费试用,商业项目需付费 **集成步骤** ```javascript import go from 'gojs'; export default { data() { return { diagram: null }; }, mounted() { this.diagram = new go.Diagram(this.$refs.diagramDiv); // 配置节点模板... } }; ``` --- ### 五、AntV X6 (推荐方案) **特点** - 阿里系流程图库 - 官方提供Vue2适配方案 - 支持SVG/Canvas双渲染引擎 **快速使用** ```javascript // 安装 npm install @antv/x6 @antv/x6-vue-shape // Vue组件 <template> <div ref="container"></div> </template> <script> import { Graph } from '@antv/x6'; import { register } from '@antv/x6-vue-shape'; register({ component: YourVueNodeComponent }); // 注册自定义节点 export default { mounted() { const graph = new Graph({ container: this.$refs.container }); graph.addNode({ shape: 'vue-shape', component: 'your-component' }); } }; </script> ``` --- ### 六、Mermaid + Vue2 (文本驱动) **特点** - 通过文本语法生成流程图 - 适合静态展示场景 **集成示例** ```javascript // 安装 npm install mermaid // 封装为组件 <template> <div v-html="renderedDiagram"></div> </template> <script> import mermaid from 'mermaid'; export default { data() { return { renderedDiagram: '' }; }, mounted() { mermaid.initialize({ startOnLoad: false }); mermaid.render('diagram', 'graph TD; A-->B').then(svg => { this.renderedDiagram = svg; }); } }; </script> ``` --- ### 选择建议 1. **企业级复杂应用** → GoJS (预算允许) / AntV X6 2. **BPMN规范需求** → BPMN-JS 3. **快速实现轻量级流程图** → Vue-Flowchart (确认兼容性) 4. **文本转图表** → Mermaid 建议优先验证AntV X6,其文档完善且对Vue2支持友好。若需更低成本方案,可尝试通过`vue-draggable`等通用拖拽库自行实现基础功能。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值