优化二维码获取逻辑,新增URL验证和重试机制,提升用户体验;更新Play.vue界面,增加重试信息显示和区域选择处理逻辑。
This commit is contained in:
@@ -51,6 +51,7 @@
|
||||
<div class="loading-spinner"></div>
|
||||
<p class="waiting-text">正在准备二维码...</p>
|
||||
<p class="waiting-desc">预计等待 {{ state.qrDelaySeconds }} 秒</p>
|
||||
<p v-if="state.qrRetryCount > 0" class="retry-info">重试中... ({{ state.qrRetryCount }}/{{ state.maxQrRetries }})</p>
|
||||
</div>
|
||||
|
||||
<!-- 二维码区域 -->
|
||||
@@ -172,7 +173,7 @@
|
||||
|
||||
<script>
|
||||
import { reactive, ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {
|
||||
getLinkStatus,
|
||||
@@ -187,6 +188,7 @@ export default {
|
||||
name: 'Play',
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// 状态管理
|
||||
const state = reactive({
|
||||
@@ -202,7 +204,10 @@ export default {
|
||||
totalPoints: 1000,
|
||||
error: null,
|
||||
qrDelaySeconds: 0,
|
||||
isWaitingQr: false
|
||||
isWaitingQr: false,
|
||||
qrRetryCount: 0,
|
||||
maxQrRetries: 3,
|
||||
qrRetryDelay: 2000
|
||||
})
|
||||
|
||||
// 计时器
|
||||
@@ -431,12 +436,38 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
// 延迟获取二维码
|
||||
const fetchQrCodeAfterDelay = async (qrCodeUrl, qrCreatedAt, qrExpireAt) => {
|
||||
// 验证二维码URL是否可访问
|
||||
const validateQrCodeUrl = async (url) => {
|
||||
try {
|
||||
// 这里可以添加额外的验证或处理逻辑
|
||||
// 比如检查 URL 是否可访问
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), 5000)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'HEAD',
|
||||
signal: controller.signal
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
return response.ok
|
||||
} catch (error) {
|
||||
console.warn('二维码URL验证失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 延迟获取二维码(带重试机制)
|
||||
const fetchQrCodeAfterDelay = async (qrCodeUrl, qrCreatedAt, qrExpireAt, retryCount = 0) => {
|
||||
try {
|
||||
console.log(`尝试获取二维码 (第${retryCount + 1}次):`, qrCodeUrl)
|
||||
|
||||
// 验证二维码URL是否可访问
|
||||
const isUrlValid = await validateQrCodeUrl(qrCodeUrl)
|
||||
|
||||
if (!isUrlValid) {
|
||||
throw new Error('二维码URL无法访问')
|
||||
}
|
||||
|
||||
// URL验证通过,设置二维码信息
|
||||
state.qrInfo = {
|
||||
url: qrCodeUrl,
|
||||
createdAt: qrCreatedAt,
|
||||
@@ -447,10 +478,104 @@ export default {
|
||||
const expireTime = new Date(qrExpireAt).getTime()
|
||||
countdown.value = Math.max(0, Math.floor((expireTime - now) / 1000))
|
||||
|
||||
// 重置重试计数
|
||||
state.qrRetryCount = 0
|
||||
|
||||
ElMessage.success('二维码已准备就绪,请扫码登录')
|
||||
console.log('二维码获取成功')
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取二维码失败:', error)
|
||||
ElMessage.error('二维码获取失败,请重试')
|
||||
console.error(`二维码获取失败 (第${retryCount + 1}次):`, error)
|
||||
|
||||
// 如果还有重试次数,则进行重试
|
||||
if (retryCount < state.maxQrRetries) {
|
||||
state.qrRetryCount = retryCount + 1
|
||||
const delay = state.qrRetryDelay * Math.pow(2, retryCount) // 指数退避
|
||||
|
||||
console.log(`${delay}ms后进行第${retryCount + 2}次重试`)
|
||||
ElMessage.warning(`二维码获取失败,${delay/1000}秒后重试...`)
|
||||
|
||||
setTimeout(() => {
|
||||
fetchQrCodeAfterDelay(qrCodeUrl, qrCreatedAt, qrExpireAt, retryCount + 1)
|
||||
}, delay)
|
||||
} else {
|
||||
// 重试次数用完,重新请求新的二维码
|
||||
console.error('二维码获取重试次数用完,重新请求区域选择')
|
||||
ElMessage.error('二维码获取失败,正在重新请求...')
|
||||
state.qrRetryCount = 0
|
||||
|
||||
// 重新调用区域选择API
|
||||
if (state.region) {
|
||||
retrySelectRegion(state.region)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重试选择区域(内部使用,不显示loading状态)
|
||||
const retrySelectRegion = async (region) => {
|
||||
try {
|
||||
console.log('重试选择区域:', region)
|
||||
const response = await selectRegionAPI({ code: state.code, region })
|
||||
const data = response.data
|
||||
|
||||
console.log('retrySelectRegion 响应数据:', data)
|
||||
|
||||
// 处理响应数据,使用重试逻辑
|
||||
await processSelectRegionResponse(data)
|
||||
|
||||
} catch (error) {
|
||||
console.error('重试选择区域失败:', error)
|
||||
ElMessage.error('重新请求二维码失败,请手动刷新页面')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理选择区域响应的通用逻辑
|
||||
const processSelectRegionResponse = async (data) => {
|
||||
// 如果返回了延迟时间和二维码URL,需要延迟处理
|
||||
if (data.qrDelaySeconds && data.qrDelaySeconds > 0 && data.qrCodeUrl) {
|
||||
console.log('进入延迟分支')
|
||||
// 跳过二维码处理,只更新基本状态
|
||||
await updateStateFromResponse(data, true)
|
||||
|
||||
state.qrDelaySeconds = data.qrDelaySeconds
|
||||
state.isWaitingQr = true
|
||||
console.log('设置状态:', { status: state.status, isWaitingQr: state.isWaitingQr })
|
||||
ElMessage.info(`正在准备二维码,请等待 ${data.qrDelaySeconds} 秒...`)
|
||||
|
||||
setTimeout(async () => {
|
||||
state.isWaitingQr = false
|
||||
|
||||
// 延迟后获取二维码(带重试机制)
|
||||
await fetchQrCodeAfterDelay(data.qrCodeUrl, data.qrCreatedAt, data.qrExpireAt)
|
||||
|
||||
if (state.status === 'USING') {
|
||||
startCountdown()
|
||||
startLoginPolling()
|
||||
}
|
||||
}, data.qrDelaySeconds * 1000)
|
||||
} else {
|
||||
console.log('进入立即处理分支')
|
||||
// 没有延迟时间,立即处理二维码
|
||||
await updateStateFromResponse(data)
|
||||
state.isWaitingQr = false
|
||||
console.log('设置状态:', { status: state.status, isWaitingQr: state.isWaitingQr, qrInfo: !!state.qrInfo })
|
||||
|
||||
// 对于立即处理的二维码,也要进行验证
|
||||
if (state.qrInfo && state.qrInfo.url) {
|
||||
const isUrlValid = await validateQrCodeUrl(state.qrInfo.url)
|
||||
if (!isUrlValid) {
|
||||
console.warn('立即获取的二维码URL无法访问,尝试重试')
|
||||
// 清除当前二维码信息,触发重试
|
||||
state.qrInfo = null
|
||||
await fetchQrCodeAfterDelay(data.qrCodeUrl, data.qrCreatedAt, data.qrExpireAt)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.status === 'USING') {
|
||||
startCountdown()
|
||||
startLoginPolling()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,6 +584,7 @@ export default {
|
||||
if (state.submitting) return
|
||||
|
||||
state.submitting = true
|
||||
state.qrRetryCount = 0 // 重置重试计数
|
||||
|
||||
try {
|
||||
const response = await selectRegionAPI({ code: state.code, region })
|
||||
@@ -466,40 +592,8 @@ export default {
|
||||
|
||||
console.log('selectRegion 响应数据:', data)
|
||||
|
||||
// 如果返回了延迟时间和二维码URL,需要延迟处理
|
||||
if (data.qrDelaySeconds && data.qrDelaySeconds > 0 && data.qrCodeUrl) {
|
||||
console.log('进入延迟分支')
|
||||
// 跳过二维码处理,只更新基本状态
|
||||
await updateStateFromResponse(data, true)
|
||||
|
||||
state.qrDelaySeconds = data.qrDelaySeconds
|
||||
state.isWaitingQr = true
|
||||
console.log('设置状态:', { status: state.status, isWaitingQr: state.isWaitingQr })
|
||||
ElMessage.info(`正在准备二维码,请等待 ${data.qrDelaySeconds} 秒...`)
|
||||
|
||||
setTimeout(async () => {
|
||||
state.isWaitingQr = false
|
||||
|
||||
// 延迟后获取二维码
|
||||
await fetchQrCodeAfterDelay(data.qrCodeUrl, data.qrCreatedAt, data.qrExpireAt)
|
||||
|
||||
if (state.status === 'USING') {
|
||||
startCountdown()
|
||||
startLoginPolling()
|
||||
}
|
||||
}, data.qrDelaySeconds * 1000)
|
||||
} else {
|
||||
console.log('进入立即处理分支')
|
||||
// 没有延迟时间,立即处理二维码
|
||||
await updateStateFromResponse(data)
|
||||
state.isWaitingQr = false
|
||||
console.log('设置状态:', { status: state.status, isWaitingQr: state.isWaitingQr, qrInfo: !!state.qrInfo })
|
||||
|
||||
if (state.status === 'USING') {
|
||||
startCountdown()
|
||||
startLoginPolling()
|
||||
}
|
||||
}
|
||||
// 使用通用处理逻辑
|
||||
await processSelectRegionResponse(data)
|
||||
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
@@ -628,8 +722,18 @@ export default {
|
||||
// 获取HTTP状态码
|
||||
const status = error?.response?.status
|
||||
|
||||
// 如果是认证相关错误,跳转到登录页面
|
||||
if (status === 401) {
|
||||
console.log('检测到401错误,跳转到登录页面')
|
||||
router.replace({
|
||||
name: 'Login',
|
||||
query: { redirect: route.fullPath }
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 根据HTTP状态码设置错误状态
|
||||
if (status === 400 || status === 401 || status === 403) {
|
||||
if (status === 400 || status === 403) {
|
||||
state.error = 'INVALID_CODE'
|
||||
} else if (status === 410) {
|
||||
state.error = 'EXPIRED'
|
||||
@@ -956,6 +1060,14 @@ export default {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.retry-info {
|
||||
font-size: 14px;
|
||||
margin: 8px 0 0 0;
|
||||
opacity: 0.9;
|
||||
color: #ffd700;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.qr-wrapper {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
|
||||
Reference in New Issue
Block a user