vue實現一個6個輸入框的驗證碼輸入組件

image.png

要實現的功能:css

徹底和單輸入框同樣的操做,甚至能夠插入覆蓋:
1,限制輸入數字
2,正常輸入
3,backspace刪除
4,paste任意位置粘貼輸入
5,光標選中一個數字,滾輪能夠微調數字大小,限制0-9
6,123|456 自動覆蓋光標後輸入的字符,此時光標在3後,繼續輸入111,會獲得123111,而不用手動刪除456
7,封裝成vue單文件組件,方便任意調用。vue

模板代碼git

<template>
    <div class="input-box">
        <div class="input-content" @keydown="keydown" @keyup="keyup" @paste="paste" @mousewheel="mousewheel"
                @input="inputEvent">
            <input max="9" min="0" maxlength="1" data-index="0" v-model.trim.number="input[0]" type="number"
                    ref="firstinput"/>
            <input max="9" min="0" maxlength="1" data-index="1" v-model.trim.number="input[1]" type="number"/>
            <input max="9" min="0" maxlength="1" data-index="2" v-model.trim.number="input[2]" type="number"/>
            <input 
            <input max="9" min="0" maxlength="1" data-index="4" v-model.trim.number="input[4]" type="number"/>
            <input max="9" min="0" maxlength="1" data-index="5" v-model.trim.number="input[5]" type="number"/>
        </div>
    </div>
</template>

實現了鍵盤的keydown/keyup/paste/input和鼠標滾輪mousewheel事件
使用了6個輸入框的方案來實現。web

樣式部分:使用了scss模式app

<style scoped lang="scss">
    .input-box {
        .input-content {
            width: 512px;
            height: 60px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            
            input {
                color: inherit;
                font-family: inherit;
                border: 0;
                outline: 0;
                border-bottom: 1px solid #919191;
                height: 60px;
                width: 60px;
                font-size: 44px;
                text-align: center;
            }
        }
        
        input::-webkit-outer-spin-button,
        input::-webkit-inner-spin-button {
            appearance: none;
            margin: 0;
        }
    }
</style>

具體實現邏輯:主要實現以上幾個鍵盤事件操做。dom

<script>
    export default {
        data() {
            return {
                pasteResult: [],
            };
        },
        props: ['code'],
        computed: {
            input() {
                if (this.code && Array.isArray(this.code) && this.code.length === 6) {
                    return this.code;
                } else if (/^\d{6}$/.test(this.code.toString())) {
                    return this.code.toString().split('');
                } else if (this.pasteResult.length === 6) {
                    return this.pasteResult;
                } else {
                    return new Array(6);
                }
            }
        },
        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) {
                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);
                if (/Digit|Numpad/i.test(e.code)) {
                    // 必須在這裏符直,不然輸入框會是空值
                    this.$set(this.input, index, e.code.replace(/Digit|Numpad/i, ''));
                    el.nextElementSibling && el.nextElementSibling.focus();
                    if (index === 5) {
                        if (this.input.join('').length === 6) {
                            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 === 6) {
                        document.activeElement.blur();
                        this.$emit('complete', this.input);
                    }
                }
            },
            paste(e) {
                // 當進行粘貼時
                e.clipboardData.items[0].getAsString(str => {
                    if (str.toString().length === 6) {
                        this.pasteResult = str.split('');
                        document.activeElement.blur();
                        this.$emit('complete', this.input);
                        this.pasteResult = [];
                    } else {
                        // 若是粘貼內容不合規,清除全部內容
                        this.input[0] = new Array(6)
                    }
                })
            }
        },
        mounted() {
            // 等待dom渲染完成,在執行focus,不然沒法獲取到焦點
            this.$nextTick(() => {
                this.$refs.firstinput.focus()
            })
        },
    }
</script>

若是你發現了bug,或者有優化空間,歡迎你的指正和建議。我會隨時更新到原代碼當中,分享給你們。flex

相關文章
相關標籤/搜索