添加验证码处理逻辑

This commit is contained in:
张成
2025-08-24 15:40:37 +08:00
parent b92301a887
commit 5e31adee53
5 changed files with 152 additions and 33 deletions

View File

@@ -10,12 +10,13 @@ export default defineConfig<'webpack5'>(async (merge, { command, mode }) => {
const baseConfig: UserConfigExport<'webpack5'> = { const baseConfig: UserConfigExport<'webpack5'> = {
projectName: 'playBallTogether', projectName: 'playBallTogether',
date: '2025-8-9', date: '2025-8-9',
designWidth: 375, designWidth: 390,
deviceRatio: { deviceRatio: {
640: 2.34 / 2, 640: 2.34 / 2,
750: 1, 750: 1,
375: 2, 375: 2,
828: 1.81 / 2 828: 1.81 / 2,
390: 1.92
}, },
sourceRoot: 'src', sourceRoot: 'src',
outputRoot: 'dist', outputRoot: 'dist',

View File

@@ -273,6 +273,16 @@
font-size: 14px; font-size: 14px;
line-height: 1.714; line-height: 1.714;
color: rgba(60, 60, 67, 0.3); color: rgba(60, 60, 67, 0.3);
.count_number {
color: rgba(60, 60, 67, 0.3);
transition: color 0.3s ease;
&.active {
color: #000000;
font-weight: 500;
}
}
} }
} }
} }
@@ -321,6 +331,16 @@
font-size: 14px; font-size: 14px;
line-height: 1.714; line-height: 1.714;
color: rgba(60, 60, 67, 0.3); color: rgba(60, 60, 67, 0.3);
.count_number {
color: rgba(60, 60, 67, 0.3);
transition: color 0.3s ease;
&.active {
color: #000000;
font-weight: 500;
}
}
} }
} }

View File

