|
|
|
|
@@ -1,8 +1,8 @@
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, computed } from 'vue';
|
|
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
|
import api from '../../api';
|
|
|
|
|
import { useBetSlipStore } from '../../stores/betSlip';
|
|
|
|
|
import { usePlayerMatches, type ParlayMatch } from '../../composables/usePlayerMatches';
|
|
|
|
|
import { useAuthStore } from '../../stores/auth';
|
|
|
|
|
import { PARLAY_MAX_LEGS, canSelectForParlay } from '@thebet365/shared';
|
|
|
|
|
import { PARLAY_MARKET_TYPES, PARLAY_SELECTION_KEYS, PARLAY_MARKET_GROUPS } from '../../utils/parlayColumns';
|
|
|
|
|
@@ -33,28 +33,6 @@ interface Market {
|
|
|
|
|
selections: Selection[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ParlayMatch {
|
|
|
|
|
id: string;
|
|
|
|
|
leagueName: string;
|
|
|
|
|
leagueId?: string;
|
|
|
|
|
homeTeamName: string;
|
|
|
|
|
awayTeamName: string;
|
|
|
|
|
homeTeamCode?: string;
|
|
|
|
|
awayTeamCode?: string;
|
|
|
|
|
homeTeamLogoUrl?: string | null;
|
|
|
|
|
awayTeamLogoUrl?: string | null;
|
|
|
|
|
startTime: string;
|
|
|
|
|
bettingOpen?: boolean;
|
|
|
|
|
matchPhase?: MatchPhase;
|
|
|
|
|
score?: {
|
|
|
|
|
htHome: number;
|
|
|
|
|
htAway: number;
|
|
|
|
|
ftHome: number;
|
|
|
|
|
ftAway: number;
|
|
|
|
|
} | null;
|
|
|
|
|
markets: Market[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { t, locale } = useI18n();
|
|
|
|
|
const slip = useBetSlipStore();
|
|
|
|
|
const auth = useAuthStore();
|
|
|
|
|
@@ -63,8 +41,9 @@ function goLogin() {
|
|
|
|
|
auth.showLoginPrompt('/bet');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const loading = ref(true);
|
|
|
|
|
const matches = ref<ParlayMatch[]>([]);
|
|
|
|
|
const { parlayMatches, parlayLoading, loadParlay } = usePlayerMatches();
|
|
|
|
|
const matches = parlayMatches;
|
|
|
|
|
const loading = parlayLoading;
|
|
|
|
|
const timeFilter = ref<TimeFilter>('all');
|
|
|
|
|
const leagueFilter = ref('');
|
|
|
|
|
const showClosed = ref(false);
|
|
|
|
|
@@ -72,25 +51,6 @@ const collapsed = ref<Set<string>>(new Set());
|
|
|
|
|
|
|
|
|
|
const parlayMarketKeys = PARLAY_MARKET_TYPES.map((c) => c.key);
|
|
|
|
|
|
|
|
|
|
async function loadParlayMatches() {
|
|
|
|
|
const hadData = matches.value.length > 0;
|
|
|
|
|
if (!hadData) loading.value = true;
|
|
|
|
|
try {
|
|
|
|
|
const { data } = await api.get('/player/matches');
|
|
|
|
|
const fresh = (data.data ?? []).filter(
|
|
|
|
|
(m: ParlayMatch) => m.markets?.length && hasParlayMarkets(m),
|
|
|
|
|
);
|
|
|
|
|
if (!hadData) {
|
|
|
|
|
matches.value = fresh;
|
|
|
|
|
syncCollapsedAfterLoad();
|
|
|
|
|
} else {
|
|
|
|
|
mergeOddsOnly(fresh);
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
if (!hadData) loading.value = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function syncCollapsedAfterLoad() {
|
|
|
|
|
const ids = matches.value.map((m) => m.id);
|
|
|
|
|
// 只保留仍然存在的 id
|
|
|
|
|
@@ -107,32 +67,10 @@ function syncCollapsedAfterLoad() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mergeOddsOnly(fresh: ParlayMatch[]) {
|
|
|
|
|
const matchMap = new Map<string, ParlayMatch>();
|
|
|
|
|
for (const m of fresh) matchMap.set(m.id, m);
|
|
|
|
|
|
|
|
|
|
for (const match of matches.value) {
|
|
|
|
|
const freshMatch = matchMap.get(match.id);
|
|
|
|
|
if (!freshMatch) continue;
|
|
|
|
|
const marketMap = new Map<string, Market>();
|
|
|
|
|
for (const mk of freshMatch.markets) marketMap.set(mk.id, mk);
|
|
|
|
|
for (const market of match.markets) {
|
|
|
|
|
const freshMarket = marketMap.get(market.id);
|
|
|
|
|
if (!freshMarket) continue;
|
|
|
|
|
const selMap = new Map<string, Selection>();
|
|
|
|
|
for (const s of freshMarket.selections) selMap.set(s.id, s);
|
|
|
|
|
for (const sel of market.selections) {
|
|
|
|
|
const fs = selMap.get(sel.id);
|
|
|
|
|
if (fs) {
|
|
|
|
|
sel.odds = fs.odds;
|
|
|
|
|
sel.oddsVersion = fs.oddsVersion;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
useOnLocaleChange(loadParlayMatches);
|
|
|
|
|
useOnLocaleChange(async () => {
|
|
|
|
|
await loadParlay(true);
|
|
|
|
|
syncCollapsedAfterLoad();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const leagues = computed(() => {
|
|
|
|
|
const seen = new Set<string>();
|
|
|
|
|
@@ -583,7 +521,7 @@ function toggleCollapse(id: string) {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: v-bind(matchCardBg) center top / 100% auto no-repeat;
|
|
|
|
|
background: v-bind(matchCardBg) center / 100% 100% no-repeat;
|
|
|
|
|
opacity: 0.25;
|
|
|
|
|
z-index: -1;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
@@ -600,6 +538,13 @@ function toggleCollapse(id: string) {
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.match-head-teams {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toggle-icon {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
|