227 lines
8.2 KiB
TypeScript
227 lines
8.2 KiB
TypeScript
"use client"
|
||
|
||
import { ArrowLeft, CheckCircle, CreditCard, ShieldCheck } from "lucide-react"
|
||
import Link from "next/link"
|
||
import { useRouter, useSearchParams } from "next/navigation"
|
||
import { useState } from "react"
|
||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||
import { Input } from "@/components/ui/input"
|
||
import { Label } from "@/components/ui/label"
|
||
import { Separator } from "@/components/ui/separator"
|
||
import { Textarea } from "@/components/ui/textarea"
|
||
import { getPlayerById, getServiceById } from "@/lib/api"
|
||
import { notifySuccess } from "@/lib/toast"
|
||
import { useRequireAuth } from "@/lib/use-require-auth"
|
||
import { useAuthStore } from "@/store/auth"
|
||
import { useChatStore } from "@/store/chat"
|
||
import { useOrderStore } from "@/store/orders"
|
||
import { useWalletStore } from "@/store/wallet"
|
||
|
||
export default function NewOrderPage() {
|
||
const router = useRouter()
|
||
const searchParams = useSearchParams()
|
||
const { requireAuth } = useRequireAuth()
|
||
const createOrder = useOrderStore((state) => state.createOrder)
|
||
const ensureOrderSession = useChatStore((state) => state.ensureOrderSession)
|
||
const balance = useWalletStore((state) => state.balance)
|
||
const deductBalance = useWalletStore((state) => state.deductBalance)
|
||
const serviceId = searchParams.get("serviceId")
|
||
|
||
const service = serviceId ? getServiceById(serviceId) : undefined
|
||
const player = service ? getPlayerById(service.playerId) : undefined
|
||
|
||
const [quantity, setQuantity] = useState(1)
|
||
const [note, setNote] = useState("")
|
||
const [submitted, setSubmitted] = useState(false)
|
||
|
||
if (!service || !player) {
|
||
return (
|
||
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">
|
||
服务不存在
|
||
</div>
|
||
)
|
||
}
|
||
|
||
const totalPrice = service.price * quantity
|
||
|
||
if (submitted) {
|
||
return (
|
||
<div className="container mx-auto py-8 px-4 max-w-lg text-center space-y-4">
|
||
<CheckCircle className="h-12 w-12 mx-auto text-green-500" />
|
||
<h2 className="text-xl font-bold">下单成功</h2>
|
||
<p className="text-sm text-muted-foreground">
|
||
订单已创建,等待打手接单。你可以在订单列表中查看进度。
|
||
</p>
|
||
<div className="flex gap-2 justify-center">
|
||
<Button asChild>
|
||
<Link href="/orders">查看订单</Link>
|
||
</Button>
|
||
<Button variant="outline" asChild>
|
||
<Link href={`/player/${player.id}`}>返回打手主页</Link>
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="container mx-auto py-8 px-4 max-w-2xl">
|
||
<Link
|
||
href={`/player/${player.id}`}
|
||
className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-4"
|
||
>
|
||
<ArrowLeft className="h-4 w-4" />
|
||
返回打手主页
|
||
</Link>
|
||
|
||
<h1 className="text-2xl font-bold mb-6">确认下单</h1>
|
||
|
||
<div className="space-y-6">
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle className="text-base">服务信息</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-4">
|
||
<div className="flex items-center gap-3">
|
||
<Avatar className="h-10 w-10">
|
||
<AvatarImage src={player.user.avatar} />
|
||
<AvatarFallback>{player.user.nickname[0]}</AvatarFallback>
|
||
</Avatar>
|
||
<div>
|
||
<p className="font-medium">{player.user.nickname}</p>
|
||
<p className="text-xs text-muted-foreground">
|
||
{player.shopName ? `${player.shopName} · ` : ""}
|
||
{service.gameName}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<Separator />
|
||
<div className="space-y-2 text-sm">
|
||
<div className="flex justify-between">
|
||
<span className="text-muted-foreground">服务</span>
|
||
<span>{service.title}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-muted-foreground">单价</span>
|
||
<span>
|
||
¥{service.price}/{service.unit}
|
||
</span>
|
||
</div>
|
||
{service.rankRange && (
|
||
<div className="flex justify-between">
|
||
<span className="text-muted-foreground">段位范围</span>
|
||
<span>{service.rankRange}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle className="text-base">订单信息</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-4">
|
||
<div className="space-y-2">
|
||
<Label htmlFor="quantity">数量({service.unit})</Label>
|
||
<Input
|
||
id="quantity"
|
||
type="number"
|
||
min={1}
|
||
max={99}
|
||
value={quantity}
|
||
onChange={(e) => setQuantity(Math.max(1, Number.parseInt(e.target.value, 10) || 1))}
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="note">备注(可选)</Label>
|
||
<Textarea
|
||
id="note"
|
||
placeholder="例如:希望晚上8点后开始"
|
||
value={note}
|
||
onChange={(e) => setNote(e.target.value)}
|
||
rows={3}
|
||
/>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle className="text-base">支付信息</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-3">
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-muted-foreground">
|
||
{service.title} × {quantity}
|
||
</span>
|
||
<span>¥{totalPrice}</span>
|
||
</div>
|
||
<Separator />
|
||
<div className="flex justify-between font-medium">
|
||
<span>应付金额</span>
|
||
<span className="text-lg text-primary">¥{totalPrice}</span>
|
||
</div>
|
||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||
<CreditCard className="h-4 w-4" />
|
||
<span>钱包余额: ¥{balance.toFixed(2)}</span>
|
||
{balance < totalPrice && <span className="text-destructive">(余额不足)</span>}
|
||
{balance < totalPrice && (
|
||
<Button variant="outline" size="sm" asChild>
|
||
<Link href="/wallet">充值</Link>
|
||
</Button>
|
||
)}
|
||
</div>
|
||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||
<ShieldCheck className="h-3.5 w-3.5" />
|
||
<span>资金由平台托管,服务完成后支付给打手</span>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<Button
|
||
className="w-full"
|
||
size="lg"
|
||
disabled={balance < totalPrice}
|
||
onClick={() =>
|
||
requireAuth(async () => {
|
||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||
const authUser = useAuthStore.getState().user
|
||
if (!authUser) return
|
||
|
||
const order = createOrder({
|
||
consumerId: authUser.id,
|
||
consumerName: authUser.nickname,
|
||
playerId: player.id,
|
||
playerName: player.user.nickname,
|
||
shopId: player.shopId,
|
||
shopName: player.shopName,
|
||
service,
|
||
totalPrice,
|
||
note,
|
||
status: "pending_accept",
|
||
})
|
||
|
||
const paid = deductBalance(order.id, totalPrice)
|
||
if (!paid) {
|
||
return
|
||
}
|
||
|
||
ensureOrderSession(order)
|
||
setSubmitted(true)
|
||
notifySuccess("下单成功")
|
||
setTimeout(() => {
|
||
router.push(`/order/${order.id}`)
|
||
}, 800)
|
||
})
|
||
}
|
||
>
|
||
确认支付 ¥{totalPrice}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|