@@ -10,11 +10,11 @@ const VerificationPage: React.FC = () => {
const [countdown, setCountdown] = useState(0); const [countdown, setCountdown] = useState(0);
const [can_send_code, setCanSendCode] = useState(true); const [can_send_code, setCanSendCode] = useState(true);
const [is_loading, setIsLoading] = useState(false); const [is_loading, setIsLoading] = useState(false);
const [code_input_focus, setCodeInputFocus] = useState(false);
// 计算登录按钮是否应该启用
const can_login = phone.length === 11 && verification_code.length === 6 && !is_loading;
// 返回上一页
const handle_go_back = () => {
Taro.navigateBack();
};
// 发送验证码 // 发送验证码
const handle_send_code = async () => { const handle_send_code = async () => {
@@ -30,10 +30,16 @@ const VerificationPage: React.FC = () => {
if (!can_send_code) return; if (!can_send_code) return;
try { try {
console.log('开始发送验证码,手机号:', phone);
// 调用发送短信接口 // 调用发送短信接口
const result = await send_sms_code(phone); const result = await send_sms_code(phone);
console.log('发送验证码结果:', result);
if (result.success) { if (result.success) {
console.log('验证码发送成功,开始倒计时');
Taro.showToast({ Taro.showToast({
title: '验证码已发送', title: '验证码已发送',
icon: 'success', icon: 'success',
@@ -43,7 +49,19 @@ const VerificationPage: React.FC = () => {
// 开始倒计时 // 开始倒计时
setCanSendCode(false); setCanSendCode(false);
setCountdown(60); setCountdown(60);
console.log('设置状态: can_send_code = false, countdown = 60');
// 发送验证码成功后,让验证码输入框获得焦点并调用系统键盘
setTimeout(() => {
// 设置验证码输入框聚焦状态
setCodeInputFocus(true);
// 清空验证码,让用户重新输入
setVerificationCode('');
console.log('设置验证码输入框聚焦');
}, 500); // 延迟500ms确保Toast显示完成后再聚焦
} else { } else {
console.log('验证码发送失败:', result.message);
Taro.showToast({ Taro.showToast({
title: result.message || '发送失败', title: result.message || '发送失败',
icon: 'none', icon: 'none',
@@ -51,6 +69,7 @@ const VerificationPage: React.FC = () => {
}); });
} }
} catch (error) { } catch (error) {
console.error('发送验证码异常:', error);
Taro.showToast({ Taro.showToast({
title: '发送失败,请重试', title: '发送失败,请重试',
icon: 'none', icon: 'none',
@@ -61,15 +80,19 @@ const VerificationPage: React.FC = () => {
// 倒计时效果 // 倒计时效果
useEffect(() => { useEffect(() => {
console.log('倒计时 useEffect 触发countdown:', countdown);
if (countdown > 0) { if (countdown > 0) {
const timer = setTimeout(() => { const timer = setTimeout(() => {
console.log('倒计时减少,从', countdown, '到', countdown - 1);
setCountdown(countdown - 1); setCountdown(countdown - 1);
}, 1000); }, 1000);
return () => clearTimeout(timer); return () => clearTimeout(timer);
} else { } else if (countdown === 0 && !can_send_code) {
console.log('倒计时结束,重新启用发送按钮');
setCanSendCode(true); setCanSendCode(true);
} }
}, [countdown]); }, [countdown, can_send_code]);
// 手机号登录 // 手机号登录
const handle_phone_login = async () => { const handle_phone_login = async () => {
@@ -96,7 +119,7 @@ const VerificationPage: React.FC = () => {
try { try {
// 调用登录服务 // 调用登录服务
const result = await phone_auth_login({ phone, verification_code }); const result = await phone_auth_login({ phone, verification_code });
if (result.success) { if (result.success) {
setTimeout(() => { setTimeout(() => {
@@ -122,12 +145,6 @@ const VerificationPage: React.FC = () => {
} }
}; };
// 查看条款
const handle_view_terms = (type: string = 'terms') => {
Taro.navigateTo({
url: `/pages/login/terms/index?type=${type}`
});
};
return ( return (
<View className="verification_page"> <View className="verification_page">
@@ -159,7 +176,12 @@ const VerificationPage: React.FC = () => {
onInput={(e) => setPhone(e.detail.value)} onInput={(e) => setPhone(e.detail.value)}
maxlength={11} maxlength={11}
/> />
<View className="char_count">{phone.length}/11</View> <View className="char_count">
<Text className={phone.length > 0 ? 'count_number active' : 'count_number'}>
{phone.length}
</Text>
/11
</View>
</View> </View>
</View> </View>
@@ -171,11 +193,20 @@ const VerificationPage: React.FC = () => {
type="number" type="number"
placeholder="输入短信验证码" placeholder="输入短信验证码"
placeholderClass="input_placeholder" placeholderClass="input_placeholder"
placeholderStyle="color:#999999;"
focus={code_input_focus}
value={verification_code} value={verification_code}
onInput={(e) => setVerificationCode(e.detail.value)} onInput={(e) => setVerificationCode(e.detail.value)}
onFocus={() => setCodeInputFocus(true)}
onBlur={() => setCodeInputFocus(false)}
maxlength={6} maxlength={6}
/> />
<View className="char_count">{verification_code.length}/6</View> <View className="char_count">
<Text className={verification_code.length > 0 ? 'count_number active' : 'count_number'}>
{verification_code.length}
</Text>
/6
</View>
</View> </View>
<Button <Button
className={`send_code_button ${!can_send_code ? 'disabled' : ''}`} className={`send_code_button ${!can_send_code ? 'disabled' : ''}`}
@@ -191,21 +222,33 @@ const VerificationPage: React.FC = () => {
</View> </View>
)} )}
</Button> </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> </View>
{/* 登录按钮 */} {/* 登录按钮 */}
<View className="button_section"> <View className="button_section">
<Button <Button
className={`login_button ${is_loading ? 'loading' : ''}`} className={`login_button ${is_loading ? 'loading' : ''} ${!can_login ? 'disabled' : ''}`}
onClick={handle_phone_login} onClick={handle_phone_login}
disabled={is_loading} disabled={!can_login}
> >
{is_loading ? '登录中...' : '登录'} {'登录'}
</Button> </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>
</View> </View>
{/* 底部指示器 */} {/* 底部指示器 */}

View File

@@ -1,5 +1,6 @@
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import httpService from './httpService'; import httpService from './httpService';
import tokenManager from '../utils/tokenManager';
// 微信用户信息接口 // 微信用户信息接口
export interface WechatUserInfo { export interface WechatUserInfo {
@@ -130,7 +131,8 @@ export const send_sms_code = async (phone: string): Promise<SmsResponse> => {
phone: phone phone: phone
}); });
if (response.success) { // 修复响应检查逻辑:检查 code === 0 或 success === true
if (response.code === 0 || response.success === true) {
return { return {
success: true, success: true,
message: '验证码发送成功' message: '验证码发送成功'
@@ -199,7 +201,14 @@ export const get_user_profile = (): Promise<WechatUserInfo> => {
// 保存用户登录状态 // 保存用户登录状态
export const save_login_state = (token: string, user_info: WechatUserInfo) => { export const save_login_state = (token: string, user_info: WechatUserInfo) => {
try { try {
Taro.setStorageSync('user_token', token); // 使用 tokenManager 保存令牌信息设置24小时过期
const expires_at = Date.now() + 24 * 60 * 60 * 1000; // 24小时后过期
tokenManager.setToken({
accessToken: token,
expiresAt: expires_at
});
// 保存用户信息
Taro.setStorageSync('user_info', user_info); Taro.setStorageSync('user_info', user_info);
Taro.setStorageSync('is_logged_in', true); Taro.setStorageSync('is_logged_in', true);
Taro.setStorageSync('login_time', Date.now()); Taro.setStorageSync('login_time', Date.now());
@@ -211,7 +220,10 @@ export const save_login_state = (token: string, user_info: WechatUserInfo) => {
// 清除登录状态 // 清除登录状态
export const clear_login_state = () => { export const clear_login_state = () => {
try { try {
Taro.removeStorageSync('user_token'); // 使用 tokenManager 清除令牌
tokenManager.clearTokens();
// 清除其他登录状态
Taro.removeStorageSync('user_info'); Taro.removeStorageSync('user_info');
Taro.removeStorageSync('is_logged_in'); Taro.removeStorageSync('is_logged_in');
Taro.removeStorageSync('login_time'); Taro.removeStorageSync('login_time');
@@ -223,22 +235,53 @@ export const clear_login_state = () => {
// 检查是否已登录 // 检查是否已登录
export const check_login_status = (): boolean => { export const check_login_status = (): boolean => {
try { try {
const is_logged_in = Taro.getStorageSync('is_logged_in'); // 使用 tokenManager 检查令牌有效性
const token = Taro.getStorageSync('user_token'); if (!tokenManager.hasValidToken()) {
const login_time = Taro.getStorageSync('login_time');
// 检查登录是否过期7天
if (login_time && Date.now() - login_time > 7 * 24 * 60 * 60 * 1000) {
clear_login_state(); clear_login_state();
return false; return false;
} }
return !!(is_logged_in && token); const is_logged_in = Taro.getStorageSync('is_logged_in');
return !!is_logged_in;
} catch (error) { } catch (error) {
return false; return false;
} }
}; };
// 检查令牌是否需要刷新剩余时间少于1小时时
export const should_refresh_token = (): boolean => {
try {
const remaining_time = tokenManager.getTokenRemainingTime();
const one_hour = 60 * 60 * 1000; // 1小时
return remaining_time > 0 && remaining_time < one_hour;
} catch (error) {
return false;
}
};
// 获取令牌状态信息
export const get_token_status = () => {
try {
const is_valid = tokenManager.hasValidToken();
const remaining_time = tokenManager.getTokenRemainingTime();
const is_expired = tokenManager.isTokenExpired();
return {
is_valid,
remaining_time,
is_expired,
expires_in_minutes: Math.floor(remaining_time / (60 * 1000))
};
} catch (error) {
return {
is_valid: false,
remaining_time: 0,
is_expired: true,
expires_in_minutes: 0
};
}
};
// 获取用户信息 // 获取用户信息
export const get_user_info = (): WechatUserInfo | null => { export const get_user_info = (): WechatUserInfo | null => {
try { try {
@@ -251,7 +294,8 @@ export const get_user_info = (): WechatUserInfo | null => {
// 获取用户token // 获取用户token
export const get_user_token = (): string | null => { export const get_user_token = (): string | null => {
try { try {
return Taro.getStorageSync('user_token') || null; // 使用 tokenManager 获取令牌
return tokenManager.getAccessToken();
} catch (error) { } catch (error) {
return null; return null;
} }

View File

@@ -85,6 +85,17 @@ class TokenManager {
return !!token && !this.isTokenExpired() return !!token && !this.isTokenExpired()
} }
// 获取令牌剩余有效时间(毫秒)
getTokenRemainingTime(): number {
const expiresAt = this.getTokenExpires()
if (!expiresAt) {
return 0 // 如果没有过期时间返回0
}
const remaining = expiresAt - Date.now()
return remaining > 0 ? remaining : 0
}
// 获取Authorization头 // 获取Authorization头
getAuthHeader(): Record<string, string> { getAuthHeader(): Record<string, string> {
const token = this.getAccessToken() const token = this.getAccessToken()
@@ -93,7 +104,7 @@ class TokenManager {
} }
return { return {
'Authorization': `Bearer ${token}` 'applet-token': `${token}`
} }
} }
} }