新增二维码加载状态管理,优化二维码获取逻辑,支持提前获取二维码并清理定时器,提升用户体验

This commit is contained in:
yahaozhang
2025-09-16 01:20:46 +08:00
parent f250fd07c4
commit de107c0f06
4 changed files with 111 additions and 6 deletions

View File

@@ -16,7 +16,7 @@
<!-- 二维码区域 --> <!-- 二维码区域 -->
<div v-else-if="qrInfo && countdown > 0" class="qr-container"> <div v-else-if="qrInfo && countdown > 0" class="qr-container">
<div class="qr-wrapper"> <div class="qr-wrapper">
<img :src="qrInfo.url" class="qr-code" alt="扫码登录" @error="$emit('qrImageError', $event)" /> <img :src="qrInfo.url" class="qr-code" alt="扫码登录" @error="$emit('qrImageError', $event)" @load="$emit('qrImageLoad', $event)" />
</div> </div>
<div class="countdown-timer">{{ formatTime(countdown) }}</div> <div class="countdown-timer">{{ formatTime(countdown) }}</div>
</div> </div>
@@ -103,12 +103,60 @@ export default {
default: null default: null
} }
}, },
emits: ['qrImageError', 'retryQrCode', 'pageRefresh'], emits: ['qrImageError', 'qrImageLoad', 'retryQrCode', 'pageRefresh'],
data() {
return {
probeTimer: null
}
},
watch: {
isWaitingQr(newVal) {
if (newVal && this.mecmachineId) {
this.startQrProbe()
} else {
this.stopQrProbe()
}
},
mecmachineId(newVal) {
if (this.isWaitingQr && newVal) {
this.startQrProbe()
}
}
},
mounted() {
if (this.isWaitingQr && this.mecmachineId) {
this.startQrProbe()
}
},
beforeUnmount() {
this.stopQrProbe()
},
methods: { methods: {
formatTime(seconds) { formatTime(seconds) {
const mins = Math.floor(seconds / 60) const mins = Math.floor(seconds / 60)
const secs = seconds % 60 const secs = seconds % 60
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}` return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
},
startQrProbe() {
this.stopQrProbe()
const attemptLoad = () => {
if (!this.mecmachineId) return
const testImg = new Image()
testImg.onload = () => {
this.$emit('qrImageLoad')
this.stopQrProbe()
}
testImg.onerror = () => {}
testImg.src = `https://uzi1.cn/image/${this.mecmachineId}/二维码.png?t=${Date.now()}`
}
attemptLoad()
this.probeTimer = setInterval(attemptLoad, 1000)
},
stopQrProbe() {
if (this.probeTimer) {
clearInterval(this.probeTimer)
this.probeTimer = null
}
} }
} }
} }

View File

