Browse Source

bpm:完成流程表单

YunaiV 2 years ago
parent
commit
fe6adf693b

+ 3 - 6
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;
 import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormCreateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormUpdateReqVO;
@@ -107,17 +106,15 @@ public class BpmFormServiceImpl implements BpmFormService {
         return null;
     }
 
-    private void checkKeyNCName(String key) {
-        if (!ValidationUtils.isXmlNCName(key)) {
-            throw exception(MODEL_KEY_VALID);
-        }
-    }
     /**
      * 校验 Field,避免 field 重复
      *
      * @param fields field 数组
      */
     private void checkFields(List<String> fields) {
+        if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验
+            return;
+        }
         Map<String, String> fieldMap = new HashMap<>(); // key 是 vModel,value 是 label
         for (String field : fields) {
             BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class);

+ 10 - 1
yudao-ui-admin-vue3/src/api/bpm/form/index.ts

@@ -1,5 +1,14 @@
 import request from '@/config/axios'
-import { FormVO } from './types'
+
+export type FormVO = {
+  id: number
+  name: string
+  conf: string
+  fields: string[]
+  status: number
+  remark: string
+  createTime: string
+}
 
 // 创建工作流的表单定义
 export const createFormApi = async (data: FormVO) => {

+ 0 - 9
yudao-ui-admin-vue3/src/api/bpm/form/types.ts

@@ -1,9 +0,0 @@
-export type FormVO = {
-  id: number
-  name: string
-  conf: string
-  fields: string[]
-  status: number
-  remark: string
-  createTime: string
-}

+ 1 - 1
yudao-ui-admin-vue3/src/router/modules/remaining.ts

@@ -194,7 +194,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
           noCache: true,
           hidden: true,
           canTo: true,
-          title: '流程表单',
+          title: '设计流程表单',
           activeMenu: 'bpm/manager/form/formEditor'
         }
       },

+ 45 - 0
yudao-ui-admin-vue3/src/utils/formCreate.ts

@@ -0,0 +1,45 @@
+/**
+ * 针对 https://github.com/xaboy/form-create-designer 封装的工具类
+ */
+
+// 编码表单 Conf
+export const encodeConf = (designerRef: object) => {
+  // @ts-ignore
+  return JSON.stringify(designerRef.value.getOption())
+}
+
+// 编码表单 Fields
+export const encodeFields = (designerRef: object) => {
+  // @ts-ignore
+  const rule = designerRef.value.getRule()
+  const fields: string[] = []
+  rule.forEach((item) => {
+    fields.push(JSON.stringify(item))
+  })
+  return fields
+}
+
+// 解码表单 Fields
+export const decodeFields = (fields: string[]) => {
+  const rule: object[] = []
+  fields.forEach((item) => {
+    rule.push(JSON.parse(item))
+  })
+  return rule
+}
+
+// 设置表单的 Conf 和 Fields
+export const setConfAndFields = (designerRef: object, conf: string, fields: string) => {
+  // @ts-ignore
+  designerRef.value.setOption(JSON.parse(conf))
+  // @ts-ignore
+  designerRef.value.setRule(decodeFields(fields))
+}
+
+// 设置表单的 Conf 和 Fields
+export const setConfAndFields2 = (detailPreview: object, conf: string, fields: string) => {
+  // @ts-ignore
+  detailPreview.value.option = JSON.parse(conf)
+  // @ts-ignore
+  detailPreview.value.rule = decodeFields(fields)
+}

+ 47 - 10
yudao-ui-admin-vue3/src/views/bpm/form/formEditor.vue

@@ -8,7 +8,7 @@
     </fc-designer>
     <!-- 表单保存的弹窗 -->
     <XModal v-model="dialogVisible" title="保存表单">
-      <el-form :model="formValues" :rules="formRules" label-width="80px">
+      <el-form ref="formRef" :model="formValues" :rules="formRules" label-width="80px">
         <el-form-item label="表单名" prop="name">
           <el-input v-model="formValues.name" placeholder="请输入表单名" />
         </el-form-item>
@@ -43,22 +43,26 @@
   </ContentWrap>
 </template>
 <script setup lang="ts" name="BpmFormEditor">
+import { reactive } from 'vue'
+import { FormInstance } from 'element-plus'
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 import { CommonStatusEnum } from '@/utils/constants'
-import { reactive } from 'vue'
-
+import * as FormApi from '@/api/bpm/form'
+import { encodeConf, encodeFields, setConfAndFields } from '@/utils/formCreate'
 const { t } = useI18n() // 国际化
-// const message = useMessage() // 消息
+const message = useMessage() // 消息
+const { query } = useRoute() // 路由
 
 const designer = ref() // 表单设计器
 
-const dialogVisible = ref(false)
-const dialogLoading = ref(false)
+const dialogVisible = ref(false) // 弹窗是否展示
+const dialogLoading = ref(false) // 弹窗的加载中
+const formRef = ref<FormInstance>()
 const formRules = reactive({
   name: [{ required: true, message: '表单名不能为空', trigger: 'blur' }],
   status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }]
 })
