add:引入bpmn

This commit is contained in:
zhangzq
2024-03-19 11:04:40 +08:00
parent 8fdb1a45dd
commit c10b3e2054
32 changed files with 18419 additions and 37 deletions

View File

@@ -0,0 +1,67 @@
/**
* 存储流程设计相关参数
*/
export default class BpmData {
constructor () {
this.controls = [] // 设计器控件
this.init()
}
init () {
this.controls = [
{
action: 'create.start-event',
title: '开始'
},
{
action: 'create.intermediate-event',
title: '中间'
},
{
action: 'create.end-event',
title: '结束'
},
{
action: 'create.exclusive-gateway',
title: '网关'
},
{
action: 'create.task',
title: '任务'
},
{
action: 'create.user-task',
title: '用户任务'
},
{
action: 'create.user-sign-task',
title: '会签任务'
},
{
action: 'create.subprocess-expanded',
title: '子流程'
},
{
action: 'create.data-object',
title: '数据对象'
},
{
action: 'create.data-store',
title: '数据存储'
},{
action: 'create.participant-expanded',
title: '扩展流程'
},
{
action: 'create.group',
title: '分组'
}
]
}
// 获取控件配置信息
getControl (action) {
const result = this.controls.filter(item => item.action === action)
return result[0] || {}
}
}

View File

@@ -0,0 +1,226 @@
<template>
<div class="property-panel" ref="propertyPanel">
<el-form :inline="true" :model="form" label-width="100px" size="small">
<el-form-item label="节点ID">
<el-input v-model="form.id" disabled></el-input>
</el-form-item>
<el-form-item label="节点名称">
<el-input v-model="form.name" @input="nameChange"></el-input>
</el-form-item>
<el-form-item label="节点颜色">
<el-color-picker v-model="form.color" @active-change="colorChange"></el-color-picker>
</el-form-item>
<!-- 任务节点允许选择人员 -->
<el-form-item label="节点人员" v-if="userTask">
<el-select v-model="form.userType" placeholder="请选择" @change="typeChange">
<el-option value="assignee" label="指定人员"></el-option>
<el-option value="candidateUsers" label="候选人员"></el-option>
<el-option value="candidateGroups" label="角色/岗位"></el-option>
</el-select>
</el-form-item>
<!-- 指定人员 -->
<el-form-item label="指定人员" v-if="userTask && form.userType === 'assignee'">
<el-select
v-model="form.assignee"
placeholder="请选择"
key="1"
@change="(value) => addUser({assignee: value})"
>
<el-option
v-for="item in users"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<!-- 候选人员 -->
<el-form-item label="候选人员" v-else-if="userTask && form.userType === 'candidateUsers'">
<el-select
v-model="form.candidateUsers"
placeholder="请选择"
key="2"
multiple
@change="(value) => addUser({candidateUsers: value.join(',') || value})"
>
<el-option
v-for="item in users"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<!-- 角色/岗位 -->
<el-form-item label="角色/岗位" v-else-if="userTask && form.userType === 'candidateGroups'">
<el-select
v-model="form.candidateGroups"
placeholder="请选择"
@change="(value) => addUser({candidateGroups: value})"
>
<el-option
v-for="item in roles"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<!-- 分支允许添加条件 -->
<el-form-item label="分支条件" v-if="sequenceFlow">
<el-select v-model="form.user" placeholder="请选择">
<el-option
v-for="item in users"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "PropertyPanel",
props: {
modeler: {
type: Object,
required: true
}
},
computed: {
userTask() {
if (!this.element) {
return;
}
return this.element.type === 'bpmn:UserTask'
},
sequenceFlow() {
if (!this.element) {
return
}
return this.element.type === 'bpmn:SequenceFlow'
}
},
data() {
return {
form: {
id: "",
name: "",
color: null
},
element: {},
users: [
{
value: "zhangsan",
label: "张三"
},
{
value: "lisi",
label: "李四"
},
{
value: "wangwu",
label: "王五"
}
],
roles: [
{
value: "manager",
label: "经理"
},
{
value: "personnel",
label: "人事"
},
{
value: 'charge',
label: '主管'
}
]
};
},
mounted() {
this.handleModeler()
},
methods: {
handleModeler() {
// 监听节点选择变化
this.modeler.on("selection.changed", e => {
const element = e.newSelection[0];
this.element = element;
console.log(this.element);
if (!element) return;
this.form = {
...element.businessObject,
...element.businessObject.$attrs
};
if (this.form.userType === "candidateUsers") {
this.form["candidateUsers"] =
this.form["candidateUsers"].split(",") || [];
}
});
// 监听节点属性变化
this.modeler.on("element.changed", e => {
const { element } = e;
if (!element) return;
// 新增节点需要更新回属性面板
if (element.id === this.form.id) {
this.form.name = element.businessObject.name;
this.form = { ...this.form };
}
});
},
// 属性面板名称,更新回流程节点
nameChange(name) {
const modeling = this.modeler.get("modeling");
modeling.updateLabel(this.element, name);
},
// 属性面板颜色,更新回流程节点
colorChange(color) {
const modeling = this.modeler.get("modeling");
modeling.setColor(this.element, {
fill: null,
stroke: color
});
modeling.updateProperties(this.element, { color: color });
},
// 任务节点配置人员
addUser(properties) {
this.updateProperties(
Object.assign(properties, {
userType: Object.keys(properties)[0]
})
);
},
// 切换人员类型
typeChange() {
const types = ["assignee", "candidateUsers", "candidateGroups"];
types.forEach(type => {
delete this.element.businessObject.$attrs[type];
delete this.form[type];
});
},
// 在这里我们封装一个通用的更新节点属性的方法
updateProperties(properties) {
const modeling = this.modeler.get("modeling");
modeling.updateProperties(this.element, properties);
}
}
};
</script>
<style lang="scss" scoped>
.property-panel {
position: absolute;
right: 0px;
top: 0px;
border-left: 1px solid #cccccc;
padding: 20px 0;
width: 300px;
height: 100%;
}
</style>

