107 lines
2.2 KiB
Vue
107 lines
2.2 KiB
Vue
<template>
|
|
<div class="input-code">
|
|
<input
|
|
ref="inputRef"
|
|
type="tel"
|
|
maxlength="1"
|
|
v-for="(v, i) in props.length"
|
|
:key="i"
|
|
v-model="code[i]"
|
|
@input="handleInput(i)"
|
|
@keydown.delete="handleDelete(i)"
|
|
@paste="handlePaste"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, ref, computed, watch, nextTick } from 'vue'
|
|
const emit = defineEmits(['submit', 'update:modelValue'])
|
|
const props = defineProps({
|
|
length: {
|
|
type: Number,
|
|
default: 6
|
|
}
|
|
})
|
|
const inputRef = ref('')
|
|
const code = ref([])
|
|
const codeStr = computed(() => code.value.join(''))
|
|
watch(codeStr, (newVal) => {
|
|
emit('update:modelValue', newVal)
|
|
})
|
|
const resetCode = (size = props.length) => {
|
|
code.value = []
|
|
for (let i = 0; i < size; i++) {
|
|
code.value.push('')
|
|
}
|
|
}
|
|
resetCode(props.length)
|
|
const handleInput = (index: number) => {
|
|
const value = code.value[index]
|
|
if (value) {
|
|
if (/[0-9]/.test(value)) {
|
|
code.value[index] = value
|
|
focusLast()
|
|
} else {
|
|
code.value[index] = ''
|
|
}
|
|
}
|
|
submit()
|
|
}
|
|
const handleDelete = (index: number) => {
|
|
if (code.value[index].length == 0) {
|
|
focusLast(-1)
|
|
}
|
|
}
|
|
const handlePaste = (e: ClipboardEvent) => {
|
|
const text = e.clipboardData?.getData('text')
|
|
if (text) {
|
|
const nums = text.match(/[0-9]/g) || []
|
|
if (nums.length === code.value.length) {
|
|
code.value = [...nums]
|
|
focusLast()
|
|
nextTick(submit)
|
|
}
|
|
}
|
|
}
|
|
// 聚焦最后一个没有输入的
|
|
const focusLast = (step = 0) => {
|
|
let index = code.value.findIndex((item) => !item) + step
|
|
index < 0 && (index = 0)
|
|
if (index >= 0 && index < props.length) {
|
|
inputRef.value[index]?.focus?.()
|
|
}
|
|
if (code.value.every((item) => item.length)) {
|
|
inputRef.value?.forEach((item) => item.blur?.())
|
|
}
|
|
}
|
|
const submit = () => {
|
|
if (codeStr.value.length === props.length) {
|
|
emit('submit', codeStr.value)
|
|
}
|
|
}
|
|
onMounted(() => {
|
|
focusLast()
|
|
})
|
|
defineExpose({
|
|
resetCode
|
|
})
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.input-code {
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
> input {
|
|
width: 7rem;
|
|
height: 7rem;
|
|
border-radius: 1rem;
|
|
border: 0.02rem solid #dfdfdf;
|
|
text-align: center;
|
|
margin: auto;
|
|
}
|
|
}
|
|
</style>
|