diff --git a/AGENTS.md b/AGENTS.md index c6a7f31..072603f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -90,6 +90,14 @@ - 管理端切页慢的分析与优化任务见 `docs/admin-page-switch-performance.md`。 - **不要**把 `apps/player/dist/` 提交进 Git;生产由 Docker 构建生成静态资源。 +### 玩家端主题生图约束 + +- 生成 `apps/player` 主题图片资源时,必须先查看并参考现有同场景资源,优先保持当前暗金足球主题一致,不要另起一套风格。 +- PC 首页/赛事详情/弹窗类赛事面板应优先对齐 `apps/player/src/assets/images/generated/pc-featured-match-bg-v1.webp`:暗金球场、中央 V 型光线、薄金色边框、低亮度背景、内容区域干净。 +- 弹窗资源必须是“带完整边框的面板图”,不能只是普通背景图;四边边框要在图片内完整可见,中心区域要足够暗且简洁,保证文字可读。 +- 避免生成过度写实、照片质感过重、厚重金属、强眩光、烟雾、人物、旗帜、logo、文字、数字;也不要生成与主题不一致的纯矢量科幻线框风。 +- 生成资源默认输出 WebP,控制体积,文件放在 `apps/player/src/assets/images/generated/`,命名带清晰场景和版本号(如 `modal-gold-frame-v4.webp`)。 + ## i18n 改文案流程 支持语言均为 **zh-CN / en-US / ms-MY**(马来语)。改文案须**三语同步**,不要只改中文。 @@ -209,4 +217,3 @@ - 不要提交 `.env`、`.env.docker`、镜像 tar、`apps/*/dist/`。 - 改 player 主题相关文件时,先确认当前分支是 `main` 还是 `theme-`*,避免用错 CSS 变量(如 theme-2 无 `--gold`)。 - 管理端改列表页性能时,参考 `docs/admin-page-switch-performance.md` 任务清单,优先 KeepAlive + 缓存而非盲目减 API 字段。 - diff --git a/apps/player/src/assets/images/generated/detail-ledger-ticket-bg-v1.webp b/apps/player/src/assets/images/generated/detail-ledger-ticket-bg-v1.webp new file mode 100644 index 0000000..c9868e9 Binary files /dev/null and b/apps/player/src/assets/images/generated/detail-ledger-ticket-bg-v1.webp differ diff --git a/apps/player/src/assets/images/generated/empty-matches-stadium-v1.webp b/apps/player/src/assets/images/generated/empty-matches-stadium-v1.webp new file mode 100644 index 0000000..7b98fc6 Binary files /dev/null and b/apps/player/src/assets/images/generated/empty-matches-stadium-v1.webp differ diff --git a/apps/player/src/assets/images/generated/floating-mailbox-fab-v1.webp b/apps/player/src/assets/images/generated/floating-mailbox-fab-v1.webp new file mode 100644 index 0000000..158de38 Binary files /dev/null and b/apps/player/src/assets/images/generated/floating-mailbox-fab-v1.webp differ diff --git a/apps/player/src/assets/images/generated/home-tab-frame-gold-v1.webp b/apps/player/src/assets/images/generated/home-tab-frame-gold-v1.webp new file mode 100644 index 0000000..5baa703 Binary files /dev/null and b/apps/player/src/assets/images/generated/home-tab-frame-gold-v1.webp differ diff --git a/apps/player/src/assets/images/generated/match-card-bg-gold-arena-v1.webp b/apps/player/src/assets/images/generated/match-card-bg-gold-arena-v1.webp new file mode 100644 index 0000000..1338bf6 Binary files /dev/null and b/apps/player/src/assets/images/generated/match-card-bg-gold-arena-v1.webp differ diff --git a/apps/player/src/assets/images/generated/match-card-bg-gold-stadium-v1.webp b/apps/player/src/assets/images/generated/match-card-bg-gold-stadium-v1.webp new file mode 100644 index 0000000..7ec3e4f Binary files /dev/null and b/apps/player/src/assets/images/generated/match-card-bg-gold-stadium-v1.webp differ diff --git a/apps/player/src/assets/images/generated/match-card-bg-gold-tunnel-v1.webp b/apps/player/src/assets/images/generated/match-card-bg-gold-tunnel-v1.webp new file mode 100644 index 0000000..0e22524 Binary files /dev/null and b/apps/player/src/assets/images/generated/match-card-bg-gold-tunnel-v1.webp differ diff --git a/apps/player/src/assets/images/generated/modal-gold-frame-v1.webp b/apps/player/src/assets/images/generated/modal-gold-frame-v1.webp new file mode 100644 index 0000000..8f1bd51 Binary files /dev/null and b/apps/player/src/assets/images/generated/modal-gold-frame-v1.webp differ diff --git a/apps/player/src/assets/images/generated/modal-gold-frame-v2.webp b/apps/player/src/assets/images/generated/modal-gold-frame-v2.webp new file mode 100644 index 0000000..ab0a52e Binary files /dev/null and b/apps/player/src/assets/images/generated/modal-gold-frame-v2.webp differ diff --git a/apps/player/src/assets/images/generated/modal-gold-frame-v3.webp b/apps/player/src/assets/images/generated/modal-gold-frame-v3.webp new file mode 100644 index 0000000..021e3e3 Binary files /dev/null and b/apps/player/src/assets/images/generated/modal-gold-frame-v3.webp differ diff --git a/apps/player/src/assets/images/generated/modal-gold-frame-v4.webp b/apps/player/src/assets/images/generated/modal-gold-frame-v4.webp new file mode 100644 index 0000000..a8fad75 Binary files /dev/null and b/apps/player/src/assets/images/generated/modal-gold-frame-v4.webp differ diff --git a/apps/player/src/assets/images/generated/outright-bet-modal-frame-v1.webp b/apps/player/src/assets/images/generated/outright-bet-modal-frame-v1.webp new file mode 100644 index 0000000..9b7f987 Binary files /dev/null and b/apps/player/src/assets/images/generated/outright-bet-modal-frame-v1.webp differ diff --git a/apps/player/src/assets/images/generated/outright-bet-modal-frame-v2.webp b/apps/player/src/assets/images/generated/outright-bet-modal-frame-v2.webp new file mode 100644 index 0000000..070829b Binary files /dev/null and b/apps/player/src/assets/images/generated/outright-bet-modal-frame-v2.webp differ diff --git a/apps/player/src/assets/images/generated/pc-betslip-btn-primary-pressed-v1.webp b/apps/player/src/assets/images/generated/pc-betslip-btn-primary-pressed-v1.webp new file mode 100644 index 0000000..a806cd5 Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-betslip-btn-primary-pressed-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-betslip-btn-primary-v1.webp b/apps/player/src/assets/images/generated/pc-betslip-btn-primary-v1.webp new file mode 100644 index 0000000..c9df9f9 Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-betslip-btn-primary-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-betslip-btn-secondary-pressed-v1.webp b/apps/player/src/assets/images/generated/pc-betslip-btn-secondary-pressed-v1.webp new file mode 100644 index 0000000..4d198e9 Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-betslip-btn-secondary-pressed-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-betslip-btn-secondary-v1.webp b/apps/player/src/assets/images/generated/pc-betslip-btn-secondary-v1.webp new file mode 100644 index 0000000..35c06df Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-betslip-btn-secondary-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-betslip-sidebar-bg-v1.webp b/apps/player/src/assets/images/generated/pc-betslip-sidebar-bg-v1.webp new file mode 100644 index 0000000..6f2f79b Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-betslip-sidebar-bg-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-featured-match-bg-v1.webp b/apps/player/src/assets/images/generated/pc-featured-match-bg-v1.webp new file mode 100644 index 0000000..b9e50ba Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-featured-match-bg-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-global-bg-v1.webp b/apps/player/src/assets/images/generated/pc-global-bg-v1.webp new file mode 100644 index 0000000..a17608c Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-global-bg-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-match-detail-bg-v1.webp b/apps/player/src/assets/images/generated/pc-match-detail-bg-v1.webp new file mode 100644 index 0000000..116f3fa Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-match-detail-bg-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-match-list-card-bg-v1.webp b/apps/player/src/assets/images/generated/pc-match-list-card-bg-v1.webp new file mode 100644 index 0000000..412656f Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-match-list-card-bg-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-outright-detail-bg-v1.webp b/apps/player/src/assets/images/generated/pc-outright-detail-bg-v1.webp new file mode 100644 index 0000000..7c7a188 Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-outright-detail-bg-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-outright-list-card-bg-v1.webp b/apps/player/src/assets/images/generated/pc-outright-list-card-bg-v1.webp new file mode 100644 index 0000000..a3ad37b Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-outright-list-card-bg-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-sidebar-bg-v1.webp b/apps/player/src/assets/images/generated/pc-sidebar-bg-v1.webp new file mode 100644 index 0000000..5e126e3 Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-sidebar-bg-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-top-nav-bg-v1.webp b/apps/player/src/assets/images/generated/pc-top-nav-bg-v1.webp new file mode 100644 index 0000000..cb0514f Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-top-nav-bg-v1.webp differ diff --git a/apps/player/src/assets/images/generated/pc-wallet-banner-v1.webp b/apps/player/src/assets/images/generated/pc-wallet-banner-v1.webp new file mode 100644 index 0000000..2e9067b Binary files /dev/null and b/apps/player/src/assets/images/generated/pc-wallet-banner-v1.webp differ diff --git a/apps/player/src/assets/images/generated/saishi-gold-trophy-v1.webp b/apps/player/src/assets/images/generated/saishi-gold-trophy-v1.webp new file mode 100644 index 0000000..33d0299 Binary files /dev/null and b/apps/player/src/assets/images/generated/saishi-gold-trophy-v1.webp differ diff --git a/apps/player/src/assets/images/generated/vs-gold-alpha-v1.webp b/apps/player/src/assets/images/generated/vs-gold-alpha-v1.webp new file mode 100644 index 0000000..71a2af6 Binary files /dev/null and b/apps/player/src/assets/images/generated/vs-gold-alpha-v1.webp differ diff --git a/apps/player/src/components/ConfirmDialog.vue b/apps/player/src/components/ConfirmDialog.vue index e2422ba..c2fba70 100644 --- a/apps/player/src/components/ConfirmDialog.vue +++ b/apps/player/src/components/ConfirmDialog.vue @@ -101,18 +101,33 @@ function onConfirm() { } .confirm-modal { + position: relative; width: 100%; - max-width: 340px; - background: linear-gradient(165deg, #1a1810 0%, #121212 45%, #0a0a0a 100%); - border: 1px solid var(--border-gold-soft, rgba(200, 168, 78, 0.25)); - border-radius: 12px; - padding: 22px 18px 16px; - box-shadow: 0 0 24px rgba(212, 175, 55, 0.08); + max-width: 374px; + overflow: hidden; + background: + linear-gradient(180deg, rgba(212, 175, 55, 0.08), transparent 38%), + rgba(14, 14, 12, 0.96); + border: 1px solid rgba(212, 175, 55, 0.34); + border-radius: 8px; + padding: 31px 28px 25px; + box-shadow: none; +} + +.confirm-modal::before { + content: ''; + position: absolute; + top: 0; + left: 24px; + right: 24px; + height: 1px; + background: linear-gradient(90deg, transparent, rgba(232, 200, 74, 0.65), transparent); + pointer-events: none; } .confirm-title { - margin: 0 0 10px; - font-size: 17px; + margin: 0 0 12px; + font-size: 18px; font-weight: 800; color: var(--gold, #c8a84e); text-align: center; @@ -120,7 +135,7 @@ function onConfirm() { } .confirm-message { - margin: 0 0 20px; + margin: 0 0 26px; font-size: 14px; line-height: 1.6; color: #c8c8c8; @@ -130,17 +145,17 @@ function onConfirm() { .confirm-actions { display: flex; - gap: 10px; + gap: 12px; } .confirm-btn { flex: 1; - min-height: 44px; - border-radius: 8px; + min-height: 49px; + border-radius: 0; font-size: 14px; font-weight: 700; cursor: pointer; - transition: opacity 0.15s; + transition: opacity 0.15s, filter 0.15s; } .confirm-btn:disabled { @@ -149,20 +164,30 @@ function onConfirm() { } .confirm-btn.cancel { - border: 1px solid #333; - background: transparent; - color: #888; + border: 0; + background: url('../assets/images/generated/pc-betslip-btn-secondary-v1.webp') center / 100% 100% no-repeat; + color: #9f9273; } .confirm-btn.confirm { border: none; - background: linear-gradient(135deg, #d4a017, #e8c84a); - color: #1a1a1a; + background: url('../assets/images/generated/pc-betslip-btn-primary-v1.webp') center / 100% 100% no-repeat; + color: #1b1200; } -.confirm-btn.confirm.danger { - background: linear-gradient(135deg, #c0392b, #e74c3c); - color: #fff; +.confirm-btn.cancel:not(:disabled):hover, +.confirm-btn.confirm:not(:disabled):hover { + filter: brightness(1.06); +} + +.confirm-btn.cancel:not(:disabled):active { + background-image: url('../assets/images/generated/pc-betslip-btn-secondary-pressed-v1.webp'); + filter: brightness(0.96); +} + +.confirm-btn.confirm:not(:disabled):active { + background-image: url('../assets/images/generated/pc-betslip-btn-primary-pressed-v1.webp'); + filter: brightness(0.98); } .confirm-fade-enter-active, diff --git a/apps/player/src/components/FloatingMailbox.vue b/apps/player/src/components/FloatingMailbox.vue index 27e3d48..537478c 100644 --- a/apps/player/src/components/FloatingMailbox.vue +++ b/apps/player/src/components/FloatingMailbox.vue @@ -317,47 +317,61 @@ onMounted(() => { } .fab-btn { - width: 56px; - height: 56px; + width: 48px; + height: 48px; border-radius: 50%; - background: var(--primary); - color: #fff; - border: none; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + background: url('../assets/images/generated/floating-mailbox-fab-v1.webp') center / cover no-repeat; + color: var(--primary-light); + border: 0; + box-shadow: + 0 8px 22px rgba(0, 0, 0, 0.42), + 0 0 16px rgba(212, 175, 55, 0.12); display: flex; align-items: center; justify-content: center; cursor: pointer; - transition: transform 0.2s, background 0.2s; + transition: transform 0.2s, box-shadow 0.2s, color 0.2s, filter 0.2s; position: relative; } .fab-btn:hover { - transform: scale(1.05); - background: var(--primary-light); + transform: translateY(-1px); + color: #fff1a8; + filter: brightness(1.08); + box-shadow: + 0 10px 26px rgba(0, 0, 0, 0.5), + 0 0 20px rgba(212, 175, 55, 0.18); } .fab-active { - transform: scale(0.95); + transform: scale(0.96); + color: #fff1a8; + filter: brightness(0.94); } .fab-icon { - width: 28px; - height: 28px; + width: 24px; + height: 24px; } .badge { position: absolute; - top: 0; - right: 0; + top: -3px; + right: -3px; background: var(--danger, #ff4d4f); color: #fff; - font-size: 11px; + font-size: 10px; font-weight: 700; - padding: 2px 6px; + min-width: 18px; + height: 18px; + padding: 0 5px; border-radius: 10px; - border: 2px solid var(--bg-card); + border: 1px solid rgba(255, 255, 255, 0.9); line-height: 1; + display: inline-flex; + align-items: center; + justify-content: center; + box-shadow: 0 2px 8px rgba(255, 69, 58, 0.42); } .popup-panel { diff --git a/apps/player/src/components/LeagueAccordionItem.vue b/apps/player/src/components/LeagueAccordionItem.vue index a3e1526..bdc967e 100644 --- a/apps/player/src/components/LeagueAccordionItem.vue +++ b/apps/player/src/components/LeagueAccordionItem.vue @@ -2,8 +2,8 @@ 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'; +import saishiImg from '../assets/images/generated/saishi-gold-trophy-v1.webp'; +import cardBg from '../assets/images/generated/pc-match-list-card-bg-v1.webp'; const matchCardBg = `url(${cardBg})`; diff --git a/apps/player/src/components/LoginPromptModal.vue b/apps/player/src/components/LoginPromptModal.vue index e669142..881993f 100644 --- a/apps/player/src/components/LoginPromptModal.vue +++ b/apps/player/src/components/LoginPromptModal.vue @@ -28,7 +28,7 @@ function continueBrowsing() {
@@ -64,10 +64,13 @@ function continueBrowsing() { position: relative; width: 90%; max-width: 360px; - background: #1a1a1a; - border: 1px solid var(--border-gold-soft, rgba(200, 168, 78, 0.25)); - border-radius: 12px; - padding: 28px 24px 24px; + background: + linear-gradient(rgba(16, 14, 10, 0.12), rgba(16, 14, 10, 0.16)), + url('../assets/images/generated/modal-gold-frame-v4.webp') center / 102% 102% no-repeat; + border: 0; + border-radius: 0; + padding: 36px 30px 30px; + box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55), 0 0 24px rgba(212, 175, 55, 0.1); } .close-btn { diff --git a/apps/player/src/components/MatchBetCard.vue b/apps/player/src/components/MatchBetCard.vue index 3f57ae8..4a54114 100644 --- a/apps/player/src/components/MatchBetCard.vue +++ b/apps/player/src/components/MatchBetCard.vue @@ -9,6 +9,10 @@ import { useBetSlipStore } from '../stores/betSlip'; import { useDesktopBetPopover } from '../composables/useDesktopBetPopover'; import { resolveSelectionLabel } from '../utils/selectionLabel'; import MarketSelectionsPanel from './match-detail/MarketSelectionsPanel.vue'; +import vsImg from '../assets/images/generated/vs-gold-alpha-v1.webp'; +import matchListCardBg from '../assets/images/generated/pc-match-list-card-bg-v1.webp'; + +const matchCardBg = `url(${matchListCardBg})`; const props = defineProps<{ match: { @@ -229,7 +233,9 @@ function goMatchDetail() {
{{ kickoffText }} {{ liveScoreText }} - VS + + VS +
@@ -294,9 +300,9 @@ function goMatchDetail() { diff --git a/apps/player/src/components/outright/OutrightEventSection.vue b/apps/player/src/components/outright/OutrightEventSection.vue index dfb646a..e4fd78a 100644 --- a/apps/player/src/components/outright/OutrightEventSection.vue +++ b/apps/player/src/components/outright/OutrightEventSection.vue @@ -2,8 +2,8 @@ import { computed } from 'vue'; import { useI18n } from 'vue-i18n'; import OutrightOptionCard from './OutrightOptionCard.vue'; -import saishiImg from '../../assets/images/saishi.webp'; -import cardBg from '../../assets/images/card-bg.webp'; +import saishiImg from '../../assets/images/generated/saishi-gold-trophy-v1.webp'; +import cardBg from '../../assets/images/generated/pc-match-list-card-bg-v1.webp'; const matchCardBg = `url(${cardBg})`; diff --git a/apps/player/src/components/outright/OutrightPanel.vue b/apps/player/src/components/outright/OutrightPanel.vue index db37c26..e9cfce9 100644 --- a/apps/player/src/components/outright/OutrightPanel.vue +++ b/apps/player/src/components/outright/OutrightPanel.vue @@ -7,7 +7,7 @@ import OutrightEventSection, { } from './OutrightEventSection.vue'; import OutrightBetModal, { type OutrightPick } from './OutrightBetModal.vue'; import { useAuthStore } from '../../stores/auth'; -import emptyMatchesImg from '../../assets/images/empty-matches.svg'; +import emptyMatchesImg from '../../assets/images/generated/empty-matches-stadium-v1.webp'; import GoldSpinner from '../../components/GoldSpinner.vue'; import { useOutrightEvents } from '../../composables/useOutrightEvents'; @@ -159,16 +159,25 @@ function closeModal() { .state, .empty { + display: flex; + flex-direction: column; + align-items: center; text-align: center; color: var(--text-muted); - padding: 48px 20px; + padding: 34px 20px 44px; font-weight: 600; } .empty-icon { - width: 96px; - height: 96px; - margin-bottom: 14px; + width: min(270px, 82vw); + aspect-ratio: 16 / 9; + height: auto; + margin-bottom: 12px; + border-radius: 8px; + object-fit: cover; + opacity: 0.86; + mask-image: linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%); + -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%); } .empty-hint { diff --git a/apps/player/src/composables/useOutrightEvents.ts b/apps/player/src/composables/useOutrightEvents.ts index f4d28fa..13cacc5 100644 --- a/apps/player/src/composables/useOutrightEvents.ts +++ b/apps/player/src/composables/useOutrightEvents.ts @@ -1,15 +1,23 @@ -import { ref } from 'vue'; +import { onMounted, ref, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import api from '../api'; import type { OutrightEvent } from '../components/outright/OutrightEventSection.vue'; import type { OutrightSelection } from '../components/outright/OutrightEventSection.vue'; -import { useOnLocaleChange } from './useOnLocaleChange'; + +const CACHE_TTL_MS = 8000; +const loading = ref(false); +const loadError = ref(''); +const events = ref([]); +let lastLoadedAt = 0; +let inFlight: Promise | null = null; + +type LoadOptions = { + silent?: boolean; + force?: boolean; +}; export function useOutrightEvents() { - const { t } = useI18n(); - const loading = ref(true); - const loadError = ref(''); - const events = ref([]); + const { t, locale } = useI18n(); function mergeOddsOnly(fresh: OutrightEvent[]) { const freshMap = new Map(); @@ -30,15 +38,34 @@ export function useOutrightEvents() { } } - async function load(options?: { silent?: boolean }) { + async function load(options?: LoadOptions) { const hadData = events.value.length > 0; + if (!options?.force && hadData && Date.now() - lastLoadedAt < CACHE_TTL_MS) { + loading.value = false; + loadError.value = ''; + return; + } + + if (inFlight) { + if (!hadData && !options?.silent) loading.value = true; + try { + await inFlight; + } finally { + if (!hadData && !options?.silent) loading.value = false; + } + return; + } + if (!hadData && !options?.silent) loading.value = true; loadError.value = ''; try { - const { data } = await api.get('/player/outrights'); - const list = (data?.data ?? []) as OutrightEvent[]; - const fresh = list.filter((e) => e.selections?.length > 0); - if (!hadData) { + inFlight = api.get('/player/outrights').then(({ data }) => { + const list = (data?.data ?? []) as OutrightEvent[]; + return list.filter((e) => e.selections?.length > 0); + }); + const fresh = await inFlight; + lastLoadedAt = Date.now(); + if (!hadData || options?.force) { events.value = fresh; } else { mergeOddsOnly(fresh); @@ -53,10 +80,19 @@ export function useOutrightEvents() { } } finally { if (!hadData && !options?.silent) loading.value = false; + inFlight = null; } } - useOnLocaleChange(load); + onMounted(() => { + void load({ silent: events.value.length > 0 }); + }); + + watch(locale, (next, prev) => { + if (prev && next !== prev) { + void load({ silent: events.value.length > 0, force: true }); + } + }); return { loading, diff --git a/apps/player/src/layouts/MainLayout.vue b/apps/player/src/layouts/MainLayout.vue index 8448002..d3a35cd 100644 --- a/apps/player/src/layouts/MainLayout.vue +++ b/apps/player/src/layouts/MainLayout.vue @@ -261,7 +261,7 @@ watch( align-items: center; padding: 8px 12px; min-height: var(--player-header-h); - background: rgba(17, 17, 17, 0.94); + background: rgba(29, 27, 22, 0.88); border-bottom: 1px solid var(--border); z-index: 120; } @@ -371,7 +371,7 @@ watch( box-sizing: border-box; min-height: calc(var(--player-bottom-nav-h) + var(--safe-bottom)); padding-bottom: var(--safe-bottom); - background: rgba(17, 17, 17, 0.96); + background: rgba(29, 27, 22, 0.9); border-top: 1px solid var(--border); box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.35); z-index: 100; diff --git a/apps/player/src/styles.css b/apps/player/src/styles.css index 922fd9f..b82ac1f 100644 --- a/apps/player/src/styles.css +++ b/apps/player/src/styles.css @@ -7,13 +7,13 @@ --secondary: #1A1A1A; --tertiary: #000000; --neutral: #8E8E93; - --bg-body: #000000; - --bg-card: rgba(20, 20, 20, 0.65); - --bg-hover: rgba(34, 34, 34, 0.65); - --bg-elevated: rgba(42, 42, 42, 0.65); + --bg-body: #080806; + --bg-card: rgba(33, 31, 25, 0.76); + --bg-hover: rgba(47, 44, 35, 0.82); + --bg-elevated: rgba(58, 54, 43, 0.86); --text: #FFFFFF; - --text-muted: #8E8E93; - --border: #333333; + --text-muted: #B7B0A0; + --border: rgba(255, 226, 156, 0.18); --border-gold: #D4AF37; --border-gold-soft: rgba(212, 175, 55, 0.28); --border-w: 1px; @@ -209,9 +209,9 @@ body { /* 保留 bg 位图;用 scroll 替代 fixed,减轻移动端滚动重绘(后续可换 WebP/AVIF) */ background-color: var(--tertiary); background-image: - radial-gradient(ellipse 120% 60% at 50% -10%, rgba(212, 175, 55, 0.14), transparent 55%), - linear-gradient(rgba(0, 0, 0, 0.88), rgba(0, 0, 0, 0.88)), - url('./assets/images/bg.webp'); + radial-gradient(ellipse 120% 60% at 50% -10%, rgba(246, 210, 108, 0.2), transparent 56%), + linear-gradient(rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0.7)), + url('./assets/images/generated/saishi-gold-trophy-v1.webp'); background-repeat: no-repeat; background-position: center center; background-size: cover; @@ -223,7 +223,10 @@ body { @media (min-width: 1024px) { body { - background-image: url('./assets/images/pcbg.png'); + background-image: + radial-gradient(ellipse 90% 70% at 50% 0%, rgba(246, 210, 108, 0.14), transparent 58%), + linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.58)), + url('./assets/images/generated/pc-global-bg-v1.webp'); background-attachment: fixed; } } diff --git a/apps/player/src/styles/desktop/layout.css b/apps/player/src/styles/desktop/layout.css index 9744000..19e1928 100644 --- a/apps/player/src/styles/desktop/layout.css +++ b/apps/player/src/styles/desktop/layout.css @@ -8,8 +8,11 @@ inset: 0; display: flex; flex-direction: column; - background-color: #000; - background-image: url('../../assets/images/pcbg.png'); + background-color: #0d0b08; + background-image: + radial-gradient(ellipse 90% 68% at 50% 0%, rgba(246, 210, 108, 0.14), transparent 58%), + linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.58)), + url('../../assets/images/generated/pc-global-bg-v1.webp'); background-size: cover; background-position: center; background-attachment: fixed; @@ -21,7 +24,7 @@ content: ''; position: fixed; inset: 0; - background: radial-gradient(ellipse 80% 60% at 50% 0%, rgba(212, 175, 55, 0.03) 0%, transparent 60%); + background: radial-gradient(ellipse 80% 60% at 50% 0%, rgba(255, 221, 128, 0.08) 0%, transparent 60%); pointer-events: none; z-index: 0; } @@ -29,10 +32,8 @@ .desktop-header-wrap { flex-shrink: 0; height: var(--desktop-header-h); - background: rgba(14, 14, 14, 0.88); - backdrop-filter: var(--blur-md); - -webkit-backdrop-filter: var(--blur-md); - border-bottom: 1px solid var(--border); + background: #12100c url('../../assets/images/generated/pc-top-nav-bg-v1.webp') center / 100% 100% no-repeat; + border-bottom: 0; z-index: var(--z-header); display: flex; align-items: center; @@ -45,7 +46,7 @@ flex-shrink: 0; z-index: var(--z-sticky); border-bottom: 1px solid var(--border); - background: rgba(14, 14, 14, 0.85); + background: rgba(26, 24, 19, 0.78); backdrop-filter: var(--blur-sm); -webkit-backdrop-filter: var(--blur-sm); } @@ -90,7 +91,7 @@ width: 100%; padding: 0 16px; border-bottom: 1px solid var(--border); - background: rgba(14, 14, 14, 0.85); + background: rgba(26, 24, 19, 0.76); backdrop-filter: var(--blur-sm); -webkit-backdrop-filter: var(--blur-sm); } @@ -105,7 +106,7 @@ .desktop-betting-left { border-right: 1px solid var(--border); - background: rgba(14, 14, 14, 0.6); + background: rgba(26, 24, 19, 0.56); backdrop-filter: var(--blur-sm); -webkit-backdrop-filter: var(--blur-sm); overflow-y: auto; @@ -209,7 +210,7 @@ .desktop-hub-right { overflow-y: auto; padding: var(--space-5); - background: rgba(10, 10, 10, 0.3); + background: rgba(18, 16, 12, 0.18); } /* ── Auth Layout: Centered container ── */ diff --git a/apps/player/src/styles/desktop/tokens.css b/apps/player/src/styles/desktop/tokens.css index b61f93d..72cdd5a 100644 --- a/apps/player/src/styles/desktop/tokens.css +++ b/apps/player/src/styles/desktop/tokens.css @@ -12,20 +12,20 @@ --primary: #D4AF37; --primary-dark: #A8841F; --primary-light: #F0D875; - --secondary: #141414; - --tertiary: #0A0A0A; + --secondary: #1c1a15; + --tertiary: #0f0d09; --neutral: #8E8E93; - --bg-body: #050505; - --bg-card: rgba(20, 20, 20, 0.92); - --bg-card-hover: rgba(28, 28, 28, 0.95); - --bg-hover: rgba(38, 38, 38, 0.95); - --bg-elevated: rgba(45, 45, 45, 0.95); - --bg-surface: rgba(16, 16, 16, 0.88); + --bg-body: #0d0b08; + --bg-card: rgba(32, 30, 24, 0.82); + --bg-card-hover: rgba(44, 40, 31, 0.9); + --bg-hover: rgba(52, 48, 38, 0.9); + --bg-elevated: rgba(62, 57, 45, 0.92); + --bg-surface: rgba(28, 26, 21, 0.8); --text: #FFFFFF; - --text-secondary: #B8B8BD; - --text-muted: #8E8E93; - --border: #262626; - --border-subtle: rgba(255, 255, 255, 0.06); + --text-secondary: #D3CBB8; + --text-muted: #B5AD9B; + --border: rgba(255, 226, 156, 0.16); + --border-subtle: rgba(255, 235, 185, 0.1); --border-gold: #D4AF37; --border-gold-soft: rgba(212, 175, 55, 0.25); --border-w: 1px; @@ -62,10 +62,10 @@ --desktop-right-betslip-collapsed-w: 40px; /* ── Desktop specific vars ── */ - --desktop-bg: #050505; - --desktop-border: #262626; - --desktop-sidebar-bg: rgba(20, 20, 20, 0.95); - --desktop-sidebar-panel-bg: linear-gradient(rgba(16, 16, 16, 0.88), rgba(16, 16, 16, 0.88)), url('../../assets/images/cebian.webp') no-repeat center / cover; + --desktop-bg: #0d0b08; + --desktop-border: rgba(255, 226, 156, 0.16); + --desktop-sidebar-bg: rgba(30, 28, 23, 0.82); + --desktop-sidebar-panel-bg: linear-gradient(rgba(18, 16, 12, 0.52), rgba(18, 16, 12, 0.64)), url('../../assets/images/generated/pc-sidebar-bg-v1.webp') no-repeat center / cover; /* ── Spacing Scale (8dp rhythm) ── */ --space-1: 4px; diff --git a/apps/player/src/views/MobileBetDetailView.vue b/apps/player/src/views/MobileBetDetailView.vue index ff8fb52..e3fac80 100644 --- a/apps/player/src/views/MobileBetDetailView.vue +++ b/apps/player/src/views/MobileBetDetailView.vue @@ -364,12 +364,19 @@ const showPhaseWatermark = computed( .receipt { position: relative; border-radius: var(--radius); - background: rgba(18, 18, 18, 0.96); + background: + linear-gradient(rgba(18, 16, 12, 0.78), rgba(16, 14, 10, 0.86)), + url('../assets/images/generated/detail-ledger-ticket-bg-v1.webp') center / cover no-repeat; border: 1px solid var(--border); box-shadow: var(--shadow); overflow: hidden; } +.receipt > * { + position: relative; + z-index: 1; +} + /* result strip */ .receipt-result { display: flex; @@ -533,7 +540,7 @@ const showPhaseWatermark = computed( position: relative; overflow: hidden; padding: 14px; - background: rgba(255, 255, 255, 0.02); + background: rgba(255, 242, 198, 0.045); border: 1px solid rgba(255, 255, 255, 0.06); border-radius: var(--radius-sm); } diff --git a/apps/player/src/views/MobileFootballView.vue b/apps/player/src/views/MobileFootballView.vue index dd53356..9fe14e9 100644 --- a/apps/player/src/views/MobileFootballView.vue +++ b/apps/player/src/views/MobileFootballView.vue @@ -6,7 +6,7 @@ import { usePlayerMatches } from '../composables/usePlayerMatches'; import LeagueAccordionItem from '../components/LeagueAccordionItem.vue'; import MatchBetCard from '../components/MatchBetCard.vue'; import OutrightPanel from '../components/outright/OutrightPanel.vue'; -import emptyMatchesImg from '../assets/images/empty-matches.svg'; +import emptyMatchesImg from '../assets/images/generated/empty-matches-stadium-v1.webp'; import { useOnLocaleChange } from '../composables/useOnLocaleChange'; import GoldSpinner from '../components/GoldSpinner.vue'; import { usePullToRefresh } from '../composables/usePullToRefresh'; @@ -452,7 +452,6 @@ function goMatch(id: string) { .bet-page { margin: 0 -16px; padding-bottom: 8px; - background: var(--bg-body); } /* Tab panel toggle: avoid display:none to prevent browser releasing image resources */ @@ -772,16 +771,25 @@ function goMatch(id: string) { .state, .placeholder, .empty { + display: flex; + flex-direction: column; + align-items: center; text-align: center; color: var(--text-muted); - padding: 48px 20px; + padding: 34px 20px 44px; font-weight: 600; } .empty-icon { - width: 96px; - height: 96px; - margin-bottom: 14px; + width: min(270px, 82vw); + aspect-ratio: 16 / 9; + height: auto; + margin-bottom: 12px; + border-radius: 8px; + object-fit: cover; + opacity: 0.86; + mask-image: linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%); + -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%); } .placeholder { diff --git a/apps/player/src/views/MobileHomeView.vue b/apps/player/src/views/MobileHomeView.vue index 5ed7fa0..8d5713d 100644 --- a/apps/player/src/views/MobileHomeView.vue +++ b/apps/player/src/views/MobileHomeView.vue @@ -2,11 +2,11 @@ import { onActivated, computed, ref } from 'vue'; import { useRouter } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import emptyMatchesImg from '../assets/images/empty-matches.svg'; -import vsImg from '../assets/images/vs.png'; -import cardBg from '../assets/images/card-bg.webp'; +import emptyMatchesImg from '../assets/images/generated/empty-matches-stadium-v1.webp'; +import vsImg from '../assets/images/generated/vs-gold-alpha-v1.webp'; +import hotCardBg from '../assets/images/generated/match-card-bg-gold-stadium-v1.webp'; +import upcomingCardBg from '../assets/images/generated/match-card-bg-gold-tunnel-v1.webp'; import BannerCarousel from '../components/BannerCarousel.vue'; -import HomeAnnouncementCard from '../components/HomeAnnouncementCard.vue'; import { usePlayerHome } from '../composables/usePlayerHome'; import TeamEmblem from '../components/TeamEmblem.vue'; import GoldSpinner from '../components/GoldSpinner.vue'; @@ -16,7 +16,8 @@ import type { PlayerHomeMatch } from '../composables/usePlayerHome'; type HotTab = 'hot' | 'upcoming'; -const matchCardBg = `url(${cardBg})`; +const hotMatchCardBg = `url(${hotCardBg})`; +const upcomingMatchCardBg = `url(${upcomingCardBg})`; const { t, locale } = useI18n(); const router = useRouter(); const { banners, hotMatches, upcomingMatches, loading, load, announcementItems } = usePlayerHome(); @@ -54,10 +55,16 @@ function formatKickoff(startTime: string) { return formatLocalMatchDateTime(startTime, locale.value, { variant: 'full' }); } +function matchCardStyle(tab: HotTab) { + return { + '--match-card-bg': tab === 'hot' ? hotMatchCardBg : upcomingMatchCardBg, + }; +} + diff --git a/apps/player/src/views/MobileMatchDetailView.vue b/apps/player/src/views/MobileMatchDetailView.vue index eaccd87..57e389c 100644 --- a/apps/player/src/views/MobileMatchDetailView.vue +++ b/apps/player/src/views/MobileMatchDetailView.vue @@ -14,7 +14,7 @@ import CorrectScorePanel from '../components/match-detail/CorrectScorePanel.vue' import { isCorrectScoreMarket, parseScoreCode } from '../utils/correctScoreLayout'; import { useOnLocaleChange } from '../composables/useOnLocaleChange'; import { usePullToRefresh } from '../composables/usePullToRefresh'; -import vsImg from '../assets/images/vs.png'; +import vsImg from '../assets/images/generated/vs-gold-alpha-v1.webp'; import GoldSpinner from '../components/GoldSpinner.vue'; import { matchPhaseLabel, type MatchPhase } from '../utils/matchPhase'; import { formatLocalMatchDateTime } from '@thebet365/shared'; @@ -911,18 +911,36 @@ function onPickSelection(selId: string, marketId: string) { } .my-bets-list { - display: flex; - flex-direction: column; - gap: 8px; + display: grid; + grid-auto-flow: column; + grid-template-rows: repeat(2, minmax(72px, auto)); + grid-auto-columns: minmax(158px, calc((100% - 6px) / 2)); + gap: 6px; + overflow-x: auto; + overflow-y: hidden; + padding-bottom: 2px; + scroll-snap-type: x proximity; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; +} + +.my-bets-list::-webkit-scrollbar { + display: none; } .my-bet-card { background: #1c1c1c; border: 1px solid #2e2e2e; border-radius: 8px; - padding: 10px 12px; + padding: 8px 9px; cursor: pointer; transition: background 0.15s; + min-width: 0; + min-height: 72px; + display: flex; + flex-direction: column; + justify-content: space-between; + scroll-snap-align: start; } .my-bet-card:active { @@ -931,27 +949,29 @@ function onPickSelection(selId: string, marketId: string) { .bet-header { display: flex; + align-items: flex-start; justify-content: space-between; - align-items: center; + gap: 6px; margin-bottom: 6px; } .bet-type { - font-size: 12px; + font-size: 11px; font-weight: 700; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; - margin-right: 8px; + min-width: 0; } .bet-status { - font-size: 11px; + font-size: 10px; font-weight: 700; - padding: 2px 8px; + padding: 1px 6px; border-radius: 4px; + line-height: 1.5; flex-shrink: 0; } @@ -972,18 +992,30 @@ function onPickSelection(selId: string, marketId: string) { .bet-footer { display: flex; + align-items: flex-end; justify-content: space-between; - align-items: center; + gap: 6px; } .bet-stake { - font-size: 11px; + font-size: 10px; color: #888; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .bet-return { - font-size: 13px; + font-size: 12px; font-weight: 800; + flex-shrink: 0; +} + +@media (max-width: 340px) { + .my-bets-list { + grid-auto-columns: minmax(148px, calc((100% - 6px) / 2)); + } } diff --git a/apps/player/src/views/MobileRechargeDetailView.vue b/apps/player/src/views/MobileRechargeDetailView.vue index 20a6ed5..83f8e2d 100644 --- a/apps/player/src/views/MobileRechargeDetailView.vue +++ b/apps/player/src/views/MobileRechargeDetailView.vue @@ -281,13 +281,23 @@ function auditNoteDisplayText(remark: { kind: 'note'; text: string }) { .summary-card, .audit-card { - background: linear-gradient(135deg, #1a1810 0%, #1f1b0e 40%, #16140c 100%); + position: relative; + overflow: hidden; + background: + linear-gradient(rgba(22, 20, 15, 0.74), rgba(18, 16, 12, 0.84)), + url('../assets/images/generated/detail-ledger-ticket-bg-v1.webp') center / cover no-repeat; border: 1px solid rgba(212, 175, 55, 0.2); border-radius: 12px; padding: 16px; margin-bottom: 12px; } +.summary-card > *, +.audit-card > * { + position: relative; + z-index: 1; +} + .summary-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; } .method-badge { font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 4px; } .method-badge.bank { background: rgba(212,175,55,0.15); color: var(--primary-light); } diff --git a/apps/player/src/views/MobileWalletTransactionDetailView.vue b/apps/player/src/views/MobileWalletTransactionDetailView.vue index 65b13fe..a2f33bc 100644 --- a/apps/player/src/views/MobileWalletTransactionDetailView.vue +++ b/apps/player/src/views/MobileWalletTransactionDetailView.vue @@ -262,23 +262,31 @@ function goCashbackDetail() { gap: 6px; margin-bottom: 12px; text-align: center; - background: #141414; + background: + linear-gradient(rgba(22, 20, 15, 0.74), rgba(18, 16, 12, 0.84)), + url('../assets/images/generated/detail-ledger-ticket-bg-v1.webp') center / cover no-repeat; border: 1px solid #222; } .hero.pos { border-color: rgba(212, 175, 55, 0.25); - background: linear-gradient(135deg, rgba(212, 175, 55, 0.1), rgba(212, 175, 55, 0.03)); + background: + linear-gradient(135deg, rgba(212, 175, 55, 0.14), rgba(20, 16, 8, 0.76)), + url('../assets/images/generated/detail-ledger-ticket-bg-v1.webp') center / cover no-repeat; } .hero.neg { border-color: rgba(224, 80, 80, 0.2); - background: linear-gradient(135deg, rgba(224, 80, 80, 0.1), rgba(224, 80, 80, 0.03)); + background: + linear-gradient(135deg, rgba(224, 80, 80, 0.1), rgba(20, 12, 10, 0.78)), + url('../assets/images/generated/detail-ledger-ticket-bg-v1.webp') center / cover no-repeat; } .hero.zero { border-color: rgba(255, 255, 255, 0.08); - background: #141414; + background: + linear-gradient(rgba(22, 20, 15, 0.76), rgba(18, 16, 12, 0.84)), + url('../assets/images/generated/detail-ledger-ticket-bg-v1.webp') center / cover no-repeat; } .hero-type { @@ -314,13 +322,22 @@ function goCashbackDetail() { } .section { - background: #141414; + position: relative; + overflow: hidden; + background: + linear-gradient(rgba(22, 20, 15, 0.78), rgba(18, 16, 12, 0.86)), + url('../assets/images/generated/detail-ledger-ticket-bg-v1.webp') center / cover no-repeat; border: 1px solid #222; border-radius: 12px; padding: 14px 16px; margin-bottom: 10px; } +.section > * { + position: relative; + z-index: 1; +} + .section-title { font-size: 12px; font-weight: 800; diff --git a/apps/player/src/views/desktop/DesktopBetDetailView.vue b/apps/player/src/views/desktop/DesktopBetDetailView.vue index 4edd000..d57e2dd 100644 --- a/apps/player/src/views/desktop/DesktopBetDetailView.vue +++ b/apps/player/src/views/desktop/DesktopBetDetailView.vue @@ -228,12 +228,21 @@ function legResultClass(status?: string) { .detail-right { display: flex; flex-direction: column; gap: 16px; } .detail-card { - background: var(--desktop-sidebar-bg); + position: relative; + overflow: hidden; + background: + linear-gradient(rgba(22, 20, 15, 0.76), rgba(18, 16, 12, 0.82)), + url('../../assets/images/generated/detail-ledger-ticket-bg-v1.webp') center / cover no-repeat; border: 1px solid var(--desktop-border); border-radius: 12px; padding: 20px; } +.detail-card > * { + position: relative; + z-index: 1; +} + .card-title { font-size: 11px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 14px; padding-bottom: 10px; border-bottom: 1px solid var(--desktop-border); } /* Selection rows */ diff --git a/apps/player/src/views/desktop/DesktopFootballView.vue b/apps/player/src/views/desktop/DesktopFootballView.vue index e70f05f..6ae4f25 100644 --- a/apps/player/src/views/desktop/DesktopFootballView.vue +++ b/apps/player/src/views/desktop/DesktopFootballView.vue @@ -7,6 +7,7 @@ import { useDesktopParlayMatches, type DesktopParlayMatch } from '../../composab import { useOutrightEvents } from '../../composables/useOutrightEvents'; import MatchBetCard from '../../components/MatchBetCard.vue'; import DesktopOutrightEventCard from '../../components/desktop/DesktopOutrightEventCard.vue'; +import emptyMatchesImg from '../../assets/images/generated/empty-matches-stadium-v1.webp'; import { isAfterLocalTodayMatchWindow as isAfterTodayMatchWindow, isInLocalTodayMatchWindow as isInTodayMatchWindow, @@ -159,7 +160,7 @@ function closeModal() { async function refresh() { if (mainTab.value === 'outright') { - await loadOutrights({ silent: false }); + await loadOutrights({ silent: false, force: true }); return; } await loadParlayMatches(true); @@ -224,6 +225,7 @@ void loadParlayMatches(true);
+

{{ t('bet.no_matches') }}

@@ -254,6 +256,7 @@ void loadParlayMatches(true);
+

{{ outrightError || t('bet.no_outright') }}

{{ t('bet.no_outright_hint') }}

@@ -438,12 +441,31 @@ void loadParlayMatches(true); } .empty-state { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; text-align: center; padding: 40px 16px; color: var(--text-muted); font-size: 13px; } +.empty-state-art { + width: min(460px, 70%); + aspect-ratio: 16 / 9; + height: auto; + border-radius: 8px; + object-fit: cover; + opacity: 0.72; + mask-image: linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%); + -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%); +} + +.empty-state p { + margin: 0; +} + .empty-hint { margin-top: 8px; font-size: 12px; @@ -459,7 +481,8 @@ void loadParlayMatches(true); .outright-event-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(340px, 420px)); + justify-content: start; gap: 12px; } diff --git a/apps/player/src/views/desktop/DesktopHomeView.vue b/apps/player/src/views/desktop/DesktopHomeView.vue index 281b2cb..161d940 100644 --- a/apps/player/src/views/desktop/DesktopHomeView.vue +++ b/apps/player/src/views/desktop/DesktopHomeView.vue @@ -8,6 +8,8 @@ import AnnouncementMarquee from '../../components/AnnouncementMarquee.vue'; import { usePlayerHome } from '../../composables/usePlayerHome'; import TeamEmblem from '../../components/TeamEmblem.vue'; import GoldSpinner from '../../components/GoldSpinner.vue'; +import emptyMatchesImg from '../../assets/images/generated/empty-matches-stadium-v1.webp'; +import vsImg from '../../assets/images/generated/vs-gold-alpha-v1.webp'; import { formatLocalMatchDateTime } from '@thebet365/shared'; import type { PlayerHomeMatch } from '../../composables/usePlayerHome'; import { useDesktopBetPopover } from '../../composables/useDesktopBetPopover'; @@ -226,7 +228,9 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin /> {{ featuredMatch.homeTeamName }}
- + -
VS
+
+ VS +
{{ match.homeTeamName }}
-
VS
+
+ VS +
+

{{ t('home.no_matches') }}

@@ -405,7 +414,9 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin /> {{ match.homeTeamName }}
-
VS
+
+ VS +
{{ match.homeTeamName }}
-
VS
+
+ VS +
+

{{ t('home.upcoming_empty') }}

@@ -505,7 +519,9 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin
{{ m.homeTeamName }} - vs + + VS + {{ m.awayTeamName }}
@@ -520,7 +536,9 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin {{ m.homeTeamName }} -
VS
+
+ VS +
{{ m.awayTeamName }} @@ -623,7 +641,7 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin } .content-section { - background: rgba(20, 20, 20, 0.88); + background: rgba(32, 30, 24, 0.78); backdrop-filter: var(--blur-md); -webkit-backdrop-filter: var(--blur-md); border: 1px solid var(--border); @@ -685,22 +703,39 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin .loading-state, .empty-state { display: flex; + flex-direction: column; justify-content: center; align-items: center; + gap: 10px; min-height: 160px; color: var(--text-muted); } +.empty-state-art { + width: min(420px, 72%); + aspect-ratio: 16 / 9; + height: auto; + border-radius: 8px; + object-fit: cover; + opacity: 0.72; + mask-image: linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%); + -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%); +} + +.empty-state p { + margin: 0; +} + /* Featured 焦点大卡 */ .featured-match { display: flex; flex-direction: column; gap: var(--space-4); - padding: 18px 20px; + padding: 18px 20px 20px; border-radius: var(--radius-lg); background: - linear-gradient(135deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.04) 50%, rgba(15, 10, 0, 0.1)), - url('../../assets/images/card-bg.webp') center/cover no-repeat; + linear-gradient(90deg, rgba(0, 0, 0, 0.34), rgba(0, 0, 0, 0.12) 50%, rgba(10, 6, 0, 0.36)), + url('../../assets/images/generated/pc-featured-match-bg-v1.webp') center / 100% 100% no-repeat; border: 1px solid var(--border-gold); box-shadow: var(--shadow-gold-lg), inset 0 0 20px rgba(212, 175, 55, 0.06); margin-bottom: var(--space-4); @@ -711,6 +746,11 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin overflow: hidden; } +.featured-match > * { + position: relative; + z-index: 1; +} + .featured-match::before { content: ''; position: absolute; @@ -718,10 +758,30 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin width: 180px; height: 180px; background: radial-gradient(circle at top right, rgba(212, 175, 55, 0.2), transparent 60%); pointer-events: none; + z-index: 0; animation: goldGlowPulse 3.5s ease-in-out infinite; transform-origin: top right; } +.featured-match::after { + content: ''; + position: absolute; + inset: 0; + z-index: 2; + pointer-events: none; + background: url('../../assets/images/generated/pc-featured-match-bg-v1.webp') center / 100% 100% no-repeat; + mask: + linear-gradient(#000 0 0) top / 100% 22px no-repeat, + linear-gradient(#000 0 0) bottom / 100% 22px no-repeat, + linear-gradient(#000 0 0) left / 22px 100% no-repeat, + linear-gradient(#000 0 0) right / 22px 100% no-repeat; + -webkit-mask: + linear-gradient(#000 0 0) top / 100% 22px no-repeat, + linear-gradient(#000 0 0) bottom / 100% 22px no-repeat, + linear-gradient(#000 0 0) left / 22px 100% no-repeat, + linear-gradient(#000 0 0) right / 22px 100% no-repeat; +} + .featured-match:hover { transform: translateY(-2px); box-shadow: var(--glow-gold-lg), inset 0 0 24px rgba(212, 175, 55, 0.08); @@ -786,22 +846,35 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin } .featured-vs { - font-family: var(--font-display); - font-size: var(--text-2xl); - font-weight: 400; - font-style: italic; - color: var(--primary-light); - text-shadow: 0 0 12px rgba(212, 175, 55, 0.5); + width: 72px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; +} + +.featured-vs-img { + width: 68px; + max-width: 100%; + height: auto; + object-fit: contain; + filter: + drop-shadow(0 0 4px rgba(255, 231, 150, 0.42)) + drop-shadow(0 0 8px rgba(212, 175, 55, 0.26)); } .featured-markets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; - margin: 14px -20px -18px; - padding: 14px 20px 18px; - background: #000000; - border-top: 1px solid rgba(212, 175, 55, 0.2); + margin: 14px -12px 0; + padding: 14px 12px 8px; + background: rgba(10, 8, 4, 0.46); + border-top: 1px solid rgba(246, 210, 108, 0.22); + border-radius: 0 0 8px 8px; + box-shadow: inset 0 1px 0 rgba(255, 236, 174, 0.08); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); } .featured-market-block { @@ -947,15 +1020,20 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin } .vs-text { - font-size: 10px; - font-weight: 800; - color: var(--text-muted); - font-style: italic; + display: flex; + align-items: center; padding-left: 36px; - line-height: 1; margin: -2px 0; } +.vs-mini-img { + width: 30px; + height: auto; + object-fit: contain; + opacity: 0.78; + filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.75)); +} + .team-row { display: flex; align-items: center; @@ -1124,10 +1202,17 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin } .sri-vs { - color: var(--text-muted); - font-size: 9px; - font-weight: 600; - font-style: italic; + display: inline-flex; + align-items: center; + flex-shrink: 0; +} + +.sri-vs-img { + width: 22px; + height: auto; + object-fit: contain; + opacity: 0.7; + filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.7)); } .sri-time { @@ -1309,19 +1394,26 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin } .hover-vs-glow { - font-size: 18px; - font-weight: 900; - font-style: italic; - color: var(--primary-light); + width: 58px; + height: 38px; + display: flex; + align-items: center; + justify-content: center; transform: scale(2.8); opacity: 0; - text-shadow: - 0 0 10px rgba(212, 175, 55, 0.8), - 0 0 20px rgba(212, 175, 55, 0.4); transition: transform 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275) 0.1s, opacity 0.3s ease 0.1s; flex-shrink: 0; } +.hover-vs-img { + width: 56px; + height: auto; + object-fit: contain; + filter: + drop-shadow(0 0 5px rgba(255, 231, 150, 0.52)) + drop-shadow(0 0 9px rgba(212, 175, 55, 0.34)); +} + .match-card:is(:hover, .match-card--engaged) .hover-vs-glow { transform: scale(1); opacity: 1; @@ -1330,11 +1422,9 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin @keyframes vs-clash-impact { 0% { - text-shadow: 0 0 25px rgba(212, 175, 55, 1); transform: scale(1.25); } 100% { - text-shadow: 0 0 8px rgba(212, 175, 55, 0.7); transform: scale(1); } } @@ -1471,17 +1561,26 @@ function getHoveredSideForMatch(match: PlayerHomeMatch | null, cardRootId: strin } .sri-preview-vs-text { - font-size: 16px; - font-weight: 900; - font-style: italic; - color: var(--primary-light); - text-shadow: 0 0 8px rgba(212, 175, 55, 0.6); + width: 48px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; flex-shrink: 0; opacity: 0; transform: scale(2.2); transition: opacity 0.3s ease 0.1s, transform 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275) 0.1s; } +.sri-preview-vs-img { + width: 46px; + height: auto; + object-fit: contain; + filter: + drop-shadow(0 0 4px rgba(255, 231, 150, 0.42)) + drop-shadow(0 0 8px rgba(212, 175, 55, 0.26)); +} + .side-recommend-item:hover .sri-preview-vs-text { opacity: 1; transform: scale(1); diff --git a/apps/player/src/views/desktop/DesktopMatchDetailView.vue b/apps/player/src/views/desktop/DesktopMatchDetailView.vue index 6b19b7d..179c178 100644 --- a/apps/player/src/views/desktop/DesktopMatchDetailView.vue +++ b/apps/player/src/views/desktop/DesktopMatchDetailView.vue @@ -16,6 +16,7 @@ import { formatLocalMatchDateTime, defaultMarketName, DETAIL_MARKET_TYPES } from import { resolveSelectionLabel } from '../../utils/selectionLabel'; import { useDesktopBetPopover } from '../../composables/useDesktopBetPopover'; import { useDesktopBetLocate, scrollToBetSelection } from '../../composables/useDesktopBetLocate'; +import vsImg from '../../assets/images/generated/vs-gold-alpha-v1.webp'; const route = useRoute(); const router = useRouter(); @@ -360,7 +361,9 @@ function getHoveredSide() { - {{ match.score?.ftAway }}
-
VS
+
+ VS +
{{ kickoff }} @@ -537,13 +540,15 @@ function getHoveredSide() { } .match-score-band { - background: - linear-gradient(135deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.04) 50%, rgba(15, 10, 0, 0.1)), - url('../../assets/images/card-bg.webp') center/cover no-repeat; - border: 1px solid var(--border-gold); - box-shadow: var(--shadow-gold), inset 0 0 20px rgba(212, 175, 55, 0.06); - border-radius: 12px; - padding: 18px 24px; + aspect-ratio: 1600 / 320; + background-image: url('../../assets/images/generated/pc-featured-match-bg-v1.webp'); + background-position: center; + background-repeat: no-repeat; + background-size: 100% 100%; + border: 0; + box-shadow: var(--shadow-gold); + border-radius: 4px; + padding: 12px 24px; overflow: hidden; display: flex; align-items: center; @@ -595,9 +600,17 @@ function getHoveredSide() { } .versus-row.vs { - font-size: 22px; - font-weight: 800; - color: var(--text-muted); + min-height: 42px; + justify-content: center; +} + +.score-vs-img { + width: 72px; + height: auto; + object-fit: contain; + filter: + drop-shadow(0 0 4px rgba(255, 231, 150, 0.42)) + drop-shadow(0 0 8px rgba(212, 175, 55, 0.26)); } .kickoff-time { diff --git a/apps/player/src/views/desktop/DesktopOutrightDetailView.vue b/apps/player/src/views/desktop/DesktopOutrightDetailView.vue index 4732060..152816e 100644 --- a/apps/player/src/views/desktop/DesktopOutrightDetailView.vue +++ b/apps/player/src/views/desktop/DesktopOutrightDetailView.vue @@ -8,11 +8,11 @@ import OutrightOptionCard from '../../components/outright/OutrightOptionCard.vue import OutrightBetModal, { type OutrightPick } from '../../components/outright/OutrightBetModal.vue'; import { useAuthStore } from '../../stores/auth'; import GoldSpinner from '../../components/GoldSpinner.vue'; -import saishiImg from '../../assets/images/saishi.webp'; -import cardBg from '../../assets/images/card-bg.webp'; +import saishiImg from '../../assets/images/generated/saishi-gold-trophy-v1.webp'; +import outrightDetailBg from '../../assets/images/generated/pc-outright-detail-bg-v1.webp'; import { useDesktopBetLocate, scrollToBetSelection } from '../../composables/useDesktopBetLocate'; -const matchCardBg = `url(${cardBg})`; +const matchCardBg = `url(${outrightDetailBg})`; const ODDS_POLL_MS = 15000; const route = useRoute(); @@ -246,7 +246,7 @@ onUnmounted(stopPolling); position: absolute; inset: 0; background: v-bind(matchCardBg) center / cover no-repeat; - opacity: 0.22; + opacity: 0.36; pointer-events: none; } diff --git a/apps/player/src/views/desktop/DesktopProfileView.vue b/apps/player/src/views/desktop/DesktopProfileView.vue index b9a21c4..2bf1e9e 100644 --- a/apps/player/src/views/desktop/DesktopProfileView.vue +++ b/apps/player/src/views/desktop/DesktopProfileView.vue @@ -10,7 +10,7 @@ import { useAppLocale } from '../../composables/useAppLocale'; import { usePlayerProfile } from '../../composables/usePlayerProfile'; import { formatMoney } from '../../utils/localeDisplay'; import { parseCashbackApiData, sumCashbackAmount } from '../../utils/cashback'; -import walletBg from '../../assets/images/wallet-bg.webp'; +import walletBg from '../../assets/images/generated/pc-wallet-banner-v1.webp'; const { t, locale } = useI18n(); const router = useRouter(); @@ -223,6 +223,12 @@ const quickLinks = computed(() => [ overflow-y: auto; height: auto; max-height: 100%; + -ms-overflow-style: none; + scrollbar-width: none; +} + +.profile-page::-webkit-scrollbar { + display: none; } .page-title { @@ -306,8 +312,8 @@ const quickLinks = computed(() => [ display: block; object-fit: cover; object-position: center center; - transform: scale(1.05); - filter: brightness(0.86); + transform: scale(1.01); + filter: brightness(0.94); pointer-events: none; user-select: none; } diff --git a/apps/player/src/views/desktop/DesktopRechargeDetailView.vue b/apps/player/src/views/desktop/DesktopRechargeDetailView.vue index de9bd3f..2862663 100644 --- a/apps/player/src/views/desktop/DesktopRechargeDetailView.vue +++ b/apps/player/src/views/desktop/DesktopRechargeDetailView.vue @@ -292,12 +292,21 @@ function auditNoteDisplayText(remark: { kind: 'note'; text: string }) { .detail-layout { display: flex; flex-direction: column; gap: 16px; max-width: 680px; } .detail-card { - background: var(--desktop-sidebar-bg); + position: relative; + overflow: hidden; + background: + linear-gradient(rgba(22, 20, 15, 0.76), rgba(18, 16, 12, 0.82)), + url('../../assets/images/generated/detail-ledger-ticket-bg-v1.webp') center / cover no-repeat; border: 1px solid var(--desktop-border); border-radius: 12px; padding: 20px; } +.detail-card > * { + position: relative; + z-index: 1; +} + .header-card { display: flex; flex-direction: column; gap: 12px; } .order-no { diff --git a/apps/player/src/views/desktop/DesktopWalletTransactionDetailView.vue b/apps/player/src/views/desktop/DesktopWalletTransactionDetailView.vue index 545d613..3c0ddf5 100644 --- a/apps/player/src/views/desktop/DesktopWalletTransactionDetailView.vue +++ b/apps/player/src/views/desktop/DesktopWalletTransactionDetailView.vue @@ -166,12 +166,21 @@ function txLabel(tx: TxDetail): string { } .detail-card { - background: var(--desktop-sidebar-bg); + position: relative; + overflow: hidden; + background: + linear-gradient(rgba(22, 20, 15, 0.76), rgba(18, 16, 12, 0.82)), + url('../../assets/images/generated/detail-ledger-ticket-bg-v1.webp') center / cover no-repeat; border: 1px solid var(--desktop-border); border-radius: 12px; padding: 28px; } +.detail-card > * { + position: relative; + z-index: 1; +} + /* Hero card (left) */ .hero-card { display: flex;