| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- import { useState, useEffect } from 'react';
- import { loginSendCodeApi, getAccountListByCodeApi } from '../../api/login';
- import { SelectAccount, type Account } from './select-account';
- import { Agreement } from '../agreement';
- import { pmBridge } from '../../lib/PostMessageBridge';
- import { useAtomValue } from 'jotai';
- import { modalStackAtom } from '../../store';
- export interface phoneLoginProps {
- switchAccountLogin: () => void;
- switchRegister:()=> void;
- }
- export const PhoneLogin = ({ switchAccountLogin, switchRegister }: phoneLoginProps) => {
- const modalState = useAtomValue(modalStackAtom);
- const [mobile, setMobile] = useState('');
- const [code, setCode] = useState('');
- const [countdown, setCountdown] = useState(0);
- const [loading, setLoading] = useState(false);
- const [accountList, setAccountList] = useState<Account[]>([]);
- const [showSelectAccount, setShowSelectAccount] = useState(false);
- const [showAgreement, setShowAgreement] = useState<{ title: string; url: string } | null>(null);
- useEffect(() => {
- let timer: ReturnType<typeof setInterval>;
- if (countdown > 0) {
- timer = setInterval(() => {
- setCountdown((prev) => prev - 1);
- }, 1000);
- }
- return () => {
- if (timer) clearInterval(timer);
- };
- }, [countdown]);
- const handleGetCode = async () => {
- if (!mobile) {
- alert('请输入手机号');
- return;
- }
- if (!/^1[3-9]\d{9}$/.test(mobile)) {
- alert('请输入正确的手机号');
- return;
- }
- setLoading(true);
- try {
- await loginSendCodeApi({ mobile });
- setCountdown(60);
- } catch (error: any) {
- console.error('获取验证码失败:', error);
- alert(error.message || '获取验证码失败');
- } finally {
- setLoading(false);
- }
- };
- const handleLogin = async () => {
- if (!mobile) {
- alert('请输入手机号');
- return;
- }
- if (!code) {
- alert('请输入验证码');
- return;
- }
- setLoading(true);
- try {
- const res = await getAccountListByCodeApi({ mobile, code });
- if (res.length > 0) {
- setAccountList(res);
- setShowSelectAccount(true);
- } else {
- alert('登录成功')
- const requestId = modalState.login.requestId;
- pmBridge.sendToIframe("LOGIN_REQUEST", {success:true, data: res}, requestId);
- }
- } catch (error: any) {
- console.error('验证失败:', error);
- alert(error.message || '验证失败');
- } finally {
- setLoading(false);
- }
- };
- const handleSelectConfirm = (account: Account) => {
- console.log('选择账号确认:', account);
- setShowSelectAccount(false);
- };
- return (
- <>
- <div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm">
- <div className="w-full max-w-[320px] mx-5 bg-[#f5f7f8] rounded-2xl shadow-2xl overflow-hidden flex flex-col scale-100">
- <div className="flex flex-col items-center pt-4 pb-6 px-6">
- <h1 className="text-xl font-bold text-slate-900 dark:text-slate-100">游戏登录</h1>
- </div>
- {/* Tabs */}
- <div className="flex border-b border-slate-200 dark:border-slate-800 px-6 gap-8">
- <button className="flex flex-col items-center justify-center border-b-[3px] border-primary text-primary pb-3 pt-2">
- <p className="text-sm font-bold leading-normal tracking-wide">手机号登录</p>
- </button>
- <button onClick={switchAccountLogin} className="flex flex-col items-center justify-center border-b-[3px] border-transparent text-slate-500 dark:text-slate-400 pb-3 pt-2 hover:text-primary transition-colors">
- <p className="text-sm font-bold leading-normal tracking-wide">账号登录</p>
- </button>
- </div>
- {/* Form Content */}
- <div className="p-6 space-y-4">
- {/* Country Code & Phone Input */}
- <div className="flex gap-2">
- <div className="flex-1">
- <input
- className="p-2 form-input input-field w-full rounded-lg border-slate-200 bg-white text-slate-900 h-12 text-sm"
- placeholder="请输入手机号"
- type="tel"
- value={mobile}
- onChange={(e) => setMobile(e.target.value)}
- />
- </div>
- </div>
- {/* Verification Code */}
- <div className="flex gap-2">
- <div className="flex-1">
- <input
- className="p-2 form-input input-field w-full rounded-lg border-slate-200 bg-white text-slate-900 h-12 text-sm"
- placeholder="请输入验证码"
- type="text"
- value={code}
- onChange={(e) => setCode(e.target.value)}
- />
- </div>
- <button
- className={`px-4 h-12 text-primary font-medium text-sm rounded-lg whitespace-nowrap transition-colors border border-primary/20 ${
- countdown > 0 || loading ? 'bg-slate-100 text-slate-400 border-slate-200 cursor-not-allowed' : 'bg-primary/10 hover:bg-primary/20'
- }`}
- onClick={handleGetCode}
- disabled={countdown > 0 || loading}
- >
- {countdown > 0 ? `${countdown}s` : loading ? '发送中...' : '获取验证码'}
- </button>
- </div>
- {/* Action Button */}
- <button
- className={`w-full bg-primary hover:bg-primary/90 text-white font-bold h-12 rounded-lg shadow-lg shadow-primary/30 transition-all flex items-center justify-center gap-2 ${
- loading ? 'opacity-70 cursor-not-allowed' : ''
- }`}
- onClick={handleLogin}
- disabled={loading}
- >
- {loading ? '处理中...' : '进入游戏'}
- </button>
- {/* Secondary Links */}
- <div className="flex justify-between items-center px-1">
- <a className="text-xs text-slate-500 dark:text-slate-400 hover:text-primary transition-colors cursor-pointer" onClick={switchRegister}>快速注册</a>
- <a className="text-xs text-slate-500 dark:text-slate-400 hover:text-primary transition-colors" href="#">遇到问题?</a>
- </div>
- </div>
- {/* Footer Privacy */}
- <div className="px-6 pb-6 text-center">
- <p className="text-[10px] text-slate-400 leading-relaxed">
- 登录即代表您已同意 <button className="text-primary hover:underline" onClick={() => setShowAgreement({ title: '用户协议', url: '/static/user.html' })}>用户协议</button> 和 <button className="text-primary hover:underline" onClick={() => setShowAgreement({ title: '隐私政策', url: '/static/ys.html' })}>隐私政策</button>
- </p>
- </div>
- </div>
- </div>
- {showSelectAccount && (
- <SelectAccount
- mobile={mobile}
- code={code}
- accounts={accountList}
- onConfirm={handleSelectConfirm}
- onClose={() => setShowSelectAccount(false)}
- />
- )}
- {showAgreement && (
- <Agreement
- title={showAgreement.title}
- url={showAgreement.url}
- onBack={() => setShowAgreement(null)}
- />
- )}
- </>
- );
- };
|