重构 Play.vue 组件,拆分为多个子组件,提升代码可维护性和可读性。

主要变更:
- 将 Play.vue 拆分为 LoadingOverlay、SelectRegion、ScanPage、GamePage、RefreshWaitPage、ErrorPage 等组件
- 新增 composables 目录,分离业务逻辑(usePlayState、useTimers、useQrCode)
- 优化代码结构,提升开发效率和维护性

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zyh
2025-08-29 19:03:56 +08:00
parent a8083088c6
commit 7497817640
12 changed files with 3578 additions and 1612 deletions

View File

@@ -0,0 +1,437 @@
import { reactive } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import {
getLinkStatus,
selectRegion as selectRegionAPI,
refreshLink as refreshLinkAPI,
pollLoginStatus,
getGameProgress,
getGameInterface as getGameInterfaceAPI
} from '@/api/play'
export function usePlayState() {
const route = useRoute()
const router = useRouter()
const state = reactive({
code: '',
status: 'NEW',
loading: true,
submitting: false,
needRefresh: false,
region: null,
qrInfo: null,
assets: null,
currentPoints: 0,
totalPoints: 1000,
completedPoints: 0,
error: null,
qrDelaySeconds: 0,
isWaitingQr: false,
qrRetryCount: 0,
maxQrRetries: 3,
qrRetryDelay: 2000,
qrError: null,
mecmachineId: null
})
const initializePage = async () => {
try {
await fetchStatus()
} catch (error) {
handleError(error)
} finally {
state.loading = false
}
}
const fetchStatus = async () => {
try {
const response = await getLinkStatus(state.code)
const data = response.data
await updateStateFromResponse(data)
} catch (error) {
throw error
}
}
const getGameInterface = async () => {
try {
console.log('调用游戏界面接口code:', state.code)
const response = await getGameInterfaceAPI(state.code)
console.log('游戏界面接口响应:', response.data)
return response
} catch (error) {
console.error('获取游戏界面数据失败:', error)
ElMessage.error('获取游戏界面数据失败')
throw error
}
}
const handleLoggedInStatus = async () => {
try {
console.log('检测到LOGGED_IN状态获取游戏界面数据')
const gameResponse = await getGameInterface()
const gameData = gameResponse.data
console.log('游戏界面数据:', gameData)
state.status = 'LOGGED_IN'
if(gameData.status == "COMPLETED"){
state.assets = {
homepageUrl: gameData.homepageUrl,
firstRewardUrl: gameData.firstRewardUrl,
midRewardUrl: gameData.midRewardUrl,
endRewardUrl: gameData.endRewardUrl,
qrCodeUrl: gameData.mecmachineId ? `https://2.uzi0.cc/image/${gameData.mecmachineId}/二维码.png?t=${new Date().getTime()}` : null,
...(gameData.assets || {})
}
} else {
state.assets = {
homepageUrl: gameData.mecmachineId ? `https://2.uzi0.cc/image/${gameData.mecmachineId}/首次主页.png?t=${new Date().getTime()}` : null,
firstRewardUrl: gameData.mecmachineId ? `https://2.uzi0.cc/image/${gameData.mecmachineId}/首次赏金.png?t=${new Date().getTime()}` : null,
midRewardUrl: gameData.mecmachineId ? `https://2.uzi0.cc/image/${gameData.mecmachineId}/中途赏金.png?t=${new Date().getTime()}` : null,
endRewardUrl: gameData.mecmachineId ? `https://2.uzi0.cc/image/${gameData.mecmachineId}/结束赏金.png?t=${new Date().getTime()}` : null,
qrCodeUrl: gameData.mecmachineId ? `https://2.uzi0.cc/image/${gameData.mecmachineId}/二维码.png?t=${new Date().getTime()}` : null,
...(gameData.assets || {})
}
}
console.log('更新区域信息:', {
gameDataRegion: gameData.region,
originalStateRegion: state.region
})
if (gameData.region) {
state.region = gameData.region
console.log('已设置 state.region =', state.region)
} else {
console.log('gameData.region 为空,未更新 state.region')
}
if (gameData.mecmachineId) {
state.mecmachineId = gameData.mecmachineId
}
if (gameData.totalPoints) {
state.totalPoints = gameData.totalPoints
} else if (gameData.assets && gameData.assets.totalPoints) {
state.totalPoints = gameData.assets.totalPoints
}
state.completedPoints = gameData.completedPoints || 0
state.currentPoints = 0
console.log('handleLoggedInStatus 执行完成,最终状态:', {
status: state.status,
region: state.region,
totalPoints: state.totalPoints,
completedPoints: state.completedPoints,
mecmachineId: state.mecmachineId
})
ElMessage.success('登录成功,正在进入游戏界面...')
} catch (error) {
console.error('获取游戏界面数据失败:', error)
ElMessage.error('获取游戏数据失败,请稍后重试')
}
}
const handleCompletedStatus = async () => {
try {
const gameResponse = await getGameInterfaceAPI(state.code)
const gameData = gameResponse.data
console.log('已完成状态 - 游戏界面数据:', gameData)
state.status = 'COMPLETED'
state.assets = {
homepageUrl: gameData.homepageUrl,
firstRewardUrl: gameData.firstRewardUrl,
midRewardUrl: gameData.midRewardUrl,
endRewardUrl: gameData.endRewardUrl,
qrCodeUrl: gameData.mecmachineId ? `https://2.uzi0.cc/image/${gameData.mecmachineId}/二维码.png?t=${Date.now()}` : null
}
state.totalPoints = gameData.totalPoints || 50
state.completedPoints = gameData.completedPoints || state.totalPoints
state.currentPoints = state.totalPoints
console.log('已完成状态更新完成:', {
status: state.status,
totalPoints: state.totalPoints,
completedPoints: state.completedPoints,
currentPoints: state.currentPoints,
assets: !!state.assets
})
} catch (error) {
console.error('获取已完成状态游戏数据失败:', error)
state.status = 'COMPLETED'
ElMessage.error('获取游戏数据失败,但订单已完成')
}
}
const updateStateFromResponse = async (data, skipQrProcessing = false) => {
if (data.status === 'LOGGED_IN') {
await handleLoggedInStatus()
return
}
if (data.status === 'COMPLETED') {
await handleCompletedStatus()
return
}
state.status = data.status
state.needRefresh = data.needRefresh || false
state.region = data.region
state.assets = data.assets
state.mecmachineId = data.mecmachineId || null
if (data.totalPoints) {
state.totalPoints = data.totalPoints
}
if (data.completedPoints !== undefined) {
state.completedPoints = data.completedPoints
}
if (data.assets && data.assets.totalPoints) {
state.totalPoints = data.assets.totalPoints
if (state.currentPoints === undefined) {
state.currentPoints = 0
}
}
console.log('updateStateFromResponse:', {
status: data.status,
dataRegion: data.region,
stateRegion: state.region,
mecmachineId: data.mecmachineId,
totalPoints: state.totalPoints,
completedPoints: state.completedPoints,
skipQrProcessing
})
if (skipQrProcessing) {
return
}
if (data.mecmachineId) {
const qrUrl = `https://2.uzi0.cc/image/${data.mecmachineId}/二维码.png?t=${Date.now()}`
state.qrInfo = {
url: qrUrl,
createdAt: data.qrCreatedAt,
expireAt: data.qrExpireAt
}
} else if (data.qr) {
state.qrInfo = data.qr
}
}
const selectRegion = async (region) => {
if (state.submitting) return
state.submitting = true
state.qrRetryCount = 0
try {
const response = await selectRegionAPI({ code: state.code, region })
const data = response.data
console.log('selectRegion 响应数据:', data)
return data
} catch (error) {
handleError(error)
throw error
} finally {
state.submitting = false
}
}
const handleRefresh = async (clearAllTimers) => {
try {
const response = await refreshLinkAPI(state.code)
const data = response.data
if (clearAllTimers) {
clearAllTimers()
}
state.needRefresh = false
state.status = 'NEW'
return data
} catch (error) {
handleError(error)
throw error
}
}
const handlePageRefresh = () => {
window.location.reload()
}
const handleRetry = () => {
state.error = null
state.loading = true
initializePage()
}
const handleError = (error) => {
console.error('API错误:', error)
const status = error?.response?.status
if (status === 401) {
console.log('检测到401错误跳转到登录页面')
router.replace({
name: 'Login',
query: { redirect: route.fullPath }
})
return
}
if (status === 400 || status === 403) {
state.error = 'INVALID_CODE'
} else if (status === 410) {
state.error = 'EXPIRED'
} else {
state.error = 'NETWORK_ERROR'
}
}
const formatTime = (seconds) => {
const mins = Math.floor(seconds / 60)
const secs = seconds % 60
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
}
const getRegionName = () => {
return state.region === 'Q' ? 'QQ区' : state.region === 'V' ? '微信区' : ''
}
const getCurrentUrl = () => {
return window.location.href
}
const getGameStatus = () => {
if (state.currentPoints >= state.totalPoints) {
return '已打完'
} else if (state.currentPoints > 0) {
return '代练中'
} else {
return '空闲'
}
}
const getStatusClass = () => {
const status = getGameStatus()
return {
'status-completed': status === '已打完',
'status-playing': status === '代练中',
'status-idle': status === '空闲'
}
}
const getDisplayStatus = () => {
if (state.status === 'COMPLETED') {
return '已完成'
} else if (state.status === 'LOGGED_IN') {
return '代练中'
} else {
return '状态'
}
}
const getStatusMessage = () => {
if (state.status === 'COMPLETED') {
return '代练已完成!感谢您的使用,订单已结束。'
} else if (state.status === 'LOGGED_IN') {
return '正在代练中,期间请勿操号,耐心等待代练完成......'
} else {
return '正在代练中,期间请勿操号,耐心等待代练完成......'
}
}
const getStatusMessageClass = () => {
if (state.status === 'COMPLETED') {
return 'status-message-completed'
} else {
return ''
}
}
const getProgressPercent = () => {
if (!state.totalPoints) return 0
return Math.min(100, (state.currentPoints / state.totalPoints) * 100)
}
const getCurrentGameImage = () => {
if (!state.assets) return null
const progress = getProgressPercent()
if (progress === 0) {
return state.assets.homepageUrl
} else if (progress < 50) {
return state.assets.firstRewardUrl
} else if (progress < 100) {
return state.assets.midRewardUrl
} else {
return state.assets.endRewardUrl
}
}
const getErrorTitle = () => {
const titles = {
'INVALID_CODE': '链接无效',
'EXPIRED': '链接已过期',
'REFUNDED': '订单已退单',
'NETWORK_ERROR': '网络错误'
}
return titles[state.error] || '出现错误'
}
const getErrorMessage = () => {
const messages = {
'INVALID_CODE': '请联系商家获取有效链接',
'EXPIRED': '请联系商家重新获取链接',
'REFUNDED': '该订单已被退单,无法继续使用',
'NETWORK_ERROR': '网络连接失败,请检查网络后重试'
}
return messages[state.error] || '请稍后重试或联系客服'
}
return {
state,
initializePage,
fetchStatus,
updateStateFromResponse,
handleLoggedInStatus,
handleCompletedStatus,
selectRegion,
handleRefresh,
handlePageRefresh,
handleRetry,
handleError,
formatTime,
getRegionName,
getCurrentUrl,
getGameStatus,
getStatusClass,
getDisplayStatus,
getStatusMessage,
getStatusMessageClass,
getProgressPercent,
getCurrentGameImage,
getErrorTitle,
getErrorMessage
}
}