微服务架构下Camunda流程引擎的前端的实现(一)- 基于Vue3

枫林橙 2024-10-02 15:03:02 阅读 67

1.简介

后端的实现可以参考文章:https://blog.csdn.net/fly_cheng_zi/article/details/141359478,这一篇主要是前端页面的实现,技术架构就是在node下跑vue3,页面的部分ui是Element UI。主要是两个页面,一个是流程设计页面,一个是流程编辑页面。

2.使用

1.依赖引入

# bpmn组件

npm install --save vue-bpmn

# bpmn依赖

npm install --save bpmn-js

# 属性面板

npm install --save bpmn-js-properties-panel

# 扩展属性

npm install --save camunda-bpmn-moddle #

导入bpmn组件所需

npm install --save raw-loader

2.流程设计页面

定义流程的canvas

<div class="containers" ref="containers">

    <div id="js-canvas" class="canvas" ref="canvas"></div>

    <div id="js-properties-panel"></div>

</div>

引入流程引擎的js配置等

import BpmnModeler from 'bpmn-js/lib/Modeler' // 引入 bpmn-js

import customTranslate from './customTranslate/customTranslate' //汉化

import xmlStr from './xml' //引入默认显示的xml字符串数据

import propertiesPanelModule from 'bpmn-js-properties-panel' //节点编辑

import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'

import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'

创建流程设计器model,将属性挂载上去就完成了

<code> this.containers = this.$refs.containers // 获取到属性ref为“containers”的dom节点

const canvas = this.$refs.canvas // 获取到属性ref为“canvas”的dom节点

this.bpmnModeler = new BpmnModeler({

container: canvas,

//添加控制板

propertiesPanel: {

parent: '#js-properties-panel'

},

//左侧

additionalModules: [

this.customTranslateModule,

// 右边的属性栏

propertiesProviderModule,

propertiesPanelModule

],

moddleExtensions: {

camunda: camundaModdleDescriptor

}

})

this.createNewDiagram()

},

3.完整代码

<template>

<ElButton class="btn" type="primary" @click="saveModelBpmnXml()">保存</ElButton>code>

<ElButton class="btn" type="primary" @click="chooseAgignee()">选择审核人</ElButton>code>

<div class="add_btn" id="btn_center"></div><div class="mask"></divcode>

><div class="window" id="center"code>

><div class="border_add_nav"code>

><div> </div

><div class="border_add_nav_box flex-container"code>

><span class="add_title">选择审核人</span><span class="close_btn">X</span></divcode>

></div

><div class="border_add_main_content"code>

><div class="border_add_main_content_box">code>

<div class="popover">code>

<input

class="search"code>

id="serchName"code>

@keyup.enter="getUserList()"code>

type="text"code>

placeholder="搜索..."code>

/>

<ul class="unstyled list">code>

<li v-for="item in userList" :key="item.id"code>

><a @click="setAgignee(item.username)">{ { item.name }}</a></licode>

>

</ul>

</div>

</div></div

><div class="border_add_btn_box"><div class="pull-right" style="float: right"></div></divcode>

></div>

<div class="containers" ref="containers">code>

<div id="js-canvas" class="canvas" ref="canvas"></div>code>

<div id="js-properties-panel"></div>code>

</div>

</template>

<script>

import { ElButton, ElTag, ElInput } from 'element-plus'

import BpmnModeler from 'bpmn-js/lib/Modeler' // 引入 bpmn-js

import customTranslate from './customTranslate/customTranslate' //汉化

import xmlStr from './xml' //引入默认显示的xml字符串数据

import { useCache } from '@/hooks/web/useCache'

const { wsCache } = useCache()

//右侧属性栏功能

import propertiesPanelModule from 'bpmn-js-properties-panel'

import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'

import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'

import request from '@/config/axios'

import axios from 'axios'

import { config } from '@/config/axios/config'

import $ from 'jquery'

const baseUrl = config.base_url.base

