hookehuyr

feat(登录页): 添加验证码登录功能

在登录页面中添加验证码登录选项,用户可以选择使用验证码或密码进行登录。新增验证码输入框、发送验证码按钮以及相关逻辑,包括手机号验证、验证码发送和倒计时功能
......@@ -33,11 +33,12 @@
placeholder="请输入手机号"
@input="mobile = $event.target.value.replace(/\D/g, '')"
@focus="handleInputFocus"
@blur="validatePhone"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500"
/>
</div>
<div>
<div v-if="!isVerifyCodeLogin">
<label for="password" class="block text-sm font-medium text-gray-700">
密码 <span class="text-red-500">*</span>
</label>
......@@ -53,7 +54,32 @@
/>
</div>
<div class="flex justify-end">
<div v-else>
<label for="verificationCode" class="block text-sm font-medium text-gray-700">
验证码 <span class="text-red-500">*</span>
</label>
<div class="flex space-x-2">
<input
id="verificationCode"
v-model="verificationCode"
type="text"
required
maxlength="6"
placeholder="请输入验证码"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500"
/>
<button
type="button"
:disabled="countdown > 0 || !isPhoneValid"
@click="sendVerificationCode"
class="mt-1 px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap"
>
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
</button>
</div>
</div>
<div class="flex justify-end space-x-4">
<div class="text-sm">
<router-link
to="/forgotPwd"
......@@ -62,6 +88,15 @@
忘记密码?
</router-link>
</div>
<div class="text-sm">
<button
type="button"
class="font-medium text-green-600 hover:text-green-500"
@click="isVerifyCodeLogin = !isVerifyCodeLogin"
>
{{ isVerifyCodeLogin ? '密码登录' : '验证码登录' }}
</button>
</div>
</div>
<div>
......@@ -133,6 +168,8 @@ import FrostedGlass from "@/components/ui/FrostedGlass.vue";
import { useAuth } from "@/contexts/auth";
import { loginAPI, getUserInfoAPI } from "@/api/users";
import { useTitle } from "@vueuse/core";
import { smsAPI } from "@/api/common";
import { showToast } from "vant";
const handleInputFocus = () => {
setTimeout(() => {
......@@ -152,12 +189,60 @@ const { login } = useAuth();
const mobile = ref("");
const password = ref("");
const verificationCode = ref("");
const error = ref("");
const loading = ref(false);
const isVerifyCodeLogin = ref(false);
const countdown = ref(0);
const isPhoneValid = ref(false);
const validatePhone = () => {
if (!mobile.value) {
error.value = '请输入手机号';
isPhoneValid.value = false;
return;
}
if (!/^1[3-9]\d{9}$/.test(mobile.value)) {
error.value = '请输入正确的手机号';
isPhoneValid.value = false;
return;
}
error.value = '';
isPhoneValid.value = true;
};
const startCountdown = () => {
countdown.value = 60;
const timer = setInterval(() => {
countdown.value--;
if (countdown.value <= 0) {
clearInterval(timer);
}
}, 1000);
};
const sendVerificationCode = async () => {
if (!isPhoneValid.value) {
return;
}
try {
const { code } = await smsAPI({ mobile: mobile.value });
if (code) {
showToast('验证码已发送');
startCountdown();
return;
}
} catch (err) {
console.error('Send verification code error:', err);
error.value = '发送验证码失败,请稍后重试';
}
};
// 原登录逻辑
const handleSubmit = async () => {
if (!mobile.value || !password.value) {
if (!mobile.value || (!isVerifyCodeLogin.value && !password.value) || (isVerifyCodeLogin.value && !verificationCode.value)) {
error.value = "请填写所有字段";
return;
}
......@@ -169,7 +254,9 @@ const handleSubmit = async () => {
// 调用登录接口
const response = await loginAPI({
mobile: mobile.value,
password: password.value,
...(isVerifyCodeLogin.value
? { sms_code: verificationCode.value }
: { password: password.value }),
});
if (response.code !== 1) {
......