предисловие
В: Что такое bpmn.js?
"
bpmn.jsЭто набор инструментов рендеринга BPMN2.0 и средство веб-моделирования, которое позволяет выполнять функцию рисования блок-схем во внешнем интерфейсе.
В: Почему я пишу эту серию учебников? 🤔️
"
Из-за потребностей бизнеса компании его необходимо использовать в проектеbpmn.js, Однако из-заbpmn.jsРазработчики игры зарубежные друзья,поэтому отечественных методических материалов мало и нет подробных документов.Поэтому много способов использования и много ям приходится искать самому.Поразмыслив,решил написать серию учебные материалы об этом. чтобы помочь большеbpmn.jsПользователи или разработчики, которые ищут хороший способ рисовать блок-схемы, в то же время для них это еще и своего рода консолидация.
Поскольку это серия статей, она может часто обновляться.Пожалуйста, простите меня, если вы случайно смахнули его, и это не то, что вам нужно😊.
Не просите похвалы👍 не просите сердца❤️ Я просто надеюсь, что смогу вам немного помочь.
Статьи о пользовательском рендерере
Следуя предыдущей главе, мы уже знаем, как настроить панель инструментов (Палитру) слева, а те, кто не понимает, могут перемещаться:"Самый подробный учебный материал по bpmn.js во всей сети - пользовательская палитра".
Но в то же время мы также знаем, что только меняетсяPaletteнедостаточно, потому что нарисованная фигура еще «голая»:
В этой главе давайте рассмотрим, как настроить графику на холсте, то есть добиться настройкиRendererфункция.
Читая, вы узнаете:
- Изменить на основе средства визуализации по умолчанию
- Полностью настраиваемый рендерер
- Тег label настраивается под элементом
Изменить на основе средства визуализации по умолчанию
и обычайPaletteТочно так же давайте рассмотрим простейшую модификацию исходного элемента.
Предварительная подготовка
продолжимLinDaiDai/bpmn-vue-customразработка кейс-проекта.
существуетcomponentsсоздать новую папкуcustom-renderer.vueфайл и настройте маршрут «пользовательский рендерер».
существуетcomponents/customсоздать новую папкуCustomRenderer.jsфайл для настройкиrenderer.
существуетcomponentsсоздать новую папкуutilsОдновременно создайте новую папкуutil.jsфайл, используемый для размещения некоторых общедоступных методов и конфигурации.
записыватьCustomRenderer.vueкод
из-заbpmn.jsИзмените существующие элементы, поэтому сначала мы можем сначалаBaseRendererЭтот класс импортируется, а затем пусть наш пользовательскийrendererунаследовать это:
import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer' // 引入默认的renderer
const HIGH_PRIORITY = 1500 // 最高优先级
export default class CustomRenderer extends BaseRenderer { // 继承BaseRenderer
constructor(eventBus, bpmnRenderer) {
super(eventBus, HIGH_PRIORITY)
this.bpmnRenderer = bpmnRenderer
}
canRender(element) {
// ignore labels
return !element.labelTarget
}
drawShape(parentNode, element) { // 核心函数就是绘制shape
const shape = this.bpmnRenderer.drawShape(parentNode, element)
return shape
}
getShapePath(shape) {
return this.bpmnRenderer.getShapePath(shape)
}
}
CustomRenderer.$inject = ['eventBus', 'bpmnRenderer']
Приведенный выше код 👆 очень прост, я думаю, его сможет понять каждый.
Примечание. Здесь есть небольшая яма, на которую следует обратить внимание, т.HIGH_PRIORITYЕго нельзя удалить, иначе вы обнаружите, что он не будет выполнять следующиеdrawShpeфункция
Когда вы придете сюда, могут быть некоторые друзья, которые захотят спросить. Я чувствую, что вы сделали так много, и это бесполезно. Я до сих пор не видел настройки.rendererЭффект 😅!
Да, недостаточно просто выполнить вышеперечисленные действия, главное, как написатьdrawShapeСюда.
записыватьdrawShapeкод
Сначала мы можем создатьutils/util.jsНапишите этот код под файлом:
// util.js
const customElements = ['bpmn:Task']
export { customElements }
То есть создатьcustomElementsМассив , а затем экспортированный, почему в массиве только один элементbpmn:Task?🤔️
Это потому, что в предыдущем случае я создалlindaidai-taskТипbpmn:TaskТип.
Таким образом, функция этого массива состоит в том, чтобы указать, какие типы нам нужно настроить, чтобы мы могли отличить их от элементов, которые не нужно настраивать при рендеринге.
Вы даже можете сделать некоторую настройку:
const customElements = ['bpmn:Task'] // 自定义元素的类型
const customConfig = { // 自定义元素的配置(后面会用到)
'bpmn:Task': {
'url': 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png',
'attr': { x: 0, y: 0, width: 48, height: 48 }
}
}
export { customElements, customConfig }
впусти насCustomRenderer.jsиспользуйте и напишите его в:
import { customElements, customConfig } from '../utils/util'
...
drawShape(parentNode, element) {
const type = element.type // 获取到类型
if (customElements.includes(type)) { // or customConfig[type]
const { url, attr } = customConfig[type]
const customIcon = svgCreate('image', { // 在这里创建了一个image
...attr,
href: url
})
element['width'] = attr.width // 这里我是取了巧, 直接修改了元素的宽高
element['height'] = attr.height
svgAppend(parentNode, customIcon)
return customIcon
}
const shape = this.bpmnRenderer.drawShape(parentNode, element)
return shape
}
...
Как видите, способ заставить страницу отображать нужный эффект — использоватьsvgCreateметод созданияimageи добавлен к родительскому узлу.
экспортировать и использоватьCustomRenderer
такая же настройкаrendererНеобходимо экспортировать, чтобы использовать, модифицироватьcustom/index.jsдокумент:
import CustomPalette from './CustomPalette'
import CustomRenderer from './CustomRenderer'
export default {
__init__: ['customPalette', 'customRenderer'],
customPalette: ['type', CustomPalette],
customRenderer: ['type', CustomRenderer]
}
Уведомление:__init__именование атрибутов вcustomRendererВсе они имеют фиксированное написание и не могут быть изменены, иначе не будет никакого эффекта.
если ты читал это раньшеcustom-palette.vueЕсли это так, вы знаете, что можете применить его прямо на странице:
<!--custom-renderer.vue-->
<script>
...
import customModule from './custom'
...
this.bpmnModeler = new BpmnModeler({
...
additionalModules: [
// 左边工具栏以及节点
propertiesProviderModule,
// 自定义的节点
customModule
]
})
Примечание: В случае с проектом для удобства демонстрации вcustom-paletteПредставлен вImportJS/onlyRenderer.js, а рассмотренный выше случай основан на введенииcustom/index.jsДля того, чтобы объяснить, это надо самому понять, как различать.
На данный момент вы можете увидеть эффект при открытии страницы, типbpmn:TaskУзел отображается как пользовательский «золотой строительный блок» 😝:
Полностью настраиваемый рендерер
полностью настраиваемыйRendererозначает, что первоначальное использованиеnew BpmnModelerСпособ создания холста изменился на использованиеnew CustomModelerсоздавать.
Эта часть находится в"Самый подробный учебный материал по bpmn.js во всей сети - пользовательская палитра"Это объясняется очень подробно, поэтому я не буду вдаваться в подробности.
Также вcustomModeler/customсоздать папку вcustomRender.jsфайл, а затем напишите следующий код:
/* eslint-disable no-unused-vars */
import inherits from 'inherits'
import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer'
import {
append as svgAppend,
create as svgCreate
} from 'tiny-svg'
import { customElements, customConfig } from '../../utils/util'
/**
* A renderer that knows how to render custom elements.
*/
export default function CustomRenderer(eventBus, styles) {
BaseRenderer.call(this, eventBus, 2000)
var computeStyle = styles.computeStyle
this.drawCustomElements = function(parentNode, element) {
console.log(element)
const type = element.type // 获取到类型
if (customElements.includes(type)) { // or customConfig[type]
const { url, attr } = customConfig[type]
const customIcon = svgCreate('image', {
...attr,
href: url
})
element['width'] = attr.width // 这里我是取了巧, 直接修改了元素的宽高
element['height'] = attr.height
svgAppend(parentNode, customIcon)
return customIcon
}
const shape = this.bpmnRenderer.drawShape(parentNode, element)
return shape
}
}
inherits(CustomRenderer, BaseRenderer)
CustomRenderer.$inject = ['eventBus', 'styles']
CustomRenderer.prototype.canRender = function(element) {
// ignore labels
return !element.labelTarget;
}
CustomRenderer.prototype.drawShape = function(p, element) {
return this.drawCustomElements(p, element)
}
CustomRenderer.prototype.getShapePath = function(shape) {
console.log(shape)
}
Изменять непосредственно в цепочке прототиповdrawShapeметода достаточно.
тогда не забудьтеcustomModeler/custom/index.jsчтобы экспортировать его.
Тег label настраивается под элементом
Из-за вопроса, поднятого небольшим партнером в области комментариев: какlabelПользовательский ярлык ниже элемента?
Итак, Лин был ошеломлен, и я вернулся и потратил некоторое время на его изучение.labelЭтикетка.
первыйlabelЭтикетка на самом делеxmlИмя на каждой этикетке вnameсвойства, как показано ниже:
начальный узел иlindaidai-taskв целомnameсвойства, но вbpmn:StartEventможет этоlabelотображается, потому что естьbpmndi:BPMNLabelТег.
В результате график выглядит так:
Итак, как мы поместимlabelпокажи это?
Сначала положимShapeРаспечатайте и посмотрите:
можно найти вbusinessObjectесть одинnameАтрибуты...
В этом случае мы, безусловно, можемdrawShapeполучить этоnameсвойства, которые можно использовать позжеsvgCreateМетод добавляет метку текстового типа к родительскому узлу.
// CustomRenderer.js
import { hasLabelElements } from '../../utils/util'
drawShape(parentNode, element) {
const type = element.type // 获取到类型
if (customElements.includes(type)) { // or customConfig[type]
const { url, attr } = customConfig[type]
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) {
const text = svgCreate('text', {
x: attr.x,
y: attr.y + attr.height + 20, // y取的是父元素的y+height+20
"font-size": "14",
"fill": "#000"
})
text.innerHTML = element.businessObject.name
svgAppend(parentNode, text)
console.log(text)
}
return customIcon
}
const shape = this.bpmnRenderer.drawShape(parentNode, element)
return shape
}
потому что некоторые элементы имеютlabelатрибуты, такие какbpmn:StartEvent, так что нет необходимости повторно рендерить, поэтому я используюutil.jsдобавил одинhasLabelElementsмножество:
// utils/util.js
const hasLabelElements = ['bpmn:StartEvent', 'bpmn:EndEvent'] // 一开始就有label标签的元素类型
Я хотел пройти мимоelement.labels.length<=0чтобы отфильтровать началоlabelЭлемент метки, но обнаружил, что он недоступен на этапе рендерингаlabels, поэтому длина всегда будет0, просто определитеhasLabelElementsПриходите судить 😓...
Эффект от открытия страницы следующий:
Похоже, это сработало! Молодец! 😄
но когда я дважды щелкаю, хочу перейти к редактированиюlabelПри записи появляется такой эффект:
Он создал новое поле ввода прямо поверх моего исходного графика...
Эх😅... На самом деле, я не придумал никакого хорошего способа решить эту проблему. Здесь я предлагаю решение, которое кажется возможным:При двойном щелчке по элементуtextбыть удалены или егоinnerHTMLУстановить как''.
Конечно, если вы чувствуете, что можете смотреть это в таком виде, ничего страшного, если мы не возимся с этим, ведь здесь вы редактируете контент.labelОн также будет меняться синхронно.
Если нет, вы можете изменить его глобальноdjs-direct-editing-parentДля стиля этого класса также можно прикрыть текст ниже... Конечно, я чувствую, что это не очень хороший способ.
существуетapp.cssнаписать в:
.djs-direct-editing-parent {
top: 130px!important;
width: 60px!important;
}
Суммировать
Вышеупомянутый подход в основном используется дляsvgCreateсоздаватьtextэлемент добавлен вparentNodeв, на самом делеbpmn.jsиспользовал многоting-svgЯ никогда раньше не сталкивался с такими вещами, а потом я также узнал, ища информацию.svgCreateиспользование...
Волна научно-популярного это хорошо, ха-ха 😄:Основы SVG
послесловие
В приведенных выше 👆 случаях используется один и тот же проект🦐
Git-адрес кейса проекта:LinDaiDai/bpmn-vue-customЕсли вам это нравится, пожалуйста, дайтеStar🌟 Да, спасибо😊
Полный каталог серии можно найти здесь:"Самый подробный учебник по bpmn.js во всей сети"
Рекомендации по сериалу:
"Самый подробный учебник по bpmn.js во всей сети - основы"
"Самый подробный учебный материал по bpmn.js во всей сети - http запрос"
"Самый подробный учебный материал по bpmn.js во всей сети - мероприятия"
"Самый подробный учебник по bpmn.js во всей сети - contextPad"
"Самый подробный учебник по bpmn.js во всей сети - редактирование и удаление узлов"