import { SearchOutlined } from '@ant-design/icons' import { Alert, Button, Card, Descriptions, Form, Input, Result, Space, Typography } from 'antd' import { useState } from 'react' import { showError, showSuccess } from '@/lib/feedback' import { confirmRedeemOrder, lookupRedeemOrder } from '@/services/redeem' import type { RedeemOrderStatus, RedeemOrderView } from '@/types/redeem' import { formatRedeemAmount, formatRedeemTime, resolveRedeemStatusView } from '@/utils/redeem' /** * 固定兑换链接页面(`https://<域名>/redeem`)。 * * 只用 `useState` 驱动、不引 TanStack Query:这是一次性公开链接, * 查询与确认都由用户显式触发,没有需要缓存/复用的服务端状态。 * 流程:输订单号 → lookup → 按 status 渲染 → 可兑换时 confirm。 */ export default function RedeemPage() { const [orderNo, setOrderNo] = useState('') const [view, setView] = useState(null) const [lookupLoading, setLookupLoading] = useState(false) const [confirmLoading, setConfirmLoading] = useState(false) async function submitLookup(values: { orderNo: string }) { const nextOrderNo = String(values.orderNo || '').trim() setOrderNo(nextOrderNo) await runLookup(nextOrderNo) } async function runLookup(currentOrderNo: string) { setLookupLoading(true) try { const response = await lookupRedeemOrder(currentOrderNo) setView(response.data) } catch (error) { setView(null) showError(error instanceof Error ? error.message : '查询失败') } finally { setLookupLoading(false) } } function refreshLookup() { if (!orderNo) return void runLookup(orderNo) } async function submitConfirm() { if (!orderNo) return setConfirmLoading(true) try { const response = await confirmRedeemOrder(orderNo) // confirm 在不可兑换时不抛错,会把当前状态原样返回:一律按 status 重新渲染。 setView(response.data) if (response.data.status === 'verified') { showSuccess(response.data.message || '兑换成功') } else { showError(response.data.message || '当前订单暂不可兑换') } } catch (error) { showError(error instanceof Error ? error.message : '兑换失败') } finally { setConfirmLoading(false) } } return (
兑换中心 请输入订单号,核对订单状态后按提示完成兑换。
{view?.status === 'not_found' ? ( ) : null} {view && (view.status === 'unpaid' || view.status === 'cancelled' || view.status === 'refunded') ? ( ) : null} {view?.status === 'pending_certificate' ? ( <> {/* 券在后台异步准备:不做自动轮询,由用户点「刷新」按需重查,避免空转请求。 */} ) : null} {view?.status === 'redeemable' ? (
) : null} {view?.status === 'verified' ? (
{/* 只展示后端返回的掩码券码,绝不明文铺开 certNo。 */} {view.credential?.maskedCertNo || '—'} {formatRedeemTime(view.credential?.verifiedAt) || '—'}
) : null}
) } function RedeemStatusResult({ status, message }: { status: RedeemOrderStatus; message: string }) { const { tone, title } = resolveRedeemStatusView(status) return } function RedeemSummaryDetails({ summary }: { summary: RedeemOrderView['summary'] }) { return (
{summary?.productName || '—'} {/* 平台标题只是引流词,降级为副信息,避免买家误认商品 */} {summary?.productTitle && summary.productTitle !== summary.productName ? ( {summary.productTitle} ) : null}
{summary ? summary.quantity : '—'} {summary ? formatRedeemAmount(summary.amountFen) : '—'} {formatRedeemTime(summary?.paidAt) || '—'}
) }