-
-
-
+
+
+
+ ✓
+
+ {{ validated ? t('auth.verified') : t('auth.click_to_verify') }}
+
+
+
{{ errorMsg }}
+
+
+
+
+
+
+
+
+
diff --git a/apps/player/src/components/UserAvatarMenu.vue b/apps/player/src/components/UserAvatarMenu.vue
index 29597f0..8a2aee4 100644
--- a/apps/player/src/components/UserAvatarMenu.vue
+++ b/apps/player/src/components/UserAvatarMenu.vue
@@ -45,6 +45,11 @@ function goEdit() {
router.push('/profile/edit');
}
+function goRecharge() {
+ close();
+ router.push('/wallet/recharge');
+}
+
function logout() {
close();
auth.logout();
@@ -60,6 +65,7 @@ function logout() {
@@ -139,6 +145,11 @@ function logout() {
background: rgba(255, 255, 255, 0.04);
}
+.menu-item.recharge {
+ color: var(--primary-light);
+ font-weight: 700;
+}
+
.menu-item.danger {
color: var(--danger);
}
diff --git a/apps/player/src/layouts/MainLayout.vue b/apps/player/src/layouts/MainLayout.vue
index ff5ee83..1bdfff6 100644
--- a/apps/player/src/layouts/MainLayout.vue
+++ b/apps/player/src/layouts/MainLayout.vue
@@ -188,6 +188,7 @@ watch(
display: flex;
gap: 6px;
align-items: center;
+ min-width: 0;
}
.header-actions :deep(.locale-switch:not(.compact)),
.header-actions :deep(.cash-chip),
@@ -213,6 +214,8 @@ watch(
font-weight: 700;
cursor: pointer;
transition: background 0.2s;
+ flex-shrink: 1;
+ min-width: 0;
}
.support-btn:active {
diff --git a/apps/player/src/main.ts b/apps/player/src/main.ts
index aac7029..98e3a6b 100644
--- a/apps/player/src/main.ts
+++ b/apps/player/src/main.ts
@@ -70,16 +70,26 @@ const i18n = createI18n({
},
auth: {
login: '登录',
+ register: '注册账号',
logout: '退出登录',
username: '账号',
password: '密码',
+ invite_code: '邀请码',
+ optional: '选填',
captcha_placeholder: 'Captcha',
captcha_refresh: '点击换一张',
- captcha_wrong: '验证码错误',
+ captcha_wrong: '请完成滑块验证',
+ slide_to_verify: '向右滑动完成验证',
+ click_to_verify: '点击验证',
+ verified: '验证成功',
login_required: '请先登录',
login_hint: '登录后可下注及访问更多功能',
go_login: '去登录',
- continue_browsing: '暂不登录,继续浏览',
+ go_register: '没有账号?立即注册',
+ have_account: '已有账号?去登录',
+ register_btn: '注册',
+ register_failed: '注册失败,请重试',
+ continue_browsing: '暂不登录',
username_placeholder: '请输入账号',
password_placeholder: '请输入密码',
login_btn: '登录',
@@ -99,7 +109,7 @@ const i18n = createI18n({
unsettled: '未结算',
available: '可用',
no_records: '暂无账单记录',
- tx_deposit: '人工存款',
+ tx_deposit: '充值',
tx_withdraw: '人工提款',
tx_adjust: '人工调整',
tx_bet_freeze: '投注冻结',
@@ -116,7 +126,7 @@ const i18n = createI18n({
stats_net: '净额',
stats_cashback: '反水',
filter_all: '全部',
- filter_deposit: '存款',
+ filter_deposit: '充值',
filter_withdraw: '提款',
filter_bet: '投注',
filter_cashback: '反水',
@@ -135,7 +145,7 @@ const i18n = createI18n({
detail_tx_id: '流水号',
detail_not_found: '账单不存在',
ref_bet: '投注',
- ref_deposit: '存款',
+ ref_deposit: '充值',
ref_withdraw: '提款',
view_cashbacks: '返水明细',
view_cashbacks_detail: '查看返水周期明细',
@@ -143,6 +153,42 @@ const i18n = createI18n({
detail_cashback_link: '查看返水明细',
ref_cashback: '返水批次',
},
+ recharge: {
+ title: '充值',
+ history: '记录',
+ history_title: '充值记录',
+ bank_transfer: '银行转账',
+ bank_name: '银行名称',
+ account_holder: '账户名',
+ account_number: '账号',
+ usdt_address: 'USDT 地址',
+ amount_label: '充值金额',
+ amount_placeholder: '请输入充值金额',
+ screenshot_label: '上传转账截图',
+ upload_hint: '点击上传截图(最大 5MB)',
+ compressing: '压缩中',
+ submit: '提交充值',
+ submitting: '提交中',
+ submitted: '充值已提交',
+ pending_review: '管理员正在审核,请耐心等待',
+ new_recharge: '继续充值',
+ no_methods: '暂无可用充值方式',
+ select_method: '请选择充值方式',
+ enter_amount: '请输入充值金额',
+ upload_screenshot: '请上传转账截图',
+ submit_failed: '提交失败,请重试',
+ file_must_be_image: '请上传图片文件',
+ file_too_large: '文件不能超过 10MB',
+ status_pending: '审核中',
+ status_approved: '已通过',
+ status_rejected: '已拒绝',
+ no_orders: '暂无充值记录',
+ credited: '实际到账',
+ reject_reason: '拒绝原因',
+ apply_time: '申请时间',
+ review_time: '审核时间',
+ remark: '审核备注',
+ },
cashback: {
title: '返水明细',
list_title: '发放明细',
@@ -398,16 +444,26 @@ const i18n = createI18n({
},
auth:
{ login: 'Login',
+ register: 'Create Account',
logout: 'Log out',
username: 'Username',
password: 'Password',
+ invite_code: 'Invitation Code',
+ optional: 'Optional',
captcha_placeholder: 'Captcha',
captcha_refresh: 'Click to refresh',
- captcha_wrong: 'Invalid captcha',
+ captcha_wrong: 'Please complete the slider verification',
+ slide_to_verify: 'Slide to verify',
+ click_to_verify: 'Click to verify',
+ verified: 'Verified',
login_required: 'Login Required',
login_hint: 'Log in to place bets and access more features',
go_login: 'Go to login',
- continue_browsing: 'Continue browsing',
+ go_register: 'No account? Register now',
+ have_account: 'Already have an account? Log in',
+ register_btn: 'Register',
+ register_failed: 'Registration failed, please try again',
+ continue_browsing: 'Skip login',
username_placeholder: 'Enter username',
password_placeholder: 'Enter password',
login_btn: 'Log In',
@@ -471,6 +527,42 @@ const i18n = createI18n({
ref_cashback: 'Cashback batch',
detail_cashback_link: 'View cashback details',
},
+ recharge: {
+ title: 'Recharge',
+ history: 'History',
+ history_title: 'Recharge History',
+ bank_transfer: 'Bank Transfer',
+ bank_name: 'Bank Name',
+ account_holder: 'Account Holder',
+ account_number: 'Account Number',
+ usdt_address: 'USDT Address',
+ amount_label: 'Amount',
+ amount_placeholder: 'Enter recharge amount',
+ screenshot_label: 'Upload Screenshot',
+ upload_hint: 'Click to upload screenshot (max 5MB)',
+ compressing: 'Compressing',
+ submit: 'Submit',
+ submitting: 'Submitting',
+ submitted: 'Recharge Submitted',
+ pending_review: 'Admin is reviewing, please wait',
+ new_recharge: 'New Recharge',
+ no_methods: 'No payment methods available',
+ select_method: 'Please select a payment method',
+ enter_amount: 'Please enter the amount',
+ upload_screenshot: 'Please upload a screenshot',
+ submit_failed: 'Submit failed, please retry',
+ file_must_be_image: 'Please upload an image file',
+ file_too_large: 'File exceeds 10MB',
+ status_pending: 'Pending',
+ status_approved: 'Approved',
+ status_rejected: 'Rejected',
+ no_orders: 'No recharge records',
+ credited: 'Credited',
+ reject_reason: 'Rejection reason',
+ apply_time: 'Apply time',
+ review_time: 'Review time',
+ remark: 'Remark',
+ },
cashback: {
title: 'Cashback Details',
list_title: 'Payout details',
@@ -732,16 +824,26 @@ const i18n = createI18n({
},
auth: {
login: 'Log Masuk',
+ register: 'Daftar Akaun',
logout: 'Log Keluar',
username: 'Nama Pengguna',
password: 'Kata Laluan',
+ invite_code: 'Kod Jemputan',
+ optional: 'Pilihan',
captcha_placeholder: 'Captcha',
captcha_refresh: 'Klik untuk muat semula',
- captcha_wrong: 'Kod pengesahan salah',
+ captcha_wrong: 'Sila lengkapkan pengesahan gelongsor',
+ slide_to_verify: 'Gelongsor untuk mengesahkan',
+ click_to_verify: 'Klik untuk mengesahkan',
+ verified: 'Disahkan',
login_required: 'Sila Log Masuk',
login_hint: 'Log masuk untuk bertaruh dan akses lebih banyak ciri',
go_login: 'Pergi log masuk',
- continue_browsing: 'Teruskan melayari',
+ go_register: 'Tiada akaun? Daftar sekarang',
+ have_account: 'Sudah ada akaun? Log masuk',
+ register_btn: 'Daftar',
+ register_failed: 'Pendaftaran gagal, sila cuba lagi',
+ continue_browsing: 'Langkau log masuk',
username_placeholder: 'Masukkan nama pengguna',
password_placeholder: 'Masukkan kata laluan',
login_btn: 'Log Masuk',
@@ -805,6 +907,42 @@ const i18n = createI18n({
ref_cashback: 'Batch rebat',
detail_cashback_link: 'Lihat butiran rebat',
},
+ recharge: {
+ title: 'Topup',
+ history: 'Sejarah',
+ history_title: 'Sejarah Topup',
+ bank_transfer: 'Pindahan Bank',
+ bank_name: 'Nama Bank',
+ account_holder: 'Pemegang Akaun',
+ account_number: 'Nombor Akaun',
+ usdt_address: 'Alamat USDT',
+ amount_label: 'Jumlah',
+ amount_placeholder: 'Masukkan jumlah topup',
+ screenshot_label: 'Muat Naik Screenshot',
+ upload_hint: 'Klik untuk muat naik (maks 5MB)',
+ compressing: 'Memampat',
+ submit: 'Hantar',
+ submitting: 'Menghantar',
+ submitted: 'Topup Dihantar',
+ pending_review: 'Admin sedang menyemak, sila tunggu',
+ new_recharge: 'Topup Baru',
+ no_methods: 'Tiada kaedah pembayaran tersedia',
+ select_method: 'Sila pilih kaedah pembayaran',
+ enter_amount: 'Sila masukkan jumlah',
+ upload_screenshot: 'Sila muat naik screenshot',
+ submit_failed: 'Gagal, sila cuba lagi',
+ file_must_be_image: 'Sila muat naik fail imej',
+ file_too_large: 'Fail melebihi 10MB',
+ status_pending: 'Menunggu',
+ status_approved: 'Diluluskan',
+ status_rejected: 'Ditolak',
+ no_orders: 'Tiada rekod topup',
+ credited: 'Dikreditkan',
+ reject_reason: 'Sebab penolakan',
+ apply_time: 'Masa permohonan',
+ review_time: 'Masa semakan',
+ remark: 'Catatan',
+ },
cashback: {
title: 'Butiran Rebat',
list_title: 'Butiran pembayaran',
diff --git a/apps/player/src/router/index.ts b/apps/player/src/router/index.ts
index e5f7d10..0333502 100644
--- a/apps/player/src/router/index.ts
+++ b/apps/player/src/router/index.ts
@@ -5,6 +5,7 @@ const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/login', component: () => import('../views/LoginView.vue') },
+ { path: '/register', component: () => import('../views/RegisterView.vue') },
{
path: '/',
component: () => import('../layouts/MainLayout.vue'),
@@ -20,6 +21,8 @@ const router = createRouter({
{ path: 'wallet', component: () => import('../views/WalletView.vue'), meta: { keepAlive: true, requiresAuth: true } },
{ path: 'wallet/detail', component: () => import('../views/WalletDetailView.vue'), meta: { requiresAuth: true } },
{ path: 'wallet/cashbacks', component: () => import('../views/CashbackRecordsView.vue'), meta: { requiresAuth: true } },
+ { path: 'wallet/recharge', component: () => import('../views/RechargeView.vue'), meta: { requiresAuth: true } },
+ { path: 'wallet/recharge/history', component: () => import('../views/RechargeHistoryView.vue'), meta: { requiresAuth: true } },
{ path: 'wallet/transactions/:transactionId', component: () => import('../views/WalletTransactionDetailView.vue'), meta: { requiresAuth: true } },
{ path: 'profile', component: () => import('../views/ProfileView.vue'), meta: { keepAlive: true, requiresAuth: true } },
{ path: 'profile/cashbacks', component: () => import('../views/CashbackRecordsView.vue'), meta: { requiresAuth: true } },
@@ -31,7 +34,7 @@ const router = createRouter({
router.beforeEach((to) => {
const auth = useAuthStore();
- if (to.path === '/login' && auth.token) return '/';
+ if ((to.path === '/login' || to.path === '/register') && auth.token) return '/';
// 需要登录的页面 — 未登录时弹出登录提示,留在当前页
if (to.meta.requiresAuth && !auth.token) {
auth.showLoginPrompt(to.fullPath);
diff --git a/apps/player/src/stores/auth.ts b/apps/player/src/stores/auth.ts
index 05c562c..256a5e2 100644
--- a/apps/player/src/stores/auth.ts
+++ b/apps/player/src/stores/auth.ts
@@ -32,6 +32,23 @@ export const useAuthStore = defineStore('auth', () => {
return returnTo;
}
+ async function register(username: string, password: string, inviteCode?: string) {
+ const locale = localStorage.getItem('locale') || 'zh-CN';
+ const code = inviteCode?.trim();
+ const { data } = await api.post('/player/auth/register', {
+ username,
+ password,
+ locale,
+ ...(code ? { inviteCode: code } : {}),
+ });
+ token.value = data.data.token;
+ user.value = data.data.user;
+ localStorage.setItem('token', token.value);
+ localStorage.setItem('user', JSON.stringify(user.value));
+ loginReturnTo.value = '';
+ loginPromptVisible.value = false;
+ }
+
function logout() {
token.value = '';
user.value = null;
@@ -40,7 +57,7 @@ export const useAuthStore = defineStore('auth', () => {
}
return {
- token, user, login, logout,
+ token, user, login, register, logout,
loginPromptVisible, loginReturnTo,
showLoginPrompt, hideLoginPrompt,
};
diff --git a/apps/player/src/utils/walletTx.ts b/apps/player/src/utils/walletTx.ts
index 8fc3146..61dbe90 100644
--- a/apps/player/src/utils/walletTx.ts
+++ b/apps/player/src/utils/walletTx.ts
@@ -16,6 +16,7 @@ export const TX_KEY_MAP: Record
= {
RESETTLE_REVERSE: 'wallet.tx_resettle',
DEPOSIT: 'wallet.tx_deposit',
WITHDRAW: 'wallet.tx_withdraw',
+ PLAYER_DEPOSIT: 'wallet.tx_deposit',
};
export function txTypeKey(type: string): string {
diff --git a/apps/player/src/views/LoginView.vue b/apps/player/src/views/LoginView.vue
index c0a7fc9..b4dd3c2 100644
--- a/apps/player/src/views/LoginView.vue
+++ b/apps/player/src/views/LoginView.vue
@@ -51,6 +51,13 @@ function continueBrowsing() {
const target = isGuestBrowsablePath(redirect) ? redirect || '/' : '/';
router.replace(target);
}
+
+function goRegister() {
+ router.push({
+ path: '/register',
+ query: route.query.redirect ? { redirect: route.query.redirect as string } : {},
+ });
+}
@@ -68,10 +75,13 @@ function continueBrowsing() {
-