效果图

1.第一种实现方式

  使用li元素来模拟输入框的显示,没有别的目的,就只是为了语义化,当然你也可以使用其他任意一个元素来模拟,比如div。使用label标签的好处在于它可以跟input的click事件关联上,一方面实现了语义化解决方案,另一方面也省去了我们通过js来唤起虚拟键盘,隐藏输入框

<template>
 <div>
    <label for="code">
        <ul class="security-code-container">
            <li class="field-wrap" :class="{'on':seat==index}" v-for="(item, index) in list" :key="index" @click="onClick(index)">
                <i class="char-field">{{value[index] || placeholder}}</i>
            </li>
        </ul>
    </label>
    <input ref="input" 
            class="input-code"  
            @keyup="keyup($event)" 
            @keydown="keydown($event)"
            v-model="value"
            id="code" 
            type="number" 
            :maxlength="number" 
            @touchstart="keyup($event)"
            @touchend="keydown($event)"
           />
    <div class="clean"><span  @click.stop="onClean">清除</span></div> 
  </div>
</template>
<script>
 export default {
 name: 'SecurityCode',
 props: {
      number: {
        type: Number,
        default: 10
      },
      placeholder: {
        type: String,
        default:'-'
      }
 },
 data() {
  return {
    value:'',
    seat:-1,
    list:''
  }
 },
 created(){
   this.list=new Array(this.number)
 },
 methods: {
        hideKeyboard() {
              // 输入完成隐藏键盘
              document.activeElement.blur() // ios隐藏键盘
              this.$refs.input.blur() // android隐藏键盘
        },
        keydown(e){//当按下按键时
            console.log(e)
            //alert(JSON.stringify(e))
        },
        keyup(){ //按键松开时运行
                this.seat+=1
                this.$refs.input.value = this.value
                if (this.value.length >= this.number){
                     this.hideKeyboard()
                     this.seat= this.number
                 } 
                this.handleSubmit()
              },
        handleSubmit() { //返回信息
           this.$emit('returnresult',this.value)
        },
        onClean(){ //清除
            this.value=''
            this.seat=-1
        },
        onClick(index){//点击
           this.seat=index
        }
  }
}
</script>
<style scoped lang="less">
 .security-code-container {
    margin: 0;
    padding:20px 0;
    display: flex;
    justify-content: center;
 .field-wrap {
        list-style: none;
        display: block;
        border: 1px solid #eee;
        height:25px;
        width:25px;
        font-size:18px;
        text-align: center;
        overflow: hidden;
        background-color: #fff;
        margin-right:5px;
        .char-field {
            font-style: normal;
        }
        &.on{
          border:1px solid #0E88EF;
        }
   }
 }
 .input-code {
    width:100%;
    height:30px;
    position:absolute;
    left:-999999999999px;
    opacity:0;
    z-index:-1;
 }
  .clean{
      width:100%;
      box-sizing: border-box;
      text-align: right;
      color: #0E88EF;
      font-size: 12px;
      padding:0 10px;
  }
</style>

2.第二种实现方式

 1.使用css3隐藏输入框

<template>
<div>
  <div class="code-input-main">
    <div
      class="code-input-main-item"
      v-for="(item,index) in codeList"
      :class="{'on':seat==index}"
      :key="index"
      @click="onClick(index)"
    >{{code[index] ||''}}</div>
    <input class="code-input-input" ref="input" v-model="code" :maxlength="codeLength"  type="tel" />
  </div>
   <div class="clean"><span  @click.stop="onClean">清除</span></div> 
</div>
</template>
 
<script>
export default {
  name:'VueVercode',
  props: {
    codeLength: {
      default: 10,
      type: Number
    }
  },
  data() {
    return {
      codeList: [],
      code:'',
      seat:0,
    }
  },
  mounted() {
    // 定义一个数组用于循环
    this.codeList = new Array(this.codeLength).fill('')
  },
  watch: {
    // 截取字符长度
    code() {
        if(this.code.length <=0){
            this.seat=0
        }else{
            this.seat+=1 
        }  
      if (this.code.length > this.codeLength) {
         this.code = this.code.substring(0, this.codeLength)
      }
      this.$emit('returnresult',this.code)
    }
  },
  methods: {
    getCode() {
      return this.code
    },
   onClean(){ //清除
          this.code=''
    },
   onClick(index){
       this.seat=index
   } 
  }
}
</script>
<style scoped>
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
    -webkit-appearance: none !important;
    margin: 0;
}

.code-input-main {
    width:100%;
    box-sizing: border-box;
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    position: relative;
    padding:20px 0 0;
}

.code-input-input {
    height:25px;
    width: 100%;
    position: absolute;
    border:none;
    outline: none;
    color: transparent;
    background-color:transparent;
    text-shadow: 0 0 0 transparent;
    caret-color: transparent;
    opacity:0
}

.code-input-main-item {
    width:25px;
    height:25px;
    line-height:25px;
    margin-right:5px;
    padding:0;
    opacity: 0.8;
    border: solid #eee 1px;
    text-align: center;
    font-size:14px;
    color: #323232;
   }
