'use client' import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import * as z from "zod" import { Button } from "@/registry/new-york/ui/button" import { Input } from "@/registry/new-york/ui/input" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription, } from "@/registry/new-york/ui/form" import { NavigationSubItem } from "@/types/navigation" import { Icons } from "@/components/icons" import { Textarea } from "@/registry/new-york/ui/textarea" import { Switch } from "@/registry/new-york/ui/switch" import { useState, useEffect } from "react" import { useToast } from "@/registry/new-york/hooks/use-toast" const formSchema = z.object({ id: z.string().optional(), title: z.string().min(2, { message: "网站标题至少需要2个字符" }), href: z.string().url({ message: "请输入有效的网站链接" }), icon: z.string().optional(), description: z.string().optional(), enabled: z.boolean().default(true), }) interface AddItemFormProps { onSubmit: (values: NavigationSubItem) => Promise onCancel: () => void defaultValues?: NavigationSubItem } export function AddItemForm({ onSubmit, onCancel, defaultValues }: AddItemFormProps) { const { toast } = useToast() const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: defaultValues || { id: String(Date.now()), title: "", href: "", icon: "", description: "", enabled: true, } }) const isSubmitting = form.formState.isSubmitting const [isUploading, setIsUploading] = useState(false) const [isFetchingMetadata, setIsFetchingMetadata] = useState(false) // 监听 href 字段变化,自动获取网站信息 const hrefValue = form.watch("href") useEffect(() => { const timeoutId = setTimeout(() => { if (hrefValue && isValidUrl(hrefValue) && !defaultValues) { fetchWebsiteMetadata(hrefValue) } }, 1000) // 延迟1秒执行,避免频繁请求 return () => clearTimeout(timeoutId) }, [hrefValue, defaultValues]) const isValidUrl = (string: string): boolean => { try { new URL(string) return true } catch (_) { return false } } const fetchWebsiteMetadata = async (url: string) => { if (isFetchingMetadata) return setIsFetchingMetadata(true) try { const response = await fetch('/api/website-metadata', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ url }), }) if (!response.ok) { throw new Error('获取网站信息失败') } const metadata = await response.json() // 只在字段为空时自动填充 if (!form.getValues('title')) { form.setValue('title', metadata.title) } if (!form.getValues('description')) { form.setValue('description', metadata.description) } if (!form.getValues('icon') && metadata.icon) { form.setValue('icon', metadata.icon) } toast({ title: "成功", description: "已自动获取网站信息" }) } catch (error) { console.error('Failed to fetch website metadata:', error) toast({ title: "提示", description: "自动获取网站信息失败,请手动填写", variant: "destructive" }) } finally { setIsFetchingMetadata(false) } } return (
{ try { const values: NavigationSubItem = { id: data.id || crypto.randomUUID(), title: data.title, href: data.href, description: data.description, icon: data.icon, enabled: data.enabled } await onSubmit(values) } catch (error) { console.error('保存失败:', error) } })} className="space-y-4"> ( 网站链接
{isFetchingMetadata && (
)}
输入完整的网站链接后,系统将自动获取网站标题、描述和图标
)} /> ( 网站标题 )} /> ( 图标
{field.value && (
图标预览 { const target = e.target as HTMLImageElement target.style.display = 'none' }} />
)}
系统会自动获取网站图标,也可手动输入URL或上传本地图片
)} /> ( 网站描述