引言
在现代Web应用中,提供多种登录方式已成为一种标准做法,这不仅能提升用户体验,还能满足不同用户的需求。本文将详细介绍如何使用Vue.js框架结合Element UI组件库,实现一个包含账号登录和手机号验证码登录两种方式的登录页面。通过本文,你将能够掌握如何根据用户选择动态切换登录表单、如何进行表单验证以及如何实现验证码的发送和倒计时功能。
功能需求
- 账号登录:用户通过输入用户名和密码进行登录。
- 验证码登录:用户通过输入手机号码并接收验证码进行登录。
效果图
1. 登录页面设计
在src/components
目录下创建一个名为Login.vue
的文件,用于实现登录页面。
1.1 模板部分(Template)
使用Element UI的el-form
组件构建登录表单,通过v-if
指令根据flag
变量的值动态显示不同的登录方式(账号登录或手机号验证码登录)
<template>
<div class="login">
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules"
class="login-form"
:inline="true"
>
<!-- 标题部分 -->
<div class="title">
<div
@click="showPhone"
v-if="flag == 'verificationCode'"
class="title-back"
>
<i class="el-icon-arrow-left"></i>返回
</div>
<div class="title-span">{{ formTitle }}</div>
</div>
<!-- 账号登录表单 -->
<div class="formCont" v-if="flag == 'account'">
<el-form-item prop="username" label="用户名:">
<el-input
v-model="loginForm.username"
type="text"
auto-complete="off"
placeholder="用户名"
>
</el-input>
</el-form-item>
<el-form-item prop="password" label="密码:">
<el-input
v-model="loginForm.password"
type="password"
auto-complete="off"
placeholder="密码"
show-password
@keyup.enter.native="handleLogin"
>
</el-input>
</el-form-item>
<div class="button">
<el-form-item style="width: 100%">
<el-button
size="medium"
type="primary"
style="width: 100%"
@click.native.prevent="handleLogin"
>
登 录
</el-button>
</el-form-item>
</div>
</div>
<!-- 手机号码登录表单 -->
<div class="formCont phone-cont" v-if="flag == 'phone'">
<el-form-item prop="phoneNumber">
<div class="text">请使用人事处登记手机号进行登录</div>
<el-input
v-model="loginForm.phoneNumber"
type="text"
auto-complete="off"
placeholder="请输入手机号码"
>
</el-input>
</el-form-item>
<div class="button">
<el-form-item style="width: 100%">
<el-button
size="medium"
type="primary"
style="width: 100%"
@click.native.prevent="handlePhoneNumber"
>
验证手机号码
</el-button>
</el-form-item>
</div>
</div>
<!-- 验证码输入部分 -->
<div v-if="flag == 'verificationCode'" class="verification-code">
<div class="tx">输入验证码</div>
<div class="tx">我们已向{{ phoneNumber }} 发送验证码</div>
<div class="tx">请查看短信并输入验证码</div>
<div class="six-digit-wrapper">
<input
v-for="(item, index) in digits"
:key="index"
:ref="`ref${index}`"
class="input"
v-model="item.value"
type="text"
oninput="value=value.replace(/[^\d]/g,'')"
@input="onInput(index)"
@keyup.delete="onDelete(index)"
maxlength="1"
/>
</div>
<div
class="anew-verification-code anew-verification-code-send"
v-if="show"
@click="handleGetCode"
>
重新获取验证码
</div>
<div class="anew-verification-code" v-else>
{{ count }}秒后重新获取短信
</div>
</div>
<!-- 切换登录方式按钮 -->
<div class="btn-cont" @click="showPhone" v-if="flag == 'account'">
验证码登录
</div>
<div class="btn-cont" @click="showAccount" v-if="flag == 'phone'">
账号字码登录
</div>
</el-form>
</div>
</template>
1.2 脚本部分(Script)
在脚本部分,定义登录表单的数据模型、验证规则以及方法。
<script>
export default {
name: "Login",
components: {},
data() {
return {
loginForm: {
username: "",
password: "",
phoneNumber: "",
},
loginRules: {
username: [
{ required: true, trigger: "blur", message: "请输入您的账号" },
],
password: [
{ required: true, trigger: "blur", message: "请输入您的密码" },
],
phoneNumber: [
{ required: true, message: "手机号码不能为空", trigger: "blur" },
{
pattern: /^1(3|4|5|6|7|8|9)\d{9}$/,
message: "手机号码格式错误",
trigger: "blur",
},
],
},
formTitle: "管理员账号登录",
flag: "account",
phoneNumber: "",
show: true, // 控制倒计时状态
count: 60, // 倒计时秒数
timer: null, //计时器
digits: [
{
value: "",
},
{
value: "",
},
{
value: "",
},
{
value: "",
},
{
value: "",
},
{
value: "",
},
],
};
},
// 省略了部分代码,完整代码见下文
};
</script>
2. 功能实现
2.1 账号登录
用户选择账号登录时,表单显示用户名和密码输入框。通过Element UI的el-form
组件的validate
方法进行表单验证,验证通过后调用登录接口。
//管理员账号登录
handleLogin() {
this.$refs.loginForm.validate((valid) => {
if (valid) {
//你的调用登录接口的代码
}
});
},
2.2 手机号验证码登录
用户选择手机号验证码登录时,表单显示手机号输入框。用户输入手机号后,点击“验证手机号码”按钮,调用发送验证码接口,并启动倒计时。
// 验证手机号码
handlePhoneNumber() {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.formTitle = "验证手机号码";
this.phoneNumber = this.loginForm.phoneNumber;
this.flag = "verificationCode";
this.handleGetCode();
}
});
},
// 获取短信验证码
handleGetCode() {
// 验证码倒计时
if (!this.timer) {
//调取你的发送验证码的接口
sendCode({ phone: this.phoneNumber }).then((res) => {
// 处理发送验证码的响应
});
this.count = 60;
this.show = false;
this.timer = setInterval(() => {
if (this.count > 0 && this.count <= 60) {
this.count--;
} else {
this.show = true;
clearInterval(this.timer);
this.timer = null;
}
}, 1000);
}
},
在验证码输入框中,通过监听input
和keyup.delete
事件,实现光标的自动移动和删除功能。
// 输入验证码时的处理
onInput(index) {
// index < 5 ,如果是第6格,不触发光标移动至下一个输入框。
if (this.digits[index].value && index < 5) {
this.$refs["ref" + (index + 1)][0].focus();
} else {
let resultString = this.digits.map((obj) => obj.value).join("");
if (resultString.length == 6) {
let data = { phone: this.phoneNumber, code: resultString };
//调取你的验证码登录接口
mwlogin(data).then((res) => {
// 处理验证码登录的响应
});
}
}
},
// 删除验证码时的处理
onDelete(index) {
// 如果是第1格,不触发光标移动至上一个输入框
if (index > 0) {
this.$refs["ref" + (index - 1)][0].focus();
}
},
3. 样式调整
根据需要调整样式,确保表单在不同设备上的显示效果。
4. 测试与优化
- 测试:确保在不同情况下(如输入错误、验证码发送失败等)都有相应的提示和处理。
- 优化:根据实际需求优化用户体验,如添加加载动画、错误提示等。
完整代码
<template>
<div class="login">
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules"
class="login-form"
:inline="true"
>
<!-- 标题部分 -->
<div class="title">
<div
@click="showPhone"
v-if="flag == 'verificationCode'"
class="title-back"
>
<i class="el-icon-arrow-left"></i>返回
</div>
<div class="title-span">{{ formTitle }}</div>
</div>
<!-- 账号登录表单 -->
<div class="formCont" v-if="flag == 'account'">
<el-form-item prop="username" label="用户名:">
<el-input
v-model="loginForm.username"
type="text"
auto-complete="off"
placeholder="用户名"
>
</el-input>
</el-form-item>
<el-form-item prop="password" label="密码:">
<el-input
v-model="loginForm.password"
type="password"
auto-complete="off"
placeholder="密码"
show-password
@keyup.enter.native="handleLogin"
>
</el-input>
</el-form-item>
<div class="button">
<el-form-item style="width: 100%">
<el-button
size="medium"
type="primary"
style="width: 100%"
@click.native.prevent="handleLogin"
>
登 录
</el-button>
</el-form-item>
</div>
</div>
<!-- 手机号码登录表单 -->
<div class="formCont phone-cont" v-if="flag == 'phone'">
<el-form-item prop="phoneNumber">
<div class="text">请使用人事处登记手机号进行登录</div>
<el-input
v-model="loginForm.phoneNumber"
type="text"
auto-complete="off"
placeholder="请输入手机号码"
>
</el-input>
</el-form-item>
<div class="button">
<el-form-item style="width: 100%">
<el-button
size="medium"
type="primary"
style="width: 100%"
@click.native.prevent="handlePhoneNumber"
>
验证手机号码
</el-button>
</el-form-item>
</div>
</div>
<!-- 验证码输入部分 -->
<div v-if="flag == 'verificationCode'" class="verification-code">
<div class="tx">输入验证码</div>
<div class="tx">我们已向{{ phoneNumber }} 发送验证码</div>
<div class="tx">请查看短信并输入验证码</div>
<div class="six-digit-wrapper">
<input
v-for="(item, index) in digits"
:key="index"
:ref="`ref${index}`"
class="input"
v-model="item.value"
type="text"
oninput="value=value.replace(/[^\d]/g,'')"
@input="onInput(index)"
@keyup.delete="onDelete(index)"
maxlength="1"
/>
</div>
<div
class="anew-verification-code anew-verification-code-send"
v-if="show"
@click="handleGetCode"
>
重新获取验证码
</div>
<div class="anew-verification-code" v-else>
{{ count }}秒后重新获取短信
</div>
</div>
<!-- 切换登录方式按钮 -->
<div class="btn-cont" @click="showPhone" v-if="flag == 'account'">
验证码登录
</div>
<div class="btn-cont" @click="showAccount" v-if="flag == 'phone'">
账号字码登录
</div>
</el-form>
</div>
</template>
<script>
import { sendCode, mwlogin } from "@/api/login";
export default {
name: "Login",
components: {},
data() {
return {
loginForm: {
username: "",
password: "",
phoneNumber: "",
},
loginRules: {
username: [
{ required: true, trigger: "blur", message: "请输入您的账号" },
],
password: [
{ required: true, trigger: "blur", message: "请输入您的密码" },
],
phoneNumber: [
{ required: true, message: "手机号码不能为空", trigger: "blur" },
{
pattern: /^1(3|4|5|6|7|8|9)\d{9}$/,
message: "手机号码格式错误",
trigger: "blur",
},
],
},
formTitle: "管理员账号登录",
flag: "account",
phoneNumber: "",
show: true, // 控制倒计时状态
count: 60, // 倒计时秒数
timer: null, //计时器
digits: [
{
value: "",
},
{
value: "",
},
{
value: "",
},
{
value: "",
},
{
value: "",
},
{
value: "",
},
],
};
},
watch: {},
mounted() {},
created() {},
methods: {
//管理员账号登录
handleLogin() {
this.$refs.loginForm.validate((valid) => {
if (valid) {
//你的调用登录接口的代码
}
});
},
// 切换到手机号码登录
showPhone() {
this.formTitle = "管理员手机号码登录";
this.flag = "phone";
},
// 切换到账号登录
showAccount() {
this.formTitle = "管理员账号登录";
this.flag = "account";
},
// 验证手机号码
handlePhoneNumber() {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.formTitle = "验证手机号码";
this.phoneNumber = this.loginForm.phoneNumber;
this.flag = "verificationCode";
this.handleGetCode();
}
});
},
// 获取短信验证码
handleGetCode() {
// 验证码倒计时
if (!this.timer) {
//调取你的发送验证码的接口
sendCode({ phone: this.phoneNumber }).then((res) => {
// 处理发送验证码的响应
});
this.count = 60;
this.show = false;
this.timer = setInterval(() => {
if (this.count > 0 && this.count <= 60) {
this.count--;
} else {
this.show = true;
clearInterval(this.timer);
this.timer = null;
}
}, 1000);
}
},
// 输入验证码时的处理
onInput(index) {
// index < 5 ,如果是第6格,不触发光标移动至下一个输入框。
if (this.digits[index].value && index < 5) {
this.$refs["ref" + (index + 1)][0].focus();
} else {
let resultString = this.digits.map((obj) => obj.value).join("");
if (resultString.length == 6) {
let data = { phone: this.phoneNumber, code: resultString };
//调取你的验证码登录接口
mwlogin(data).then((res) => {
// 处理验证码登录的响应
});
}
}
},
// 删除验证码时的处理
onDelete(index) {
// 如果是第1格,不触发光标移动至上一个输入框
if (index > 0) {
this.$refs["ref" + (index - 1)][0].focus();
}
},
},
};
</script>
<style rel="stylesheet/scss" lang="scss">
.login {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
background-image: url("https://img202.yun300.cn/img/s05bg.jpg?tenantId=238938&viewType=1&k=1632806193000");
background-size: 100% 100%;
background-repeat: no-repeat;
position: relative;
}
.btn-cont {
font-family: "Arial Normal", "Arial", sans-serif;
font-weight: 400;
font-style: normal;
color: #ca512f;
line-height: 20px;
text-align: center;
margin-top: 30px;
cursor: pointer;
}
.title {
text-align: left;
margin: 0 auto 35px;
height: 40px;
padding-top: 10px;
display: flex;
.title-back {
color: #ca512f;
}
.title-span {
position: relative;
margin: 0 auto 32px auto;
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
height: -webkit-fit-content;
height: -moz-fit-content;
height: fit-content;
background: -webkit-gradient(
linear,
left top,
left bottom,
color-stop(50%, #e2eff6),
to(#8cbcdf)
);
background: linear-gradient(180deg, #e2eff6 50%, #8cbcdf);
color: transparent;
-webkit-background-clip: text;
font-size: 38px;
letter-spacing: 2px;
font-family: cursive;
font-weight: 700;
}
}
.verification-code {
text-align: center;
font-family: "Arial Normal", "Arial", sans-serif;
font-weight: 400;
font-style: normal;
font-size: 18px;
letter-spacing: normal;
.tx {
margin-bottom: 16px;
color: #ffffff;
}
.anew-verification-code {
color: #ca512f;
}
.anew-verification-code-send {
cursor: pointer;
}
}
.login .login-form {
position: absolute;
top: 30vh;
left: 50%;
transform: translateX(-50%);
border-radius: 6px;
background: rgba(0, 0, 0, 0.2);
width: 503px;
height: 400px;
.formCont {
padding: 0 40px 0;
}
.el-input {
height: 40px;
width: 300px;
input {
height: 40px;
border: 1px solid RGBA(28, 57, 77, 0.6);
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
.phone-cont {
.text {
font-family: "微软雅黑", sans-serif;
font-weight: 400;
font-size: 14px;
color: #ffffff;
text-align: center;
}
.el-input {
width: 423px;
}
}
}
.login-code {
height: 38px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.login {
.login-code-img {
height: 38px;
width: 75px;
margin-left: 25px;
}
.el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label:before {
display: none;
}
.el-form-item--medium .el-form-item__label {
color: #fff;
width: 64px;
height: 22px;
text-align: left;
font-size: 15px;
font-weight: unset;
}
.el-form-item {
margin: 22px 10px 22px 0;
}
.button {
.el-form-item {
margin-bottom: unset;
}
.el-form-item__content {
width: 100%;
}
.el-button--primary {
background: linear-gradient(
to top right,
rgb(231, 133, 106) rgb(202, 81, 47, 1)
);
}
}
.el-tabs__item.is-active {
color: rgba(0, 204, 255, 1) !important;
}
.el-tabs--top .el-tabs__item {
color: RGBA(3, 129, 170, 1);
padding: unset;
}
.six-digit-wrapper {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin: 20px 0 50px 0;
.input {
display: flex;
width: 60px;
margin-left: 10px;
height: 60px;
font-size: 24px;
color: #333333;
background-color: #f2f2f2;
text-align: center;
outline: none; // 去除选中状态边框
border: solid 1px #d2d2d2;
border-top: 0px;
border-left: 0px;
border-right: 0px;
}
}
}
</style>
<style scoped lang="scss">
::v-deep {
.el-dialog {
.el-dialog__header {
background: #ffffff !important;
padding: 0;
height: 30px;
}
}
}
</style>
总结
本文详细介绍了如何使用Vue.js和Element UI实现一个多方式登录功能的登录页面。通过本文的学习,你可以掌握如何根据用户选择动态切换表单、如何进行表单验证以及如何实现验证码的发送和倒计时功能。希望本文对你有所帮助,如果你在实现过程中遇到任何问题,欢迎在评论区留言讨论。