fix: 增加流程图前端代码

This commit is contained in:
yunlong.li 2021-11-18 12:06:24 +08:00
parent 00f59fdc75
commit 3f90e79d8a
30 changed files with 7241 additions and 4 deletions

View File

@ -43,8 +43,8 @@ public class ModelController {
@GetMapping ("/page")
@ApiOperation(value = "分页数据")
public PageResult<Model> pageList(ModelPageReqVo modelPageReqVo) {
return bpmModelService.pageList(modelPageReqVo);
public CommonResult<PageResult<Model>> pageList(ModelPageReqVo modelPageReqVo) {
return CommonResult.success(bpmModelService.pageList(modelPageReqVo));
}
@PostMapping("/create")

View File

@ -40,7 +40,8 @@
"@riophae/vue-treeselect": "0.4.0",
"axios": "0.21.0",
"clipboard": "2.0.6",
"core-js": "3.8.1",
"core-js": "3.19.1",
"jeeplus-bpmn": "^6.0.10",
"echarts": "4.9.0",
"element-ui": "2.15.0",
"file-saver": "2.0.4",
@ -75,7 +76,8 @@
"sass-loader": "10.1.0",
"script-ext-html-webpack-plugin": "2.1.5",
"svg-sprite-loader": "5.1.1",
"vue-template-compiler": "2.6.12"
"vue-template-compiler": "2.6.12",
"vue2-ace-editor": "^0.0.15"
},
"engines": {
"node": ">=8.9",

View File

@ -0,0 +1,10 @@
import request from '@/utils/request'
export function page(query) {
return request({
url: '/workflow/models/page',
method: 'get',
params: query
})
}

View File

@ -0,0 +1,139 @@
<template>
<div>
<el-button-group>
<el-tooltip class="item" effect="dark" content="保存并发布" placement="bottom">
<el-button type="primary" size="small" @click="deploy"><i class="fa fa-save"> 保存并发布</i></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="保存草稿" placement="bottom">
<el-button type="primary" size="small" @click="save"><i class="fa fa-save"> 保存草稿</i></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="打开流程文件" placement="bottom">
<el-button type="primary" size="small" @click="importXml"><i class="fa fa-folder-open"></i></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="创建新流程图" placement="bottom">
<el-button type="primary" size="small" @click="reset"><i class="fa fa-plus-circle"></i></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="下载流程图" placement="bottom">
<el-button type="primary" size="small" @click="downloadSvg"><i class="fa fa-picture-o"></i></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="下载流程文件" placement="bottom">
<el-button type="primary" size="small" @click="downloadBpmn"><i class="fa fa-download"></i></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="撤销" placement="bottom">
<el-button size="small"><i class="fa fa-rotate-left" @click="undo"></i></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="恢复" placement="bottom">
<el-button size="small"><i class="fa fa-rotate-right" :class="!canRedo?'default-undo':''"
@click="redo"></i></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="放大" placement="bottom">
<el-button size="small" @click="zoom(0.05)"><i class="fa fa-search-plus"></i></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="缩小" placement="bottom">
<el-button size="small" @click="zoom(-0.05)"><i class="fa fa-search-minus"></i></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="重置" placement="bottom">
<el-button size="small" @click="zoom(0)"><i class="fa fa-arrows"></i></el-button>
</el-tooltip>
</el-button-group>
</div>
</template>
<script>
export default {
name: "ViewerHeader",
data() {
return {
scale: 1.0,
canRedo: false
}
},
props: {
processData: {
type: Object
},
modeler: {
type: Object
}
},
components: {},
methods: {
deploy() {
let that = this;
let _xml;
let _svg;
this.modeler.saveXML((err, xml) => {
if (err) {
console.error(err)
}
_xml = xml;
})
this.modeler.saveSVG((err, svg) => {
if (err) {
console.error(err)
}
_svg = svg;
})
that.post(this.Apis.deployProcess, {
processKey: "s1111",
processName: "阿达达",
resourceName: "test01",
xml: _xml,
svg: _svg
}, function (data) {
console.log(data)
});
},
save(){
let that = this;
let _xml;
let _svg;
this.modeler.saveXML((err, xml) => {
if (err) {
console.error(err)
}
_xml = xml;
})
this.modeler.saveSVG((err, svg) => {
if (err) {
console.error(err)
}
_svg = svg;
})
that.$emit("processSave",{"xmlStr":_xml,"svgStr":_svg});
},
reset() {
this.$emit('restart')
},
importXml() {
this.$emit('importXml')
},
downloadSvg() {
this.$emit("handleExportSvg")
},
downloadBpmn() {
this.$emit("handleExportBpmn");
},
undo() {
this.modeler.get('commandStack').undo();
this.canRedo = this.modeler.get('commandStack').canRedo();
},
redo() {
if (!this.canRedo) {
return;
}
this.modeler.get('commandStack').redo()
this.canRedo = this.modeler.get('commandStack').canRedo();
},
zoom(val) {
let newScale = !val ? 1.0 : ((this.scale + val) <= 0.2) ? 0.2 : (this.scale + val);
this.modeler.get('canvas').zoom(newScale);
this.scale = newScale;
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,264 @@
<template>
<div class="">
<ImportDialog :dialogVisibleBool="importXmlShow" @closeShowXmlDialog="closeShowXmlDialog"></ImportDialog>
<el-row>
<el-col :span="24">
<vue-header class="bpmn-viewer-header" :processData="initData" :modeler="bpmnModeler" @restart="restart" @importXml="importXml"
@handleExportSvg="handleExportSvg" @handleExportBpmn="handleExportBpmn" @processSave="processSave"></vue-header>
</el-col>
</el-row>
<el-row style="margin-left: 1%">
<el-tabs v-model="activeName" type="card" >
<el-tab-pane label="流程设计器" name="first">
<el-col :span="19">
<div class="bpmn-viewer">
<div class="bpmn-viewer-content" ref="bpmnViewer" ></div>
</div>
</el-col>
<el-col :span="5">
<bpmn-panel style="width: 92%" @updateXml="updateXml" v-if="bpmnModeler" :modeler="bpmnModeler" :process="initData"></bpmn-panel>
</el-col>
</el-tab-pane>
<el-tab-pane label="流程XML" name="second">
<editor v-model="xmlShowWatch" @init="editorInit" lang="xml" theme="chrome" width="98%" height="calc(100vh - 100px)"></editor>
</el-tab-pane>
</el-tabs>
</el-row>
</div>
</template>
<script>
import templateXml from "./data/template";
import ImportDialog from "./dialog/ImportDialog";
// import activitiCom from "../provider/activiti";
// import BpmnModeler2 from 'bpmn-js/lib/Modeler';
import BpmnModeler from 'jeeplus-bpmn/lib/Modeler'
import customTranslate from "./data/translate/customTranslate";
import VueHeader from "./Header";
import BpmnPanel from "./panel/index";
import activitiModule from './data/activiti.json'
import flowableModule from './data/flowable.json'
import './assets/css/vue-bmpn.css'
import './assets/css/font-awesome.min.css'
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'
import './assets/css/vue-bmpn.css'
export default {
name: "VueBpmn",
data() {
return {
bpmnModeler: null,
importXmlShow: false,
xmlShowWatch: "",
initTemplate: "",
activeName: "first",
initData: {},
xmlShow: ""
}
},
props: {
product: String,
bpmnXml: {
type: String,
required: false
}
},
components: {
editor: require('vue2-ace-editor'),
VueHeader, BpmnPanel,ImportDialog
},
watch: {
xmlShow: {
handler(){
this.xmlShowWatch = this.xmlShow
}
}
},
mounted() {
let processId = new Date().getTime();
this.initTemplate = templateXml.initTemplate(processId)
this.initData = {key: "process" + processId, name: "流程" + processId, xml: this.initTemplate}
if (this.bpmnXml != null) {
this.initTemplate = this.bpmnXml
}
this.init();
},
methods: {
editorInit: function (editor) {
require('brace/mode/xml')
require('brace/theme/chrome')
editor.setOptions({
fontSize: "15px"
})
editor.getSession().setUseWrapMode(true);
},
init() {
const _this = this;
// activitiflowable
let _moddleExtensions = this.getModdleExtensions();
// element
this.canvas = this.$refs.bpmnViewer;
// Bpmn
this.bpmnModeler = new BpmnModeler({
container: this.canvas,
additionalModules: [
{
translate: ['value', customTranslate]
},
// activitiCom
],
moddleExtensions: _moddleExtensions
});
//
this.initDiagram(this.initTemplate);
setTimeout(function () {
_this.bpmnModeler.saveXML({format: true}, function (err, xml) {
_this.updateXml(xml)
});
// console.log(_this.bpmnModeler);
// const elementRegistry = _this.bpmnModeler.get('elementRegistry');
// const startEvenList = elementRegistry.filter (
// (item) => item.type === 'bpmn:StartEvent'
// );
// //
// _this.bpmnModeler.get("modeling").updateProperties(startEvenList[0], {"activiti:initiator": "applyUserId"});
}, 300)
},
initDiagram(xml) {
this.bpmnModeler.importXML(xml, err => {
if (err) {
// this.$Message.error(",Bpmn2.0");
}
let _tag = document.getElementsByTagName("svg")[0];
if (_tag) {
_tag.style.width = "100%";
_tag.style.height = "700px";
}
});
},
handleExportBpmn() {
const _this = this;
this.bpmnModeler.saveXML(function (err, xml) {
if (err) {
console.error(err)
}
let {filename, href} = _this.setEncoded('BPMN', xml);
if (href && filename) {
let a = document.createElement('a');
a.download = filename; //
a.href = href; // URL
a.click(); //
URL.revokeObjectURL(a.href); // URL
}
});
},
updateXml(xmlShow) {
this.xmlShow = xmlShow
},
handleExportSvg() {
const _this = this;
this.bpmnModeler.saveSVG(function (err, svg) {
if (err) {
console.error(err)
}
let {filename, href} = _this.setEncoded('SVG', svg);
if (href && filename) {
let a = document.createElement('a');
a.download = filename;
a.href = href;
a.click();
URL.revokeObjectURL(a.href);
}
});
},
setEncoded(type, data) {
const encodedData = encodeURIComponent(data);
if (data) {
if (type === 'XML') {
return {
filename: 'diagram.bpmn20.xml',
href: "data:application/bpmn20-xml;charset=UTF-8," + encodedData,
data: data
}
}
if (type === 'BPMN') {
return {
filename: 'diagram.bpmn',
href: "data:application/bpmn20-xml;charset=UTF-8," + encodedData,
data: data
}
}
if (type === 'SVG') {
this.initData.svg = data;
return {
filename: 'diagram.svg',
href: "data:application/text/xml;charset=UTF-8," + encodedData,
data: data
}
}
}
},
processSave(data){
let initData = this.initData;
data.procId = initData.key;
data.name = initData.name;
this.$emit("processSave",data);
},
restart() {
let processId = new Date().getTime();
this.initTemplate = templateXml.initTemplate(processId)
this.initData = {key: "process" + processId, name: "流程" + processId, xml: this.initTemplate}
this.initDiagram(this.initTemplate)
},
importXml() {
this.importXmlShow = true
},
closeShowXmlDialog(xmlData) {
this.importXmlShow = false
const that =this;
if (xmlData != null) {
this.initDiagram(xmlData)
//
setTimeout(function () {
that.bpmnModeler.saveXML({format: true}, function (err, xml) {
that.updateXml(xml)
})
},500)
}
},
getModdleExtensions() {
let moddleExtensions = {};
if (this.product === "flowable") {
moddleExtensions = {
flowable: flowableModule
}
}
if (this.product === "activiti") {
moddleExtensions = {
activiti: activitiModule
}
}
return moddleExtensions;
}
}
}
</script>
<style scoped>
.djs-palette{
top: 10px;
left: 10px;
}
/deep/.el-tabs__header{
margin: 0;
}
/deep/.el-tabs--card>.el-tabs__header{
border-bottom: 0;
}
</style>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,55 @@
.bjs-powered-by{
display: none;
}
.djs-palette.two-column.open{
width: 48px;
}
.container{
display: flex;
}
.bpmn-viewer{
flex: 1;
}
.bpmn-viewer-header{
height: 50px;
line-height: 50px;
padding-left: 20px;
background: #f8f8f8;
}
.bpmn-viewer-container{
padding: 10px;
height: 100%;
}
.bpmn-viewer-content{
width: 100%;
/*height: 100%;*/
height: calc(100vh - 150px);
overflow-x: hidden;
background: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+") repeat!important;
}
.el-collapse-item__header{
padding-left: 15px;
}
.djs-direct-editing-parent{
/*display: none;*/
}
.bpmn-panel {
/*width: 370px;*/
border: 1px solid #eeeeee;
padding: 0 5px;
}
.el-select--small{
width: 100%;
}
.el-dialog > .el-dialog__header{
padding: 5px 20px ;
}
.el-dialog > .el-dialog__body{
padding: 0px;
margin: 0 20px;
border: 1px solid #cccccc;
}
.default-undo{
color: #c0c4cc;
}

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

View File

@ -0,0 +1,13 @@
{
"datalist": {
"error": {
"type": 1,
"number": [
1
],
"list": []
},
"not": [],
"right": []
}
}

View File

@ -0,0 +1,20 @@
<template>
<div>
<div v-if="type==1"></div>
</div>
</template>
<script>
export default {
name: "data",
data(){
return {
}
}
}
</script>
<style scoped>
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
let xml = `
<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" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
<bpmn2:process id="process{str}" name="流程{str}">
<bpmn2:startEvent id="StartEvent_01ydzqe" name="开始" activiti:initiator="applyUserId" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="process{str}">
<bpmndi:BPMNShape id="StartEvent_01ydzqe_di" bpmnElement="StartEvent_01ydzqe">
<dc:Bounds x="142" y="212" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="149" y="255" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>`;
export default {
initTemplate: function (str) {
return xml.replace(/{str}/g, str)
}
};

View File

@ -0,0 +1,12 @@
import translations from './zn'
export default function customTranslate(template, replacements) {
replacements = replacements || {};
// Translate
template = translations[template] || template;
// Replace
return template.replace(/{([^}]+)}/g, function(_, key) {
return replacements[key] || '{' + key + '}';
});
}

View File

@ -0,0 +1,116 @@
export default {
// Labels
'Activate the global connect tool': '启动全局连接工具',
'Append {type}': '追加 {type}',
'Add Lane above': '添加到通道之上',
'Divide into two Lanes': '分成两条通道',
'Divide into three Lanes': '分成三条通道',
'Add Lane below': '添加到通道之下',
'Append compensation activity': '追加补偿活动',
'Change type': '更改类型',
'Connect using Association': '文本关联',
'Connect using Sequence/MessageFlow or Association': '消息关联',
'Connect using DataInputAssociation': '数据关联',
'Remove': '移除',
'Activate the hand tool': '启动手动工具',
'Activate the lasso tool': '启动 Lasso 工具',
'Activate the create/remove space tool': '启动创建/删除空间工具',
'Create expanded SubProcess': '创建可折叠子流程',
'Create IntermediateThrowEvent/BoundaryEvent': '创建中间抛出/边界事件',
'Create Pool/Participant': '创建池/参与者',
'Parallel Multi Instance': '并行多实例',
'Sequential Multi Instance': '串行多实例',
'Loop': '循环',
'Ad-hoc': 'Ad-hoc子流程',
'Create {type}': '创建 {type}',
'Task': '任务',
'Send Task': '发送任务',
'Receive Task': '接受任务',
'User Task': '用户任务',
'Manual Task': '手动任务',
'Business Rule Task': '规则任务',
'Service Task': '服务任务',
'Script Task': '脚本任务',
'Call Activity': '引用流程',
'Sub Process (collapsed)': '可折叠子流程',
'Sub Process (expanded)': '可展开子流程',
'Start Event': '开始事件',
'Intermediate Throw Event': '中间抛出事件',
'End Event': '结束事件',
'Message Start Event': '消息启动事件',
'Timer Start Event': '定时启动事件',
'Conditional Start Event': '条件启动事件',
'Signal Start Event': '信号启动事件',
'Error Start Event': '错误启动事件',
'Escalation Start Event': '升级启动事件',
'Compensation Start Event': '补偿启动事件',
'Message Start Event (non-interrupting)': '消息启动事件 (非中断)',
'Timer Start Event (non-interrupting)': '定时启动事件 (非中断)',
'Conditional Start Event (non-interrupting)': '条件启动事件 (非中断)',
'Signal Start Event (non-interrupting)': '信号启动事件 (非中断)',
'Escalation Start Event (non-interrupting)': '升级启动事件 (非中断)',
'Message Intermediate Catch Event': '中间消息捕获事件',
'Message Intermediate Throw Event': '中间消息抛出事件',
'Timer Intermediate Catch Event': '中间定时捕获事件',
'Escalation Intermediate Throw Event': '中间升级抛出事件',
'Conditional Intermediate Catch Event': '中间条件捕获事件',
'Link Intermediate Catch Event': '中间链接捕获事件',
'Link Intermediate Throw Event': '中间链接抛出事件',
'Compensation Intermediate Throw Event': '中间补偿抛出事件',
'Signal Intermediate Catch Event': '中间信号捕获事件',
'Signal Intermediate Throw Event': '中间信号抛出事件',
'Message End Event': '结束消息事件',
'Escalation End Event': '结束升级事件',
'Error End Event': '结束错误事件',
'Cancel End Event': '结束取消事件',
'Compensation End Event': '结束补偿事件',
'Signal End Event': '结束信号事件',
'Terminate End Event': '终止边界事件',
'Message Boundary Event': '消息边界事件',
'Message Boundary Event (non-interrupting)': '消息边界事件 (非中断)',
'Timer Boundary Event': '定时边界事件',
'Timer Boundary Event (non-interrupting)': '定时边界事件 (非中断)',
'Escalation Boundary Event': '升级边界事件',
'Escalation Boundary Event (non-interrupting)': '升级边界事件 (非中断)',
'Conditional Boundary Event': '条件边界事件',
'Conditional Boundary Event (non-interrupting)': '条件边界事件 (非中断)',
'Error Boundary Event': '错误边界事件',
'Cancel Boundary Event': '取消边界事件',
'Signal Boundary Event': '信号边界事件',
'Signal Boundary Event (non-interrupting)': '信号边界事件 (非中断)',
'Compensation Boundary Event': '补偿边界事件',
'Exclusive Gateway': '独占网关',
'Parallel Gateway': '并行网关',
'Inclusive Gateway': '包容网关',
'Complex Gateway': '复杂网关',
'Event based Gateway': '事件网关',
'Transaction': '事务',
'Sub Process': '子流程',
'Event Sub Process': '事件子流程',
'Collapsed Pool': '折叠池',
'Expanded Pool': '展开池',
// Errors
'no parent for {element} in {parent}': '在 {element} 中没有父元素 {parent}',
'no shape type specified': '未指定形状类型',
'flow elements must be children of pools/participants': '元素必须是池/参与者的子级',
'out of bounds release': '越界释放',
'more than {count} child lanes': '超过 {count} 条通道',
'element required': '需要元素',
'diagram not part of bpmn:Definitions': '图表不是 bpmn:Definitions 的一部分',
'no diagram to display': '没有要显示的图表',
'no process or collaboration to display': '没有可显示的流程或协作',
'element {element} referenced by {referenced}#{property} not yet drawn': '元素 {element} 的引用 {referenced}#{property} 尚未绘制',
'already rendered {element}': '{element} 已呈现',
'failed to import {element}': '{element} 导入失败',
'Create StartEvent':'创建开始节点',
'Create Intermediate/Boundary Event':'创建中间/边界事件',
'Create EndEvent':'创建结束节点',
'Create Gateway':'创建网关',
'Create Task':'创建任务',
'Append ExclusiveGateway':'网关',
'Append UserTask':'用户任务',
'Append EndEvent':'结束节点'
};

View File

@ -0,0 +1,73 @@
<template>
<el-dialog title="导入BPMN" :visible.sync="dialogFormVisible" :before-close="close">
<el-form>
<editor v-model="xmlData" @init="editorInit" lang="xml" theme="chrome" width="98%" height="calc(60vh - 100px)"></editor>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="commitForm()"> </el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: "ImportDialog",
data() {
return {
activeNames: ['1'],
formLabelWidth: "200px",
xmlData: "类",
value: "类",
options: [
{
value: "类",
label: "类"
}
]
}
},
components: {
editor: require('vue2-ace-editor'),
},
props: {
dialogVisibleBool:{
type: Boolean,
required: true
}
},
computed:{
dialogFormVisible:{
get(){
this.xmlData = null
return this.dialogVisibleBool
}
}
},
mounted() {
this.xmlData = null
},
methods: {
editorInit: function (editor) {
require('brace/mode/xml')
require('brace/theme/chrome')
editor.setOptions({
fontSize: "15px"
})
editor.getSession().setUseWrapMode(true);
},
commitForm(){
this.$emit('closeShowXmlDialog', this.xmlData);
},
close(){
this.$emit('closeShowXmlDialog', null);
}
}
}
</script>
<style scoped>
/deep/.el-dialog > .el-dialog__header{
padding: 24px 20px
}
</style>

View File

@ -0,0 +1,318 @@
<template>
<div>
<!-- 流程属性 -->
<el-collapse v-model="activeNames" accordion>
<el-collapse-item name="1">
<template slot="title">
<div class="title">
<i class="header-icon el-icon-setting"></i>
<span>基本设置</span>
</div>
</template>
<div>
<el-form label-position="right" label-width="70px">
<el-form-item label="节点类型">
<el-input v-model="localFormData.type" disabled></el-input>
</el-form-item>
<el-form-item label="ID">
<el-input v-model="localFormData.id" @input="updateId"></el-input>
</el-form-item>
<el-form-item label="名称">
<el-input v-model="localFormData.name " @input="updateName"></el-input>
</el-form-item>
<el-form-item v-if="localFormData.type==='bpmn:SequenceFlow'" label="分支条件">
<el-input v-model="localFormData.sequenceFlow" @input="updateSequenceFlow"></el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template slot="title">
<div class="title">
<i class="header-icon el-icon-setting"></i>
<span>执行监听</span>
</div>
</template>
<div>
<div style="margin: 10px 3%;float: right">
<el-button size="mini" plain @click="showEventDialogMethod">添加</el-button>
</div>
<el-table
border :data="listenerTable"
style="width: 93%;margin: 0 auto">
<el-table-column align="center" prop="event"
label="事件">
</el-table-column>
<el-table-column align="center" prop="type" :show-overflow-tooltip="true"
label="类型">
</el-table-column>
<el-table-column align="center" prop="class" :show-overflow-tooltip="true"
label="实现">
</el-table-column>
<el-table-column align="center"
label="操作">
<template slot-scope="scope">
<div>
<i class="el-icon-delete" @click="deleteEvent(scope.$index)"></i>
</div>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
<el-collapse-item name="3" v-if="localFormData.type=='bpmn:UserTask'" >
<template slot="title">
<div class="title">
<i class="header-icon el-icon-setting"></i>
<span>权限设置</span>
</div>
</template>
<div>
<el-form style="margin-top: 25px" label-position="right" label-width="90px">
<el-form-item label="用户类型:">
<el-select v-model="localFormData.userType" placeholder="请选择" @change="changeUserType" style="width: 90%">
<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="localFormData.userType === 'assignee'">
<el-input placeholder="请输入内容" v-model="localFormData.assignee" @input="(value) => addUser({assignee: value})" style="width: 90%">
<template slot="append">
<div class="icon-div">
<i class="el-icon-user" @click="showUserDialogMethod" />
<!-- <div role="separator" class="ant-divider ant-divider-vertical"></div>-->
<!-- <i class="el-icon-c-scale-to-original" />-->
</div>
</template>
</el-input>
</el-form-item>
<el-form-item label="候选人员" v-else-if="localFormData.userType === 'candidateUsers'">
<el-input placeholder="请输入内容" v-model="localFormData.candidateUsers" @input="(value) => addUser({candidateUsers: value})" style="width: 90%">
<template slot="append">
<div class="icon-div">
<i class="el-icon-user" @click="showUserDialogMethod" />
<!-- <div role="separator" class="ant-divider ant-divider-vertical"></div>-->
<!-- <i class="el-icon-c-scale-to-original" />-->
</div>
</template>
</el-input>
</el-form-item>
<el-form-item label="角色/岗位" v-else-if="localFormData.userType === 'candidateGroups'">
<el-input placeholder="请输入内容" v-model="localFormData.candidateGroups" @input="(value) => addUser({candidateGroups: value})" style="width: 90%">
<template slot="append">
<div class="icon-div">
<i class="el-icon-user" @click="showUserDialogMethod" />
<!-- <div role="separator" class="ant-divider ant-divider-vertical"></div>-->
<!-- <i class="el-icon-c-scale-to-original" />-->
</div>
</template>
</el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
</el-collapse>
<EventListenerDialog @commitEventForm="commitEventForm"
:dialogFormVisibleBool="showEventDialog"
:formData="eventFormData" :nodeElement="nodeElement" :modeler="modeler" :listenerTable="listenerTable"></EventListenerDialog>
<UserSelectDialog @commitUserForm="commitUserForm"
:type="localFormData.userType"
:dialogFormVisibleBool="showUserDialog"
:formData="userFormData"
:nodeElement="nodeElement"
:modeler="modeler"></UserSelectDialog>
</div>
</template>
<script>
import EventListenerDialog from "./dialog/EventListenerDialog"
import UserSelectDialog from "./dialog/UserSelectDialog"
export default {
name: "NodePropertyPanel",
data() {
return {
activeNames: ['1'],
listenerTable: [],
showEventDialog: false,
showUserDialog: false,
eventFormData: {},
userFormData: {}
}
},
components: {
EventListenerDialog,UserSelectDialog
},
props: {
modeler: {
type: Object,
required: true
},
nodeElement: {
type: Object,
required: true
},
formData:{
type: Object,
required: true
}
},
computed:{
localFormData:{
get(){
return this.formData
}
}
},
mounted() {
const that = this
if (this.nodeElement.businessObject
&& this.nodeElement.businessObject.extensionElements
&& this.nodeElement.businessObject.extensionElements.values) {
// xml
that.listenerTable = this.nodeElement.businessObject.extensionElements.values.filter(
(item) => {
if (item.$type === 'activiti:ExecutionListener') {
item.type = Object.keys(item)[1]
item.class = item[Object.keys(item)[1]]
return true;
}
}
)
}
},
watch:{
nodeElement:{
handler(){
if(this.nodeElement.type==="bpmn:StartEvent"){
this.updateName("开始");
}
if(this.nodeElement.type==="bpmn:EndEvent"){
this.updateName("结束");
}
}
}
},
methods: {
updateProperties(properties){
this.modeler.get("modeling").updateProperties(this.nodeElement, properties);
},
updateId(name) {
this.updateProperties({id: name});
},
updateName(name) {
this.updateProperties({name: name});
},
changeUserType() {
},
updateSequenceFlow(val){
let newCondition = this.modeler.get("moddle").create('bpmn:FormalExpression', {
body: val
});
this.updateProperties({conditionExpression:newCondition});
},
addUser(properties){
this.updateProperties(properties);
Object.assign(properties, {
userType: Object.keys(properties)[0]
});
console.log(properties)
this.$emit('modifyFormData',properties);
},
showEventDialogMethod(){
this.showEventDialog = true;
this.eventFormData= {
event:"start",
type:"class"
}
},
showUserDialogMethod(){
this.showUserDialog = true;
this.eventFormData= {
event:"start",
type:"class"
}
},
deleteEvent(index){
const data = this.listenerTable[index]
if (this.nodeElement.businessObject
&& this.nodeElement.businessObject.extensionElements
&& this.nodeElement.businessObject.extensionElements.values) {
// classclass
const filterArr = this.nodeElement.businessObject.extensionElements.values.filter(
(item) => !(item.$type === 'activiti:ExecutionListener' && item.event === data.event && data.class === item[data.type])
)
//
let extensionElements = this.modeler.get("bpmnFactory").create("bpmn:ExtensionElements", {values: filterArr});
this.modeler.get("modeling").updateProperties(this.nodeElement, {extensionElements});
}
this.listenerTable.splice(index, 1);
},
commitEventForm(from){
this.showEventDialog = false
if (from != null) {
//
this.listenerTable.push(from)
}
},
commitUserForm(from){
this.showUserDialog = false
if (from != null) {
console.log(from)
//
// this.listenerTable.push(from)
}
}
}
}
</script>
<style scoped>
.title span{
font-weight: bold;
margin-left: 5px;
}
/deep/.el-select .el-input .el-select__caret{
margin-top: 5px;
}
/deep/.el-input__icon{
height: 20px !important;
line-height: 20px !important;
}
/deep/.el-input-group__append{
padding: 0 5px !important;
}
.icon-div{
font-size: 1.2em;
font-weight: bold !important;
width: 100%;
}
.icon-div i{
padding: 5px;
}
.icon-div i:hover{
cursor:pointer;
}
.ant-divider-vertical {
position: relative;
top: -0.08em;
display: inline-block;
width: 2px;
height: 0.9em;
/*margin: 0 8px;*/
vertical-align: middle;
}
.ant-divider {
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
/*font-feature-settings: "tnum";*/
background: #e8e8e8;
}
</style>

View File

@ -0,0 +1,244 @@
<template>
<div>
<el-collapse v-model="activeNames" accordion>
<el-collapse-item name="1">
<template slot="title">
<div class="title">
<i class="header-icon el-icon-setting"></i>
<span>基本设置</span>
</div>
</template>
<div>
<el-form label-position="right" label-width="70px">
<el-form-item label="流程ID">
<el-input v-model="localProcessData.key" @input="updateId"></el-input>
</el-form-item>
<el-form-item label="流程名称">
<el-input v-model="localProcessData.name" @input="updateName"></el-input>
</el-form-item>
<el-form-item label="流程描述">
<el-input v-model="localProcessData.description" @input="updateDesc"></el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template slot="title">
<div class="title">
<i class="header-icon el-icon-setting"></i>
<span>执行监听</span>
</div>
</template>
<div>
<div style="margin: 10px 3%;float: right">
<el-button size="mini" plain @click="showEventDialogMethod">添加</el-button>
</div>
<el-table
border :data="listenerTable"
style="width: 93%;margin: 0 auto">
<el-table-column align="center" prop="event"
label="事件">
</el-table-column>
<el-table-column align="center" prop="type" :show-overflow-tooltip="true"
label="类型">
</el-table-column>
<el-table-column align="center" prop="class" :show-overflow-tooltip="true"
label="实现">
</el-table-column>
<el-table-column align="center"
label="操作">
<template slot-scope="scope">
<div>
<i class="el-icon-delete" @click="deleteEvent(scope.$index)"></i>
</div>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
<el-collapse-item name="3">
<template slot="title">
<div class="title">
<i class="header-icon el-icon-setting"></i>
<span>全局监听</span>
</div>
</template>
<div>
<div style="margin: 10px 3%;float: right">
<el-button size="mini" plain @click="showGlobalDialogMethod">添加</el-button>
</div>
<el-table
border :data="globalFormTable"
style="width: 93%;margin: 0 auto">
<el-table-column align="center" prop="class" :show-overflow-tooltip="true"
label="值">
</el-table-column>
<el-table-column align="center"
label="操作">
<template slot-scope="scope">
<div>
<i class="el-icon-delete" @click="deleteGlobalEvent(scope.$index)"></i>
</div>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
</el-collapse>
<GlobalEventListenerDialog @commitGlobalForm="commitGlobalForm"
:dialogFormVisibleBool="showGlobalDialog"
:formData="globalFormData" :element="element" :modeler="modeler" :globalFormTable="globalFormTable"></GlobalEventListenerDialog>
<EventListenerDialog @commitEventForm="commitEventForm"
:dialogFormVisibleBool="showEventDialog"
:formData="eventFormData" :nodeElement="element" :modeler="modeler" :listenerTable="listenerTable"></EventListenerDialog>
</div>
</template>
<script>
import GlobalEventListenerDialog from "./dialog/GlobalEventListenerDialog"
import EventListenerDialog from "./dialog/EventListenerDialog"
export default {
name: "ProcessProperty",
data() {
return {
activeNames: ['1'],
showGlobalDialog: false,
showEventDialog: false,
listenerTable: [
],
globalFormTable: [
],
globalFormData: {
type:"start"
},
eventFormData: {
event:"class",
type:"start"
},
localProcessData:this.processData
}
},
components: {
GlobalEventListenerDialog,EventListenerDialog
},
props: {
processData: {
type: Object,
required: true
},
modeler: {
type: Object,
required: true
},
element: {
type: Object,
required: true
}
},
mounted() {
var that = this
if (this.element.businessObject
&& this.element.businessObject.extensionElements
&& this.element.businessObject.extensionElements.values) {
that.globalFormTable = this.element.businessObject.extensionElements.values.filter(
(item) => item.$type === 'activiti:EventListener'
)
that.listenerTable = this.element.businessObject.extensionElements.values.filter(
(item) => {
if (item.$type === 'activiti:ExecutionListener') {
item.type = Object.keys(item)[1]
item.class = item[Object.keys(item)[1]]
return true;
}
}
)
}
},
methods: {
deleteGlobalEvent(index){
const data = this.globalFormTable[index]
if (this.element.businessObject
&& this.element.businessObject.extensionElements
&& this.element.businessObject.extensionElements.values) {
// classclass
const filterArr = this.element.businessObject.extensionElements.values.filter(
(item) => !(item.$type === 'activiti:EventListener' && item.class === data.class)
)
//
let extensionElements = this.modeler.get("bpmnFactory").create("bpmn:ExtensionElements", {values: filterArr});
this.modeler.get("modeling").updateProperties(this.element, {extensionElements});
}
this.globalFormTable.splice(index, 1);
},
deleteEvent(index){
const data = this.listenerTable[index]
if (this.element.businessObject
&& this.element.businessObject.extensionElements
&& this.element.businessObject.extensionElements.values) {
// classclass
const filterArr = this.element.businessObject.extensionElements.values.filter(
(item) => !(item.$type === 'activiti:ExecutionListener' && item.event === data.event && data.class === item[data.type])
)
//
let extensionElements = this.modeler.get("bpmnFactory").create("bpmn:ExtensionElements", {values: filterArr});
this.modeler.get("modeling").updateProperties(this.element, {extensionElements});
}
this.listenerTable.splice(index, 1);
},
showGlobalDialogMethod(){
this.showGlobalDialog = true;
this.globalFormData={
type:"class"
}
},
showEventDialogMethod(){
this.showEventDialog = true;
this.eventFormData= {
event:"start",
type:"class"
}
},
updateId(name) {
this.modeler.get("modeling").updateProperties(this.element, {id: name});
},
updateName(name) {
this.modeler.get("modeling").updateProperties(this.element, {name: name});
},
updateDesc(name) {
let doc = this.modeler.get("bpmnFactory").create("bpmn:Documentation", {text: name});
this.modeler.get("modeling").updateProperties(this.element, {documentation: [doc]});
console.log( this.modeler.get("modeling"))
},
commitEventForm(from){
this.showEventDialog = false
if (from != null) {
this.listenerTable.push(from)
}
},
commitGlobalForm(from){
this.showGlobalDialog = false
if (from != null) {
this.globalFormTable.push(from)
}
}
}
}
</script>
<style scoped>
.title span{
font-weight: bold;
margin-left: 5px;
}
.el-tooltip__popper{
font-size: 14px;
max-width:50%;
backgroud: #68859a !important; /*背景色  !important优先级*/
}
</style>

View File

@ -0,0 +1,240 @@
<template>
<div>
<!-- 开始节点 -->
<el-collapse v-if="localFormData.type=='bpmn:StartEvent'" v-model="activeNames" accordion>
<el-collapse-item name="1">
<template slot="title">
<div class="title">
<i class="header-icon el-icon-setting"></i>
<span>基本设置</span>
</div>
</template>
<div>
<el-form label-position="right" label-width="70px">
<el-form-item label="节点类型">
<el-input v-model="localFormData.type" disabled></el-input>
</el-form-item>
<el-form-item label="ID">
<el-input v-model="localFormData.id" @input="updateId"></el-input>
</el-form-item>
<el-form-item label="名称">
<el-input v-model="localFormData.name " @input="updateName"></el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template slot="title">
<div class="title">
<i class="header-icon el-icon-setting"></i>
<span>执行监听</span>
</div>
</template>
<div>
<div style="margin: 10px 3%;float: right">
<el-button size="mini" plain @click="showEventDialogMethod">添加</el-button>
</div>
<el-table
border :data="listenerTable"
style="width: 93%;margin: 0 auto">
<el-table-column align="center" prop="event"
label="事件">
</el-table-column>
<el-table-column align="center" prop="type" :show-overflow-tooltip="true"
label="类型">
</el-table-column>
<el-table-column align="center" prop="class" :show-overflow-tooltip="true"
label="实现">
</el-table-column>
<el-table-column align="center"
label="操作">
<template slot-scope="scope">
<div>
<i class="el-icon-delete" @click="deleteEvent(scope.$index)"></i>
</div>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
</el-collapse>
<EventListenerDialog @commitEventForm="commitEventForm"
:dialogFormVisibleBool="showEventDialog"
:formData="eventFormData" :nodeElement="nodeElement" :modeler="modeler" :listenerTable="listenerTable"></EventListenerDialog>
</div>
</template>
<script>
import EventListenerDialog from "./dialog/EventListenerDialog"
import GlobalEventListenerDialog from "./dialog/GlobalEventListenerDialog";
export default {
name: "NodePropertyPanel",
data() {
return {
activeNames: ['1'],
input3: 1,
listenerTable: [],
showEventDialog: false,
eventFormData: {},
bpmnData: {
assignees: [{
value: "${assignee}",
label: "表达式"
}, {
value: "1001",
label: "张三"
}, {
value: "1002",
label: "李四"
}, {
value: "1003",
label: "王五"
}],
candidateUsers:[{
value: "1001",
label: "张三"
}, {
value: "1002",
label: "李四"
}, {
value: "1003",
label: "王五"
}],
roles: [
{
value: "manager",
label: "经理"
},
{
value: "personnel",
label: "人事"
},
{
value: "charge",
label: "主管"
}
]
}
}
},
components: {
EventListenerDialog
},
props: {
modeler: {
type: Object,
required: true
},
nodeElement: {
type: Object,
required: true
},
formData:{
type: Object,
required: true
}
},
computed:{
localFormData:{
get(){
return this.formData
}
}
},
watch:{
nodeElement:{
handler(){
if(this.nodeElement.type==="bpmn:StartEvent"){
this.updateName("开始");
}
if(this.nodeElement.type==="bpmn:EndEvent"){
this.updateName("结束");
}
}
}
},
mounted() {
const that = this
if (this.nodeElement.businessObject
&& this.nodeElement.businessObject.extensionElements
&& this.nodeElement.businessObject.extensionElements.values) {
// xml
that.listenerTable = this.nodeElement.businessObject.extensionElements.values.filter(
(item) => {
if (item.$type === 'activiti:ExecutionListener') {
item.type = Object.keys(item)[1]
item.class = item[Object.keys(item)[1]]
return true;
}
}
)
}
},
methods: {
updateProperties(properties){
console.log(this.nodeElement)
console.log(properties)
this.modeler.get("modeling").updateProperties(this.nodeElement, properties);
},
updateId(name) {
this.updateProperties({id: name});
},
updateName(name) {
this.updateProperties({name: name});
},
changeUserType() {
},
updateSequenceFlow(val){
let newCondition = this.modeler.get("moddle").create('bpmn:FormalExpression', {
body: val
});
this.updateProperties({conditionExpression:newCondition});
},
addUser(properties){
this.updateProperties(properties);
Object.assign(properties, {
userType: Object.keys(properties)[0]
});
console.log(properties)
this.$emit('modifyFormData',properties);
},
showEventDialogMethod(){
this.showEventDialog = true;
this.eventFormData= {
event:"start",
type:"class"
}
},
deleteEvent(index){
const data = this.listenerTable[index]
if (this.nodeElement.businessObject
&& this.nodeElement.businessObject.extensionElements
&& this.nodeElement.businessObject.extensionElements.values) {
// classclass
const filterArr = this.nodeElement.businessObject.extensionElements.values.filter(
(item) => !(item.$type === 'activiti:ExecutionListener' && item.event === data.event && data.class === item[data.type])
)
//
let extensionElements = this.modeler.get("bpmnFactory").create("bpmn:ExtensionElements", {values: filterArr});
this.modeler.get("modeling").updateProperties(this.nodeElement, {extensionElements});
}
this.listenerTable.splice(index, 1);
},
commitEventForm(from){
this.showEventDialog = false
if (from != null) {
this.listenerTable.push(from)
}
}
}
}
</script>
<style scoped>
.title span{
font-weight: bold;
margin-left: 5px;
}
</style>

View File

@ -0,0 +1,117 @@
<template>
<el-dialog title="自定义全局监听器" :visible.sync="dialogFormVisible" :before-close="close">
<el-form :model="form">
<el-form-item label="事件类型:" :label-width="formLabelWidth">
<el-select v-model="form.type" placeholder="选择">
<el-option label="类" value="class"></el-option>
<el-option label="表达式" value="expression"></el-option>
<el-option label="代理表达式" value="delegateExpression"></el-option>
</el-select>
</el-form-item>
<el-form-item label="事件类型:" :label-width="formLabelWidth">
<el-select v-model="form.event" placeholder="选择">
<el-option label="start" value="start"></el-option>
<el-option label="take" value="take"></el-option>
<el-option label="end" value="end"></el-option>
</el-select>
</el-form-item>
<el-form-item label="值:" :label-width="formLabelWidth">
<el-input v-model="form.class" style="width: 50%"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="commitForm()"> </el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: "EventListenerDialog",
data() {
return {
activeNames: ['1'],
formLabelWidth: "200px",
value: "类",
options: [
{
value: "类",
label: "类"
}
]
}
},
props: {
formData:{
type: Object,
required: true
},
dialogFormVisibleBool:{
type: Boolean,
required: false
},
modeler: {
type: Object,
required: false
},
nodeElement:{
type: Object,
required: false
},
listenerTable:{
type: Array,
required: false
}
},
computed:{
form:{
get(){
return this.formData;
}
},
dialogFormVisible:{
get(){
return this.dialogFormVisibleBool
}
}
},
methods: {
commitForm(){
const from = this.form
const filterArr = this.listenerTable.filter(
(item) => (item.event === from.event && from.type === item.type && from.class === item.class)
)
if (filterArr.length !== 0) {
this.$emit('commitEventForm', null);
return
}
let data = {}
data[from.type] = from.class
data['event'] = from.event
if (this.nodeElement.businessObject
&& this.nodeElement.businessObject.extensionElements
&& this.nodeElement.businessObject.extensionElements.values) {
let eventListener = this.modeler.get("bpmnFactory").create("activiti:ExecutionListener", data);
let extensionElements = this.modeler.get("bpmnFactory").create("bpmn:ExtensionElements",
{values: this.nodeElement.businessObject.extensionElements.values.concat(eventListener),});
this.modeler.get("modeling").updateProperties(this.nodeElement, {extensionElements});
} else {
let eventListener = this.modeler.get("bpmnFactory").create("activiti:ExecutionListener", data);
let extensionElements = this.modeler.get("bpmnFactory").create("bpmn:ExtensionElements", {values: [eventListener],});
this.modeler.get("modeling").updateProperties(this.nodeElement, {extensionElements});
}
this.$emit('commitEventForm', this.form);
},
close(){
this.$emit('commitEventForm', null);
}
}
}
</script>
<style scoped>
/deep/.el-dialog > .el-dialog__header{
padding: 24px 20px
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<el-dialog title="自定义全局监听器" :visible.sync="dialogFormVisible" :before-close="close">
<el-form :model="form">
<el-form-item label="监听类型:" :label-width="formLabelWidth">
<el-select v-model="form.type" placeholder="选择">
<el-option label="类" value="class"></el-option>
</el-select>
</el-form-item>
<el-form-item label="值:" :label-width="formLabelWidth">
<el-input v-model="form.class" style="width: 50%"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="commitForm()"> </el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: "GlobalEventListenerDialog",
data() {
return {
activeNames: ['1'],
listenerTable: [],
formLabelWidth: "200px",
value: "类",
options: [
{
value: "类",
label: "类"
}
]
}
},
props: {
formData:{
type: Object,
required: true
},
dialogFormVisibleBool:{
type: Boolean,
required: false
},
modeler: {
type: Object,
required: false
},
element:{
type: Object,
required: false
},
globalFormTable:{
type: Array,
required: false
}
},
computed:{
form:{
get(){
return this.formData;
}
},
dialogFormVisible:{
get(){
return this.dialogFormVisibleBool
}
}
},
methods: {
commitForm(){
const from = this.formData;
// class 退
const filterArr = this.globalFormTable.filter(
(item) => item.class === from.class
)
if (filterArr.length !== 0) {
this.$emit('commitGlobalForm', null);
return
}
if (this.element.businessObject
&& this.element.businessObject.extensionElements
&& this.element.businessObject.extensionElements.values) {
let eventListener = this.modeler.get("bpmnFactory").create("activiti:EventListener", from);
let extensionElements = this.modeler.get("bpmnFactory").create("bpmn:ExtensionElements",
{
values: this.element.businessObject.extensionElements.values.concat(eventListener),
}
);
this.modeler.get("modeling").updateProperties(this.element, {extensionElements});
} else {
let eventListener = this.modeler.get("bpmnFactory").create("activiti:EventListener", from);
let extensionElements = this.modeler.get("bpmnFactory").create("bpmn:ExtensionElements",
{
values: [eventListener],
}
);
this.modeler.get("modeling").updateProperties(this.element, {extensionElements});
}
this.$emit('commitGlobalForm', this.form);
},
close(){
this.$emit('commitGlobalForm', null);
}
}
}
</script>
<style scoped>
/deep/.el-dialog > .el-dialog__header{
padding: 24px 20px
}
</style>

View File

@ -0,0 +1,173 @@
<template>
<el-dialog width="60%" title="用户选择" :visible.sync="dialogFormVisible" :before-close="close">
<el-form>
<el-row style="width: 93%;margin: 0 auto">
<el-col :span="6">
<el-form-item label="账户:" >
<el-input v-model="searchData.account" style="width:80%" ></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="昵称:">
<el-input v-model="searchData.name" style="width:80%" ></el-input>
</el-form-item>
</el-col>
<el-col :span="5">
<el-button type="primary">查询</el-button>
<el-button >重置</el-button>
</el-col>
</el-row>
<el-form-item>
<el-table v-show="type === 'assignee'"
border :data="options"
style="width: 93%;margin: 0 auto">
<el-table-column align="center" prop="account"
label="账户">
</el-table-column>
<el-table-column align="center" prop="name" :show-overflow-tooltip="true"
label="昵称">
</el-table-column>
<el-table-column align="center"
label="操作">
<template slot-scope="scope">
<div>
<el-button type="primary" @click="commitForm(scope.$index)">选择</el-button>
</div>
</template>
</el-table-column>
</el-table>
<el-table v-show="type === 'candidateUsers'"
border :data="options" @selection-change="handleSelectionChange"
style="width: 93%;margin: 0 auto">
<!--批量选择用户的时候需要复选框-->
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column align="center" prop="account"
label="账户">
</el-table-column>
<el-table-column align="center" prop="name" :show-overflow-tooltip="true"
label="昵称">
</el-table-column>
</el-table>
<el-pagination style="float: right;margin:20px 2.7% 0 0"
background
layout="prev, pager, next"
:total="1000">
</el-pagination>
</el-form-item>
</el-form>
<!--批量选择用户的时候需要确认按钮-->
<div slot="footer" class="dialog-footer" v-if="type === 'candidateUsers'">
<el-button type="primary" @click="commitForm()"> </el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: "UserSelectDialog",
data() {
return {
activeNames: ['1'],
formLabelWidth: "200px",
value: "类",
searchData: {},
multipleSelection: [],
options: [
{
name: "张三",
account: "admin1"
},
{
name: "李四",
account: "admin2"
},
{
name: "王五",
account: "admin3"
}
]
}
},
props: {
formData:{
type: Object,
required: true
},
type: {
type: String,
required: false
},
dialogFormVisibleBool:{
type: Boolean,
required: false
},
modeler: {
type: Object,
required: false
},
nodeElement:{
type: Object,
required: false
}
},
computed:{
form:{
get(){
return this.formData;
}
},
dialogFormVisible:{
get(){
return this.dialogFormVisibleBool
}
}
},
watch:{
type:{
handler(){
console.log(this.type)
}
}
},
mounted() {
},
methods: {
updateProperties(properties){
this.modeler.get("modeling").updateProperties(this.nodeElement, properties);
},
commitForm(index){
if (this.type === 'assignee') {
this.modeler.get("modeling").updateProperties(this.nodeElement, {
assignee: this.options[index].account
});
this.$emit('commitUserForm', this.options[index]);
} else if(this.type === 'candidateUsers'){
let updateStr = null
let candidateUsersStr = this.multipleSelection.map(item => item.account);
if (candidateUsersStr.length > 0) {
updateStr = candidateUsersStr.join(",")
}
this.modeler.get("modeling").updateProperties(this.nodeElement, {
candidateUsers: updateStr
});
this.$emit('commitUserForm', null);
}
},
close(){
this.$emit('commitUserForm', null);
},
handleSelectionChange(val) {
this.multipleSelection = val;
}
}
}
</script>
<style scoped>
/deep/.el-dialog > .el-dialog__header{
padding: 24px 20px
}
</style>

View File

@ -0,0 +1,220 @@
<template>
<div class="bpmn-panel">
<el-container>
<!-- <el-header height="45px">-->
<!-- <div class="config-tab" :class="{active: configTab=='node'}" @click="handleConfigSelect('node')">节点属性</div>-->
<!-- <div class="config-tab" :class="{active: configTab=='process'}" @click="handleConfigSelect('process')">流程属性</div>-->
<!-- </el-header>-->
<el-main>
<!-- 节点的控制面板-->
<node-property-panel v-if="configTab==='node'" :modeler="modeler" @modifyConfigTab="modifyConfigTab"
:nodeElement="nodeElement" :formData="formData" @modifyFormData="modifyFormData"></node-property-panel>
<start-event-node-property-panel v-if="configTab==='start-node'" :modeler="modeler" @modifyConfigTab="modifyConfigTab"
:nodeElement="nodeElement" :formData="formData" @modifyFormData="modifyFormData"></start-event-node-property-panel>
<!-- 流程的控制面板-->
<process-property-panel v-if="configTab==='process'" :modeler="modeler" :process-data="process"
:element="element"></process-property-panel>
</el-main>
</el-container>
</div>
</template>
<script>
import NodePropertyPanel from "./NodePropertyPanel";
import ProcessPropertyPanel from "./ProcessPropertyPanel";
import StartEventNodePropertyPanel from "./StartEventNodePropertyPanel";
export default {
name: "index",
data() {
return {
configTab: 'process',
panelIndex: 8,
element: {},
nodeElement: {},
formData: {}
}
},
props: {
modeler: {
type: Object,
required: true
},
process: {
type: Object,
required: true
}
},
mounted() {
this.handleModeler();
},
methods: {
handleConfigSelect(value) {
this.configTab = value;
},
handleModeler() {
const _this = this;
this.modeler.on("root.added", e => {
let element = e.element;
if (this.isImplicitRoot(element)) {
return;
}
this.element = element;
});
//
this.modeler.on("commandStack.changed", () => {
_this.modeler.saveXML({format: true}, function (err, xml) {
_this.$emit('updateXml', xml)
});
})
//
this.modeler.on("selection.changed", e => {
const element = e.newSelection[0];
if (!element) {
return;
}
console.log("selection.changed: element", element)
this.modifyConfigTab(element);
this.handleFormData(element);
this.changeFormData(element)
})
this.modeler.on("element.changed", e => {
const {element} = e;
if (!element) {
return;
}
console.log("element.changed: element", element)
this.handleFormData(element);
this.changeFormData(element)
});
this.modeler.on("element.click", e => {
const {element} = e;
console.log("click: element", element)
this.handleFormData(element)
if (element.type == this.modeler._definitions.rootElements[0].$type) {
this.modifyConfigTab(element)
} else {
this.modifyConfigTab(element)
this.changeFormData(element)
}
})
},
changeFormData(element){
const _this = this;
if(element.type === "bpmn:UserTask"){
let _businessObject = element.businessObject;
if(_businessObject.assignee){
_this.formData.userType = "assignee";
_this.formData.assignee = _businessObject.assignee;
}
if (_this.formData.assignee) {
_this.formData.userType = "assignee"
}
if (_this.formData.candidateGroups) {
_this.formData.userType = "candidateGroups"
}
if (_this.formData.candidateUsers) {
_this.formData.userType = "candidateUsers"
}
}
},
isImplicitRoot(element) {
return element.id === '__implicitroot';
},
modifyConfigTab(element) {
let configTab = 'node'
if (element !== undefined
&& element.type !== undefined
&& element.type==="bpmn:Process") {
configTab = 'process'
}
if (element !== undefined
&& element.type !== undefined
&& element.type==='bpmn:StartEvent') {
configTab = 'start-node'
}
console.log("configTab:" + configTab)
this.configTab = configTab
},
handleFormData(element) {
if (!element.id) {
return;
}
let businessObject = element.businessObject;
this.formData = {
type: element.type,
id: businessObject.id,
name: businessObject.name,
userType: businessObject.$attrs.userType ? businessObject.$attrs.userType : businessObject.userType,
assignee: businessObject.$attrs.assignee ? businessObject.$attrs.assignee : businessObject.assignee,
candidateGroups: businessObject.$attrs.candidateGroups ? businessObject.$attrs.candidateGroups : businessObject.candidateGroups,
candidateUsers: businessObject.$attrs.candidateUsers ? businessObject.$attrs.candidateUsers : businessObject.candidateUsers,
sequenceFlow: businessObject.conditionExpression ? businessObject.conditionExpression.body : '',
extensionElements: businessObject.extensionElements ? businessObject.extensionElements.values : null
}
console.log("formData",this.formData)
this.nodeElement = element;
},
modifyFormData(data){
this.formData.assignee = data.assignee;
this.formData.userType = data.userType;
}
},
components: {
NodePropertyPanel, ProcessPropertyPanel, StartEventNodePropertyPanel
}
}
</script>
<style scoped>
.el-main{
padding: 0 !important;
}
/deep/.el-collapse .el-input__inner{
height: 30px !important;
line-height: 30px !important;
}
/deep/.el-collapse .el-form-item__content{
height: 30px !important;
line-height: 30px !important;
}
/deep/.el-collapse .el-form-item__label{
height: 30px !important;
line-height: 30px !important;
}
/deep/.el-collapse .el-form-item__label{
font-size: 12px;
}
/deep/.el-collapse .el-form{
margin-top: 10px;
}
/deep/.el-collapse .is-active{
border-bottom: 1px solid #EBEEF5
}
.bpmn-panel {
/*width: 350px;*/
border: 1px solid #eeeeee;
padding: 0 5px;
}
.el-header {
border-bottom: solid 2px #e4e7ed;
padding: 0;
}
.config-tab {
height: 43px;
line-height: 43px;
display: inline-block;
width: 50%;
text-align: center;
font-size: 14px;
font-weight: 500;
position: relative;
cursor: pointer;
}
.config-tab.active {
border-bottom: solid 2px #409EFF;
}
</style>

View File

@ -98,6 +98,19 @@ export const constantRoutes = [
}
]
},
{
path: '/bpmn',
component: Layout,
hidden: true,
children: [
{
path: 'model',
component: (resolve) => require(['@/views/bpm/model'], resolve),
name: 'model',
meta: { title: '工作流模型', icon: '' }
}
]
},
{
path: '/job',
component: Layout,

View File

@ -0,0 +1,138 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="模型名字" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入模型名字" clearable style="width: 240px;" size="small"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport"
v-hasPermi="['system:login-log:export']">导出</el-button>
<el-button type="warning" icon="el-icon-download" size="mini" @click="openBpmn"
v-hasPermi="['system:login-log:export']">新建</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="list">
<el-table-column label="ID" align="center" prop="id" />
<el-table-column label="name" align="center" prop="metaInfo" >
<template slot-scope="scope">
<span>{{ scope.row.metaInfo ? JSON.parse(scope.row.metaInfo).name : "" }}</span>
</template>
</el-table-column>
<el-table-column label="description" align="center" prop="metaInfo" >
<template slot-scope="scope">
<span>{{ JSON.parse(scope.row.metaInfo).description }}</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<el-dialog title="新建流程" :visible.sync="showBpmnBool" :before-close="close" :fullscreen="true">
<vue-bpmn product="activiti" @processSave="processSave"></vue-bpmn>
</el-dialog>
</div>
</template>
<script>
import { page } from "@/api/bpm/model";
import VueBpmn from "@/components/bpmn/VueBpmn";
export default {
name: "model",
data() {
return {
//
loading: true,
//
showSearch: true,
showBpmnBool: false,
//
total: 0,
//
list: [],
bpmnXML: null,
//
queryParams: {
pageNo: 1,
pageSize: 10
}
};
},
components: {VueBpmn},
created() {
this.getList();
},
methods: {
/** 查询登录日志列表 */
getList() {
this.loading = true;
page(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
}
);
},
//
statusFormat(row, column) {
return this.selectDictLabel(this.statusOptions, row.status);
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$confirm('是否确认导出所有操作日志数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return exportLoginLog(queryParams);
}).then(response => {
this.downloadExcel(response, '登录日志.xls');
})
},
processSave() {
console.log("processSave")
},
openBpmn() {
this.showBpmnBool = true
},
close() {
this.showBpmnBool = false
}
}
};
</script>
<style>
.el-dialog > .el-dialog__body{
margin: 0;
border: 0;
}
.bpmn-viewer-header{
background: white;
}
.v-modal{
z-index: 2000!important;
}
</style>