feat: 流程模型新增: 表单设计
							parent
							
								
									43b7ac52b1
								
							
						
					
					
						commit
						da3a580711
					
				|  | @ -1,6 +1,7 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import type { BpmCategoryApi } from '#/api/bpm/category'; | import type { BpmCategoryApi } from '#/api/bpm/category'; | ||||||
| import type { BpmProcessDefinitionApi } from '#/api/bpm/definition'; | import type { BpmProcessDefinitionApi } from '#/api/bpm/definition'; | ||||||
|  | import type { BpmFormApi } from '#/api/bpm/form'; | ||||||
| import type { SystemDeptApi } from '#/api/system/dept'; | import type { SystemDeptApi } from '#/api/system/dept'; | ||||||
| import type { SystemUserApi } from '#/api/system/user'; | import type { SystemUserApi } from '#/api/system/user'; | ||||||
| 
 | 
 | ||||||
|  | @ -16,6 +17,7 @@ import { Button, Card, message } from 'ant-design-vue'; | ||||||
| 
 | 
 | ||||||
| import { getCategorySimpleList } from '#/api/bpm/category'; | import { getCategorySimpleList } from '#/api/bpm/category'; | ||||||
| import { getProcessDefinition } from '#/api/bpm/definition'; | import { getProcessDefinition } from '#/api/bpm/definition'; | ||||||
|  | import { getFormSimpleList } from '#/api/bpm/form'; | ||||||
| import { | import { | ||||||
|   createModel, |   createModel, | ||||||
|   deployModel, |   deployModel, | ||||||
|  | @ -26,6 +28,7 @@ import { getSimpleDeptList } from '#/api/system/dept'; | ||||||
| import { getSimpleUserList } from '#/api/system/user'; | import { getSimpleUserList } from '#/api/system/user'; | ||||||
| 
 | 
 | ||||||
| import BasicInfo from './modules/basic-info.vue'; | import BasicInfo from './modules/basic-info.vue'; | ||||||
|  | import FormDesign from './modules/form-design.vue'; | ||||||
| 
 | 
 | ||||||
| defineOptions({ name: 'BpmModelCreate' }); | defineOptions({ name: 'BpmModelCreate' }); | ||||||
| 
 | 
 | ||||||
|  | @ -62,7 +65,9 @@ const route = useRoute(); | ||||||
| const userStore = useUserStore(); | const userStore = useUserStore(); | ||||||
| 
 | 
 | ||||||
| // 基础信息组件引用 | // 基础信息组件引用 | ||||||
| const basicInfoRef = ref(); | const basicInfoRef = ref<InstanceType<typeof BasicInfo>>(); | ||||||
|  | // 表单设计组件引用 | ||||||
|  | const formDesignRef = ref<InstanceType<typeof FormDesign>>(); | ||||||
| 
 | 
 | ||||||
| /** 步骤校验函数 */ | /** 步骤校验函数 */ | ||||||
| const validateBasic = async () => { | const validateBasic = async () => { | ||||||
|  | @ -71,7 +76,7 @@ const validateBasic = async () => { | ||||||
| 
 | 
 | ||||||
| /** 表单设计校验 */ | /** 表单设计校验 */ | ||||||
| const validateForm = async () => { | const validateForm = async () => { | ||||||
|   // TODO |   await formDesignRef.value?.validate(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** 流程设计校验 */ | /** 流程设计校验 */ | ||||||
|  | @ -132,7 +137,7 @@ provide('processData', processData); | ||||||
| provide('modelData', formData); | provide('modelData', formData); | ||||||
| 
 | 
 | ||||||
| // 数据列表 | // 数据列表 | ||||||
| // const formList = ref([]) | const formList = ref<BpmFormApi.FormVO[]>([]); | ||||||
| const categoryList = ref<BpmCategoryApi.CategoryVO[]>([]); | const categoryList = ref<BpmCategoryApi.CategoryVO[]>([]); | ||||||
| const userList = ref<SystemUserApi.User[]>([]); | const userList = ref<SystemUserApi.User[]>([]); | ||||||
| const deptList = ref<SystemDeptApi.Dept[]>([]); | const deptList = ref<SystemDeptApi.Dept[]>([]); | ||||||
|  | @ -187,8 +192,8 @@ const initData = async () => { | ||||||
|     formData.value.managerUserIds.push(userStore.userInfo?.userId); |     formData.value.managerUserIds.push(userStore.userInfo?.userId); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // TODO 获取表单列表 |   // 获取表单列表 | ||||||
|   // formList.value = await getFormSimpleList() |   formList.value = await getFormSimpleList(); | ||||||
|   categoryList.value = await getCategorySimpleList(); |   categoryList.value = await getCategorySimpleList(); | ||||||
|   // 获取用户列表 |   // 获取用户列表 | ||||||
|   userList.value = await getSimpleUserList(); |   userList.value = await getSimpleUserList(); | ||||||
|  | @ -393,9 +398,8 @@ onMounted(async () => { | ||||||
| /** 添加组件卸载前的清理 */ | /** 添加组件卸载前的清理 */ | ||||||
| onBeforeUnmount(() => { | onBeforeUnmount(() => { | ||||||
|   // 清理所有的引用 |   // 清理所有的引用 | ||||||
|   basicInfoRef.value = null; |   basicInfoRef.value = undefined; | ||||||
|   // TODO 后续加 |   formDesignRef.value = undefined; | ||||||
|   // formDesignRef.value = null; |  | ||||||
|   // processDesignRef.value = null; |   // processDesignRef.value = null; | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
|  | @ -471,7 +475,7 @@ onBeforeUnmount(() => { | ||||||
|       <Card :body-style="{ padding: '10px' }" class="mb-4"> |       <Card :body-style="{ padding: '10px' }" class="mb-4"> | ||||||
|         <div class="mt-[50px]"> |         <div class="mt-[50px]"> | ||||||
|           <!-- 第一步:基本信息 --> |           <!-- 第一步:基本信息 --> | ||||||
|           <div v-if="currentStep === 0" class="mx-auto w-[560px]"> |           <div v-if="currentStep === 0" class="mx-auto w-4/6"> | ||||||
|             <BasicInfo |             <BasicInfo | ||||||
|               v-model="formData" |               v-model="formData" | ||||||
|               :category-list="categoryList" |               :category-list="categoryList" | ||||||
|  | @ -480,13 +484,19 @@ onBeforeUnmount(() => { | ||||||
|               ref="basicInfoRef" |               ref="basicInfoRef" | ||||||
|             /> |             /> | ||||||
|           </div> |           </div> | ||||||
| 
 |           <!-- 第二步:表单设计  --> | ||||||
|           <!-- 第二步:表单设计 TODO --> |           <div v-show="currentStep === 1" class="mx-auto w-4/6"> | ||||||
|  |             <FormDesign | ||||||
|  |               v-model="formData" | ||||||
|  |               :form-list="formList" | ||||||
|  |               ref="formDesignRef" | ||||||
|  |             /> | ||||||
|  |           </div> | ||||||
| 
 | 
 | ||||||
|           <!-- 第三步:流程设计 TODO --> |           <!-- 第三步:流程设计 TODO --> | ||||||
| 
 | 
 | ||||||
|           <!-- 第四步:更多设置 TODO --> |           <!-- 第四步:更多设置 TODO --> | ||||||
|           <div v-show="currentStep === 3" class="mx-auto w-[700px]"></div> |           <div v-show="currentStep === 3" class="mx-auto w-4/6"></div> | ||||||
|         </div> |         </div> | ||||||
|       </Card> |       </Card> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ import type { SystemUserApi } from '#/api/system/user'; | ||||||
| 
 | 
 | ||||||
| import { ref, watch } from 'vue'; | import { ref, watch } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { IconifyIcon, Plus, ShieldQuestion, X } from '@vben/icons'; | import { CircleHelp, IconifyIcon, Plus, X } from '@vben/icons'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
|   Avatar, |   Avatar, | ||||||
|  | @ -65,6 +65,7 @@ const rules: Record<string, Rule[]> = { | ||||||
|   category: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }], |   category: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }], | ||||||
|   type: [{ required: true, message: '流程类型不能为空', trigger: 'blur' }], |   type: [{ required: true, message: '流程类型不能为空', trigger: 'blur' }], | ||||||
|   visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }], |   visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }], | ||||||
|  |   // TODO 这个的校验好像没有起作用 | ||||||
|   managerUserIds: [ |   managerUserIds: [ | ||||||
|     { required: true, message: '流程管理员不能为空', trigger: 'blur' }, |     { required: true, message: '流程管理员不能为空', trigger: 'blur' }, | ||||||
|   ], |   ], | ||||||
|  | @ -219,9 +220,7 @@ const validate = async () => { | ||||||
|   await formRef.value?.validate(); |   await formRef.value?.validate(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| defineExpose({ | defineExpose({ validate }); | ||||||
|   validate, |  | ||||||
| }); |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  | @ -229,8 +228,8 @@ defineExpose({ | ||||||
|     ref="formRef" |     ref="formRef" | ||||||
|     :model="modelData" |     :model="modelData" | ||||||
|     :rules="rules" |     :rules="rules" | ||||||
|     :label-col="{ span: 6 }" |     :label-col="{ span: 4 }" | ||||||
|     :wrapper-col="{ span: 18 }" |     :wrapper-col="{ span: 20 }" | ||||||
|     class="mt-5" |     class="mt-5" | ||||||
|   > |   > | ||||||
|     <Form.Item label="流程标识" name="key" class="mb-5"> |     <Form.Item label="流程标识" name="key" class="mb-5"> | ||||||
|  | @ -247,7 +246,7 @@ defineExpose({ | ||||||
|           " |           " | ||||||
|           placement="top" |           placement="top" | ||||||
|         > |         > | ||||||
|           <ShieldQuestion class="ml-1 text-gray-500" /> |           <CircleHelp class="ml-1 size-5 text-gray-900" /> | ||||||
|         </Tooltip> |         </Tooltip> | ||||||
|       </div> |       </div> | ||||||
|     </Form.Item> |     </Form.Item> | ||||||
|  |  | ||||||
|  | @ -0,0 +1,187 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { Rule } from 'ant-design-vue/es/form'; | ||||||
|  | 
 | ||||||
|  | import type { BpmFormApi } from '#/api/bpm/form'; | ||||||
|  | 
 | ||||||
|  | import { ref, watch } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { CircleHelp } from '@vben/icons'; | ||||||
|  | 
 | ||||||
|  | import FormCreate from '@form-create/ant-design-vue'; | ||||||
|  | import { | ||||||
|  |   Form, | ||||||
|  |   FormItem, | ||||||
|  |   Input, | ||||||
|  |   Radio, | ||||||
|  |   RadioGroup, | ||||||
|  |   Select, | ||||||
|  |   SelectOption, | ||||||
|  |   Tooltip, | ||||||
|  | } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { getFormDetail } from '#/api/bpm/form'; | ||||||
|  | import { | ||||||
|  |   BpmModelFormType, | ||||||
|  |   DICT_TYPE, | ||||||
|  |   getDictOptions, | ||||||
|  |   setConfAndFields2, | ||||||
|  | } from '#/utils'; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   formList: { | ||||||
|  |     type: Array<BpmFormApi.FormVO>, | ||||||
|  |     required: true, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const formRef = ref(); | ||||||
|  | 
 | ||||||
|  | // 创建本地数据副本 | ||||||
|  | const modelData = defineModel<any>(); | ||||||
|  | 
 | ||||||
|  | // 表单预览数据 | ||||||
|  | const formPreview = ref({ | ||||||
|  |   formData: {} as any, | ||||||
|  |   rule: [], | ||||||
|  |   option: { | ||||||
|  |     submitBtn: false, | ||||||
|  |     resetBtn: false, | ||||||
|  |     formData: {}, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | /** 监听表单ID变化,加载表单数据 */ | ||||||
|  | watch( | ||||||
|  |   () => modelData.value.formId, | ||||||
|  |   async (newFormId) => { | ||||||
|  |     if (newFormId && modelData.value.formType === BpmModelFormType.NORMAL) { | ||||||
|  |       const data = await getFormDetail(newFormId); | ||||||
|  |       setConfAndFields2(formPreview.value, data.conf, data.fields); | ||||||
|  |       // 设置只读 | ||||||
|  |       formPreview.value.rule.forEach((item: any) => { | ||||||
|  |         item.props = { ...item.props, disabled: true }; | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       formPreview.value.rule = []; | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { immediate: true }, | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | const rules: Record<string, Rule[]> = { | ||||||
|  |   formType: [{ required: true, message: '表单类型不能为空', trigger: 'blur' }], | ||||||
|  |   formId: [{ required: true, message: '流程表单不能为空', trigger: 'blur' }], | ||||||
|  |   formCustomCreatePath: [ | ||||||
|  |     { required: true, message: '表单提交路由不能为空', trigger: 'blur' }, | ||||||
|  |   ], | ||||||
|  |   formCustomViewPath: [ | ||||||
|  |     { required: true, message: '表单查看地址不能为空', trigger: 'blur' }, | ||||||
|  |   ], | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** 表单校验 */ | ||||||
|  | const validate = async () => { | ||||||
|  |   await formRef.value?.validate(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | defineExpose({ validate }); | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <Form | ||||||
|  |     ref="formRef" | ||||||
|  |     :model="modelData" | ||||||
|  |     :rules="rules" | ||||||
|  |     :label-col="{ span: 4 }" | ||||||
|  |     :wrapper-col="{ span: 20 }" | ||||||
|  |     class="mt-5" | ||||||
|  |   > | ||||||
|  |     <FormItem label="表单类型" name="formType" class="mb-5"> | ||||||
|  |       <RadioGroup v-model:value="modelData.formType"> | ||||||
|  |         <Radio | ||||||
|  |           v-for="dict in getDictOptions( | ||||||
|  |             DICT_TYPE.BPM_MODEL_FORM_TYPE, | ||||||
|  |             'number', | ||||||
|  |           )" | ||||||
|  |           :key="dict.value as string" | ||||||
|  |           :value="dict.value" | ||||||
|  |         > | ||||||
|  |           {{ dict.label }} | ||||||
|  |         </Radio> | ||||||
|  |       </RadioGroup> | ||||||
|  |     </FormItem> | ||||||
|  |     <FormItem | ||||||
|  |       v-if="modelData.formType === BpmModelFormType.NORMAL" | ||||||
|  |       label="流程表单" | ||||||
|  |       name="formId" | ||||||
|  |       class="mb-5" | ||||||
|  |     > | ||||||
|  |       <Select v-model:value="modelData.formId" clearable> | ||||||
|  |         <SelectOption | ||||||
|  |           v-for="form in props.formList" | ||||||
|  |           :key="form.id" | ||||||
|  |           :value="form.id" | ||||||
|  |         > | ||||||
|  |           {{ form.name }} | ||||||
|  |         </SelectOption> | ||||||
|  |         > | ||||||
|  |       </Select> | ||||||
|  |     </FormItem> | ||||||
|  |     <FormItem | ||||||
|  |       v-if="modelData.formType === BpmModelFormType.CUSTOM" | ||||||
|  |       label="表单提交路由" | ||||||
|  |       name="formCustomCreatePath" | ||||||
|  |       class="mb-5" | ||||||
|  |     > | ||||||
|  |       <div class="flex items-center"> | ||||||
|  |         <Input | ||||||
|  |           v-model:value="modelData.formCustomCreatePath" | ||||||
|  |           placeholder="请输入表单提交路由" | ||||||
|  |         /> | ||||||
|  |         <Tooltip | ||||||
|  |           title="自定义表单的提交路径,使用 Vue 的路由地址, 例如说: bpm/oa/leave/create.vue" | ||||||
|  |           placement="top" | ||||||
|  |         > | ||||||
|  |           <CircleHelp class="ml-1 size-5 text-gray-900" /> | ||||||
|  |         </Tooltip> | ||||||
|  |       </div> | ||||||
|  |     </FormItem> | ||||||
|  |     <FormItem | ||||||
|  |       v-if="modelData.formType === BpmModelFormType.CUSTOM" | ||||||
|  |       label="表单查看地址" | ||||||
|  |       name="formCustomViewPath" | ||||||
|  |       class="mb-5" | ||||||
|  |     > | ||||||
|  |       <div class="flex items-center"> | ||||||
|  |         <Input | ||||||
|  |           v-model:value="modelData.formCustomViewPath" | ||||||
|  |           placeholder="请输入表单查看的组件地址" | ||||||
|  |         /> | ||||||
|  |         <Tooltip | ||||||
|  |           title="自定义表单的查看组件地址,使用 Vue 的组件地址,例如说:bpm/oa/leave/detail.vue" | ||||||
|  |           placement="top" | ||||||
|  |         > | ||||||
|  |           <CircleHelp class="ml-1 size-5 text-gray-900" /> | ||||||
|  |         </Tooltip> | ||||||
|  |       </div> | ||||||
|  |     </FormItem> | ||||||
|  |     <!-- 表单预览 --> | ||||||
|  |     <div | ||||||
|  |       v-if=" | ||||||
|  |         modelData.formType === BpmModelFormType.NORMAL && | ||||||
|  |         modelData.formId && | ||||||
|  |         formPreview.rule.length > 0 | ||||||
|  |       " | ||||||
|  |       class="mb-5 mt-7 rounded-sm border border-solid border-gray-200 p-5" | ||||||
|  |     > | ||||||
|  |       <div class="mb-[15px] flex items-center"> | ||||||
|  |         <div class="mr-[10px] h-[15px] w-[4px] bg-[#1890ff]"></div> | ||||||
|  |         <span class="text-[15px] font-bold">表单预览</span> | ||||||
|  |       </div> | ||||||
|  |       <FormCreate | ||||||
|  |         v-model:api="formPreview.formData" | ||||||
|  |         :rule="formPreview.rule" | ||||||
|  |         :option="formPreview.option" | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |   </Form> | ||||||
|  | </template> | ||||||
		Loading…
	
		Reference in New Issue
	
	 jason
						jason