264 lines
6.9 KiB
Vue
264 lines
6.9 KiB
Vue
<template>
|
||
<div class="play-container">
|
||
<!-- 加载状态 -->
|
||
<LoadingOverlay v-if="state.loading" />
|
||
|
||
<!-- 选区界面 -->
|
||
<SelectRegion
|
||
v-else-if="state.status === 'NEW' && !state.needRefresh"
|
||
:submitting="state.submitting"
|
||
:mecmachine-id="state.mecmachineId || state.machineId"
|
||
:total-points="state.totalPoints"
|
||
@select-region="handleSelectRegion"
|
||
/>
|
||
|
||
<!-- 扫码界面 -->
|
||
<ScanPage
|
||
v-else-if="state.status === 'USING'"
|
||
:region-name="getRegionName()"
|
||
:is-waiting-qr="state.isWaitingQr"
|
||
:qr-info="state.qrInfo"
|
||
:countdown="countdown"
|
||
:qr-delay-seconds="state.qrDelaySeconds"
|
||
:qr-retry-count="state.qrRetryCount"
|
||
:max-qr-retries="state.maxQrRetries"
|
||
:qr-error="state.qrError"
|
||
:submitting="state.submitting"
|
||
:mecmachine-id="state.mecmachineId || state.machineId"
|
||
@qr-image-error="handleQrImageError"
|
||
@qr-image-load="handleQrReadyEarly"
|
||
@retry-qr-code="retryGetQrCode"
|
||
@page-refresh="handlePageRefresh"
|
||
/>
|
||
|
||
<!-- 游戏界面 -->
|
||
<GamePage
|
||
v-else-if="state.status === 'LOGGED_IN' || (state.status === 'COMPLETED' && !state.isCompletedExpired)"
|
||
:region="state.region"
|
||
:region-desc="state.regionDesc"
|
||
:machine-id="state.machineId"
|
||
:display-status="getDisplayStatus()"
|
||
:completed-points="state.completedPoints"
|
||
:total-points="state.totalPoints"
|
||
:progress-display-format="state.progressDisplayFormat"
|
||
:status-message="getStatusMessage()"
|
||
:status-message-class="getStatusMessageClass()"
|
||
:assets="state.assets"
|
||
:current-points="state.currentPoints"
|
||
:code-no="state.code"
|
||
/>
|
||
|
||
<!-- 完成状态(超过24小时) -->
|
||
<div v-else-if="state.status === 'COMPLETED' && state.isCompletedExpired" class="completed-page">
|
||
<div class="completed-text">已打完</div>
|
||
</div>
|
||
|
||
<!-- 刷新等待界面 -->
|
||
<RefreshWaitPage
|
||
v-else-if="state.needRefresh"
|
||
:refresh-cooldown="refreshCooldown"
|
||
:mecmachine-id="state.mecmachineId || state.machineId"
|
||
@refresh="modifiedHandleRefresh"
|
||
/>
|
||
|
||
<!-- 错误界面 -->
|
||
<ErrorPage
|
||
v-else
|
||
:error-title="getErrorTitle()"
|
||
:error-message="getErrorMessage()"
|
||
@retry="handleRetry"
|
||
/>
|
||
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
||
import { useRoute } from 'vue-router'
|
||
import { usePlayState } from '@/composables/usePlayState'
|
||
import { useTimers } from '@/composables/useTimers'
|
||
import { useQrCode } from '@/composables/useQrCode'
|
||
|
||
import LoadingOverlay from '@/components/play/LoadingOverlay.vue'
|
||
import SelectRegion from '@/components/play/SelectRegion.vue'
|
||
import ScanPage from '@/components/play/ScanPage.vue'
|
||
import GamePage from '@/components/play/GamePage.vue'
|
||
import RefreshWaitPage from '@/components/play/RefreshWaitPage.vue'
|
||
import ErrorPage from '@/components/play/ErrorPage.vue'
|
||
|
||
export default {
|
||
name: 'Play',
|
||
components: {
|
||
LoadingOverlay,
|
||
SelectRegion,
|
||
ScanPage,
|
||
GamePage,
|
||
RefreshWaitPage,
|
||
ErrorPage
|
||
},
|
||
setup() {
|
||
const route = useRoute()
|
||
|
||
const {
|
||
state,
|
||
initializePage,
|
||
updateStateFromResponse,
|
||
handleLoggedInStatus,
|
||
handleCompletedStatus,
|
||
selectRegion,
|
||
handleRefresh,
|
||
handlePageRefresh,
|
||
handleRetry,
|
||
getRegionName,
|
||
getDisplayStatus,
|
||
getStatusMessage,
|
||
getStatusMessageClass,
|
||
getProgressPercent,
|
||
getCurrentGameImage,
|
||
getGameStatus,
|
||
getStatusClass,
|
||
getErrorTitle,
|
||
getErrorMessage
|
||
} = usePlayState()
|
||
|
||
const {
|
||
countdown,
|
||
refreshCooldown,
|
||
clearAllTimers,
|
||
startCountdown,
|
||
startLoginPolling,
|
||
startRefreshCooldown,
|
||
startProgressPolling,
|
||
startQrDelayCountdown,
|
||
clearQrDelayCountdown
|
||
} = useTimers()
|
||
|
||
const {
|
||
processSelectRegionResponse,
|
||
handleQrImageError,
|
||
retryGetQrCode,
|
||
fetchQrCodeAfterDelay,
|
||
clearQrDelayTimeout
|
||
} = useQrCode()
|
||
|
||
onMounted(() => {
|
||
// 兼容两种方式:路径参数和查询参数
|
||
const code = route.params.code || route.query.code
|
||
if (!code) {
|
||
state.error = 'INVALID_CODE'
|
||
state.loading = false
|
||
return
|
||
}
|
||
|
||
state.code = code
|
||
initializePage()
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
clearAllTimers()
|
||
})
|
||
|
||
const handleSelectRegion = async (region) => {
|
||
try {
|
||
const data = await selectRegion(region)
|
||
await processSelectRegionResponse(
|
||
state,
|
||
countdown,
|
||
data,
|
||
() => 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
|
||
}
|
||
})
|
||
},
|
||
updateStateFromResponse,
|
||
startQrDelayCountdown,
|
||
clearQrDelayCountdown
|
||
)
|
||
} catch (error) {
|
||
console.error('处理选择区域失败:', error)
|
||
}
|
||
}
|
||
|
||
const modifiedHandleRefresh = async () => {
|
||
try {
|
||
const data = await handleRefresh(clearAllTimers)
|
||
|
||
if (data.waitSeconds) {
|
||
startRefreshCooldown(data.waitSeconds)
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('刷新失败:', error)
|
||
}
|
||
}
|
||
|
||
const handleQrReadyEarly = async () => {
|
||
// 移除提前显示二维码的逻辑,确保必须等待指定时间后才显示
|
||
console.log('二维码提前就绪事件被忽略,必须等待指定时间后才显示')
|
||
}
|
||
|
||
return {
|
||
state,
|
||
countdown,
|
||
refreshCooldown,
|
||
handleSelectRegion,
|
||
handleRefresh: modifiedHandleRefresh,
|
||
handlePageRefresh,
|
||
handleQrImageError,
|
||
retryGetQrCode,
|
||
handleQrReadyEarly,
|
||
handleRetry,
|
||
getRegionName,
|
||
getDisplayStatus,
|
||
getStatusMessage,
|
||
getStatusMessageClass,
|
||
getProgressPercent,
|
||
getCurrentGameImage,
|
||
getGameStatus,
|
||
getStatusClass,
|
||
getErrorTitle,
|
||
getErrorMessage
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.play-container {
|
||
min-height: 100vh;
|
||
background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
|
||
display: flex;
|
||
flex-direction: column;
|
||
position: relative;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
}
|
||
|
||
.completed-page {
|
||
flex: 1;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
background: white;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.completed-text {
|
||
font-size: 48px;
|
||
font-weight: bold;
|
||
color: #28a745;
|
||
text-align: center;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.completed-text {
|
||
font-size: 36px;
|
||
}
|
||
}
|
||
</style>
|