更新页面样式和结构,优化加载、错误和刷新页面的用户体验,调整Vite配置以使用生产环境API,新增订单详情展示功能,提升整体界面一致性和可读性

This commit is contained in:
yahaozhang
2025-11-03 21:20:52 +08:00
parent fba18fc32c
commit 2e35f219e7
9 changed files with 781 additions and 235 deletions

View File

@@ -1,11 +1,25 @@
<template> <template>
<div class="error-page"> <div class="error-page">
<div class="page-header">
<h1 class="title">出现错误</h1>
</div>
<div class="error-container"> <div class="error-container">
<div class="error-icon"></div> <div class="icon-wrapper">
<svg class="error-svg" viewBox="0 0 24 24" width="80" height="80">
<circle cx="12" cy="12" r="11" fill="#f44336" opacity="0.1"/>
<path fill="#f44336" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>
</svg>
</div>
<h2 class="error-title">{{ errorTitle }}</h2> <h2 class="error-title">{{ errorTitle }}</h2>
<p class="error-message">{{ errorMessage }}</p> <p class="error-message">{{ errorMessage }}</p>
<button @click="$emit('retry')" class="retry-btn">重新尝试</button> <button @click="$emit('retry')" class="retry-btn">重新尝试</button>
</div> </div>
<div class="notice-text">
<p>遇到问题请联系客服</p>
<p>我们会尽快为您解决</p>
</div>
</div> </div>
</template> </template>
@@ -30,54 +44,110 @@ export default {
.error-page { .error-page {
flex: 1; flex: 1;
display: flex; display: flex;
justify-content: center; flex-direction: column;
align-items: center; background: white;
padding: 40px 20px; min-height: 100vh;
}
.page-header {
text-align: center;
padding: 20px;
background: white;
border-bottom: 3px solid #f44336;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.title {
font-size: 20px;
font-weight: 600;
margin: 0;
color: #333;
background: linear-gradient(135deg, #f44336 0%, #e91e63 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
} }
.error-container { .error-container {
background: white; flex: 1;
padding: 40px; display: flex;
border-radius: 20px; flex-direction: column;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); justify-content: center;
align-items: center;
padding: 40px 20px;
text-align: center; text-align: center;
max-width: 400px;
width: 100%;
} }
.error-icon { .icon-wrapper {
font-size: 48px; margin-bottom: 24px;
margin-bottom: 16px; animation: shake 0.5s ease-in-out;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.error-svg {
filter: drop-shadow(0 4px 8px rgba(244, 67, 54, 0.3));
} }
.error-title { .error-title {
font-size: 20px; font-size: 22px;
font-weight: 600; font-weight: 600;
margin: 0 0 12px 0; margin: 0 0 12px 0;
color: #333; color: #333;
} }
.error-message { .error-message {
font-size: 16px; font-size: 14px;
color: #666; color: #666;
margin: 0 0 24px 0; margin: 0 0 32px 0;
line-height: 1.5; line-height: 1.6;
max-width: 320px;
} }
.retry-btn { .retry-btn {
background: #667eea; background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
color: white; color: white;
border: none; border: none;
padding: 12px 32px; padding: 12px 32px;
border-radius: 25px; border-radius: 8px;
font-size: 16px; font-size: 14px;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
min-width: 140px;
} }
.retry-btn:hover { .retry-btn:hover {
background: #5a6fd8; opacity: 0.9;
transform: translateY(-2px); transform: translateY(-1px);
}
.notice-text {
text-align: center;
padding: 20px;
background: #f8f9fa;
border-top: 2px solid #e9ecef;
margin-top: auto;
}
.notice-text p {
margin: 4px 0;
font-size: 12px;
color: #666;
}
@media (max-width: 768px) {
.error-svg {
width: 64px;
height: 64px;
}
.error-title {
font-size: 18px;
}
} }
</style> </style>

View File

@@ -1,7 +1,22 @@
<template> <template>
<div class="loading-overlay"> <div class="loading-overlay">
<div class="loading-spinner"></div> <div class="page-header">
<p>加载中...</p> <h1 class="title">加载中</h1>
</div>
<div class="loading-container">
<div class="spinner-wrapper">
<div class="loading-spinner"></div>
<div class="loading-pulse"></div>
</div>
<p class="loading-text">正在加载订单信息...</p>
<p class="loading-subtext">请稍候</p>
</div>
<div class="notice-text">
<p>代理技术代练平台</p>
<p>安全 · 专业 · 高效</p>
</div>
</div> </div>
</template> </template>
@@ -18,26 +33,140 @@ export default {
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background: rgba(255, 255, 255, 0.9); background: white;
display: flex;
flex-direction: column;
z-index: 1000;
}
.page-header {
text-align: center;
padding: 20px;
background: white;
border-bottom: 3px solid #4776e6;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.title {
font-size: 20px;
font-weight: 600;
margin: 0;
color: #333;
background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.loading-container {
flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
z-index: 1000; padding: 40px 20px;
}
.spinner-wrapper {
position: relative;
width: 80px;
height: 80px;
margin-bottom: 32px;
} }
.loading-spinner { .loading-spinner {
width: 40px; position: absolute;
height: 40px; top: 50%;
border: 4px solid #f3f3f3; left: 50%;
border-top: 4px solid #667eea; width: 60px;
height: 60px;
margin-top: -30px;
margin-left: -30px;
border: 4px solid #e9ecef;
border-top: 4px solid #4776e6;
border-radius: 50%; border-radius: 50%;
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
margin-bottom: 16px; }
.loading-pulse {
position: absolute;
top: 50%;
left: 50%;
width: 80px;
height: 80px;
margin-top: -40px;
margin-left: -40px;
border: 2px solid #4776e6;
border-radius: 50%;
opacity: 0.3;
animation: pulse 2s ease-in-out infinite;
} }
@keyframes spin { @keyframes spin {
0% { transform: rotate(0deg); } 0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); } 100% { transform: rotate(360deg); }
} }
@keyframes pulse {
0%, 100% {
transform: scale(0.8);
opacity: 0.3;
}
50% {
transform: scale(1.1);
opacity: 0.6;
}
}
.loading-text {
font-size: 18px;
font-weight: 600;
margin: 0 0 8px 0;
color: #333;
}
.loading-subtext {
font-size: 14px;
margin: 0;
color: #666;
}
.notice-text {
text-align: center;
padding: 20px;
background: #f8f9fa;
border-top: 2px solid #e9ecef;
margin-top: auto;
}
.notice-text p {
margin: 4px 0;
font-size: 12px;
color: #666;
}
@media (max-width: 768px) {
.spinner-wrapper {
width: 64px;
height: 64px;
}
.loading-spinner {
width: 48px;
height: 48px;
margin-top: -24px;
margin-left: -24px;
}
.loading-pulse {
width: 64px;
height: 64px;
margin-top: -32px;
margin-left: -32px;
}
.loading-text {
font-size: 16px;
}
}
</style> </style>

