一、完整 代码展示
<template>
<div class="login">
<div class="login-content">
<img class="img" src="../../assets/image/loginPhone.png" />
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>手机号登录</span>
</div>
<div class="text-content">
<el-form :model="ruleForm" label-width="80px">
<el-form-item label="手机号" class="keyboard-wrapper">
<el-input placeholder="请输入手机号" v-model.sync="ruleForm.tel" clearable
@focus="viewShow('tel')" onfocus="this.blur()" @input="handleInputTel" maxlength="11" show-word-limit>
</el-input>
</el-form-item>
<el-form-item label="验证码" class="keyboard-wrapper">
<el-input placeholder="请输入验证码" v-model.sync="ruleForm.code" @focus="viewShow('code')"
onfocus="this.blur()" @input="handleInput" clearable>
<template slot="append">
<el-button style="font-size: 22px;" :disabled="Boolean(timer)" @click="getCode">
{{ timer ? time + "S" : "获取" }}
</el-button>
</template>
</el-input>
<!-- 手机号键盘 -->
<div class="keyboard" v-if="telShow">
<div class="num">
<table>
<tr>
<td @click="changeTel(1)" :class="[tel === 1 ? 'tel' : '']">1</td>
<td @click="changeTel(2)" :class="[tel === 2 ? 'tel' : '']">2</td>
<td @click="changeTel(3)" :class="[tel === 3 ? 'tel' : '']">3</td>
<td rowspan="2" class="delTel" @click="delTel">×</td>
</tr>
<tr>
<td @click="changeTel(4)" :class="[tel === 4 ? 'tel' : '']">4</td>
<td @click="changeTel(5)" :class="[tel === 5 ? 'tel' : '']">5</td>
<td @click="changeTel(6)" :class="[tel === 6 ? 'tel' : '']">6</td>
</tr>
<tr>
<td @click="changeTel(7)" :class="[tel === 7 ? 'tel' : '']">7</td>
<td @click="changeTel(8)" :class="[tel === 8 ? 'tel' : '']">8</td>
<td @click="changeTel(9)" :class="[tel === 9 ? 'tel' : '']">9</td>
<td rowspan="2" class="comfirmTel" @click="comfirmTel">确定</td>
</tr>
<tr>
<td @click="changeTel(0)" colspan="3" :class="[tel === 0 ? 'tel' : '']">0</td>
</tr>
</table>
</div>
</div>
<!-- 验证码鍵盤 -->
<div class="keyboard" v-if="show">
<div class="num">
<table>
<tr>
<td @click="change(1)" :class="[code === 1 ? 'code' : '']">1</td>
<td @click="change(2)" :class="[code === 2 ? 'code' : '']">2</td>
<td @click="change(3)" :class="[code === 3 ? 'code' : '']">3</td>
<td rowspan="2" class="del" @click="del">×</td>
</tr>
<tr>
<td @click="change(4)" :class="[code === 4 ? 'code' : '']">4</td>
<td @click="change(5)" :class="[code === 5 ? 'code' : '']">5</td>
<td @click="change(6)" :class="[code === 6 ? 'code' : '']">6</td>
</tr>
<tr>
<td @click="change(7)" :class="[code === 7 ? 'code' : '']">7</td>
<td @click="change(8)" :class="[code === 8 ? 'code' : '']">8</td>
<td @click="change(9)" :class="[code === 9 ? 'code' : '']">9</td>
<td rowspan="2" class="comfirm" @click="comfirm">确定</td>
</tr>
<tr>
<td @click="change(0)" colspan="3" :class="[code === 0 ? 'code' : '']">0</td>
</tr>
</table>
</div>
</div>
</el-form-item>
</el-form>
</div>
</el-card>
<div style="text-align: center;margin-left: 10px;">
<el-button type="primary" :loading="loading" :disabled="!ruleForm.tel || !ruleForm.code"
@click="linkToPage">
登录
</el-button>
</div>
</div>
</div>
</template>
<script>
import storage from "store";
import { useLogin, getCode } from "@/api/index.js";
export default {
data() {
return {
time: 60,
ruleForm: {
tel: "",
code: "",
},
show: false,
telShow: false,
NUM: "",
NUMTEL: "",
result: [],
resultTel: [],
tel: '',
code: '',
timer: null,
loading: false,
};
},
computed: {
// 统计数量
num: function() {
return this.result.join("");
},
// 统计数量
numtel: function() {
return this.resultTel.join("");
},
},
methods: {
//登录
linkToPage() {
this.loading = true;
useLogin(this.ruleForm).then((res) => {
if (res.code == 200) {
storage.set("Access-Token", res.data.token); // 正常请求token
storage.set("User-Info", JSON.stringify(res.data)); // 正常请求token
setTimeout(() => {
this.$router.push("/index")
}, 2000);
} else {
this.$message.error(res.msg);
}
}).finally(() => {
this.loading = false;
});
},
//获取验证码
getCode() {
if (!this.ruleForm.tel) return;
// 检查手机号格式是否正确
const phoneNumberRegex = /^1[3456789]\d{9}$/;
if (!phoneNumberRegex.test(this.ruleForm.tel)) {
this.$message.warning('手机号格式不正确,请重新输入!');
return;
}
getCode(this.ruleForm).then((res) => {
if (res.code === 200) {
this.$message.success(res.msg);
this.timer = setInterval(() => {
if (this.time == 0) {
clearInterval(this.timer);
this.timer = null;
this.time = 60;
} else {
this.time--;
}
}, 1000);
} else {
this.$message.error(res.msg);
}
});
},
//展示数字键盘
viewShow(type) {
if (type === 'tel') {
this.telShow = !this.telShow
this.show = false
} else {
this.show = !this.show
this.telShow = false
}
},
handleInput() {
if (this.ruleForm.code.length >= 6) return;
},
//获取选中的数字 验证码
change(val, $event) {
//设置验证码的长度
if (this.ruleForm.code.length >= 6) return;
this.ruleForm.code += String(val)
if (this.result.length === 0) {
return false;
} else {
this.result.push(this.ruleForm.code);
this.NUM = this.result.join("");
}
},
//验证码刪除
del() {
this.ruleForm.code = this.ruleForm.code.slice(0, -1)
this.NUM = this.result.join("");
this.$emit("del", this.NUM);
},
//验证码確認按鈕
comfirm() {
this.$emit("comfirm", this.NUM);
this.show = false;
},
//获取选中的数字 手机号
changeTel(val, $event) {
//设置验证码的长度
if (this.ruleForm.tel.length >= 11) return;
this.ruleForm.tel += String(val)
if (this.resultTel.length === 0) {
return false;
} else {
this.resultTel.push(this.ruleForm.tel);
this.NUMTEL = this.resultTel.join("");
}
},
handleInputTel() {
if (this.ruleForm.tel.length >= 11) return;
},
//刪除 手机号
delTel() {
this.ruleForm.tel = this.ruleForm.tel.slice(0, -1)
this.NUMTEL = this.resultTel.join("");
this.$emit("delTel", this.NUMTEL);
},
//確認按鈕 手机号
comfirmTel() {
this.$emit("comfirmTel", this.NUMTEL);
this.telShow = false;
},
},
};
</script>
二、方法详解
1、数据详解:
ruleForm
对象包含了手机号和验证码两个属性;show
和telShow
分别表示验证码键盘和手机号键盘是否显示状态;NUM
和NUMTEL表示
存储选中的验证码和手机号;result
和resultTel
表示用于存储选中的验证码和手机号的数组;tel
和code
表示当前选中的手机号和验证码的数字;timer
用于控制获取验证码按钮的倒计时;loading
表示登录按钮的加载状态。
2、计算属性详解:
num:
用于将选中的验证码数字拼接为字符串;numtel:
用于将选中的手机号数字拼接为字符串。
3、方法详解:
viewShow
方法用于切换显示手机号键盘或验证码键盘。handleInput
方法用于限制验证码输入框的长度不超过6位。change
方法用于获取选中的验证码数字,并将其拼接到ruleForm.code
中。del
方法用于删除最后一个输入的验证码数字。comfirm
方法用于确认验证码的输入,同时关闭验证码键盘。- 手机号键盘相关方法同验证码键盘类似。
4、方法注释:
push():
用于向数组末尾添加一个或多个元素
slice():
方法接收两个参数起始位置和结束位置(不含结束位置)
this.ruleForm.tel.slice(0, -1)
表示获取this.ruleForm.tel
的子数组,从索引0开始,到倒数第一个元素(不含倒数第一个元素)结束。简单来说,它删除了this.ruleForm.tel
的最后一个字符,并将剩余的部分赋值给this.ruleForm.tel
。
三、Css样式
<style lang="less" scoped>
.login {
padding-top: 80px;
}
.login-title {
position: fixed;
left: 0;
top: 0;
display: flex;
align-items: center;
justify-content: space-between;
width: calc(100% - 80px);
height: 160px;
padding: 0 40px;
background-color: @theme-color;
color: #fff;
font-size: 38px;
font-weight: bold;
}
.login-content {
display: flex;
justify-content: center;
align-items: center;
padding: 200px;
}
.clearfix {
text-align: center;
color: #00aaff;
font-size: 26px;
}
.box-card {
height: 520px;
width: 480px;
border-radius: 20px;
}
.keyboard-wrapper {
user-select: none;
input {
width: 100%;
height: 50px;
font-size: 26px;
}
.keyboard {
position: fixed;
margin-top: 10px;
width: 60%;
.num {
table {
width: 32%;
border: 1px solid #ccc;
border-collapse: collapse;
background: #fff;
td {
height: 60px;
vertical-align: middle;
color: #333;
font-size: 20px;
border: 1px solid #ccc;
text-align: center;
}
td.active {
background: #ccc;
}
.del {
background: #eee;
}
.comfirm {
font-size: 16px;
width: 80px;
background: #62c7eb;
color: #fff;
}
.delTel {
background: #eee;
}
.comfirmTel {
font-size: 16px;
width: 80px;
background: #118eeb;
color: #fff;
}
}
}
}
}
</style>
手机号数字键盘效果图
验证码数字键盘效果图