Files
aida_front/src/component/LoginPage/verificationCodeInput.vue

183 lines
3.8 KiB
Vue
Raw Normal View History

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