uniapp(Vue) 实现可粘贴的 个性化验证码 输入框

3,377 阅读4分钟

演示结果

效果一:可依次输入验证码到输入框

1.gif

效果二:可粘贴验证码到输入框

2.gif

需求描述

实现修改手机号的功能,就包括了验证手机号,输入接受到的验证码。

拿到这个功能的时候,就在脑海里想着输入6位验证码,是应该放6个输入框吧,然后修改输入框的样式就可以了。说干就干呗。

画UI

UI图的效果大家看一下

1.jpg

聚焦和不聚焦的样式分别如上图,因为是在uniapp的框架下搭建的项目,使用的也是它们封装过的input组件框,所以在写样式的时候与原生input有点不一样。

上代码

<view class="input-code-view">
    <input :class="['input-text', inputFocusObj['one']?'input-focus':'']"
        type="number"
        maxlength="1"
        v-model="one"
        label-position="left"
        @focus="inputFocusObj['one']=true"
        @blur="inputFocusObj['one']=false">
</view>
.input-code-view {
    opacity: 1;
    border-radius: 32rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    .input-text {
        width: 20rpx;
        height: 96rpx;
        padding: 0 38rpx;
        border-radius: 32rpx;
        background: #f6f6f6;
        border: 2rpx solid #f6f6f6;
        font-size: 32rpx;
        font-family: Alibaba, Alibaba-PuHuiTi;
        font-weight: PuHuiTi;
        color: #333333;
        line-height: 44rpx;
        text-align: center;
    }
    .input-focus {
        caret-color: #276ef1;
        background: #eff3fe !important;
        border: 2rpx solid #276ef1 !important;
    }
}

看一下uniapp封装过的input,包裹了好几层

2.jpg

原生的input框

image.png

相对于修改原生的input框,修改已经封装好的要简单的多。把内层的样式改为失焦的样式,聚焦时修改外层的样式加上边框颜色和光标颜色。样式这块我们就完成了,看一下效果

image.png 可以看到我是把input输入框的宽度设为只有10px的大小,刚好只能容下一个数字的大小。为什么要设置这么小?主要是为了让我们在粘贴代码时感觉是自动复制上去,而不是人工控制。

自动跳转

这个谁不会呀?监听前一个输入框,当输入一个数以后聚焦到下一个输入框。我也是这么做的。

watch: {
    one(val){
        // 拿到input框 dom元素
        let inputList = document.querySelectorAll('.uni-input-input');
        if(Number(val) > -1) {
            // 第二个dom 聚焦
            inputList[1].focus();
        }
    },
}

问题接踵而至了。。。。

  1. 因为我们设置了最大长度为1,所以输入一次是ok的;但是第二次就输入不了。解决方案:因为最大输入长度为1,不能再输入,所以修改为每次聚焦都清空数据 @focus="inputFocusObj['one']=true;one=''"
<view class="input-code-view">
    <input :class="['input-text', inputFocusObj['one']?'input-focus':'']"
        type="number"
        maxlength="1"
        v-model="one"
        label-position="left"
        @focus="inputFocusObj['one']=true;one=''"
        @blur="inputFocusObj['one']=false">
</view>
  1. 问题一导致了,如果输入框有数据存在的话,点击聚焦会触发if(Number(val) > -1)。解决办法:因为空字符串转为Number是0,所以首先判断是不是空字符串
watch: {
    one(val){
        let value = Number(val)
        let inputList = document.querySelectorAll('.uni-input-input');
        if((value || val === '0') && value>-1) {
            inputList[1].focus();
        }
    },
}

自动跳转也Ok了。

自动带入粘贴数

这个???好好想想,我们只能把复制来的6位验证码放在第一个输入框,然后手动分配到后面的输入框去。

因为我们设置了最大长度为1,这个肯定是不能复制的。so把第一个输入框的最大长度改为6吧,这样就可以复制上去了。

