This commit is contained in:
张成
2025-09-12 22:34:12 +08:00
parent 471244ee5d
commit 30d16946d2
30 changed files with 104 additions and 65 deletions

View File

@@ -0,0 +1,373 @@
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 [verification_code, setVerificationCode] = 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();
// 计算登录按钮是否应该启用
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("");
console.log("设置验证码输入框聚焦");
}, 500); // 延迟500ms确保Toast显示完成后再聚焦
} else {
console.log("验证码发送失败:", result.message);
Taro.showToast({
title: result.message || "发送失败",
icon: "none",
duration: 2000,
});
}
} catch (error) {
console.error("发送验证码异常:", 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: "/game_pages/list/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: "/game_pages/list/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 (
<View className="verification_page">
{/* 背景 */}
<View className="background">
<View className="bg_color"></View>
</View>
{/* 主要内容 */}
<View className="main_content">
{/* 标题区域 */}
<View className="title_section">
<Text className="main_title">/</Text>
<Text className="sub_title">Go Together, Grow Together</Text>
</View>
{/* 表单区域 */}
<View className="form_section">
{/* 手机号输入 */}
<View className="input_group">
<View className="input_container">
<Input
className="phone_input"
type="number"
placeholder="输入中国内地手机号"
placeholderClass="input_placeholder"
value={phone}
onInput={(e) => setPhone(e.detail.value)}
maxlength={11}
/>
<View className="char_count">
<Text
className={
phone.length > 0 ? "count_number active" : "count_number"
}
>
{phone.length}
</Text>
/11
</View>
</View>
</View>
{/* 验证码输入和发送按钮 */}
<View className="verification_group">
<View className="input_container">
<Input
className="code_input"
type="text"
placeholder="输入短信验证码"
placeholderClass="input_placeholder"
placeholderStyle="color:#999999;"
focus={code_input_focus}
value={verification_code}
onInput={(e) => setVerificationCode(e.detail.value)}
onFocus={() => setCodeInputFocus(true)}
onBlur={() => setCodeInputFocus(false)}
maxlength={6}
/>
<View className="char_count">
<Text
className={
verification_code.length > 0
? "count_number active"
: "count_number"
}
>
{verification_code.length}
</Text>
/6
</View>
</View>
<Button
className={`send_code_button ${!can_send_code ? "disabled" : ""}`}
onClick={handle_send_code}
disabled={!can_send_code}
>
{can_send_code ? (
"获取验证码"
) : (
<View className="countdown_text">
<Text className="countdown_line1"></Text>
<Text className="countdown_line2">{countdown}</Text>
</View>
)}
</Button>
{/* 调试信息 */}
{/* {process.env.NODE_ENV === 'development' && (
<View style={{fontSize: '12px', color: '#999', marginTop: '5px'}}>
调试: can_send_code={can_send_code.toString()}, countdown={countdown}
</View>
)} */}
</View>
</View>
{/* 登录按钮 */}
<View className="button_section">
<Button
className={`login_button ${is_loading ? "loading" : ""} ${
!can_login ? "disabled" : ""
}`}
onClick={handle_phone_login}
disabled={!can_login}
>
{"登录"}
</Button>
{/* 调试信息 */}
{/* {process.env.NODE_ENV === 'development' && (
<View style={{fontSize: '12px', color: '#999', marginTop: '5px', textAlign: 'center'}}>
调试: 手机号长度={phone.length}, 验证码长度={verification_code.length}, 可登录={can_login.toString()}
</View>
)} */}
</View>
</View>
{/* 底部指示器 */}
{show_change_mobile_layer && (
<View className="change_mobile_overlay">
{/* 底部修改手机号浮层 */}
<View className="change_mobile_float_layer">
<View className="float_title">
<Text className="title_text"></Text>
</View>
<View className="hint_content">
<Text className="hint_text">
使
</Text>
</View>
<View className="bind_mobile_info_box">
<View className="bind_item">
<Text>使</Text>
<Text className="mobile">{hidePhone(phone)}</Text>
</View>
<View className="bind_item">
<Text></Text>
<Text className="mobile">{hidePhone(oldPhone)}</Text>
</View>
</View>
<View className="button_group">
<Button
className="cancel_button"
onClick={() => {
Taro.redirectTo({
url: "/game_pages/list/index",
});
}}
>
{"保持原手机号"}
</Button>
<Button className="agree_button" onClick={change_mobile}>
{"更新为当前手机号"}
</Button>
</View>
</View>
</View>
)}
</View>
);
};
export default VerificationPage;