предисловие
В: Что такое bpmn.js?
"
bpmn.jsЭто набор инструментов рендеринга BPMN2.0 и средство веб-моделирования, которое позволяет выполнять функцию рисования блок-схем во внешнем интерфейсе.
В: Почему я пишу эту серию учебников? 🤔️
"
Из-за потребностей бизнеса компании его необходимо использовать в проектеbpmn.js, Однако из-заbpmn.jsРазработчики игры зарубежные друзья,поэтому отечественных методических материалов мало и нет подробных документов.Поэтому много способов использования и много ям приходится искать самому.Поразмыслив,решил написать серию учебные материалы об этом. чтобы помочь большеbpmn.jsПользователи или разработчики, которые ищут хороший способ рисовать блок-схемы, в то же время для них это еще и своего рода консолидация.
Поскольку это серия статей, она может часто обновляться.Пожалуйста, простите меня, если вы случайно смахнули его, и это не то, что вам нужно😊.
Адрес github всех учебников:"bpmn-китайский-документ"
Панель свойств (часть 2)
В предыдущей главе мы в основном представили, какproperties-panelРазверните на основе , но многие друзья скажут, что мне слишком не нравится стиль оригинальной панели свойств 😅... Я зрелый фронтенд, у меня должны быть свои идеи...
Хорошо... Я уважаю тебя... В этой главе Линь Дуань научит тебя, как украсить нашуproperties-panel😊.
Прочитав эту главу, вы узнаете:
- Изменить стиль панели свойств по умолчанию
- настроить
properties-panel - Изменить имя узла
labelАтрибуты - Изменить цвет узла
colorАтрибуты - Исправлять
eventТип узла - Исправлять
Taskтип узла - инициализация
properties-panelи установите некоторые значения по умолчанию
Изменить стиль панели свойств по умолчанию
Давайте сначала посмотрим, какого эффекта мы можем добиться, изменив стиль панели свойств по умолчанию🤔️!
Как показано выше 👆, вы можете настроить различные цвета темы для панели свойств, чтобы украсить ее первоначальный вид.
На самом деле, если вы хотите изменить стиль панели свойств по умолчанию, это очень просто, просто откройте консоль (Window: F12, Mac: option + command + i) и найдите значение каждого элемента, проверив элементы.class, а затем перезапишите его исходные свойства в коде.
Помните, когда мы работали над проектомmain.jsпроцитировано вproperties-panelстиль?
// main.js
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右边工具栏样式
Теперь давайте создадимstylesпапка, при созданииbpmn-properties-theme-red.cssфайл, который будет использоваться для записи стиля панели свойств, которую нам нужно настроить и изменить.
позжеmain.jsпроцитируйте его, желательно после оригинального стиля:
// main.js
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右边工具栏样式
import './styles/bpmn-properties-theme-red.css' // 绯红主题
Например, теперь я хочу изменить цвет шрифта заголовка панели свойств:
Найдите этот класс, проверив элемент, затем вbpmn-properties-theme-red.cssизменить его в:
.bpp-properties-header>.label {
color: rgb(239, 112, 96);
font-size: 16px;
}
Сохраните и снова откройте страницу, чтобы увидеть эффект.
Конечно, я здесь только для того, чтобы продемонстрировать, как изменить стиль по умолчанию, поэтому я просто использую самый простойcssдля демонстрации. На самом деле здесь много места для расширения, вы можете использоватьlessилиsassДля написания вы также можете реализовать такие функции, как переключение тем и т. д. Надеюсь, это вдохновит вас 😊...
Если хочешь воровать и быть ленивым... возьми стиль Лина...
Вышеприведенный 👆 случайgithubадрес:
настроитьproperties-panel
Иногда вас может не устраивать официальнаяproperties-panel, но хотите настроить панель свойств, что также возможно.
Например, я хочу отображать разные конфигурации атрибутов справа в соответствии с разными типами узлов, и после редактирования их можно синхронно обновлять доxmlначальство.
На самом деле принцип реализации в предыдущем"Самый подробный учебный материал по bpmn.js во всей сети - свойства"Это также упоминалось в , в основном с использованиемupdateProperties()Этот метод изменяет атрибуты узлов элементов.
Теперь давайте посмотрим, как инкапсулировать такую настраиваемую панель свойств 😊.
Предварительная подготовка
Поскольку кода для настройки панели свойств может быть много, и он может включать множество сложных бизнес-компонентов, я рекомендую вам изменить его с引入bpmn.jsМесто извлекается, то есть инкапсулируется в общий настраиваемый компонент панели свойств.
компонентprops
Теперь, когда было принято решение извлечь его в компонент, компонентpropsНа что он должен быть установлен?
(propsТо есть значение передается от родительского компонента дочернему компоненту, где родительским элементом является введение.bpmn.jsгде дочерний элемент является пользовательским компонентом панели свойств)
Давайте сначала разберемся с нашими потребностями, нам нужно щелкнуть по разным элементам, чтобы представить разные конфигурации, затем мы можем поместить одинelementтак какpropsпройти в.
Но позже в процессе написания я обнаружил, что есть много привязок событий, которые включаютmodelerДа, если эти связывающие события все сделаны в родительском компонентах, не будет ли это против нашего желания отделить отдельные компоненты? 🤔️?
Так вот, я был целымmodelerтак какpropsписать. Таким образом, будь то дляmodelerПривяжите событие или подаритеelementОбязательные события все хорошо сделаны.
Хорошо... подумайprops, разрешите намcomponentsсоздать папкуcustom-properties-panelпапку и создайте папку с именемPropertiesView.vueфайл для записи нашего пользовательского компонента панели свойств.
Мы ожидаем, что этот компонент сможетhtmlиспользуется в:
<div class="containers" ref="content">
<div class="canvas" ref="canvas"></div>
<properties-view v-if="bpmnModeler" :modeler="bpmnModeler"></properties-view>
</div>
(bpmnModelerты используешьnew BpmnModelerсозданныйmodelerОбъект)
Напишите настраиваемый компонент панели свойств
1. Компонентная структура
Сначала настроим инфраструктуру этого компонента:
<!--PropertiesView.vue-->
<template>
<div class="custom-properties-panel"></div>
</template>
<script>
export default {
name: 'PropertiesView',
props: {
modeler: {
type: Object,
default: () => ({})
}
},
data () {
return {
selectedElements: [], // 当前选择的元素集合
element: null // 当前点击的元素
}
},
created () {
this.init()
},
methods: {
init () {}
}
}
</script>
<style scoped></style>
2. Компонентыhtmlкод
Позвольте мне сначала добавить кое-что к этому компоненту:
<template>
<div class="custom-properties-panel">
<div class="empty" v-if="selectedElements.length<=0">请选择一个元素</div>
<div class="empty" v-else-if="selectedElements.length>1">只能选择一个元素</div>
<div v-else>
<fieldset class="element-item">
<label>id</label>
<span>{{ element.id }}</span>
</fieldset>
<fieldset class="element-item">
<label>name</label>
<input :value="element.name" @change="(event) => changeField(event, 'name')" />
</fieldset>
<fieldset class="element-item">
<label>customProps</label>
<input :value="element.name" @change="(event) => changeField(event, 'customProps')" />
</fieldset>
</div>
</div>
</template>
Как и выше 👆, я добавил три атрибута,id, name, customProps, В то же время существуетselectedElementsсуждение.
Это потому, что когда мы манипулируем графикой, если вы используетеcommand + 左键(Так должно бытьCtrl?) Можно выбрать несколько узлов, и в это время необходимо принять решение.
3. Компонентыjsкод
Если вы прочтете много кода, написанного Лин Дуаном, вы обнаружите, что я предпочитаю ссылаться на некоторый код инициализации, а не на код, называемыйinit()функция, это личная привычка кодирования...
Здесь наша функция инициализации в основном делает следующие вещи:
- использовать
selection.changedпрослушать выбранный элемент; - использовать
element.changedСлушайте измененные элементы.
init () {
const { modeler } = this // 父组件传递进来的 modeler
modeler.on('selection.changed', e => {
this.selectedElements = e.newSelection // 数组, 可能有多个
this.element = e.newSelection[0] // 默认取第一个
})
modeler.on('element.changed', e => {
const { element } = e
const { element: currentElement } = this
if (!currentElement) {
return
}
// update panel, if currently selected element changed
if (element.id === currentElement.id) {
this.element = element
}
})
}
В качестве альтернативы мы можем написать общедоступный метод обновления свойств для обновления свойств элементов:
/**
* 更新元素属性
* @param { Object } 要更新的属性, 例如 { name: '', id: '' }
*/
updateProperties(properties) {
const { modeler, element } = this
const modeling = modeler.get('modeling')
modeling.updateProperties(element, properties)
}
Затем дайте батонку свойствinputили другие элементы управления, добавьте@changeСобытия при изменении содержимого в элементе управления синхронно обновляютсяelement.
/**
* 改变控件触发的事件
* @param { Object } input的Event
* @param { String } 要修改的属性的名称
*/
changeField (event, type) {
const value = event.target.value
let properties = {}
properties[type] = value
this.element[type] = value
this.updateProperties(properties) // 调用属性更新方法
}
4. Полный код компонента
Объедините весь код выше 👆:
<template>
<div class="custom-properties-panel">
<div class="empty" v-if="selectedElements.length<=0">请选择一个元素</div>
<div class="empty" v-else-if="selectedElements.length>1">只能选择一个元素</div>
<div v-else>
<fieldset class="element-item">
<label>id</label>
<span>{{ element.id }}</span>
</fieldset>
<fieldset class="element-item">
<label>name</label>
<input :value="element.name" @change="(event) => changeField(event, 'name')" />
</fieldset>
<fieldset class="element-item">
<label>customProps</label>
<input :value="element.name" @change="(event) => changeField(event, 'customProps')" />
</fieldset>
</div>
</div>
</template>
<script>
export default {
name: 'PropertiesView',
props: {
modeler: {
type: Object,
default: () => ({})
}
},
data() {
return {
selectedElements: [],
element: null
}
},
created() {
this.init()
},
methods: {
init() {
const { modeler } = this
modeler.on('selection.changed', e => {
this.selectedElements = e.newSelection
this.element = e.newSelection[0]
})
modeler.on('element.changed', e => {
const { element } = e
const { element: currentElement } = this
if (!currentElement) {
return
}
// update panel, if currently selected element changed
if (element.id === currentElement.id) {
this.element = element
}
})
},
/**
* 改变控件触发的事件
* @param { Object } input的Event
* @param { String } 要修改的属性的名称
*/
changeField(event, type) {
const value = event.target.value
let properties = {}
properties[type] = value
this.element[type] = value
this.updateProperties(properties)
},
updateName(name) {
const { modeler, element } = this
const modeling = modeler.get('modeling')
// modeling.updateLabel(element, name)
modeling.updateProperties(element, {
name
})
},
/**
* 更新元素属性
* @param { Object } 要更新的属性, 例如 { name: '' }
*/
updateProperties(properties) {
const { modeler, element } = this
const modeling = modeler.get('modeling')
modeling.updateProperties(element, properties)
}
}
}
</script>
<style scoped>
/** 更多代码在git上有, git链接见底部后语 **/
.custom-properties-panel {
position: absolute;
right: 0;
top: 0;
width: 300px;
background-color: #fff9f9;
border-color: rgba(0, 0, 0, 0.09);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
padding: 20px;
}
</style>
Изменить имя узлаlabelАтрибуты
В приведенном выше примере мы продемонстрировали, как изменить свойства элемента, если вы хотите изменитьlabel, один из способов - изменить, как указано выше 👆nameэто свойство или использоватьmodeling.updateLabelЭто обновление метода также одинаково:
updateName(name) {
const { modeler, element } = this
const modeling = modeler.get('modeling')
modeling.updateLabel(element, name)
// 等同于 modeling.updateProperties(element, { name })
},
Изменить цвет узлаcolorАтрибуты
Как разрешить пользователю вручную изменять цвет узла?
годный к употреблениюmodeling.setColorСюда.
Например, я добавляю в код строку свойств:
<fieldset class="element-item">
<label>节点颜色</label>
<input type="color" :value="element.color" @change="(event) => changeField(event, 'color')" />
</fieldset>
Затем преобразуйте следующееchangeFieldметод:
/**
* 改变控件触发的事件
* @param { Object } input的Event
* @param { String } 要修改的属性的名称
*/
changeField(event, type) {
const value = event.target.value
let properties = {}
properties[type] = value
if (type === 'color') { // 若是color属性
this.onChangeColor(value)
}
this.element[type] = value
this.updateProperties(properties)
},
onChangeColor(color) {
const { modeler, element } = this
const modeling = this.modeler.get('modeling')
modeling.setColor(element, {
fill: color,
stroke: null
})
},
setColorЭтот метод принимает два свойства:
-
fill: цвет заливки узла -
stroke: цвет границы узла и самого узлаlabelцвет
То, что я продемонстрировал выше, — это изменение цвета заливки узла, то естьfill, конечно, вы также можете изменитьstroke, эффект следующий:
Интересно, если поставитьfillа такжеstrokeнастроены наcolor:
modeling.setColor(element, {
fill: color,
stroke: color
})
ТакlabelЭтикетка больше не видна... это потому, чтоstrokeтакже изменитсяlabelцвет, сделать его иfillТакой же.
Но обычно вы не будете边框和填充内容Установите его на один цвет... нет необходимости...
Если вы действительно хотите решить эту проблему, есть ненадежный подход, то есть в глобальном масштабе.cssв, будетlabelСтиль принудительно изменен:
.djs-label {
fill: #000!important;
}
ИсправлятьeventТип узла
Иногда нам также может понадобиться изменить тип этого узла на пользовательской панели свойств, например, в начальном узле щелкнитеcontextPadМаленький ключ на:
Для достижения этой функции нам нужно использоватьbpmnReplace.replaceElementСюда.
Сначала посмотримeventгде находится это свойство.
Как показано ниже: я изменил тип начального узла и изменил его наMessageEventDefinition
это соответствуетelement.businessObject.eventDefinitionsв этом массиве, еслиStartEventа такжеEndEvent, то массивundefinded.
Посмотрим, как реализована эта функция 😄.
первый вhtmlплюс修改event节点类型раскрывающийся список:
<!--PropertiesView.vue-->
<template>
<fieldset class="element-item" v-if="isEvent">
<label>修改event节点类型</label>
<select @change="changeEventType" :value="eventType">
<option
v-for="option in eventTypes"
:key="option.value"
:value="option.value"
>{{ option.label }}</option>
</select>
</fieldset>
</template>
<script>
export default {
data () {
return {
eventTypes: [
{ label: '默认', value: '' },
{ label: 'MessageEventDefinition', value: 'bpmn:MessageEventDefinition' },
{ label: 'TimerEventDefinition', value: 'bpmn:TimerEventDefinition' },
{ label: 'ConditionalEventDefinition', value: 'bpmn:ConditionalEventDefinition' }
],
eventType: ''
}
},
methods: {
verifyIsEvent (type) { // 判断类型是不是event
return type.includes('Event')
},
changeEventType (event) {}
},
computed: {
isEvent() { // 判断当前点击的element类型是不是event
const { element } = this
return this.verifyIsEvent(element.type)
}
}
}
</script>
Что ж, после выполнения базового кода выше 👆 основная логика заключается в изменении значения раскрывающегося списка:
changeEventType(event) { // 改变下拉框
const { modeler, element } = this
const value = event.target.value
const bpmnReplace = modeler.get('bpmnReplace')
this.eventType = value
bpmnReplace.replaceElement(element, {
type: element.businessObject.$type,
eventDefinitionType: value
})
},
Теперь измените значение раскрывающегося списка, вы можете изменитьeventDefinitionTypeЗначение , но все еще есть проблема, то есть, когда вы нажимаете на другие узлы, а затем снова нажимаете обратно на начальный узел, значение по умолчанию в раскрывающемся списке неверно, то есть мы также нужно получить сам стартовый узел.eventDefinitionTypeстоимость.
В это время мы можемselection.changedВыполните такую инициализацию в событии прослушивателяproperties-panelпредмет.
init () {
modeler.on('selection.changed', e => {
this.selectedElements = e.newSelection
this.element = e.newSelection[0]
console.log(this.element)
this.setDefaultProperties() // 设置一些默认的值
})
}
setDefaultProperties() {
const { element } = this
if (element) {
const { type, businessObject } = element
if (this.verifyIsEvent(type)) { // 若是event类型
// 获取默认的 eventDefinitionType
this.eventType = businessObject.eventDefinitions ? businessObject.eventDefinitions[0]['$type'] : ''
}
}
}
ИсправлятьTaskтип узла
eventМы уже знаем, как изменить тип узла, поэтому дляTaskА как насчет типов узлов 🤔️?
На самом деле это почти то же самое.
Так же давайтеhtmlплюс дляTaskРаскрывающийся список атрибутов типа:
<!--PropertiesView.vue-->
<template>
<fieldset class="element-item" v-if="isTask">
<label>修改Task节点类型</label>
<select @change="changeTaskType" :value="taskType">
<option
v-for="option in taskTypes"
:key="option.value"
:value="option.value"
>{{ option.label }}</option>
</select>
</fieldset>
</template>
<script>
export default {
data () {
return {
taskTypes: [
{ label: 'Task', value: 'bpmn:Task' },
{ label: 'ServiceTask', value: 'bpmn:ServiceTask' },
{ label: 'SendTask', value: 'bpmn:SendTask' },
{ label: 'UserTask', value: 'bpmn:UserTask' }
],
taskType: ''
}
},
methods: {
verifyIsTask(type) {
return type.includes('Task')
},
changeTaskType (event) {}
},
computed: {
isTask() { // 判断当前点击的element类型是不是task
const { element } = this
return this.verifyIsTask(element.type)
}
}
}
</script>
затем изменениеTaskВ раскрывающемся списке:
changeTaskType(event) {
const { modeler, element } = this
const value = event.target.value // 当前下拉框选择的值
const bpmnReplace = modeler.get('bpmnReplace')
bpmnReplace.replaceElement(element, {
type: value // 直接修改type就可以了
})
}
инициализацияproperties-panelи установите некоторые значения по умолчанию
Когда мы устанавливаем собственную настраиваемую панель свойств, нам, возможно, придется делать разные суждения бизнес-логики в соответствии с разными типами узлов, иproperties-panelСделайте некоторые настройки по умолчанию, такие как выше 👆修改event类型, что мы можем сделать в это время 🤔️?
а также修改event类型то же самое, мы можемselection.changedЭта функция выполняется в событии слушателя.
init () {
modeler.on('selection.changed', e => {
this.selectedElements = e.newSelection
this.element = e.newSelection[0]
console.log(this.element)
this.setDefaultProperties() // 设置一些默认的值
})
}
setDefaultProperties() {
const { element } = this
if (element) {
// 这里可以拿到当前点击的节点的所有属性
const { type, businessObject } = element
// doSomeThing
}
}
На самом деле, он вводится с вышеуказанным 👆修改event类型Инициализация такая же, но я боюсь, что некоторые друзья пропустят ее напрямую修改event类型Эту часть я не видел, поэтому взял отдельно.
Например, мы хотимShapeполучен изlabelЗатем выполните синхронизацию с настраиваемой панелью свойств справа, чтобы сделать следующее:
существуетsetDefaultPropertiesгде мы можем пройтиthis.elementПолучите элемент, который в данный момент нажат, и распечатайте этот элемент, чтобы найти его,labelв реальностиbusinessObjectв объектеnameсвойство, поэтому нам просто нужно сделать это:
element['name'] = businessObject.name
Таким образом, вы не заботитесь об изменении изображения вышеlabelИли измените панель пользовательских свойствnameБудет обновляться синхронно, подробности можно посмотреть в коде на github.
replaceтип
Выше 👆 мы представили оEventа такжеTaskКак элементы типа преобразуют тип, а в деле продемонстрировано только несколько типов, так где же посмотреть все типы 🤔️?
ты сможешьbpmn.jsИсходный код находится здесь:
https://github.com/bpmn-io/bpmn-js/blob/develop/lib/features/replace/ReplaceOptions.js
Вы даже можете перейти непосредственно к коду и экспортировать в него то, что хотите:
import { START_EVENT } from 'bpmn-js/lib/features/replace/ReplaceOptions.js'
послесловие
Кодовый адрес вышеупомянутого 👆 корпуса учебника:LinDaiDai/bpmn-vue-properties-panel
Начиная с этой главы,properties-panelЭто введение, независимо от того, хотите ли вы использовать оригиналproperties-panelили использовать пользовательскийproperties-panelЯ верю, что вы уже освоили это 😄...
В последующем Линь Дуань может быть основан наbpmn.jsИсходный код, чтобы перечислить некоторые часто используемые свойства и методы, чтобы вы могли лучше понятьbpmn.js.
Скоро Китайский Новый год 🧨...
Закончив эту главу, Линь Дуань собирается начать паковать свой багаж дома 😄 …
Еще раз поздравляю всех с Новым годом~ 🔥 🎆
Наконец, если вы также правыbpmn.jsЕсли вам интересно, вы можете присоединиться к нашей группе обмена bpmn.js👇👇👇, вместе учиться и вместе добиваться успехов.
Подпишитесь на официальный аккаунт Лин Дайдао и выберите «группу bpmn.js» в меню «Другое» 😊.
Полный каталог серии можно найти здесь:"Самый подробный учебник по bpmn.js во всей сети"
Рекомендации по сериалу:
"Самый подробный учебник по bpmn.js во всей сети - основы"
"Самый подробный учебный материал по bpmn.js во всей сети - мероприятия"
"Самый подробный учебник по bpmn.js во всей сети - рендерер"
"Самый подробный учебник по bpmn.js во всей сети - contextPad"
"Самый подробный учебный материал по bpmn.js во всей сети - пользовательская палитра"
"Самый подробный учебник по bpmn.js во всей сети - редактирование и удаление узлов"
"Самый подробный учебный материал по bpmn.js во всей сети - компоненты пакета"
"Самый подробный учебный материал по bpmn.js во всей сети - свойства"
"Самый подробный учебный материал по bpmn.js во всей панели сетевых свойств (Часть 1)" ;