需求
可以在任意位置输入,每个框只能输入一个字符且输入后自动聚焦到下一个等,具体效果如下图
实现思路
首先需要实现一个单独的输入框组件,我这里使用的时input
该输入框组件应该具有一个聚焦方法来使用js控制聚焦
focus () {
this.$refs.inputRef.focus()
}
输入方法用来控制输入内容,此处过滤了中文和空格,当输入长度超过1时取第二项数据(实现第二次输入时替换输入内容), 有值时抛出next事件
inputFun (data) {
if (this.code) {
const val = this.check(this.code)
if (val.length > 1) {
this.code = val[1]
} else {
this.code = val
}
if (this.code) {
this.next()
}
}
},
check (str) {
let temp = ''
for (var i = 0; i < str.length; i++) {
if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 255) { temp += str.charAt(i) }
}
return temp.replace(' ', '')
// return temp
},
键盘按下事件,主要用来控制左右移动和Backspace键的功能,取消左右键的默认事件的原因时防止光标移动
keydownFun (e) {
if (e.code === 'ArrowRight') {
e.preventDefault()
this.next()
} else if (e.code === 'ArrowLeft') {
e.preventDefault()
this.previous()
} else if (e.code === 'Backspace') {
if (this.code) {
this.code = ''
} else {
this.previous()
}
}
}
接下来实现整个的输入框,由于是有多个输入框,所以这里的数据使用list进行存储。可通过list的长度来控制输入框的数量
使用循环来生成多个框,在接收到CodeInput组件抛出的next(下一个)/pervious(上一个)时改变聚焦的输入框
<div class="barCode">
<CodeInput
v-for="(code, index) in list"
:placeholder="index + 1" :key="index"
ref="codeInput"
@next="focus(index + 1)"
@previous="focus(index - 1)"
v-model="list[index]"
:border-color="color[index - 13]"
/>
</div>
完整代码
<script>
import CodeInput from './CodeInput.vue'
export default {
components: {
CodeInput
},
props: {
value: String
},
data: () => ({
list: ['', '', '', '', '', '', '', '', '', '', '', '', '', '2', '', '', '1', '', '', ''],
color: ['#f7984a', '#fa2b2c', '#0d77c3']
}),
methods: {
focus (i) {
this.$refs.codeInput[i] && this.$refs.codeInput[i].focus()
}
}
}
</script>
<template>
<div class="barCode">
<CodeInput
v-for="(code, index) in list"
:placeholder="index + 1" :key="index"
ref="codeInput"
@next="focus(index + 1)"
@previous="focus(index - 1)"
v-model="list[index]"
:border-color="color[index - 13]"
/>
</div>
</template>
<style lang="scss" scoped>
</style>
- CodeInput
<script>
export default {
props: {
borderColor: String,
value: String
},
data: () => ({
code: ''
}),
watch: {
value: {
handler (val) {
if (val !== this.code) { this.code = this.check(val)[0] }
},
immediate: true
},
code (val) {
if (this.value !== val) this.$emit('input', val)
}
},
methods: {
inputFun (data) {
if (this.code) {
const val = this.check(this.code)
if (val.length > 1) {
this.code = val[1]
} else {
this.code = val
}
if (this.code) {
this.next()
}
}
},
keydownFun (e) {
if (e.code === 'ArrowRight') {
e.preventDefault()
this.next()
} else if (e.code === 'ArrowLeft') {
e.preventDefault()
this.previous()
} else if (e.code === 'Backspace') {
if (this.code) {
this.code = ''
} else {
this.previous()
}
}
},
next () {
this.$emit('next')
},
previous () {
this.$emit('previous')
},
check (str) {
let temp = ''
for (var i = 0; i < str.length; i++) {
if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 255) { temp += str.charAt(i) }
}
return temp.replace(' ', '')
// return temp
},
focus () {
this.$refs.inputRef.focus()
}
}
}
</script>
<template>
<div class="code" >
<input ref="inputRef"
v-bind="$attrs" @keydown="keydownFun"
type="text" :style="{borderColor: borderColor}"
@input="inputFun" v-model="code">
<i :class="{notnull: code}"></i>
</div>
</template>
<style lang="scss" scoped>
.code {
margin-right: 5px;
display: inline-block;
position: relative;
padding-bottom: 8px;
input {
width: 22px;
height: 30px;
text-align: center;
font-size: 18px;
border-radius: 5px;
border: 2px solid #ddd;
padding: 0;
transition: all .3s;
&:focus {
outline: none;
// border-color: #0D47A1;
box-shadow: 0 0 5px rgb(129, 211, 248);
}
&::-webkit-input-placeholder {
color: #ddd;
}
}
i {
position: absolute;
width: 22px;
height: 2px;
border-radius: 1px;
background-color: #ddd;
bottom: 0;
left: calc(50% - 11px);
transition: all .3s;
&.notnull {
background-color: #0d77c3;
}
}
}
</style>