.on{
     border:1px solid #0E88EF;
 }   
.clean{
        width:100%;
        box-sizing: border-box;
        text-align: right;
        color: #0E88EF;
        font-size: 12px;
        margin-top:20px;
        padding:0 10px;
    }
</style>

3.第三种实现方式

完全和单输入框一样的操作,甚至可以插入覆盖:
1,限制输入数字
2,正常输入
3,backspace删除
4,paste任意位置粘贴输入
5,光标选中一个数字,滚轮可以微调数字大小,限制0-9
6,123|456 自动覆盖光标后输入的字符,此时光标在3后,继续输入111,会得到123111,而不用手动删除456
7,封装成vue单文件组件,方便任意调用。

8.实现了键盘的keydown/keyup/paste/input和鼠标滚轮mousewheel事件
使用了6个输入框的方案来实现。

<!--四位验证码输入框组件-->
<template>
    <div class="input-box">
        <div class="input-content" @keydown="keydown($event)" @keyup="keyup($event)" @progress="keyup" @paste="paste"  @mousewheel="mousewheel" @input="inputEvent">
           <input  max="9"  min="0" maxlength="1"  data-index="0" v-model="input[0]"  type="tel" ref="firstinput"/>
            <template v-for="(itme,index) in isNumber">
             <input :key="index" v-if="index>0" max="9"  min="0" maxlength="1"  :data-index="index"  v-model="input[index]"  type="tel" />
            </template>
        </div>
         <div class="clean"><span  @click.stop="onClean">清除</span></div> 
    </div>
</template>

<script>
    export default {
        data() {
            return {
                pasteResult: []
            }
        },
        props:{
            code:{
                typeof:Array,
                default:[]
            },
            isNumber:{
                typeof:Number,
                default:10  //输入框个数
            }
        },
        computed: {
            input() {
                if (this.code && Array.isArray(this.code) && this.code.length === this.isNumber) {
                    return this.code
                } else if (/^\d{`${this.isNumber}`}$/.test(this.code.toString())) {
                    return this.code.toString().split('')
                } else if (this.pasteResult.length === this.isNumber) {
                    return this.pasteResult
                } else {
                    return new Array(this.isNumber)
                }
            }
        },

        methods: {
            // 解决一个输入框输入多个字符
            inputEvent(e) { //输入框
                var index = e.target.dataset.index * 1
                var el = e.target
                el.value = el.value.replace(/Digit|Numpad/i, '').replace(/1/g, '').slice(0, 1)
                this.$set(this.input, index, el.value)
            },
            keydown(e) { //键盘按下
                var index = e.target.dataset.index * 1
                var el = e.target
                if (e.key === 'Backspace') {
                    if (this.input[index].length > 0) {
                        this.$set(this.input, index, '')
                    } else {
                        if (el.previousElementSibling) {
                            el.previousElementSibling.focus()
                            this.$set(this.input, index - 1, '')
                        }
                    }
                } else if (e.key === 'Delete') {
                    if (this.input[index].length > 0) {
                        this.$set(this.input, index, '')
                    } else {
                        if (el.nextElementSibling) {
                            this.$set(this.input, index = 1, '')
                        }
                    }
                    if (el.nextElementSibling) {
                        el.nextElementSibling.focus()
                    }
                } else if (e.key === 'Home') {
                    el.parentElement.children[0] && el.parentElement.children[0].focus()
                } else if (e.key === 'End') {
                    el.parentElement.children[this.input.length - 1] && el.parentElement.children[this.input.length - 1].focus()
                } else if (e.key === 'ArrowLeft') {
                    if (el.previousElementSibling) {
                        el.previousElementSibling.focus()
                    }
                } else if (e.key === 'ArrowRight') {
                    if (el.nextElementSibling) {
                        el.nextElementSibling.focus()
                    }
                } else if (e.key === 'ArrowUp') {
                    if (this.input[index] * 1 < 9) {
                        this.$set(this.input, index, (this.input[index] * 1 + 1).toString())
                    }
                } else if (e.key === 'ArrowDown') {
                    if (this.input[index] * 1 > 0) {
                        this.$set(this.input, index, (this.input[index] * 1 - 1).toString())
                    }
                }
            },
            keyup(e) { //键盘弹开
                console.log(e,'==============================')
                var index = e.target.dataset.index * 1
                var el = e.target
                // 解决输入e的问题
                el.value = el.value.replace(/Digit|Numpad/i, '').replace(/1/g, '').slice(0, 1)
                alert(e.code)
                if (/Digit|Numpad/i.test(e.code)) {
                    // 必须在这里符直,否则输入框会是空值
                    this.$set(this.input, index, e.code.replace(/Digit|Numpad/i, ''))  //获取值是多少
                    console.log(this.input,'--------键盘弹开-----------')
                     let result=this.input.join('').trim()
                    this.$emit('returnresult',result)//返回结果

                    el.nextElementSibling && el.nextElementSibling.focus()
                    if (index === this.isNumber-1) {
                        if (this.input.join('').length === this.isNumber) {
                            document.activeElement.blur()
                            this.$emit('complete', this.input)
                        }
                    }
                } else {
                    if (this.input[index] === '') {
                        this.$set(this.input,index,'')
                    }
                }

            },
            mousewheel(e) { //滚动事件
                var index = e.target.dataset.index
                if (e.wheelDelta > 0) {
                    if (this.input[index] * 1 < 9) {
                        this.$set(this.input, index, (this.input[index] * 1 + 1).toString())
                    }
                } else if (e.wheelDelta < 0) {
                    if (this.input[index] * 1 > 0) {
                        this.$set(this.input, index, (this.input[index] * 1 - 1).toString())
                    }
                } else if (e.key === 'Enter') {
                    if (this.input.join('').length === this.isNumber) {
                        document.activeElement.blur()
                        this.$emit('complete', this.input)
                    }
                }
            },
            paste(e) {
                // 当进行粘贴时
                e.clipboardData.items[0].getAsString(str => {
                    if (str.toString().length === this.isNumber) {
                        this.pasteResult = str.split('')
                        document.activeElement.blur()
                       let result=this.pasteResult .join('').trim()
                        this.$emit('returnresult',result)//返回结果
                    } else {
                        // 如果粘贴内容不合规,清除所有内容
                        this.input[0] = new Array(this.isNumber)
                    }
                })
            },
            onClean(){ //清除
                 this.pasteResult=[] //清除所有内容
                 this.$emit('complete', this.input)
                 this.$refs.firstinput.focus()
                 this.$emit('returnresult','')//返回结果
           }
        },
        mounted() {
            // 等待dom渲染完成,在执行focus,否则无法获取到焦点
            this.$nextTick(() => {
                this.$refs.firstinput.focus()
            })
        },
    }
