import { ref, computed } from 'vue'; import { useI18n } from 'vue-i18n'; import api from '../api'; import type { BannerItem } from '../components/BannerCarousel.vue'; import { resolveBanners } from '../constants/defaultBanner'; import { resolveAnnouncements } from '../constants/defaultAnnouncement'; export interface PlayerHomeMatch { id: string; leagueName?: string; leagueLogoUrl?: string | null; homeTeamName: string; awayTeamName: string; homeTeamCode?: string; awayTeamCode?: string; homeTeamLogoUrl?: string | null; awayTeamLogoUrl?: string | null; startTime: string; isHot?: boolean; displayOrder?: number; } interface HomePayload { banners?: BannerItem[]; announcements?: Array<{ translation?: { title?: string; body?: string } }>; ticker?: Array<{ translation?: { title?: string; body?: string } }>; notices?: Array<{ translation?: { title?: string; body?: string } }>; hotMatches?: PlayerHomeMatch[]; } const homeRaw = ref(null); const loading = ref(false); function collectAnnouncementLines(data: HomePayload | null): string[] { if (!data) return []; const source = data.announcements && data.announcements.length > 0 ? data.announcements : [...(data.ticker ?? []), ...(data.notices ?? [])]; const lines: string[] = []; for (const item of source) { const text = item.translation?.title || item.translation?.body; if (text) lines.push(text); } return lines; } /** 管理端公共内容 → 玩家端首页/跑马灯(单例,避免重复请求) */ export function usePlayerHome() { const { t } = useI18n(); async function load(force = false) { if (!force && homeRaw.value) return; loading.value = true; try { const { data } = await api.get('/player/home'); const fresh = (data.data ?? null) as HomePayload | null; if (fresh && homeRaw.value) { // 已有数据 → 原地更新,保留对象引用,避免图片重新加载 const existing = homeRaw.value; existing.banners = fresh.banners; existing.announcements = fresh.announcements; existing.ticker = fresh.ticker; existing.notices = fresh.notices; if (fresh.hotMatches && existing.hotMatches) { const freshMap = new Map(fresh.hotMatches.map((m) => [m.id, m])); for (const m of existing.hotMatches) { const f = freshMap.get(m.id); if (f) Object.assign(m, f); } // 处理新增或删除的比赛 const existingIds = new Set(existing.hotMatches.map((m) => m.id)); for (const fm of fresh.hotMatches) { if (!existingIds.has(fm.id)) existing.hotMatches.push(fm); } for (let i = existing.hotMatches.length - 1; i >= 0; i--) { if (!freshMap.has(existing.hotMatches[i].id)) { existing.hotMatches.splice(i, 1); } } } else { existing.hotMatches = fresh.hotMatches; } } else { homeRaw.value = fresh; } } catch { homeRaw.value = null; } finally { loading.value = false; } } const banners = computed(() => resolveBanners(homeRaw.value?.banners)); const announcements = computed(() => resolveAnnouncements(collectAnnouncementLines(homeRaw.value), t('home.announcement_default')), ); const hotMatches = computed(() => homeRaw.value?.hotMatches ?? []); return { homeRaw, loading, load, banners, announcements, hotMatches, }; }