import React, { useState, useEffect } from "react"; import { View, Text, Input, Button, Image } from "@tarojs/components"; import Taro, { useRouter } from "@tarojs/taro"; import { phone_auth_login, send_sms_code, save_login_state, updateUserPhone, } from "@/services/loginService"; import "./index.scss"; const VerificationPage: React.FC = () => { const [phone, setPhone] = useState(""); // 存储纯数字的手机号 const [display_phone, setDisplayPhone] = useState(""); // 显示带空格的手机号 const [verification_code, setVerificationCode] = useState(""); // 存储纯数字的验证码 const [display_code, setDisplayCode] = useState(""); // 显示带空格的验证码 const [countdown, setCountdown] = useState(0); const [can_send_code, setCanSendCode] = useState(true); const [is_loading, setIsLoading] = useState(false); const [code_input_focus, setCodeInputFocus] = useState(false); const [show_change_mobile_layer, setShowChangeMobileLayer] = useState(false); const [oldPhone, setOldPhone] = useState(""); const { params: { redirect }, } = useRouter(); // 格式化手机号为 3-4-4 格式 const formatPhone = (value: string): string => { // 移除所有非数字字符 const numbers = value.replace(/\D/g, ""); // 按 3-4-4 格式添加空格 if (numbers.length <= 3) { return numbers; } else if (numbers.length <= 7) { return `${numbers.slice(0, 3)} ${numbers.slice(3)}`; } else { return `${numbers.slice(0, 3)} ${numbers.slice(3, 7)} ${numbers.slice(7, 11)}`; } }; // 格式化验证码为 3-3 格式 const formatCode = (value: string): string => { // 移除所有非数字字符 const numbers = value.replace(/\D/g, ""); // 按 3-3 格式添加空格 if (numbers.length <= 3) { return numbers; } else { return `${numbers.slice(0, 3)} ${numbers.slice(3, 6)}`; } }; // 处理手机号输入 const handlePhoneInput = (e) => { const inputValue = e.detail.value; // 移除所有非数字字符 const numbers = inputValue.replace(/\D/g, ""); // 限制最多11位 const limitedNumbers = numbers.slice(0, 11); // 保存纯数字版本用于提交 setPhone(limitedNumbers); // 保存格式化版本用于显示 setDisplayPhone(formatPhone(limitedNumbers)); }; // 处理验证码输入 const handleCodeInput = (e) => { const inputValue = e.detail.value; // 移除所有非数字字符 const numbers = inputValue.replace(/\D/g, ""); // 限制最多6位 const limitedNumbers = numbers.slice(0, 6); // 保存纯数字版本用于提交 setVerificationCode(limitedNumbers); // 保存格式化版本用于显示 setDisplayCode(formatCode(limitedNumbers)); }; // 计算登录按钮是否应该启用 const can_login = phone.length === 11 && verification_code.length === 6 && !is_loading; // 发送验证码 const handle_send_code = async () => { if (!phone || phone.length !== 11) { Taro.showToast({ title: "请输入正确的手机号", icon: "none", duration: 2000, }); return; } if (!can_send_code) return; try { console.log("开始发送验证码,手机号:", phone); // 调用发送短信接口 const result = await send_sms_code(phone); console.log("发送验证码结果:", result); if (result.success) { console.log("验证码发送成功,开始倒计时"); Taro.showToast({ title: "验证码已发送", icon: "success", duration: 2000, }); // 开始倒计时 setCanSendCode(false); setCountdown(60); console.log("设置状态: can_send_code = false, countdown = 60"); // 发送验证码成功后,让验证码输入框获得焦点并调用系统键盘 setTimeout(() => { // 设置验证码输入框聚焦状态 setCodeInputFocus(true); // 清空验证码,让用户重新输入 setVerificationCode(""); setDisplayCode(""); console.log("设置验证码输入框聚焦"); }, 500); // 延迟500ms确保Toast显示完成后再聚焦 } else { console.log("验证码发送失败:", result.message); Taro.showToast({ title: result.message || "发送失败", icon: "none", duration: 2000, }); } } catch (error) { console.warn("发送验证码异常:", error); Taro.showToast({ title: "发送失败,请重试", icon: "none", duration: 2000, }); } }; // 倒计时效果 useEffect(() => { console.log("倒计时 useEffect 触发,countdown:", countdown); if (countdown > 0) { const timer = setTimeout(() => { console.log("倒计时减少,从", countdown, "到", countdown - 1); setCountdown(countdown - 1); }, 1000); return () => clearTimeout(timer); } else if (countdown === 0 && !can_send_code) { console.log("倒计时结束,重新启用发送按钮"); setCanSendCode(true); } }, [countdown, can_send_code]); // 手机号登录 const handle_phone_login = async () => { if (!phone || phone.length !== 11) { Taro.showToast({ title: "请输入正确的手机号", icon: "none", duration: 2000, }); return; } if (!verification_code || verification_code.length !== 6) { Taro.showToast({ title: "请输入6位验证码", icon: "none", duration: 2000, }); return; } setIsLoading(true); try { // 先进行微信登录获取code const login_result = await Taro.login(); if (!login_result.code) { return { success: false, message: "微信登录失败", }; } const result = await phone_auth_login({ phone, verification_code, user_code: login_result.code, }); if (result.success) { save_login_state(result.token!, result.user_info!); if (result.phone_update_status === "existing") { const { existing_phone = "" } = result; setShowChangeMobileLayer(true); setOldPhone(existing_phone); return; } setTimeout(() => { Taro.redirectTo({ url: "/main_pages/index", }); }, 200); } else { Taro.showToast({ title: result.message || "登录失败", icon: "none", duration: 2000, }); } } catch (error) { Taro.showToast({ title: "登录失败,请重试", icon: "none", duration: 2000, }); } finally { setIsLoading(false); } }; const change_mobile = async () => { setIsLoading(true); try { const res = await updateUserPhone({ phone }); if (res.code === 0) { setTimeout(() => { Taro.redirectTo({ url: "/main_pages/index", }); }, 200); } else { Taro.showToast({ title: res.message || "更新手机号失败", icon: "none", duration: 2000, }); } } catch (e) { Taro.showToast({ title: "更新失败,请重试", icon: "none", duration: 2000, }); } finally { setIsLoading(false); } }; const hidePhone = (phone: string): string => { if (!phone) { return "*"; } return phone.replace(/(\d{3})(\d*)(\d{4})/,'$1****$3'); }; return ( {/* 背景 */} {/* 主要内容 */} {/* 标题区域 */} 手机号注册/登录有场 Go Together, Grow Together {/* 表单区域 */} {/* 手机号输入 */} 0 ? "count_number active" : "count_number" } > {phone.length} /11 {/* 验证码输入和发送按钮 */} setCodeInputFocus(true)} onBlur={() => setCodeInputFocus(false)} maxlength={7} /> 0 ? "count_number active" : "count_number" } > {verification_code.length} /6 {/* 调试信息 */} {/* {process.env.NODE_ENV === 'development' && ( 调试: can_send_code={can_send_code.toString()}, countdown={countdown} )} */} {/* 登录按钮 */} {/* 调试信息 */} {/* {process.env.NODE_ENV === 'development' && ( 调试: 手机号长度={phone.length}, 验证码长度={verification_code.length}, 可登录={can_login.toString()} )} */} {/* 底部指示器 */} {show_change_mobile_layer && ( {/* 底部修改手机号浮层 */} 是否更新已绑定的手机号? 更新后,原手机号将解除绑定,后续登录和通知将使用新的手机号 当前使用的手机号 {hidePhone(phone)} 原绑定手机号 {hidePhone(oldPhone)} )} ); }; export default VerificationPage;