feat: 前台匿名浏览、登录引导、客服入口与返水增强
前台: - 未登录可浏览首页/赛事/赔率,下注等操作弹出登录引导(去登录/继续浏览) - 顶部新增客服入口与 iframe 弹窗 - 登录页支持暂不登录返回浏览 API: - 首页/赛事/冠军盘接口改为公开访问,支持 X-Locale 头 - JWT 守卫支持可选认证 返水: - 注单新增 is_cashbacked 字段,发放时自动标记 - 预览展示玩家余额,明确平台直发不从代理扣款 - 后台注单列表与玩家历史展示回水状态 其他: - 串关禁止同场重复选号(SAME_MATCH) - 补充结算资金流分析文档 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import LocaleSwitcher from '../components/LocaleSwitcher.vue';
|
||||
import { useAppLocale } from '../composables/useAppLocale';
|
||||
import AnnouncementMarquee from '../components/AnnouncementMarquee.vue';
|
||||
import BottomNavIcon from '../components/BottomNavIcon.vue';
|
||||
import CustomerServiceModal from '../components/CustomerServiceModal.vue';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { usePlayerHome } from '../composables/usePlayerHome';
|
||||
import { usePlayerProfile } from '../composables/usePlayerProfile';
|
||||
@@ -44,6 +45,7 @@ const { announcements, load: loadPlayerHome } = usePlayerHome();
|
||||
const { loadProfile } = usePlayerProfile();
|
||||
const mainRef = ref<HTMLElement | null>(null);
|
||||
const tabScrollTops = new Map<string, number>();
|
||||
const customerServiceOpen = ref(false);
|
||||
|
||||
watch(locale, (next, prev) => {
|
||||
if (prev && next !== prev) void loadPlayerHome(true);
|
||||
@@ -66,8 +68,10 @@ onMounted(() => {
|
||||
watch(
|
||||
() => auth.token,
|
||||
(token) => {
|
||||
// 首页数据(公告、热门赛事)对所有人公开,始终加载
|
||||
void loadPlayerHome();
|
||||
// 个人资料仅登录用户需要
|
||||
if (token) {
|
||||
void loadPlayerHome();
|
||||
void loadProfile();
|
||||
}
|
||||
},
|
||||
@@ -80,9 +84,34 @@ watch(
|
||||
<header v-if="showHeader" class="header">
|
||||
<img src="/logo.png" alt="TheBet365" class="logo" />
|
||||
<div class="header-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="support-btn"
|
||||
:aria-label="t('support.open')"
|
||||
@click="customerServiceOpen = true"
|
||||
>
|
||||
<svg class="support-icon" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
d="M12 3C7.03 3 3 6.58 3 11c0 2.02.9 3.86 2.38 5.24L4 21l4.2-1.02A10.8 10.8 0 0 0 12 19c4.97 0 9-3.58 9-8s-4.03-8-9-8Z"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.8"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<circle cx="9" cy="11" r="1" fill="currentColor" />
|
||||
<circle cx="12" cy="11" r="1" fill="currentColor" />
|
||||
<circle cx="15" cy="11" r="1" fill="currentColor" />
|
||||
</svg>
|
||||
<span class="support-label">{{ t('support.short') }}</span>
|
||||
</button>
|
||||
<LocaleSwitcher />
|
||||
<CashBalanceChip v-if="auth.user" />
|
||||
<UserAvatarMenu v-if="auth.user" />
|
||||
<template v-if="auth.user">
|
||||
<CashBalanceChip />
|
||||
<UserAvatarMenu />
|
||||
</template>
|
||||
<button v-else type="button" class="login-btn" @click="auth.showLoginPrompt(route.fullPath)">
|
||||
{{ t('auth.login') }}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -127,6 +156,7 @@ watch(
|
||||
</nav>
|
||||
|
||||
<BetSlipDrawer v-model="slip.drawerOpen" />
|
||||
<CustomerServiceModal v-model="customerServiceOpen" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -168,6 +198,55 @@ watch(
|
||||
.header-actions :deep(.avatar-btn) {
|
||||
width: var(--header-chip-h);
|
||||
}
|
||||
|
||||
.support-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
height: var(--header-chip-h, 36px);
|
||||
padding: 0 10px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border-gold-soft, rgba(200, 168, 78, 0.25));
|
||||
background: rgba(200, 168, 78, 0.08);
|
||||
color: var(--primary-light, #c8a84e);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.support-btn:active {
|
||||
background: rgba(200, 168, 78, 0.16);
|
||||
}
|
||||
|
||||
.support-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.support-label {
|
||||
max-width: 48px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--primary, #c8a84e);
|
||||
background: transparent;
|
||||
color: var(--primary-light, #c8a84e);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.login-btn:active {
|
||||
background: rgba(200, 168, 78, 0.15);
|
||||
}
|
||||
.announce-strip {
|
||||
flex-shrink: 0;
|
||||
z-index: 105;
|
||||
|
||||
Reference in New Issue
Block a user