export default {

data() {

return {

bpmnModeler: null,

containers: null,

canvas: null,

customTranslateModule: {

translate: ['value', customTranslate]

},

userList: []

}

},

mounted() {

this.initDiagram()

},

methods: {

//初始化方法

initDiagram() {

axios.defaults.headers.common['Authorization'] = 'Bearer ' + wsCache.get('access_token')

this.containers = this.$refs.containers // 获取到属性ref为“containers”的dom节点

const canvas = this.$refs.canvas // 获取到属性ref为“canvas”的dom节点

this.bpmnModeler = new BpmnModeler({

container: canvas,

//添加控制板

propertiesPanel: {

parent: '#js-properties-panel'

},

//左侧

additionalModules: [

this.customTranslateModule,

// 右边的属性栏

propertiesProviderModule,

propertiesPanelModule

],

moddleExtensions: {

camunda: camundaModdleDescriptor

}

})

this.createNewDiagram()

},

// 注意:必须先加载一个bpmn文件,新建就是加载一个空的bpmn文件,否则不能拖拽节点

createNewDiagram() {

/**

* 获取后台,获取默认的xml

* */

let mr_xml = xmlStr //默认值-xml

let processKey = ''

let processName = ''

var diagramUrl = baseUrl + '/obpm/orange-work-flow/queryById?id=' + this.$route.query.id

axios

.get(diagramUrl)

.then((res) => {

console.log(res.data.data)

if (

res.data.data &&

res.data.data.workFlowData &&

res.data.data.workFlowData.replace(/^\s+|\s+$/g, '') != ''code>

) {

this.openDiagram(res.data.data.workFlowData)

return

}

if (res.data.data) {

processKey = res.data.data.orangeFlowKey

processName = res.data.data.workFlowName

mr_xml = mr_xml.replaceAll('orange_system_t', processKey)

mr_xml = mr_xml.replaceAll('t_name', processName)

this.openDiagram(mr_xml)

return

}

})

.catch((err) => {

console.log(err)

})

// let mr_xml = '' //默认值-xml

this.openDiagram(mr_xml)

},

openDiagram(xml) {

/**

* 导入xml(字符串形式),返回导入结果

* 后续会取消传入回调函数的方式

* 推荐使用async/await或者链式调用

* @param { string } xml 流程图xml字符串

* @param { Promise } callback 回调函数,出错时返回{ warnings,err }

*/

this.bpmnModeler.importXML(xml, function (err) {

if (err) {

// container

// .removeClass('with-diagram')

// .addClass('with-error');

console.error(err)

} else {

// container

// .removeClass('with-error')

// .addClass('with-diagram');

}

})

},

saveModelBpmnXml() {

const id = this.$route.query.id

const toot = this.$router

this.bpmnModeler.saveXML({ format: true }, function (err, xml) {

console.log(xml)

const orangeWorkFlow = {

id: id,

workFlowData: xml

}

var diagramUrl = baseUrl + '/obpm/orange-work-flow/edit'

axios

.post(diagramUrl, orangeWorkFlow)

.then((res) => {

console.log(res.data)

toot.push('/orangeWorkFlow/list')

})

.catch((err) => {

console.log(err)

})

})

},

getUserList() {

var diagramUrl = baseUrl + '/sys/user/list?name=' + $('#serchName').val()code>

axios

.get(diagramUrl)

.then((res) => {

this.userList = res.data.data.records

})

.catch((err) => {

console.log(err)

})

},

chooseAgignee() {

if ($('#camunda-assignee') && $('#camunda-assignee')[0]) {

//获取系统用户

var diagramUrl = baseUrl + '/sys/user/list'

axios

.get(diagramUrl)

.then((res) => {

this.userList = res.data.data.records

console.log(this.userList)

$('#btn_center').css('height', $(document).height())

$('.mask').css('display', 'block')

$('.mask').css('width', $(window).width())

$('.mask').css('height', $(document).height())

popCenterWindow()

})

.catch((err) => {

console.log(err)

})

// $('#camunda-assignee')[0].onclick = function () {

// alert('asad')

// }

} else {

alert('请选择一个用户任务节点再选择审核人')

}

},

setAgignee(username) {

// $('#camunda-assignee').val(username)

let element = document.getElementById('camunda-assignee') // input输入框

console.log(element)

element.value = username // 输入的内容

var event = new Event('input', {

bubbles: true,

cancelable: true

})

element.dispatchEvent(event)

$('.window').hide('slow')

$('.mask').css('display', 'none')

$('#btn_center').css('height', 0)

}

}

}

// $(window).ready(function () {

// $('#btn_center').click(function () {

// $('.mask').css('display', 'block')

// $('.mask').css('width', $(window).width())

// $('.mask').css('height', $(document).height())

// popCenterWindow()

// })

// })

//获取窗口的高度

var windowHeight

//获取窗口的宽度

var windowWidth

//获取弹窗的宽度

var popWidth

//获取弹窗高度

var popHeight

function init() {

windowHeight = $(window).height()

windowWidth = $(window).width()

popHeight = $('.window').height()

popWidth = $('.window').width()

}

//关闭窗口的方法

function closeWindow() {

$('.close_btn').click(function () {

$('.window').hide('slow')

$('.mask').css('display', 'none')

$('#btn_center').css('height', 0)

})

}

//定义弹出居中窗口的方法

function popCenterWindow() {

init()

//计算弹出窗口的左上角X的偏移量

var popX = (windowWidth - popWidth) / 2

// 计算弹出窗口的左上角Y的偏移量为窗口的高度 - 弹窗高度 / 2 + 被卷去的页面的top

var popY = (windowHeight - popHeight) / 2 + $(document).scrollTop()

//设定窗口的位置

$('#center').css('top', popY).css('left', popX).slideToggle('fast')

closeWindow()

}

</script>

<style lang="css">code>

/*左边工具栏以及编辑节点的样式*/

@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 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css';

.containers {

position: absolute;

background-color: #ffffff;

width: 100%;

height: 100%;

display: flex;

}

.canvas {

width: 100%;

height: 100%;

}

.bjs-powered-by {

display: none;

}

