Files
thebet365/apps/player/src/components/outright/OutrightOptionCard.vue
Mars d2c0b575b7 feat(player): 统一移动端主题样式并优化多页面视觉一致性
将玩家端组件与核心页面全面切换为统一主题变量,优化卡片、按钮、弹窗、列表和盘口相关模块的视觉表现,并补充多语言文案以匹配新的交互与布局风格。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 15:55:28 +08:00

130 lines
2.6 KiB
Vue

<script setup lang="ts">
import { computed, ref, watch, onMounted, onUnmounted } from 'vue';
import { teamFlagUrl } from '../../utils/teamFlag';
const props = defineProps<{
teamCode: string;
teamName: string;
odds: string;
logoUrl?: string | null;
}>();
const emit = defineEmits<{ pick: [] }>();
const flag = computed(
() => props.logoUrl?.trim() || teamFlagUrl(props.teamCode, props.teamName),
);
const flagFailed = ref(false);
const imgVisible = ref(false);
const cardRef = ref<HTMLElement | null>(null);
let observer: IntersectionObserver | null = null;
function onFlagError() {
flagFailed.value = true;
}
watch(
() => [props.teamCode, props.teamName, props.logoUrl] as const,
() => {
flagFailed.value = false;
},
);
function formatOdds(odds: string) {
const n = parseFloat(odds);
return Number.isFinite(n) ? n.toFixed(2) : odds;
}
onMounted(() => {
if (!cardRef.value) return;
observer = new IntersectionObserver(
(entries) => {
const entry = entries[0];
if (entry.isIntersecting) {
imgVisible.value = true;
observer?.disconnect();
}
},
{ rootMargin: '120px' }
);
observer.observe(cardRef.value);
});
onUnmounted(() => {
observer?.disconnect();
});
</script>
<template>
<button ref="cardRef" type="button" class="option-card" @click="emit('pick')">
<img
v-if="imgVisible && flag && !flagFailed"
:src="flag"
alt=""
class="flag"
@error="onFlagError"
/>
<span v-else class="flag-placeholder" aria-hidden="true"></span>
<span class="name">{{ teamName }}</span>
<span class="odds">[ {{ formatOdds(odds) }} ]</span>
</button>
</template>
<style scoped>
.option-card {
position: relative;
width: 100%;
min-width: 0;
padding: 8px 4px 6px;
border-radius: 8px;
background: var(--bg-card);
border: 1px solid var(--border);
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
text-align: center;
overflow: hidden;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.option-card:active {
border-color: var(--primary);
box-shadow: 0 0 0 1px rgba(244, 162, 97, 0.12);
}
.flag {
width: 28px;
height: 19px;
object-fit: cover;
border-radius: 2px;
}
.flag-placeholder {
width: 28px;
height: 19px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
opacity: 0.55;
}
.name {
font-size: 11px;
font-weight: 800;
color: var(--text);
line-height: 1.2;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.odds {
font-size: 11px;
font-weight: 800;
color: var(--warning);
}
</style>