From 92eb82583f3b85c84cbbd1265594af94e0b64b06 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Wed, 12 Nov 2025 13:18:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=89=E6=96=B9=E7=B3=BB=E7=BB=9F=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E5=8A=A0=E5=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Definition/List/components/EditModal.tsx | 422 +++++++++--------- .../pages/Workflow/Definition/List/index.tsx | 49 +- .../pages/Workflow/Definition/List/types.ts | 2 + 3 files changed, 257 insertions(+), 216 deletions(-) diff --git a/frontend/src/pages/Workflow/Definition/List/components/EditModal.tsx b/frontend/src/pages/Workflow/Definition/List/components/EditModal.tsx index 5471ef0f..cf125db7 100644 --- a/frontend/src/pages/Workflow/Definition/List/components/EditModal.tsx +++ b/frontend/src/pages/Workflow/Definition/List/components/EditModal.tsx @@ -1,4 +1,7 @@ import React, { useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import * as z from 'zod'; import { Dialog, DialogContent, @@ -8,8 +11,15 @@ import { DialogBody, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Loader2 } from 'lucide-react'; @@ -26,18 +36,41 @@ interface EditModalProps { record?: WorkflowDefinition; } +// Zod 验证 Schema +const workflowFormSchema = z.object({ + name: z.string() + .min(1, '请输入流程名称') + .max(100, '流程名称不能超过100个字符'), + key: z.string() + .min(1, '请输入流程标识') + .regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/, '流程标识只能包含字母、数字、下划线和连字符,且必须以字母或下划线开头') + .refine((val) => !/^xml/i.test(val), '流程标识不能以xml开头'), + categoryId: z.number({ + required_error: '请选择流程分类', + invalid_type_error: '请选择流程分类', + }), + formDefinitionId: z.number().optional().nullable(), + description: z.string().max(500, '描述不能超过500个字符').optional(), +}); + +type WorkflowFormValues = z.infer; + const EditModal: React.FC = ({ visible, onClose, onSuccess, record }) => { const { toast } = useToast(); const isEdit = !!record; const [submitting, setSubmitting] = useState(false); const [categories, setCategories] = useState([]); const [formDefinitions, setFormDefinitions] = useState([]); - const [formData, setFormData] = useState({ - name: '', - key: '', - categoryId: undefined as number | undefined, - formDefinitionId: undefined as number | undefined, - description: '', + + const form = useForm({ + resolver: zodResolver(workflowFormSchema), + defaultValues: { + name: '', + key: '', + categoryId: undefined as any, + formDefinitionId: undefined, + description: '', + }, }); useEffect(() => { @@ -45,18 +78,18 @@ const EditModal: React.FC = ({ visible, onClose, onSuccess, reco loadCategories(); loadFormDefinitions(); if (record) { - setFormData({ + form.reset({ name: record.name, key: record.key, categoryId: record.categoryId, - formDefinitionId: record.formDefinitionId, + formDefinitionId: record.formDefinitionId || undefined, description: record.description || '', }); } else { - setFormData({ + form.reset({ name: '', key: '', - categoryId: undefined, + categoryId: undefined as any, formDefinitionId: undefined, description: '', }); @@ -95,80 +128,38 @@ const EditModal: React.FC = ({ visible, onClose, onSuccess, reco } }; - const handleSubmit = async () => { - // 验证 - if (!formData.name.trim()) { - toast({ - title: '验证失败', - description: '请输入流程名称', - variant: 'destructive', - }); - return; - } - if (!formData.key.trim()) { - toast({ - title: '验证失败', - description: '请输入流程标识', - variant: 'destructive', - }); - return; - } - if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(formData.key)) { - toast({ - title: '验证失败', - description: '流程标识只能包含字母、数字、下划线和连字符,且必须以字母或下划线开头', - variant: 'destructive', - }); - return; - } - if (/^xml/i.test(formData.key)) { - toast({ - title: '验证失败', - description: '流程标识不能以xml开头', - variant: 'destructive', - }); - return; - } - if (!formData.categoryId) { - toast({ - title: '验证失败', - description: '请选择流程分类', - variant: 'destructive', - }); - return; - } - + const handleSubmit = async (values: WorkflowFormValues) => { setSubmitting(true); try { if (isEdit && record) { // 更新时需要传递完整的数据和版本号 const submitData: WorkflowDefinitionRequest = { - name: formData.name, - key: formData.key, - categoryId: formData.categoryId, - formDefinitionId: formData.formDefinitionId, - flowVersion: record.flowVersion, // 流程版本 - description: formData.description, + name: values.name, + key: values.key, + categoryId: values.categoryId, + formDefinitionId: values.formDefinitionId, + flowVersion: record.flowVersion, + description: values.description, graph: record.graph, bpmnXml: record.bpmnXml, status: record.status, - version: record.version, // 乐观锁版本号 + version: record.version, }; await updateDefinition(record.id, submitData); toast({ title: '更新成功', - description: `工作流 "${formData.name}" 已更新`, + description: `工作流 "${values.name}" 已更新`, }); } else { // 新建时初始化基本数据 const submitData: WorkflowDefinitionRequest = { - name: formData.name, - key: formData.key, - categoryId: formData.categoryId, - formDefinitionId: formData.formDefinitionId, - flowVersion: 1, // 新建时流程版本为1 - description: formData.description, + name: values.name, + key: values.key, + categoryId: values.categoryId, + formDefinitionId: values.formDefinitionId, + flowVersion: 1, + description: values.description, graph: { nodes: [], edges: [] }, status: 'DRAFT', }; @@ -176,7 +167,7 @@ const EditModal: React.FC = ({ visible, onClose, onSuccess, reco await saveDefinition(submitData); toast({ title: '创建成功', - description: `工作流 "${formData.name}" 已创建`, + description: `工作流 "${values.name}" 已创建`, }); } onSuccess?.(); @@ -200,143 +191,172 @@ const EditModal: React.FC = ({ visible, onClose, onSuccess, reco {isEdit ? '编辑流程' : '新建流程'} - - {/* 流程分类 */} -
- - - {isEdit && ( -

- 编辑时不可修改分类 -

- )} -
+ +
+ + + {/* 流程分类 */} + ( + + + 流程分类 * + + + {isEdit && ( +

+ 编辑时不可修改分类 +

+ )} + +
+ )} + /> - {/* 启动表单 */} -
- -
- - {formData.formDefinitionId && ( - - )} -
-

- 选择启动该工作流时需要填写的表单 -

-
+ {/* 启动表单 */} + ( + + 启动表单 +
+ + {field.value && ( + + )} +
+

+ 选择启动该工作流时需要填写的表单 +

+ +
+ )} + /> - {/* 流程标识 */} -
- - setFormData(prev => ({ ...prev, key: e.target.value }))} - disabled={isEdit} - className="h-10 font-mono" - /> -

- 只能包含字母、数字、下划线和连字符,建议使用小写字母和下划线 -

-
+ {/* 流程标识 */} + ( + + + 流程标识 * + {isEdit && (不可修改)} + + + + +

+ 只能包含字母、数字、下划线和连字符,建议使用小写字母和下划线 +

+ +
+ )} + /> - {/* 流程名称 */} -
- - setFormData(prev => ({ ...prev, name: e.target.value }))} - disabled={isEdit} - className="h-10" - /> -
+ {/* 流程名称 */} + ( + + + 流程名称 * + {isEdit && (不可修改)} + + + + + + + )} + /> - {/* 描述 */} -
- -