import { useCallback, useEffect, useState } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; import { useStoreSession } from '../contexts/StoreSessionContext'; import { request } from '../lib/api'; import { parseRedeemTokenFromScan } from '../lib/redeem-scan'; import { authorizeShopWechat, checkNeedsWechatAuth, fetchShopAccount, handleShopWechatCallback, handleShopWechatLoginResult, } from '../lib/wechat-auth'; import { isWechatEnv, weixinSdk } from '../lib/weixin'; import { stripOAuthParamsFromLocation } from '@dukang/weixin-sdk'; import WechatScanAuthModal from '../components/WechatScanAuthModal'; const PENDING_SCAN_KEY = 'shop_pending_scan'; function formatMoney(n: number) { return n.toLocaleString('zh-CN', { minimumFractionDigits: 0, maximumFractionDigits: 2 }); } function formatScanError(e: unknown): string { const msg = e instanceof Error ? e.message : '扫码失败,请重试'; if (/invalid signature/i.test(msg)) { return '微信 JSSDK 签名校验失败:请确认公众号已配置 JS 接口安全域名为 user.runxian.top,并刷新页面后重试'; } if (/offline verifying|权限验证中|接口未就绪/i.test(msg)) { return `${msg}(可在 URL 后加 ?wxdebug=1 开启 JSSDK 调试查看详情)`; } return msg; } export default function HomePage() { const navigate = useNavigate(); const { applySession } = useStoreSession(); const [searchParams, setSearchParams] = useSearchParams(); const [dash, setDash] = useState | null>(null); const [open, setOpen] = useState(true); const [scanMsg, setScanMsg] = useState(''); const [scanning, setScanning] = useState(false); const [authModalOpen, setAuthModalOpen] = useState(false); const [authLoading, setAuthLoading] = useState(false); const [authError, setAuthError] = useState(''); const loadDashboard = useCallback(() => { return request>('SHOP_H5', '/shop/dashboard') .then((d) => { setDash(d); setOpen(String((d.store as Record)?.status) === 'OPEN'); }) .catch(() => {}); }, []); useEffect(() => { void loadDashboard(); }, [loadDashboard]); useEffect(() => { function onResume() { setScanning(false); void loadDashboard(); } function onVisibility() { if (document.visibilityState === 'visible') onResume(); } document.addEventListener('visibilitychange', onVisibility); window.addEventListener('pageshow', onResume); window.addEventListener('focus', onResume); return () => { document.removeEventListener('visibilitychange', onVisibility); window.removeEventListener('pageshow', onResume); window.removeEventListener('focus', onResume); }; }, [loadDashboard]); useEffect(() => { if (!isWechatEnv() || !searchParams.get('code')) return; void handleShopWechatCallback() .then((result) => { if (!result) return; const session = handleShopWechatLoginResult(result); if (session) { applySession(session); } setAuthModalOpen(false); setAuthError(''); stripOAuthParamsFromLocation(); setSearchParams({}, { replace: true }); const shouldScan = sessionStorage.getItem(PENDING_SCAN_KEY) === '1'; sessionStorage.removeItem(PENDING_SCAN_KEY); if (shouldScan) { window.setTimeout(() => void runScan(), 0); } }) .catch((e) => { setAuthError(e instanceof Error ? e.message : '微信授权失败'); }); }, [searchParams, applySession, setSearchParams]); async function runScan() { if (!isWechatEnv()) { setScanMsg('请在微信内打开门店端进行扫码核销'); return; } setScanning(true); setScanMsg(''); try { await weixinSdk.init(); const raw = await weixinSdk.scanQrCode(); if (!raw) { void loadDashboard(); return; } const token = parseRedeemTokenFromScan(raw); if (!token) { setScanMsg('无法识别核销码,请扫描用户出示的核销二维码'); return; } navigate(`/redeem?token=${encodeURIComponent(token)}`); } catch (e) { setScanMsg(formatScanError(e)); } finally { setScanning(false); } } async function handleScan() { setScanMsg(''); if (!isWechatEnv()) { setScanMsg('请在微信内打开门店端进行扫码核销'); return; } try { const profile = await fetchShopAccount(); if (await checkNeedsWechatAuth(profile)) { setAuthModalOpen(true); return; } await runScan(); } catch (e) { setScanMsg(e instanceof Error ? e.message : '无法发起扫码'); } } async function startWechatAuth() { setAuthLoading(true); setAuthError(''); try { sessionStorage.setItem(PENDING_SCAN_KEY, '1'); await authorizeShopWechat(); } catch (e) { sessionStorage.removeItem(PENDING_SCAN_KEY); setAuthError(e instanceof Error ? e.message : '微信授权失败'); setAuthLoading(false); } } const store = dash?.store as Record | undefined; const recent = (dash?.recentRecords as Array>) || []; const openTime = String(store?.openTime || '10:00'); const closeTime = String(store?.closeTime || '22:00'); return (

门店管理中心

store

{String(store?.name || '门店')}

今日核销笔数

{Number(dash?.todayCount || 0)}

今日到账金额

¥ {formatMoney(Number(dash?.todayAmount || 0))}

{scanning ? '正在打开相机…' : '扫码核销'}

{scanMsg &&

{scanMsg}

} smartphone 手机号核销
schedule

营业状态

{open ? '当前正在营业中' : '当前已停止营业'}

营业时间: {openTime} - {closeTime}

核销记录

查看全部 chevron_right
{recent.length === 0 && (

暂无核销记录

)} {recent.map((r) => (

核销时间

{new Date(String(r.createdAt)).toLocaleString('zh-CN')}

¥{formatMoney(Number(r.amount))}

))}
void startWechatAuth()} onCancel={() => { setAuthModalOpen(false); setAuthError(''); sessionStorage.removeItem(PENDING_SCAN_KEY); }} />
); }