Files
thebet365/apps/player/src/views/MobileAnnouncementListView.vue
Mars e4ce5941a5 fix(player): 优化公告/消息详情页布局,隐藏右侧投注单
- 桌面端公告、消息详情页不再展示右侧投注单栏
- Hub 详情区移除重复返回按钮与竖排「查看全部公告」文字
- 左侧公告列表保留返回首页按钮
2026-06-30 16:38:12 +08:00

300 lines
5.9 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.
<script setup lang="ts">
import { computed, onActivated } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { formatLocalMatchDateTime } from '@thebet365/shared';
import GoldSpinner from '../components/GoldSpinner.vue';
import { usePlayerHome } from '../composables/usePlayerHome';
import { stripHtml } from '../utils/html';
const { embedded = false } = defineProps<{ embedded?: boolean }>();
const route = useRoute();
const router = useRouter();
const { t, locale } = useI18n();
const { announcementItems, loading, load } = usePlayerHome();
const items = computed(() => announcementItems.value);
function goBack() {
router.back();
}
function openDetail(id: string) {
router.push(`/announcements/${id}`);
}
function formatDate(createdAt?: string) {
if (!createdAt) return '';
return formatLocalMatchDateTime(createdAt, locale.value, { variant: 'full' });
}
function itemTitle(item: (typeof items.value)[number]) {
const title = item.translation?.title?.trim();
if (title) return title;
return stripHtml(item.translation?.body ?? '') || t('home.announcement_badge');
}
function itemPreview(item: (typeof items.value)[number]) {
const title = item.translation?.title?.trim();
const bodyText = stripHtml(item.translation?.body ?? '');
if (bodyText && bodyText !== title) return bodyText;
return '';
}
function isActive(id: string) {
return embedded && String(route.params.id ?? '') === id;
}
function onBack() {
if (embedded) {
router.push('/');
return;
}
goBack();
}
onActivated(() => {
void load(true);
});
</script>
<template>
<div class="announce-page" :class="{ 'is-embedded': embedded }">
<header class="page-header">
<button
type="button"
class="back-btn"
:aria-label="t('announcements.back')"
@click="onBack"
>
</button>
<h1>{{ t('announcements.title') }}</h1>
</header>
<div v-if="loading && !items.length" class="state">
<GoldSpinner :size="36" />
</div>
<div v-else-if="!items.length" class="empty">
<p>{{ t('announcements.empty') }}</p>
</div>
<div v-else class="list">
<button
v-for="item in items"
:key="item.id"
type="button"
class="list-row"
:class="{ 'is-active': isActive(item.id) }"
@click="openDetail(item.id)"
>
<span class="row-main">
<span class="title">{{ itemTitle(item) }}</span>
<span v-if="itemPreview(item)" class="preview">{{ itemPreview(item) }}</span>
<span v-if="item.createdAt" class="date">{{ formatDate(item.createdAt) }}</span>
</span>
<span class="chevron" aria-hidden="true"></span>
</button>
</div>
</div>
</template>
<style scoped>
.announce-page {
min-height: 100%;
padding: 0 0 24px;
}
.page-header {
display: flex;
align-items: center;
gap: 12px;
padding: 0 0 14px;
border-bottom: 1px solid var(--border);
margin-bottom: 0;
}
.back-btn {
width: 36px;
height: 36px;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--bg-card);
color: var(--primary);
font-size: 22px;
line-height: 1;
cursor: pointer;
}
.page-header h1 {
margin: 0;
font-size: 18px;
font-weight: 700;
color: var(--primary-dark, #002847);
}
.state,
.empty {
display: flex;
justify-content: center;
padding: 48px 16px;
color: var(--text-muted);
}
.list {
display: flex;
flex-direction: column;
}
.list-row {
width: 100%;
display: flex;
align-items: center;
gap: 12px;
padding: 16px 0;
border: none;
border-bottom: 1px solid var(--border);
background: transparent;
text-align: left;
color: inherit;
cursor: pointer;
}
.row-main {
flex: 1;
min-width: 0;
}
.title {
display: block;
font-size: 15px;
font-weight: 600;
color: var(--primary-dark, #002847);
line-height: 1.4;
}
.preview {
display: block;
margin-top: 6px;
font-size: 13px;
line-height: 1.45;
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.date {
display: block;
margin-top: 8px;
font-size: 12px;
color: var(--text-muted);
}
.chevron {
flex-shrink: 0;
font-size: 20px;
color: var(--text-muted);
line-height: 1;
}
/* Desktop hub sidebar */
.announce-page.is-embedded {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
padding: 16px 16px 0;
box-sizing: border-box;
}
.is-embedded .page-header {
flex-shrink: 0;
padding: 0 4px 12px;
margin-bottom: 4px;
}
.is-embedded .page-header h1 {
font-size: 16px;
font-weight: 800;
}
.is-embedded .back-btn {
width: 32px;
height: 32px;
font-size: 20px;
border-radius: 8px;
}
.is-embedded .list {
flex: 1;
min-height: 0;
overflow-y: auto;
margin: 0 -4px;
padding: 0 4px 16px;
}
.is-embedded .list-row {
align-items: flex-start;
gap: 10px;
padding: 12px 12px;
margin-bottom: 4px;
border: 1px solid transparent;
border-bottom: none;
border-radius: 8px;
transition: background 0.15s, border-color 0.15s;
}
.is-embedded .list-row:hover {
background: var(--bg-hover);
}
.is-embedded .list-row.is-active {
background: rgba(0, 102, 204, 0.08);
border-color: var(--border-active);
}
.is-embedded .list-row.is-active .title {
color: var(--primary);
}
.is-embedded .list-row.is-active .chevron {
color: var(--primary);
}
.is-embedded .title {
font-size: 13px;
font-weight: 700;
line-height: 1.45;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
white-space: normal;
}
.is-embedded .preview {
margin-top: 4px;
font-size: 12px;
line-height: 1.45;
white-space: normal;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.is-embedded .date {
margin-top: 6px;
font-size: 11px;
}
.is-embedded .chevron {
margin-top: 2px;
font-size: 16px;
align-self: center;
}
</style>