157 lines
3.5 KiB
Vue
157 lines
3.5 KiB
Vue
![]() |
<script setup lang="ts">
|
||
|
import { VbenButton, VbenInput, VbenPinInput } from '@vben-core/shadcn-ui';
|
||
|
|
||
|
import { $t } from '@vben/locales';
|
||
|
import { computed, onBeforeUnmount, reactive, ref } from 'vue';
|
||
|
import { useRouter } from 'vue-router';
|
||
|
|
||
|
import Title from './auth-title.vue';
|
||
|
|
||
|
import type { LoginCodeEmits } from './typings';
|
||
|
|
||
|
interface Props {
|
||
|
/**
|
||
|
* @zh_CN 是否处于加载处理状态
|
||
|
*/
|
||
|
loading?: boolean;
|
||
|
}
|
||
|
|
||
|
defineOptions({
|
||
|
name: 'AuthenticationCodeLogin',
|
||
|
});
|
||
|
|
||
|
withDefaults(defineProps<Props>(), {
|
||
|
loading: false,
|
||
|
});
|
||
|
|
||
|
const emit = defineEmits<{
|
||
|
submit: LoginCodeEmits['submit'];
|
||
|
}>();
|
||
|
|
||
|
const router = useRouter();
|
||
|
|
||
|
const formState = reactive({
|
||
|
code: '',
|
||
|
phoneNumber: '',
|
||
|
requirePhoneNumber: false,
|
||
|
submitted: false,
|
||
|
});
|
||
|
|
||
|
const countdown = ref(0);
|
||
|
const timer = ref<ReturnType<typeof setTimeout>>();
|
||
|
|
||
|
const isValidPhoneNumber = computed(() => {
|
||
|
return /^1[3-9]\d{9}$/.test(formState.phoneNumber);
|
||
|
});
|
||
|
|
||
|
const btnText = computed(() => {
|
||
|
return countdown.value > 0
|
||
|
? $t('authentication.send-text', [countdown.value])
|
||
|
: $t('authentication.send-code');
|
||
|
});
|
||
|
const btnLoading = computed(() => {
|
||
|
return countdown.value > 0;
|
||
|
});
|
||
|
|
||
|
const phoneNumberStatus = computed(() => {
|
||
|
return (formState.submitted || formState.requirePhoneNumber) &&
|
||
|
!isValidPhoneNumber.value
|
||
|
? 'error'
|
||
|
: 'default';
|
||
|
});
|
||
|
|
||
|
const codeStatus = computed(() => {
|
||
|
return formState.submitted && !formState.code ? 'error' : 'default';
|
||
|
});
|
||
|
|
||
|
function handleSubmit() {
|
||
|
formState.submitted = true;
|
||
|
if (phoneNumberStatus.value !== 'default' || codeStatus.value !== 'default') {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
emit('submit', {
|
||
|
code: formState.code,
|
||
|
phoneNumber: formState.phoneNumber,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function handleGo(path: string) {
|
||
|
router.push(path);
|
||
|
}
|
||
|
|
||
|
async function handleSendCode() {
|
||
|
if (btnLoading.value) {
|
||
|
return;
|
||
|
}
|
||
|
if (!isValidPhoneNumber.value) {
|
||
|
formState.requirePhoneNumber = true;
|
||
|
return;
|
||
|
}
|
||
|
countdown.value = 60;
|
||
|
// TODO: 调用发送验证码接口
|
||
|
startCountdown();
|
||
|
}
|
||
|
|
||
|
function startCountdown() {
|
||
|
if (countdown.value > 0) {
|
||
|
timer.value = setTimeout(() => {
|
||
|
countdown.value--;
|
||
|
startCountdown();
|
||
|
}, 1000);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
onBeforeUnmount(() => {
|
||
|
countdown.value = 0;
|
||
|
clearTimeout(timer.value);
|
||
|
});
|
||
|
</script>
|
||
|
|
||
|
<template>
|
||
|
<div>
|
||
|
<Title>
|
||
|
{{ $t('authentication.welcome-back') }} 📲
|
||
|
<template #desc>
|
||
|
<span class="text-muted-foreground">
|
||
|
{{ $t('authentication.code-subtitle') }}
|
||
|
</span>
|
||
|
</template>
|
||
|
</Title>
|
||
|
<VbenInput
|
||
|
v-model="formState.phoneNumber"
|
||
|
:status="phoneNumberStatus"
|
||
|
:error-tip="$t('authentication.mobile-tip')"
|
||
|
:label="$t('authentication.mobile')"
|
||
|
name="phoneNumber"
|
||
|
type="number"
|
||
|
:placeholder="$t('authentication.mobile')"
|
||
|
:autofocus="true"
|
||
|
@keyup.enter="handleSubmit"
|
||
|
/>
|
||
|
<VbenPinInput
|
||
|
v-model="formState.code"
|
||
|
:handle-send-code="handleSendCode"
|
||
|
:status="codeStatus"
|
||
|
:code-length="4"
|
||
|
:error-tip="$t('authentication.code-tip')"
|
||
|
:label="$t('authentication.code')"
|
||
|
name="password"
|
||
|
:placeholder="$t('authentication.code')"
|
||
|
:btn-text="btnText"
|
||
|
:btn-loading="btnLoading"
|
||
|
@keyup.enter="handleSubmit"
|
||
|
/>
|
||
|
<VbenButton :loading="loading" class="mt-2 w-full" @click="handleSubmit">
|
||
|
{{ $t('common.login') }}
|
||
|
</VbenButton>
|
||
|
<VbenButton
|
||
|
class="mt-4 w-full"
|
||
|
variant="outline"
|
||
|
@click="handleGo('/auth/login')"
|
||
|
>
|
||
|
{{ $t('common.back') }}
|
||
|
</VbenButton>
|
||
|
</div>
|
||
|
</template>
|