feat: 前台匿名浏览、登录引导、客服入口与返水增强

前台:
- 未登录可浏览首页/赛事/赔率,下注等操作弹出登录引导(去登录/继续浏览)
- 顶部新增客服入口与 iframe 弹窗
- 登录页支持暂不登录返回浏览

API:
- 首页/赛事/冠军盘接口改为公开访问,支持 X-Locale 头
- JWT 守卫支持可选认证

返水:
- 注单新增 is_cashbacked 字段,发放时自动标记
- 预览展示玩家余额,明确平台直发不从代理扣款
- 后台注单列表与玩家历史展示回水状态

其他:
- 串关禁止同场重复选号(SAME_MATCH)
- 补充结算资金流分析文档

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-11 09:36:44 +08:00
parent 785fa4416d
commit 844727c82e
35 changed files with 1007 additions and 49 deletions

View File

@@ -0,0 +1,154 @@
<script setup lang="ts">
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useAuthStore } from '../stores/auth';
const { t } = useI18n();
const auth = useAuthStore();
const router = useRouter();
function goLogin() {
const redirect = auth.loginReturnTo || undefined;
auth.hideLoginPrompt();
router.push({
path: '/login',
query: redirect ? { redirect } : {},
});
}
function continueBrowsing() {
auth.loginReturnTo = '';
auth.hideLoginPrompt();
}
</script>
<template>
<Teleport to="body">
<Transition name="fade">
<div v-if="auth.loginPromptVisible" class="login-overlay" @click.self="continueBrowsing">
<div class="login-modal">
<button type="button" class="close-btn" :aria-label="t('auth.continue_browsing')" @click="continueBrowsing">
</button>
<h2 class="login-title">{{ t('auth.login_required') }}</h2>
<p class="login-hint">{{ t('auth.login_hint') }}</p>
<div class="login-actions">
<button type="button" class="login-submit" @click="goLogin">
{{ t('auth.go_login') }}
</button>
<button type="button" class="login-secondary" @click="continueBrowsing">
{{ t('auth.continue_browsing') }}
</button>
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
<style scoped>
.login-overlay {
position: fixed;
inset: 0;
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
}
.login-modal {
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;
}
.close-btn {
position: absolute;
top: 12px;
right: 14px;
background: none;
border: none;
color: #666;
font-size: 18px;
cursor: pointer;
padding: 4px;
line-height: 1;
}
.close-btn:hover {
color: #aaa;
}
.login-title {
font-size: 18px;
font-weight: 800;
color: var(--primary-light, #c8a84e);
margin: 0 0 6px;
text-align: center;
}
.login-hint {
font-size: 12px;
color: #888;
text-align: center;
margin: 0 0 24px;
}
.login-actions {
display: flex;
flex-direction: column;
gap: 10px;
}
.login-submit {
padding: 12px;
border-radius: 8px;
border: none;
background: linear-gradient(135deg, #d4a017, #e8c84a);
color: #1a1a1a;
font-size: 15px;
font-weight: 800;
cursor: pointer;
min-height: 44px;
transition: opacity 0.2s;
}
.login-submit:active {
opacity: 0.85;
}
.login-secondary {
padding: 10px;
border-radius: 8px;
border: 1px solid #333;
background: transparent;
color: #888;
font-size: 14px;
font-weight: 600;
cursor: pointer;
min-height: 40px;
transition: color 0.2s, border-color 0.2s;
}
.login-secondary:active {
color: #aaa;
border-color: #444;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.25s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>