@@ -34,7 +34,10 @@ export function usePlayState() {
qrRetryDelay: 2000, qrRetryDelay: 2000,
qrError: null, qrError: null,
mecmachineId: null, mecmachineId: null,
machineId: null machineId: null,
qrCreatedAt: null,
qrExpireAt: null,
qrDelayTimeoutId: null
}) })
const initializePage = async () => { const initializePage = async () => {
@@ -206,6 +209,12 @@ export function usePlayState() {
state.assets = data.assets state.assets = data.assets
state.mecmachineId = data.mecmachineId || null state.mecmachineId = data.mecmachineId || null
state.machineId = data.machineId || null state.machineId = data.machineId || null
if (data.qrCreatedAt) {
state.qrCreatedAt = data.qrCreatedAt
}
if (data.qrExpireAt) {
state.qrExpireAt = data.qrExpireAt
}
if (data.totalPoints) { if (data.totalPoints) {
state.totalPoints = data.totalPoints state.totalPoints = data.totalPoints

View File

@@ -121,7 +121,7 @@ export function useQrCode() {
startQrDelayCountdown(state, data.qrDelaySeconds) startQrDelayCountdown(state, data.qrDelaySeconds)
} }
setTimeout(async () => { state.qrDelayTimeoutId = setTimeout(async () => {
state.isWaitingQr = false state.isWaitingQr = false
// 清理等待倒计时定时器 // 清理等待倒计时定时器
if (typeof clearQrDelayCountdown === 'function') { if (typeof clearQrDelayCountdown === 'function') {
@@ -134,6 +134,7 @@ export function useQrCode() {
startCountdown() startCountdown()
startLoginPolling() startLoginPolling()
} }
state.qrDelayTimeoutId = null
}, data.qrDelaySeconds * 1000) }, data.qrDelaySeconds * 1000)
} else { } else {
console.log('进入立即处理分支') console.log('进入立即处理分支')
@@ -161,6 +162,13 @@ export function useQrCode() {
} }
} }
const clearQrDelayTimeout = (state) => {
if (state.qrDelayTimeoutId) {
clearTimeout(state.qrDelayTimeoutId)
state.qrDelayTimeoutId = null
}
}
const handleQrImageError = (state, event) => { const handleQrImageError = (state, event) => {
console.error('二维码图片加载失败:', event) console.error('二维码图片加载失败:', event)
state.qrError = '二维码图片加载失败,可能是网络问题' state.qrError = '二维码图片加载失败,可能是网络问题'
@@ -193,6 +201,7 @@ export function useQrCode() {
retrySelectRegion, retrySelectRegion,
processSelectRegionResponse, processSelectRegionResponse,
handleQrImageError, handleQrImageError,
retryGetQrCode retryGetQrCode,
clearQrDelayTimeout
} }
} }

View File

@@ -25,6 +25,7 @@
:submitting="state.submitting" :submitting="state.submitting"
:mecmachine-id="state.mecmachineId || state.machineId" :mecmachine-id="state.mecmachineId || state.machineId"
@qr-image-error="handleQrImageError" @qr-image-error="handleQrImageError"
@qr-image-load="handleQrReadyEarly"
@retry-qr-code="retryGetQrCode" @retry-qr-code="retryGetQrCode"
@page-refresh="handlePageRefresh" @page-refresh="handlePageRefresh"
/> />
@@ -134,7 +135,9 @@ export default {
const { const {
processSelectRegionResponse, processSelectRegionResponse,
handleQrImageError, handleQrImageError,
retryGetQrCode retryGetQrCode,
fetchQrCodeAfterDelay,
clearQrDelayTimeout
} = useQrCode() } = useQrCode()
onMounted(() => { onMounted(() => {
@@ -193,6 +196,41 @@ export default {
} }
} }
const handleQrReadyEarly = async () => {
try {
if (!state.isWaitingQr) return
if (!state.mecmachineId) return
if (state.qrInfo && state.qrInfo.url) return
// 结束等待并清理相关定时器
state.isWaitingQr = false
clearQrDelayCountdown()
if (typeof clearQrDelayTimeout === 'function') {
clearQrDelayTimeout(state)
}
// 立即拉取二维码并开始倒计时与登录轮询
await fetchQrCodeAfterDelay(
state,
countdown,
state.mecmachineId,
state.qrCreatedAt,
state.qrExpireAt
)
if (state.status === 'USING') {
startCountdown()
startLoginPolling(state.code, handleLoggedInStatus, handleCompletedStatus)
startProgressPolling(state.code, (progressData) => {
state.currentPoints = progressData.currentPoints || state.currentPoints
state.totalPoints = progressData.totalPoints || state.totalPoints
if (progressData.completedPoints !== undefined) {
state.completedPoints = progressData.completedPoints
}
})
}
} catch (error) {
console.error('提前获取二维码失败:', error)
}
}
return { return {
state, state,
countdown, countdown,
@@ -202,6 +240,7 @@ export default {
handlePageRefresh, handlePageRefresh,
handleQrImageError, handleQrImageError,
retryGetQrCode, retryGetQrCode,
handleQrReadyEarly,
handleRetry, handleRetry,
getRegionName, getRegionName,
getDisplayStatus, getDisplayStatus,