新增玩家头像、可查密码与全局改密/改账号开关;玩家资料页合并账号密码展示;代理直属玩家列表支持自定义上下分。 Co-authored-by: Cursor <cursoragent@cursor.com>
149 lines
3.2 KiB
Vue
149 lines
3.2 KiB
Vue
<script setup lang="ts">
|
|
import { useRouter } from 'vue-router';
|
|
import { useI18n } from 'vue-i18n';
|
|
import emptyMatchesImg from '../assets/images/empty-matches.svg';
|
|
import BannerCarousel from '../components/BannerCarousel.vue';
|
|
import { usePlayerHome } from '../composables/usePlayerHome';
|
|
import { teamFlagUrl } from '../utils/teamFlag';
|
|
|
|
const { t, locale } = useI18n();
|
|
const router = useRouter();
|
|
const { banners, hotMatches, loading } = usePlayerHome();
|
|
|
|
function goMatch(id: string) {
|
|
router.push(`/match/${id}`);
|
|
}
|
|
|
|
function formatKickoff(startTime: string) {
|
|
return new Date(startTime).toLocaleString(locale.value, {
|
|
year: 'numeric',
|
|
month: 'numeric',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
});
|
|
}
|
|
|
|
function homeFlag(match: (typeof hotMatches.value)[number]) {
|
|
return teamFlagUrl(match.homeTeamCode, match.homeTeamName);
|
|
}
|
|
|
|
function awayFlag(match: (typeof hotMatches.value)[number]) {
|
|
return teamFlagUrl(match.awayTeamCode, match.awayTeamName);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<BannerCarousel :banners="banners" />
|
|
|
|
<h2 class="section-title">{{ t('home.hot_matches') }}</h2>
|
|
<div
|
|
v-for="match in hotMatches"
|
|
:key="match.id"
|
|
class="card match-card"
|
|
@click="goMatch(match.id)"
|
|
>
|
|
<div class="match-info">
|
|
<div class="match-teams">{{ match.homeTeamName }} vs {{ match.awayTeamName }}</div>
|
|
<div class="match-time">{{ formatKickoff(match.startTime) }}</div>
|
|
</div>
|
|
<div class="match-flags" aria-hidden="true">
|
|
<img v-if="homeFlag(match)" :src="homeFlag(match)" alt="" class="flag" />
|
|
<span v-else class="flag-ph">⚽</span>
|
|
<span class="vs">VS</span>
|
|
<img v-if="awayFlag(match)" :src="awayFlag(match)" alt="" class="flag" />
|
|
<span v-else class="flag-ph">⚽</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="!loading && !hotMatches.length" class="empty">
|
|
<img :src="emptyMatchesImg" alt="" class="empty-icon" />
|
|
<p>{{ t('home.no_matches') }}</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.match-card {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
cursor: pointer;
|
|
transition: border-color 0.2s, box-shadow 0.2s;
|
|
}
|
|
|
|
.match-card:active {
|
|
border-color: var(--border-gold-soft);
|
|
}
|
|
|
|
.match-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.match-teams {
|
|
font-weight: 800;
|
|
margin-bottom: 8px;
|
|
font-size: 16px;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.match-time {
|
|
font-size: 13px;
|
|
color: var(--text-muted);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.match-flags {
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 6px 8px;
|
|
background: rgba(255, 255, 255, 0.03);
|
|
border: 1px solid #2a2a2a;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.flag {
|
|
width: 32px;
|
|
height: 22px;
|
|
object-fit: cover;
|
|
border-radius: 3px;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
|
|
}
|
|
|
|
.flag-ph {
|
|
width: 32px;
|
|
height: 22px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 14px;
|
|
opacity: 0.45;
|
|
}
|
|
|
|
.vs {
|
|
font-size: 11px;
|
|
font-weight: 900;
|
|
color: var(--primary-light);
|
|
letter-spacing: 0.04em;
|
|
}
|
|
|
|
.empty {
|
|
text-align: center;
|
|
color: var(--text-muted);
|
|
padding: 40px 20px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.empty-icon {
|
|
width: 96px;
|
|
height: 96px;
|
|
margin-bottom: 14px;
|
|
}
|
|
</style>
|