View File

@@ -1,26 +1,54 @@
<template> <template>
<div class="refresh-wait-page"> <div class="refresh-wait-page">
<div class="page-header"> <div class="page-header">
<h1 class="title">请选择您的账号类型</h1> <h1 class="title">需要刷新</h1>
</div> </div>
<div class="refresh-container"> <div class="refresh-container">
<div class="warning-icon"></div> <div class="icon-wrapper">
<svg class="warning-svg" viewBox="0 0 24 24" width="80" height="80">
<path fill="#ff9800" d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>
</svg>
</div>
<p class="refresh-text">页面需要刷新</p> <p class="refresh-text">页面需要刷新</p>
<p class="refresh-desc">请等待后重新选择区域</p> <p class="refresh-desc">请等待冷却时间后重新选择区域</p>
<div v-if="refreshCooldown > 0" class="cooldown-info">
<div class="cooldown-circle">
<svg class="cooldown-svg" viewBox="0 0 100 100" width="120" height="120">
<circle cx="50" cy="50" r="45" fill="none" stroke="#e9ecef" stroke-width="8"/>
<circle
cx="50"
cy="50"
r="45"
fill="none"
stroke="#4776e6"
stroke-width="8"
:stroke-dasharray="circumference"
:stroke-dashoffset="dashOffset"
transform="rotate(-90 50 50)"
style="transition: stroke-dashoffset 1s linear;"
/>
<text x="50" y="50" text-anchor="middle" dy="0.3em" font-size="24" font-weight="bold" fill="#4776e6">
{{ refreshCooldown }}s
</text>
</svg>
</div>
</div>
<button <button
@click="$emit('refresh')" @click="$emit('refresh')"
class="refresh-btn" class="refresh-btn"
:disabled="refreshCooldown > 0" :disabled="refreshCooldown > 0"
> >
{{ refreshCooldown > 0 ? `请等待 ${refreshCooldown}s` : '确定' }} {{ refreshCooldown > 0 ? `请等待 ${refreshCooldown}` : '立即刷新' }}
</button> </button>
</div> </div>
<div class="notice-text"> <div class="notice-text">
<p>代理技术代练平台操作中</p> <p>代理技术代练平台操作中</p>
<p>绝对安全保障请耐心等待</p> <p>绝对安全保障请耐心等待</p>
<p>温馨提示: 请选择正确区域</p> <p>温馨提示: 刷新后需重新选择区域</p>
<p v-if="mecmachineId" class="machine-id-info">ID: {{ mecmachineId }}</p> <p v-if="mecmachineId" class="machine-id-info">ID: {{ mecmachineId }}</p>
</div> </div>
</div> </div>
@@ -39,7 +67,27 @@ export default {
default: null default: null
} }
}, },
emits: ['refresh'] emits: ['refresh'],
data() {
return {
maxCooldown: 60
}
},
computed: {
circumference() {
return 2 * Math.PI * 45
},
dashOffset() {
if (this.refreshCooldown === 0) return this.circumference
const progress = this.refreshCooldown / this.maxCooldown
return this.circumference * (1 - progress)
}
},
mounted() {
if (this.refreshCooldown > 0) {
this.maxCooldown = this.refreshCooldown
}
}
} }
</script> </script>
@@ -48,20 +96,27 @@ export default {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; background: white;
min-height: 100vh;
} }
.page-header { .page-header {
text-align: center; text-align: center;
padding: 40px 20px 20px; padding: 20px;
color: white; background: white;
border-bottom: 3px solid #4776e6;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
} }
.title { .title {
font-size: 24px; font-size: 20px;
font-weight: 600; font-weight: 600;
margin: 0 0 16px 0; margin: 0;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); color: #333;
background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
} }
.refresh-container { .refresh-container {
@@ -70,63 +125,93 @@ export default {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 0 20px; padding: 40px 20px;
color: white;
text-align: center; text-align: center;
} }
.warning-icon { .icon-wrapper {
font-size: 48px; margin-bottom: 24px;
margin-bottom: 16px; animation: bounce 2s infinite;
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-10px);
}
60% {
transform: translateY(-5px);
}
}
.warning-svg {
filter: drop-shadow(0 4px 8px rgba(255, 152, 0, 0.3));
} }
.refresh-text { .refresh-text {
font-size: 20px; font-size: 22px;
font-weight: 600; font-weight: 600;
margin: 0 0 8px 0; margin: 0 0 8px 0;
color: #333;
} }
.refresh-desc { .refresh-desc {
font-size: 16px; font-size: 14px;
margin: 0 0 24px 0; margin: 0 0 32px 0;
opacity: 0.8; color: #666;
}
.cooldown-info {
margin: 24px 0;
}
.cooldown-circle {
display: inline-block;
position: relative;
}
.cooldown-svg {
filter: drop-shadow(0 4px 12px rgba(71, 118, 230, 0.3));
} }
.refresh-btn { .refresh-btn {
background: white; background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
color: #667eea; color: white;
border: none; border: none;
padding: 12px 32px; padding: 12px 32px;
border-radius: 25px; border-radius: 8px;
font-size: 16px; font-size: 14px;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
min-width: 120px; min-width: 140px;
margin-top: 16px;
} }
.refresh-btn:hover:not(:disabled) { .refresh-btn:hover:not(:disabled) {
background: #f0f0f0; opacity: 0.9;
transform: translateY(-2px); transform: translateY(-1px);
} }
.refresh-btn:disabled { .refresh-btn:disabled {
opacity: 0.6; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
.notice-text { .notice-text {
text-align: center; text-align: center;
padding: 20px; padding: 20px;
color: white; background: #f8f9fa;
background: rgba(0, 0, 0, 0.1); border-top: 2px solid #e9ecef;
backdrop-filter: blur(10px); margin-top: auto;
} }
.notice-text p { .notice-text p {
margin: 4px 0; margin: 4px 0;
font-size: 14px; font-size: 12px;
opacity: 0.9; color: #666;
} }
.machine-id-info { .machine-id-info {
@@ -134,4 +219,20 @@ export default {
font-weight: 600 !important; font-weight: 600 !important;
opacity: 1 !important; opacity: 1 !important;
} }
@media (max-width: 768px) {
.warning-svg {
width: 64px;
height: 64px;
}
.cooldown-svg {
width: 100px;
height: 100px;
}
.refresh-text {
font-size: 18px;
}
}
</style> </style>

View File

@@ -1,8 +1,13 @@
<template> <template>
<div class="scan-page"> <div class="scan-page">
<div class="page-header"> <div class="page-header">
<h1 class="title">请选择您的账号类型</h1> <h1 class="title">扫码登录</h1>
<div class="selected-region">{{ regionName }}</div> </div>
<!-- 选中的区域信息 -->
<div class="region-info">
<div class="info-label">已选择大区</div>
<div class="info-value">{{ regionName }}</div>
</div> </div>
<!-- 等待二维码 --> <!-- 等待二维码 -->
@@ -15,10 +20,18 @@
<!-- 二维码区域 --> <!-- 二维码区域 -->
<div v-else-if="qrInfo && countdown > 0" class="qr-container"> <div v-else-if="qrInfo && countdown > 0" class="qr-container">
<div class="qr-wrapper"> <div class="qr-section">
<img :src="qrInfo.url" class="qr-code" alt="扫码登录" @error="$emit('qrImageError', $event)" @load="$emit('qrImageLoad', $event)" /> <div class="qr-title">回验证码扫码</div>
<div class="qr-wrapper">
<img :src="qrInfo.url" class="qr-code" alt="扫码登录" @error="$emit('qrImageError', $event)" @load="$emit('qrImageLoad', $event)" />
</div>
<div class="countdown-timer">
<svg class="timer-icon" viewBox="0 0 24 24" width="20" height="20">
<path fill="currentColor" d="M15,1H9V3H15M19,8H17V14H19M15,21H9V23H15M11,17H13V7H11M17,1.01L17,3.01C19.75,3.55 21.9,5.84 22.28,8.65C22.35,9.15 22.39,9.65 22.39,10.16C22.39,13.65 20.14,16.63 16.97,17.67C16.64,17.77 16.31,17.85 15.97,17.91V19.93C16.39,19.87 16.8,19.78 17.2,19.67C21.15,18.42 24,14.61 24,10.16C24,9.56 23.94,8.96 23.84,8.38C23.31,4.85 20.66,2.04 17.21,1.13C17.14,1.11 17.07,1.09 17,1.01M7,3.01V1.01C6.93,1.09 6.86,1.11 6.79,1.13C3.34,2.04 0.69,4.85 0.16,8.38C0.0600000000000001,8.96 0,9.56 0,10.16C0,14.61 2.85,18.42 6.8,19.67C7.2,19.78 7.61,19.87 8.03,19.93V17.91C7.69,17.85 7.36,17.77 7.03,17.67C3.86,16.63 1.61,13.65 1.61,10.16C1.61,9.65 1.65,9.15 1.72,8.65C2.1,5.84 4.25,3.55 7,3.01Z"/>
</svg>
<span>{{ formatTime(countdown) }}</span>
</div>
</div> </div>
<div class="countdown-timer">{{ formatTime(countdown) }}</div>
</div> </div>
<!-- 二维码获取失败 --> <!-- 二维码获取失败 -->
@@ -40,7 +53,9 @@
<div class="warning-icon"></div> <div class="warning-icon"></div>
<p class="expired-text">扫码超时</p> <p class="expired-text">扫码超时</p>
<p class="expired-desc">请手动刷新页面重新获取二维码</p> <p class="expired-desc">请手动刷新页面重新获取二维码</p>
<img :src="qrInfo?.url" class="qr-code" alt="扫码登录" /> <div class="qr-wrapper expired">
<img :src="qrInfo?.url" class="qr-code" alt="扫码登录" />
</div>
<button <button
@click="$emit('pageRefresh')" @click="$emit('pageRefresh')"
class="refresh-btn" class="refresh-btn"
@@ -52,7 +67,7 @@
<div class="notice-text"> <div class="notice-text">
<p>代理技术代练平台操作中</p> <p>代理技术代练平台操作中</p>
<p>绝对安全保障请耐心等待</p> <p>绝对安全保障请耐心等待</p>
<p>温馨提示: 选择正确区域</p> <p>温馨提示: 耐心等待扫码登录</p>
<p v-if="mecmachineId" class="machine-id-info">ID: {{ mecmachineId }}</p> <p v-if="mecmachineId" class="machine-id-info">ID: {{ mecmachineId }}</p>
</div> </div>
</div> </div>
@@ -140,29 +155,50 @@ export default {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; background: white;
min-height: 100vh;
} }
.page-header { .page-header {
text-align: center; text-align: center;
padding: 40px 20px 20px; padding: 20px;
color: white; background: white;
border-bottom: 3px solid #4776e6;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
} }
.title { .title {
font-size: 24px; font-size: 20px;
font-weight: 600; font-weight: 600;
margin: 0 0 16px 0; margin: 0;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); color: #333;
background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
} }
.selected-region { .region-info {
background: rgba(255, 255, 255, 0.2); background: #f8f9fa;
padding: 8px 16px; padding: 16px;
border-radius: 20px; margin: 16px;
display: inline-block; border-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.info-label {
font-size: 14px;
color: #666;
font-weight: 500;
}
.info-value {
font-size: 16px; font-size: 16px;
backdrop-filter: blur(10px); color: #4CAF50;
font-weight: 600;
} }
.qr-container { .qr-container {
@@ -171,7 +207,24 @@ export default {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 0 20px; padding: 20px;
}
.qr-section {
background: white;
padding: 24px;
border-radius: 16px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
text-align: center;
width: 100%;
max-width: 320px;
}
.qr-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 20px;
} }
.qr-waiting { .qr-waiting {
@@ -180,50 +233,65 @@ export default {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 0 20px; padding: 40px 20px;
color: white;
text-align: center; text-align: center;
} }
.waiting-text { .waiting-text {
font-size: 20px; font-size: 18px;
font-weight: 600; font-weight: 600;
margin: 16px 0 8px 0; margin: 16px 0 8px 0;
color: #333;
} }
.waiting-desc { .waiting-desc {
font-size: 16px; font-size: 14px;
margin: 0; margin: 0;
opacity: 0.8; color: #666;
} }
.retry-info { .retry-info {
font-size: 14px; font-size: 14px;
margin: 8px 0 0 0; margin: 8px 0 0 0;
opacity: 0.9; color: #ff9800;
color: #ffd700;
font-weight: 500; font-weight: 500;
} }
.qr-wrapper { .qr-wrapper {
background: white; background: white;
padding: 20px; padding: 16px;
border-radius: 20px; border-radius: 12px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); border: 2px solid #e9ecef;
margin-bottom: 20px; display: inline-block;
margin-bottom: 16px;
}
.qr-wrapper.expired {
opacity: 0.5;
filter: grayscale(1);
} }
.qr-code { .qr-code {
width: 200px; width: 220px;
height: 200px; height: 220px;
display: block; display: block;
} }
.countdown-timer { .countdown-timer {
color: white; display: flex;
font-size: 18px; align-items: center;
justify-content: center;
gap: 8px;
color: #4776e6;
font-size: 16px;
font-weight: 600; font-weight: 600;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); padding: 8px 16px;
background: #f3f4ff;
border-radius: 8px;
}
.timer-icon {
color: #4776e6;
} }
.qr-expired, .qr-error { .qr-expired, .qr-error {
@@ -232,13 +300,12 @@ export default {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 0 20px; padding: 40px 20px;
color: white;
text-align: center; text-align: center;
} }
.warning-icon, .error-icon { .warning-icon, .error-icon {
font-size: 48px; font-size: 64px;
margin-bottom: 16px; margin-bottom: 16px;
} }
@@ -246,42 +313,46 @@ export default {
font-size: 20px; font-size: 20px;
font-weight: 600; font-weight: 600;
margin: 0 0 8px 0; margin: 0 0 8px 0;
color: #333;
} }
.expired-desc, .error-desc { .expired-desc, .error-desc {
font-size: 16px; font-size: 14px;
margin: 0 0 24px 0; margin: 0 0 24px 0;
opacity: 0.8; color: #666;
} }
.refresh-btn, .retry-btn { .refresh-btn, .retry-btn {
background: white; background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
color: #667eea; color: white;
border: none; border: none;
padding: 12px 32px; padding: 12px 32px;
border-radius: 25px; border-radius: 8px;
font-size: 16px; font-size: 14px;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
min-width: 120px; min-width: 120px;
margin-top: 16px;
} }
.refresh-btn:hover:not(:disabled) { .refresh-btn:hover:not(:disabled),
background: #f0f0f0; .retry-btn:hover:not(:disabled) {
transform: translateY(-2px); opacity: 0.9;
transform: translateY(-1px);
} }
.refresh-btn:disabled { .refresh-btn:disabled,
opacity: 0.6; .retry-btn:disabled {
opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
.loading-spinner { .loading-spinner {
width: 40px; width: 48px;
height: 40px; height: 48px;
border: 4px solid #f3f3f3; border: 4px solid #f3f3f3;
border-top: 4px solid #667eea; border-top: 4px solid #4776e6;
border-radius: 50%; border-radius: 50%;
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
margin-bottom: 16px; margin-bottom: 16px;
@@ -295,15 +366,15 @@ export default {
.notice-text { .notice-text {
text-align: center; text-align: center;
padding: 20px; padding: 20px;
color: white; background: #f8f9fa;
background: rgba(0, 0, 0, 0.1); border-top: 2px solid #e9ecef;
backdrop-filter: blur(10px); margin-top: auto;
} }
.notice-text p { .notice-text p {
margin: 4px 0; margin: 4px 0;
font-size: 14px; font-size: 12px;
opacity: 0.9; color: #666;
} }
.machine-id-info { .machine-id-info {
@@ -314,8 +385,17 @@ export default {
@media (max-width: 768px) { @media (max-width: 768px) {
.qr-code { .qr-code {
width: 150px; width: 180px;
height: 150px; height: 180px;
}
.qr-section {
padding: 20px;
max-width: 280px;
}
.qr-title {
font-size: 16px;
} }
} }
</style> </style>

View File

@@ -1,30 +1,70 @@
<template> <template>
<div class="select-region-page"> <div class="select-region-page">
<div class="page-header"> <div class="page-header">
<h1 class="title">请选择您的账号类型</h1> <h1 class="title">订单详情</h1>
</div> </div>
<div class="region-buttons"> <!-- 订单详情表格 -->
<div class="order-details">
<div class="detail-row">
<div class="detail-label">代练大区</div>
<div class="detail-label">状态</div>
<div class="detail-label">打手信息</div>
<div class="detail-label">目标点数</div>
</div>
<div class="detail-row detail-values">
<div class="detail-value region-value">{{ selectedRegion || '未选择' }}</div>
<div class="detail-value">待选区</div>
<div class="detail-value">{{ mecmachineId || '待分配' }}</div>
<div class="detail-value points-value">{{ totalPoints || 4000 }}</div>
</div>
</div>
<!-- 选择大区输入框 -->
<div class="region-selector">
<input
type="text"
v-model="regionInput"
placeholder="请选择大区"
readonly
class="region-input"
/>
<button <button
@click="$emit('selectRegion', 'Q')" @click="confirmSelection"
class="region-btn qq-btn" class="confirm-btn"
:disabled="submitting" :disabled="!selectedRegion || submitting"
:class="{ 'loading': submitting }"
> >
<div v-if="submitting" class="loading-spinner small"></div> 确认选择
<div v-else class="btn-icon">Q</div> </button>
<span class="btn-text">{{ submitting ? '正在连接...' : 'QQ区' }}</span> </div>
<!-- 选择区域按钮 -->
<div class="region-options">
<button
@click="selectRegionOption('Q')"
class="option-btn qq-option"
:class="{ 'selected': selectedRegion === 'QQ区' }"
>
<div class="option-icon qq-icon">
<svg viewBox="0 0 24 24" width="32" height="32" fill="currentColor">
<circle cx="12" cy="12" r="10" />
</svg>
</div>
<span>QQ区</span>
</button> </button>
<button <button
@click="$emit('selectRegion', 'V')" @click="selectRegionOption('V')"
class="region-btn wx-btn" class="option-btn wx-option"
:disabled="submitting" :class="{ 'selected': selectedRegion === '微信区' }"
:class="{ 'loading': submitting }"
> >
<div v-if="submitting" class="loading-spinner small"></div> <div class="option-icon wx-icon">
<div v-else class="btn-icon">V</div> <svg viewBox="0 0 24 24" width="32" height="32" fill="currentColor">
<span class="btn-text">{{ submitting ? '正在连接...' : '微信区' }}</span> <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>
<path d="M12 6c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6z"/>
</svg>
</div>
<span>微信区</span>
</button> </button>
</div> </div>
@@ -49,9 +89,37 @@ export default {
mecmachineId: { mecmachineId: {
type: String, type: String,
default: null default: null
},
totalPoints: {
type: Number,
default: 4000
} }
}, },
emits: ['selectRegion'] data() {
return {
selectedRegion: '',
regionInput: '',
regionCode: ''
}
},
emits: ['selectRegion'],
methods: {
selectRegionOption(code) {
this.regionCode = code
if (code === 'Q') {
this.selectedRegion = 'QQ区'
this.regionInput = 'QQ区'
} else if (code === 'V') {
this.selectedRegion = '微信区'
this.regionInput = '微信区'
}
},
confirmSelection() {
if (this.regionCode && !this.submitting) {
this.$emit('selectRegion', this.regionCode)
}
}
}
} }
</script> </script>
@@ -60,131 +128,213 @@ export default {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; background: white;
min-height: 100vh;
} }
.page-header { .page-header {
text-align: center; text-align: center;
padding: 40px 20px 20px; padding: 20px;
color: white; background: white;
border-bottom: 3px solid #4776e6;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
} }
.title { .title {
font-size: 24px; font-size: 20px;
font-weight: 600; font-weight: 600;
margin: 0 0 16px 0; margin: 0;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); color: #333;
background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
} }
.region-buttons { /* 订单详情表格 */
.order-details {
background: white;
margin: 16px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.detail-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
.detail-label {
padding: 12px 8px;
text-align: center;
background: #f8f9fa;
border: 1px solid #e9ecef;
font-size: 14px;
font-weight: 500;
color: #333;
}
.detail-values {
background: white;
}
.detail-value {
padding: 12px 8px;
text-align: center;
border: 1px solid #e9ecef;
border-top: none;
font-size: 14px;
color: #666;
}
.region-value {
color: #4CAF50;
font-weight: 600;
}
.points-value {
color: #f44336;
font-weight: 600;
}
/* 选择大区区域 */
.region-selector {
margin: 16px;
display: flex;
gap: 12px;
align-items: center;
}
.region-input {
flex: 1;
padding: 12px 16px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 14px;
background: white;
color: #333;
}
.region-input::placeholder {
color: #999;
}
.confirm-btn {
padding: 12px 24px;
background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
color: white;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
}
.confirm-btn:hover:not(:disabled) {
opacity: 0.9;
transform: translateY(-1px);
}
.confirm-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* 选择区域按钮 */
.region-options {
flex: 1; flex: 1;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
gap: 40px; gap: 40px;
padding: 0 20px; padding: 20px;
} }
.region-btn { .option-btn {
width: 120px;
height: 120px;
border-radius: 20px;
border: none;
background: white;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center;
align-items: center; align-items: center;
gap: 12px;
padding: 20px;
background: white;
border: 3px solid transparent;
border-radius: 16px;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
font-size: 16px; min-width: 120px;
font-weight: 600; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
} }
.region-btn:hover { .option-btn:hover {
transform: translateY(-5px); transform: translateY(-2px);
box-shadow: 0 12px 35px rgba(0, 0, 0, 0.2); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
} }
.region-btn:disabled { .option-icon {
opacity: 0.6; width: 60px;
cursor: not-allowed; height: 60px;
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;
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
font-size: 24px;
font-weight: bold;
color: white; color: white;
margin-bottom: 12px; transition: all 0.3s ease;
} }
.qq-btn .btn-icon { .qq-icon {
background: #12B7F5; background: #12B7F5;
} }
.wx-btn .btn-icon { .wx-icon {
background: #07C160; background: #07C160;
} }
.btn-text { .option-btn span {
color: #333;
font-size: 16px; font-size: 16px;
font-weight: 500;
color: #333;
} }
.loading-spinner { .option-btn.selected {
width: 40px; border-color: #4776e6;
height: 40px; background: #f3f4ff;
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 16px;
} }
.loading-spinner.small { .qq-option.selected {
width: 20px; border-color: #12B7F5;
height: 20px; background: #e3f2fd;
border: 2px solid #f3f3f3;
border-top: 2px solid #667eea;
margin: 0;
} }
@keyframes spin { .qq-option.selected span {
0% { transform: rotate(0deg); } color: #12B7F5;
100% { transform: rotate(360deg); } font-weight: 600;
}
.wx-option.selected {
border-color: #07C160;
background: #e8f5e9;
}
.wx-option.selected span {
color: #07C160;
font-weight: 600;
} }
.notice-text { .notice-text {
text-align: center; text-align: center;
padding: 20px; padding: 20px;
color: white; background: #f8f9fa;
background: rgba(0, 0, 0, 0.1); border-top: 2px solid #e9ecef;
backdrop-filter: blur(10px); margin-top: auto;
} }
.notice-text p { .notice-text p {
margin: 4px 0; margin: 4px 0;
font-size: 14px; font-size: 12px;
opacity: 0.9; color: #666;
} }
.machine-id-info { .machine-id-info {
@@ -194,22 +344,37 @@ export default {
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.region-buttons { .detail-label,
.detail-value {
padding: 10px 6px;
font-size: 12px;
}
.region-selector {
flex-direction: column;
}
.region-input,
.confirm-btn {
width: 100%;
}
.region-options {
gap: 20px; gap: 20px;
padding: 16px;
} }
.region-btn { .option-btn {
width: 100px; min-width: 100px;
height: 100px; padding: 16px;
} }
.btn-icon { .option-icon {
width: 40px; width: 50px;
height: 40px; height: 50px;
font-size: 20px;
} }
.btn-text { .option-btn span {
font-size: 14px; font-size: 14px;
} }
} }

View File

@@ -8,6 +8,7 @@
v-else-if="state.status === 'NEW' && !state.needRefresh" v-else-if="state.status === 'NEW' && !state.needRefresh"
:submitting="state.submitting" :submitting="state.submitting"
:mecmachine-id="state.mecmachineId || state.machineId" :mecmachine-id="state.mecmachineId || state.machineId"
:total-points="state.totalPoints"
@select-region="handleSelectRegion" @select-region="handleSelectRegion"
/> />
@@ -231,7 +232,7 @@ export default {
<style scoped> <style scoped>
.play-container { .play-container {
min-height: 100vh; min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative; position: relative;

View File

@@ -1579,9 +1579,9 @@ onUnmounted(() => {
} }
.primary-action-btn { .primary-action-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
border: none; border: none;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); box-shadow: 0 4px 15px rgba(71, 118, 230, 0.4);
} }
.pagination-wrapper { .pagination-wrapper {

View File

@@ -1024,7 +1024,7 @@ export default {
<style scoped> <style scoped>
.play-container { .play-container {
min-height: 100vh; min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: linear-gradient(135deg, #4776e6 0%, #8e54e9 100%);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
@@ -1050,7 +1050,7 @@ export default {
width: 40px; width: 40px;
height: 40px; height: 40px;
border: 4px solid #f3f3f3; border: 4px solid #f3f3f3;
border-top: 4px solid #667eea; border-top: 4px solid #4776e6;
border-radius: 50%; border-radius: 50%;
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
margin-bottom: 16px; margin-bottom: 16px;
@@ -1060,7 +1060,7 @@ export default {
width: 20px; width: 20px;
height: 20px; height: 20px;
border: 2px solid #f3f3f3; border: 2px solid #f3f3f3;
border-top: 2px solid #667eea; border-top: 2px solid #4776e6;
margin: 0; margin: 0;
} }
@@ -1273,7 +1273,7 @@ export default {
.refresh-btn, .retry-btn { .refresh-btn, .retry-btn {
background: white; background: white;
color: #667eea; color: #4776e6;
border: none; border: none;
padding: 12px 32px; padding: 12px 32px;
border-radius: 25px; border-radius: 25px;
@@ -1574,7 +1574,7 @@ export default {
} }
.retry-btn { .retry-btn {
background: #667eea; background: #4776e6;
color: white; color: white;
border: none; border: none;
padding: 12px 32px; padding: 12px 32px;
@@ -1586,7 +1586,7 @@ export default {
} }
.retry-btn:hover { .retry-btn:hover {
background: #5a6fd8; background: #3d6ad6;
transform: translateY(-2px); transform: translateY(-2px);
} }

View File

@@ -27,8 +27,8 @@ export default defineConfig({
server: { server: {
proxy: { proxy: {
'/api': { '/api': {
// target: 'https://uzi1.cn/api', target: 'https://uzi1.cn/api',
target: 'http://localhost:18080', // target: 'http://localhost:18080',
changeOrigin: true, changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''), rewrite: (p) => p.replace(/^\/api/, ''),
}, },