View File

@@ -0,0 +1,285 @@
<template>
<div class="containers">
<div ref="canvas" class="canvas" />
<panel v-if="bpmnModeler" :modeler="bpmnModeler" />
<div class="toolbar">
<a title="download">下载</a>
<a ref="saveDiagram" href="javascript:" title="download BPMN diagram">BPMN</a>
<a ref="saveSvg" href="javascript:" title="download as SVG image">SVG</a>
</div>
</div>
</template>
<script>
import BpmnModeler from 'bpmn-js/lib/Modeler' // bpmn-js 设计器
import panel from './PropertyPanel' // 属性面板
import BpmData from './BpmData'
import customPalette from './palette'
export default {
components: {
panel
},
data() {
return {
bpmnModeler: null,
element: null,
bpmData: new BpmData()
}
},
mounted() {
const canvas = this.$refs.canvas
// 生成实例
this.bpmnModeler = new BpmnModeler({
additionalModules: [customPalette],
container: canvas
})
// 获取a标签dom节点
const downloadLink = this.$refs.saveDiagram
const downloadSvgLink = this.$refs.saveSvg
// 监听流程图改变事件
const _this = this
this.bpmnModeler.on('commandStack.changed', function() {
_this.saveSVG(function(err, svg) {
_this.setEncoded(downloadSvgLink, 'diagram.svg', err ? null : svg)
})
_this.saveDiagram(function(err, xml) {
_this.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml)
})
})
// 新增流程定义
this.createNewDiagram()
},
methods: {
createNewDiagram() {
const bpmnXmlStr = `
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
<bpmn2:process id="process1567044459787" name="流程1567044459787">
<bpmn2:documentation>描述</bpmn2:documentation>
<bpmn2:startEvent id="StartEvent_01ydzqe" name="开始">
<bpmn2:outgoing>SequenceFlow_1qw929z</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:sequenceFlow id="SequenceFlow_1qw929z" sourceRef="StartEvent_01ydzqe" targetRef="Task_1piqdk6" />
<bpmn2:userTask id="Task_1piqdk6" name="请假申请">
<bpmn2:incoming>SequenceFlow_1qw929z</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_11h4o22</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:exclusiveGateway id="ExclusiveGateway_0k39v3u">
<bpmn2:incoming>SequenceFlow_11h4o22</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_1iu7pfe</bpmn2:outgoing>
<bpmn2:outgoing>SequenceFlow_04uqww2</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="SequenceFlow_11h4o22" sourceRef="Task_1piqdk6" targetRef="ExclusiveGateway_0k39v3u" />
<bpmn2:sequenceFlow id="SequenceFlow_1iu7pfe" sourceRef="ExclusiveGateway_0k39v3u" targetRef="Task_10fqcwp" />
<bpmn2:userTask id="Task_10fqcwp" name="经理审批">
<bpmn2:incoming>SequenceFlow_1iu7pfe</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_1xod8nh</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="SequenceFlow_04uqww2" sourceRef="ExclusiveGateway_0k39v3u" targetRef="Task_15n23yh" />
<bpmn2:userTask id="Task_15n23yh" name="总部审批">
<bpmn2:incoming>SequenceFlow_04uqww2</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_0c8wrs4</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:exclusiveGateway id="ExclusiveGateway_1sq33g6">
<bpmn2:incoming>SequenceFlow_0c8wrs4</bpmn2:incoming>
<bpmn2:incoming>SequenceFlow_1xod8nh</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_0h8za82</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="SequenceFlow_0c8wrs4" sourceRef="Task_15n23yh" targetRef="ExclusiveGateway_1sq33g6" />
<bpmn2:sequenceFlow id="SequenceFlow_1xod8nh" sourceRef="Task_10fqcwp" targetRef="ExclusiveGateway_1sq33g6" />
<bpmn2:endEvent id="EndEvent_0pnmjd3">
<bpmn2:incoming>SequenceFlow_0h8za82</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:sequenceFlow id="SequenceFlow_0h8za82" sourceRef="ExclusiveGateway_1sq33g6" targetRef="EndEvent_0pnmjd3" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="process1567044459787">
<bpmndi:BPMNShape id="StartEvent_01ydzqe_di" bpmnElement="StartEvent_01ydzqe">
<dc:Bounds x="532" y="72" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="539" y="53" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1qw929z_di" bpmnElement="SequenceFlow_1qw929z">
<di:waypoint x="550" y="108" />
<di:waypoint x="550" y="150" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="UserTask_1qxjy46_di" bpmnElement="Task_1piqdk6">
<dc:Bounds x="500" y="150" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="ExclusiveGateway_0k39v3u_di" bpmnElement="ExclusiveGateway_0k39v3u" isMarkerVisible="true">
<dc:Bounds x="525" y="275" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_11h4o22_di" bpmnElement="SequenceFlow_11h4o22">
<di:waypoint x="550" y="230" />
<di:waypoint x="550" y="275" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_1iu7pfe_di" bpmnElement="SequenceFlow_1iu7pfe">
<di:waypoint x="575" y="300" />
<di:waypoint x="680" y="300" />
<di:waypoint x="680" y="380" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="UserTask_18pwui1_di" bpmnElement="Task_10fqcwp">
<dc:Bounds x="630" y="380" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_04uqww2_di" bpmnElement="SequenceFlow_04uqww2">
<di:waypoint x="525" y="300" />
<di:waypoint x="430" y="300" />
<di:waypoint x="430" y="380" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="UserTask_1j0us24_di" bpmnElement="Task_15n23yh">
<dc:Bounds x="380" y="380" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="ExclusiveGateway_1sq33g6_di" bpmnElement="ExclusiveGateway_1sq33g6" isMarkerVisible="true">
<dc:Bounds x="525" y="515" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_0c8wrs4_di" bpmnElement="SequenceFlow_0c8wrs4">
<di:waypoint x="430" y="460" />
<di:waypoint x="430" y="540" />
<di:waypoint x="525" y="540" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_1xod8nh_di" bpmnElement="SequenceFlow_1xod8nh">
<di:waypoint x="680" y="460" />
<di:waypoint x="680" y="540" />
<di:waypoint x="575" y="540" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="EndEvent_0pnmjd3_di" bpmnElement="EndEvent_0pnmjd3">
<dc:Bounds x="532" y="602" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_0h8za82_di" bpmnElement="SequenceFlow_0h8za82">
<di:waypoint x="550" y="565" />
<di:waypoint x="550" y="602" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>
`
// 将字符串转换成图显示出来
this.bpmnModeler.importXML(bpmnXmlStr, err => {
if (err) {
console.error(err)
} else {
this.adjustPalette()
}
})
},
// 调整左侧工具栏排版
adjustPalette() {
try {
// 获取 bpmn 设计器实例
const canvas = this.$refs.canvas
const djsPalette = canvas.children[0].children[1].children[4]
const djsPalStyle = {
width: '130px',
padding: '5px',
background: 'white',
left: '20px',
borderRadius: 0
}
for (var key in djsPalStyle) {
djsPalette.style[key] = djsPalStyle[key]
}
const palette = djsPalette.children[0]
const allGroups = palette.children
allGroups[0].style['display'] = 'none'
// 修改控件样式
for (var gKey in allGroups) {
const group = allGroups[gKey]
for (var cKey in group.children) {
const control = group.children[cKey]
const controlStyle = {
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
width: '100%',
padding: '5px'
}
if (
control.className &&
control.dataset &&
control.className.indexOf('entry') !== -1
) {
const controlProps = this.bpmData.getControl(
control.dataset.action
)
control.innerHTML = `<div style='font-size: 14px;font-weight:500;margin-left:15px;'>${
controlProps['title']
}</div>`
for (var csKey in controlStyle) {
control.style[csKey] = controlStyle[csKey]
}
}
}
}
} catch (e) {
console.log(e)
}
},
// 下载为SVG格式,done是个函数调用的时候传入的
saveSVG(done) {
// 把传入的done再传给bpmn原型的saveSVG函数调用
this.bpmnModeler.saveSVG(done)
},
// 下载为SVG格式,done是个函数调用的时候传入的
saveDiagram(done) {
// 把传入的done再传给bpmn原型的saveXML函数调用
this.bpmnModeler.saveXML({ format: true }, function(err, xml) {
done(err, xml)
})
},
// 当图发生改变的时候会调用这个函数这个data就是图的xml
setEncoded(link, name, data) {
// 把xml转换为URI下载要用到的
const encodedData = encodeURIComponent(data)
// 获取到图的xml保存就是把这个xml提交给后台
this.xmlStr = data
// 下载图的具体操作,改变a的属性className令a标签可点击href令能下载download是下载的文件的名字
if (data) {
link.className = 'active'
link.href = 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData
link.download = name
}
}
}
}
</script>
<style lang="scss">
/*左边工具栏以及编辑节点的样式*/
@import "~bpmn-js/dist/assets/diagram-js.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";
.containers {
position: absolute;
background-color: #ffffff;
width: 100%;
height: 100%;
.canvas {
width: 100%;
height: 100%;
}
.panel {
position: absolute;
right: 0;
top: 50px;
width: 300px;
}
.bjs-powered-by {
display: none;
}
.toolbar {
position: absolute;
top: 20px;
right: 350px;
a {
text-decoration: none;
margin: 5px;
color: #409eff;
}
}
}
</style>

