Files
login_task_web/src/views/Play.vue
yahaozhang fcfb0b3c71 1
2025-10-03 10:02:16 +08:00

262 lines
6.7 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"
@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'"
: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"
/>
<!-- 完成状态 -->
<div v-else-if="state.status === 'COMPLETED'" 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.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, #667eea 0%, #764ba2 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>