import {useEffect, useState} from "react"; import Taro from '@tarojs/taro' import { View, Text, Input } from '@tarojs/components' import {loginBySms, sendSmsCaptcha} from "@/api/passport/login"; import {LoginParam} from "@/api/passport/login/model"; import {checkAndHandleInviteRelation, hasPendingInvite, parseInviteParams, saveInviteParams, trackInviteSource} from "@/utils/invite"; import './sms-login.scss' const SmsLogin = () => { const [loading, setLoading] = useState(false) const [sendingCode, setSendingCode] = useState(false) const [countdown, setCountdown] = useState(0) const [formData, setFormData] = useState({ phone: '', code: '' }) const router = Taro.getCurrentInstance().router const redirectParam = (router?.params as any)?.redirect as string | undefined const safeDecodeMaybeEncoded = (input?: string) => { if (!input) return '' try { return decodeURIComponent(input) } catch (_e) { return input } } const redirectUrl = (() => { const decoded = safeDecodeMaybeEncoded(redirectParam) if (!decoded) return '' return decoded.startsWith('/') ? decoded : `/${decoded}` })() const isTabBarUrl = (url: string) => { const pure = url.split('?')[0] return ( pure === '/pages/index/index' || pure === '/pages/cart/cart' || pure === '/pages/user/user' || pure === '/pages/category/index' ) } const navigateAfterLogin = async () => { if (!redirectUrl) { await Taro.reLaunch({ url: '/pages/index/index' }) return } if (isTabBarUrl(redirectUrl)) { await Taro.switchTab({ url: redirectUrl.split('?')[0] }) return } await Taro.redirectTo({ url: redirectUrl }) } useEffect(() => { // sms-login 页面不是 tabBar 页面,无需调用 hideTabBar }, []) // 如果从分享/二维码链接进入短信登录页,先暂存邀请信息 useEffect(() => { try { const inviteParams = parseInviteParams({ query: router?.params }) if (inviteParams?.inviter) { saveInviteParams(inviteParams) trackInviteSource(inviteParams.source || 'share', parseInt(inviteParams.inviter, 10)) } } catch (e) { console.error('短信登录页处理邀请参数失败:', e) } }, [router?.params]) // 倒计时效果 useEffect(() => { let timer: ReturnType | undefined if (countdown > 0) { timer = setTimeout(() => { setCountdown(countdown - 1) }, 1000) } return () => { if (timer) clearTimeout(timer) } }, [countdown]) // 验证手机号格式 const validatePhone = (phone: string): boolean => { const phoneRegex = /^1[3-9]\d{9}$/ return phoneRegex.test(phone) } // 发送短信验证码 const handleSendCode = async () => { if (!formData.phone) { Taro.showToast({ title: '请输入手机号码', icon: 'none' }) return } if (!validatePhone(formData.phone)) { Taro.showToast({ title: '请输入正确的手机号码', icon: 'none' }) return } if (sendingCode || countdown > 0) { return } try { setSendingCode(true) await sendSmsCaptcha({ phone: formData.phone }) Taro.showToast({ title: '验证码已发送', icon: 'success' }) // 开始60秒倒计时 setCountdown(60) } catch (error: any) { Taro.showToast({ title: error.message || '发送失败', icon: 'error' }) } finally { setSendingCode(false) } } // 处理登录 const handleLogin = async () => { // 防止重复提交 if (loading) { return } // 表单验证 if (!formData.phone) { Taro.showToast({ title: '请输入手机号码', icon: 'none' }) return } if (!validatePhone(formData.phone)) { Taro.showToast({ title: '请输入正确的手机号码', icon: 'none' }) return } if (!formData.code) { Taro.showToast({ title: '请输入验证码', icon: 'none' }) return } if (formData.code.length !== 6) { Taro.showToast({ title: '请输入6位验证码', icon: 'none' }) return } try { setLoading(true) await loginBySms({ phone: formData.phone, code: formData.code }) // 登录成功后(可能是新注册用户),检查是否存在待处理的邀请关系并尝试绑定 if (hasPendingInvite()) { try { await checkAndHandleInviteRelation() } catch (e) { console.error('短信登录后处理邀请关系失败:', e) } } Taro.showToast({ title: '登录成功', icon: 'success' }) // 延迟跳转到首页 setTimeout(() => { navigateAfterLogin().catch((e) => { console.error('短信登录后跳转失败:', e) Taro.reLaunch({ url: '/pages/index/index' }) }) }, 1500) } catch (error: any) { Taro.showToast({ title: error.message || '登录失败', icon: 'error' }) } finally { setLoading(false) } } const phoneValid = validatePhone(formData.phone) const codeValid = formData.code.length === 6 const canSubmit = phoneValid && codeValid && !loading const sendDisabled = !phoneValid || sendingCode || countdown > 0 const sendBtnText = countdown > 0 ? `${countdown}s 后重试` : sendingCode ? '发送中' : '获取验证码' return ( {/* 渐变背景 */} {/* 头部 Logo 和标题 */} 📱 验证码登录 未注册的手机号验证后将自动注册 {/* 表单卡片 */} {/* 手机号 */} 📞 setFormData({ ...formData, phone: e.detail.value })} /> {/* 验证码 */} 🔐 setFormData({ ...formData, code: e.detail.value })} /> {sendBtnText} {/* 登录按钮 */} {loading ? '登录中...' : '登录 / 注册'} {/* 返回微信登录 */} Taro.navigateBack({ delta: 1 })} > 返回微信快捷登录 {/* 协议 */} 登录即代表您已阅读并同意 Taro.navigateTo({ url: '/passport/agreement?type=terms' })} > 《服务协议》 Taro.navigateTo({ url: '/passport/agreement?type=privacy' })} > 《隐私政策》 ) } export default SmsLogin