From 1737ee517c8d2ec1364b0a7bb7f8485b0662e425 Mon Sep 17 00:00:00 2001 From: hajimi Date: Mon, 3 Aug 2026 03:42:37 +0800 Subject: [PATCH] feat: move live line switcher into player --- .../src/pages/match_detail/match_detail.vue | 320 +++++++----------- 1 file changed, 115 insertions(+), 205 deletions(-) diff --git a/uniapp/src/pages/match_detail/match_detail.vue b/uniapp/src/pages/match_detail/match_detail.vue index 24216e4..17549eb 100644 --- a/uniapp/src/pages/match_detail/match_detail.vue +++ b/uniapp/src/pages/match_detail/match_detail.vue @@ -13,61 +13,53 @@ - - - - 当前线路暂无可用直播放流 - 可切换其他线路,或打开当前线路源站继续观看 - - 打开当前线路源站 - - - - - - {{ resolvedLiveList.length > 1 ? `直播间 · ${resolvedLiveList.length} 路` : '直播间' }} - {{ activeLiveLineTitle }} - {{ activeLiveMetaText }} - - - 源站 - - - - - {{ activeLiveError }} - - - - - {{ index + 1 }} - {{ item.anchor_name || item.displayTitle }} + + + - + + 当前线路暂无可用直播放流 + 可切换其他线路,或打开当前线路源站继续观看 + + 打开当前线路源站 + + + + + 切换线路 + {{ showLiveLineMenu ? '▴' : '▾' }} + + + + {{ index + 1 }} + {{ item.displayTitle }} + + + + + + {{ activeLiveError }} @@ -482,7 +474,6 @@ import { AI_DAILY_FREE_TEXT, getAiFreeRemain } from '@/config/ai' type MatchDetailLiveLine = MatchLiveLineItem & { lineKey: string displayTitle: string - metaText: string playUrl: string sourceUrl: string hasDirectPlay: boolean @@ -507,6 +498,7 @@ const liveTextList = ref([]) const activeLiveLineKey = ref('') const activeLiveError = ref('') const livePlayerLoading = ref(false) +const showLiveLineMenu = ref(false) let liveTextTimer: ReturnType | null = null const matchDetailTitle = computed(() => { @@ -553,16 +545,6 @@ const awaySubs = computed(() => lineupList.value.filter((p: any) => p.team_side const normalizeLiveText = (value: unknown) => String(value || '').trim() -const getLiveFetchStatusText = (value: unknown) => { - const text = normalizeLiveText(value) - if (!text) return '' - const normalized = text.toLowerCase() - if (normalized === 'pending' || normalized === '0') return '待抓取' - if (normalized === 'success' || normalized === '1') return '已抓取' - if (normalized === 'failed' || normalized === 'error' || normalized === '2') return '抓取失败' - return text -} - const getLiveLineTitle = (item: MatchLiveLineItem) => { return ( normalizeLiveText(item?.title) || @@ -574,28 +556,6 @@ const getLiveLineTitle = (item: MatchLiveLineItem) => { ) } -const formatLiveLineUpdate = (value: unknown) => { - const text = normalizeLiveText(value) - if (!text) return '' - const hhmmss = text.match(/(\d{2}:\d{2}:\d{2})$/) - if (hhmmss) return hhmmss[1] - const hhmm = text.match(/(\d{2}:\d{2})$/) - if (hhmm) return hhmm[1] - if (/^\d{10,13}$/.test(text)) { - const raw = text.length === 13 ? Number(text) : Number(text) * 1000 - return formatLiveTime(raw / 1000) - } - return text -} - -const getLiveLineMeta = (item: MatchLiveLineItem) => { - const parts = [ - getLiveFetchStatusText(item?.fetch_status), - formatLiveLineUpdate(item?.last_fetch_at || item?.update_time), - ].filter(Boolean) - return parts.length ? parts.join(' · ') : '点击选择直播线路' -} - const getLiveLineKey = (item: MatchLiveLineItem, index: number) => { return normalizeLiveText(item?.id) || `${getLiveLineTitle(item)}-${index}` } @@ -656,7 +616,6 @@ const resolvedLiveList = computed(() => { ...item, lineKey: getLiveLineKey(item, index), displayTitle: getLiveLineTitle(item), - metaText: getLiveLineMeta(item), playUrl, sourceUrl: resolveLiveSourceUrl(item), hasDirectPlay: !!playUrl, @@ -674,8 +633,6 @@ const activeLiveLine = computed(() => { const activeLivePlayUrl = computed(() => activeLiveLine.value?.playUrl || '') const activeLiveSourceUrl = computed(() => activeLiveLine.value?.sourceUrl || thirdPartyLiveUrl.value) -const activeLiveLineTitle = computed(() => activeLiveLine.value?.displayTitle || '直播线路') -const activeLiveMetaText = computed(() => activeLiveLine.value?.metaText || '') const activeLivePlayerKey = computed(() => `${activeLiveLine.value?.lineKey || 'live'}-${activeLivePlayUrl.value}`) const triggerLiveAutoPlay = () => { @@ -699,6 +656,7 @@ watch( activeLiveLineKey.value = '' activeLiveError.value = '' livePlayerLoading.value = false + showLiveLineMenu.value = false return } if (list.some((item) => item.lineKey === activeLiveLineKey.value)) return @@ -852,11 +810,16 @@ const selectLiveLine = (item: MatchDetailLiveLine) => { activeLiveLineKey.value = item.lineKey activeLiveError.value = '' livePlayerLoading.value = !!item.playUrl + showLiveLineMenu.value = false if (item.playUrl) { triggerLiveAutoPlay() } } +const toggleLiveLineMenu = () => { + showLiveLineMenu.value = !showLiveLineMenu.value +} + const openActiveLiveSource = () => { navigateToLiveWebview(activeLiveSourceUrl.value) } @@ -1830,41 +1793,6 @@ onUnload(() => { gap: 18rpx; } - &__player-header { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 16rpx; - } - - &__player-title-wrap { - min-width: 0; - display: flex; - flex-direction: column; - gap: 6rpx; - flex: 1; - } - - &__player-title { - font-size: 30rpx; - font-weight: 700; - color: #0f172a; - } - - &__player-meta { - font-size: 22rpx; - color: #64748b; - line-height: 1.5; - } - - &__player-actions { - display: flex; - align-items: center; - gap: 12rpx; - flex-wrap: wrap; - flex-shrink: 0; - } - &__player-wrap { position: relative; border-radius: 24rpx; @@ -2085,6 +2013,11 @@ onUnload(() => { box-shadow: none; } + &__stage { + position: relative; + min-height: 430rpx; + } + &__player-wrap { border-radius: 0; height: 430rpx; @@ -2143,109 +2076,86 @@ onUnload(() => { line-height: 1; } - &__player-header { - align-items: center; - padding: 20rpx 28rpx 22rpx; - background: #fff; + &__line-switcher { + position: absolute; + bottom: 22rpx; + left: 24rpx; + z-index: 4; } - &__player-title-wrap { - gap: 4rpx; - } - - &__eyebrow { - font-size: 20rpx; - color: #8c95a6; - line-height: 1.2; - } - - &__player-title { - font-size: 25rpx; - font-weight: 700; - color: #141a27; - line-height: 1.35; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - &__player-meta { - font-size: 19rpx; - line-height: 1.3; - color: #8c95a6; - } - - &__source-action { + &__line-trigger { display: flex; align-items: center; - gap: 4rpx; - padding: 10rpx 14rpx; - border: 1rpx solid #d7e2f7; - border-radius: 999rpx; - color: #2376f5; - font-size: 20rpx; + gap: 12rpx; + min-height: 62rpx; + padding: 0 18rpx; + border-radius: 12rpx; + background: rgba(3, 9, 17, 0.74); + color: #fff; + font-size: 24rpx; font-weight: 600; + } + + &__line-trigger-arrow { + font-size: 20rpx; line-height: 1; - flex-shrink: 0; } - &__room-tabs { - width: 100%; - white-space: nowrap; - background: #fff; - border-top: 1rpx solid #eff2f7; - } - - &__room-tabs-inner { - display: inline-flex; - min-width: 100%; - gap: 14rpx; - padding: 16rpx 28rpx; + &__line-menu { + position: absolute; + bottom: 78rpx; + left: 0; + width: 360rpx; + max-height: 292rpx; + padding: 8rpx; + overflow-y: auto; box-sizing: border-box; + border: 1rpx solid rgba(255, 255, 255, 0.22); + border-radius: 12rpx; + background: rgba(3, 9, 17, 0.9); + box-shadow: 0 12rpx 32rpx rgba(0, 0, 0, 0.3); } - &__room-tab { - display: inline-flex; + &__line-option { + display: flex; align-items: center; - max-width: 240rpx; - gap: 8rpx; - padding: 12rpx 16rpx; - border: 1rpx solid #e5eaf2; - border-radius: 999rpx; - background: #f7f9fc; - color: #657086; - flex: 0 0 auto; + gap: 12rpx; + min-height: 60rpx; + padding: 0 12rpx; + border-radius: 8rpx; + color: rgba(255, 255, 255, 0.82); &--active { - border-color: #1468f5; - background: #1468f5; + background: rgba(20, 104, 245, 0.28); color: #fff; } } - &__room-index { - min-width: 28rpx; - height: 28rpx; - border-radius: 50%; - background: rgba(20, 104, 245, 0.12); - color: #1468f5; - font-size: 18rpx; - font-weight: 700; - line-height: 28rpx; - text-align: center; - } - - &__room-tab--active &__room-index { - background: rgba(255, 255, 255, 0.2); - color: #fff; - } - - &__room-name { - overflow: hidden; + &__line-option-index { + width: 32rpx; + color: rgba(255, 255, 255, 0.6); font-size: 20rpx; + font-weight: 700; + text-align: center; + flex-shrink: 0; + } + + &__line-option-name { + flex: 1; + min-width: 0; + overflow: hidden; + font-size: 22rpx; font-weight: 600; line-height: 1.3; text-overflow: ellipsis; + white-space: nowrap; + } + + &__line-option-check { + color: #7db3ff; + font-size: 24rpx; + font-weight: 700; + flex-shrink: 0; } &__player-empty {