1
This commit is contained in:
373
src/login_pages/verification/index.tsx
Normal file
373
src/login_pages/verification/index.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user