diff --git a/apps/player/src/components/desktop/DesktopAuthLayout.vue b/apps/player/src/components/desktop/DesktopAuthLayout.vue new file mode 100644 index 0000000..401c2ac --- /dev/null +++ b/apps/player/src/components/desktop/DesktopAuthLayout.vue @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/player/src/components/match-detail/CorrectScorePanel.vue b/apps/player/src/components/match-detail/CorrectScorePanel.vue index 1a1d477..4c1a727 100644 --- a/apps/player/src/components/match-detail/CorrectScorePanel.vue +++ b/apps/player/src/components/match-detail/CorrectScorePanel.vue @@ -2,6 +2,7 @@ import { computed } from 'vue'; import { useI18n } from 'vue-i18n'; import { groupCorrectScoreSelections, type CsSelection } from '../../utils/correctScoreLayout'; +import { useDesktopBetPopover } from '../../composables/useDesktopBetPopover'; const props = defineProps<{ marketType: string; @@ -24,6 +25,7 @@ const emit = defineEmits<{ }>(); const { t } = useI18n(); +const { cancelClose } = useDesktopBetPopover(); const columns = computed(() => groupCorrectScoreSelections( @@ -48,7 +50,11 @@ function formatOdds(odds: string) { - + {{ t('bet.col_home') }} {{ t('bet.col_draw') }} @@ -65,6 +71,7 @@ function formatOdds(odds: string) { :class="{ selected: isSelected(sel.id), 'score-card--locked': locked }" :data-bet-selection-id="sel.id" :disabled="locked" + @mouseenter="onPick(sel, $event)" @click="onPick(sel, $event)" > {{ sel.scoreDisplay }} diff --git a/apps/player/src/i18n/en-US.ts b/apps/player/src/i18n/en-US.ts index 7ae0841..05cfd76 100644 --- a/apps/player/src/i18n/en-US.ts +++ b/apps/player/src/i18n/en-US.ts @@ -164,6 +164,7 @@ export default { }, auth: { login: 'Login', + logging_in: 'Logging in…', register: 'Create Account', logout: 'Log out', username: 'Username', @@ -218,6 +219,7 @@ export default { forgot_password: 'Forgot password?', forgot_password_title: 'Reset password', reset_password_btn: 'Reset password', + resetting_password: 'Resetting…', reset_success: 'Password reset. Please sign in with your new password. Contact your agent if you cannot log in.', reset_failed: 'Reset failed. Please try again.', phone_not_registered: 'This phone number is not registered', diff --git a/apps/player/src/i18n/ms-MY.ts b/apps/player/src/i18n/ms-MY.ts index 1169d2f..925983d 100644 --- a/apps/player/src/i18n/ms-MY.ts +++ b/apps/player/src/i18n/ms-MY.ts @@ -177,6 +177,7 @@ export default { }, auth: { login: 'Log Masuk', + logging_in: 'Sedang log masuk…', register: 'Daftar Akaun', logout: 'Log Keluar', username: 'Nama Pengguna', @@ -231,6 +232,7 @@ export default { forgot_password: 'Lupa kata laluan?', forgot_password_title: 'Tetapkan semula kata laluan', reset_password_btn: 'Tetapkan semula', + resetting_password: 'Sedang tetapkan semula…', reset_success: 'Kata laluan ditetapkan semula. Sila log masuk dengan kata laluan baharu. Hubungi ejen anda jika tidak dapat log masuk.', reset_failed: 'Gagal menetapkan semula. Sila cuba lagi.', phone_not_registered: 'Nombor telefon ini belum didaftarkan', diff --git a/apps/player/src/i18n/zh-CN.ts b/apps/player/src/i18n/zh-CN.ts index 32bf5ca..21b48d0 100644 --- a/apps/player/src/i18n/zh-CN.ts +++ b/apps/player/src/i18n/zh-CN.ts @@ -164,6 +164,7 @@ export default { }, auth: { login: '登录', + logging_in: '登录中…', register: '注册账号', logout: '退出登录', username: '账号', @@ -218,6 +219,7 @@ export default { forgot_password: '忘记密码?', forgot_password_title: '重置密码', reset_password_btn: '重置密码', + resetting_password: '重置中…', reset_success: '密码已重置,请使用新密码登录。若无法登录请联系代理。', reset_failed: '重置失败,请重试', phone_not_registered: '该手机号未注册', diff --git a/apps/player/src/views/ForgotPasswordView.vue b/apps/player/src/views/ForgotPasswordView.vue index ebfe727..5f7204f 100644 --- a/apps/player/src/views/ForgotPasswordView.vue +++ b/apps/player/src/views/ForgotPasswordView.vue @@ -7,8 +7,11 @@ import { useSmsCode } from '../composables/useSmsCode'; import api from '../api'; import LocaleSwitcher from '../components/LocaleSwitcher.vue'; import PhoneCountrySelect from '../components/PhoneCountrySelect.vue'; +import GoldSpinner from '../components/GoldSpinner.vue'; import loginBg from '../assets/images/zt2.webp'; +const props = withDefaults(defineProps<{ desktop?: boolean }>(), { desktop: false }); + const { t, locale } = useI18n(); const router = useRouter(); const route = useRoute(); @@ -98,12 +101,16 @@ function goLogin() { - - + + - + + + + {{ t('auth.resetting_password') }} + {{ t('auth.forgot_password_title') }} @@ -116,6 +123,7 @@ function goLogin() { type="tel" required autocomplete="tel-national" + :disabled="loading" :placeholder="t('auth.phone_local_placeholder')" /> @@ -130,12 +138,13 @@ function goLogin() { inputmode="numeric" maxlength="6" autocomplete="one-time-code" + :disabled="loading" :placeholder="t('auth.sms_code_placeholder')" /> {{ countdown > 0 ? `${countdown}s` : t('auth.send_sms') }} @@ -152,6 +161,7 @@ function goLogin() { required autocomplete="new-password" minlength="8" + :disabled="loading" :placeholder="t('auth.password_placeholder')" /> @@ -165,15 +175,26 @@ function goLogin() { required autocomplete="new-password" minlength="8" + :disabled="loading" /> {{ fieldError() }} - - {{ t('auth.reset_password_btn') }} + + + + {{ t('auth.resetting_password') }} + + {{ t('auth.reset_password_btn') }} - + {{ t('auth.back_to_login') }} @@ -205,6 +226,22 @@ function goLogin() { background-repeat: no-repeat; } +.login-page--desktop { + min-height: auto; + height: auto; + width: 100%; + align-items: stretch; + justify-content: flex-start; + padding: 0; + background-color: transparent; + overflow: visible; +} + +.login-page--desktop .login-stack { + max-width: none; + width: 100%; +} + .login-stack { position: relative; z-index: 1; @@ -213,6 +250,7 @@ function goLogin() { } .login-form { + position: relative; width: 100%; display: flex; flex-direction: column; @@ -220,6 +258,48 @@ function goLogin() { padding: 14px; } +.login-form.is-busy { + pointer-events: none; +} + +.form-busy-overlay { + position: absolute; + inset: 0; + z-index: 3; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + border-radius: inherit; + background: rgba(255, 255, 255, 0.88); + backdrop-filter: blur(4px); + pointer-events: all; +} + +.form-busy-text { + margin: 0; + font-size: 13px; + font-weight: 700; + color: var(--primary); + letter-spacing: 0.04em; +} + +.btn-inline-loading { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +.login-page--desktop .login-form { + gap: 10px; + padding: 0; + border: none !important; + background: transparent !important; + box-shadow: none !important; +} + .form-title { margin: 0 0 2px; font-size: 16px; @@ -228,6 +308,56 @@ function goLogin() { text-align: center; } +.login-page--desktop .form-title { + margin: 0 0 8px; + font-size: 18px; + text-align: left; + color: var(--primary); +} + +.login-page--desktop .field { + gap: 8px; +} + +.login-page--desktop label { + font-size: 13px; +} + +.login-page--desktop .field-input, +.login-page--desktop .btn-secondary { + height: 46px; +} + +.login-page--desktop .btn-login { + margin-top: 10px; + min-height: 50px; + font-size: 15px; +} + +.login-page--desktop .btn-skip { + margin-top: 6px; + padding: 12px 16px; + border-radius: var(--radius-sm); + border: 1px solid var(--border); + background: var(--bg-card); +} + +.login-page--desktop .btn-skip:hover:not(:disabled) { + border-color: var(--border-active); + background: var(--surface-accent); +} + +.login-page--desktop .error { + padding: 6px 10px; + border-radius: var(--radius-sm); + background: rgba(220, 38, 38, 0.08); + border-left: 2px solid var(--danger); +} + +.btn-login.is-loading:disabled { + opacity: 1; +} + .field { display: flex; flex-direction: column; diff --git a/apps/player/src/views/LoginView.vue b/apps/player/src/views/LoginView.vue index 267d876..624832a 100644 --- a/apps/player/src/views/LoginView.vue +++ b/apps/player/src/views/LoginView.vue @@ -8,8 +8,11 @@ import { useAppLocale } from '../composables/useAppLocale'; import LocaleSwitcher from '../components/LocaleSwitcher.vue'; import PhoneCountrySelect from '../components/PhoneCountrySelect.vue'; import RobotVerify from '../components/RobotVerify.vue'; +import GoldSpinner from '../components/GoldSpinner.vue'; import loginBg from '../assets/images/zt2.webp'; +const props = withDefaults(defineProps<{ desktop?: boolean }>(), { desktop: false }); + type LoginMode = 'account' | 'phone'; const { t, locale } = useI18n(); @@ -85,17 +88,27 @@ function goRegister() { - - + + - + + + + {{ t('auth.logging_in') }} + + {{ t('auth.login') }} {{ t('auth.login_by_account') }} @@ -104,6 +117,7 @@ function goRegister() { type="button" class="auth-tab" :class="{ active: loginMode === 'phone' }" + :disabled="loading" @click="switchLoginMode('phone')" > {{ t('auth.login_by_phone') }} @@ -118,6 +132,7 @@ function goRegister() { required autocomplete="username" maxlength="32" + :disabled="loading" :placeholder="t('auth.username_placeholder')" /> @@ -132,6 +147,7 @@ function goRegister() { type="tel" required autocomplete="tel-national" + :disabled="loading" :placeholder="t('auth.phone_local_placeholder')" /> @@ -139,25 +155,38 @@ function goRegister() { {{ t('auth.password') }} - + - + {{ t('auth.forgot_password') }} {{ t('auth.reset_success') }} {{ error }} - - {{ t('auth.login') }} + + + + {{ t('auth.logging_in') }} + + {{ t('auth.login') }} + + + {{ t('auth.go_register') }} - - + + {{ t('auth.go_register') }} - + {{ t('auth.continue_browsing') }} @@ -189,6 +218,22 @@ function goRegister() { background-repeat: no-repeat; } +.login-page--desktop { + min-height: auto; + height: auto; + width: 100%; + align-items: stretch; + justify-content: flex-start; + padding: 0; + background-color: transparent; + overflow: visible; +} + +.login-page--desktop .login-stack { + max-width: none; + width: 100%; +} + .login-stack { position: relative; z-index: 1; @@ -200,6 +245,7 @@ function goRegister() { } .login-form { + position: relative; width: 100%; display: flex; flex-direction: column; @@ -207,6 +253,118 @@ function goRegister() { padding: 14px; } +.login-form.is-busy { + pointer-events: none; +} + +.form-busy-overlay { + position: absolute; + inset: 0; + z-index: 3; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + border-radius: inherit; + background: rgba(255, 255, 255, 0.88); + backdrop-filter: blur(4px); + pointer-events: all; +} + +.form-busy-text { + margin: 0; + font-size: 13px; + font-weight: 700; + color: var(--primary); + letter-spacing: 0.04em; +} + +.btn-inline-loading { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +.login-page--desktop .login-form { + gap: 10px; + padding: 0; + border: none !important; + background: transparent !important; + box-shadow: none !important; +} + +.form-title { + margin: 0 0 8px; + font-size: 18px; + font-weight: 800; + color: var(--primary); + text-align: left; + letter-spacing: 0.02em; +} + +.login-page--desktop .field { + gap: 8px; +} + +.login-page--desktop label { + font-size: 13px; +} + +.login-page--desktop .field-input { + height: 46px; + padding: 0 14px; +} + +.login-page--desktop .auth-tabs { + margin-bottom: 8px; +} + +.login-page--desktop .btn-login { + margin-top: 10px; + min-height: 50px; + font-size: 15px; +} + +.login-page--desktop .btn-skip { + margin-top: 6px; + padding: 12px 16px; + border-radius: var(--radius-sm); + border: 1px solid var(--border); + background: var(--bg-card); +} + +.login-page--desktop .btn-skip:hover:not(:disabled) { + border-color: var(--border-active); + background: var(--surface-accent); +} + +.login-page--desktop .error { + padding: 6px 10px; + border-radius: var(--radius-sm); + background: rgba(220, 38, 38, 0.08); + border-left: 2px solid var(--danger); +} + +.login-page--desktop .success { + padding: 6px 10px; + border-radius: var(--radius-sm); + background: rgba(5, 150, 105, 0.08); + border-left: 2px solid var(--success); +} + +.btn-login.is-loading:disabled { + opacity: 1; +} + +.auth-tab:disabled, +.btn-forgot:disabled, +.btn-skip:disabled { + opacity: 0.45; + cursor: not-allowed; +} + .login-actions { display: flex; flex-direction: column; diff --git a/apps/player/src/views/RegisterView.vue b/apps/player/src/views/RegisterView.vue index 7f2950e..1d03780 100644 --- a/apps/player/src/views/RegisterView.vue +++ b/apps/player/src/views/RegisterView.vue @@ -11,6 +11,8 @@ import PhoneCountrySelect from '../components/PhoneCountrySelect.vue'; import GoldSpinner from '../components/GoldSpinner.vue'; import loginBg from '../assets/images/zt2.webp'; +const props = withDefaults(defineProps<{ desktop?: boolean }>(), { desktop: false }); + const { t, locale } = useI18n(); const { initFromUser } = useAppLocale(); const auth = useAuthStore(); @@ -101,12 +103,16 @@ const fieldError = () => { - - + + - + {{ t('auth.registering') }} @@ -200,8 +206,11 @@ const fieldError = () => { {{ t('auth.register_btn') }} + + {{ t('auth.have_account') }} + - + {{ t('auth.have_account') }} @@ -237,6 +246,22 @@ const fieldError = () => { background-repeat: no-repeat; } +.login-page--desktop { + min-height: auto; + height: auto; + width: 100%; + align-items: stretch; + justify-content: flex-start; + padding: 0; + background-color: transparent; + overflow: visible; +} + +.login-page--desktop .login-stack { + max-width: none; + width: 100%; +} + .login-stack { position: relative; z-index: 1; @@ -298,6 +323,60 @@ const fieldError = () => { gap: 8px; } +.login-page--desktop .login-form { + gap: 10px; + padding: 0; + border: none !important; + background: transparent !important; + box-shadow: none !important; +} + +.login-page--desktop .form-title { + margin: 0 0 8px; + font-size: 18px; + text-align: left; + color: var(--primary); +} + +.login-page--desktop .field { + gap: 8px; +} + +.login-page--desktop label { + font-size: 13px; +} + +.login-page--desktop .field-input, +.login-page--desktop .btn-secondary { + height: 46px; +} + +.login-page--desktop .btn-login { + margin-top: 10px; + min-height: 50px; + font-size: 15px; +} + +.login-page--desktop .btn-skip { + margin-top: 6px; + padding: 12px 16px; + border-radius: var(--radius-sm); + border: 1px solid var(--border); + background: var(--bg-card); +} + +.login-page--desktop .btn-skip:hover:not(:disabled) { + border-color: var(--border-active); + background: var(--surface-accent); +} + +.login-page--desktop .error { + padding: 6px 10px; + border-radius: var(--radius-sm); + background: rgba(220, 38, 38, 0.08); + border-left: 2px solid var(--danger); +} + .form-title { margin: 0 0 2px; font-size: 16px; diff --git a/apps/player/src/views/desktop/DesktopForgotPasswordView.vue b/apps/player/src/views/desktop/DesktopForgotPasswordView.vue index d8b2e38..92e2cb4 100644 --- a/apps/player/src/views/desktop/DesktopForgotPasswordView.vue +++ b/apps/player/src/views/desktop/DesktopForgotPasswordView.vue @@ -1,121 +1,10 @@ - - {{ t('common.back') }} - - - - THEBET365 - 重置您的登录密码 - - - - - - - - + + + - - diff --git a/apps/player/src/views/desktop/DesktopLoginView.vue b/apps/player/src/views/desktop/DesktopLoginView.vue index d3867b2..0b1a882 100644 --- a/apps/player/src/views/desktop/DesktopLoginView.vue +++ b/apps/player/src/views/desktop/DesktopLoginView.vue @@ -1,156 +1,10 @@ - - {{ t('common.back') }} - - - - THEBET365 - 专业体育赛事投注平台 - - - ⚡ - 实时赔率更新 - - - 🔒 - 安全稳定平台 - - - 💰 - 快速充值提款 - - - - - - - - - - + + + - - diff --git a/apps/player/src/views/desktop/DesktopRegisterView.vue b/apps/player/src/views/desktop/DesktopRegisterView.vue index 2380131..883d6e2 100644 --- a/apps/player/src/views/desktop/DesktopRegisterView.vue +++ b/apps/player/src/views/desktop/DesktopRegisterView.vue @@ -1,122 +1,10 @@ - - {{ t('common.back') }} - - - - THEBET365 - 加入我们,开始精彩体育投注 - - - - - - - - + + + - -
{{ t('auth.resetting_password') }}
{{ fieldError() }}
{{ t('auth.logging_in') }}
{{ t('auth.reset_success') }}
{{ error }}
{{ t('auth.registering') }}
重置您的登录密码
专业体育赛事投注平台
加入我们,开始精彩体育投注