Files
login_task_web/src/components/play/GamePage.vue

426 lines
9.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="game-page">
<!-- 顶部标签页 -->
<div class="tab-header">
<div class="tab-item active">代练大区</div>
<div class="tab-item status-tab">状态</div>
<div class="tab-item target-tab">目标点数</div>
</div>
<div class="tab-header">
<div class="tab-item active">{{ regionDesc || (region === 'Q' ? 'QQ区' : region === 'V' ? '微信区' : region) }}</div>
<div class="tab-item status-tab">{{ displayStatus }}</div>
<div class="tab-item target-tab" v-if="progressDisplayFormat === '1'">{{ completedPoints || 0 }}/{{ totalPoints || 0 }}</div>
<div class="tab-item target-tab" v-else>{{ remainingPoints }}</div>
</div>
<div class="tab-content">
<!-- 状态提示 -->
<div class="status-message" :class="statusMessageClass">
{{ statusMessage }}
</div>
<!-- 游戏截图展示区域 -->
<div class="image-gallery">
<div class="image-item" v-if="machineId">
<img :src="`https://uzi1.cn/image/${machineId}/首次主页.png?t=${timestamp}`" alt="首次主页" class="game-image" />
<div class="image-label">首次主页</div>
</div>
<div class="image-item" v-if="machineId">
<img :src="`https://uzi1.cn/image/${machineId}/首次赏金.png?t=${timestamp}`" alt="首次赏金" class="game-image" style="transform: rotate(-90deg);" />
<div class="image-label">首次赏金</div>
</div>
<div class="image-item" v-if="machineId">
<img :src="`https://uzi1.cn/image/${machineId}/中途赏金.png?t=${timestamp}`" alt="中途赏金" class="game-image" />
<div class="image-label">中途赏金</div>
</div>
<div class="image-item" v-if="machineId">
<img :src="`https://uzi1.cn/image/${machineId}/结束赏金.png?t=${timestamp}`" alt="结束赏金" class="game-image" />
<div class="image-label">结束赏金</div>
</div>
</div>
<!-- 底部状态显示 -->
<div class="bottom-status">
ID: {{ machineId || 'N/A' }}
</div>
</div>
<!-- 公告弹窗仅在游戏页面显示 -->
<el-dialog
v-model="announcementVisible"
title="代理商公告"
:width="'90%'"
:before-close="handleAnnouncementClose"
class="announcement-dialog"
>
<div v-if="announcement" class="announcement-content">
<h3 class="announcement-title">{{ announcement.title }}</h3>
<div class="announcement-text">{{ announcement.content }}</div>
</div>
<div v-else class="announcement-empty">
暂无公告内容
</div>
<template #footer>
<div class="announcement-footer">
<el-checkbox v-model="dontShowToday" class="dont-show-checkbox">
今日不再弹出
</el-checkbox>
<el-button type="primary" @click="handleAnnouncementClose">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script>
import { onMounted, ref, watch } from 'vue'
import { getAnnouncementByCode } from '@/api/announcement'
export default {
name: 'GamePage',
props: {
region: {
type: String,
required: true
},
regionDesc: {
type: String,
default: null
},
machineId: {
type: String,
default: null
},
displayStatus: {
type: String,
required: true
},
completedPoints: {
type: Number,
default: 0
},
totalPoints: {
type: Number,
default: 0
},
statusMessage: {
type: String,
required: true
},
statusMessageClass: {
type: String,
default: ''
},
assets: {
type: Object,
default: null
},
currentPoints: {
type: Number,
default: 0
},
codeNo: {
type: String,
default: null
},
progressDisplayFormat: {
type: String,
default: '1'
}
},
data() {
return {
timestamp: Date.now()
}
},
setup(props) {
const announcementVisible = ref(false)
const announcement = ref(null)
const dontShowToday = ref(false)
const getStorageKey = (codeNo, announcementId) => {
return `announcement_dismissed_${codeNo}_${announcementId}`
}
const getTodayKey = (codeNo) => {
const today = new Date().toDateString()
return `announcement_dont_show_today_${codeNo}_${today}`
}
const isDismissedPermanently = (codeNo, announcementId) => {
const key = getStorageKey(codeNo, announcementId)
return localStorage.getItem(key) === 'true'
}
const isDontShowToday = (codeNo) => {
const key = getTodayKey(codeNo)
return localStorage.getItem(key) === 'true'
}
const markDismissedPermanently = (codeNo, announcementId) => {
const key = getStorageKey(codeNo, announcementId)
localStorage.setItem(key, 'true')
}
const markDontShowToday = (codeNo) => {
const key = getTodayKey(codeNo)
localStorage.setItem(key, 'true')
}
const checkAnnouncement = async (codeNo) => {
if (!codeNo) return
if (isDontShowToday(codeNo)) return
try {
const response = await getAnnouncementByCode(codeNo)
if (response.data && response.data.success && response.data.data && response.data.data.length > 0) {
const announcementData = response.data.data[0]
if (announcementData.enabled) {
const dismissed = isDismissedPermanently(codeNo, announcementData.id)
if (!dismissed) {
announcement.value = announcementData
announcementVisible.value = true
}
}
}
} catch (error) {
console.error('获取公告失败:', error)
}
}
const handleAnnouncementClose = () => {
const codeNo = props.codeNo
const announcementData = announcement.value
if (dontShowToday.value && codeNo) {
markDontShowToday(codeNo)
}
if (announcementData && codeNo) {
markDismissedPermanently(codeNo, announcementData.id)
}
announcementVisible.value = false
dontShowToday.value = false
}
onMounted(() => {
// 仅在进入 GamePage 时检查公告
setTimeout(() => checkAnnouncement(props.codeNo), 1000)
})
watch(
() => props.codeNo,
(newCode) => {
if (newCode) {
checkAnnouncement(newCode)
}
}
)
return {
announcementVisible,
announcement,
dontShowToday,
handleAnnouncementClose
}
},
computed: {
remainingPoints() {
const total = this.totalPoints || 0
const completed = this.completedPoints || 0
const diff = total - completed
return diff > 0 ? diff : 0
}
}
}
</script>
<style scoped>
.game-page {
flex: 1;
background: white;
display: flex;
flex-direction: column;
min-height: 100vh;
}
.tab-header {
display: flex;
background: #f8f9fa;
border-bottom: 1px solid #e9ecef;
}
.tab-item {
flex: 1;
padding: 12px 16px;
text-align: center;
font-size: 14px;
font-weight: 500;
color: #666;
border-bottom: 2px solid transparent;
cursor: pointer;
transition: all 0.3s ease;
}
.tab-item.active {
background: #4CAF50;
color: white;
border-bottom-color: #4CAF50;
}
.tab-item.status-tab {
background: #2196F3;
color: white;
}
.tab-item.target-tab {
color: #f44336;
font-weight: 600;
}
.tab-content {
flex: 1;
padding: 16px;
display: flex;
flex-direction: column;
}
.status-message {
background: #fff3cd;
color: #856404;
padding: 12px 16px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
font-size: 14px;
border-left: 4px solid #f44336;
}
.status-message-completed {
background: #d4edda !important;
color: #155724 !important;
border-left: 4px solid #28a745 !important;
}
.image-gallery {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
margin-bottom: 20px;
}
.image-item {
display: flex;
flex-direction: column;
align-items: center;
background: white;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 12px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.image-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.game-image {
width: 100%;
max-width: 150px;
height: auto;
border-radius: 6px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
margin-bottom: 8px;
}
.image-label {
font-size: 12px;
color: #333;
font-weight: 500;
text-align: center;
}
.bottom-status {
text-align: center;
padding: 20px;
background: #f8f9fa;
border-top: 1px solid #e9ecef;
margin: 0 -16px -16px -16px;
font-size: 18px;
font-weight: 600;
color: #333;
}
.announcement-dialog {
:deep(.el-dialog__body) {
padding: 16px 20px;
}
}
.announcement-content {
max-height: 60vh;
overflow-y: auto;
}
.announcement-title {
color: #333;
font-size: 18px;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e9ecef;
}
.announcement-text {
color: #666;
font-size: 14px;
line-height: 1.6;
white-space: pre-wrap;
}
.announcement-empty {
text-align: center;
color: #999;
padding: 20px 0;
font-size: 14px;
}
.announcement-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.dont-show-checkbox {
font-size: 14px;
color: #666;
}
@media (max-width: 768px) {
.tab-item {
padding: 10px 8px;
font-size: 12px;
}
.status-message {
font-size: 13px;
padding: 10px 12px;
}
.image-gallery {
grid-template-columns: repeat(2, 1fr);
gap: 8px;
margin-bottom: 16px;
}
.image-item {
padding: 8px;
}
.game-image {
max-width: 120px;
}
.image-label {
font-size: 11px;
}
}
</style>