From a3e8958406e8a661f02f55a283e6320286791e69 Mon Sep 17 00:00:00 2001 From: Mars <3361409208a@gmail.com> Date: Tue, 23 Jun 2026 13:53:20 +0800 Subject: [PATCH] =?UTF-8?q?sync(theme-4):=20=E4=BB=8E=20main=20=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E4=BC=98=E8=83=9C=E8=B5=9B=E7=BB=93=E7=AE=97=E6=80=81?= =?UTF-8?q?=E4=B8=8E=E9=92=B1=E5=8C=85=E6=B5=81=E6=B0=B4=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - checkout shared/api/admin 自 d3c2114 - player: outright 结算态逻辑、wallet txAmountClass、i18n 新增 key(保留海军蓝主题样式) --- .../admin/src/components/LeagueRowActions.vue | 8 +- .../components/PlayerWalletLedgerDialog.vue | 74 +++++++------------ apps/admin/src/i18n/admin-pages-ms.ts | 5 ++ apps/admin/src/i18n/admin-pages.ts | 10 +++ apps/admin/src/i18n/pages/en.ts | 11 +++ apps/admin/src/i18n/pages/zh.ts | 11 +++ apps/admin/src/utils/adminListStale.ts | 13 ++++ apps/admin/src/utils/format-datetime.ts | 39 ++++++++++ apps/admin/src/utils/walletTx.ts | 44 +++++++++++ apps/admin/src/views/FinanceLogs.vue | 51 ++++--------- apps/admin/src/views/Matches.vue | 26 ++++++- apps/admin/src/views/MatchesOutrights.vue | 29 +++++++- apps/admin/src/views/Settlement.vue | 5 +- .../src/views/matches/LeagueMatchesPage.vue | 16 +++- .../src/views/matches/LeagueMatchesPanel.vue | 22 +++++- .../views/matches/LeagueOutrightOddsPanel.vue | 17 ++++- .../domains/catalog/matches.service.spec.ts | 43 ++++++++++- .../src/domains/catalog/matches.service.ts | 26 +++++-- .../src/domains/catalog/outright.service.ts | 54 +++++++++++--- .../src/components/WalletStatsPanel.vue | 4 + .../outright/OutrightEventSection.vue | 32 +++++++- .../outright/OutrightOptionCard.vue | 25 ++++++- .../src/components/outright/OutrightPanel.vue | 16 ++++ apps/player/src/i18n/en-US.ts | 2 + apps/player/src/i18n/ms-MY.ts | 2 + apps/player/src/i18n/zh-CN.ts | 2 + apps/player/src/utils/walletTx.ts | 12 +++ apps/player/src/views/WalletDetailView.vue | 7 +- .../src/views/WalletTransactionDetailView.vue | 14 ++-- apps/player/src/views/WalletView.vue | 22 +++++- packages/shared/src/api-errors.js | 25 +++++++ packages/shared/src/api-errors.ts | 25 +++++++ packages/shared/src/index.js | 1 + packages/shared/src/index.ts | 1 + packages/shared/src/walletTx.js | 11 +++ packages/shared/src/walletTx.ts | 15 ++++ 36 files changed, 593 insertions(+), 127 deletions(-) create mode 100644 apps/admin/src/utils/adminListStale.ts create mode 100644 apps/admin/src/utils/format-datetime.ts create mode 100644 packages/shared/src/walletTx.js create mode 100644 packages/shared/src/walletTx.ts diff --git a/apps/admin/src/components/LeagueRowActions.vue b/apps/admin/src/components/LeagueRowActions.vue index 40f87f1..f1f24bd 100644 --- a/apps/admin/src/components/LeagueRowActions.vue +++ b/apps/admin/src/components/LeagueRowActions.vue @@ -2,6 +2,7 @@ export interface LeagueRowView { id: string; isPublished: boolean; + isOutrightSettled?: boolean; isPublishing: boolean; labels: { edit: string; @@ -30,7 +31,12 @@ defineEmits<{ {{ row.labels.edit }} - + {{ row.labels.createFixture }} (); -const { t, locale, localeTag } = useAdminLocale(); +const { t, locale } = useAdminLocale(); const auth = useAuthStore(); const visible = computed({ @@ -68,17 +69,6 @@ function depositMethodLabel(row: WalletTxRow) { return walletDepositMethodLabel(row, t); } -function formatTime(v: string) { - return new Date(v).toLocaleString(localeTag.value, { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }); -} - function dateParams() { if (!dateRange.value?.length) return {}; const [from, to] = dateRange.value; @@ -172,8 +162,12 @@ watch( - - + + @@ -181,45 +175,27 @@ watch( - + - + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/apps/admin/src/views/Matches.vue b/apps/admin/src/views/Matches.vue index f12c21b..ead8952 100644 --- a/apps/admin/src/views/Matches.vue +++ b/apps/admin/src/views/Matches.vue @@ -3,6 +3,7 @@ import { ref, computed, watch, onBeforeUnmount, onDeactivated } from 'vue'; defineOptions({ name: 'AdminMatches' }); import { useStaleListLifecycle } from '../composables/useStaleList'; +import { consumeAdminListStale } from '../utils/adminListStale'; import { useRoute, useRouter } from 'vue-router'; import { useAdminLocale } from '../composables/useAdminLocale'; import { resolveFormError } from '../i18n/form-validation'; @@ -38,6 +39,7 @@ const isMatchChildRoute = computed(() => interface LeagueTableRow extends Record { id: string; isPublished: boolean; + isOutrightSettled: boolean; isPublishing: boolean; labels: { edit: string; @@ -50,7 +52,7 @@ interface LeagueTableRow extends Record { displayNameZh: string; displayNameEn: string; displayStatusLabel: string; - displayStatusTagType: 'success' | 'info'; + displayStatusTagType: 'success' | 'info' | 'warning'; displayMatchCount: number; displayBetCount: number; displayBetCountActive: boolean; @@ -121,6 +123,7 @@ function mapLeagueRows(items: unknown[], publishingId = ''): LeagueTableRow[] { const r = rowOf(item); const id = String(r.id ?? ''); const published = Boolean(r.isPublished); + const outrightSettled = Boolean(r.isOutrightSettled); const stats = r.betStats as | { betCount?: number; totalStake?: string; pendingCount?: number } | undefined; @@ -129,13 +132,18 @@ function mapLeagueRows(items: unknown[], publishingId = ''): LeagueTableRow[] { ...r, id, isPublished: published, + isOutrightSettled: outrightSettled, isPublishing: publishingId === id, labels, displaySeq: start + index + 1, displayNameZh: String(r.leagueZh ?? '').trim() || '—', displayNameEn: String(r.leagueEn ?? '').trim() || '—', - displayStatusLabel: published ? t('league.status.PUBLISHED') : t('league.status.UNPUBLISHED'), - displayStatusTagType: published ? 'success' : 'info', + displayStatusLabel: outrightSettled + ? t('league.status.OUTRIGHT_SETTLED') + : published + ? t('league.status.PUBLISHED') + : t('league.status.UNPUBLISHED'), + displayStatusTagType: outrightSettled ? 'warning' : published ? 'success' : 'info', displayMatchCount: Number(r.matchCount ?? 0), displayBetCount: betCount, displayBetCountActive: betCount > 0, @@ -163,6 +171,7 @@ function persistListUiState() { type LoadOptions = { restore?: boolean }; async function load(options: LoadOptions = {}) { + consumeAdminListStale(); const saved = options.restore ? readMatchesListUiState() : null; if (saved) { page.value = saved.page; @@ -189,6 +198,17 @@ function onSearch() { void runLoad(true); } +const MATCH_CHILD_ROUTE = /^\/matches\/leagues\/[^/]+/; + +watch( + () => route.path, + (path, prevPath) => { + if (prevPath && MATCH_CHILD_ROUTE.test(prevPath) && path === '/matches') { + void load(); + } + }, +); + async function initialLoad() { if (isMatchChildRoute.value) return; const qStatus = route.query.status; diff --git a/apps/admin/src/views/MatchesOutrights.vue b/apps/admin/src/views/MatchesOutrights.vue index fe5011b..81b9d74 100644 --- a/apps/admin/src/views/MatchesOutrights.vue +++ b/apps/admin/src/views/MatchesOutrights.vue @@ -1,5 +1,6 @@ + + + @@ -270,4 +294,7 @@ function outrightTeamCount(row: unknown) { color: var(--text-muted); font-size: 13px; } +.status-dash { + color: var(--text-muted); +} diff --git a/apps/admin/src/views/Settlement.vue b/apps/admin/src/views/Settlement.vue index d2dbe0a..92bf6ea 100644 --- a/apps/admin/src/views/Settlement.vue +++ b/apps/admin/src/views/Settlement.vue @@ -23,7 +23,7 @@ import { betTypeLabel, betResultLabel, } from '../utils/bet-labels'; -import { adminSelectionLabel } from '../utils/adminSelectionLabel'; +import { markAdminListStale } from '../utils/adminListStale'; import type { AdminMatchDetail } from './match-form'; import AdminSubNav from '../components/AdminSubNav.vue'; @@ -658,6 +658,7 @@ async function confirmResettle() { resettleDialogVisible.value = false; await loadMatch(); void loadSettlementHistory(); + markAdminListStale(); } catch (e: unknown) { ElMessage.error(settlementApiError(e, t('settlement.preview_failed'))); } finally { @@ -726,6 +727,7 @@ async function previewSettlement() { previewPageSize.value = itemsPage.pageSize; previewDialogVisible.value = true; await loadMatch(); + markAdminListStale(); } catch (e: unknown) { ElMessage.error(settlementApiError(e, t('settlement.preview_failed'))); } finally { @@ -754,6 +756,7 @@ async function confirm() { previewDialogVisible.value = false; await loadMatch(); void loadSettlementHistory(); + markAdminListStale(); } catch (e: unknown) { ElMessage.error(settlementApiError(e, t('settlement.preview_failed'))); } finally { diff --git a/apps/admin/src/views/matches/LeagueMatchesPage.vue b/apps/admin/src/views/matches/LeagueMatchesPage.vue index f2eb289..9db1296 100644 --- a/apps/admin/src/views/matches/LeagueMatchesPage.vue +++ b/apps/admin/src/views/matches/LeagueMatchesPage.vue @@ -34,9 +34,15 @@ const leagueTitle = computed(() => { const panelRef = ref<{ reload: () => void } | null>(null); const createVisible = ref(false); const createLoading = ref(false); +const isOutrightSettled = ref(false); const form = ref(emptyMatchForm()); +function onLeagueMeta(meta: { isOutrightSettled: boolean }) { + isOutrightSettled.value = meta.isOutrightSettled; +} + function openCreateFixture() { + if (isOutrightSettled.value) return; form.value = emptyMatchForm(); form.value.leagueId = leagueId.value; createVisible.value = true; @@ -85,9 +91,10 @@ watch(leagueId, () => { :subtitle="t('match.league_fixtures_subtitle')" > @@ -97,6 +104,7 @@ watch(leagueId, () => { :league-id="leagueId" :filter-status="filterStatus" :keyword="filterKeyword" + @league-meta="onLeagueMeta" /> @@ -254,6 +262,12 @@ watch(leagueId, () => { font-weight: 700; } +.settled-hint { + font-size: 13px; + color: var(--warning-text); + font-weight: 600; +} + @media (max-width: 760px) { .teams-row { grid-template-columns: 1fr; diff --git a/apps/admin/src/views/matches/LeagueMatchesPanel.vue b/apps/admin/src/views/matches/LeagueMatchesPanel.vue index 4eec792..d5e2e4e 100644 --- a/apps/admin/src/views/matches/LeagueMatchesPanel.vue +++ b/apps/admin/src/views/matches/LeagueMatchesPanel.vue @@ -1,5 +1,6 @@