feat(player): ???????????????
??????????????????????????????????????????????????????????? Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import MatchBetCard from './MatchBetCard.vue';
|
||||
import saishiImg from '../assets/images/saishi.webp';
|
||||
import cardBg from '../assets/images/card-bg.webp';
|
||||
@@ -40,6 +41,7 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{ toggle: []; bet: [id: string] }>();
|
||||
const { t } = useI18n();
|
||||
|
||||
const totalCount = computed(() => props.matches.length);
|
||||
const liveCount = computed(() => props.matches.filter(m => m.matchPhase === 'closed_pending').length);
|
||||
@@ -55,9 +57,9 @@ const openCount = computed(() => props.matches.filter(m => m.matchPhase === 'ope
|
||||
<span class="league-info">
|
||||
<span class="league-title">*{{ leagueName }}</span>
|
||||
<span class="league-stats">
|
||||
<span class="stat-item">{{ totalCount }}场</span>
|
||||
<span v-if="liveCount > 0" class="stat-item stat-live">{{ liveCount }}进行中</span>
|
||||
<span v-if="openCount > 0" class="stat-item stat-open">{{ openCount }}待开赛</span>
|
||||
<span class="stat-item">{{ totalCount }} {{ t('bet.match_unit') }}</span>
|
||||
<span v-if="liveCount > 0" class="stat-item stat-live">{{ liveCount }} {{ t('bet.status_live') }}</span>
|
||||
<span v-if="openCount > 0" class="stat-item stat-open">{{ openCount }} {{ t('bet.status_open') }}</span>
|
||||
</span>
|
||||
</span>
|
||||
<img
|
||||
|
||||
@@ -78,7 +78,7 @@ const liveScoreText = computed(() => {
|
||||
:class="{ 'match-card--phase': phase !== 'open' }"
|
||||
@click="emit('bet', match.id)"
|
||||
>
|
||||
<span v-if="phase === 'open'" class="status-tag status-tag--open">待开赛</span>
|
||||
<span v-if="phase === 'open'" class="status-tag status-tag--open">{{ t('bet.status_open') }}</span>
|
||||
<span v-else-if="phase === 'settled'" class="status-tag status-tag--settled">{{ phaseLabel }}</span>
|
||||
<span v-else class="status-tag status-tag--pending">{{ phaseLabel }}</span>
|
||||
|
||||
|
||||
@@ -266,6 +266,13 @@ export default {
|
||||
tab_outright: 'Outright',
|
||||
tab_today: 'Today',
|
||||
tab_early: 'Early',
|
||||
filter_status_all: 'All',
|
||||
filter_status_open: 'Open',
|
||||
filter_status_settled: 'Settled',
|
||||
filter_leagues_all: 'All Leagues',
|
||||
status_open: 'Open',
|
||||
status_live: 'Live',
|
||||
match_unit: 'matches',
|
||||
show_open_only: 'Open only',
|
||||
show_all_matches: 'Show all',
|
||||
today: 'Today',
|
||||
|
||||
@@ -272,6 +272,13 @@ export default {
|
||||
tab_outright: 'Juara',
|
||||
tab_today: 'Hari Ini',
|
||||
tab_early: 'Awal',
|
||||
filter_status_all: 'Semua',
|
||||
filter_status_open: 'Buka',
|
||||
filter_status_settled: 'Selesai',
|
||||
filter_leagues_all: 'Semua Liga',
|
||||
status_open: 'Buka',
|
||||
status_live: 'Sedang berlangsung',
|
||||
match_unit: 'perlawanan',
|
||||
show_open_only: 'Buka sahaja',
|
||||
show_all_matches: 'Tunjuk semua',
|
||||
today: 'Hari Ini',
|
||||
|
||||
@@ -266,6 +266,13 @@ export default {
|
||||
tab_outright: '优胜冠军',
|
||||
tab_today: '今日',
|
||||
tab_early: '早盘',
|
||||
filter_status_all: '全部',
|
||||
filter_status_open: '待开赛',
|
||||
filter_status_settled: '已结束',
|
||||
filter_leagues_all: '全部联赛',
|
||||
status_open: '待开赛',
|
||||
status_live: '进行中',
|
||||
match_unit: '场',
|
||||
show_open_only: '仅显示待开赛',
|
||||
show_all_matches: '显示全部',
|
||||
today: '今日',
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
} from '@thebet365/shared';
|
||||
|
||||
type MainTab = 'matches' | 'outright';
|
||||
type TimeTab = 'today' | 'early';
|
||||
|
||||
interface Match {
|
||||
id: string;
|
||||
@@ -62,17 +61,16 @@ const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
|
||||
const mainTab = ref<MainTab>('matches');
|
||||
const timeTab = ref<TimeTab>('today');
|
||||
const showAll = ref(false);
|
||||
const filterState = ref({
|
||||
time: 'all' as 'all' | 'today' | 'early',
|
||||
status: 'open' as 'all' | 'open' | 'settled',
|
||||
leagueIds: [] as string[]
|
||||
});
|
||||
const outrightActivated = ref(false);
|
||||
const filterNow = ref(new Date());
|
||||
const { summaryMatches, summaryLoading, loadSummary } = usePlayerMatches();
|
||||
const matches = summaryMatches;
|
||||
const loading = summaryLoading;
|
||||
const expandedLeagues = ref<Record<TimeTab, Set<string>>>({
|
||||
today: new Set(),
|
||||
early: new Set(),
|
||||
});
|
||||
|
||||
async function loadMatches() {
|
||||
filterNow.value = new Date();
|
||||
@@ -90,18 +88,81 @@ const pullIndicatorStyle = () => ({
|
||||
opacity: Math.min(pullDistance.value / 48, 1),
|
||||
});
|
||||
|
||||
function filterMatchesForTab(tab: TimeTab) {
|
||||
function normalizeLeagueName(name: string): string {
|
||||
return name
|
||||
.replace(/\.unit$/i, '')
|
||||
.replace(/[-_]+/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
const filteredMatches = computed(() => {
|
||||
if (mainTab.value !== 'matches') return [];
|
||||
const now = filterNow.value;
|
||||
return matches.value.filter((m) => {
|
||||
const timeMatch =
|
||||
tab === 'today'
|
||||
? isInTodayMatchWindow(m.startTime, now)
|
||||
: isAfterTodayMatchWindow(m.startTime, now);
|
||||
let timeMatch = true;
|
||||
if (filterState.value.time === 'today') {
|
||||
timeMatch = isInTodayMatchWindow(m.startTime, now);
|
||||
} else if (filterState.value.time === 'early') {
|
||||
timeMatch = isAfterTodayMatchWindow(m.startTime, now);
|
||||
}
|
||||
if (!timeMatch) return false;
|
||||
if (!showAll.value && m.matchPhase !== 'open' && m.matchPhase !== undefined) return false;
|
||||
|
||||
if (filterState.value.status === 'open' && m.matchPhase !== 'open' && m.matchPhase !== undefined) return false;
|
||||
if (filterState.value.status === 'settled' && m.matchPhase !== 'settled') return false;
|
||||
|
||||
if (filterState.value.leagueIds.length > 0) {
|
||||
const id = m.leagueId ?? m.leagueName;
|
||||
if (!filterState.value.leagueIds.includes(id)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
const availableLeagues = computed(() => {
|
||||
const map = new Map<string, { id: string, name: string }>();
|
||||
for (const m of matches.value) {
|
||||
let timeMatch = true;
|
||||
if (filterState.value.time === 'today') {
|
||||
timeMatch = isInTodayMatchWindow(m.startTime, filterNow.value);
|
||||
} else if (filterState.value.time === 'early') {
|
||||
timeMatch = isAfterTodayMatchWindow(m.startTime, filterNow.value);
|
||||
}
|
||||
if (!timeMatch) continue;
|
||||
|
||||
const id = m.leagueId ?? m.leagueName;
|
||||
if (!map.has(id)) {
|
||||
map.set(id, { id, name: normalizeLeagueName(m.leagueName) });
|
||||
}
|
||||
}
|
||||
return [...map.values()].sort((a, b) => a.name.localeCompare(b.name));
|
||||
});
|
||||
|
||||
watch(() => filterState.value.time, () => {
|
||||
filterState.value.leagueIds = [];
|
||||
});
|
||||
|
||||
const dropdownOpen = ref(false);
|
||||
|
||||
const selectedLeagueName = computed(() => {
|
||||
if (filterState.value.leagueIds.length === 0) return '';
|
||||
const firstId = filterState.value.leagueIds[0];
|
||||
const lg = availableLeagues.value.find(l => l.id === firstId);
|
||||
return lg ? lg.name : '';
|
||||
});
|
||||
|
||||
function selectLeague(id: string) {
|
||||
if (id === '') {
|
||||
filterState.value.leagueIds = [];
|
||||
} else {
|
||||
filterState.value.leagueIds = [id];
|
||||
}
|
||||
dropdownOpen.value = false;
|
||||
}
|
||||
|
||||
function toggleStatusOpen() {
|
||||
filterState.value.status = filterState.value.status === 'open' ? 'all' : 'open';
|
||||
}
|
||||
|
||||
function buildLeagueGroups(source: Match[]): LeagueGroup[] {
|
||||
@@ -111,7 +172,7 @@ function buildLeagueGroups(source: Match[]): LeagueGroup[] {
|
||||
if (!map.has(id)) {
|
||||
map.set(id, {
|
||||
leagueId: id,
|
||||
leagueName: m.leagueName,
|
||||
leagueName: normalizeLeagueName(m.leagueName),
|
||||
leagueLogoUrl: m.leagueLogoUrl ?? null,
|
||||
matches: [],
|
||||
});
|
||||
@@ -133,32 +194,28 @@ function buildLeagueGroups(source: Match[]): LeagueGroup[] {
|
||||
);
|
||||
}
|
||||
|
||||
const todayLeagueGroups = computed(() => buildLeagueGroups(filterMatchesForTab('today')));
|
||||
const earlyLeagueGroups = computed(() => buildLeagueGroups(filterMatchesForTab('early')));
|
||||
const leagueGroups = computed(() => buildLeagueGroups(filteredMatches.value));
|
||||
|
||||
function syncExpandedLeagues(groups: LeagueGroup[], tab: TimeTab) {
|
||||
const current = expandedLeagues.value[tab];
|
||||
const ids = new Set(current);
|
||||
const expandedLeagues = ref(new Set<string>());
|
||||
|
||||
watch(leagueGroups, (groups) => {
|
||||
const ids = new Set(expandedLeagues.value);
|
||||
for (const id of [...ids]) {
|
||||
if (!groups.some((g) => g.leagueId === id)) ids.delete(id);
|
||||
}
|
||||
if (groups.length > 0 && ids.size === 0) {
|
||||
ids.add(groups[0].leagueId);
|
||||
}
|
||||
if (ids.size !== current.size || [...ids].some((id) => !current.has(id))) {
|
||||
expandedLeagues.value = { ...expandedLeagues.value, [tab]: ids };
|
||||
if (ids.size !== expandedLeagues.value.size || [...ids].some((id) => !expandedLeagues.value.has(id))) {
|
||||
expandedLeagues.value = ids;
|
||||
}
|
||||
}
|
||||
|
||||
watch(todayLeagueGroups, (groups) => syncExpandedLeagues(groups, 'today'));
|
||||
watch(earlyLeagueGroups, (groups) => syncExpandedLeagues(groups, 'early'));
|
||||
});
|
||||
|
||||
function toggleLeague(leagueId: string) {
|
||||
const tab = timeTab.value;
|
||||
const next = new Set(expandedLeagues.value[tab]);
|
||||
const next = new Set(expandedLeagues.value);
|
||||
if (next.has(leagueId)) next.delete(leagueId);
|
||||
else next.add(leagueId);
|
||||
expandedLeagues.value = { ...expandedLeagues.value, [tab]: next };
|
||||
expandedLeagues.value = next;
|
||||
}
|
||||
|
||||
function selectMainTab(tab: MainTab) {
|
||||
@@ -168,7 +225,7 @@ function selectMainTab(tab: MainTab) {
|
||||
|
||||
onActivated(() => {
|
||||
filterNow.value = new Date();
|
||||
timeTab.value = 'today';
|
||||
filterState.value.time = 'all';
|
||||
void loadSummary(true, true);
|
||||
});
|
||||
|
||||
@@ -208,72 +265,90 @@ function goMatch(id: string) {
|
||||
</div>
|
||||
|
||||
<div :class="['tab-panel', { 'tab-panel--hidden': mainTab !== 'matches' }]">
|
||||
<div class="time-tabs">
|
||||
<button
|
||||
type="button"
|
||||
class="time-tab"
|
||||
:class="{ active: timeTab === 'today', 'tab-gold-active': timeTab === 'today' }"
|
||||
@click="timeTab = 'today'"
|
||||
>
|
||||
{{ t('bet.tab_today') }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="time-tab"
|
||||
:class="{ active: timeTab === 'early', 'tab-gold-active': timeTab === 'early' }"
|
||||
@click="timeTab = 'early'"
|
||||
>
|
||||
{{ t('bet.tab_early') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="filters-bar">
|
||||
<div class="phase-filter">
|
||||
<div class="league-dropdown">
|
||||
<button type="button" class="dropdown-trigger" @click="dropdownOpen = !dropdownOpen">
|
||||
<span class="dropdown-text">{{ selectedLeagueName || t('bet.filter_leagues_all') }}</span>
|
||||
<span class="arrow-icon" :class="{ open: dropdownOpen }">▼</span>
|
||||
</button>
|
||||
<div v-if="dropdownOpen" class="dropdown-backdrop" @click="dropdownOpen = false"></div>
|
||||
<div v-if="dropdownOpen" class="dropdown-menu">
|
||||
<div
|
||||
class="dropdown-item"
|
||||
:class="{ active: filterState.leagueIds.length === 0 }"
|
||||
@click="selectLeague('')"
|
||||
>
|
||||
{{ t('bet.filter_leagues_all') }}
|
||||
</div>
|
||||
<div
|
||||
v-for="lg in availableLeagues"
|
||||
:key="lg.id"
|
||||
class="dropdown-item"
|
||||
:class="{ active: filterState.leagueIds.includes(lg.id) }"
|
||||
@click="selectLeague(lg.id)"
|
||||
>
|
||||
{{ lg.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="phase-filter">
|
||||
<button
|
||||
type="button"
|
||||
class="phase-toggle"
|
||||
:class="{ 'phase-toggle--active': !showAll }"
|
||||
:aria-pressed="!showAll"
|
||||
@click="showAll = !showAll"
|
||||
>
|
||||
<span class="phase-toggle-dot" />
|
||||
{{ t('bet.show_open_only') }}
|
||||
</button>
|
||||
<div class="right-filters">
|
||||
<div class="time-tabs">
|
||||
<button
|
||||
type="button"
|
||||
class="time-tab"
|
||||
:class="{ active: filterState.time === 'all' }"
|
||||
@click="filterState.time = 'all'"
|
||||
>
|
||||
{{ t('bet.filter_status_all') }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="time-tab"
|
||||
:class="{ active: filterState.time === 'today' }"
|
||||
@click="filterState.time = 'today'"
|
||||
>
|
||||
{{ t('bet.tab_today') }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="time-tab"
|
||||
:class="{ active: filterState.time === 'early' }"
|
||||
@click="filterState.time = 'early'"
|
||||
>
|
||||
{{ t('bet.tab_early') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="status-toggle-btn"
|
||||
:class="{ active: filterState.status === 'open' }"
|
||||
@click="toggleStatusOpen"
|
||||
>
|
||||
{{ t('bet.filter_status_open') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="state">
|
||||
<GoldSpinner :size="36" />
|
||||
</div>
|
||||
<template v-else>
|
||||
<div v-show="timeTab === 'today'">
|
||||
<div v-if="todayLeagueGroups.length" class="league-list">
|
||||
<div>
|
||||
<div v-if="leagueGroups.length" class="league-list">
|
||||
<LeagueAccordionItem
|
||||
v-for="group in todayLeagueGroups"
|
||||
:key="`today-${group.leagueId}`"
|
||||
v-for="group in leagueGroups"
|
||||
:key="group.leagueId"
|
||||
:league-id="group.leagueId"
|
||||
:league-name="group.leagueName"
|
||||
:league-logo-url="group.leagueLogoUrl"
|
||||
:matches="group.matches"
|
||||
:expanded="expandedLeagues.today.has(group.leagueId)"
|
||||
@toggle="toggleLeague(group.leagueId)"
|
||||
@bet="goMatch"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="empty">
|
||||
<img :src="emptyMatchesImg" alt="" class="empty-icon" />
|
||||
<p>{{ t('bet.no_matches') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="timeTab === 'early'">
|
||||
<div v-if="earlyLeagueGroups.length" class="league-list">
|
||||
<LeagueAccordionItem
|
||||
v-for="group in earlyLeagueGroups"
|
||||
:key="`early-${group.leagueId}`"
|
||||
:league-id="group.leagueId"
|
||||
:league-name="group.leagueName"
|
||||
:league-logo-url="group.leagueLogoUrl"
|
||||
:matches="group.matches"
|
||||
:expanded="expandedLeagues.early.has(group.leagueId)"
|
||||
:expanded="expandedLeagues.has(group.leagueId)"
|
||||
@toggle="toggleLeague(group.leagueId)"
|
||||
@bet="goMatch"
|
||||
/>
|
||||
@@ -292,6 +367,8 @@ function goMatch(id: string) {
|
||||
:activated="mainTab === 'outright'"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -307,7 +384,7 @@ function goMatch(id: string) {
|
||||
.bet-page {
|
||||
margin: 0 -16px;
|
||||
padding-bottom: 8px;
|
||||
background: #E8EDF2;
|
||||
background: var(--bg-body);
|
||||
}
|
||||
|
||||
/* Tab panel toggle: avoid display:none to prevent browser releasing image resources */
|
||||
@@ -341,22 +418,22 @@ function goMatch(id: string) {
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 10px 8px;
|
||||
border-radius: 8px;
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #CBD5E1;
|
||||
color: #6B7280;
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: var(--shadow);
|
||||
transition: background 0.2s, border-color 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
.main-tab.active {
|
||||
font-weight: 700;
|
||||
background: #EBF3FB;
|
||||
border-color: #003D6B;
|
||||
color: #003D6B;
|
||||
background: rgba(244, 162, 97, 0.1);
|
||||
border-color: var(--primary);
|
||||
color: var(--primary-light);
|
||||
}
|
||||
|
||||
.tab-icon {
|
||||
@@ -364,68 +441,174 @@ function goMatch(id: string) {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.time-tabs {
|
||||
.filters-bar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 0 12px 14px;
|
||||
}
|
||||
|
||||
.time-tab {
|
||||
flex: 1;
|
||||
padding: 9px 16px;
|
||||
border-radius: 999px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #003D6B;
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #CBD5E1;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: background 0.2s, border-color 0.2s, color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.time-tab.active {
|
||||
background: #003D6B !important;
|
||||
border-color: #003D6B !important;
|
||||
color: #FFFFFF !important;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 2px 8px rgba(0, 61, 107, 0.25);
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 12px;
|
||||
padding: 0 16px 12px;
|
||||
}
|
||||
|
||||
.phase-filter {
|
||||
padding: 0 12px 10px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.phase-toggle {
|
||||
.right-filters {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.time-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 2px 3px;
|
||||
height: 26px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.time-tab {
|
||||
padding: 0 10px;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--text-muted);
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.time-tab.active {
|
||||
color: var(--primary-light);
|
||||
background: rgba(244, 162, 97, 0.15);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.right-filters .divider {
|
||||
width: 1px;
|
||||
height: 12px;
|
||||
background: var(--border);
|
||||
}
|
||||
|
||||
.status-toggle-btn {
|
||||
padding: 0 10px;
|
||||
height: 26px;
|
||||
box-sizing: border-box;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: var(--text-muted);
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.status-toggle-btn.active {
|
||||
color: var(--primary-light);
|
||||
background: rgba(244, 162, 97, 0.15);
|
||||
border-color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* League Dropdown Selector */
|
||||
.league-dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dropdown-trigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 14px;
|
||||
border-radius: 999px;
|
||||
padding: 0 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #6B7280;
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #CBD5E1;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
height: 24px;
|
||||
box-sizing: border-box;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.phase-toggle--active {
|
||||
color: #003D6B;
|
||||
border-color: #003D6B;
|
||||
background: rgba(0, 61, 107, 0.06);
|
||||
.dropdown-text {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.phase-toggle-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #CBD5E1;
|
||||
transition: background 0.2s ease;
|
||||
.dropdown-trigger .arrow-icon {
|
||||
font-size: 8px;
|
||||
color: var(--text-muted);
|
||||
transition: transform 0.2s ease;
|
||||
display: inline-block;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
.phase-toggle--active .phase-toggle-dot {
|
||||
background: #003D6B;
|
||||
.dropdown-trigger .arrow-icon.open {
|
||||
transform: scale(0.8) rotate(180deg);
|
||||
}
|
||||
|
||||
.dropdown-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 98;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 4px);
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
min-width: 170px;
|
||||
max-width: 240px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
background: #FFFFFF;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.35);
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
padding: 8px 14px;
|
||||
font-size: 13px;
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.dropdown-item:hover,
|
||||
.dropdown-item:active {
|
||||
background: var(--bg-body);
|
||||
}
|
||||
|
||||
.dropdown-item.active {
|
||||
color: var(--primary-light);
|
||||
background: rgba(244, 162, 97, 0.1);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.league-list {
|
||||
@@ -436,7 +619,7 @@ function goMatch(id: string) {
|
||||
.placeholder,
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: #6B7280;
|
||||
color: var(--text-muted);
|
||||
padding: 48px 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -68,12 +68,6 @@ function formatKickoff(startTime: string) {
|
||||
</span>
|
||||
<span class="qe-label">{{ t('wallet.view_all') }}</span>
|
||||
</button>
|
||||
<button type="button" class="qe-item" @click="router.push('/bet')">
|
||||
<span class="qe-icon qe-icon--promo">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" width="22" height="22"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
|
||||
</span>
|
||||
<span class="qe-label">{{ t('home.hot_matches') || '热门赛事' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h2 class="section-title">{{ t('home.hot_matches') }}</h2>
|
||||
@@ -147,7 +141,7 @@ function formatKickoff(startTime: string) {
|
||||
|
||||
.quick-entries {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
margin-bottom: 14px;
|
||||
padding: 0 2px;
|
||||
@@ -196,11 +190,6 @@ function formatKickoff(startTime: string) {
|
||||
color: #E8870A;
|
||||
}
|
||||
|
||||
.qe-icon--promo {
|
||||
background: rgba(220, 38, 38, 0.08);
|
||||
color: #DC2626;
|
||||
}
|
||||
|
||||
.qe-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
|
||||
@@ -142,21 +142,21 @@ const balanceAmountClass = computed(() => {
|
||||
</div>
|
||||
|
||||
<div class="bank-card-balance">
|
||||
<span class="bank-card-label">当前余额</span>
|
||||
<span class="bank-card-label">{{ t('wallet.balance') }}</span>
|
||||
<p class="bank-card-number balance-amount" :class="balanceAmountClass">{{ balanceDisplay }}</p>
|
||||
</div>
|
||||
|
||||
<div class="bank-card-footer">
|
||||
<div class="bank-card-field">
|
||||
<span class="bank-card-label">持卡人</span>
|
||||
<span class="bank-card-label">{{ t('wallet.card_holder') }}</span>
|
||||
<span class="bank-card-holder">{{ profileRaw?.username }}</span>
|
||||
</div>
|
||||
<div class="bank-card-field bank-card-field--center">
|
||||
<span class="bank-card-label">累计返水</span>
|
||||
<span class="bank-card-label">{{ t('cashback.total_received') }}</span>
|
||||
<span class="bank-card-stat">{{ formatMoney(cashbackTotal, locale) }}</span>
|
||||
</div>
|
||||
<div class="bank-card-field bank-card-field--right">
|
||||
<span class="bank-card-label">未结算</span>
|
||||
<span class="bank-card-label">{{ t('wallet.unsettled') }}</span>
|
||||
<span class="bank-card-stat">{{ formatMoney(profileRaw?.wallet?.frozenBalance, locale) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -528,11 +528,12 @@ const balanceAmountClass = computed(() => {
|
||||
}
|
||||
|
||||
.qa-item {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 14px 4px 12px;
|
||||
padding: 12px 4px 10px;
|
||||
border-radius: 10px;
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #CBD5E1;
|
||||
@@ -577,15 +578,19 @@ const balanceAmountClass = computed(() => {
|
||||
}
|
||||
|
||||
.qa-label {
|
||||
font-size: 11px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
color: #1A1A2E;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
white-space: normal;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-height: 2.4em;
|
||||
max-width: 100%;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.settings-group {
|
||||
|
||||
Reference in New Issue
Block a user