import { useEffect, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import WeakNetFallbackPanel from '../components/WeakNetFallbackPanel'; import { request } from '../lib/api'; import { reportRedeemFailure } from '../lib/redeem-failure'; import { useStorePageView } from '../lib/usePageView'; function formatAmount(n: number) { return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } type Preview = { amount: number; expireInSeconds: number; user?: { userNo?: string; phone?: string; nickname?: string }; redeemType?: string; boundStoreId?: string | null; }; export default function RedeemConfirmPage() { useStorePageView('store_redeem_confirm_view'); const navigate = useNavigate(); const [searchParams] = useSearchParams(); const [token, setToken] = useState(''); const [msg, setMsg] = useState(''); const [loading, setLoading] = useState(false); const [storeName, setStoreName] = useState(''); const [preview, setPreview] = useState(null); const [storeClosed, setStoreClosed] = useState(false); const [failCount, setFailCount] = useState(0); const [showWeakNet, setShowWeakNet] = useState(false); useEffect(() => { request>('SHOP_H5', '/shop/store') .then((s) => { setStoreName(String(s.name || '当前门店')); if (s.status && s.status !== 'OPEN') setStoreClosed(true); }) .catch(() => setStoreName('当前门店')); }, []); useEffect(() => { const scanned = searchParams.get('token')?.trim(); if (!scanned) { navigate('/', { replace: true }); return; } setToken(scanned); }, [searchParams, navigate]); useEffect(() => { if (!token.trim()) { setPreview(null); return; } request('SHOP_H5', '/shop/redeem/preview', { method: 'POST', body: JSON.stringify({ token }), }) .then(setPreview) .catch(async (e) => { setPreview(null); setMsg(e instanceof Error ? e.message : '无法预览核销码'); const report = await reportRedeemFailure(token, 'preview', e); if (report?.thresholdReached) { setFailCount(report.failCount); setShowWeakNet(true); } else if (report) { setFailCount(report.failCount); } }); }, [token]); async function confirm() { if (storeClosed) { setMsg('门店未营业,无法核销'); return; } if (!token.trim()) { setMsg('请先扫码获取核销码'); return; } setLoading(true); setMsg(''); try { const result = await request>('SHOP_H5', '/shop/redeem/confirm', { method: 'POST', body: JSON.stringify({ token }), }); sessionStorage.setItem('lastRedeemResult', JSON.stringify(result)); navigate('/redeem/success', { state: { result, storeName, user: preview?.user } }); } catch (e) { setMsg(e instanceof Error ? e.message : '核销失败'); const report = await reportRedeemFailure(token, 'confirm', e); if (report?.thresholdReached) { setFailCount(report.failCount); setShowWeakNet(true); } else if (report) { setFailCount(report.failCount); } } finally { setLoading(false); } } const previewAmount = preview?.amount ?? 0; const userLabel = preview?.user?.nickname || preview?.user?.phone || '—'; return (

核销确认

{storeClosed && (

门店当前未营业,无法核销

)}
verified_user

当前登录核销门店

{storeName}

person 下单用户
{userLabel}

核销金额

¥ {formatAmount(previewAmount)}
confirmation_number 好客权益 · {preview?.redeemType === 'COUPON' ? '单据核销' : '直接核销'}
核销码 ID {token || '扫码后显示'}
有效期 {preview ? `${preview.expireInSeconds} 秒` : '—'}
{preview?.boundStoreId && (
绑定门店 仅限指定门店
)} {failCount > 0 && !showWeakNet && (
网络失败 {failCount} / 5 次
)}
{msg &&

{msg}

} {!showWeakNet && ( <>

请核对金额后点击确认

)}
{showWeakNet && token && ( )}
wine_bar
); }