feat(admin,player,api): 优胜冠军通用管理与界面精简

管理端新增冠军盘列表/编辑、展开懒加载与 ECharts 修复;各列表页去掉重复标题。玩家端支持多赛事冠军盘、分批加载与语言切换刷新。API 扩展 outright CRUD 与列表性能优化。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-04 09:17:01 +08:00
parent 9b63d67e7c
commit 27580b2479
39 changed files with 2250 additions and 578 deletions

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue';
import { ref, computed, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import api from '../api';
@@ -8,6 +8,7 @@ import LeagueAccordionItem from '../components/LeagueAccordionItem.vue';
import OutrightPanel from '../components/outright/OutrightPanel.vue';
import ParlayPanel from '../components/parlay/ParlayPanel.vue';
import emptyMatchesImg from '../assets/images/empty-matches.svg';
import { useOnLocaleChange } from '../composables/useOnLocaleChange';
type MainTab = 'matches' | 'outright' | 'parlay';
type TimeTab = 'today' | 'early';
@@ -39,7 +40,7 @@ const matches = ref<Match[]>([]);
const loading = ref(true);
const expandedLeagues = ref<Set<string>>(new Set());
onMounted(async () => {
async function loadMatches() {
loading.value = true;
try {
const { data } = await api.get('/player/matches');
@@ -47,7 +48,9 @@ onMounted(async () => {
} finally {
loading.value = false;
}
});
}
useOnLocaleChange(loadMatches);
function dayStart(d: Date) {
const x = new Date(d);
@@ -186,7 +189,9 @@ function goMatch(id: string) {
</div>
</template>
<OutrightPanel v-else-if="mainTab === 'outright'" />
<div v-else-if="mainTab === 'outright'" class="outright-tab">
<OutrightPanel />
</div>
<ParlayPanel v-else-if="mainTab === 'parlay'" />
</div>
@@ -290,4 +295,8 @@ function goMatch(id: string) {
.parlay-tab.tab-gold-active {
flex: 1.15;
}
.outright-tab {
min-height: 0;
}
</style>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import { ref, computed } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
@@ -8,6 +8,7 @@ import api from '../api';
import emptyMatchesImg from '../assets/images/empty-matches.svg';
import BannerCarousel from '../components/BannerCarousel.vue';
import { resolveBanners } from '../constants/defaultBanner';
import { useOnLocaleChange } from '../composables/useOnLocaleChange';
const router = useRouter();
const home = ref<{
@@ -38,10 +39,12 @@ interface Match {
const displayBanners = computed(() => resolveBanners(home.value?.banners));
onMounted(async () => {
async function loadHome() {
const { data } = await api.get('/player/home');
home.value = data.data;
});
}
useOnLocaleChange(loadHome);
function goMatch(id: string) {
router.push(`/match/${id}`);

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { ref, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import api from '../api';
@@ -14,6 +14,7 @@ import CorrectScoreConfirmModal, {
type CsConfirmLine,
} from '../components/match-detail/CorrectScoreConfirmModal.vue';
import { isCorrectScoreMarket, parseScoreCode } from '../utils/correctScoreLayout';
import { useOnLocaleChange } from '../composables/useOnLocaleChange';
const route = useRoute();
const router = useRouter();
@@ -197,7 +198,7 @@ async function loadMatch() {
}
}
onMounted(loadMatch);
useOnLocaleChange(loadMatch);
function isSelected(id: string) {
return slip.items.some((i) => i.selectionId === id);

View File

@@ -1,16 +1,15 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import api from '../api';
import BetHistoryCard, { type BetHistoryItem } from '../components/BetHistoryCard.vue';
import { useOnLocaleChange } from '../composables/useOnLocaleChange';
const { t } = useI18n();
const bets = ref<{ items: BetHistoryItem[]; total: number }>({ items: [], total: 0 });
const loading = ref(true);
onMounted(load);
async function load() {
loading.value = true;
try {
@@ -20,6 +19,8 @@ async function load() {
loading.value = false;
}
}
useOnLocaleChange(load);
</script>
<template>