2025-05-22 20:42:10 +08:00

264 lines
9.7 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"
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 {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
import { useToast } from "@/components/ui/use-toast"
import { useAuth } from "@/context/auth-context"
import { Package } from "lucide-react"
import Link from "next/link"
import { useRouter } from "next/navigation"
import { useEffect, useState } 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[]
}
interface OrdersResponse {
pageNum: number
pageSize: number
total: number
data: Order[]
}
export default function OrdersPage() {
const [orders, setOrders] = useState<OrdersResponse | null>(null)
const [loading, setLoading] = useState(true)
const [currentPage, setCurrentPage] = useState(1)
const { toast } = useToast()
const router = useRouter()
const { user } = useAuth()
useEffect(() => {
if (!user) {
toast({
title: "请先登录",
description: "您需要登录后才能查看订单",
variant: "destructive",
})
router.push("/login")
return
}
const fetchOrders = async () => {
try {
const response = await fetchWithAuth(`orders/my?pageNum=${currentPage}&pageSize=10`)
const result = await response.json()
if (result.code === 0) {
setOrders(result.data)
} else {
toast({
variant: "destructive",
title: "获取订单失败",
description: result.msg || "无法获取订单信息",
})
}
} catch (error) {
toast({
variant: "destructive",
title: "获取订单失败",
description: "服务器连接错误,请稍后再试",
})
} finally {
setLoading(false)
}
}
fetchOrders()
}, [currentPage, toast, router, user])
const handlePageChange = (page: number) => {
setCurrentPage(page)
}
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>
)
}
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>
{!orders || orders.data.length === 0 ? (
<div className="flex flex-col items-center justify-center py-12">
<Package className="h-12 w-12 text-muted-foreground mb-4" />
<h2 className="text-xl font-semibold"></h2>
<p className="text-muted-foreground mt-2"></p>
<Button className="mt-4" onClick={() => router.push("/books")}>
</Button>
</div>
) : (
<>
<div className="grid gap-4">
{orders.data.map((order) => (
<Card key={order.orderId}>
<CardHeader className="pb-2">
<div className="flex justify-between items-center">
<CardTitle className="text-lg">: {order.orderId}</CardTitle>
<span className="text-sm text-green-600 dark:text-green-400 font-medium">{order.status}</span>
</div>
<p className="text-sm text-muted-foreground">
: {new Date(order.orderDate).toLocaleString()}
</p>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="grid gap-2">
{order.items.slice(0, 2).map((item) => (
<div key={item.orderItemId} className="flex justify-between items-center">
<div className="flex-1">
<p className="font-medium truncate">{item.bookTitle}</p>
<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>
))}
{order.items.length > 2 && (
<p className="text-sm text-muted-foreground">... {order.items.length} </p>
)}
</div>
<div className="flex justify-between items-center pt-2">
<div className="text-sm">
<span className="text-muted-foreground">: </span>
<span className="font-medium">¥{order.totalAmount.toFixed(2)}</span>
</div>
<Link href={`/orders/${order.orderId}`}>
<Button variant="outline" size="sm">
</Button>
</Link>
</div>
</div>
</CardContent>
</Card>
))}
</div>
{orders.total > orders.pageSize && (
<Pagination className="mt-8">
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href="#"
onClick={(e) => {
e.preventDefault()
if (currentPage > 1) handlePageChange(currentPage - 1)
}}
className={currentPage === 1 ? "pointer-events-none opacity-50" : ""}
/>
</PaginationItem>
{Array.from({ length: Math.ceil(orders.total / orders.pageSize) }).map((_, index) => (
<PaginationItem key={index}>
<PaginationLink
href="#"
onClick={(e) => {
e.preventDefault()
handlePageChange(index + 1)
}}
isActive={currentPage === index + 1}
>
{index + 1}
</PaginationLink>
</PaginationItem>
))}
<PaginationItem>
<PaginationNext
href="#"
onClick={(e) => {
e.preventDefault()
if (currentPage < Math.ceil(orders.total / orders.pageSize)) {
handlePageChange(currentPage + 1)
}
}}
className={
currentPage >= Math.ceil(orders.total / orders.pageSize)
? "pointer-events-none opacity-50"
: ""
}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
)}
</>
)}
</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">
&copy; {new Date().getFullYear()} . .
</p>
</div>
</footer>
</div>
)
}