<view class="input-code-view">
    <input :class="['input-text', inputFocusObj['one']?'input-focus':'']"
        type="number"
        maxlength="6"
        v-model="one"
        label-position="left"
        @focus="inputFocusObj['one']=true;one=''"
        @blur="inputFocusObj['one']=false">
</view>

监听数据one,当数据长度大于1时,我们把多余的数都依次分配给后面的输入框。这里为什么要用到

watch: {
    one(val){
        let value = Number(val)
        let inputList = document.querySelectorAll('.uni-input-input');
        if ((value || val === '0') && value>-1 ){
            if (val.length > 1) {
                this.one = val.slice(0,1)
                if (val.length > 1) {
                    this.two = val.slice(1,2)
                }
                if (val.length > 2) {
                    this.three = val.slice(2,3)
                }
                if (val.length > 3) {
                    this.four = val.slice(3,4)
                }
                if (val.length > 4) {
                    this.five = val.slice(4,5)
                }
                if (val.length > 5) {
                    this.six = val.slice(5,6)
                }
            } else {
                inputList[1].focus();
            }
        }
    },
}

问题又来了。。。

  1. 当我们粘贴123456的时候,发现第二、第四、第六输入框为空。仔细看我们的代码发现,当给第一个赋值时,触发了第二的聚焦事件,依次类推,所以导致了现在结果。解决方法:定义一个变量,标识我此时正在进行的是粘贴动作,要求代码不要执行聚焦事件。

image.png

watch: {
    one(val){
        let value = Number(val)
        let inputList = document.querySelectorAll('.uni-input-input');
        if ((value || val === '0') && value>-1 ){
            if (val.length > 1) {
                this.copyFlag = true
                this.one = val.slice(0,1)
                if (val.length > 1) {
                    this.two = val.slice(1,2)
                }
                if (val.length > 2) {
                    this.three = val.slice(2,3)
                }
                if (val.length > 3) {
                    this.four = val.slice(3,4)
                }
                if (val.length > 4) {
                    this.five = val.slice(4,5)
                }
                if (val.length > 5) {
                    this.six = val.slice(5,6)
                }
                setTimeout(() => {
                    this.copyFlag = false;
                }, 0);
            } 
            if(!this.copyFlag) {
                inputList[1].focus();
            }
        }
    },
    two(val){
        let value = Number(val)
        let inputList = document.querySelectorAll('.uni-input-input');
        if ((value || val === '0') && value>-1 && !this.copyFlag){
            inputList[2].focus();
        }
    },
    ...
}

  1. 既然第一个输入框可以输入6个字符,当我们输入小数点时,光标竟然不自动跳转了,竟然可以一直输入,这肯定也是一个问题。解决方法:判断是否存在小数点,存在时,把数据置为0,然后把光标跳转。
watch: {
    one(val){
        let value = Number(val)
        let inputList = document.querySelectorAll('.uni-input-input');
        if ((value || val === '0') && value>-1 ){
            if (val.length > 1) {
                this.copyFlag = true
                this.one = val.slice(0,1)
                if (val.length > 1) {
                    this.two = val.slice(1,2)
                }
                if (val.length > 2) {
                    this.three = val.slice(2,3)
                }
                if (val.length > 3) {
                    this.four = val.slice(3,4)
                }
                if (val.length > 4) {
                    this.five = val.slice(4,5)
                }
                if (val.length > 5) {
                    this.six = val.slice(5,6)
                }
                setTimeout(() => {
                    this.copyFlag = false;
                }, 0);
            } 
            if(!this.copyFlag) {
                inputList[1].focus();
            }
        }else {
            if (val && String(val).indexOf('.') > -1) {
                this.one = 0;
                inputList[1].focus();
            }
        }
    },    
}

遗留问题

  1. 当粘贴超过6位数的时候,监听不到输入框的值
  2. uniapp未提供监听删除键盘事件,所以做不到依次删除数据

有什么问题可以留言讨论