fix: 获取验证码有问题
This commit is contained in:
parent
8fc4f0c7e6
commit
8f88302624
101
app/auth.tsx
101
app/auth.tsx
|
|
@ -1,13 +1,13 @@
|
||||||
import { Ionicons } from '@expo/vector-icons'
|
import { Ionicons } from '@expo/vector-icons'
|
||||||
import { Block, Text, Toast } from '@share/components'
|
import { Block, Text, Toast } from '@share/components'
|
||||||
import { router } from 'expo-router'
|
import { router } from 'expo-router'
|
||||||
import React, { useCallback, useMemo, useState } from 'react'
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { TextInput } from 'react-native'
|
import { TextInput } from 'react-native'
|
||||||
import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'
|
import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'
|
||||||
|
|
||||||
import { APP_VERSION } from '@/app.config'
|
import { APP_VERSION } from '@/app.config'
|
||||||
import BannerSection from '@/components/BannerSection'
|
import BannerSection from '@/components/BannerSection'
|
||||||
import { phoneNumber, setAuthToken } from '@/lib/auth'
|
import { phoneNumber } from '@/lib/auth'
|
||||||
import { isValidPhone } from '@/utils'
|
import { isValidPhone } from '@/utils'
|
||||||
import { openUrl } from '@/utils/webview-helper'
|
import { openUrl } from '@/utils/webview-helper'
|
||||||
|
|
||||||
|
|
@ -19,30 +19,49 @@ export default function Auth() {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [countdown, setCountdown] = useState(0)
|
const [countdown, setCountdown] = useState(0)
|
||||||
const [agreed, setAgreed] = useState(false)
|
const [agreed, setAgreed] = useState(false)
|
||||||
|
const countdownTimerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||||
|
|
||||||
const canSendCode = useMemo(() => {
|
const canSendCode = useMemo(() => {
|
||||||
return isValidPhone(phone)
|
return isValidPhone(phone) && countdown === 0
|
||||||
}, [phone])
|
}, [phone, countdown])
|
||||||
|
|
||||||
// TODO: 获取验证码接口还未实现,需要后端提供手机号验证码发送接口
|
// 清理倒计时定时器
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (countdownTimerRef.current) {
|
||||||
|
clearInterval(countdownTimerRef.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// 获取验证码
|
||||||
const handleSendCode = useCallback(async () => {
|
const handleSendCode = useCallback(async () => {
|
||||||
if (!canSendCode) {
|
if (!isValidPhone(phone)) {
|
||||||
Toast.show({ title: '请输入有效的手机号' })
|
Toast.show({ title: '请输入有效的手机号' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast.showLoading({ title: '正在发送验证码...' })
|
if (countdown > 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.showLoading({ title: '正在获取验证码...' })
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
// TODO: 调用获取验证码接口
|
|
||||||
// 当前使用 better-auth 的 phoneNumber.sendOtp,但后端可能还未实现
|
|
||||||
// 如果后端未实现,这里会报错,需要后端提供手机号验证码发送接口
|
|
||||||
const result = await phoneNumber.sendOtp({
|
const result = await phoneNumber.sendOtp({
|
||||||
phoneNumber: phone,
|
phoneNumber: phone,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
Toast.show({ title: result.error.message || '验证码发送失败,请检查后端接口是否已实现' })
|
// 处理不同类型的错误
|
||||||
|
const errorMessage = result.error.message || '验证码发送失败'
|
||||||
|
|
||||||
|
// 如果是 403 错误,可能是请求过于频繁
|
||||||
|
if (result.error.status === 403) {
|
||||||
|
Toast.show({ title: '请求过于频繁,请稍后再试' })
|
||||||
|
} else {
|
||||||
|
Toast.show({ title: errorMessage })
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,23 +69,31 @@ export default function Auth() {
|
||||||
setCountdown(60) // 开始倒计时
|
setCountdown(60) // 开始倒计时
|
||||||
|
|
||||||
// 倒计时逻辑
|
// 倒计时逻辑
|
||||||
const timer = setInterval(() => {
|
if (countdownTimerRef.current) {
|
||||||
|
clearInterval(countdownTimerRef.current)
|
||||||
|
}
|
||||||
|
countdownTimerRef.current = setInterval(() => {
|
||||||
setCountdown((prev) => {
|
setCountdown((prev) => {
|
||||||
if (prev <= 1) {
|
if (prev <= 1) {
|
||||||
clearInterval(timer)
|
if (countdownTimerRef.current) {
|
||||||
|
clearInterval(countdownTimerRef.current)
|
||||||
|
countdownTimerRef.current = null
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return prev - 1
|
return prev - 1
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
Toast.show({ title: error.message || '验证码发送失败' })
|
console.error('获取验证码失败:', error)
|
||||||
|
Toast.show({ title: error.message || '验证码发送失败,请稍后重试' })
|
||||||
} finally {
|
} finally {
|
||||||
Toast.hideLoading()
|
Toast.hideLoading()
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [phone, canSendCode])
|
}, [phone, countdown])
|
||||||
|
|
||||||
|
// 验证码登录/注册
|
||||||
const handleLogin = useCallback(async () => {
|
const handleLogin = useCallback(async () => {
|
||||||
if (!phone || !code) {
|
if (!phone || !code) {
|
||||||
Toast.show({ title: '请填写手机号和验证码' })
|
Toast.show({ title: '请填写手机号和验证码' })
|
||||||
|
|
@ -78,15 +105,19 @@ export default function Auth() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (code.length !== 6) {
|
||||||
|
Toast.show({ title: '请输入6位验证码' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!agreed) {
|
if (!agreed) {
|
||||||
Toast.show({ title: '请先阅读并同意服务条款和隐私协议' })
|
Toast.show({ title: '请先阅读并同意服务条款和隐私协议' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
Toast.showLoading({ title: '正在验证...' })
|
||||||
try {
|
try {
|
||||||
// 使用 better-auth 的 phoneNumber.verify 进行验证和登录
|
|
||||||
// verify 方法会自动创建会话,如果用户不存在会自动注册
|
|
||||||
const result = await phoneNumber.verify({
|
const result = await phoneNumber.verify({
|
||||||
phoneNumber: phone,
|
phoneNumber: phone,
|
||||||
code,
|
code,
|
||||||
|
|
@ -94,21 +125,35 @@ export default function Auth() {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
Toast.show({ title: result.error.message || '验证码错误或已过期' })
|
const errorStatus = result.error.status
|
||||||
|
const errorMessage = result.error.message || '验证失败'
|
||||||
|
|
||||||
|
if (errorStatus === 403) {
|
||||||
|
Toast.show({ title: '验证码尝试次数过多,请重新获取验证码' })
|
||||||
|
setCode('') // 清空验证码
|
||||||
|
setCountdown(0) // 重置倒计时,允许重新发送
|
||||||
|
if (countdownTimerRef.current) {
|
||||||
|
clearInterval(countdownTimerRef.current)
|
||||||
|
countdownTimerRef.current = null
|
||||||
|
}
|
||||||
|
} else if (errorStatus === 401) {
|
||||||
|
Toast.show({ title: '手机号未验证,请先验证手机号' })
|
||||||
|
} else {
|
||||||
|
Toast.show({ title: errorMessage })
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取认证 token(如果后端返回)
|
|
||||||
// better-auth 会自动处理 session,但如果有自定义 token,需要手动设置
|
|
||||||
if (result.data && 'token' in result.data && result.data.token) {
|
|
||||||
await setAuthToken(result.data.token)
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.show({ title: '登录成功!' })
|
Toast.show({ title: '登录成功!' })
|
||||||
|
|
||||||
|
// 延迟跳转,确保 toast 显示
|
||||||
|
setTimeout(() => {
|
||||||
router.replace('/(tabs)')
|
router.replace('/(tabs)')
|
||||||
|
}, 500)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
Toast.show({ title: error.message || '登录失败' })
|
console.error('登录失败:', error)
|
||||||
|
Toast.show({ title: error.message || '登录失败,请稍后重试' })
|
||||||
} finally {
|
} finally {
|
||||||
|
Toast.hideLoading()
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [phone, code, agreed])
|
}, [phone, code, agreed])
|
||||||
|
|
@ -160,7 +205,7 @@ export default function Auth() {
|
||||||
className={`border-2 border-black px-[6px] py-[4px] ${canSendCode && countdown === 0 ? 'bg-black' : 'bg-gray-200'}`}
|
className={`border-2 border-black px-[6px] py-[4px] ${canSendCode && countdown === 0 ? 'bg-black' : 'bg-gray-200'}`}
|
||||||
>
|
>
|
||||||
<Text className={`text-[10px] font-[900] ${canSendCode && countdown === 0 ? 'text-accent' : 'text-gray-500'}`}>
|
<Text className={`text-[10px] font-[900] ${canSendCode && countdown === 0 ? 'text-accent' : 'text-gray-500'}`}>
|
||||||
{countdown > 0 ? `${countdown}秒` : '发送验证码'}
|
{countdown > 0 ? `${countdown}秒` : '获取验证码'}
|
||||||
</Text>
|
</Text>
|
||||||
</Block>
|
</Block>
|
||||||
</Block>
|
</Block>
|
||||||
|
|
@ -225,7 +270,7 @@ export default function Auth() {
|
||||||
) : (
|
) : (
|
||||||
<Ionicons color="black" name="flash" size={20} />
|
<Ionicons color="black" name="flash" size={20} />
|
||||||
)}
|
)}
|
||||||
<Text className="font-900 text-[16px] text-black">{loading ? '处理中...' : '登录'}</Text>
|
<Text className="font-900 text-[16px] text-black">{loading ? '验证中...' : '登录/注册'}</Text>
|
||||||
</Block>
|
</Block>
|
||||||
</Block>
|
</Block>
|
||||||
</Block>
|
</Block>
|
||||||
|
|
|
||||||
|
|
@ -127,9 +127,5 @@ export const {
|
||||||
phoneNumber,
|
phoneNumber,
|
||||||
} = authClient
|
} = authClient
|
||||||
|
|
||||||
// TODO: 手机号验证码发送接口还未实现
|
|
||||||
// 需要后端提供手机号验证码发送接口,类似于 emailOtp.sendVerificationOtp
|
|
||||||
// 预期接口:phoneOtp.sendVerificationOtp({ phone, type: 'phone-verification' })
|
|
||||||
// 当前在 app/auth.tsx 中的 handleSendCode 函数中使用了临时模拟代码,需要替换为真实 API 调用
|
|
||||||
|
|
||||||
export const subscription: ISubscription = Reflect.get(authClient, 'subscription')
|
export const subscription: ISubscription = Reflect.get(authClient, 'subscription')
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue