feat(admin,api,player): 赛事分组管理、盘口独立页与多语言展示优化
- 管理端按联赛展示单场,新增赛事/单场流程与列表展开状态保持 - 盘口赔率迁至独立页面,保存按钮仅在有修改时高亮 - API 新增联赛列表与子场查询,按 locale 返回队名并修复编译 - 波胆其它选项与促销标签等 i18n 补齐,文案更易懂
This commit is contained in:
@@ -27,6 +27,7 @@ interface Market {
|
||||
period: string;
|
||||
lineValue?: string | number | null;
|
||||
allowParlay?: boolean;
|
||||
promoLabel?: string | null;
|
||||
selections: Selection[];
|
||||
}
|
||||
|
||||
@@ -40,11 +41,17 @@ interface Selection {
|
||||
|
||||
interface MatchDetail {
|
||||
id: string;
|
||||
leagueName?: string;
|
||||
leagueLogoUrl?: string | null;
|
||||
homeTeamName: string;
|
||||
awayTeamName: string;
|
||||
homeTeamCode?: string;
|
||||
awayTeamCode?: string;
|
||||
homeTeamLogoUrl?: string | null;
|
||||
awayTeamLogoUrl?: string | null;
|
||||
startTime: string;
|
||||
stage?: string | null;
|
||||
groupName?: string | null;
|
||||
markets: Market[];
|
||||
}
|
||||
|
||||
@@ -65,12 +72,17 @@ const marketsByType = computed(() => {
|
||||
});
|
||||
|
||||
const homeFlag = computed(() =>
|
||||
teamFlagUrl(match.value?.homeTeamCode, match.value?.homeTeamName),
|
||||
teamFlagUrl(match.value?.homeTeamCode, match.value?.homeTeamName, match.value?.homeTeamLogoUrl),
|
||||
);
|
||||
const awayFlag = computed(() =>
|
||||
teamFlagUrl(match.value?.awayTeamCode, match.value?.awayTeamName),
|
||||
teamFlagUrl(match.value?.awayTeamCode, match.value?.awayTeamName, match.value?.awayTeamLogoUrl),
|
||||
);
|
||||
|
||||
function marketPromoLabel(marketType: string) {
|
||||
const m = marketsByType.value.get(marketType);
|
||||
return m?.promoLabel?.trim() || '';
|
||||
}
|
||||
|
||||
const kickoff = computed(() => {
|
||||
if (!match.value) return '';
|
||||
return new Date(match.value.startTime).toLocaleString(locale.value, {
|
||||
@@ -123,7 +135,7 @@ const csConfirmLines = computed((): CsConfirmLine[] => {
|
||||
return market.selections
|
||||
.filter((s) => (correctScoreStakes.value[s.id] ?? 0) > 0)
|
||||
.map((s) => {
|
||||
const parsed = parseScoreCode(s.selectionCode);
|
||||
const parsed = parseScoreCode(s.selectionCode, t);
|
||||
return {
|
||||
scoreDisplay: parsed?.display ?? s.selectionName,
|
||||
odds: s.odds,
|
||||
@@ -262,6 +274,16 @@ function hasSlipPickForMarket(marketType: string) {
|
||||
<div v-if="loading" class="state">{{ t('bet.loading') }}</div>
|
||||
|
||||
<template v-else-if="match">
|
||||
<section v-if="match.leagueName" class="league-banner">
|
||||
<span class="league-title">*{{ match.leagueName }}</span>
|
||||
<img
|
||||
v-if="match.leagueLogoUrl"
|
||||
:src="match.leagueLogoUrl"
|
||||
alt=""
|
||||
class="league-logo"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section class="match-hero">
|
||||
<p class="kickoff">{{ kickoff }}</p>
|
||||
<div class="match-line">
|
||||
@@ -298,6 +320,7 @@ function hasSlipPickForMarket(marketType: string) {
|
||||
>
|
||||
<MarketTypeTile
|
||||
:label="marketLabel(marketType)"
|
||||
:promo-label="marketPromoLabel(marketType)"
|
||||
:has-market="marketsByType.has(marketType)"
|
||||
:expanded="isExpanded(marketType)"
|
||||
@toggle="toggleMarket(marketType)"
|
||||
@@ -382,6 +405,30 @@ function hasSlipPickForMarket(marketType: string) {
|
||||
padding: 2px 12px 10px;
|
||||
}
|
||||
|
||||
.league-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 6px 12px 2px;
|
||||
}
|
||||
|
||||
.league-banner .league-title {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
color: var(--primary-light);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.league-logo {
|
||||
flex-shrink: 0;
|
||||
height: 40px;
|
||||
width: auto;
|
||||
max-width: 44px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.kickoff {
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
|
||||
Reference in New Issue
Block a user