新增公告弹窗功能,优化公告检查逻辑,支持今日不再弹出设置,提升用户体验
This commit is contained in:
@@ -45,10 +45,37 @@
|
|||||||
ID: {{ machineId || 'N/A' }}
|
ID: {{ machineId || 'N/A' }}
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { onMounted, ref, watch } from 'vue'
|
||||||
|
import { getAnnouncementByCode } from '@/api/announcement'
|
||||||
export default {
|
export default {
|
||||||
name: 'GamePage',
|
name: 'GamePage',
|
||||||
props: {
|
props: {
|
||||||
@@ -106,6 +133,94 @@ export default {
|
|||||||
timestamp: Date.now()
|
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: {
|
computed: {
|
||||||
remainingPoints() {
|
remainingPoints() {
|
||||||
const total = this.totalPoints || 0
|
const total = this.totalPoints || 0
|
||||||
@@ -235,6 +350,50 @@ export default {
|
|||||||
color: #333;
|
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) {
|
@media (max-width: 768px) {
|
||||||
.tab-item {
|
.tab-item {
|
||||||
padding: 10px 8px;
|
padding: 10px 8px;
|
||||||
|
|||||||
@@ -67,30 +67,6 @@
|
|||||||
@retry="handleRetry"
|
@retry="handleRetry"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 公告弹窗 -->
|
|
||||||
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -100,7 +76,6 @@ import { useRoute } from 'vue-router'
|
|||||||
import { usePlayState } from '@/composables/usePlayState'
|
import { usePlayState } from '@/composables/usePlayState'
|
||||||
import { useTimers } from '@/composables/useTimers'
|
import { useTimers } from '@/composables/useTimers'
|
||||||
import { useQrCode } from '@/composables/useQrCode'
|
import { useQrCode } from '@/composables/useQrCode'
|
||||||
import { getAnnouncementByCode } from '@/api/announcement'
|
|
||||||
|
|
||||||
import LoadingOverlay from '@/components/play/LoadingOverlay.vue'
|
import LoadingOverlay from '@/components/play/LoadingOverlay.vue'
|
||||||
import SelectRegion from '@/components/play/SelectRegion.vue'
|
import SelectRegion from '@/components/play/SelectRegion.vue'
|
||||||
@@ -122,119 +97,6 @@ export default {
|
|||||||
setup() {
|
setup() {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
// 公告相关状态
|
|
||||||
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
|
|
||||||
|
|
||||||
console.log('检查公告:', codeNo)
|
|
||||||
|
|
||||||
// 检查今日是否设置为不弹出
|
|
||||||
if (isDontShowToday(codeNo)) {
|
|
||||||
console.log('今日已设置不弹出')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await getAnnouncementByCode(codeNo)
|
|
||||||
console.log('公告接口响应:', response)
|
|
||||||
|
|
||||||
// 修复:应该检查 response.data.data 而不是 response.data
|
|
||||||
if (response.data && response.data.success && response.data.data && response.data.data.length > 0) {
|
|
||||||
const announcementData = response.data.data[0]
|
|
||||||
console.log('公告数据:', announcementData)
|
|
||||||
|
|
||||||
if (announcementData.enabled) {
|
|
||||||
// 检查是否已永久关闭此公告
|
|
||||||
const isDismissed = isDismissedPermanently(codeNo, announcementData.id)
|
|
||||||
console.log('是否已永久关闭:', isDismissed)
|
|
||||||
|
|
||||||
if (!isDismissed) {
|
|
||||||
announcement.value = announcementData
|
|
||||||
announcementVisible.value = true
|
|
||||||
console.log('显示公告弹窗')
|
|
||||||
} else {
|
|
||||||
console.log('公告已被永久关闭')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('公告未启用')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('无公告数据')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取公告失败:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理公告关闭
|
|
||||||
const handleAnnouncementClose = () => {
|
|
||||||
const codeNo = state.code
|
|
||||||
const announcementData = announcement.value
|
|
||||||
|
|
||||||
if (dontShowToday.value && codeNo) {
|
|
||||||
markDontShowToday(codeNo)
|
|
||||||
console.log('已设置今日不弹出')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (announcementData && codeNo) {
|
|
||||||
markDismissedPermanently(codeNo, announcementData.id)
|
|
||||||
console.log('已永久关闭公告:', announcementData.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
announcementVisible.value = false
|
|
||||||
dontShowToday.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 临时调试:清除本地存储
|
|
||||||
const clearAnnouncementStorage = () => {
|
|
||||||
Object.keys(localStorage).forEach(key => {
|
|
||||||
if (key.startsWith('announcement_')) {
|
|
||||||
localStorage.removeItem(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
console.log('已清除所有公告本地存储')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开发环境下添加到window对象方便调试
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
window.clearAnnouncementStorage = clearAnnouncementStorage
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state,
|
state,
|
||||||
initializePage,
|
initializePage,
|
||||||
@@ -285,11 +147,6 @@ export default {
|
|||||||
|
|
||||||
state.code = code
|
state.code = code
|
||||||
initializePage()
|
initializePage()
|
||||||
|
|
||||||
// 检查公告
|
|
||||||
setTimeout(() => {
|
|
||||||
checkAnnouncement(code)
|
|
||||||
}, 1000) // 延迟1秒执行,确保页面已经加载完成
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -355,12 +212,7 @@ export default {
|
|||||||
getGameStatus,
|
getGameStatus,
|
||||||
getStatusClass,
|
getStatusClass,
|
||||||
getErrorTitle,
|
getErrorTitle,
|
||||||
getErrorMessage,
|
getErrorMessage
|
||||||
// 公告相关
|
|
||||||
announcementVisible,
|
|
||||||
announcement,
|
|
||||||
dontShowToday,
|
|
||||||
handleAnnouncementClose
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,77 +244,9 @@ export default {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.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) {
|
@media (max-width: 768px) {
|
||||||
.completed-text {
|
.completed-text {
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.announcement-dialog {
|
|
||||||
:deep(.el-dialog) {
|
|
||||||
width: 95vw !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.announcement-title {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.announcement-text {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.announcement-footer {
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dont-show-checkbox {
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user