fix: keep worldcup live cards above tabbar

This commit is contained in:
hajimi
2026-07-03 17:51:08 +08:00
parent 7c260b1f99
commit affb0a4002
2 changed files with 99 additions and 4 deletions
@@ -0,0 +1,48 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
PANEL = ROOT / "uniapp" / "src" / "packages_match" / "components" / "WorldCupPanel.vue"
def extract_style_block(source: str, selector: str) -> str:
start = source.find(selector)
if start < 0:
raise AssertionError(f"missing selector: {selector}")
brace = source.find("{", start)
if brace < 0:
raise AssertionError(f"missing style body for: {selector}")
depth = 0
for pos in range(brace, len(source)):
char = source[pos]
if char == "{":
depth += 1
elif char == "}":
depth -= 1
if depth == 0:
return source[brace + 1:pos]
raise AssertionError(f"unterminated style body for: {selector}")
def main() -> int:
panel = PANEL.read_text(encoding="utf-8")
scroll_style = extract_style_block(panel, ".worldcup-scroll")
required_tokens = [
"140rpx",
"constant(safe-area-inset-bottom)",
"env(safe-area-inset-bottom)",
]
for token in required_tokens:
if token not in scroll_style:
raise AssertionError(f"{PANEL} .worldcup-scroll bottom padding should reserve tabbar space: {token}")
print("worldcup live bottom padding static checks passed")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -34,11 +34,12 @@
<block v-if="activeTab === 'live'">
<view v-if="liveCards.length" class="worldcup-live-grid">
<view v-for="live in liveCards" :key="`live-${live.live_id}`" class="worldcup-live-card"
@tap="goMatchDetail(live)">
@tap="openWorldCupLiveCard(live)">
<view class="worldcup-live-card__player-wrap">
<video :id="`worldcup-live-${live.live_id}`" class="worldcup-live-card__player"
:src="live.play_url" :poster="live.home_icon || live.away_icon || ''" :autoplay="true"
:muted="true" :controls="!hideLiveCardControlsOnAndroid"
<video :id="buildWorldCupLiveVideoId(live)" class="worldcup-live-card__player"
:src="resolveWorldCupLivePlayUrl(live)"
:poster="live.home_icon || live.away_icon || ''" :autoplay="true" :muted="true"
:controls="!hideLiveCardControlsOnAndroid"
:show-play-btn="!hideLiveCardControlsOnAndroid"
:show-fullscreen-btn="!hideLiveCardControlsOnAndroid" object-fit="cover" />
</view>
@@ -425,6 +426,32 @@ const liveLineTitle = (live: WorldCupLiveCardItem) => {
return normalizeLiveText(live.title) || '直播线路'
}
const buildWorldCupLiveVideoId = (live: WorldCupLiveCardItem) => {
return `worldcup-live-${live.live_id || live.id || 0}`
}
const resolveWorldCupLivePlayUrl = (live: WorldCupLiveCardItem) => {
const urls: string[] = [
live.play_url,
live.default_play_url,
].map(normalizeLiveText).filter(Boolean)
if (Array.isArray(live.stream_options)) {
live.stream_options.forEach((option) => {
urls.push(
normalizeLiveText(option?.url),
normalizeLiveText(option?.play_url),
normalizeLiveText(option?.play_url_m3u8),
normalizeLiveText(option?.play_url_hd_m3u8),
normalizeLiveText(option?.play_url_flv),
normalizeLiveText(option?.play_url_hd_flv)
)
})
}
return urls.find((url) => /\.(m3u8|mp4|flv)(\?|$)/i.test(url)) || urls[0] || ''
}
const liveStatusText = (live: WorldCupLiveCardItem) => {
if ((live.status ?? 0) === 1) {
return normalizeLiveText(live.current_minute) || '进行中'
@@ -602,6 +629,24 @@ const goMatchDetail = (match: any) => {
uni.navigateTo({ url: `/pages/match_detail/match_detail?id=${id}` })
}
const openWorldCupLiveCard = (live: WorldCupLiveCardItem) => {
if (!resolveWorldCupLivePlayUrl(live)) {
uni.showToast({ title: '暂无直播链接', icon: 'none' })
return
}
try {
const videoContext = uni.createVideoContext(buildWorldCupLiveVideoId(live))
videoContext?.requestFullScreen?.({
direction: 90,
})
videoContext?.play?.()
} catch (error) {
console.warn('进入世界杯直播全屏失败', error)
uni.showToast({ title: '当前设备暂不支持全屏', icon: 'none' })
}
}
const getWorldCupDisplayDate = (timestamp: number) => {
return getLocalMatchDate(timestamp, WORLD_CUP_MATCH_CONTEXT)
}
@@ -888,6 +933,8 @@ onMounted(() => {
height: 0;
min-height: 0;
padding: 0 14rpx 18rpx;
padding-bottom: calc(140rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
background: #fff;
}