"use client" import { AdminNav } from "@/components/admin-nav" import { ThemeToggle } from "@/components/theme-toggle" import { Button } from "@/components/ui/button" import { Calendar } from "@/components/ui/calendar" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" import { Combobox } from "@/components/ui/combobox" import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form" import { Input } from "@/components/ui/input" import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" import { Textarea } from "@/components/ui/textarea" import { useToast } from "@/components/ui/use-toast" import { useAuth } from "@/context/auth-context" import { zodResolver } from "@hookform/resolvers/zod" import { format } from "date-fns" import { ArrowLeft, BookOpen, CalendarIcon, ImageIcon, Loader2, Plus, Save, X } from "lucide-react" import { useRouter } from "next/navigation" import { useEffect, useState } from "react" import { useFieldArray, useForm } from "react-hook-form" import * as z from "zod" import { fetchWithAuth } from "@/lib/api" interface Publisher { publisherId: number name: string address: string } const formSchema = z.object({ title: z.string().min(1, "标题不能为空"), isbn: z.string().min(1, "ISBN不能为空"), price: z.coerce.number().min(0, "价格不能为负数"), stock: z.coerce.number().min(0, "库存不能为负数").int("库存必须是整数"), publishDate: z.date(), publisherId: z.coerce.number(), description: z.string().optional(), coverImage: z.string().optional(), authors: z .array( z.object({ name: z.string().min(1, "作者名不能为空"), }), ) .min(1, "至少需要一位作者"), }) export default function AddBookPage() { const { user } = useAuth() const router = useRouter() const { toast } = useToast() const [publishers, setPublishers] = useState([]) const [loading, setLoading] = useState(false) const [coverPreview, setCoverPreview] = useState(null) const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { title: "", isbn: "", price: 0, stock: 0, publishDate: new Date(), publisherId: 0, description: "", coverImage: "", authors: [{ name: "" }], }, }) const { fields, append, remove } = useFieldArray({ control: form.control, name: "authors", }) // 监听封面图片URL变化,更新预览 const coverImageValue = form.watch("coverImage") useEffect(() => { if (coverImageValue) { setCoverPreview(coverImageValue) } }, [coverImageValue]) useEffect(() => { // 检查用户是否登录且是管理员 if (!user) { toast({ title: "请先登录", description: "您需要登录后才能访问管理页面", variant: "destructive", }) router.push("/login") return } if (!user.isAdmin) { toast({ title: "权限不足", description: "您没有管理员权限", variant: "destructive", }) router.push("/") return } // 获取出版社列表 const fetchPublishers = async () => { try { const response = await fetchWithAuth("publisher/all") const result = await response.json() if (result.code === 0) { setPublishers(result.data) } else { toast({ variant: "destructive", title: "获取出版社失败", description: result.msg || "无法获取出版社信息", }) } } catch (error) { toast({ variant: "destructive", title: "获取出版社失败", description: "服务器连接错误,请稍后再试", }) } } fetchPublishers() }, [user, router, toast]) const onSubmit = async (values: z.infer) => { setLoading(true) try { const bookData = { ...values, authors: values.authors.map((author) => author.name), } const response = await fetchWithAuth("book/admin/add", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(bookData), }) const result = await response.json() if (result.code === 0) { toast({ title: "添加成功", description: "图书已成功添加", }) router.push("/admin/books") } else { toast({ variant: "destructive", title: "添加失败", description: result.msg || "无法添加图书", }) } } catch (error) { toast({ variant: "destructive", title: "添加失败", description: "服务器连接错误,请稍后再试", }) } finally { setLoading(false) } } if (!user || !user.isAdmin) { return null } return (
图书管理系统 - 管理后台

添加图书

基本信息 填写图书的基本信息 ( 标题 )} /> ( ISBN )} />
( 价格 )} /> ( 库存 )} />
( 出版日期 )} /> ( 出版社 ({ label: publisher.name, value: publisher.publisherId.toString(), }))} value={field.value.toString()} onChange={(value) => field.onChange(Number.parseInt(value))} placeholder="选择出版社" /> )} />
封面与描述 填写图书的封面和描述信息 ( 封面图片URL 输入图书封面的URL地址 )} />

封面预览

{coverPreview ? ( 封面预览 setCoverPreview(null)} /> ) : (

无封面图片

)}
( 描述