验证码防抖

This commit is contained in:
李志鹏
2025-10-13 10:27:07 +08:00
parent 39839a4d45
commit c3cd00dc1c

View File

@@ -1,151 +1,177 @@
<template> <template>
<div id="app"> <div id="app">
<div class="captcha"> <div class="captcha">
<input v-for="(c, index) in getCtData" :key="index" <input
type="text" v-for="(c, index) in getCtData"
v-model="getCtData[index]" ref="input" :key="index"
inputmode="numeric" type="text"
pattern="[0-9]*" v-model="getCtData[index]"
@input="e => {onInput(e.target.value, index)}" ref="input"
@keydown="e => {onKeydown(e, index)}" inputmode="numeric"
@keypress="e => {onKeypress(e)}" pattern="[0-9]*"
@focus="onFocus" @input="(e) => onInput(e.target.value, index)"
:disabled="loading" @keydown="(e) => onKeydown(e, index)"
> @keypress="(e) => onKeypress(e)"
</div> @focus="onFocus"
</div> @pause="onPause"
:disabled="loading"
/>
</div>
</div>
</template> </template>
<script> <script>
import { defineComponent } from 'vue' import { defineComponent } from "vue";
export default defineComponent({ export default defineComponent({
props:['ct'], props: ["ct"],
data() { data() {
return { return {
loading: false, loading: false,
} timeout: null,
}, };
computed: { },
getCtData(){ computed: {
return this.ct getCtData() {
}, return this.ct;
ctSize() { },
return this.getCtData.length; ctSize() {
}, return this.getCtData.length;
cIndex() { },
let i = this.getCtData.findIndex(item => item === ''); cIndex() {
i = (i + this.ctSize) % this.ctSize; let i = this.getCtData.findIndex((item) => item === "");
return i; i = (i + this.ctSize) % this.ctSize;
}, return i;
lastCode() { },
return this.getCtData[this.ctSize - 1]; lastCode() {
} return this.getCtData[this.ctSize - 1];
}, },
watch: { },
cIndex() { watch: {
this.resetCaret(); cIndex() {
}, this.resetCaret();
lastCode(newVal,oldVal) { },
if (newVal && newVal != oldVal) { lastCode(newVal, oldVal) {
this.$refs.input[this.ctSize - 1].blur(); if (newVal && newVal != oldVal) {
this.sendCaptcha(); this.$refs.input[this.ctSize - 1].blur();
} this.sendCaptcha();
} }
}, },
mounted() { },
this.resetCaret(); mounted() {
}, this.resetCaret();
methods: { },
onInput(val, index) { methods: {
console.log('input',val,index) onInput(val, index) {
// val = val.replace(/[^0-9]/g, ''); console.log("input", val, index);
val = String(val).replace(/\D/g, ''); clearTimeout(this.timeout);
this.getCtData[index] = val this.timeout = setTimeout(() => {
if (index == this.ctSize - 1) { console.log("input=>", val, index);
this.getCtData[this.ctSize - 1] = val[0]; // 最后一个码,只允许输入一个字符。 // val = val.replace(/[^0-9]/g, '');
} else if(val.length > 1) { val = String(val).replace(/\D/g, "");
let i = index; this.getCtData[index] = val;
for (i = index; i < this.ctSize && i - index < val.length; i++) { if (index == this.ctSize - 1) {
this.getCtData[i] = val[i]; this.getCtData[this.ctSize - 1] = val[0]; // 最后一个码,只允许输入一个字符。
} } else if (val.length > 1) {
this.resetCaret(); let i = index;
}else if(!(val+'')){ for (
this.getCtData[index] = '' i = index;
} i < this.ctSize && i - index < val.length;
}, i++
// 重置光标位置。 ) {
resetCaret() { this.getCtData[i] = val[i];
this.$refs.input[this.ctSize-1].focus(); }
}, this.resetCaret();
onFocus() { } else if (!(val + "")) {
// 监听 focus 事件,将光标重定位到“第一个空白符的位置”。 this.getCtData[index] = "";
let index = this.getCtData.findIndex(item => item === ''); }
index = (index + this.ctSize) % this.ctSize; }, 10);
this.$refs.input[index].focus(); },
}, onPause() {},
onKeypress(e) { // 重置光标位置。
// 只允许输入数字0-9 resetCaret() {
const char = String.fromCharCode(e.which); this.$refs.input[this.ctSize - 1].focus();
if (!/[0-9]/.test(char)) { },
e.preventDefault(); onFocus() {
} // 监听 focus 事件,将光标重定位到“第一个空白符的位置”。
}, let index = this.getCtData.findIndex((item) => item === "");
onKeydown(e, index) { index = (index + this.ctSize) % this.ctSize;
// 处理删除键 this.$refs.input[index].focus();
if (e.key === 'Backspace' || e.key === 'Delete') { },
const val = e.target.value; onKeypress(e) {
if (val === '') { // 只允许输入数字0-9
// 删除上一个input里的值并对其focus。 const char = String.fromCharCode(e.which);
if (index > 0) { if (!/[0-9]/.test(char)) {
this.getCtData[index - 1] = ''; e.preventDefault();
this.$refs.input[index - 1].focus(); }
} },
} onKeydown(e, index) {
} // 处理删除键
// 阻止其他非数字字符 if (e.key === "Backspace" || e.key === "Delete") {
else if (e.key && !/[0-9]/.test(e.key) && !['Backspace', 'Delete', 'Tab', 'Enter', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) { const val = e.target.value;
e.preventDefault(); if (val === "") {
} // 删除上一个input里的值并对其focus。
}, if (index > 0) {
this.getCtData[index - 1] = "";
this.$refs.input[index - 1].focus();
}
}
}
// 阻止其他非数字字符
else if (
e.key &&
!/[0-9]/.test(e.key) &&
![
"Backspace",
"Delete",
"Tab",
"Enter",
"ArrowLeft",
"ArrowRight",
"ArrowUp",
"ArrowDown",
].includes(e.key)
) {
e.preventDefault();
}
},
sendCaptcha() { sendCaptcha() {
let password = this.getCtData.map(item => item).join(''); let password = this.getCtData.map((item) => item).join("");
this.$emit('sendCaptcha',password) this.$emit("sendCaptcha", password);
}, },
reset() { reset() {
// 重置。一般是验证码错误时触发。 // 重置。一般是验证码错误时触发。
this.getCtData = this.getCtData.map(item => ''); this.getCtData = this.getCtData.map((item) => "");
this.resetCaret(); this.resetCaret();
} },
} },
}) });
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.captcha { .captcha {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
input { input {
width: 8.7rem; width: 8.7rem;
height: 8.7rem; height: 8.7rem;
border: 0.1rem solid #B4BED7; border: 0.1rem solid #b4bed7;
border-radius: 2rem; border-radius: 2rem;
text-align: center; text-align: center;
font-size: 2.4rem; font-size: 2.4rem;
line-height: 8.7rem; line-height: 8.7rem;
outline: none; outline: none;
} }
input:last-of-type { input:last-of-type {
margin-right: 0; margin-right: 0;
} }
input:disabled { input:disabled {
color: #000; color: #000;
background-color: #fff; background-color: #fff;
} }
.msg { .msg {
text-align: center; text-align: center;
} }
</style> </style>