feat(passport): 优化手机号授权流程及超时处理机制

- 新增手机号授权等待状态及定时器,防止重复触发授权操作
- 增加手机号授权超时弹窗提示,支持切换短信验证码登录
- 修改手机号授权冷却时间由3000ms调整为8000ms,减少频繁操作
- 控制手机号授权按钮状态,确保用户协议同意且无冷却或等待中才能操作
- 清理手机号授权等待定时器,防止内存泄露
- 调整加载状态时按钮UI表现,提高用户交互体验
This commit is contained in:
2026-07-16 21:33:13 +08:00
parent 46298c011f
commit 8d46016d8b
2 changed files with 59 additions and 6 deletions

View File

@@ -105,14 +105,17 @@ async function fetchUserInfo(token: string) {
const Login = () => {
const [isAgree, setIsAgree] = useState(false)
const [loading, setLoading] = useState(false)
const [phoneAuthPending, setPhoneAuthPending] = useState(false)
const [phoneAuthCooling, setPhoneAuthCooling] = useState(isPhoneAuthCoolingDown())
const [showContent, setShowContent] = useState(false)
const phoneAuthInFlightRef = useRef(false)
const phoneAuthPendingRef = useRef(false)
const phoneAuthTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const phoneAuthPendingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const router = Taro.getCurrentInstance().router
const isWeapp = IS_WEAPP
const canUsePhoneAuth = isAgree && !loading && !phoneAuthCooling
const canUsePhoneAuth = isAgree && !loading && !phoneAuthPending && !phoneAuthCooling
/** 页面加载动画 */
useEffect(() => {
@@ -125,6 +128,10 @@ const Login = () => {
clearTimeout(phoneAuthTimerRef.current)
phoneAuthTimerRef.current = null
}
if (phoneAuthPendingTimerRef.current) {
clearTimeout(phoneAuthPendingTimerRef.current)
phoneAuthPendingTimerRef.current = null
}
}
}, [])
@@ -138,6 +145,49 @@ const Login = () => {
}, remaining)
}
const clearPhoneAuthPending = () => {
phoneAuthPendingRef.current = false
setPhoneAuthPending(false)
if (phoneAuthPendingTimerRef.current) {
clearTimeout(phoneAuthPendingTimerRef.current)
phoneAuthPendingTimerRef.current = null
}
}
const startPhoneAuthPending = () => {
phoneAuthPendingRef.current = true
setPhoneAuthPending(true)
markPhoneAuthCalled()
startPhoneAuthCooldown()
if (phoneAuthPendingTimerRef.current) clearTimeout(phoneAuthPendingTimerRef.current)
phoneAuthPendingTimerRef.current = setTimeout(() => {
clearPhoneAuthPending()
Taro.showModal({
title: '手机号授权超时',
content: '微信手机号授权暂时无响应,请稍后重试或改用短信验证码登录。',
confirmText: '短信登录',
cancelText: '稍后重试',
confirmColor: '#07c160',
success: (res) => {
if (res.confirm) goSmsLogin()
},
})
}, 12000)
}
const handlePhoneAuthTap = () => {
if (!isAgree) {
Taro.showToast({ title: '请先勾选同意协议', icon: 'none' })
return
}
if (loading || phoneAuthInFlightRef.current || phoneAuthPendingRef.current || isPhoneAuthCoolingDown()) {
startPhoneAuthCooldown()
Taro.showToast({ title: '请稍后再试', icon: 'none' })
return
}
startPhoneAuthPending()
}
/** 解析 redirect 参数 */
const redirectUrl = (() => {
const raw = (router?.params as Record<string, string> | undefined)?.redirect
@@ -226,18 +276,19 @@ const Login = () => {
/** 手机号快捷登录 */
const handleGetPhoneNumber = async ({ detail }: GetPhoneNumberEvent) => {
clearPhoneAuthPending()
if (!isAgree) {
Taro.showToast({ title: '请先勾选同意协议', icon: 'none' })
return
}
if (loading || phoneAuthInFlightRef.current || isPhoneAuthCoolingDown()) {
if (loading || phoneAuthInFlightRef.current) {
startPhoneAuthCooldown()
Taro.showToast({ title: '请稍后再试', icon: 'none' })
return
}
phoneAuthInFlightRef.current = true
markPhoneAuthCalled()
startPhoneAuthCooldown()
const { code: phoneCode, encryptedData, iv, errMsg } = detail || {}
@@ -312,6 +363,7 @@ const Login = () => {
Taro.showToast({ title: e?.message || '登录失败', icon: 'none' })
} finally {
phoneAuthInFlightRef.current = false
clearPhoneAuthPending()
setLoading(false)
}
}
@@ -351,14 +403,15 @@ const Login = () => {
color: '#ffffff',
fontWeight: '600',
textAlign: 'center',
opacity: (!isAgree || loading) ? 0.5 : 1,
opacity: canUsePhoneAuth ? 1 : 0.5,
margin: 0,
padding: 0,
}}
openType={canUsePhoneAuth ? 'getPhoneNumber' : undefined}
onClick={handlePhoneAuthTap}
onGetPhoneNumber={handleGetPhoneNumber}
disabled={!canUsePhoneAuth}
loading={loading}
loading={loading || phoneAuthPending}
>
</Button>

View File

@@ -11,7 +11,7 @@
let lastPhoneAuthTime = 0
/** 冷却期ms在此期间不允许再次触发 getPhoneNumber */
const PHONE_AUTH_COOLDOWN = 3000
const PHONE_AUTH_COOLDOWN = 8000
/**
* 检查当前是否在冷却期内。