效果
实现
实现思路
将 input 输入框颜色设置透明,定位到自定义输入框的样式上面,点击输入框时聚焦,可以输入内容,再将输入的值分别赋值到已经渲染好的横线上,最后监听 input 的值,实现输入完直接自动登录。
实现代码
父组件 login/index.wxa
<button bindtap="getCode">获取短信验证码</button>
<Code wx:if="{{showCode}}" phoneNumber="{{phone}}" currentTime="{{currentTime}}" clearCode="{{clearCode}}" bindclose="close" bindcodeLogin="codeLogin" bindgetCode="getCode"></Code>
data = {
showCode: false,
phone: "",
currentTime: 0,
clearCode: false, // 是否清空code输入框
};
// 先登录后选医院 获取短信验证码
getCode() {
try {
if($data.currentTime <= 0) {
var phoneReg = /^1[3456789]\d{9}$/;
if (!phoneReg.test($data.phone)) {
wx.showToast({
title: "请输入正确的手机号",
icon: "none",
duration: 2000
});
return;
}
if (!$data.isConfirm) {
wx.showToast({
title: "请勾选用户协议与隐私政策",
icon: "none",
duration: 2000
});
return;
}
// 获取验证码接口
wx.http.POST({
url: "/generateCode",
thirdPartyAPI: true,
loadTitle: "发送中...",
isLoad: true,
needLog: true,
params: {
mobile: $data.phone,
userType: 0,
},
success(res) {
if (res.status == 200) {
that.setData({currentTime: 60, showCode: true, clearCode: false})
// 在父组件倒计时,将值 currentTime 传给子组件显示
let interval = null;
interval = setInterval(() => {
$data.currentTime--;
that.setData({currentTime: $data.currentTime});
if ($data.currentTime <= 0) {
clearInterval(interval);
that.resetCode()
}
}, 1000);
} else {
wx.showToast({
title: res.message,
icon: "none",
duration: 2000
});
that.resetCode()
}
},
fail() {
that.resetCode()
}
});
} else {
this.setData({showCode: true})
}
} catch (error) {
wx.http.LOG('发送验证码失败:' + JSON.stringify(error), {}, '', 'error')
}
}
// 重置获取验证码功能
resetCode() {
that.setData({
currentTime: 0
});
}
codeLogin(e) {
this.setData({code: e.detail, clearCode: false}) // 这里的 clearCode 要先设置为 false,因为子组件是监听进行操作的,不设置的话值一直为 true,没有变化监听不到
// 调登录接口
if() {
// 登录失败
// 验证码错误将 clearCode 设置为 true 进行提示和清空输入框
that.setData({clearCode: true})
}
}
close(e) {
this.setData({showCode: false})
}
<config>
{
"usingComponents": {
"Code": "./code"
}
}
</config>
子组件 login/code.wxa
<template>
<view class="page">
<image
class="codeCloseIcon"
src="../../../static/img/login/close.svg"
mode="widthFix"
bindtap="close"
></image>
<view class="codeCard">
<view class="weightTitle">输入验证码</view>
<view>验证码已发送至 {{phoneNumber}}</view>
<input
type="number"
model:value="{{code}}"
focus="{{focus}}"
bindtap="onFocus"
bind:blur="onBlur"
bind:input="getCodeValue"
maxlength="6"
class="input"
>
<view class="codeBox">
<view class="codeBoxItem" wx:for="{{codeBox}}" wx:for-item="item">
<text class="codeText">{{code[index]}}</text>
<view class="line" wx:if="{{!code[index] && focus && index == 0}}">|</view>
</view>
</view>
<view class="redText" wx:if="{{clearInput}}">验证码错误,请重新输入</view>
<view class="codeTextColor" wx:if="{{currentTime > 0}}">{{'重新发送(' + currentTime + 's)'}}</view>
<view class="codeTextColor" bindtap="getCode" wx:else>{{'重新获取验证码'}}</view>
</view>
</view>
</template>
<script>
import { Page } from "@wxa/core";
var that, $data;
Component({
properties: {
phoneNumber: {
type: String,
value: ''
},
currentTime: {
type: String,
value: ''
},
clearCode: {
type: Boolean,
value: false,
// 监听
observer(val) {
val && this.clear(val)
}
}
},
data: {
code: "",
codeBox: [0, 1, 2, 3, 4, 5], // 用于渲染横线,只需要数组长度
focus: false, // 输入框焦点
clearInput: false,
},
lifetimes: {
attached() {
that = this;
$data = this.data;
// 一进入页面自动弹出键盘
this.onFocus()
}
},
methods: {
clear(e) {
this.setData({clearInput: e, code: ""})
},
onFocus() {
this.setData({ focus: true });
},
onBlur() {
this.setData({ focus: false });
},
// 获取输入的验证码
getCodeValue(e) {
this.setData({clearInput: false})
let code = e.detail.value;
if (code && code.length === 6) {
// 调用父组件的 codeLogin 方法,直接登录
this.triggerEvent("codeLogin", code)
}
},
close() {
this.triggerEvent("close")
},
getCode() {
this.triggerEvent("getCode")
}
}
})
</script>
<config>
{
"usingComponents": {}
}
</config>
<style lang="scss">
.page {
width: 100%;
height: 100%;
color: #333333;
position: relative;
background: #ffffff;
& > .codeCloseIcon {
position: absolute;
top: 8%;
left: 8%;
width: 60rpx;
}
.codeCard {
position: absolute;
top: 18%;
left: 0;
margin: 0 8%;
width: 84%;
display: flex;
flex-direction: column;
.weightTitle {
font-size: 48rpx;
font-weight: bold;
margin-bottom: 14rpx;
}
.input {
color: transparent;
caret-color: transparent;
// 输入框用这种方式定位,在真机上也不会出现光标
position: absolute;
width: 200%;
height: 100%;
left: -100%;
bottom: 20%;
}
.codeBox {
display: flex;
justify-content: space-between;
padding-top: 40rpx;
margin-bottom: 20rpx;
height: 50rpx;
.codeBoxItem {
width: 10%;
border-bottom: 2rpx solid #979797;
text-align: center;
.codeText {
font-size: 40rpx;
}
.line {
color: #226592;
font-size: 34rpx;
animation: cursorImg 1s infinite steps(1, start); // 模拟光标闪动
@keyframes cursorImg {
0%,
100% {
opacity: 0;
}
50% {
opacity: 1;
}
}
}
}
}
.redText {
color: #ff0000;
margin-bottom: 20rpx;
}
.codeTextColor {
color: #979797;
}
}
}
</style>