add:引入bpmn
This commit is contained in:
67
wms_pro/qd/src/views/components/VueBpmn/BpmData.js
Normal file
67
wms_pro/qd/src/views/components/VueBpmn/BpmData.js
Normal 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] || {}
|
||||
}
|
||||
}
|
||||
226
wms_pro/qd/src/views/components/VueBpmn/PropertyPanel.vue
Normal file
226
wms_pro/qd/src/views/components/VueBpmn/PropertyPanel.vue
Normal 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>
|
||||
285
wms_pro/qd/src/views/components/VueBpmn/index.vue
Normal file
285
wms_pro/qd/src/views/components/VueBpmn/index.vue
Normal 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>
|
||||
401
wms_pro/qd/src/views/components/VueBpmn/palette/CustomPalette.js
Normal file
401
wms_pro/qd/src/views/components/VueBpmn/palette/CustomPalette.js
Normal 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)
|
||||
})
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
13
wms_pro/qd/src/views/components/VueBpmn/palette/index.js
Normal file
13
wms_pro/qd/src/views/components/VueBpmn/palette/index.js
Normal 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]
|
||||
}
|
||||
15
wms_pro/qd/src/views/wms/flow_manage/flowable/bpmn/index.vue
Normal file
15
wms_pro/qd/src/views/wms/flow_manage/flowable/bpmn/index.vue
Normal 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>
|
||||
Reference in New Issue
Block a user