参考文章:Vue如何实现验证码输入交互
验证码输入,支持复制粘贴
实现效果
html部分:验证码数组循环input输入框,最后一个输入框限制只能输入1个长度的值;输入监听、删除监听、焦点监听
js部分:
特别注意:uniapp 的 input 组件是封装过的,不是原生input标签,在调用focus,blur事件时,需要这么写:
可以控制台打印 this.$refs.input[0].$el.childNodes 查看元素结构
完整代码
<template>
<view :style="colorStyle" class="main-page">
<view v-if="isFirst" class="first-box">
我们将发送验证码到您的绑定手机:
<view class="phone">{{phoneComputed}}</view>
<view class="code-box">
验证码:
<input
v-for="(item, index) in codes"
:key="index"
ref="input"
v-model="codes[index]"
type="number"
:maxlength="index === cSize - 1 ? 1 : 6"
:disabled="loading"
@input="e => {onInput(e.target.value, index)}"
@keydown.delete="onKeydown(index)"
@focus="onFocus" />
<button class="theme-color code-btn" :disabled="disabled" :class="disabled === true ? 'on' : ''" @click="getCode">
{{ text }}
</button>
</view>
</view>
<view v-if="!isFirst" class="second-box">
<form>
<view class="form-item">
输入新密码:
<input v-model="password" type="text" maxlength="20" password />
</view>
<view class="form-item">
确认新密码:
<input v-model="confirmPwd" type="text" maxlength="20" password />
</view>
<view class="remind-txt">必须是6-20个英文字母、数字或符号(除空格),且字母、数字和标点符号至少包含两种</view>
</form>
</view>
<button class="set-btn-main" hover-class="none" @tap="onSubmit">{{isFirst ? $t('下一步') : $t('确定')}}</button>
</view>
</template>
<script>
import colors from '@/mixins/color.js';
import sendVerifyCode from "@/mixins/SendVerifyCode";
export default {
mixins: [colors, sendVerifyCode],
data() {
return {
text: this.$t('获取'),
isFirst: true,
loading: false,
phone: '13741741741',
codes: ['', '', '', '', '', ''],
password: '',
confirmPwd: ''
}
},
computed: {
phoneComputed () {
let str = '';
for (let i=0; i<this.phone.length; i++) {
if (i > 2 && i < 7) {
str += '*';
} else {
str += this.phone[i];
}
}
return str
},
cSize() {
return this.codes.length;
},
cIndex() {
let i = this.codes.findIndex(item => item === '');
i = (i + this.cSize) % this.cSize;
return i;
},
lastCode() {
return this.codes[this.cSize - 1];
}
},
watch: {
cIndex() {
this.resetCaret();
},
lastCode(val) {
// 输入最后一位验证码,触发失去焦点并自动校验验证码
if (val) {
this.$refs.input[this.cSize - 1].$el.childNodes[0].childNodes[1].blur();
this.sendCaptcha();
}
}
},
mounted() {
console.log(this.$refs.input[0].$el.childNodes)
this.resetCaret();
},
methods: {
onInput(val, index) {
val = val.replace(/\s/g, '');
if (index == this.cSize - 1) {
// 最后一个码,只允许输入一个字符。
this.$nextTick(() => {
this.$set(this.codes, [this.cSize - 1], val[0]);
})
} else if(val.length > 1) {
let i = index;
for (i = index; i < this.cSize && i - index < val.length; i++) {
this.codes[i] = val[i];
}
this.resetCaret();
}
},
// 重置光标位置。
resetCaret() {
this.$refs.input[this.cSize-1].$el.childNodes[0].childNodes[1].focus();
},
onFocus() {
// 监听 focus 事件,将光标重定位到“第一个空白符的位置”。
let index = this.codes.findIndex(item => item === '');
index = (index + this.cSize) % this.cSize;
this.$refs.input[index].$el.childNodes[0].childNodes[1].focus();
},
onKeydown(index) {
let val = this.codes[index];
if (!val || val === '') {
// 删除上一个input里的值,并对其focus。
if (index > 0) {
this.$nextTick(() => {
this.$set(this.codes, [index - 1], '');
this.$refs.input[index - 1].$el.childNodes[0].childNodes[1].focus();
// 删除上一个input值并移动focus焦点,光标出现在数字左边-尝试用定时器处理(效果不明显)
// setTimeout(() => {
// this.$refs.input[index - 1].$el.childNodes[0].childNodes[1].focus();
// }, 100)
})
}
}
},
sendCaptcha() {
console.log('发送验证码到服务器');
this.$util.Tips({
title: this.$t(`发送验证码到服务器`)
});
// 此时无法操作 input。。
this.loading = true;
// 假设验证码错误情况下重置输入框
setTimeout(() => {
this.$util.Tips({
title: this.$t(`验证码错误`)
});
this.loading = false;
this.$nextTick(() => {
this.reset();
})
}, 3000)
},
// 重置。一般是验证码错误时触发。
reset() {
this.codes = this.codes.map(item => '');
console.log(this.codes)
this.resetCaret();
},
getCode() {
this.sendCode();
},
onSubmit () {
this.loading = true;
if (this.isFirst) {
if (this.codes.includes('')) {
return this.$util.Tips({
title: this.$t(`验证码不正确`)
});
}
this.isFirst = !this.isFirst;
this.loading = false;
return
}
if (!this.password) {
return this.$util.Tips({
title: this.$t(`请输入新密码`)
});
}
if (this.confirmPwd !== this.password) {
return this.$util.Tips({
title: this.$t(`确认密码与新密码不一致`)
});
}
let regex = /^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$)([^\u4e00-\u9fa5\s]){6,20}$/;
if (!regex.test(this.password)) {
return this.$util.Tips({
title: this.$t(`密码格式不正确`)
});
}
console.log('提交')
}
}
}
</script>