全网最详bpmn.js教材-群友问题汇总(一)

Q: bpmn.js是什么? 🤔️javascript

bpmn.js是一个BPMN2.0渲染工具包和web建模器, 使得画流程图的功能在前端来完成.css

Q: 我为何要写该系列的教材? 🤔️前端

由于公司业务的须要于是要在项目中使用到bpmn.js,可是因为bpmn.js的开发者是国外友人, 所以国内对这方面的教材不多, 也没有详细的文档. 因此不少使用方式不少坑都得本身去找.在将其琢磨完以后, 决定写一系列关于它的教材来帮助更多bpmn.js的使用者或者是期于找到一种好的绘制流程图的开发者. 同时也是本身对其的一种巩固.vue

因为是系列的文章, 因此更新的可能会比较频繁, 您要是无心间刷到了且不是您所须要的还请谅解😊.java

求赞👍求心❤️. 更但愿能对你有一点帮助.webpack

本教材全部内容已更新至GitHub 🌟ios

请认准GitHub地址: bpmn-chinese-document 🎉🎉🎉git

全网最详bpmn.js教材-群友问题汇总(一)

这一章节主要是将近段时间前端bpmn.js交流群中群友提的一些问题作一个汇总...github

后面有碰到一样问题的小伙伴但愿能帮到大家...web

问题的解答有的是群友给出的方案有些是我本身想的方案, 可能不是最优解, 若是有更好解决办法的小伙伴还但愿可以提出来呀 😁.

目录

  • palette左侧工具栏
    • 如何给工具栏的每一项都加上标题
    • paletterenderer中的图片如何用本地图片
    • 自定义 palette中如何使用它自己的图标样式
  • contextPad
    • contextPad中的内容根据元素类型不一样显示不一样
  • 文件
    • 如何加载本地 bpmn或者 xml文件
  • 属性
    • 每一个元素的 id是否可以修改
  • 其它
    • 如何建立线节点
    • 右下角的绿色 logo可否隐去

palette左侧工具栏

1. 如何给工具栏的每一项都加上标题

实现相似于下面这张图的效果:

原先咱们实现自定义palette的时候只考虑到了显示图片的状况, 有一些业务场景可能须要将每种元素的标题显示出来.

这里我提供了两种解决方案:

  1. 给每一个类定义一个伪类, 将title写到这个伪类里
  2. 额...要UI设计师将每一个title画到每一个元素图表的下面, 也就是将title做为图标的一部分

这里我主要讲解一下第一种实现方式.

首先咱们知道在customPalette中是有这么一个东西的:

'append.lindaidai-task': {
    group: 'model',
    className: 'icon-custom lindaidai-task',
    title: translate('建立一个类型为lindaidai-task的任务节点'),
    action: {
        click: appendTask,
        dragstart: appendTaskStart
    }
}
复制代码

主要看className.

以前我教材中的css代码是这样写的:

.icon-custom {
    border-radius: 50%;
    background-size: 65%;
    background-repeat: no-repeat;
    background-position: center;
}
.icon-custom.lindaidai-task {
    position: relative;
    background-image: url('https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png');
}
复制代码

如今我想在它下面加一个标题:

.icon-custom.lindaidai-task::after {
    font-size: 12px;
    content: 'LinDaiDai'; /* 这里放的就是标题 */
    position: absolute;
    top: 17px;
    left: 0;
}
复制代码

这样就简单的实现了这么一个显示标题的功能.

具体案例能够看这里: bpmn-vue-basic

2. palette和renderer中的图片如何用本地图片

palette上想要用本地图片很简单, 由于自定义palette主要是依靠className, 而className确定是写在css文件中的, 咱们只须要找到图片对应的相对路径就能够了:

例如项目目录为:

/src
    |- /assets
        |- rules.png
    |- css
        |- app.css

复制代码

它对应的引用:

/*app.css*/
.icon-custom.lindaidai-task {
    position: relative;
    /* background-image: url('https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png'); */
    background-image: url('../assets/rules.png');
}
复制代码

咱们知道自定义renderer里想要实现自定义效果主要是靠svgCreate方法建立出一个image元素而后添加到返回值中, 这个图片的url我原先一直用的是网络图片, 那确定没什么问题.