.btn {

display: inline-flex;

justify-content: center;

align-items: center;

line-height: 0.6;

height: 30px;

white-space: nowrap;

cursor: pointer;

color: var(--el-button-text-color);

text-align: center;

box-sizing: border-box;

outline: 0;

transition: 0.1s;

font-weight: var(--el-button-font-weight);

-webkit-user-select: none;

user-select: none;

vertical-align: middle;

background-color: #409eff;

border: var(--el-border);

border-color: var(--el-button-border-color);

padding: 8px 15px;

font-size: var(--el-font-size-base);

border-radius: var(--el-border-radius-base);

margin-bottom: 10px;

margin-left: 10px;

}

.btn:hover {

background-color: #333;

color: #fff;

}

.flex-container {

display: -webkit-flex;

display: flex;

-webkit-justify-content: space-between;

justify-content: space-between;

}

.window {

width: 20%;

padding-bottom: 20px;

background-color: #fff;

position: fixed;

display: none;

margin-bottom: 100px;

border: 1px solid #e0dfdf;

}

.add_btn {

height: 0px;

cursor: pointer;

}

.btn_text {

width: 80px;

height: 40px;

line-height: 40px;

text-align: center;

color: #fff;

position: absolute;

top: 50%;

left: 50%;

margin-top: -40px;

margin-left: -20px;

background-color: #fddb54;

}

.border_add_nav {

width: 100%;

border-bottom: 1px solid #e0dfdf;

}

.border_add_nav_box {

width: 90%;

margin: 0 auto;

font-size: 16px;

}

.border_add_main_content {

width: 100%;

margin-left: 5%;

margin-bottom: 3%;

overflow: hidden;

overflow-y: auto;

}

.border_add_btn_box {

width: 90%;

height: 100%;

margin: 0 auto;

overflow: hidden;

}

.add_title {

color: #20aae4;

}

.name,

.input {

margin-top: 30px;

float: left;

}

.input {

width: 160px;

height: 40px;

text-align: center;

outline: none;

appearance: none;

-moz-appearance: none;

border-radius: 4px;

border: 1px solid #c8cccf;

color: #000;

}

.cancel,

.save {

width: 80px;

height: 40px;

line-height: 40px;

float: left;

color: #fff;

text-align: center;

border-radius: 5%;

cursor: pointer;

}

.cancel {

margin-right: 10px;

background: #e0dfdf;

}

.save {

background: #20aae4;

}

.mask {

position: absolute;

top: 0;

display: none;

background-color: rgba(0, 0, 0, 0.5);

}

button {

background-color: #eee;

font-weight: 300;

font-size: 16px;

font-family: 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande',

sans-serif;

text-decoration: none;

text-align: center;

line-height: 28px;

height: 28px;

padding: 0 16px;

margin: 0;

display: inline-block;

appearance: none;

cursor: pointer;

border: none;

box-sizing: border-box;

transition: all 0.3s;

}

button:focus,

button:hover {

background-color: #f6f6f6;

text-decoration: none;

outline: 0;

}

button:active {

text-shadow: 0 1px 0 rgb(255 255 255 / 30%);

text-decoration: none;

background-color: #eee;

border-color: #cfcfcf;

color: #999;

transition-duration: 0s;

box-shadow: inset 0 1px 3px rgb(0 0 0 / 20%);

}

ul.unstyled {

padding: 0;

margin: 0;

list-style: none;

}

ul.unstyled > li {

list-style-type: none;

}

.popover-wrapper {

position: relative;

}

.popover-wrapper .popover {

padding: 8px;

border: 1px solid #ebeef5;

box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);

width: 150px;

position: absolute;

right: 0;

top: 28px;

margin-top: 4px;

display: none;

}

.popover-wrapper .popover .search {

-webkit-appearance: none;

background-color: #fff;

background-image: none;

border-radius: 4px;

border: 1px solid #dcdfe6;

box-sizing: border-box;

color: #606266;

display: inline-block;

font-size: inherit;

height: 28px;

line-height: 28px;

outline: none;

padding: 0 15px;

transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);

width: 100%;

}

.popover-wrapper .popover .list {

margin-top: 4px;

}

.popover-wrapper .popover .list li a {

display: block;

padding: 4px 8px;

text-decoration: none;

color: #000;

transition: all 0.3s;

}

.popover-wrapper .popover .list li a:hover,

.popover-wrapper .popover .list li a:focus {

background: rgba(39, 174, 96, 0.2);

}

.popover-wrapper .popover .list li a:active {

background: rgba(39, 174, 96, 0.8);

}

.logs {

display: inline-block;

vertical-align: text-top;

width: 400px;

padding: 8px;

border: 1px solid;

height: 400px;

overflow: auto;

margin-left: 16px;

}

.logs > p {

margin: 0 0 8px;

}

.container {

height: 600px;

width: 600px;

position: absolute;

left: 0;

top: 0;

right: 0;

bottom: 0;

margin: auto;

}

</style>

4.总结

前端部分比较简单,引入对应的依赖之后,就可以设计流程,一般来说,我们设计好之后,需要将流程图保存到业务库,然后部署发布流程之后,才可以审批等。如大家需要前后端架构源码可联系博主。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。