验证码或条形码输入框(vue2)

229 阅读1分钟

需求

可以在任意位置输入,每个框只能输入一个字符且输入后自动聚焦到下一个等,具体效果如下图

实现思路

首先需要实现一个单独的输入框组件,我这里使用的时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>