</script>

<style lang="less" scope>
    .input-box {
        width:100%;
        position: relative;
        padding:10px 0;
        .input-content {
            width:100%;
            height:40px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            input {
                color: inherit;
                font-family: inherit;
                border: 0;
                outline: 0;
                border: 1px solid #eee;
                height:25px;
                width:25px;
                font-size:18px;
                text-align: center;
                overflow: hidden;
                outline:none;
                background:none;
            }
        }
        .clean{
            width:100%;
            text-align: right;
            color: #0E88EF;
            font-size: 12px;
            margin-top:20px;
        }
    }
input::-webkit-outer-spin-button,input::-webkit-inner-spin-button {appearance: none;margin: 0;}
input::-webkit-outer-spin-button,input::-webkit-inner-spin-button {-webkit-appearance: none;}
input[type="number"] {-moz-appearance: textfield;-webkit-appearance: none;margin: 0;}
</style>

4.如何引用组件

<template>
    <div>
        <div class="box">
          <div class="h3">请输入兑换码</div>
          <vercode @returnresult="returnresult"></vercode>
          <!-- <digital-input-box @returnresult="returnresult"></digital-input-box>
          
          <securityCode @returnresult="returnresult"></securityCode> -->
         
        </div> 
        <div class="but-box">
           <van-button :loading="loading" @click="onCxchange"  round size="large" loading-text="加载中..." type="info" class="sjht-shadow" :disabled="disabled">兑换</van-button>
        </div>
    </div>
</template>

<script>
// import  digitalInputBox from './components/digitalInputBox.vue'
// import  securityCode from './components/securityCode.vue'
import  vercode from './components/vercode.vue'
export default {
  name: 'Cxchange', //公众号首页
  components:{
    //   digitalInputBox,
    //   securityCode,
      vercode
  },
  data() {
    return {
        disabled:true,
        loading:false,
        couponeCode:''
    }
  },
  methods:{
      onCxchange(){
           if(this.couponeCode.length>=9){
                this.disabled=false
            }else{
               this.disabled=true
            }
           this.loading=true
           this.disabled=true
           //这里是填写你的AJAX代码
        },
        returnresult(data){
             console.log(data,'-----------------')
             this.couponeCode=data
            if(this.couponeCode.length>=9){
                this.disabled=false
            }else{
               this.disabled=true
            }
            
        }
  }
}
</script>

<style lang="less" scope>
.box{
    width:95%;
    height: 142px;
    background: #FFFFFF;
    border-radius: 7px;
    margin:20px auto;
    box-sizing: border-box;
    padding:14px;
    .h3{
        width:100%;
        height: 21px;
        font-size: 15px;
        font-family: PingFangSC-Semibold, PingFang SC;
        font-weight: 600;
        color: #333333;
        line-height: 21px;
    }
}

.but-box{
    width: 90%;
    margin:0 auto;
    position: relative;
}

</style>

GitHub 加速计划 / vu / vue
207.54 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
73486cb5 * chore: fix link broken Signed-off-by: snoppy <michaleli@foxmail.com> * Update packages/template-compiler/README.md [skip ci] --------- Signed-off-by: snoppy <michaleli@foxmail.com> Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 4 个月前
e428d891 Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