'use client'; import { useEffect } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { Loader2 } from 'lucide-react'; import { toast } from 'sonner'; import { z } from 'zod'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { type OriginItem, type OriginMutationPayload, OriginService, } from '@/lib/services/openflare'; const originSchema = z.object({ name: z.string().max(255), address: z .string() .trim() .min(1, '请输入源站地址') .refine( (value) => !/[/?#]/.test(value) && !value.includes('://'), '源站地址格式不合法', ), remark: z.string().max(255), }); type OriginFormValues = z.infer; const originsQueryKey = ['openflare', 'origins'] as const; function toFormValues(origin?: OriginItem | null): OriginFormValues { if (!origin) return { name: '', address: '', remark: '' }; return { name: origin.name, address: origin.address, remark: origin.remark || '', }; } function toPayload(values: OriginFormValues): OriginMutationPayload { return { name: values.name.trim(), address: values.address.trim(), remark: values.remark.trim(), }; } interface OriginEditorDialogProps { open: boolean; onOpenChange: (open: boolean) => void; origin?: OriginItem | null; onSaved?: () => void; } export function OriginEditorDialog({ open, onOpenChange, origin, onSaved, }: OriginEditorDialogProps) { const queryClient = useQueryClient(); const form = useForm({ resolver: zodResolver(originSchema), defaultValues: toFormValues(origin), }); useEffect(() => { if (open) form.reset(toFormValues(origin)); }, [form, origin, open]); const mutation = useMutation({ mutationFn: async (values: OriginFormValues) => { const payload = toPayload(values); return origin ? OriginService.update(origin.id, payload) : OriginService.create(payload); }, onSuccess: async () => { toast.success(origin ? '源站已更新' : '源站已创建'); await queryClient.invalidateQueries({ queryKey: originsQueryKey }); if (origin) { await queryClient.invalidateQueries({ queryKey: ['openflare', 'origins', String(origin.id)], }); } onSaved?.(); onOpenChange(false); }, onError: (error) => { toast.error(error instanceof Error ? error.message : '保存失败'); }, }); return ( {origin ? '编辑源站' : '新增源站'} 源站作为规则里的可复用地址目录,协议和端口仍由规则决定。
mutation.mutate(values))} >
{form.formState.errors.address ? (

{form.formState.errors.address.message}

) : null}