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

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 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 class="countdown-timer">{{ formatTime(countdown) }}</div>
</div>
@@ -103,12 +103,60 @@ export default {
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: {
formatTime(seconds) {
const mins = Math.floor(seconds / 60)
const secs = seconds % 60
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,
qrError: null,
mecmachineId: null,
machineId: null
machineId: null,
qrCreatedAt: null,
qrExpireAt: null,
qrDelayTimeoutId: null
})
const initializePage = async () => {
@@ -206,6 +209,12 @@ export function usePlayState() {
state.assets = data.assets
state.mecmachineId = data.mecmachineId || null
state.machineId = data.machineId || null
if (data.qrCreatedAt) {
state.qrCreatedAt = data.qrCreatedAt
}
if (data.qrExpireAt) {
state.qrExpireAt = data.qrExpireAt
}
if (data.totalPoints) {
state.totalPoints = data.totalPoints

View File

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

View File

@@ -25,6 +25,7 @@
:submitting="state.submitting"
:mecmachine-id="state.mecmachineId || state.machineId"
@qr-image-error="handleQrImageError"
@qr-image-load="handleQrReadyEarly"
@retry-qr-code="retryGetQrCode"
@page-refresh="handlePageRefresh"
/>
@@ -134,7 +135,9 @@ export default {
const {
processSelectRegionResponse,
handleQrImageError,
retryGetQrCode
retryGetQrCode,
fetchQrCodeAfterDelay,
clearQrDelayTimeout
} = useQrCode()
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 {
state,
countdown,
@@ -202,6 +240,7 @@ export default {
handlePageRefresh,
handleQrImageError,
retryGetQrCode,
handleQrReadyEarly,
handleRetry,
getRegionName,
getDisplayStatus,