import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import type { RedeemPhoneDirectPrepareResult } from '@dukang/shared-types'; import { request } from '../lib/api'; import { toastError, toastSuccess } from '../lib/toast'; import { useStorePageView } from '../lib/usePageView'; function formatAmount(n: number) { return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } export default function PhoneRedeemPage() { useStorePageView('store_phone_redeem_view'); const navigate = useNavigate(); const [phone, setPhone] = useState(''); const [amount, setAmount] = useState(''); const [confirmCode, setConfirmCode] = useState(''); const [prepared, setPrepared] = useState(null); const [storeName, setStoreName] = useState(''); const [storeClosed, setStoreClosed] = useState(false); const [loading, setLoading] = useState(false); const [confirmCooldown, setConfirmCooldown] = useState(0); const [showOpenModal, setShowOpenModal] = useState(false); const [opening, setOpening] = 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(() => { if (confirmCooldown <= 0) return; const timer = window.setTimeout(() => setConfirmCooldown((v) => v - 1), 1000); return () => window.clearTimeout(timer); }, [confirmCooldown]); async function prepareDirectRedeem() { const value = Number(amount); if (!Number.isFinite(value) || value <= 0) { toastError('请输入有效核销金额'); return; } setLoading(true); try { const result = await request('SHOP_H5', '/shop/redeem/phone/prepare-direct', { method: 'POST', body: JSON.stringify({ phone: phone.trim(), amount: value }), }); setPrepared(result); setConfirmCode(''); setConfirmCooldown(60); toastSuccess(`验证码已发送至 ${result.maskedPhone},验证成功后将直接核销`); } catch (e) { setPrepared(null); toastError(e instanceof Error ? e.message : '发送验证码失败'); } finally { setLoading(false); } } async function sendConfirmSms() { if (!/^1\d{10}$/.test(phone.trim())) { toastError('请输入正确的手机号'); return; } if (storeClosed) { setShowOpenModal(true); return; } await prepareDirectRedeem(); } async function openStoreAndContinue() { if (opening) return; setOpening(true); try { await request('SHOP_H5', '/shop/store/status', { method: 'PUT', body: JSON.stringify({ status: 'OPEN' }), }); setStoreClosed(false); setShowOpenModal(false); await prepareDirectRedeem(); } catch (e) { setShowOpenModal(false); toastError(e instanceof Error ? e.message : '开启营业失败'); } finally { setOpening(false); } } async function confirmRedeem() { if (!prepared) { toastError('请先发送核销验证码'); return; } if (!confirmCode.trim()) { toastError('请输入确认验证码'); return; } setLoading(true); try { const result = await request>('SHOP_H5', '/shop/redeem/phone/confirm', { method: 'POST', body: JSON.stringify({ sessionId: prepared.sessionId, code: confirmCode.trim(), }), }); sessionStorage.setItem('lastRedeemResult', JSON.stringify(result)); navigate('/redeem/success', { state: { result, storeName, user: prepared.user }, }); } catch (e) { toastError(e instanceof Error ? e.message : '核销失败'); } finally { setLoading(false); } } const amountValue = Number(amount); const canSendCode = /^1\d{10}$/.test(phone.trim()) && Number.isFinite(amountValue) && amountValue > 0; return (

手机号核销

{storeClosed && (

门店目前休息中无法核销,开启营业后可继续

)}
smartphone

当前登录核销门店

{storeName}

{ setPhone(e.target.value.replace(/\D/g, '')); setPrepared(null); setConfirmCode(''); setConfirmCooldown(0); }} />
{ setAmount(e.target.value); setPrepared(null); setConfirmCode(''); setConfirmCooldown(0); }} />
setConfirmCode(e.target.value.replace(/\D/g, ''))} />

验证码将发送到用户手机号,验证成功后直接完成核销。

{showOpenModal && (

门店休息中

门店目前休息中无法核销,是否开启营业?

)}
); }