Commit fe6adf693b94cfe6f3292f7e0396c7114e335b23

Authored by YunaiV
1 parent a3f57a44

bpm:完成流程表单

yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceImpl.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.bpm.service.definition; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.bpm.service.definition;
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6 -import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;  
7 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormCreateReqVO; 6 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormCreateReqVO;
8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; 7 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;
9 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormUpdateReqVO; 8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormUpdateReqVO;
@@ -107,17 +106,15 @@ public class BpmFormServiceImpl implements BpmFormService { @@ -107,17 +106,15 @@ public class BpmFormServiceImpl implements BpmFormService {
107 return null; 106 return null;
108 } 107 }
109 108
110 - private void checkKeyNCName(String key) {  
111 - if (!ValidationUtils.isXmlNCName(key)) {  
112 - throw exception(MODEL_KEY_VALID);  
113 - }  
114 - }  
115 /** 109 /**
116 * 校验 Field,避免 field 重复 110 * 校验 Field,避免 field 重复
117 * 111 *
118 * @param fields field 数组 112 * @param fields field 数组
119 */ 113 */
120 private void checkFields(List<String> fields) { 114 private void checkFields(List<String> fields) {
  115 + if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验
  116 + return;
  117 + }
121 Map<String, String> fieldMap = new HashMap<>(); // key 是 vModel,value 是 label 118 Map<String, String> fieldMap = new HashMap<>(); // key 是 vModel,value 是 label
122 for (String field : fields) { 119 for (String field : fields) {
123 BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class); 120 BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class);
yudao-ui-admin-vue3/src/api/bpm/form/index.ts
1 import request from '@/config/axios' 1 import request from '@/config/axios'
2 -import { FormVO } from './types' 2 +
  3 +export type FormVO = {
  4 + id: number
  5 + name: string
  6 + conf: string
  7 + fields: string[]
  8 + status: number
  9 + remark: string
  10 + createTime: string
  11 +}
3 12
4 // 创建工作流的表单定义 13 // 创建工作流的表单定义
5 export const createFormApi = async (data: FormVO) => { 14 export const createFormApi = async (data: FormVO) => {
yudao-ui-admin-vue3/src/api/bpm/form/types.ts deleted
1 -export type FormVO = {  
2 - id: number  
3 - name: string  
4 - conf: string  
5 - fields: string[]  
6 - status: number  
7 - remark: string  
8 - createTime: string  
9 -}  
yudao-ui-admin-vue3/src/router/modules/remaining.ts
@@ -194,7 +194,7 @@ const remainingRouter: AppRouteRecordRaw[] = [ @@ -194,7 +194,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
194 noCache: true, 194 noCache: true,
195 hidden: true, 195 hidden: true,
196 canTo: true, 196 canTo: true,
197 - title: '流程表单', 197 + title: '设计流程表单',
198 activeMenu: 'bpm/manager/form/formEditor' 198 activeMenu: 'bpm/manager/form/formEditor'
199 } 199 }
200 }, 200 },
yudao-ui-admin-vue3/src/utils/formCreate.ts 0 → 100644
  1 +/**
  2 + * 针对 https://github.com/xaboy/form-create-designer 封装的工具类
  3 + */
  4 +
  5 +// 编码表单 Conf
  6 +export const encodeConf = (designerRef: object) => {
  7 + // @ts-ignore
  8 + return JSON.stringify(designerRef.value.getOption())
  9 +}
  10 +
  11 +// 编码表单 Fields
  12 +export const encodeFields = (designerRef: object) => {
  13 + // @ts-ignore
  14 + const rule = designerRef.value.getRule()
  15 + const fields: string[] = []
  16 + rule.forEach((item) => {
  17 + fields.push(JSON.stringify(item))
  18 + })
  19 + return fields
  20 +}
  21 +
  22 +// 解码表单 Fields
  23 +export const decodeFields = (fields: string[]) => {
  24 + const rule: object[] = []
  25 + fields.forEach((item) => {
  26 + rule.push(JSON.parse(item))
  27 + })
  28 + return rule
  29 +}
  30 +
  31 +// 设置表单的 Conf 和 Fields
  32 +export const setConfAndFields = (designerRef: object, conf: string, fields: string) => {
  33 + // @ts-ignore
  34 + designerRef.value.setOption(JSON.parse(conf))
  35 + // @ts-ignore
  36 + designerRef.value.setRule(decodeFields(fields))
  37 +}
  38 +
  39 +// 设置表单的 Conf 和 Fields
  40 +export const setConfAndFields2 = (detailPreview: object, conf: string, fields: string) => {
  41 + // @ts-ignore
  42 + detailPreview.value.option = JSON.parse(conf)
  43 + // @ts-ignore
  44 + detailPreview.value.rule = decodeFields(fields)
  45 +}
