工作流表单+模型代码提交

This commit is contained in:
2024-05-09 14:18:39 +08:00
parent eeef608ccd
commit 06611f527a
266 changed files with 18227 additions and 22039 deletions

View File

@@ -0,0 +1,154 @@
<template>
<div>
<content-wrap>
<!-- 表单设计器 -->
<FcDesigner ref='designer' height='780px'>
<template #handle>
<el-button round size='small' type='primary' @click='handleSave' class='ml10' :icon='Plus'>保存
</el-button>
<el-button round size='small' type='primary' @click='go(-1)' class='ml10' :icon='Back'>取消
</el-button>
</template>
</FcDesigner>
</content-wrap>
<!-- 表单保存的弹窗 -->
<el-dialog v-model='dialogVisible' title='保存表单' class='cn-operate-dialog' style='width: 415px;height: 350px'
top='30vh'>
<el-scrollbar>
<el-form ref='formRef' :model='formData' :rules='formRules' label-width='80px'>
<el-form-item label='表单名' prop='name'>
<el-input v-model='formData.name' placeholder='请输入表单名' />
</el-form-item>
<el-form-item label='状态' prop='status'>
<el-radio-group v-model='formData.status'>
<el-radio
v-for='dict in options'
:key='dict.value'
:label='dict.value'
>
{{ dict.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label='备注' prop='remark'>
<el-input v-model='formData.remark' placeholder='请输入备注' type='textarea' />
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<span class='dialog-footer'>
<el-button :disabled='formLoading' type='primary' @click='submitForm'> </el-button>
<el-button @click='dialogVisible = false'> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang='ts' setup>
import { onMounted, ref, reactive } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import FcDesigner from '@form-create/designer'
import { useI18n } from '@/hooks/web/useI18n'
import { encodeConf, encodeFields, setConfAndFields } from '@/utils/formCreate'
import { useTagsViewStore } from '@/stores/modules/tagsView'
import { useFormCreateDesigner } from '@/components/FormCreate'
import { Plus, Back } from '@element-plus/icons-vue'
import ContentWrap from '@/components/ContentWrap/src/ContentWrap.vue'
const options = ref([
{
value: 1,
name: '开启'
},
{
value: 0,
name: '关闭'
}
])
defineOptions({ name: 'BpmFormEditor' })
import { ElMessage } from 'element-plus'
import { addForm, getById, updateForm } from '@/api/bpm-boot/form'
const { push, currentRoute, go } = useRouter() // 路由
const { query } = useRoute() // 路由信息
const { delView } = useTagsViewStore() // 视图操作
const designer = ref() // 表单设计器
useFormCreateDesigner(designer) // 表单设计器增强
const dialogVisible = ref(false) // 弹窗是否展示
const formLoading = ref(false) // 表单的加载中:提交的按钮禁用
const formData = ref({
name: '',
status: 1,
remark: ''
})
const formRules = reactive({
name: [{ required: true, message: '表单名不能为空', trigger: 'blur' }],
status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
/** 处理保存按钮 */
const handleSave = () => {
dialogVisible.value = true
}
type FormVO = {
id: number
name: string
conf: string
fields: string[]
status: number
remark: string
createTime: string
}
/** 提交表单 */
const submitForm = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
const data = formData.value as FormVO
data.conf = encodeConf(designer) // 表单配置
data.fields = encodeFields(designer) // 表单字段
if (!data.id) {
await addForm(data)
ElMessage.success('保存成功')
} else {
await updateForm(data)
ElMessage.success('更新成功')
}
dialogVisible.value = false
close()
} finally {
formLoading.value = false
}
}
/** 关闭按钮 */
const close = () => {
delView(unref(currentRoute))
go(-1)
}
/** 初始化 **/
onMounted(async () => {
// 场景一:新增表单
const id = query.id as unknown as string
if (!id) {
return
}
// 场景二:修改表单
await getById(id).then(res => {
formData.value = res.data
setConfAndFields(designer, res.data.conf, res.data.fields)
})
})
</script>

View File

@@ -0,0 +1,157 @@
<!--流程表单-->
<template>
<div class='default-main'>
<TableHeader>
<template v-slot:select>
<el-form-item label='表单名称'>
<el-input
v-model='tableStore.table.params.name'
clearable
/>
</el-form-item>
</template>
<template v-slot:operation>
<el-button type='primary' class='ml10' :icon='Plus' @click='openForm'>新增</el-button>
</template>
</TableHeader>
<!--表格-->
<Table ref='tableRef'></Table>
<!-- 预览表单对话框 -->
<el-dialog :title='render.title' v-model='render.visible' width='60%' append-to-body>
<v-form-render :form-json='formJson' :form-data='formData' :option-data='optionData' ref='vfRenderRef' />
</el-dialog>
</div>
</template>
<script lang='ts' setup>
import { Plus } from '@element-plus/icons-vue'
import TableHeader from '@/components/table/header/index.vue'
import Table from '@/components/table/index.vue'
import TableStore from '@/utils/tableStore'
import { ElMessage } from 'element-plus'
import { onMounted, provide, ref, reactive,nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { deleteWFForm, getFormById } from '@/api/process-boot/workflow/form'
const { push } = useRouter()
defineOptions({
name: 'category'
})
const formJson = reactive({"widgetList":[],"formConfig":{"modelName":"formData","refName":"vForm","rulesName":"rules","labelWidth":80,"labelPosition":"left","size":"","labelAlign":"label-left-align","cssCode":"","customClass":"","functions":"","layoutType":"PC","jsonVersion":3,"onFormCreated":"","onFormMounted":"","onFormDataChange":"","onFormValidate":""}})
const formData = reactive({})
const optionData = reactive({})
const vfRenderRef = ref(null)
const render = reactive({
visible: false,
title: ''
})
const tableStore = new TableStore({
url: '/bpm-boot/bpm/form/list',
method: 'POST',
column: [
{ title: '序号', type: 'seq', width: 80 },
{ title: '表单名称', minWidth: '160', field: 'name' },
{ title: '备注', minWidth: '140', field: 'remark' },
{ title: '状态', minWidth: '140', field: 'status',
render: 'tag',
custom: {
0: 'danger',
1: 'success',
},
replaceValue: {
0: '关闭',
1: '开启',
}
},
{
title: '操作',
align: 'center',
render: 'buttons',
minWidth: '230',
fixed: 'right',
buttons: [
{
name: 'view',
title: '预览',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: row => {
//首先根据id查询出详细数据然后渲染json
getFormById(row.id).then(res => {
render.visible = true;
render.title = '查看表单详情';
nextTick(async () => {
vfRenderRef.value.setFormJson(res.data.content || {formConfig: {}, widgetList: []});
});
})
}
},
{
name: 'update',
title: '设计',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: row => {
// push(`/admin/form/formDesigner?id=${row.id}`)
openForm(row.id)
}
},
{
name: 'update',
title: '删除',
type: 'danger',
icon: 'el-icon-Delete',
render: 'confirmButton',
popconfirm: {
confirmButtonText: '确认',
cancelButtonText: '取消',
confirmButtonType: 'danger',
title: '确定删除吗?'
},
click: row => {
deleteWFForm(row.id).then(res => {
ElMessage.success('删除成功')
tableStore.index()
})
}
}
]
}
],
beforeSearchFun: () => {
for (let key in tableStore.table.params) {
if (tableStore.table.params[key] === '') {
delete tableStore.table.params[key]
}
}
}
})
/** 添加/修改操作表单 */
const openForm = (id?: string) => {
const toRouter: { name: string; query?: { id: string } } = {
name: 'BpmFormEditor'
}
// 表单新建的时候id传的是event需要排除
if (typeof id === 'string') {
toRouter.query = {
id
}
}
push(toRouter)
}
tableStore.table.params.orderBy = 'desc'
tableStore.table.params.name = ''
provide('tableStore', tableStore)
onMounted(() => {
// 加载数据
tableStore.index()
})
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,149 @@
<template>
<ContentWrap>
<!-- 流程设计器负责绘制流程等 -->
<MyProcessDesigner
key='designer'
v-if='xmlString !== undefined'
v-model='xmlString'
:value='xmlString'
v-bind='controlForm'
keyboard
ref='processDesigner'
@init-finished='initModeler'
:additionalModel='controlForm.additionalModel'
@save='save'
/>
<!-- 流程属性器负责编辑每个流程节点的属性 -->
<MyProcessPenal
key='penal'
:bpmnModeler='modeler as any'
:prefix='controlForm.prefix'
class='process-panel'
:model='model'
/>
</ContentWrap>
</template>
<script lang='ts' setup>
import ContentWrap from '@/components/ContentWrap/src/ContentWrap.vue'
import { onMounted, provide, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { MyProcessDesigner, MyProcessPenal } from '@/components/bpmnProcessDesigner/package'
// 自定义元素选中时的弹出菜单(修改 默认任务 为 用户任务)
import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/content-pad'
// 自定义左侧菜单(修改 默认任务 为 用户任务)
import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette'
import { addModel, getById, updateModel } from '@/api/bpm-boot/model'
defineOptions({ name: 'BpmModelEditor' })
const { push, currentRoute, go } = useRouter() // 路由
const { query } = useRoute() // 路由信息
const xmlString = ref(undefined) // BPMN XML
const modeler = ref(null) // BPMN Modeler
const controlForm = ref({
simulation: true,
labelEditing: false,
labelVisible: false,
prefix: 'flowable',
headerButtonSize: 'mini',
additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
})
type ProcessDefinitionVO = {
id: string
version: number
deploymentTIme: string
suspensionState: number
}
type ModelVO = {
id: number
formName: string
key: string
name: string
description: string
category: string
formType: number
formId: number
formCustomCreatePath: string
formCustomViewPath: string
processDefinition: ProcessDefinitionVO
status: number
remark: string
createTime: string
bpmnXml: string
}
const model = ref<ModelVO>() // 流程模型的信息
/** 初始化 modeler */
const initModeler = (item: any) => {
setTimeout(() => {
modeler.value = item
}, 10)
}
/** 添加/修改模型 */
const save = async (bpmnXml: any) => {
const data = {
...model.value,
bpmnXml: bpmnXml // bpmnXml 只是初始化流程图,后续修改无法通过它获得
} as unknown as ModelVO
// 提交
if (data.id) {
await updateModel(data)
ElMessage.success('修改成功')
} else {
await addModel(data)
ElMessage.success('新增成功')
}
// 跳转回去
go(-1)
}
/** 初始化 */
onMounted(async () => {
const modelId = query.modelId as unknown as string
if (!modelId) {
ElMessage.error('缺少模型 modelId 编号')
return
}
// 查询模型
let data = {
bpmnXml: '',
key: '',
name: ''
}
await getById(modelId).then(res => {
data = res.data
})
if (!data.bpmnXml) {
// 首次创建的 Model 模型,它是没有 bpmnXml此时需要给它一个默认的
data.bpmnXml = ` <?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="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:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
<process id="${data.key}" name="${data.name}" isExecutable="true" />
<bpmndi:BPMNDiagram id="BPMNDiagram">
<bpmndi:BPMNPlane id="${data.key}_di" bpmnElement="${data.key}" />
</bpmndi:BPMNDiagram>
</definitions>`
}
model.value = {
...data,
bpmnXml: undefined // 清空 bpmnXml 属性
}
xmlString.value = data.bpmnXml
})
</script>
<style lang='scss'>
.process-panel__container {
position: absolute;
top: 190px;
right: 20px;
}
</style>

View File

@@ -0,0 +1,231 @@
<!--流程模型管理界面-->
<template>
<div class='default-main'>
<TableHeader>
<template v-slot:select>
<!-- <el-form-item label='用户'>-->
<!-- <el-select v-model='tableStore.table.params.userId' filterable clearable>-->
<!-- <el-option-->
<!-- v-for='item in userListData'-->
<!-- :key='item.id'-->
<!-- :label='item.userName'-->
<!-- :value='item.id'-->
<!-- />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
</template>
<template v-slot:operation>
<el-button type='primary' @click='add' class='ml10' :icon='Plus'>新增模型</el-button>
</template>
</TableHeader>
<!--表格-->
<Table ref='tableRef' isGroup></Table>
<model-popup ref='modelPopup' />
<!-- 弹窗表单详情 -->
<el-dialog title='表单详情' v-model='formDetailVisible' width='800'>
<form-create :rule='formDetailPreview.rule' :option='formDetailPreview.option' />
</el-dialog>
</div>
</template>
<script setup lang='ts'>
import { Plus } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import { onMounted, provide, ref } from 'vue'
import { useRouter } from 'vue-router'
import { sgUserList } from '@/api/advance-boot/sgGroven/sgUser'
import { deleteSgScheme } from '@/api/advance-boot/sgGroven/sgScheme'
import ModelPopup from '@/views/system/bpm/model/modelPopup.vue'
import { deployModel } from '@/api/bpm-boot/model'
import { getById } from '@/api/bpm-boot/form'
import { setConfAndFields2 } from '@/utils/formCreate'
defineOptions({
name: 'governSchemeHistory'
})
const { push } = useRouter()
const modelPopup = ref()
const tableStore = new TableStore({
url: '/bpm-boot/bpm/model/list',
method: 'POST',
column: [
{ title: '序号', type: 'seq', width: 80 },
{ title: '模型标识', field: 'key', width: 160 },
{ title: '模型名称', field: 'name', width: 160 },
{ title: '流程分类', field: 'categoryName', width: 150 },
{ title: '表单信息', field: 'formName', width: 170 },
{
title: '创建时间', field: 'createTime', width: 150
},
{
title: '最新部署的流程定义',
children: [
{ title: '模型版本', width: '150', field: 'processDefinition.version' },
{
title: '激活状态', width: '150', field: 'processDefinition.suspensionState',
render: 'tag',
custom: {
2: 'danger',
1: 'success'
},
replaceValue: {
2: '关闭',
1: '开启'
}
},
{ title: '部署时间', width: '150', field: 'processDefinition.deploymentTime' }
]
},
{
title: '操作',
align: 'center',
minWidth: '200',
render: 'buttons',
fixed: 'right',
buttons: [
{
name: 'productSetting',
title: '设计流程',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: row => {
handleDesign(row.id)
}
},
{
name: 'productSetting',
title: '部署流程',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: row => {
handleDeployConfirm(row.id)
}
},
{
name: 'productSetting',
title: '表单预览',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: row => {
handleFormDetail(row.formId)
}
},
{
name: 'update',
title: '删除',
type: 'danger',
icon: 'el-icon-Delete',
render: 'confirmButton',
popconfirm: {
confirmButtonText: '确认',
cancelButtonText: '取消',
confirmButtonType: 'danger',
title: '确定删除吗?'
},
click: row => {
deleteSgScheme(row.id).then(res => {
ElMessage.success('删除成功')
tableStore.index()
})
}
}
]
}
],
beforeSearchFun: () => {
if (tableStore.table.params.addr) {
tableStore.table.params.addrStrOption = tableStore.table.params.addr.map(tempArray => tempArray.join('/'))
}
for (let key in tableStore.table.params) {
if (tableStore.table.params[key] === '') {
delete tableStore.table.params[key]
}
}
}
})
onMounted(() => {
// 加载数据
tableStore.index()
})
tableStore.table.params.userId = ''
provide('tableStore', tableStore)
//新增用户信息
const add = () => {
modelPopup.value.open('新增流程模型')
}
/** 设计流程 */
const handleDesign = (rowId: string) => {
push({
name: 'BpmModelEditor',
query: {
modelId: rowId
}
})
}
/** 发布流程 */
const handleDeployConfirm = (rowId: string) => {
try {
// 删除的二次确认
ElMessageBox.confirm(
'是否部署该流程?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
draggable: true,
type: 'warning'
}
).then(() => {
handleDeploy(rowId)
})
.catch(() => {
})
} catch {
}
}
const handleDeploy = async (rowId: string) => {
// 发起部署
await deployModel(rowId)
ElMessage.success('部署成功')
tableStore.index()
}
/** 流程表单的详情按钮操作 */
const formDetailVisible = ref(false)
const formDetailPreview = ref({
rule: [],
option: {}
})
const handleFormDetail = async (formId: string) => {
// 设置表单
await getById(formId).then(res => {
const data = res.data
setConfAndFields2(formDetailPreview, data.conf, data.fields)
// 弹窗打开
formDetailVisible.value = true
})
}
</script>

View File

@@ -0,0 +1,176 @@
<template>
<el-dialog draggable class='cn-operate-dialog' v-model='dialogVisible' :title='title' style='max-width: 450px;height: 450px'>
<el-scrollbar>
<el-form :inline='false' :model='formData' label-width='120px' :rules='rules' ref='formRef'>
<el-form-item label='流程标识' prop='key'>
<el-input
v-model='formData.key'
disabled
/>
</el-form-item>
<el-form-item label='流程名称' prop='name'>
<el-input
v-model='formData.name'
:disabled='!!formData.id'
clearable
placeholder='请输入流程名称'
/>
</el-form-item>
<el-form-item label='流程分类' prop='category'>
<el-select
v-model='formData.category'
clearable
placeholder='请选择流程分类'
style='width: 100%'
>
<el-option
v-for='category in categoryList'
:key='category.id'
:label='category.name'
:value='category.id'
/>
</el-select>
</el-form-item>
<el-form-item v-if='formData.id' label='流程图标' prop='icon'>
<UploadImg v-model='formData.icon' :limit='1' height='128px' width='128px' />
</el-form-item>
<el-form-item label='流程描述' prop='description'>
<el-input v-model='formData.description' clearable type='textarea' />
</el-form-item>
<el-form-item label='流程表单' prop='formId'>
<el-select v-model='formData.formId' clearable style='width: 100%'>
<el-option
v-for='form in formList'
:key='form.id'
:label='form.name'
:value='form.id'
/>
</el-select>
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<span class='dialog-footer'>
<el-button @click='dialogVisible = false'>取消</el-button>
<el-button type='primary' @click='submit'>确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang='ts' setup>
import { ref, inject, reactive, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import TableStore from '@/utils/tableStore' // 若不是列表页面弹框可删除
import { getFormSimpleList } from '@/api/bpm-boot/form'
import { getCategorySimpleList } from '@/api/bpm-boot/category'
import { addModel, updateModel } from '@/api/bpm-boot/model'
//传入type的名称
const categoryList = ref()
const formList = ref()
const title = ref('')
const tableStore = inject('tableStore') as TableStore
const formRef = ref()
const dialogVisible = ref(false)
// 注意不要和表单ref的命名冲突
const formData = reactive({
id: '',
key: '',
name: '',
category: '',
icon: '',
description: '',
formId: ''
})
//form表单校验规则
const rules = {
key: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }],
name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }],
category: [{ required: true, message: '流程分类不能为空', trigger: 'change' }],
formId: [{ required: true, message: '流程表单不能为空', trigger: 'change' }]
}
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields()
}
}
const open = async (text: string, data?: any) => {
title.value = text
await getFormSimpleList().then(res => {
formList.value = res.data
})
await getCategorySimpleList().then(res => {
categoryList.value = res.data
})
//默认选中第一个tab
if (data) {
// 表单赋值
for (let key in formData) {
formData[key] = data[key]
}
} else {
resetForm()
// 在此处恢复默认表单
for (let key in formData) {
formData[key] = ''
}
//随机一个key给该模型
formData.key = `Process_${new Date().getTime()}`
}
dialogVisible.value = true
}
/**
* 提交用户表单数据
*/
const submit = () => {
formRef.value.validate(async (valid: any) => {
if (valid) {
if (formData.id) {
await updateModel(formData)
ElMessage.success('更新成功')
tableStore.index()
dialogVisible.value = false
} else {
await addModel(formData).then(res => {
formData.id = res.data
//查询进线数据避免一直处于loading状态
ElMessage.success('保存成功')
tableStore.index()
dialogVisible.value = false
})
}
}
})
}
defineExpose({ open })
</script>
<style scoped>
.el-upload-list__item {
transition: none !important;
}
.el-select {
min-width: 180px;
}
</style>