Files
2025-11-13 05:05:53 +08:00

321 lines
12 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
export const runtime = 'edge'
import { useEffect } from "react"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import * as z from "zod"
import { Card, CardContent, CardHeader, CardTitle } from "@/registry/new-york/ui/card"
import { Input } from "@/registry/new-york/ui/input"
import { Button } from "@/registry/new-york/ui/button"
import { useToast } from "@/registry/new-york/hooks/use-toast"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/registry/new-york/ui/tabs"
import { Icons } from "@/components/icons"
import { Textarea } from "@/registry/new-york/ui/textarea"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/registry/new-york/ui/select"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/registry/new-york/ui/form"
import { SiteConfigService} from "@/services/siteConfigService"
import type { SiteConfig } from "@/types/site"
import { Toaster } from "@/registry/new-york/ui/toaster"
const formSchema = z.object({
basic: z.object({
title: z.string().min(2, {
message: "标题至少需要2个字符.",
}),
description: z.string().min(10, {
message: "描述至少需要10个字符.",
}),
keywords: z.string(),
}),
appearance: z.object({
logo: z.string().regex(/^(https?:\/\/|\/)\S+$/i, {
message: "请输入有效的URL地址或相对路径",
}),
favicon: z.string().regex(/^(https?:\/\/|\/)\S+$/i, {
message: "请输入有效的URL地址或相对路径",
}),
theme: z.enum(['light', 'dark', 'system']),
}),
navigation: z.object({
linkTarget: z.enum(['_blank', '_self']),
}),
})
export default function SiteSettings() {
const { toast } = useToast()
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
basic: {
title: "",
description: "",
keywords: "",
},
appearance: {
logo: "",
favicon: "",
theme: "system",
},
navigation: {
linkTarget: "_blank",
},
},
})
useEffect(() => {
const loadConfig = async () => {
const siteConfigService = new SiteConfigService();
const config = await siteConfigService.getSiteConfig();
if (config) {
form.reset(config);
}
}
loadConfig()
}, [])
async function onSubmit(values: z.infer<typeof formSchema>) {
try {
const siteConfigService = new SiteConfigService();
await siteConfigService.updateSiteConfig(values);
toast({
title: "成功",
description: "站点信息已保存",
});
} catch (error) {
toast({
title: "错误",
description: "保存失败",
variant: "destructive",
});
}
}
return (
<>
<div className="space-y-6">
<Tabs defaultValue="basic" className="space-y-4">
<TabsList>
<TabsTrigger value="basic"></TabsTrigger>
<TabsTrigger value="appearance"></TabsTrigger>
<TabsTrigger value="navigation"></TabsTrigger>
</TabsList>
<TabsContent value="basic">
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="basic.title"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Input placeholder="输入网站标题" {...field} />
</FormControl>
<FormDescription>
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="basic.description"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Textarea
placeholder="输入网站描述"
className="resize-none"
{...field}
/>
</FormControl>
<FormDescription>
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="basic.keywords"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Input placeholder="输入关键词,用英文逗号分隔" {...field} />
</FormControl>
<FormDescription>
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-start">
<Button
type="submit"
className="w-[120px]"
disabled={form.formState.isSubmitting}
>
{form.formState.isSubmitting ? (
<>
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
</>
) : (
<>
<Icons.save className="mr-2 h-4 w-4" />
</>
)}
</Button>
</div>
</form>
</Form>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="appearance">
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="appearance.logo"
render={({ field }) => (
<FormItem>
<FormLabel>Logo URL</FormLabel>
<FormControl>
<Input placeholder="输入Logo图片URL" {...field} />
</FormControl>
<FormDescription>
建议尺寸: 160x64px
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="appearance.favicon"
render={({ field }) => (
<FormItem>
<FormLabel>Favicon URL</FormLabel>
<FormControl>
<Input placeholder="输入网站图标URL" {...field} />
</FormControl>
<FormDescription>
建议尺寸: 32x32px
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-start">
<Button
type="submit"
className="w-[120px]"
disabled={form.formState.isSubmitting}
>
{form.formState.isSubmitting ? (
<>
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
</>
) : (
<>
<Icons.save className="mr-2 h-4 w-4" />
</>
)}
</Button>
</div>
</form>
</Form>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="navigation">
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="navigation.linkTarget"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="选择链接打开方式" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="_blank"> (_blank)</SelectItem>
<SelectItem value="_self"> (_self)</SelectItem>
</SelectContent>
</Select>
<FormDescription>
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-start">
<Button
type="submit"
className="w-[120px]"
disabled={form.formState.isSubmitting}
>
{form.formState.isSubmitting ? (
<>
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
</>
) : (
<>
<Icons.save className="mr-2 h-4 w-4" />
</>
)}
</Button>
</div>
</form>
</Form>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
<Toaster />
</>
)
}