yudao-ui-admin-vue3/src/views/bpm/form/formEditor.vue
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 </fc-designer> 8 </fc-designer>
9 <!-- 表单保存的弹窗 --> 9 <!-- 表单保存的弹窗 -->
10 <XModal v-model="dialogVisible" title="保存表单"> 10 <XModal v-model="dialogVisible" title="保存表单">
11 - <el-form :model="formValues" :rules="formRules" label-width="80px"> 11 + <el-form ref="formRef" :model="formValues" :rules="formRules" label-width="80px">
12 <el-form-item label="表单名" prop="name"> 12 <el-form-item label="表单名" prop="name">
13 <el-input v-model="formValues.name" placeholder="请输入表单名" /> 13 <el-input v-model="formValues.name" placeholder="请输入表单名" />
14 </el-form-item> 14 </el-form-item>
@@ -43,22 +43,26 @@ @@ -43,22 +43,26 @@
43 </ContentWrap> 43 </ContentWrap>
44 </template> 44 </template>
45 <script setup lang="ts" name="BpmFormEditor"> 45 <script setup lang="ts" name="BpmFormEditor">
  46 +import { reactive } from 'vue'
  47 +import { FormInstance } from 'element-plus'
46 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' 48 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
47 import { CommonStatusEnum } from '@/utils/constants' 49 import { CommonStatusEnum } from '@/utils/constants'
48 -import { reactive } from 'vue'  
49 - 50 +import * as FormApi from '@/api/bpm/form'
  51 +import { encodeConf, encodeFields, setConfAndFields } from '@/utils/formCreate'
50 const { t } = useI18n() // 国际化 52 const { t } = useI18n() // 国际化
51 -// const message = useMessage() // 消息 53 +const message = useMessage() // 消息
  54 +const { query } = useRoute() // 路由
52 55
53 const designer = ref() // 表单设计器 56 const designer = ref() // 表单设计器
54 57
55 -const dialogVisible = ref(false)  
56 -const dialogLoading = ref(false) 58 +const dialogVisible = ref(false) // 弹窗是否展示
  59 +const dialogLoading = ref(false) // 弹窗的加载中
  60 +const formRef = ref<FormInstance>()