View File

@@ -0,0 +1,401 @@
import {
isFunction,
isArray,
forEach
} from 'min-dash'
import {
domify,
query as domQuery,
attr as domAttr,
clear as domClear,
classes as domClasses,
matches as domMatches,
delegate as domDelegate,
event as domEvent
} from 'min-dom'
var TOGGLE_SELECTOR = '.djs-palette-toggle'
var ENTRY_SELECTOR = '.entry'
var ELEMENT_SELECTOR = TOGGLE_SELECTOR + ', ' + ENTRY_SELECTOR
var PALETTE_OPEN_CLS = 'open'
var PALETTE_TWO_COLUMN_CLS = 'two-column'
/**
* A palette containing modeling elements.
*/
export default function Palette(
eventBus,
canvas,
elementFactory,
create,
paletteContainer,
paletteEntries) {
this._eventBus = eventBus
this._canvas = canvas
this._providers = []
var self = this
eventBus.on('tool-manager.update', function(event) {
var tool = event.tool
self.updateToolHighlight(tool)
})
eventBus.on('i18n.changed', function() {
self._update()
})
eventBus.on('diagram.init', function() {
self._diagramInitialized = true
// initialize + update once diagram is ready
if (self._providers.length) {
self._init()
self._update()
}
})
}
Palette.$inject = [
'eventBus',
'canvas',
/* 自定义 */
]
/**
* Register a provider with the palette
*
* @param {PaletteProvider} provider
*/
Palette.prototype.registerProvider = function(provider) {
this._providers.push(provider)
// postpone init / update until diagram is initialized
if (!this._diagramInitialized) {
return
}
if (!this._container) {
this._init()
}
this._update()
}
/**
* Returns the palette entries for a given element
*
* @return {Array<PaletteEntryDescriptor>} list of entries
*/
Palette.prototype.getEntries = function() {
var entries = {}
// loop through all providers and their entries.
// group entries by id so that overriding an entry is possible
forEach(this._providers, function(provider) {
var e = provider.getPaletteEntries()
forEach(e, function(entry, id) {
entries[id] = entry
})
})
return entries
}
/**
* Initialize
*/
Palette.prototype._init = function() {
var self = this
var eventBus = this._eventBus
var parentContainer = this._getParentContainer()
var container = this._container = domify(Palette.HTML_MARKUP)
parentContainer.appendChild(container)
domDelegate.bind(container, ELEMENT_SELECTOR, 'click', function(event) {
var target = event.delegateTarget
if (domMatches(target, TOGGLE_SELECTOR)) {
return self.toggle()
}
self.trigger('click', event)
})
// prevent drag propagation
domEvent.bind(container, 'mousedown', function(event) {
event.stopPropagation()
})
// prevent drag propagation
domDelegate.bind(container, ENTRY_SELECTOR, 'dragstart', function(event) {
self.trigger('dragstart', event)
})
eventBus.on('canvas.resized', this._layoutChanged, this)
eventBus.fire('palette.create', {
container: container
})
}
/**
* Update palette state.
*
* @param {Object} [state] { open, twoColumn }
*/
Palette.prototype._toggleState = function(state) {
state = state || {}
var parent = this._getParentContainer()
var container = this._container
var eventBus = this._eventBus
var twoColumn
var cls = domClasses(container)
if ('twoColumn' in state) {
twoColumn = state.twoColumn
} else {
twoColumn = this._needsCollapse(parent.clientHeight, this._entries || {})
}
// always update two column
cls.toggle(PALETTE_TWO_COLUMN_CLS, twoColumn)
if ('open' in state) {
cls.toggle(PALETTE_OPEN_CLS, state.open)
}
eventBus.fire('palette.changed', {
twoColumn: twoColumn,
open: this.isOpen()
})
}
Palette.prototype._update = function() {
var entriesContainer = domQuery('.djs-palette-entries', this._container)
var entries = this._entries = this.getEntries()
domClear(entriesContainer)
forEach(entries, function(entry, id) {
var grouping = entry.group || 'default'
var container = domQuery('[data-group=' + grouping + ']', entriesContainer)
if (!container) {
container = domify('<div class="group" data-group="' + grouping + '"></div>')
entriesContainer.appendChild(container)
}
var html = entry.html || (
entry.separator
? '<hr class="separator" />'
: '<div class="entry" draggable="true"></div>')
var control = domify(html)
container.appendChild(control)
if (!entry.separator) {
domAttr(control, 'data-action', id)
if (entry.title) {
domAttr(control, 'title', entry.title)
}
if (entry.className) {
addClasses(control, entry.className)
}
if (entry.imageUrl) {
control.appendChild(domify('<img src="' + entry.imageUrl + '">'))
}
}
})
// open after update
this.open()
}
/**
* Trigger an action available on the palette
*
* @param {String} action
* @param {Event} event
*/
Palette.prototype.trigger = function(action, event, autoActivate) {
var entries = this._entries
var entry
var handler
var originalEvent
var button = event.delegateTarget || event.target
if (!button) {
return event.preventDefault()
}
entry = entries[domAttr(button, 'data-action')]
// when user clicks on the palette and not on an action
if (!entry) {
return
}
handler = entry.action
originalEvent = event.originalEvent || event
// simple action (via callback function)
if (isFunction(handler)) {
if (action === 'click') {
handler(originalEvent, autoActivate)
}
} else {
if (handler[action]) {
handler[action](originalEvent, autoActivate)
}
}
// silence other actions
event.preventDefault()
}
Palette.prototype._layoutChanged = function() {
this._toggleState({})
}
/**
* Do we need to collapse to two columns?
*
* @param {Number} availableHeight
* @param {Object} entries
*
* @return {Boolean}
*/
Palette.prototype._needsCollapse = function(availableHeight, entries) {
// top margin + bottom toggle + bottom margin
// implementors must override this method if they
// change the palette styles
var margin = 20 + 10 + 20
var entriesHeight = Object.keys(entries).length * 46
return availableHeight < entriesHeight + margin
}
/**
* Close the palette
*/
Palette.prototype.close = function() {
this._toggleState({
open: false,
twoColumn: false
})
}
/**
* Open the palette
*/
Palette.prototype.open = function() {
this._toggleState({ open: true })
}
Palette.prototype.toggle = function(open) {
if (this.isOpen()) {
this.close()
} else {
this.open()
}
}
Palette.prototype.isActiveTool = function(tool) {
return tool && this._activeTool === tool
}
Palette.prototype.updateToolHighlight = function(name) {
var entriesContainer,
toolsContainer
if (!this._toolsContainer) {
entriesContainer = domQuery('.djs-palette-entries', this._container)
this._toolsContainer = domQuery('[data-group=tools]', entriesContainer)
}
toolsContainer = this._toolsContainer
forEach(toolsContainer.children, function(tool) {
var actionName = tool.getAttribute('data-action')
if (!actionName) {
return
}
var toolClasses = domClasses(tool)
actionName = actionName.replace('-tool', '')
if (toolClasses.contains('entry') && actionName === name) {
toolClasses.add('highlighted-entry')
} else {
toolClasses.remove('highlighted-entry')
}
})
}
/**
* Return true if the palette is opened.
*
* @example
*
* palette.open();
*
* if (palette.isOpen()) {
* // yes, we are open
* }
*
* @return {boolean} true if palette is opened
*/
Palette.prototype.isOpen = function() {
return domClasses(this._container).has(PALETTE_OPEN_CLS)
}
/**
* Get container the palette lives in.
*
* @return {Element}
*/
Palette.prototype._getParentContainer = function() {
return this._canvas.getContainer()
}
/* markup definition */
Palette.HTML_MARKUP =
'<div class="djs-palette">' +
'<div class="djs-palette-entries"></div>' +
'<div class="djs-palette-toggle"></div>' +
'</div>'
// helpers //////////////////////
function addClasses(element, classNames) {
var classes = domClasses(element)
var actualClassNames = isArray(classNames) ? classNames : classNames.split(/\s+/g)
actualClassNames.forEach(function(cls) {
classes.add(cls)
})
}