而若是你想要用一张本地图片的话, 你开始想到的多是这样使用相对路径:

// customRenderer.js
const imageConfig = {
    'url': '../../assets/rules.png',
    // 'url': 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png',
    'attr': { x: 0, y: 0, width: 48, height: 48 }
}

const { attr, url } = imageConfig;
const customIcon = svgCreate('image', {
    ...attr,
    href: url
})
复制代码

可是保存打开页面以后发现不尽人意...

在这里你须要使用CommonJS的引入方式才能够, 将它转换为base64Data URL:

// customRenderer.js
const imageConfig = {
    'url': require('../../assets/rules.png'),
    // 'url': 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png',
    'attr': { x: 0, y: 0, width: 48, height: 48 }
}

const { attr, url } = imageConfig;
const customIcon = svgCreate('image', {
    ...attr,
    href: url
})
复制代码

保存打开页面发现是能够的.

可是在这里我不推荐你使用相对路径的方式, 由于配置文件的位置可能随时会变, 一变的话相对路径也得更这边, 因此若是你是使用以webpack打包工具为基础的脚手架的话, 我建议你配置一个alias(别名), 那样也能方便你开发.

配置alias的方式很简单, 若是你和我同样是用vue开发项目的话, 请检查一下你的根目录有没有一个叫vue.config.js的文件, 若是没有的话, 建立一个, 并在其中写上:

// customRenderer.js
const path = require('path')

const resolve = dir => path.join(__dirname, dir)

module.exports = {
    chainWebpack: config => {
        config.resolve.alias
            .set('@', resolve('src'))
            .set('@assets', resolve('src/assets'))
    }
}
复制代码

(其它框架请自行度娘...)

是否是看着也很简单, 和它的英文同样, 其实也就是给某个文件夹配置一个别名.

好比我这里就是给srcsrc/assets配置了别名.

这样你在代码里写@/views/xxx.vue就当于写src/views/xxx.vue.

如今让咱们来修改一下前面的路径:

// customRenderer.js
const imageConfig = {
    'url': require('@assets/rules.png'),
    // 'url': require('../../assets/rules.png'),
    // 'url': 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png',
    'attr': { x: 0, y: 0, width: 48, height: 48 }
}

const { attr, url } = imageConfig;
const customIcon = svgCreate('image', {
    ...attr,
    href: url
})
复制代码

如今不管你如何移动你的customRenderer.js文件, 图片的路径都不会错了.

案例GitHub地址: bpmn-vue-custom

(该问题解决方案来自简书网友 梦想仍是要有的_bfc7)

3. 自定义palette中如何使用它自己的图标样式

咱们以前的自定义palette一直都是使用咱们本身找的一些图片图标...

而若是你某一个元素的样式就想要它官方提供的怎么办 🤔️?

例如我要实现这样的效果:

前两个元素是我自定义的, 最后一个网关用官方提供的原始样式, 以下图:

想要作到这一点其实很简单, 还记得咱们自定义palette的时候是依赖着一个className属性的吗?

你只须要将这个className设置成它官方提供的就能够了.

那有人就要问了,这个官方原始的className我该到哪找呢 😂?

审查元素, 找到对应的类名, 好比这里是bpmn-icon-gateway-none

而后在将customPalette中的网关设置成这个className:

PaletteProvider.prototype.getPaletteEntries = function(element) {
    ...
    return {
        ...
        'create.exclusive-gateway': {
            group: 'gateway',
            className: 'bpmn-icon-gateway-none', // 重点是这个
            title: '建立一个网关',
            action: {
                dragstart: createGateway(),
                click: createGateway()
            }
        }
    }
}
复制代码

如今左侧的工具栏就已经能够将原始的网关样式显示出来了.

可是有一个问题了, 那就是此时你想要用你定义好的这个网关在右边画图, 也就是进入renderer阶段, 若是你是彻底自定义renderer的话, 控制台可能就会报错了...

先让咱们来回顾一下customRenderer.js是怎么写的:

export default function CustomRenderer(eventBus, styles, textRenderer) {
    this.drawCustomElements = function(parentNode, element) {
        if (customElements.includes(type)) { // or customConfig[type]
            // 这里是自定义的元素
        }
    }
}

CustomRenderer.prototype.drawShape = function(p, element) {
    return this.drawCustomElements(p, element)
}

复制代码

若是你和我同样是将是不是自定义的元素这个判断放到drawCustomElements这个方法里写的话你可能就会报错了...由于它会告诉你找不到这个类型的渲染方式.

解决办法是这层判断放到CustomRenderer.prototype.drawShape里:

export default function CustomRenderer(eventBus, styles, textRenderer) {
    this.drawCustomElements = function(parentNode, element) {
        // 这里是自定义的元素
    }
}

CustomRenderer.prototype.drawShape = function(p, element) {
    if (customElements.includes(element.type)) { // 放到这里判断
        return this.drawCustomElements(p, element)
    }
}
复制代码

这样修改以后, 在执行drawShape方法的时候, 它就会判断是不是自定义元素, 若是是自定义元素的话才有返回值, 不然就没有返回值.

没有返回值时它就会根据原始的样式进行渲染了.

这是由于咱们在设计自定义modeler的时候将原始的modeler也引用进来了:

关于上述案例可查看: bpmn-vue-custom 中的自定义modeler那一个tab项.

contextPad

1. contextPad中的内容根据元素类型不一样显示不一样

不一样类型的节点出现的contextPad的内容多是不一样的. 好比:

StartEvent会出现edit、delete、Task、BusinessRuleTask、ExclusiveGateway等等;

EndEvent只能出现edit、delete;SequenceFlow只能出现edit、delete.

也就是说咱们须要根据节点类型来返回不一样的contextPad.

这个其实我在《全网最详bpmn.js教材-封装组件篇》 这里面已经提到过该如何处理了, 具体能够看那篇文章:

文件

1. 如何加载本地bpmn或者xml文件

http篇那一章节, 我向你们演示的是经过一个远程的文件连接(多是后台传递过来的), 而后经过axios解析获取的文件, 从而获得xml的字符串再调用importXML方法显示出图形.

那么如何加载一个本地的bpmn文件或者xml文件呢.

方案一: 使用raw-loader

我首先想到的是经过xml-loader解析这两类文件, 可是不知道能不能成, 因而试了试.

(项目案例基于: bpmn-vue-custom)

首先在项目中安装xml-loader:

$ npm i --save-dev xml-loader
复制代码

而后配置一下vue.config.js这个文件(这个文件在上面👆palette和renderer中的图片如何用本地图片已经提到过了, 没有的话就在根目录建立一个)

vue.config.js:

const path = require('path')

const resolve = dir => path.join(__dirname, dir)

module.exports = {
    chainWebpack: config => {
        config.resolve.alias
            .set('@', resolve('src'))
            .set('@assets', resolve('src/assets'))
            .end()
        config.module // 主要是看这部分
            .rule('xml-loader')
            .test(/.(bpmn|xml)$/)
            .use('xml-loader')
            .loader('xml-loader')
            .end()
    }
}
复制代码

这里的意思就是以bpmn或者xml为后缀的文件会被xml-loader处理.

如今让咱们在custom-renderer.vue这个页面中来试试:

<script> const bpmnXml = require('../mock/diagram.bpmn') console.log(bpmnXml) </script>
复制代码

打印出来的bpmnXml倒是一个对象, 而不是字符串:

并且使用importXML想要转换这个对象显然是不行的.

这可怎么办呢...

等等, 既然importXML解析只须要一个字符串的话, 让我想到了前几天刚学到的raw-loader, 它能够获取txt中的文本内容, 那是否是也能获取bpmn和xml呢 🤔️?

说干就干, 继续安装raw-loader:

$ npm i --save-dev raw-loader
复制代码

而后修改vue.config.js:

const path = require('path')

const resolve = dir => path.join(__dirname, dir)

