feat: award页面
This commit is contained in:
236
src/views/AwardPage/components/VerificationCodeInput.vue
Normal file
236
src/views/AwardPage/components/VerificationCodeInput.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div class="verification-code-input">
|
||||
<input
|
||||
v-for="(code, index) in verificationCode"
|
||||
:key="index"
|
||||
type="text"
|
||||
:value="verificationCode[index]"
|
||||
ref="inputRefs"
|
||||
inputmode="numeric"
|
||||
pattern="[0-9]*"
|
||||
:disabled="loading"
|
||||
@input="(e) => onCodeInput(e, index)"
|
||||
@paste="(e) => onCodePaste(e, index)"
|
||||
@keydown="(e) => onCodeKeydown(e, index)"
|
||||
@keypress="(e) => onCodeKeypress(e)"
|
||||
@focus="onCodeFocus"
|
||||
class="code-input"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick } from 'vue'
|
||||
|
||||
interface Props {
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'complete', code: string): void
|
||||
(e: 'change', code: string): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
loading: false
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
// 验证码输入相关
|
||||
const verificationCode = ref(['', '', '', '', '', ''])
|
||||
const inputRefs = ref<HTMLInputElement[]>([])
|
||||
|
||||
// 验证码输入相关方法
|
||||
const onCodeInput = (e: Event, index: number) => {
|
||||
const input = e.target as HTMLInputElement
|
||||
const val = input.value
|
||||
const cleanVal = String(val).replace(/\D/g, '')
|
||||
|
||||
// 检查是否已经输入了5位数字(不包括当前正在输入的第6位)
|
||||
const filledCount = verificationCode.value.filter(v => v !== '').length
|
||||
const isAlmostComplete = filledCount >= 5
|
||||
|
||||
// 如果已经输入了5位数字,检查当前输入是否会导致超过6位
|
||||
if (isAlmostComplete && cleanVal.length === 1 && verificationCode.value[index] === '') {
|
||||
// 如果当前输入框是空的,说明这是第6位输入,允许
|
||||
// 如果当前输入框有值,说明是替换,不允许
|
||||
// 这里我们允许输入,但会在设置后检查
|
||||
}
|
||||
|
||||
// 只处理单个字符输入,粘贴由 paste 事件处理
|
||||
if (cleanVal.length === 1) {
|
||||
verificationCode.value[index] = cleanVal
|
||||
|
||||
// 自动跳转到下一个输入框(只有在还没到第6个时)
|
||||
if (index < 5) {
|
||||
nextTick(() => {
|
||||
inputRefs.value[index + 1]?.focus()
|
||||
})
|
||||
}
|
||||
|
||||
// 发出变化事件
|
||||
const finalCode = verificationCode.value.join('')
|
||||
emit('change', finalCode)
|
||||
|
||||
// 如果完成了6位,发出完成事件
|
||||
if (finalCode.length === 6) {
|
||||
emit('complete', finalCode)
|
||||
}
|
||||
} else if (cleanVal.length === 0) {
|
||||
// 处理删除
|
||||
verificationCode.value[index] = ''
|
||||
emit('change', verificationCode.value.join(''))
|
||||
}
|
||||
// 如果是多个字符,可能是粘贴,由 paste 事件处理,这里不做处理
|
||||
}
|
||||
|
||||
const onCodePaste = (e: ClipboardEvent, index: number) => {
|
||||
e.preventDefault()
|
||||
|
||||
// 检查是否已经输入了6位数字
|
||||
const currentCode = verificationCode.value.join('')
|
||||
if (currentCode.length === 6) {
|
||||
return // 如果已经完成,不允许粘贴
|
||||
}
|
||||
|
||||
const pasteData = (e.clipboardData || (window as any).clipboardData).getData('text')
|
||||
const cleanData = pasteData.replace(/\D/g, '') // 只保留数字
|
||||
|
||||
if (cleanData.length === 0) return
|
||||
|
||||
console.log('Paste detected:', cleanData)
|
||||
|
||||
// 从当前输入框开始填充
|
||||
for (let i = 0; i < Math.min(cleanData.length, 6 - index); i++) {
|
||||
verificationCode.value[index + i] = cleanData[i]
|
||||
}
|
||||
|
||||
// 移动焦点到下一个空白输入框
|
||||
const nextEmptyIndex = verificationCode.value.findIndex((val, i) => i >= index && val === '')
|
||||
if (nextEmptyIndex !== -1) {
|
||||
nextTick(() => {
|
||||
inputRefs.value[nextEmptyIndex]?.focus()
|
||||
})
|
||||
} else {
|
||||
nextTick(() => {
|
||||
inputRefs.value[5]?.focus()
|
||||
})
|
||||
}
|
||||
|
||||
// 发出完成事件
|
||||
emit('complete', verificationCode.value.join(''))
|
||||
emit('change', verificationCode.value.join(''))
|
||||
}
|
||||
|
||||
const onCodeKeydown = (e: KeyboardEvent, index: number) => {
|
||||
// 处理删除键
|
||||
if (e.key === 'Backspace') {
|
||||
const input = e.target as HTMLInputElement
|
||||
const val = input.value
|
||||
|
||||
if (val === '') {
|
||||
// 当前输入框为空,删除上一个输入框的值
|
||||
if (index > 0) {
|
||||
verificationCode.value[index - 1] = ''
|
||||
nextTick(() => {
|
||||
inputRefs.value[index - 1]?.focus()
|
||||
})
|
||||
emit('change', verificationCode.value.join(''))
|
||||
}
|
||||
} else {
|
||||
// 当前输入框有值,清空当前输入框
|
||||
verificationCode.value[index] = ''
|
||||
emit('change', verificationCode.value.join(''))
|
||||
}
|
||||
}
|
||||
|
||||
// 阻止其他非数字字符(除了允许的控制键)
|
||||
else if (
|
||||
e.key &&
|
||||
!/[0-9]/.test(e.key) &&
|
||||
![
|
||||
'Backspace',
|
||||
'Delete',
|
||||
'Tab',
|
||||
'Enter',
|
||||
'ArrowLeft',
|
||||
'ArrowRight',
|
||||
'ArrowUp',
|
||||
'ArrowDown'
|
||||
].includes(e.key)
|
||||
) {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
const onCodeKeypress = (e: KeyboardEvent) => {
|
||||
// 检查是否已经输入了6位数字
|
||||
const currentCode = verificationCode.value.join('')
|
||||
if (currentCode.length === 6) {
|
||||
e.preventDefault() // 如果已经完成,阻止任何按键输入
|
||||
return
|
||||
}
|
||||
|
||||
// 只允许输入数字0-9
|
||||
const char = String.fromCharCode(e.which || (e as any).keyCode)
|
||||
if (!/[0-9]/.test(char)) {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
const onCodeFocus = () => {
|
||||
// 聚焦到第一个空白输入框
|
||||
const index = verificationCode.value.findIndex(item => item === '')
|
||||
const focusIndex = index === -1 ? 5 : index
|
||||
nextTick(() => {
|
||||
inputRefs.value[focusIndex]?.focus()
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
const reset = () => {
|
||||
verificationCode.value = ['', '', '', '', '', '']
|
||||
nextTick(() => {
|
||||
inputRefs.value[0]?.focus()
|
||||
})
|
||||
}
|
||||
|
||||
const getCode = () => {
|
||||
return verificationCode.value.join('')
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset,
|
||||
getCode,
|
||||
verificationCode
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.verification-code-input {
|
||||
display: flex;
|
||||
column-gap: 1.2rem;
|
||||
|
||||
.code-input {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
border: 0.15rem solid #d5d5d5;
|
||||
border-radius: 0.8rem;
|
||||
text-align: center;
|
||||
font-size: 2rem;
|
||||
line-height: 5rem;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: #232323;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user