vue6格验证码
根据项目需求,可将发送验证码功能抽离成单独的组件使用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="screen-orientation" content="portrait">
<meta name="full-screen" content="no">
<meta name="x5-orientation" content="portrait">
<meta name="x5-fullscreen" content="false">
<meta name="viewport" content="initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no,width=device-width,viewport-fit=cover">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="format-detection" content="telephone=no">
<title>vue-6格验证码-支持自动填充</title>
<script src="https://www.jq22.com/demo/vue6pwd202209080005/js/rem.js"></script>
<link rel="stylesheet" href="https://www.jq22.com/demo/vue6pwd202209080005/css/index.css">
</head>
<body>
<div id="wrapper" v-cloak>
<div class="content">
<h2>{{ this.phone | mobileToStar }}</h2>
<div class="input-box code">
<div class="input-content" @keydown="keydown" @keyup="keyup" @paste="paste" @mousewheel="mousewheel" @input="inputEvent">
<input
ref="firstinput"
v-model.trim.number="input"
class="code-text"
max="9"
min="0"
maxlength="6"
data-index="0"
type="tel"
:class="{'btnLight': iptFocus || input}"
@focus="handleFocus(1)"
@blur="handleBlur(1)"
>
<input v-model.trim.number="input" class="code-text" max="9" min="0" maxlength="6" data-index="1" type="tel" :class="{'btnLight': iptFocus || input}" @focus="handleFocus(2)" @blur="handleBlur(2)">
<input v-model.trim.number="input" class="code-text" max="9" min="0" maxlength="6" data-index="2" type="tel" :class="{'btnLight': iptFocus || input}" @focus="handleFocus(3)" @blur="handleBlur(3)">
<input v-model.trim.number="input" class="code-text" max="9" min="0" maxlength="6" data-index="3" type="tel" :class="{'btnLight': iptFocus || input}" @focus="handleFocus(4)" @blur="handleBlur(4)">
<input v-model.trim.number="input" class="code-text" max="9" min="0" maxlength="6" data-index="4" type="tel" :class="{'btnLight': iptFocus || input}" @focus="handleFocus(5)" @blur="handleBlur(5)">
<input v-model.trim.number="input" class="code-text" max="9" min="0" maxlength="6" data-index="5" type="tel" :class="{'btnLight': iptFocus || input}" @focus="handleFocus(6)" @blur="handleBlur(6)">
</div>
</div>
<div class="form-button">
<button
v-show="showTime"
type="button"
class="btn-submit"
@click="sendCaptcha"
>重新发送</button>
<button
v-show="!showTime"
type="button"
class="btn-submit active"
><span>重新发送({{ count }}s)</span></button>
</div>
</div>
<!-- <Captcha ref="captchaLogin" @complete="handleLogin" />
handleLogin(value) {
this.loginForm.smsCode = value.join('')
},
handleClear() {
this.loginForm.smsCode = ''
this.$refs['captchaLogin'].clear()
this.$refs['captchaLogin'].focus()
},
-->
</div>
<script type="text/javascript" src="https://www.jq22.com/jquery/jquery-1.10.2.js"></script>
<script type="text/javascript" src="https://www.jq22.com/demo/vue6pwd202209080005/js/vue.min.js"></script>
<script>
// problem: 点击短信通知栏的“复制”后,6位验证码会自动显示在软键盘左上角,点击一下即完成填充,某些 android 手机不生效,这个事件是 inputEvent,是先填充内容再进行处理的,但是输入框的 input maxlength 是1,如果更改该 maxlength 值为6可以实现填充,但是,如果从中间输入,最后一个输入框会输入最多6个值,输入完成后自动填充了最后一个输入框的6个值了。
// 如果有更好的解决办法,欢迎留言交流!
/*
toast 提示:绑定在window上,使用方法:showMessage('提示信息', time)
*/
window.showMessage = function( $msg, $time, callback ) {
$time = $time === 0 ? 0 : ($time || 1000);
var oDiv = document.createElement("div");
oDiv.setAttribute("id", "toastTip");
// var oBody = document.getElementsByClassName('wrapper');
var oBody = document.getElementsByTagName('body');
oBody.append(oDiv);
$('#toastTip').text( $msg );
$('#toastTip').fadeIn();
setTimeout(function() {
callback()
$('#toastTip').fadeOut(function() {
$('#toastTip').remove();
});
}, $time);
}
var mainVM = new Vue({
el: "#wrapper",
data: {
pasteResult: [], // 存放粘贴进来的数字
iptFocus: [],
phone: 18800001111,
smsCode: '',
countryCode: '+86',
showTime: true,
count: '', // 初始化次数
timer: null,
},
filters: {
/**
* @param {string} 18310746666
* @returns {string} 183****6666 前3后4,中间4星
*/
mobileToStar (value) {
if (!value) return null
let reg = '', str = ''
if (!isNaN(value)) value = value.toString()
let len = value.length
if (len === 4) {
reg = /^({1})({2})({1})$/
str = value.replace(reg, '$1**$3')
} else if (len === 5) {
reg = /^({1})({3})({1})$/
str = value.replace(reg, '$1***$3')
} else if (len > 5 && len < 21) { // 隐藏中间4位
var num = Math.floor(len / 2) - 2
str = value.substr(0, num) + '****' + value.substr(num + 4)
} else if (len > 20) {
str = value.substr(0, 6) + '****' + value.substr(len - 6)
}
return str
}
},
computed: {
input() {
return this.pasteResult.length === 6 ? this.pasteResult : ['', '', '', '', '', '']
}
},
mounted() {
document.getElementById('wrapper').style.display = 'block';
// 这里如果是从上一页过来可以使用 session 取出上一页传递过来的 phone
// 可以从上一页发送验证码,也可以在此页面发送验证码
if (this.validMobile()) {
const TIME_COUNT = 60 // 更改倒计时时间
if (!this.timer) {
this.count = TIME_COUNT
this.showTime = false
this.timer = setInterval(() => {
if (this.count > 1 && this.count <= TIME_COUNT) {
this.count--
} else {
this.showTime = true
clearInterval(this.timer) // 清除定时器
this.timer = null
}
}, 1000)
}
}
this.focus()
this.$on('complete', this.handleComplete)
},
methods: {
sendCaptcha() { // 发送验证码
var phonePre = this.countryCode.replace(/^\+/, '')
if (this.validMobile()) {
this.handleClear()
const TIME_COUNT = 60 // 更改倒计时时间
if (!this.timer) {
this.count = TIME_COUNT
this.showTime = false
this.timer = setInterval(() => {
if (this.count > 1 && this.count <= TIME_COUNT) {
this.count--
} else {
this.showTime = true
clearInterval(this.timer) // 清除定时器
this.timer = null
}
}, 1000)
}
}
},
handleComplete(value) {
this.smsCode = value.join('')
if (this.validCaptcha()) {
showMessage('登录', 1800)
}
},
handleClear() {
this.smsCode = ''
this.clear()
this.focus()
},
// 验证手机号
validMobile(hasToast) {
const reg = /^1{9}$/
if (reg.test(this.phone)) {
return true
} else {
this.errorMsg = '请输入正确的手机号'
if (hasToast) {
// this.$toast(this.errorMsg)
showMessage(this.errorMsg, 1800)
}
return false
}
},
// 验证验证码
validCaptcha() {
var reg = /\d{6}/
if (reg.test(this.smsCode)) {
return true
} else {
this.errorMsg = '请输入正确的验证码'
let that = this
showMessage(this.errorMsg, 1800, () => {
this.handleClear()
});
// this.$toast({
// message: that.errorMsg,
// onClose() {
// that.handleClear()
// }
// })
return false
}
},
focus() {
this.$nextTick(() => {
this.$refs.firstinput.focus()
})
},
clear() {
this.pasteResult = []
},
handleBlur(index) {
this.iptFocus = false
this.$set(this.iptFocus, index, this.iptFocus)
},
handleFocus(index) {
this.iptFocus = true
this.$set(this.iptFocus, index, this.iptFocus)
},
// inputEvent (e) {
// var index = e.target.dataset.index * 1;
// var el = e.target;
// this.$set(this.input, index, el.value.slice(0, 1))
// },
inputEvent(e) {
var index = e.target.dataset.index * 1
var el = e.target
var elVal = el.value
var elStr = elVal.toString()
if (elStr.length === 6 ) {
var reg = /^\d{6}/
if (reg.test(elStr)) {
let v = elStr.match(reg)
v = v
this.pasteResult = [...v]
this.$emit('complete', this.input)
return false
} else {
this.pasteResult = elStr.split('')
document.activeElement.blur()
this.$emit('complete', this.input)
}
}
var val = el.value.slice(0, 1)
if (val) {
this.$set(this.input, index, val)
} else {
if (this.input === '') {
this.$set(this.input, index, '0')
setTimeout(() => {
this.$set(this.input, index, '')
}, 0)
}
}
},
keydown(e) {
var index = e.target.dataset.index * 1
var el = e.target
if (e.key === 'Backspace') {
if (this.input.length > 0) {
this.$set(this.input, index, '')
} else {
if (el.previousElementSibling) {
el.previousElementSibling.focus()
this.$set(this.input, index - 1, '')
}
}
} else if (e.key === 'Delete') {
if (this.input.length > 0) {
this.$set(this.input, index, '')
} else {
if (el.nextElementSibling) {
this.$set(this.input, index = 1, '')
}
}
if (el.nextElementSibling) {
el.nextElementSibling.focus()
}
} else if (e.key === 'Home') {
el.parentElement.children && el.parentElement.children.focus()
} else if (e.key === 'End') {
el.parentElement.children && el.parentElement.children.focus()
} else if (e.key === 'ArrowLeft') {
if (el.previousElementSibling) {
el.previousElementSibling.focus()
}
} else if (e.key === 'ArrowRight') {
if (el.nextElementSibling) {
el.nextElementSibling.focus()
}
} else if (e.key === 'ArrowUp') {
if (this.input * 1 < 9) {
this.$set(this.input, index, (this.input * 1 + 1).toString())
}
} else if (e.key === 'ArrowDown') {
if (this.input * 1 > 0) {
this.$set(this.input, index, (this.input * 1 - 1).toString())
}
}
},
keyup(e) {
var index = e.target.dataset.index * 1
var el = e.target
var elVal = el.value
var elStr = elVal.toString()
if (elStr.length === 6 ) {
var reg = /^\d{6}/
if (reg.test(elStr)) {
let v = elStr.match(reg)
v = v
this.pasteResult = [...v]
this.$emit('complete', this.input)
return false
} else {
this.pasteResult = elStr.split('')
document.activeElement.blur()
this.$emit('complete', this.input)
}
}
var val = el.value.slice(0, 1)
// if (/Digit|Numpad/i.test(e.code)) {
// digit数字;numpad是指整个小键盘的所有键
if ((/Digit|Numpad/i.test(e.code) || e.isTrusted) && val) {
// this.$set(this.input, index, e.code.replace(/Digit|Numpad/i, ''));
el.nextElementSibling && el.nextElementSibling.focus()
if (index === 5) {
// 注意:android ios keyup事件不同,android获取值是keydown
if (this.input.join('').length === 6) {
document.activeElement.blur()
this.$emit('complete', this.input)
}
} else {
if (this.input.join('').length === 6) {
document.activeElement.blur()
this.$emit('complete', this.input)
}
}
} else {
if (this.input === '') {
// this.$set(this.input, index, '');
this.$set(this.input, index, '0')
setTimeout(() => {
this.$set(this.input, index, '')
}, 0)
}
}
},
mousewheel(e) {
var index = e.target.dataset.index
if (e.wheelDelta > 0) {
if (this.input * 1 < 9) {
this.$set(this.input, index, (this.input * 1 + 1).toString())
}
} else if (e.wheelDelta < 0) {
if (this.input * 1 > 0) {
this.$set(this.input, index, (this.input * 1 - 1).toString())
}
} else if (e.key === 'Enter') {
if (this.input.join('').length === 6) {
document.activeElement.blur()
this.$emit('complete', this.input)
}
}
},
paste(e) {
e.clipboardData.items.getAsString(str => {
if (str.toString().length === 6) {
this.pasteResult = str.split('')
document.activeElement.blur()
this.$emit('complete', this.input)
}
})
}
}
})
</script>
</body>
</html>
页:
[1]