/** * # 路由 /checkout 结账页 * 未登录:内联登录 / 注册表单(复用 ~/server/auth 的 loginFn/registerFn)。 * 已登录:账单信息表单 → createOrder(银行转账占位支付)→ 成功页展示订单号。 */ import { createFileRoute, Link, useRouter } from "@tanstack/react-router"; import { useState } from "react"; import { getCart, createOrder, type BillingInfo } from "~/server/commerce"; import { useT } from "~/i18n"; import { getCurrentUser, loginFn, registerFn } from "~/server/auth"; import { getLayoutData, Layout } from "~/components/website/Layout"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Textarea } from "~/components/ui/textarea"; export const Route = createFileRoute("/checkout")({ loader: async () => { const [layout, user, cart] = await Promise.all([getLayoutData(), getCurrentUser(), getCart()]); return { layout, user, cart }; }, head: () => ({ meta: [{ title: "Checkout - DusunIoT" }] }), component: CheckoutPage, }); /** # 函数 组件 结账页面(外壳:公共布局) */ function CheckoutPage() { const { layout } = Route.useLoaderData(); return ( ); } /** # 函数 组件 结账表单区 */ function CheckoutInner() { const t = useT(); const { user, cart } = Route.useLoaderData(); const router = useRouter(); const [authError, setAuthError] = useState(""); const [orderNumber, setOrderNumber] = useState(""); const [orderError, setOrderError] = useState(""); const [submitting, setSubmitting] = useState(false); /** # 函数 登录提交(成功后 invalidate 以带会话重跑 loader) */ async function handleLogin(e: React.FormEvent) { e.preventDefault(); setAuthError(""); const fd = new FormData(e.currentTarget); const res = await loginFn({ data: { email: String(fd.get("email") ?? ""), password: String(fd.get("password") ?? "") } }); if (!res.ok) return setAuthError(res.error); router.invalidate(); } /** # 函数 注册提交 */ async function handleRegister(e: React.FormEvent) { e.preventDefault(); setAuthError(""); const fd = new FormData(e.currentTarget); const res = await registerFn({ data: { email: String(fd.get("email") ?? ""), password: String(fd.get("password") ?? ""), displayName: String(fd.get("displayName") ?? "") || undefined, }, }); if (!res.ok) return setAuthError(res.error); router.invalidate(); } /** # 函数 下单提交 */ async function handlePlaceOrder(e: React.FormEvent) { e.preventDefault(); setOrderError(""); setSubmitting(true); const fd = new FormData(e.currentTarget); const billing: BillingInfo = { firstName: String(fd.get("firstName") ?? ""), lastName: String(fd.get("lastName") ?? ""), email: String(fd.get("email") ?? ""), phone: String(fd.get("phone") ?? ""), address: String(fd.get("address") ?? ""), city: String(fd.get("city") ?? ""), country: String(fd.get("country") ?? ""), postcode: String(fd.get("postcode") ?? ""), }; const shippingSame = fd.get("shippingSame") === "on"; const shipping = shippingSame ? undefined : { firstName: String(fd.get("shipFirstName") ?? ""), lastName: String(fd.get("shipLastName") ?? ""), email: billing.email, address: String(fd.get("shipAddress") ?? ""), city: String(fd.get("shipCity") ?? ""), country: String(fd.get("shipCountry") ?? ""), postcode: String(fd.get("shipPostcode") ?? ""), }; const res = await createOrder({ data: { billing, shippingSame, shipping, note: String(fd.get("note") ?? "") }, }); setSubmitting(false); if (!res.ok) return setOrderError(res.error); setOrderNumber(res.orderNumber); router.invalidate(); } // 下单成功页 if (orderNumber) { return (

Order received

Thank you! Your order {orderNumber} has been placed.

Payment method: bank transfer (our sales team will contact you with payment instructions).

); } // 未登录:内联登录 / 注册 if (!user) { return (

Checkout

Please log in or create an account to place your order.{" "} {cart.count > 0 && <>{t("checkout_cart_kept", { count: cart.count })}}

{authError && (

{authError}

)}

Log in

Create account

); } // 购物车为空 if (cart.items.length === 0) { return (

Checkout

{t("cart_empty")}

); } // 已登录:账单表单 + 订单汇总 return (

Checkout

{orderError ?

{orderError}

: null}

Billing details