慢直播
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

513 lines
18 KiB

<template>
<basic-container class="wf-design">
<el-steps :active="step"
finish-status="success"
simple
style="margin-bottom: 20px;">
<el-step title="设计表单"
icon="el-icon-edit"></el-step>
<el-step title="设计流程"
icon="el-icon-upload">
<template #title>
设计流程
<el-tooltip v-show="step == '1'"
content="全屏">
<i class="el-icon-full-screen"
@click="handleFullScreen"></i>
</el-tooltip>
</template>
</el-step>
<el-step title="完成"
icon="el-icon-circle-check"></el-step>
</el-steps>
<div v-show="step == 0">
<avue-form style="margin-bottom: 66px;"
ref="form1"
:option="step1.option"
v-model="step1.form">
<template #tip>
<el-link type="primary"
:underline="false"
@click="$router.push('/plugin/workflow/design/form')">没有想要的表单?点击去设计</el-link>
</template>
<template #form>
<avue-form v-if="option && ((option.column && option.column.length > 0) || (option.group && option.group.length > 0))"
ref="form2"
v-model="form"
:option="option"></avue-form>
</template>
</avue-form>
</div>
<div v-if="step == 1">
<wf-design id="bpmn2"
ref="bpmn2"
style="height: calc(100vh - 290px); background: white;"
:options="step2.option"></wf-design>
</div>
<div v-if="step == 2">
<wf-design ref="bpmn3"
style="height: calc(100vh - 290px)"
:options="step3.option"></wf-design>
</div>
<div class="foot-item"
:style="{width: isCollapse? 'calc(100% - 80px)': 'calc(100% - 260px)' }">
<el-button type="primary"
size="medium"
v-if="step > 0"
@click="step--">上一步</el-button>
<el-button type="success"
size="medium"
v-if="step < 2"
@click="handleNextStep">下一步</el-button>
<el-button type="success"
size="medium"
v-if="step == 2"
@click="handleSave">保存</el-button>
</div>
</basic-container>
</template>
<script>
import { getDetail as getFormByKey } from '@/api/plugin/workflow/form'
import { submit, getDetail } from '@/api/plugin/workflow/model'
import { getList as buttonList } from '@/api/plugin/workflow/button'
import { getList as formList } from '@/api/plugin/workflow/form'
import { getList as conditionList } from '@/api/plugin/workflow/condition'
import { fullscreenToggel } from "@/util/util";
import { mapGetters } from 'vuex'
export default {
name: 'design',
computed: {
...mapGetters(['tag', 'isCollapse', 'language']),
},
watch: {
'$route.params.id': {
handler(val) {
if (!val || val == 0) return
getDetail(val).then(res => {
this.process = res.data.data
const { formKey, xml, exForm } = this.process
this.$set(this.step2.option, 'xml', xml)
this.$set(this.step2.option, 'process', this.process)
if (formKey.startsWith('wf_ex_')) { // 外置表单
const column = []
exForm.forEach(ex => {
column.push({
label: ex.name,
prop: ex.id,
readable: true,
writable: true
})
})
this.$set(this.step1.form, 'column', column)
this.$set(this.step1.form, 'formType', 2)
this.$set(this.step1.form, 'exFormKey', formKey.substring(6))
} else if (formKey.startsWith('wf_indep_')) {
this.$set(this.step1.form, 'formType', 3)
} else {
this.$set(this.step1.form, 'formKey', formKey)
}
})
},
immediate: true
},
language(val) {
const option = {
lang: val
}
if (this.$refs.bpmn2) {
this.$refs.bpmn2.getData('xml', false, false).then(data => {
option.xml = data
this.$set(this.step2, 'option', {
...this.step2.option,
...option
})
})
} else this.$set(this.step2, 'option', {
...this.step2.option,
...option
})
}
},
data() {
const _this = this
return {
form: {},
option: {},
step: 0,
step1: {
form: {},
option: {
menuBtn: false,
group: [{
labelPosition: 'left',
label: '选择表单',
icon: 'el-icon-warning-outline',
arrow: false,
column: [{
label: '表单类型',
prop: 'formType',
type: 'radio',
dicData: [{
label: '内置表单',
value: 1
}, {
label: '外置表单',
value: 2
}, {
label: '节点独立表单',
value: 3
}],
span: 24,
value: 1,
event: {
change: (val) => {
if (!val) return
val = typeof val == 'object'? val.value: val
if (val == 1) {
this.findObject(this.step1.option.group[0].column, 'exFormKey').display = false
this.findObject(this.step1.option.group[1].column, 'column').display = false
this.findObject(this.step1.option.group[0].column, 'formKey').display = true
this.findObject(this.step1.option.group[0].column, 'tip').display = true
this.findObject(this.step1.option.group[1].column, 'form').display = true
this.step1.option.group[1].display = true
} else if (val == 2) {
this.findObject(this.step1.option.group[0].column, 'exFormKey').display = true
this.findObject(this.step1.option.group[1].column, 'column').display = true
this.findObject(this.step1.option.group[0].column, 'formKey').display = false
this.findObject(this.step1.option.group[0].column, 'tip').display = false
this.option = {}
this.step1.option.group[1].display = true
} else if (val == 3) {
this.findObject(this.step1.option.group[0].column, 'exFormKey').display = false
this.findObject(this.step1.option.group[1].column, 'column').display = false
this.findObject(this.step1.option.group[0].column, 'formKey').display = false
this.findObject(this.step1.option.group[0].column, 'tip').display = false
this.option = {}
this.step1.option.group[1].display = false
}
}
}
}, {
label: '表单key',
prop: 'exFormKey',
display: false,
rules: [{ required: true, message: '请输入外置表单key' }]
}, {
label: '表单',
prop: 'formKey',
type: 'select',
props: {
label: 'name',
value: 'formKey'
},
dicData: [],
event: {
change: (val) => {
val = typeof val == 'object'? val.value: val
_this.option = { menuBtn: false, readonly: true }
if (val) {
getFormByKey({ formKey: val }).then(res => {
_this.option = { ...eval('(' + res.data.data.content.replace(/this/g, '_this') + ')'), menuBtn: false, readonly: true }
_this.findObject(this.step1.option.group[1].column, 'form').display = true
}).catch(() => {
_this.findObject(this.step1.option.group[1].column, 'form').display = false
})
} else {
_this.findObject(this.step1.option.group[1].column, 'form').display = false
}
}
},
rules: [{ required: true, message: '请选择表单' }],
display: true,
filterable: true,
}, {
labelWidth: 0,
prop: 'tip',
formslot: true
}]
}, {
label: '表单预览',
icon: 'el-icon-view',
display: true,
arrow: false,
column: [{
prop: 'form',
labelWidth: 0,
span: 24,
formslot: true,
display: false
}, {
prop: 'column',
labelWidth: '0',
tip: '可用于控制外置表单字段的显隐配置,如果希望自己控制请忽略此字段',
tipPlacement: 'top',
span: 24,
type: 'dynamic',
children: {
align: 'center',
column: [{
label: '字段',
prop: 'label',
rules: [{ required: true, message: '请输入字段名' }]
}, {
label: '属性',
prop: 'prop',
rules: [{ required: true, message: '请输入属性名' }]
}, {
label: '默认可读',
prop: 'readable',
type: 'switch',
disabled: true,
value: true
}, {
label: '默认可写',
prop: 'writable',
type: 'switch',
disabled: true,
value: true
}]
},
display: false
}]
}]
},
},
step2: {
option: {
config: false,
mode: 'edit',
engine: 'flowable',
toolbar: ['open', 'create', 'fit', 'zoom-in', 'zoom-out', 'undo', 'redo', 'import', 'preview'],
script: {
script: {
enable: false,
alert: '使用之前请先了解脚本会带来的危害,若确定使用请参考文档放开此配置。<br>1、脚本中可以完全访问JVM。<br>2、脚本执行时阻塞许多系统资源。<br>3、脚本执行死循环/占用大量内存等会导致程序崩溃。'
},
shell: {
enable: false,
alert: '使用之前请先了解Shell会带来的危害,若确定使用请参考文档放开此配置。<br>因不确定是否可执行危险命令,如rm -rf *,请充分了解之后再使用。',
pattern: '(rm|mv|kill|ifconfig|docker|reboot|dd|wget|shutdown|halt|poweroff|init|:(){:|:&};:|^foo^bar)'
}
}
}
},
step3: {
option: {
config: false,
mode: 'view',
simulation: true,
minimap: true,
engine: 'flowable'
}
},
process: {},
fullscreen: false
}
},
mounted() {
this.getButtonList()
this.getUserListV2()
this.getFormList()
this.getConditionList()
},
methods: {
handleNextStep() {
switch (this.step) {
case 0:
this.$refs.form1.validate((valid, done) => {
if (valid) {
const { formType, formKey, exFormKey, column } = this.step1.form
if (formType == 1) { // 内置表单
this.process.formKey = formKey
this.$set(this.step2.option, 'form', this.option)
} else if (formType == 2) { // 外置表单
this.process.formKey = "wf_ex_" + exFormKey
this.$set(this.step2.option, 'exForm', {
exFormKey, column
})
} else if (formType == 3) { // 独立表单
this.$set(this.step2.option, 'indepForm', {
mode: 'indep',
list: this.formList
})
}
this.step++
done()
}
})
break;
case 1:
if (this.step1.form.formType == 3) { // 节点独立表单
const registry = this.$refs.bpmn2.getElementRegistry().getAll()
let errorList = []
registry.forEach(ele => {
this.validateIndepFormOption(ele, errorList)
})
if (errorList.length > 0) {
errorList = new Set(errorList)
let message = ''
errorList.forEach(err => {
const { businessObject } = err
const { id, name } = businessObject
message += `<p>${name || id} 节点未正确配置表单</p>`
})
this.$message({
type: 'error',
dangerouslyUseHTMLString: true,
message
})
return
}
}
this.$refs.bpmn2.getData('xml').then(data => {
this.$set(this.step2.option, 'xml', data)
this.$set(this.step3.option, 'xml', data)
this.process.xml = data
this.step++
})
break;
}
},
handleSave() {
const registry = this.$refs.bpmn3.getElementRegistry().getAll()
const { businessObject } = registry[0]
const { id, name, documentation } = businessObject
const description = (documentation && documentation.length > 0) ? documentation[0].text : null
const { formType } = this.step1.form
if (formType == 3) { // 节点独立表单
const startEvent = registry.find(r => r.type == 'bpmn:StartEvent')
if (startEvent) {
const indepFormKey = startEvent.businessObject.extensionElements.values.find(v => v.$type == 'flowable:indepFormKey')
if (indepFormKey) this.process.formKey = 'wf_indep_' + indepFormKey.value
}
}
const params = {
...this.process,
modelKey: id,
name,
description
}
if (this.process.id) {
this.$confirm('是否将此模型保存为新版本?这意味着可以返回到以前的版本。', '提示', {
distinguishCancelAndClose: true,
confirmButtonText: '否',
cancelButtonText: '是',
type: 'warning'
}).then(() => {
params.newVersion = false
submit(params).then(() => {
this.$message.success("操作成功")
this.$store.commit('DEL_TAG', this.tag)
this.$router.push("/plugin/workflow/design/model")
})
}).catch(action => {
if (action == 'cancel') {
params.newVersion = true
submit(params).then(() => {
this.$message.success("操作成功")
this.$store.commit('DEL_TAG', this.tag)
this.$router.push("/plugin/workflow/design/model")
})
}
})
} else {
submit(params).then(() => {
this.$message.success("操作成功")
this.$store.commit('DEL_TAG', this.tag)
this.$router.push("/plugin/workflow/design/model")
})
}
},
validateIndepFormOption(element, errorList) {
const { type, businessObject, children } = element
const indepFormKey = 'flowable:IndepFormKey'
const indepFormSummary = 'flowable:IndepFormSummary'
if ('bpmn:StartEvent' == type) {
const extensionElements = businessObject.extensionElements
if (extensionElements && extensionElements.values && extensionElements.values.length > 0) {
const summary = extensionElements.values.find(v => v.$type == indepFormSummary)
if (!extensionElements.values.find(v => v.$type == indepFormKey) && (!summary || summary == '0')) errorList.push(element)
} else errorList.push(element)
} else if ('bpmn:UserTask' == type) {
const summary = businessObject['indepFormSummary']
if (!businessObject['indepFormKey'] && (!summary || summary == '0')) errorList.push(element)
} else if ('bpmn:SubProcess' == type) {
children.forEach(ele => this.validateIndepFormOption(ele, errorList))
}
},
getButtonList() {
buttonList(1, -1, { status: 1 }).then(res => {
const list = res.data.data.records.map(l => {
return {
label: l.name,
prop: l.buttonKey,
display: l.display
}
})
this.$set(this.step2.option, 'button', list)
})
},
getFormList() {
formList(1, -1, { 'status_equal': '1' }).then(res => {
this.formList = res.data.data.records
this.findObject(this.step1.option.group, 'formKey').dicData = this.formList
})
},
getUserListV2() {
this.$set(this.step2.option, 'user', {
version: 'v2',
userUrl: '/api/blade-user/search/user',
roleUrl: '/api/blade-system/search/role',
deptUrl: '/api/blade-system/search/dept',
postUrl: '/api/blade-system/search/post',
customUrl: '/api/blade-workflow/design/condition/list'
})
},
getConditionList() {
conditionList(1, -1, { type: 'flow', status: 1 }).then(res => {
this.$set(this.step2.option, 'condition', res.data.data.records)
})
},
handleFullScreen() {
fullscreenToggel()
this.$store.commit('SET_COLLAPSE')
},
}
}
</script>
<style lang="scss">
.wf-design {
.avue-group__title {
margin-top: 8px;
}
}
</style>
<style scoped lang="scss">
.foot-item {
position: fixed;
bottom: 0;
margin-left: -20px;
// right: 0;
z-index: 101;
height: 66px;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
-webkit-transition: 0.3s;
transition: 0.3s;
-webkit-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
</style>