管理端新增冠军盘列表/编辑、展开懒加载与 ECharts 修复;各列表页去掉重复标题。玩家端支持多赛事冠军盘、分批加载与语言切换刷新。API 扩展 outright CRUD 与列表性能优化。 Co-authored-by: Cursor <cursoragent@cursor.com>
79 lines
2.3 KiB
Vue
79 lines
2.3 KiB
Vue
<script setup lang="ts">
|
|
import { ref, computed } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
const { t } = useI18n();
|
|
import api from '../api';
|
|
import emptyMatchesImg from '../assets/images/empty-matches.svg';
|
|
import BannerCarousel from '../components/BannerCarousel.vue';
|
|
import { resolveBanners } from '../constants/defaultBanner';
|
|
import { useOnLocaleChange } from '../composables/useOnLocaleChange';
|
|
|
|
const router = useRouter();
|
|
const home = ref<{
|
|
banners: Banner[];
|
|
hotMatches: Match[];
|
|
ticker: ContentItem[];
|
|
notices: ContentItem[];
|
|
} | null>(null);
|
|
|
|
interface ContentItem {
|
|
translation?: { title?: string; body?: string; imageUrl?: string };
|
|
}
|
|
|
|
interface Banner {
|
|
id?: string;
|
|
linkType?: string | null;
|
|
linkTarget?: string | null;
|
|
translation?: { title?: string; body?: string; imageUrl?: string };
|
|
}
|
|
|
|
interface Match {
|
|
id: string;
|
|
homeTeamName: string;
|
|
awayTeamName: string;
|
|
startTime: string;
|
|
isHot: boolean;
|
|
}
|
|
|
|
const displayBanners = computed(() => resolveBanners(home.value?.banners));
|
|
|
|
async function loadHome() {
|
|
const { data } = await api.get('/player/home');
|
|
home.value = data.data;
|
|
}
|
|
|
|
useOnLocaleChange(loadHome);
|
|
|
|
function goMatch(id: string) {
|
|
router.push(`/match/${id}`);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<BannerCarousel :banners="displayBanners" />
|
|
|
|
<h2 class="section-title">{{ t('home.hot_matches') }}</h2>
|
|
<div v-for="match in home?.hotMatches || []" :key="match.id" class="card match-card" @click="goMatch(match.id)">
|
|
<div class="match-teams">{{ match.homeTeamName }} vs {{ match.awayTeamName }}</div>
|
|
<div class="match-time">{{ new Date(match.startTime).toLocaleString() }}</div>
|
|
</div>
|
|
|
|
<div v-if="home && !home.hotMatches?.length" class="empty">
|
|
<img :src="emptyMatchesImg" alt="" class="empty-icon" />
|
|
<p>{{ t('home.no_matches') }}</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.match-card { cursor: pointer; transition: border-color 0.2s, box-shadow 0.2s; }
|
|
.match-card:active { border-color: var(--border-gold-soft); }
|
|
.match-teams { font-weight: 800; margin-bottom: 8px; font-size: 16px; }
|
|
.match-time { font-size: 13px; color: var(--text-muted); font-weight: 500; }
|
|
.empty { text-align: center; color: var(--text-muted); padding: 40px 20px; font-weight: 600; }
|
|
.empty-icon { width: 96px; height: 96px; margin-bottom: 14px; }
|
|
</style>
|