diff --git a/frontend/src/pages/System/Release/List/components/ReleaseEditDialog.tsx b/frontend/src/pages/System/Release/List/components/ReleaseEditDialog.tsx new file mode 100644 index 00000000..1163394a --- /dev/null +++ b/frontend/src/pages/System/Release/List/components/ReleaseEditDialog.tsx @@ -0,0 +1,421 @@ +import React, { useState, useEffect } from 'react'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import * as z from 'zod'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogBody, + DialogFooter, +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Textarea } from '@/components/ui/textarea'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { useToast } from '@/components/ui/use-toast'; +import type { SystemReleaseResponse } from '../types'; +import { ModuleType, ModuleTypeLabels } from '../types'; +import { createSystemRelease, updateSystemRelease, getSystemReleaseList } from '../service'; +import { Switch } from '@/components/ui/switch'; + +// 创建动态表单验证 schema +const createReleaseFormSchema = (maxVersion: number, isEdit: boolean) => z.object({ + releaseVersion: isEdit + ? z.number().min(0.1, '版本号必须大于0') + : z.number() + .min(0.1, '版本号必须大于0') + .min(maxVersion, `版本号不能小于当前最大版本 ${maxVersion}`), + module: z.nativeEnum(ModuleType, { required_error: '请选择模块类型' }), + releaseDate: z.string().min(1, '请选择发布时间'), + changes: z.string().min(1, '请输入变更内容'), + notified: z.boolean(), + delayMinutes: z.number().min(0, '延迟分钟数不能为负数'), + estimatedDuration: z.number().min(1, '预计时长必须大于0'), + enableAutoShutdown: z.boolean(), +}); + +type ReleaseFormValues = z.infer>; + +interface ReleaseEditDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + release?: SystemReleaseResponse | null; + onSuccess?: () => void; +} + +export const ReleaseEditDialog: React.FC = ({ + open, + onOpenChange, + release, + onSuccess, +}) => { + const { toast } = useToast(); + const [loading, setLoading] = useState(false); + const [maxVersion, setMaxVersion] = useState(1.0); + const isEdit = !!release?.id; + + const form = useForm({ + resolver: zodResolver(createReleaseFormSchema(maxVersion, isEdit)), + defaultValues: { + releaseVersion: 1.0, + module: ModuleType.ALL, + releaseDate: '', + changes: '', + notified: false, + delayMinutes: 0, + estimatedDuration: 30, + enableAutoShutdown: false, + }, + }); + + // 初始化表单数据 + useEffect(() => { + const initForm = async () => { + if (open) { + if (release) { + // 编辑模式:回填数据 + form.reset({ + releaseVersion: release.releaseVersion, + module: release.module, + releaseDate: release.releaseDate, + changes: release.changes || '', + notified: release.notified ?? false, + delayMinutes: release.delayMinutes ?? 0, + estimatedDuration: release.estimatedDuration ?? 30, + enableAutoShutdown: release.enableAutoShutdown ?? false, + }); + } else { + // 新增模式:获取最大版本号 + try { + const queryParams: any = { + page: 0, + size: 1 + }; + const result = await getSystemReleaseList(queryParams); + const latestVersion = result?.content?.[0]?.releaseVersion || 0; + const nextVersion = Math.round((latestVersion + 0.1) * 10) / 10; // 最大版本 + 0.1 + setMaxVersion(latestVersion); + + // 设置默认发布时间为当前时间 + const now = new Date(); + const dateStr = now.toISOString().slice(0, 16).replace('T', ' ') + ':00'; + + form.reset({ + releaseVersion: nextVersion, + module: ModuleType.ALL, + releaseDate: dateStr, + changes: '', + notified: false, + delayMinutes: 0, + estimatedDuration: 30, + enableAutoShutdown: false, + }); + } catch (error) { + console.error('获取最大版本号失败:', error); + // 失败时使用默认值 + const now = new Date(); + const dateStr = now.toISOString().slice(0, 16).replace('T', ' ') + ':00'; + form.reset({ + releaseVersion: 1.0, + module: ModuleType.ALL, + releaseDate: dateStr, + changes: '', + notified: false, + delayMinutes: 0, + estimatedDuration: 30, + enableAutoShutdown: false, + }); + } + } + } + }; + + initForm(); + }, [release, open, form]); + + const onSubmit = async (values: ReleaseFormValues) => { + try { + setLoading(true); + // 确保数据符合 API 要求的类型 + const requestData = { + releaseVersion: values.releaseVersion, + module: values.module, + releaseDate: values.releaseDate, + changes: values.changes, + notified: values.notified, + delayMinutes: values.delayMinutes, + estimatedDuration: values.estimatedDuration, + enableAutoShutdown: values.enableAutoShutdown, + }; + + if (isEdit && release) { + await updateSystemRelease(release.id, requestData); + toast({ + title: '更新成功', + description: `版本 ${values.releaseVersion} 已更新`, + }); + } else { + await createSystemRelease(requestData); + toast({ + title: '创建成功', + description: `版本 ${values.releaseVersion} 已创建`, + }); + } + onSuccess?.(); + onOpenChange(false); + } catch (error: any) { + // 错误已在request拦截器中处理 + } finally { + setLoading(false); + } + }; + + return ( + + + + {isEdit ? '编辑版本' : '新增版本'} + + + +
+ +
+ {/* 版本号 */} + ( + + + 版本号 * + + + field.onChange(parseFloat(e.target.value) || 0)} + /> + + {!isEdit && maxVersion > 0 && ( +

+ 当前最大版本:{maxVersion}, + 新版本号不能小于此值 +

+ )} + +
+ )} + /> + + {/* 模块类型 */} + ( + + + 模块类型 * + + + + + )} + /> + + {/* 发布时间 */} + ( + + + 发布时间 * + + + { + const value = e.target.value ? e.target.value.replace('T', ' ') + ':00' : ''; + field.onChange(value); + }} + /> + + + + )} + /> + + {/* 延迟执行分钟数 */} + ( + + + 延迟执行(分钟) * + + + field.onChange(e.target.value ? parseInt(e.target.value) : 0)} + /> + +

+ 延迟执行分钟数(0表示立即执行) +

+ +
+ )} + /> + + {/* 预计维护时长 */} + ( + + + 预计时长(分钟) * + + + field.onChange(e.target.value ? parseInt(e.target.value) : 30)} + /> + +

+ 预计维护所需时长(分钟) +

+ +
+ )} + /> + + {/* 是否自动停止服务 */} + ( + +
+ 自动停止服务 +
+ 维护期间是否自动停止相关服务 +
+
+ + + +
+ )} + /> + + {/* 是否已通知 */} + {isEdit && ( + ( + +
+ 已发送通知 +
+ 标记此版本是否已发送通知给用户 +
+
+ + + +
+ )} + /> + )} + + {/* 变更内容 */} + ( + + + 变更内容 * + + +