diff --git a/src/components/play/ScanPage.vue b/src/components/play/ScanPage.vue index 37b7473..2e3c609 100644 --- a/src/components/play/ScanPage.vue +++ b/src/components/play/ScanPage.vue @@ -16,7 +16,7 @@
- 扫码登录 + 扫码登录
{{ formatTime(countdown) }}
@@ -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 + } } } } diff --git a/src/composables/usePlayState.js b/src/composables/usePlayState.js index bfb6592..580b3d5 100644 --- a/src/composables/usePlayState.js +++ b/src/composables/usePlayState.js @@ -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 diff --git a/src/composables/useQrCode.js b/src/composables/useQrCode.js index b4e3d91..58e5c10 100644 --- a/src/composables/useQrCode.js +++ b/src/composables/useQrCode.js @@ -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 } } \ No newline at end of file diff --git a/src/views/Play.vue b/src/views/Play.vue index 1ba514a..12bb938 100644 --- a/src/views/Play.vue +++ b/src/views/Play.vue @@ -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,