feat: add world cup schedule live entry

This commit is contained in:
hajimi
2026-08-03 02:11:07 +08:00
parent 895607e03f
commit a7c4e3f673
@@ -111,6 +111,11 @@
<view class="wc-match-card__center">
<text class="wc-match-card__time">{{ matchCenterText(match) }}</text>
<text class="wc-match-card__meta">{{ matchMetaText(match, index) }}</text>
<view v-if="resolveScheduleMatchLive(match)" class="wc-match-card__live-entry"
@tap.stop="watchScheduleMatchLive(match)">
<u-icon name="play-right-fill" size="19" color="#ffffff" />
<text>看直播</text>
</view>
</view>
<view class="wc-match-card__team wc-match-card__team--right">
@@ -420,6 +425,7 @@ const hideLiveCardControlsOnAndroid = ref(false)
const useLiveCardPosterMode = ref(false)
const liveCards = ref<WorldCupLiveCardItem[]>([])
const liveCardsLoaded = ref(false)
let liveCardsRequest: Promise<void> | null = null
const oddsList = ref<WorldCupOddsMatchItem[]>([])
const oddsLoading = ref(false)
const oddsLoaded = ref(false)
@@ -645,6 +651,36 @@ const liveStatusText = (live: WorldCupLiveCardItem) => {
return '未开赛'
}
const normalizeMatchName = (value: unknown) => {
return String(value || '')
.toLowerCase()
.replace(/[\s·•,,。.:;'"“”‘’()()\[\]【】_-]/g, '')
}
const resolveScheduleMatchLive = (match: any): WorldCupLiveCardItem | undefined => {
const scheduleIds = [match?.id, match?.match_id]
.map((item) => Number(item || 0))
.filter((item) => item > 0)
const matchedById = liveCards.value.find((live) => scheduleIds.includes(Number(live.match_id || live.id || 0)))
if (matchedById) return matchedById
const homeTeam = normalizeMatchName(match?.home_team)
const awayTeam = normalizeMatchName(match?.away_team)
if (!homeTeam || !awayTeam) return undefined
return liveCards.value.find((live) => {
const liveHome = normalizeMatchName(live.home_team)
const liveAway = normalizeMatchName(live.away_team)
const teamsMatch = (liveHome === homeTeam && liveAway === awayTeam)
|| (liveHome === awayTeam && liveAway === homeTeam)
if (teamsMatch) return true
const liveTitle = normalizeMatchName(live.title)
return !!liveTitle && liveTitle.includes(homeTeam) && liveTitle.includes(awayTeam)
})
}
const flattenScheduleMatches = computed(() => {
const matches: any[] = []
; (scheduleData.value.schedule_rounds || []).forEach((round: any) => {
@@ -730,13 +766,23 @@ const scheduleGroupAnchorId = (key: string) => `worldcup-schedule-group-${key}`
const fetchWorldCupLiveCards = async () => {
if (liveCardsLoaded.value) return
const data = await getWorldCupLiveCards()
liveCards.value = Array.isArray(data?.list) ? data.list : []
liveCardsLoaded.value = true
if (!liveCardsRequest) {
liveCardsRequest = getWorldCupLiveCards().then((data) => {
liveCards.value = Array.isArray(data?.list) ? data.list : []
liveCardsLoaded.value = true
})
}
try {
await liveCardsRequest
} finally {
liveCardsRequest = null
}
}
const fetchSchedule = async () => {
scheduleData.value = await getWorldCupSchedule() || { current_round_name: '', bracket_rounds: [], schedule_rounds: [] }
const [schedule] = await Promise.all([getWorldCupSchedule(), fetchWorldCupLiveCards()])
scheduleData.value = schedule || { current_round_name: '', bracket_rounds: [], schedule_rounds: [] }
if (activeTab.value === 'schedule') {
void scrollScheduleToToday()
}
@@ -918,6 +964,15 @@ const openWorldCupLiveCard = (live: WorldCupLiveCardItem) => {
}
}
const watchScheduleMatchLive = async (match: any) => {
const live = resolveScheduleMatchLive(match)
if (!live) return
activeTab.value = 'live'
await nextTick()
openWorldCupLiveCard(live)
}
const getWorldCupDisplayDate = (timestamp: number) => {
return getLocalMatchDate(timestamp, WORLD_CUP_MATCH_CONTEXT)
}
@@ -1567,6 +1622,23 @@ onMounted(() => {
gap: 4rpx;
}
.wc-match-card__live-entry {
height: 36rpx;
margin-top: 4rpx;
padding: 0 12rpx;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4rpx;
border-radius: 999rpx;
color: #ffffff;
background: linear-gradient(90deg, #176af6 0%, #0878f8 100%);
box-shadow: 0 4rpx 10rpx rgba(20, 104, 245, 0.18);
font-size: 20rpx;
font-weight: 600;
line-height: 1;
}
.wc-match-card__time {
font-size: 32rpx;
font-weight: 700;