202 lines
7.7 KiB
TypeScript
202 lines
7.7 KiB
TypeScript
import { ArrowLeft, CheckCircle, Clock, Star } from "lucide-react"
|
||
import Link from "next/link"
|
||
import { notFound } from "next/navigation"
|
||
import OrderActions from "@/components/order-actions"
|
||
import { Badge } from "@/components/ui/badge"
|
||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||
import { Separator } from "@/components/ui/separator"
|
||
import { statusLabels } from "@/lib/constants"
|
||
import { mockChatSessions, mockOrders, mockReviews } from "@/lib/mock-data"
|
||
import type { OrderStatus } from "@/lib/types"
|
||
|
||
const statusSteps: OrderStatus[] = [
|
||
"pending_payment",
|
||
"pending_accept",
|
||
"in_progress",
|
||
"pending_close",
|
||
"pending_review",
|
||
"completed",
|
||
]
|
||
|
||
export default async function OrderDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
||
const { id } = await params
|
||
const order = mockOrders.find((o) => o.id === id)
|
||
if (!order) notFound()
|
||
|
||
const reviews = mockReviews.filter((r) => r.orderId === id)
|
||
const chatSession = mockChatSessions.find((s) => s.orderId === id)
|
||
const currentStepIndex = statusSteps.indexOf(order.status)
|
||
|
||
return (
|
||
<div className="container mx-auto py-8 px-4 max-w-3xl">
|
||
<Link
|
||
href="/orders"
|
||
className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-4"
|
||
>
|
||
<ArrowLeft className="h-4 w-4" />
|
||
返回订单列表
|
||
</Link>
|
||
|
||
<div className="flex items-center justify-between mb-6">
|
||
<h1 className="text-2xl font-bold">订单详情</h1>
|
||
<Badge variant="outline">{statusLabels[order.status]}</Badge>
|
||
</div>
|
||
|
||
{order.status !== "disputed" && order.status !== "cancelled" && (
|
||
<Card className="mb-6">
|
||
<CardContent className="pt-6">
|
||
<div className="flex items-center justify-between">
|
||
{statusSteps.map((step, i) => {
|
||
const isActive = i <= currentStepIndex
|
||
const isCurrent = i === currentStepIndex
|
||
return (
|
||
<div key={step} className="flex flex-col items-center gap-1 flex-1">
|
||
<div
|
||
className={`h-8 w-8 rounded-full flex items-center justify-center text-xs font-medium ${
|
||
isCurrent
|
||
? "bg-primary text-primary-foreground"
|
||
: isActive
|
||
? "bg-primary/20 text-primary"
|
||
: "bg-muted text-muted-foreground"
|
||
}`}
|
||
>
|
||
{isActive ? <CheckCircle className="h-4 w-4" /> : i + 1}
|
||
</div>
|
||
<span className="text-[10px] text-muted-foreground text-center">
|
||
{statusLabels[step]}
|
||
</span>
|
||
</div>
|
||
)
|
||
})}
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
)}
|
||
|
||
<Card className="mb-6">
|
||
<CardHeader>
|
||
<CardTitle className="text-base">服务信息</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-3">
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-muted-foreground">服务</span>
|
||
<span>{order.service.title}</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-muted-foreground">游戏</span>
|
||
<span>{order.service.gameName}</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-muted-foreground">单价</span>
|
||
<span>
|
||
¥{order.service.price}/{order.service.unit}
|
||
</span>
|
||
</div>
|
||
<Separator />
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-muted-foreground">打手</span>
|
||
<Link href={`/player/${order.playerId}`} className="text-primary hover:underline">
|
||
{order.playerName}
|
||
</Link>
|
||
</div>
|
||
{order.shopName && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-muted-foreground">店铺</span>
|
||
<Link href={`/shop/${order.shopId}`} className="text-primary hover:underline">
|
||
{order.shopName}
|
||
</Link>
|
||
</div>
|
||
)}
|
||
<Separator />
|
||
<div className="flex justify-between text-sm font-medium">
|
||
<span>总价</span>
|
||
<span className="text-lg">¥{order.totalPrice}</span>
|
||
</div>
|
||
{order.note && (
|
||
<div className="text-sm">
|
||
<span className="text-muted-foreground">备注: </span>
|
||
{order.note}
|
||
</div>
|
||
)}
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<Card className="mb-6">
|
||
<CardHeader>
|
||
<CardTitle className="text-base">时间线</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-2 text-sm">
|
||
<div className="flex items-center gap-2">
|
||
<Clock className="h-3.5 w-3.5 text-muted-foreground" />
|
||
<span className="text-muted-foreground">下单时间:</span>
|
||
{new Date(order.createdAt).toLocaleString("zh-CN")}
|
||
</div>
|
||
{order.acceptedAt && (
|
||
<div className="flex items-center gap-2">
|
||
<CheckCircle className="h-3.5 w-3.5 text-green-500" />
|
||
<span className="text-muted-foreground">接单时间:</span>
|
||
{new Date(order.acceptedAt).toLocaleString("zh-CN")}
|
||
</div>
|
||
)}
|
||
{order.closedAt && (
|
||
<div className="flex items-center gap-2">
|
||
<CheckCircle className="h-3.5 w-3.5 text-green-500" />
|
||
<span className="text-muted-foreground">结单时间:</span>
|
||
{new Date(order.closedAt).toLocaleString("zh-CN")}
|
||
</div>
|
||
)}
|
||
{order.completedAt && (
|
||
<div className="flex items-center gap-2">
|
||
<CheckCircle className="h-3.5 w-3.5 text-green-500" />
|
||
<span className="text-muted-foreground">完成时间:</span>
|
||
{new Date(order.completedAt).toLocaleString("zh-CN")}
|
||
</div>
|
||
)}
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{reviews.length > 0 && (
|
||
<Card className="mb-6">
|
||
<CardHeader>
|
||
<CardTitle className="text-base">评价</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-4">
|
||
{reviews.map((review) => (
|
||
<div key={review.id} className="space-y-1">
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-sm font-medium">{review.fromUserName}</span>
|
||
<div className="flex">
|
||
{[1, 2, 3, 4, 5].map((star) => (
|
||
<Star
|
||
key={`star-${star}`}
|
||
className={`h-3.5 w-3.5 ${star <= review.rating ? "fill-yellow-400 text-yellow-400" : "text-muted"}`}
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
{review.sealed ? (
|
||
<p className="text-sm text-muted-foreground">评价已提交,待揭晓</p>
|
||
) : (
|
||
review.content && (
|
||
<p className="text-sm text-muted-foreground">{review.content}</p>
|
||
)
|
||
)}
|
||
<p className="text-xs text-muted-foreground">
|
||
{new Date(review.createdAt).toLocaleDateString("zh-CN")}
|
||
</p>
|
||
</div>
|
||
))}
|
||
</CardContent>
|
||
</Card>
|
||
)}
|
||
|
||
<OrderActions
|
||
orderId={order.id}
|
||
initialStatus={order.status}
|
||
chatSessionId={chatSession?.id}
|
||
serviceId={order.service.id}
|
||
/>
|
||
</div>
|
||
)
|
||
}
|