deploy: auto commit repo-root changes 2026-07-13 13:26:55
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
<template>
|
||||
<view class="match-odds-detail">
|
||||
<scroll-view scroll-x class="match-odds-detail__tabs" show-scrollbar="false">
|
||||
<view class="match-odds-detail__tabs-inner">
|
||||
<view v-for="item in platformOptions" :key="item.key" class="match-odds-detail__tab"
|
||||
:class="{ 'match-odds-detail__tab--active': sourceSite === item.key }"
|
||||
@tap="switchPlatform(item.key)">
|
||||
<text>{{ item.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view v-if="loading" class="match-odds-detail__state">
|
||||
<u-loading mode="circle" />
|
||||
<text>赔率加载中...</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="platforms.length" class="match-odds-detail__list">
|
||||
<view v-for="platform in platforms"
|
||||
:key="`${platform.platform_key}-${platform.source_match_id || platform.update_time || 0}`"
|
||||
class="match-odds-platform">
|
||||
<view class="match-odds-platform__head">
|
||||
<view class="match-odds-platform__name">
|
||||
<text>{{ platform.platform_name }}</text>
|
||||
<text v-if="platform.show_type_text" class="match-odds-platform__tag">
|
||||
{{ platform.show_type_text }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="match-odds-platform__update">{{ formatUpdateTime(platform.update_time) }}</text>
|
||||
</view>
|
||||
|
||||
<view v-for="market in platform.markets" :key="`${platform.source_match_id}-${market.label}`"
|
||||
class="match-odds-market">
|
||||
<view class="match-odds-market__title">
|
||||
<text>{{ market.label }}</text>
|
||||
<text v-if="market.line" class="match-odds-market__line">{{ market.line }}</text>
|
||||
</view>
|
||||
<view class="match-odds-market__values">
|
||||
<view v-for="item in market.values" :key="`${market.label}-${item.label}`"
|
||||
class="match-odds-value">
|
||||
<text class="match-odds-value__label">{{ item.label }}</text>
|
||||
<text class="match-odds-value__number">{{ item.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="match-odds-detail__state">
|
||||
<u-empty text="暂无赔率" mode="data" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import {
|
||||
getMatchOdds,
|
||||
type WorldCupOddsPlatform,
|
||||
type MatchOddsMatchItem,
|
||||
} from '@/api/match'
|
||||
import { getLocalMatchDate } from '@/utils/match-time'
|
||||
|
||||
type PlatformOption = { key: string; label: string }
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
homeTeam?: string
|
||||
awayTeam?: string
|
||||
matchTime?: number | string
|
||||
competitionId?: number | string
|
||||
leagueName?: string
|
||||
}>(), {
|
||||
homeTeam: '',
|
||||
awayTeam: '',
|
||||
matchTime: 0,
|
||||
competitionId: 0,
|
||||
leagueName: '',
|
||||
})
|
||||
|
||||
const defaultPlatformOptions: PlatformOption[] = [
|
||||
{ key: '', label: '全部平台' },
|
||||
{ key: 'hg', label: '滚球' },
|
||||
{ key: 'titan007', label: '球探' },
|
||||
]
|
||||
|
||||
const oddsList = ref<MatchOddsMatchItem[]>([])
|
||||
const platformOptions = ref<PlatformOption[]>([...defaultPlatformOptions])
|
||||
const sourceSite = ref('')
|
||||
const loading = ref(false)
|
||||
const loaded = ref(false)
|
||||
const requestSeq = ref(0)
|
||||
|
||||
const platforms = computed<WorldCupOddsPlatform[]>(() => {
|
||||
const result: WorldCupOddsPlatform[] = []
|
||||
const seen = new Set<string>()
|
||||
oddsList.value.forEach((match) => {
|
||||
const matchPlatforms = match.platforms || []
|
||||
matchPlatforms.forEach((platform) => {
|
||||
const key = `${platform.platform_key}-${platform.source_match_id || ''}`
|
||||
if (seen.has(key)) return
|
||||
seen.add(key)
|
||||
result.push(platform)
|
||||
})
|
||||
})
|
||||
return result
|
||||
})
|
||||
|
||||
const matchDate = computed(() => {
|
||||
const date = getLocalMatchDate(props.matchTime, {
|
||||
competition_id: props.competitionId,
|
||||
league_name: props.leagueName,
|
||||
})
|
||||
if (!date) return ''
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
})
|
||||
|
||||
const mergePlatformOptions = (options: unknown) => {
|
||||
const map = new Map<string, PlatformOption>()
|
||||
defaultPlatformOptions.forEach((item) => map.set(item.key, item))
|
||||
if (Array.isArray(options)) {
|
||||
options.forEach((item: any) => {
|
||||
const key = String(item?.key || '').trim()
|
||||
const label = String(item?.label || '').trim()
|
||||
if (key && label) map.set(key, { key, label })
|
||||
})
|
||||
}
|
||||
platformOptions.value = Array.from(map.values())
|
||||
}
|
||||
|
||||
const fetchOdds = async (force = false) => {
|
||||
if (!force && (loaded.value || loading.value)) return
|
||||
if (!props.homeTeam.trim() || !props.awayTeam.trim() || !matchDate.value) {
|
||||
oddsList.value = []
|
||||
loaded.value = true
|
||||
return
|
||||
}
|
||||
|
||||
const seq = requestSeq.value + 1
|
||||
requestSeq.value = seq
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await getMatchOdds({
|
||||
home_team: props.homeTeam.trim(),
|
||||
away_team: props.awayTeam.trim(),
|
||||
match_date: matchDate.value,
|
||||
source_site: sourceSite.value || undefined,
|
||||
limit: 120,
|
||||
})
|
||||
if (seq !== requestSeq.value) return
|
||||
oddsList.value = Array.isArray(data?.list) ? data.list : []
|
||||
mergePlatformOptions(data?.platform_options)
|
||||
loaded.value = true
|
||||
} catch (error) {
|
||||
if (seq !== requestSeq.value) return
|
||||
console.error('获取比赛赔率失败', error)
|
||||
oddsList.value = []
|
||||
} finally {
|
||||
if (seq === requestSeq.value) loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const switchPlatform = (platformKey: string) => {
|
||||
if (sourceSite.value === platformKey) return
|
||||
sourceSite.value = platformKey
|
||||
loaded.value = false
|
||||
void fetchOdds(true)
|
||||
}
|
||||
|
||||
const formatUpdateTime = (timestamp?: number) => {
|
||||
const value = Number(timestamp || 0)
|
||||
if (!value) return ''
|
||||
const date = new Date(value * 1000)
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hour = String(date.getHours()).padStart(2, '0')
|
||||
const minute = String(date.getMinutes()).padStart(2, '0')
|
||||
return `${month}-${day} ${hour}:${minute}`
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.homeTeam, props.awayTeam, props.matchTime, props.competitionId, props.leagueName],
|
||||
() => {
|
||||
loaded.value = false
|
||||
oddsList.value = []
|
||||
void fetchOdds(true)
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
void fetchOdds()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.match-odds-detail {
|
||||
min-height: 100%;
|
||||
padding: 20rpx 24rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.match-odds-detail__tabs {
|
||||
width: 100%;
|
||||
margin-bottom: 20rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.match-odds-detail__tabs-inner {
|
||||
display: inline-flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.match-odds-detail__tab {
|
||||
min-width: 112rpx;
|
||||
height: 58rpx;
|
||||
padding: 0 22rpx;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
border: 1rpx solid #e2e8f0;
|
||||
border-radius: 8rpx;
|
||||
background: #ffffff;
|
||||
color: #64748b;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.match-odds-detail__tab--active {
|
||||
border-color: #2563eb;
|
||||
background: #2563eb;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.match-odds-detail__state {
|
||||
min-height: 360rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 18rpx;
|
||||
color: #94a3b8;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.match-odds-detail__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.match-odds-platform {
|
||||
padding: 22rpx;
|
||||
border: 1rpx solid #e2e8f0;
|
||||
border-radius: 8rpx;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.match-odds-platform__head,
|
||||
.match-odds-platform__name,
|
||||
.match-odds-market__title,
|
||||
.match-odds-market__values {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.match-odds-platform__head {
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 18rpx;
|
||||
}
|
||||
|
||||
.match-odds-platform__name {
|
||||
min-width: 0;
|
||||
gap: 12rpx;
|
||||
color: #0f172a;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.match-odds-platform__tag {
|
||||
padding: 4rpx 10rpx;
|
||||
border-radius: 6rpx;
|
||||
background: #eff6ff;
|
||||
color: #2563eb;
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.match-odds-platform__update {
|
||||
flex-shrink: 0;
|
||||
color: #94a3b8;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.match-odds-market {
|
||||
padding: 16rpx 0;
|
||||
border-top: 1rpx solid #f1f5f9;
|
||||
}
|
||||
|
||||
.match-odds-market__title {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12rpx;
|
||||
color: #334155;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.match-odds-market__line {
|
||||
color: #2563eb;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.match-odds-market__values {
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.match-odds-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 86rpx;
|
||||
padding: 12rpx 8rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 6rpx;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.match-odds-value__label {
|
||||
color: #64748b;
|
||||
font-size: 21rpx;
|
||||
}
|
||||
|
||||
.match-odds-value__number {
|
||||
color: #0f172a;
|
||||
font-size: 27rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user