57 const formRules = reactive({ 61 const formRules = reactive({
58 name: [{ required: true, message: '表单名不能为空', trigger: 'blur' }], 62 name: [{ required: true, message: '表单名不能为空', trigger: 'blur' }],
59 status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }] 63 status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }]
60 }) 64 })
61 -const formValues = reactive({ 65 +const formValues = ref({
62 name: '', 66 name: '',
63 status: CommonStatusEnum.ENABLE, 67 status: CommonStatusEnum.ENABLE,
64 remark: '' 68 remark: ''
@@ -71,9 +75,42 @@ const handleSave = () =&gt; { @@ -71,9 +75,42 @@ const handleSave = () =&gt; {
71 75
72 // 提交保存表单 76 // 提交保存表单
73 const submitForm = async () => { 77 const submitForm = async () => {
74 - console.log('保存') 78 + // 参数校验
  79 + const elForm = unref(formRef)
  80 + if (!elForm) return
  81 + const valid = await elForm.validate()
  82 + if (!valid) return
  83 +
  84 + // 提交请求
  85 + dialogLoading.value = true
  86 + try {
  87 + const data = formValues.value as FormApi.FormVO
  88 + data.conf = encodeConf(designer) // 表单配置
  89 + data.fields = encodeFields(designer) // 表单字段
  90 + if (!data.id) {
  91 + await FormApi.createFormApi(data)
  92 + message.success(t('common.createSuccess'))
  93 + } else {
  94 + await FormApi.updateFormApi(data)
  95 + message.success(t('common.updateSuccess'))
  96 + }
  97 + dialogVisible.value = false
  98 + } finally {
  99 + dialogLoading.value = false
  100 + }
75 } 101 }
76 102
77 -// formValue.value = designer.value.getOption()  
78 -// formValue.value = designer.value.getRule() 103 +// ========== 初始化 ==========
  104 +onMounted(() => {
  105 + // 场景一:新增表单
  106 + const id = query.id as unknown as number
  107 + if (!id) {
  108 + return
  109 + }
  110 + // 场景二:修改表单
  111 + FormApi.getFormApi(id).then((data) => {
  112 + formValues.value = data
  113 + setConfAndFields(designer, data.conf, data.fields)
  114 + })
  115 +})
79 </script> 116 </script>
yudao-ui-admin-vue3/src/views/bpm/form/index.vue
@@ -37,12 +37,9 @@ @@ -37,12 +37,9 @@
37 /> 37 />
38 </template> 38 </template>
39 </XTable> 39 </XTable>
40 - <!-- TODO 芋艿:待完善 -->  
41 - <XModal v-model="detailOpen" width="400" title="表单详情">  
42 - <!-- <div class="test-form">  
43 - <parser :key="new Date().getTime()" :form-conf="detailForm" />  
44 - </div> -->  
45 - 表单详情 40 + <!-- 表单详情的弹窗 -->
  41 + <XModal v-model="detailOpen" width="800" title="表单详情">
  42 + <ViewForm :rule="detailPreview.rule" :option="detailPreview.option" v-if="detailOpen" />
46 </XModal> 43 </XModal>
47 </div> 44 </div>
48 </ContentWrap> 45 </ContentWrap>
@@ -50,21 +47,18 @@ @@ -50,21 +47,18 @@
50 47
51 <script setup lang="ts" name="BpmForm"> 48 <script setup lang="ts" name="BpmForm">
52 // 全局相关的 import 49 // 全局相关的 import
53 -// import { ref } from 'vue'  
54 -import { useI18n } from '@/hooks/web/useI18n'  
55 -import { useMessage } from '@/hooks/web/useMessage'  
56 -import { useXTable } from '@/hooks/web/useXTable'  
57 -// import { FormExpose } from '@/components/Form'  
58 // 业务相关的 import 50 // 业务相关的 import
59 import * as FormApi from '@/api/bpm/form' 51 import * as FormApi from '@/api/bpm/form'
60 import { allSchemas } from './form.data' 52 import { allSchemas } from './form.data'
61 -import { useRouter } from 'vue-router'  
62 -// import { decodeFields } from '@/utils/formGenerator' // TODO 芋艿:可能要清理下  
63 53
64 const { t } = useI18n() // 国际化 54 const { t } = useI18n() // 国际化
65 -const message = useMessage() // 消息弹窗  
66 const router = useRouter() // 路由 55 const router = useRouter() // 路由
67 56
  57 +// 表单详情相关的变量和 import
  58 +import viewForm from '@form-create/element-ui'
  59 +const ViewForm = viewForm.$form()
  60 +import { setConfAndFields2 } from '@/utils/formCreate'
  61 +
68 // 列表相关的变量 62 // 列表相关的变量
69 const [registerTable, { deleteData }] = useXTable({ 63 const [registerTable, { deleteData }] = useXTable({
70 allSchemas: allSchemas, 64 allSchemas: allSchemas,
@@ -72,23 +66,6 @@ const [registerTable, { deleteData }] = useXTable({ @@ -72,23 +66,6 @@ const [registerTable, { deleteData }] = useXTable({
72 deleteApi: FormApi.deleteFormApi 66 deleteApi: FormApi.deleteFormApi
73 }) 67 })
74 68
75 -// 弹窗相关的变量  
76 -// const modelVisible = ref(false) // 是否显示弹出层  
77 -// const modelTitle = ref('edit') // 弹出层标题  
78 -// const modelLoading = ref(false) // 弹出层loading  
79 -// const actionType = ref('') // 操作按钮的类型  
80 -// const actionLoading = ref(false) // 按钮 Loading  
81 -// const formRef = ref<FormExpose>() // 表单 Ref  
82 -// const detailData = ref() // 详情 Ref  
83 -  
84 -// 设置标题  
85 -// const setDialogTile = (type: string) => {  
86 -// modelLoading.value = true  
87 -// modelTitle.value = t('action.' + type)  
88 -// actionType.value = type  
89 -// modelVisible.value = true  
90 -// }  
91 -  
92 // 新增操作 69 // 新增操作
93 const handleCreate = () => { 70 const handleCreate = () => {
94 router.push({ 71 router.push({
@@ -98,38 +75,25 @@ const handleCreate = () =&gt; { @@ -98,38 +75,25 @@ const handleCreate = () =&gt; {
98 75
99 // 修改操作 76 // 修改操作
100 const handleUpdate = async (rowId: number) => { 77 const handleUpdate = async (rowId: number) => {
101 - console.log(rowId, '修改')  
102 - if (true) {  
103 - message.success('动态表单开发中,预计 2 月底完成')  
104 - return  
105 - }  
106 await router.push({ 78 await router.push({
107 - path: '/bpm/manager/form/edit', 79 + name: 'bpmFormEditor',
108 query: { 80 query: {
109 - formId: rowId 81 + id: rowId
110 } 82 }
111 }) 83 })
112 } 84 }
113 85
114 -// 详情操作 // TODO 芋艿:详情的实现  
115 -// const detailForm = ref({  
116 -// fields: []  
117 -// })  
118 -// 表单详情弹出窗显示隐藏  
119 -// const detailOpen = ref(false)  
120 - 86 +// 详情操作
  87 +const detailOpen = ref(false)
  88 +const detailPreview = ref({
  89 + rule: [],
  90 + option: {}
  91 +})
121 const handleDetail = async (rowId: number) => { 92 const handleDetail = async (rowId: number) => {
122 - console.log(rowId, '详情')  
123 - message.success('动态表单开发中,预计 2 月底完成')  
124 - // FormApi.getFormApi(row.id).then((response) => {  
125 - // // 设置值  
126 - // const data = response.data  
127 - // detailForm.value = {  
128 - // ...JSON.parse(data.conf),  
129 - // fields: decodeFields([], data.fields)  
130 - // }  
131 - // // 弹窗打开  
132 - // detailOpen.value = true  
133 - // }) 93 + // 设置表单
  94 + const data = await FormApi.getFormApi(rowId)
  95 + setConfAndFields2(detailPreview, data.conf, data.fields)
  96 + // 弹窗打开
  97 + detailOpen.value = true
134 } 98 }
135 </script> 99 </script>