module.exports = {
    chainWebpack: config => {
        config.resolve.alias
            .set('@', resolve('src'))
            .set('@assets', resolve('src/assets'))
            .end()
        config.module // 将xml-loader替换成raw-loader
            .rule('raw-loader')
            .test(/.(bpmn|xml)$/)
            .use('raw-loader')
            .loader('raw-loader')
            .end()
    }
}
复制代码

修改完以后记得重启项目...

而后让咱们来看看效果:

<script> const bpmnXml = require('../mock/diagram.bpmn') console.log(bpmnXml) console.log(typeof bpmnXml) // object console.log(bpmnXml.default) </script>
复制代码

此时打印出来的虽然也是个对象, 可是里面有个default属性, 它存储的就是xml字符串

因此咱们取default属性就能够了:

this.bpmnModeler.importXML(bpmnXml.default, err => {
    if (err) {
        
    } else {
        // 这里是成功以后的回调, 能够在这里作一系列事情
        this.success()
    }
})
复制代码

不知道是否是版本的缘由, 有些经过raw-loader转换的bpmn文件就直接是字符串, 而不是这个对象, 你们在使用的时候注意一下.

注意⚠️:

关于上面vue.config.jsvue-cli3webpack的配置, 若是你的项目的构建方式是使用原始webpack的话, 它就至关于webpack.config.js中的:

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /.(bpmn|xml)$/,
                use: 'raw-loader'
            }
        ]
    }
}
复制代码

其它打包方式我这里就不说了.

方案二: 使用new FileReader()

这个方案是群里的群友火莲提出来的, 他已经实现了, 我就没去试了, 不过应该是能够的.

var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(oFREvent){
    var xmlDoc = oFREvent.target.result;
    openDiagram(xmlDoc);
}
复制代码

属性

1. 每一个元素的id是否可以修改

其实每一个元素的id也是一个属性而已, 可是它并不会随着元素类型的改变而改变, 也就是说正常状况下它是不会变更的.

不过既然它是一个属性, 那么咱们就能经过modeling.updateProperties()修改它:

const properties = { id: 'id0001' } 
const { modeler, element } = this
const modeling = modeler.get('modeling')
modeling.updateProperties(element, properties)
复制代码

其它

1. 如何建立线节点

建立线节点在《全网最详bpmn.js教材-封装组件篇》 这里面也提到过该如何处理, 具体能够看那篇文章.

2. 右下角的绿色logo可否隐去

关于右下角logo可否隐去这个问题, 群里产生了激烈的讨论, 由于你们都怕吃官司侵权...

用官网的话来讲就是不能:

不过群友zaw也提供了一种解决方案😂:

找到那个类名, 而后样式设置 display : none.

我认为你能不隐就不要隐去了吧, 虽然人家这东西是开源的, 可是也说了不要去掉, 就听从做者的意愿吧(就像我在这里求你们一键三连同样: 点赞, 收藏, Star 呀 哈哈哈...)

后语

所有教材目录: 《全网最详bpmn.js教材》

GitHub教材地址: bpmn-chinese-document 求Star 🌟 求Fork 📓...

疫情四溢, 足不出户, 霖呆呆从大年初二到今天就只出过一次门 😂...

不知道大家那边状况怎么样, 反正我家后面300米处的那户人家夫妻俩已经被感染隔离起来了...

因此咱们小镇也被全面封锁了, 还不知道啥时候能返深...

不过在家呆着挺好的, 可贵有和家人相处的机会, 要好好珍惜呀, 并且能趁着假期恶补一下本身薄弱的知识点就很好, 哈哈😄.

喜欢霖呆呆的小伙还但愿能够关注霖呆呆的公众号 LinDaiDai 或者扫一扫下面的二维码👇👇👇.

我会不定时的更新一些前端方面的知识内容以及本身的原创文章🎉

LinDaiDai公众号二维码.png
LinDaiDai公众号二维码.png

你的鼓励就是我持续创做的主要动力 😊.

相关推荐:

《前面系列-this/apply/call问点(假期一块儿来学习吧, 武汉加油!!!)》

《JavaScript进阶-执行上下文(理解执行上下文一篇就够了)》

《霖呆呆你来讲说浏览器缓存吧》

《怎样让后台小哥哥快速对接你的前端页面》

相关文章
相关标签/搜索