diff --git a/.env.production b/.env.production
index 41d4531..a9aafec 100644
--- a/.env.production
+++ b/.env.production
@@ -2,4 +2,6 @@
VITE_API_BASE=https://2.uzi0.cc/api
# 前端基础URL(用于生成分享链接)
-VITE_BASE_URL=https://2.uzi0.cc
\ No newline at end of file
+VITE_BASE_URL=https://2.uzi0.cc
+
+IMAGE_BASE_URL=https://2.uzi0.cc/image
\ No newline at end of file
diff --git a/src/views/Play.vue b/src/views/Play.vue
index 582415f..a44028b 100644
--- a/src/views/Play.vue
+++ b/src/views/Play.vue
@@ -17,18 +17,22 @@
@click="selectRegion('Q')"
class="region-btn qq-btn"
:disabled="state.submitting"
+ :class="{ 'loading': state.submitting }"
>
-
⚠️
@@ -207,7 +225,8 @@ export default {
isWaitingQr: false,
qrRetryCount: 0,
maxQrRetries: 3,
- qrRetryDelay: 2000
+ qrRetryDelay: 2000,
+ qrError: null // 二维码错误信息
})
// 计时器
@@ -298,7 +317,7 @@ export default {
firstRewardUrl: gameData.firstRewardUrl,
midRewardUrl: gameData.midRewardUrl,
endRewardUrl: gameData.endRewardUrl,
- qrCodeUrl: gameData.qrCodeUrl,
+ qrCodeUrl: gameData.mecmachineId ? `https://2.uzi0.cc/image/${gameData.mecmachineId}/二维码.png` : null,
// 保留原有的assets数据(如果存在)
...(gameData.assets || {})
}
@@ -345,7 +364,7 @@ export default {
firstRewardUrl: gameData.firstRewardUrl,
midRewardUrl: gameData.midRewardUrl,
endRewardUrl: gameData.endRewardUrl,
- qrCodeUrl: gameData.qrCodeUrl
+ qrCodeUrl: gameData.mecmachineId ? `https://2.uzi0.cc/image/${gameData.mecmachineId}/二维码.png` : null
}
// 设置点数信息 - 已完成时当前点数等于目标点数
@@ -404,7 +423,7 @@ export default {
console.log('updateStateFromResponse:', {
status: data.status,
region: data.region,
- qrCodeUrl: data.qrCodeUrl,
+ mecmachineId: data.mecmachineId,
skipQrProcessing
})
@@ -414,9 +433,10 @@ export default {
}
// 处理二维码信息
- if (data.qrCodeUrl) {
+ if (data.mecmachineId) {
+ const qrUrl = `https://2.uzi0.cc/image/${data.mecmachineId}/二维码.png`
state.qrInfo = {
- url: data.qrCodeUrl,
+ url: qrUrl,
createdAt: data.qrCreatedAt,
expireAt: data.qrExpireAt
}
@@ -436,38 +456,63 @@ export default {
}
}
- // 验证二维码URL是否可访问
+ // 验证二维码URL是否可访问(生产环境优化版本)
const validateQrCodeUrl = async (url) => {
try {
+ console.log('开始验证二维码URL:', url)
+
+ // 检查URL格式
+ if (!url || typeof url !== 'string') {
+ console.error('无效的二维码URL:', url)
+ return false
+ }
+
+ // 在生产环境中,跳过HEAD请求验证,直接返回true
+ // 因为某些服务器可能不支持HEAD请求或存在CORS问题
+ if (process.env.NODE_ENV === 'production') {
+ console.log('生产环境跳过URL验证')
+ return true
+ }
+
+ // 开发环境进行完整验证
const controller = new AbortController()
- const timeoutId = setTimeout(() => controller.abort(), 5000)
+ const timeoutId = setTimeout(() => {
+ controller.abort()
+ console.log('URL验证超时')
+ }, 3000) // 减少超时时间到3秒
const response = await fetch(url, {
method: 'HEAD',
- signal: controller.signal
+ signal: controller.signal,
+ mode: 'no-cors' // 添加no-cors模式避免CORS问题
})
clearTimeout(timeoutId)
- return response.ok
+ console.log('URL验证响应:', response.status)
+ return true // no-cors模式下无法检查status,直接返回true
} catch (error) {
- console.warn('二维码URL验证失败:', error)
- return false
+ console.warn('二维码URL验证失败:', error.name, error.message)
+ // 即使验证失败,也返回true,让用户尝试加载图片
+ return true
}
}
// 延迟获取二维码(带重试机制)
- const fetchQrCodeAfterDelay = async (qrCodeUrl, qrCreatedAt, qrExpireAt, retryCount = 0) => {
+ const fetchQrCodeAfterDelay = async (mecmachineId, qrCreatedAt, qrExpireAt, retryCount = 0) => {
try {
+ const qrCodeUrl = `https://2.uzi0.cc/image/${mecmachineId}/二维码.png`
console.log(`尝试获取二维码 (第${retryCount + 1}次):`, qrCodeUrl)
// 验证二维码URL是否可访问
const isUrlValid = await validateQrCodeUrl(qrCodeUrl)
+ console.log('URL验证结果:', isUrlValid)
if (!isUrlValid) {
throw new Error('二维码URL无法访问')
}
// URL验证通过,设置二维码信息
+ console.log('设置二维码信息:', { qrCodeUrl, qrCreatedAt, qrExpireAt })
state.qrInfo = {
url: qrCodeUrl,
createdAt: qrCreatedAt,
@@ -478,14 +523,15 @@ export default {
const expireTime = new Date(qrExpireAt).getTime()
countdown.value = Math.max(0, Math.floor((expireTime - now) / 1000))
- // 重置重试计数
+ // 重置重试计数和等待状态
state.qrRetryCount = 0
+ state.isWaitingQr = false
+ console.log('二维码设置完成,countdown:', countdown.value)
ElMessage.success('二维码已准备就绪,请扫码登录')
- console.log('二维码获取成功')
} catch (error) {
- console.error(`二维码获取失败 (第${retryCount + 1}次):`, error)
+ console.error(`二维码获取失败 (第${retryCount + 1}次):`, error.message || error)
// 如果还有重试次数,则进行重试
if (retryCount < state.maxQrRetries) {
@@ -496,18 +542,15 @@ export default {
ElMessage.warning(`二维码获取失败,${delay/1000}秒后重试...`)
setTimeout(() => {
- fetchQrCodeAfterDelay(qrCodeUrl, qrCreatedAt, qrExpireAt, retryCount + 1)
+ fetchQrCodeAfterDelay(mecmachineId, qrCreatedAt, qrExpireAt, retryCount + 1)
}, delay)
} else {
- // 重试次数用完,重新请求新的二维码
- console.error('二维码获取重试次数用完,重新请求区域选择')
- ElMessage.error('二维码获取失败,正在重新请求...')
+ // 重试次数用完,显示错误状态而不是重新请求
+ console.error('二维码获取重试次数用完,显示错误状态')
state.qrRetryCount = 0
-
- // 重新调用区域选择API
- if (state.region) {
- retrySelectRegion(state.region)
- }
+ state.isWaitingQr = false
+ state.qrError = `二维码获取失败,已重试${state.maxQrRetries}次。可能是网络问题或服务器繁忙,请稍后重试。`
+ ElMessage.error('二维码获取失败,请点击重新获取按钮')
}
}
}
@@ -532,8 +575,8 @@ export default {
// 处理选择区域响应的通用逻辑
const processSelectRegionResponse = async (data) => {
- // 如果返回了延迟时间和二维码URL,需要延迟处理
- if (data.qrDelaySeconds && data.qrDelaySeconds > 0 && data.qrCodeUrl) {
+ // 如果返回了延迟时间和机器ID,需要延迟处理
+ if (data.qrDelaySeconds && data.qrDelaySeconds > 0 && data.mecmachineId) {
console.log('进入延迟分支')
// 跳过二维码处理,只更新基本状态
await updateStateFromResponse(data, true)
@@ -547,7 +590,7 @@ export default {
state.isWaitingQr = false
// 延迟后获取二维码(带重试机制)
- await fetchQrCodeAfterDelay(data.qrCodeUrl, data.qrCreatedAt, data.qrExpireAt)
+ await fetchQrCodeAfterDelay(data.mecmachineId, data.qrCreatedAt, data.qrExpireAt)
if (state.status === 'USING') {
startCountdown()
@@ -568,7 +611,7 @@ export default {
console.warn('立即获取的二维码URL无法访问,尝试重试')
// 清除当前二维码信息,触发重试
state.qrInfo = null
- await fetchQrCodeAfterDelay(data.qrCodeUrl, data.qrCreatedAt, data.qrExpireAt)
+ await fetchQrCodeAfterDelay(data.mecmachineId, data.qrCreatedAt, data.qrExpireAt)
}
}
@@ -629,6 +672,34 @@ export default {
window.location.reload()
}
+ // 处理二维码图片加载错误
+ const handleQrImageError = (event) => {
+ console.error('二维码图片加载失败:', event)
+ state.qrError = '二维码图片加载失败,可能是网络问题'
+ ElMessage.error('二维码图片加载失败')
+ }
+
+ // 重新获取二维码
+ const retryGetQrCode = async () => {
+ if (state.submitting) return
+
+ state.submitting = true
+ state.qrError = null
+ state.isWaitingQr = true
+ state.qrRetryCount = 0
+
+ try {
+ if (state.region) {
+ await selectRegion(state.region)
+ }
+ } catch (error) {
+ console.error('重新获取二维码失败:', error)
+ state.qrError = '重新获取失败,请刷新页面重试'
+ } finally {
+ state.submitting = false
+ }
+ }
+
// 开始倒计时
const startCountdown = () => {
clearTimer('countdown')
@@ -868,6 +939,8 @@ export default {
selectRegion,
handleRefresh,
handlePageRefresh,
+ handleQrImageError,
+ retryGetQrCode,
handleRetry,
formatTime,
getRegionName,
@@ -921,6 +994,14 @@ export default {
margin-bottom: 16px;
}
+.loading-spinner.small {
+ width: 20px;
+ height: 20px;
+ border: 2px solid #f3f3f3;
+ border-top: 2px solid #667eea;
+ margin: 0;
+}
+
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
@@ -994,6 +1075,17 @@ export default {
transform: none;
}
+.region-btn.loading {
+ opacity: 0.8;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.region-btn.loading:hover {
+ transform: none;
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+}
+
.btn-icon {
width: 50px;
height: 50px;
@@ -1089,7 +1181,7 @@ export default {
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
-.qr-expired {
+.qr-expired, .qr-error {
flex: 1;
display: flex;
flex-direction: column;
@@ -1100,24 +1192,24 @@ export default {
text-align: center;
}
-.warning-icon {
+.warning-icon, .error-icon {
font-size: 48px;
margin-bottom: 16px;
}
-.expired-text {
+.expired-text, .error-text {
font-size: 20px;
font-weight: 600;
margin: 0 0 8px 0;
}
-.expired-desc {
+.expired-desc, .error-desc {
font-size: 16px;
margin: 0 0 24px 0;
opacity: 0.8;
}
-.refresh-btn {
+.refresh-btn, .retry-btn {
background: white;
color: #667eea;
border: none;