feat(player): 注册账号、登录双模式与移动端性能优化
注册必填 7-32 位账号,手机号区号/本地号分存;登录默认账号模式并支持切换手机号登录;Player i18n 拆包与赛事接口优化。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,23 +2,36 @@
|
||||
import { ref } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { defaultPhoneIsoForLocale, getPhoneDialFromIso } from '@thebet365/shared';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import { useAppLocale } from '../composables/useAppLocale';
|
||||
import LocaleSwitcher from '../components/LocaleSwitcher.vue';
|
||||
import PhoneCountrySelect from '../components/PhoneCountrySelect.vue';
|
||||
import RobotVerify from '../components/RobotVerify.vue';
|
||||
import loginBg from '../assets/images/h5bg.png';
|
||||
|
||||
const { t } = useI18n();
|
||||
type LoginMode = 'account' | 'phone';
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const { syncLocaleToBackend } = useAppLocale();
|
||||
const auth = useAuthStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const captchaRef = ref<InstanceType<typeof RobotVerify> | null>(null);
|
||||
const username = ref('');
|
||||
const loginMode = ref<LoginMode>('account');
|
||||
const account = ref('');
|
||||
const phone = ref('');
|
||||
const countryIso = ref(defaultPhoneIsoForLocale(locale.value));
|
||||
const password = ref('');
|
||||
const error = ref('');
|
||||
const loading = ref(false);
|
||||
|
||||
function switchLoginMode(mode: LoginMode) {
|
||||
if (loginMode.value === mode) return;
|
||||
loginMode.value = mode;
|
||||
error.value = '';
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if (!captchaRef.value?.validate()) {
|
||||
error.value = t('auth.captcha_wrong');
|
||||
@@ -28,7 +41,10 @@ async function submit() {
|
||||
loading.value = true;
|
||||
error.value = '';
|
||||
try {
|
||||
await auth.login(username.value, password.value);
|
||||
const isPhone = loginMode.value === 'phone';
|
||||
const loginId = isPhone ? phone.value : account.value;
|
||||
const countryCode = isPhone ? getPhoneDialFromIso(countryIso.value) : undefined;
|
||||
await auth.login(loginId, password.value, countryCode);
|
||||
await syncLocaleToBackend();
|
||||
const redirectTo = (route.query.redirect as string) || '/';
|
||||
router.push(redirectTo);
|
||||
@@ -47,7 +63,6 @@ function isGuestBrowsablePath(path: string): boolean {
|
||||
|
||||
function continueBrowsing() {
|
||||
const redirect = typeof route.query.redirect === 'string' ? route.query.redirect : '';
|
||||
// redirect 是登录成功后的目标;跳过登录时只能去公开页,避免跳回需登录页形成死循环
|
||||
const target = isGuestBrowsablePath(redirect) ? redirect || '/' : '/';
|
||||
router.replace(target);
|
||||
}
|
||||
@@ -66,20 +81,42 @@ function goRegister() {
|
||||
<LocaleSwitcher compact />
|
||||
</div>
|
||||
<form @submit.prevent="submit" class="login-form ps-gold-frame">
|
||||
<div class="field">
|
||||
<div v-if="loginMode === 'account'" class="field">
|
||||
<label>{{ t('auth.username') }}</label>
|
||||
<input
|
||||
v-model="username"
|
||||
v-model="account"
|
||||
class="field-input"
|
||||
required
|
||||
autocomplete="username"
|
||||
:placeholder="t('auth.login_username_placeholder')"
|
||||
maxlength="32"
|
||||
:placeholder="t('auth.username_placeholder')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else class="field">
|
||||
<label>{{ t('auth.phone') }}</label>
|
||||
<div class="inline-row">
|
||||
<PhoneCountrySelect v-model="countryIso" />
|
||||
<input
|
||||
v-model="phone"
|
||||
class="field-input"
|
||||
type="tel"
|
||||
required
|
||||
autocomplete="tel-national"
|
||||
:placeholder="t('auth.phone_local_placeholder')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>{{ t('auth.password') }}</label>
|
||||
<input v-model="password" class="field-input" type="password" required />
|
||||
<input v-model="password" class="field-input" type="password" required autocomplete="current-password" />
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn-mode-switch" @click="switchLoginMode(loginMode === 'account' ? 'phone' : 'account')">
|
||||
{{ loginMode === 'account' ? t('auth.login_by_phone') : t('auth.login_by_account') }}
|
||||
</button>
|
||||
|
||||
<RobotVerify ref="captchaRef" />
|
||||
<p v-if="error" class="error">{{ error }}</p>
|
||||
<button type="submit" class="btn-login btn-gold-outline" :disabled="loading">
|
||||
@@ -169,6 +206,22 @@ label {
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.btn-mode-switch {
|
||||
align-self: flex-start;
|
||||
margin: -2px 0 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: rgba(240, 216, 117, 0.75);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-mode-switch:active {
|
||||
color: rgba(240, 216, 117, 0.95);
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
margin-top: 4px;
|
||||
padding: 10px 14px;
|
||||
|
||||
Reference in New Issue
Block a user