246 lines
8.7 KiB
TypeScript
246 lines
8.7 KiB
TypeScript
"use client"
|
||
|
||
import { MainNav } from "@/components/main-nav"
|
||
import { SearchBar } from "@/components/search-bar"
|
||
import { ThemeToggle } from "@/components/theme-toggle"
|
||
import { UserNav } from "@/components/user-nav"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||
import { Separator } from "@/components/ui/separator"
|
||
import { useToast } from "@/components/ui/use-toast"
|
||
import { useAuth } from "@/context/auth-context"
|
||
import { CheckCircle, Package } from "lucide-react"
|
||
import { useRouter } from "next/navigation"
|
||
import { useEffect, useState, use } from "react"
|
||
import { fetchWithAuth } from "@/lib/api"
|
||
|
||
interface OrderItem {
|
||
orderItemId: number
|
||
bookId: number
|
||
bookTitle: string
|
||
quantity: number
|
||
unitPrice: number
|
||
}
|
||
|
||
interface Order {
|
||
orderId: number
|
||
readerId: number
|
||
readerName: string
|
||
orderDate: string
|
||
totalAmount: number
|
||
status: string
|
||
items: OrderItem[]
|
||
}
|
||
|
||
export default function OrderDetailPage({ params }: { params: Promise<{ id: string }> | { id: string } }) {
|
||
// 使用 React.use() 解包 params
|
||
const resolvedParams = "then" in params ? use(params) : params
|
||
const orderId = resolvedParams.id
|
||
|
||
const [order, setOrder] = useState<Order | null>(null)
|
||
const [loading, setLoading] = useState(true)
|
||
const { toast } = useToast()
|
||
const router = useRouter()
|
||
const { user } = useAuth()
|
||
|
||
useEffect(() => {
|
||
if (!user) {
|
||
toast({
|
||
title: "请先登录",
|
||
description: "您需要登录后才能查看订单",
|
||
variant: "destructive",
|
||
})
|
||
router.push("/login")
|
||
return
|
||
}
|
||
|
||
const fetchOrder = async () => {
|
||
try {
|
||
const response = await fetchWithAuth(`orders/${orderId}`)
|
||
const result = await response.json()
|
||
|
||
if (result.code === 0) {
|
||
setOrder(result.data)
|
||
} else {
|
||
toast({
|
||
variant: "destructive",
|
||
title: "获取订单失败",
|
||
description: result.msg || "无法获取订单信息",
|
||
})
|
||
}
|
||
} catch (error) {
|
||
toast({
|
||
variant: "destructive",
|
||
title: "获取订单失败",
|
||
description: "服务器连接错误,请稍后再试",
|
||
})
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
fetchOrder()
|
||
}, [orderId, toast, router, user])
|
||
|
||
if (loading) {
|
||
return (
|
||
<div className="flex min-h-screen flex-col">
|
||
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||
<div className="container flex h-16 items-center justify-between">
|
||
<MainNav />
|
||
<div className="flex items-center gap-4">
|
||
<SearchBar />
|
||
<ThemeToggle />
|
||
<UserNav />
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<main className="flex-1 container py-8">
|
||
<div className="flex justify-center items-center h-[60vh]">
|
||
<div className="animate-pulse text-xl">加载中...</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
if (!order) {
|
||
return (
|
||
<div className="flex min-h-screen flex-col">
|
||
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||
<div className="container flex h-16 items-center justify-between">
|
||
<MainNav />
|
||
<div className="flex items-center gap-4">
|
||
<SearchBar />
|
||
<ThemeToggle />
|
||
<UserNav />
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<main className="flex-1 container py-8">
|
||
<div className="flex justify-center items-center h-[60vh]">
|
||
<div className="text-xl">订单不存在</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="flex min-h-screen flex-col">
|
||
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||
<div className="container flex h-16 items-center justify-between">
|
||
<MainNav />
|
||
<div className="flex items-center gap-4">
|
||
<SearchBar />
|
||
<ThemeToggle />
|
||
<UserNav />
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<main className="flex-1">
|
||
<div className="container py-8">
|
||
<div className="flex flex-col space-y-8">
|
||
<div className="flex items-center space-x-2">
|
||
<Package className="h-6 w-6" />
|
||
<h1 className="text-3xl font-bold tracking-tight">订单详情</h1>
|
||
</div>
|
||
|
||
<div className="grid gap-8 md:grid-cols-3">
|
||
<div className="md:col-span-2 space-y-6">
|
||
<Card>
|
||
<CardHeader className="pb-4">
|
||
<CardTitle className="text-xl">订单状态</CardTitle>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="flex items-center gap-2 text-green-600 dark:text-green-400">
|
||
<CheckCircle className="h-5 w-5" />
|
||
<span className="font-medium">{order.status}</span>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<Card>
|
||
<CardHeader className="pb-4">
|
||
<CardTitle className="text-xl">订单商品</CardTitle>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="space-y-4">
|
||
{order.items.map((item) => (
|
||
<div key={item.orderItemId} className="flex items-center gap-4 py-2">
|
||
<div className="flex-1">
|
||
<h3 className="font-medium">{item.bookTitle}</h3>
|
||
<p className="text-sm text-muted-foreground">
|
||
单价: ¥{item.unitPrice.toFixed(2)} × {item.quantity}
|
||
</p>
|
||
</div>
|
||
<div className="text-right">¥{(item.unitPrice * item.quantity).toFixed(2)}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
|
||
<div>
|
||
<Card className="sticky top-24">
|
||
<CardHeader>
|
||
<CardTitle>订单信息</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-4">
|
||
<div className="space-y-2">
|
||
<div className="flex justify-between">
|
||
<span className="text-muted-foreground">订单号</span>
|
||
<span>{order.orderId}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-muted-foreground">下单时间</span>
|
||
<span>{new Date(order.orderDate).toLocaleString()}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-muted-foreground">用户名</span>
|
||
<span>{order.readerName}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<Separator />
|
||
|
||
<div className="space-y-2">
|
||
<div className="flex justify-between">
|
||
<span className="text-muted-foreground">商品总额</span>
|
||
<span>¥{order.totalAmount.toFixed(2)}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-muted-foreground">运费</span>
|
||
<span>免费</span>
|
||
</div>
|
||
</div>
|
||
|
||
<Separator />
|
||
|
||
<div className="flex justify-between font-medium">
|
||
<span>实付款</span>
|
||
<span className="text-lg">¥{order.totalAmount.toFixed(2)}</span>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex justify-center mt-8">
|
||
<Button onClick={() => router.push("/orders")}>查看所有订单</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
<footer className="border-t py-6 md:py-0">
|
||
<div className="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
|
||
<p className="text-center text-sm leading-loose text-muted-foreground md:text-left">
|
||
© {new Date().getFullYear()} 图书管理系统. 保留所有权利.
|
||
</p>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
)
|
||
}
|