-const formValues = reactive({
+const formValues = ref({
   name: '',
   status: CommonStatusEnum.ENABLE,
   remark: ''
@@ -71,9 +75,42 @@ const handleSave = () => {
 
 // 提交保存表单
 const submitForm = async () => {
-  console.log('保存')
+  // 参数校验
+  const elForm = unref(formRef)
+  if (!elForm) return
+  const valid = await elForm.validate()
+  if (!valid) return
+
+  // 提交请求
+  dialogLoading.value = true
+  try {
+    const data = formValues.value as FormApi.FormVO
+    data.conf = encodeConf(designer) // 表单配置
+    data.fields = encodeFields(designer) // 表单字段
+    if (!data.id) {
+      await FormApi.createFormApi(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await FormApi.updateFormApi(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+  } finally {
+    dialogLoading.value = false
+  }
 }
 
-// formValue.value = designer.value.getOption()
-// formValue.value = designer.value.getRule()
+// ========== 初始化 ==========
+onMounted(() => {
+  // 场景一:新增表单
+  const id = query.id as unknown as number
+  if (!id) {
+    return
+  }
+  // 场景二:修改表单
+  FormApi.getFormApi(id).then((data) => {
+    formValues.value = data
+    setConfAndFields(designer, data.conf, data.fields)
+  })
+})
 </script>

+ 21 - 57
yudao-ui-admin-vue3/src/views/bpm/form/index.vue

@@ -37,12 +37,9 @@
           />
         </template>
       </XTable>
-      <!-- TODO 芋艿:待完善 -->
-      <XModal v-model="detailOpen" width="400" title="表单详情">
-        <!-- <div class="test-form">
-          <parser :key="new Date().getTime()" :form-conf="detailForm" />
-        </div> -->
-        表单详情
+      <!-- 表单详情的弹窗 -->
+      <XModal v-model="detailOpen" width="800" title="表单详情">
+        <ViewForm :rule="detailPreview.rule" :option="detailPreview.option" v-if="detailOpen" />
       </XModal>
     </div>
   </ContentWrap>
@@ -50,21 +47,18 @@
 
 <script setup lang="ts" name="BpmForm">
 // 全局相关的 import
-// import { ref } from 'vue'
-import { useI18n } from '@/hooks/web/useI18n'
-import { useMessage } from '@/hooks/web/useMessage'
-import { useXTable } from '@/hooks/web/useXTable'
-// import { FormExpose } from '@/components/Form'
 // 业务相关的 import
 import * as FormApi from '@/api/bpm/form'
 import { allSchemas } from './form.data'
-import { useRouter } from 'vue-router'
-// import { decodeFields } from '@/utils/formGenerator' // TODO 芋艿:可能要清理下
 
 const { t } = useI18n() // 国际化
-const message = useMessage() // 消息弹窗
 const router = useRouter() // 路由
 
+// 表单详情相关的变量和 import
+import viewForm from '@form-create/element-ui'
+const ViewForm = viewForm.$form()
+import { setConfAndFields2 } from '@/utils/formCreate'
+
 // 列表相关的变量
 const [registerTable, { deleteData }] = useXTable({
   allSchemas: allSchemas,
@@ -72,23 +66,6 @@ const [registerTable, { deleteData }] = useXTable({
   deleteApi: FormApi.deleteFormApi
 })
 
-// 弹窗相关的变量
-// const modelVisible = ref(false) // 是否显示弹出层
-// const modelTitle = ref('edit') // 弹出层标题
-// const modelLoading = ref(false) // 弹出层loading
-// const actionType = ref('') // 操作按钮的类型
-// const actionLoading = ref(false) // 按钮 Loading
-// const formRef = ref<FormExpose>() // 表单 Ref
-// const detailData = ref() // 详情 Ref
-
-// 设置标题
-// const setDialogTile = (type: string) => {
-//   modelLoading.value = true
-//   modelTitle.value = t('action.' + type)
-//   actionType.value = type
-//   modelVisible.value = true
-// }
-
 // 新增操作
 const handleCreate = () => {
   router.push({
@@ -98,38 +75,25 @@ const handleCreate = () => {
 
 // 修改操作
 const handleUpdate = async (rowId: number) => {
-  console.log(rowId, '修改')
-  if (true) {
-    message.success('动态表单开发中,预计 2 月底完成')
-    return
-  }
   await router.push({
-    path: '/bpm/manager/form/edit',
+    name: 'bpmFormEditor',
     query: {
-      formId: rowId
+      id: rowId
     }
   })
 }
 
-// 详情操作 // TODO 芋艿:详情的实现
-// const detailForm = ref({
-//   fields: []
-// })
-// 表单详情弹出窗显示隐藏
-// const detailOpen = ref(false)
-
+// 详情操作
+const detailOpen = ref(false)
+const detailPreview = ref({
+  rule: [],
+  option: {}
+})
 const handleDetail = async (rowId: number) => {
-  console.log(rowId, '详情')
-  message.success('动态表单开发中,预计 2 月底完成')
-  // FormApi.getFormApi(row.id).then((response) => {
-  //   // 设置值
-  //   const data = response.data
-  //   detailForm.value = {
-  //     ...JSON.parse(data.conf),
-  //     fields: decodeFields([], data.fields)
-  //   }
-  //   // 弹窗打开
-  //   detailOpen.value = true
-  // })
+  // 设置表单
+  const data = await FormApi.getFormApi(rowId)
+  setConfAndFields2(detailPreview, data.conf, data.fields)
+  // 弹窗打开
+  detailOpen.value = true
 }
 </script>