import React, { useState, useEffect, useRef } from 'react'; import { IcClose, IcDelete, IcWallet, IcBuilding, IcNavBack, IcCheck, IcNavForward, IcExpand, IcCollapse } from '../res/icons'; import { useAlipayGestures } from '../hooks/useAlipayGestures'; import { useAlipayStrings } from '../hooks/useAlipayStrings'; import { useLocale } from '@/apps/Alipay/locale'; import { localizeBankName } from '../utils/localizeBankName'; interface BankCardInfo { id: string; bankName: string; last4: string; bound: boolean; available: number; } interface PaymentPasswordModalProps { visible: boolean; variant?: 'transfer' | 'cashier'; amount: string; payeeName: string; expectedPassword?: string; onSuccess: (methodId: string) => void; bankCards?: BankCardInfo[]; balance?: { total: number }; defaultMethodId?: string; actionPrefix?: string; maskedPhone?: string; subject?: string; } export const PaymentPasswordModal: React.FC = ({ visible, variant = 'transfer', amount, payeeName, expectedPassword = '000000', onSuccess, bankCards = [], balance, defaultMethodId, actionPrefix = 'transferPassword', maskedPhone = '', subject = '', }) => { const s = useAlipayStrings(); const { bindTap, bindBack } = useAlipayGestures(); const locale = useLocale(); const isEnglish = locale === 'en'; const [password, setPassword] = useState(''); const [error, setError] = useState(false); const calledRef = useRef(false); const [selectedMethodId, setSelectedMethodId] = useState('balance'); const [expanded, setExpanded] = useState(false); const boundCards = bankCards.filter(c => c.bound); useEffect(() => { if (visible) { setPassword(''); setError(false); calledRef.current = false; setExpanded(false); // 每次进入弹窗都重新选付款方式并输密码,避免沿用上次的银行卡导致「不用再选卡、像没走密码」的体验 setSelectedMethodId(defaultMethodId ?? 'balance'); } else { setPassword(''); calledRef.current = false; } }, [visible, defaultMethodId]); useEffect(() => { if (!visible) return; if (password.length === 6) { if (calledRef.current) return; if (password === expectedPassword) { calledRef.current = true; setTimeout(() => { onSuccess(selectedMethodId); }, 200); } else { setError(true); setPassword(''); } } else { if (error && password.length > 0) { setError(false); } } }, [visible, password, expectedPassword, onSuccess, selectedMethodId, error]); const handleNumberClick = (num: string) => { if (password.length < 6) { setPassword(prev => prev + num); } }; const handleDelete = () => { setPassword(prev => prev.slice(0, -1)); }; const handleSelectMethod = (id: string) => { setSelectedMethodId(id); }; const handleConfirmExpanded = () => { setExpanded(false); }; if (!visible) return null; const CheckIcon = ({ size = 16 }: { size?: number }) => ( ); const Keypad = () => (
{[1, 2, 3, 4, 5, 6, 7, 8, 9].map((num) => ( ))}
); const PasswordDots = () => (
{[0, 1, 2, 3, 4, 5].map((index) => (
{password.length > index && (
)}
))}
); // ═══════════════════════════════════════════════════ // Transfer variant: bottom-sheet (original design) // ═══════════════════════════════════════════════════ return (
{/* Header */}
{s.enter_payment_password}
{/* Scrollable body */}
{s.transfer_to_payee.replace('{payeeName}', payeeName)}
¥{amount}
{/* Payment method selector — in-place expand/collapse */}
{/* Balance */} {expanded ? ( <> {/* All bound bank cards */} {boundCards.length > 0 && ( <>
{s.payment_password_modal_bank_cards}
{boundCards.map((card) => ( ))} )} {/* Credit */}
{s.payment_password_modal_credit}
{s.payment_password_modal_mybank_loan}
{s.payment_password_modal_available_after_open}
{/* Unavailable */}
{s.payment_password_modal_unavailable_methods}
{s.huabei}
{/* Collapse button */} ) : ( <> {/* First bound card (compact) */} {boundCards.length > 0 && ( )}
{s.payment_password_modal_add_bank_card}
{/* Expand button */} )}
{/* Error + password dots */}
{error &&
{s.incorrect_payment_password}
}
{[0, 1, 2, 3, 4, 5].map((index) => (
{password.length > index && (
)}
))}
{error && ( )}
{/* Keypad — always visible, pinned at bottom */}
{[1, 2, 3, 4, 5, 6, 7, 8, 9].map((num) => ( ))}
); };