View File

@@ -0,0 +1,191 @@
import { assign } from 'min-dash';
export default function PaletteProvider(
palette, create, elementFactory,
spaceTool, lassoTool, handTool,
globalConnect, translate) {
this._palette = palette;
this._create = create;
this._elementFactory = elementFactory;
this._spaceTool = spaceTool;
this._lassoTool = lassoTool;
this._handTool = handTool;
this._globalConnect = globalConnect;
this._translate = translate;
palette.registerProvider(this);
}
PaletteProvider.$inject = [
'palette',
'create',
'elementFactory',
'spaceTool',
'lassoTool',
'handTool',
'globalConnect',
'translate'
]
PaletteProvider.prototype.getPaletteEntries = function(element) {
var actions = {}
var create = this._create
var elementFactory = this._elementFactory
var spaceTool = this._spaceTool
var lassoTool = this._lassoTool
var handTool = this._handTool
var globalConnect = this._globalConnect
var translate = this._translate
function createAction(type, group, className, title, options) {
function createListener(event) {
var shape = elementFactory.createShape(assign({ type: type }, options));
if (options) {
shape.businessObject.di.isExpanded = options.isExpanded;
}
create.start(event, shape);
}
var shortType = type.replace(/^bpmn:/, '');
return {
group: group,
className: className,
title: title || translate('Create {type}', { type: shortType }),
action: {
dragstart: createListener,
click: createListener
}
}
}
function createSubprocess(event) {
var subProcess = elementFactory.createShape({
type: 'bpmn:SubProcess',
x: 0,
y: 0,
isExpanded: true
})
var startEvent = elementFactory.createShape({
type: 'bpmn:StartEvent',
x: 40,
y: 82,
parent: subProcess
})
create.start(event, [ subProcess, startEvent ], {
hints: {
autoSelect: [ startEvent ]
}
})
}
function createParticipant(event) {
create.start(event, elementFactory.createParticipantShape());
}
assign(actions, {
'hand-tool': {
group: 'tools',
className: 'bpmn-icon-hand-tool',
title: translate('Activate the hand tool'),
action: {
click: function(event) {
handTool.activateHand(event);
}
}
},
'lasso-tool': {
group: 'tools',
className: 'bpmn-icon-lasso-tool',
title: translate('Activate the lasso tool'),
action: {
click: function(event) {
lassoTool.activateSelection(event);
}
}
},
'space-tool': {
group: 'tools',
className: 'bpmn-icon-space-tool',
title: translate('Activate the create/remove space tool'),
action: {
click: function(event) {
spaceTool.activateSelection(event);
}
}
},
'global-connect-tool': {
group: 'tools',
className: 'bpmn-icon-connection-multi',
title: translate('Activate the global connect tool'),
action: {
click: function(event) {
globalConnect.toggle(event);
}
}
},
'tool-separator': {
group: 'tools',
separator: true
},
'create.start-event': createAction(
'bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none',
translate('Create StartEvent')
),
'create.intermediate-event': createAction(
'bpmn:IntermediateThrowEvent', 'event', 'bpmn-icon-intermediate-event-none',
translate('Create Intermediate/Boundary Event')
),
'create.end-event': createAction(
'bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none',
translate('Create EndEvent')
),
'create.exclusive-gateway': createAction(
'bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none',
translate('Create Gateway')
),
'create.task': createAction(
'bpmn:Task', 'activity', 'bpmn-icon-task',
translate('Create Task')
),
'create.data-object': createAction(
'bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object',
translate('Create DataObjectReference')
),
'create.data-store': createAction(
'bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store',
translate('Create DataStoreReference')
),
'create.subprocess-expanded': {
group: 'activity',
className: 'bpmn-icon-subprocess-expanded',
title: translate('Create expanded SubProcess'),
action: {
dragstart: createSubprocess,
click: createSubprocess
}
},
'create.participant-expanded': {
group: 'collaboration',
className: 'bpmn-icon-participant',
title: translate('Create Pool/Participant'),
action: {
dragstart: createParticipant,
click: createParticipant
}
},
'create.group': createAction(
'bpmn:Group', 'artifact', 'bpmn-icon-group',
translate('Create Group')
),
});
return actions;
};

View File

@@ -0,0 +1,13 @@
import customPalette from './CustomPalette'
import PaletteProvider from './CustomPaletteProvider'
// 除了引进的模块的名字可以修改,其他的不建议修改,会报错
export default {
__depends__: [
{
__init__: ['customPalette'],
customPalette: ['type', customPalette]
}
], // 依赖于 customPalette 这个模块
__init__: ['customPaletteProvider'], // 调用 customPaletteProvider 来初始化
customPaletteProvider: ['type', PaletteProvider]
}

View File

@@ -0,0 +1,15 @@
<template>
<vue-bpmn />
</template>
<script>
import VueBpmn from '@/views/components/VueBpmn';
export default {
components: {
VueBpmn
}
}
</script>
<style lang="scss" scoped>
</style>