验证码组件开发,本地缓存倒计时

发布时间 2023-12-18 17:43:23作者: 蔚蓝Azure

vue组件

<template>
    <div class="verifyCode mt-24">
        <div class="header">
            <span class="logo inline-block mr-6 w-[26px] h-[26px] bg-black-0"></span>
            <span class="title text-[22px] font-semibold"> {{ title }}</span>
        </div>
        <div class="desc text-sm mt-6">
            {{ desc }}
        </div>
        <div class="main flex justify-between items-center">
            <div class="number">
                <div class="input-box flex-1">
                    <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"
                            :class="isError || isExpired ? 'error' : 'default'" />
                        <input max="9" min="0" maxlength="1" data-index="1" v-model.trim.number="input[1]" type="number"
                            :class="isError || isExpired ? 'error' : 'default'" />
                        <input max="9" min="0" maxlength="1" data-index="2" v-model.trim.number="input[2]" type="number"
                            :class="isError || isExpired ? 'error' : 'default'" />
                        <input max="9" min="0" maxlength="1" data-index="3" v-model.trim.number="input[3]" type="number"
                            :class="isError || isExpired ? 'error' : 'default'" />
                        <input max="9" min="0" maxlength="1" data-index="4" v-model.trim.number="input[4]" type="number"
                            :class="isError || isExpired ? 'error' : 'default'" />
                        <input max="9" min="0" maxlength="1" data-index="5" v-model.trim.number="input[5]" type="number"
                            :class="isError || isExpired ? 'error' : 'default'" />
                    </div>
                </div>
            </div>
            <div v-show="isCodeShow">
                <div v-show="sendMsg.length >= 7" @click="handleReSend"
                    class="resend mt-4 w-[96px] h-9 cursor-pointer leading-9 text-center border-2 border-[#000]  text-sm font-semibold rounded-[16px]">
                    Get code
                </div>
                <div v-show="parseInt(sendMsg) >= 1"
                    class="resend mt-4 w-[96px] h-9  leading-9 text-center   text-sm font-semibold ">
                    {{ sendMsg }}s
                </div>
            </div>
        </div>
        <div class="mailCode mt-3 text-[13px] font-medium text-[#868D9A]">
            {{ mark }}
        </div>
        <!-- 错误 -->
        <div v-show="isError" class="errCode mt-3 text-[13px] font-medium text-[#F36343]">
            Verification code error, please try again
        </div>
        <!-- 过期 -->
        <div v-show="isExpired" class="errCode mt-3 text-[13px] font-medium text-[#F36343]">
            The verification code has expired, please obtain it again
        </div>
        <!-- 忘记密码 -->
        <div v-show="isResetPassword" class="text-[#868D9A] text-[13px] mt-3 cursor-pointer">
            Forget number
            <svg xmlns="http://www.w3.org/2000/svg" width="6" height="10" viewBox="0 0 6 10" fill="none">
                <rect width="6.01783" height="1.3373" rx="0.668648"
                    transform="matrix(-0.707108 0.707106 -0.707108 -0.707106 6 5.04492)" fill="#A8AAAE" />
                <rect width="6.01783" height="1.3373" rx="0.668648"
                    transform="matrix(-0.707108 -0.707106 0.707108 -0.707106 5.05469 5.83496)" fill="#A8AAAE" />
            </svg>
        </div>
    </div>
</template>

<script>
let TIME_COUNT = 60; // 设置一个全局的倒计时的时间
export default {
    props: {
        "isCodeShow": {
            type: Boolean,
            default: () => {
                return false;
            }
        },
        "isResetPassword": {
            type: Boolean,
            default: () => {
                return false;
            }
        },
        "title": {
            type: String,
            default: () => {
                return 'defalut title';
            }
        },
        "desc": {
            type: String,
            default: () => {
                return 'defalut desc';
            }
        },
        "mark": {
            type: String,
            default: () => {
                return '';
            }
        }
    },
    data() {
        return {
            input: [],
            sendMsg: "",
            timer: null,
            isError: false,
            isExpired: false,
        }
    },
    created() {
        this.input = new Array(6).fill('');
        if(this.getLocalStorage('TIME_COUNT')){
            this.handleReSend()
        } else {
            this.sendMsg = 'Get code'
        }
    },
    methods: {
        // 设置缓存
        setLocalStorage(key, value) {
            try {
                window.localStorage.setItem(key, JSON.stringify(value));
            } catch (error) {
                // 处理缓存设置失败的情况
                console.error('设置缓存失败:', error);
            }
        },
        // 获取缓存
        getLocalStorage(key) {
            try {
                const value = window.localStorage.getItem(key);
                return value ? JSON.parse(value) : null;
            } catch (error) {
                // 处理缓存获取失败的情况
                console.error('获取缓存失败:', error);
                return null;
            }
        },
        removeLocalStorage(key) {
            try {
                window.localStorage.removeItem(key);
            } catch (error) {
                // 处理缓存删除失败的情况
                console.error('删除缓存失败:', error);
            }
        },
        // 解决一个输入框输入多个字符
        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.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] && this.input[index].length > 0) {
                    this.input[index] = ''
                } else {
                    if (el.previousElementSibling) {
                        el.previousElementSibling.focus()
                        this.input[index - 1] = ''
                    }
                }
            } else if (e.key === 'Delete') {
                if (this.input[index].length > 0) {
                    this.input[index] = ''
                } else {
                    if (el.nextElementSibling) {
                        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.input[index] = (this.input[index] * 1 + 1).toString()
                }
            } else if (e.key === 'ArrowDown') {
                if (this.input[index] * 1 > 0) {
                    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.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()
                    }
                }
            } else {
                if (this.input[index] === '') {
                    this.input[index] = ''
                }
            }
            this.$emit('complete', this.input.slice());
        },
        mousewheel(e) {
            var index = e.target.dataset.index
            if (e.wheelDelta > 0) {
                if (this.input[index] * 1 < 9) {
                    this.input[index] = (this.input[index] * 1 + 1).toString()
                }
            } else if (e.wheelDelta < 0) {
                if (this.input[index] * 1 > 0) {
                    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.slice())
                }
            }
        },
        paste(e) {
            // 当进行粘贴时
            e.clipboardData.items[0].getAsString((str) => {
                if (str.toString().length === 6) {
                    document.activeElement.blur();
                    this.input = str.split('');
                    this.$emit('complete', this.input.slice())
                } else {
                    // 如果粘贴内容不合规,清除所有内容
                    this.input[0] = new Array(6)
                }
            })
        },
        handleReSend() {
            // 模拟生成code
            this.sendMsg = this.getLocalStorage('TIME_COUNT') || TIME_COUNT;
            this.timer = setInterval(() => {
                if (this.sendMsg <= 1) {
                    clearInterval(this.timer)
                    this.removeLocalStorage('TIME_COUNT')
                    this.sendMsg = "Get code"
                    return
                }
                this.sendMsg -= 1;
                this.setLocalStorage('TIME_COUNT',this.sendMsg)
            }, 1000)
        },
        getCode() {
            let resultArray = this.input.filter(item => {
                return item != '' && item >= 0
            })
            if (resultArray.length == 6) {
                let originStr = resultArray.toString().replace(/,/g, '')
                return originStr;
            } else {
                this.$message.error('请检查输入的数据!')
            }

        }
    }
}
</script>
<style>
.input-box .input-content {
    margin-top: 15px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.input-box .input-content input {
    color: inherit;
    font-family: inherit;
    border: 0;
    outline: 0;
    height: 42px;
    width: 42px;
    line-height: 42px;
    border-radius: 12px;
    font-size: 22px;
    text-align: center;
    margin-right: 14px;
}

.error {
    color: #F36343 !important;
    background: #f7e6e5 !important;
}

.default {
    color: #000;
    background: #F2F4F5;
}

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
    appearance: none;
    margin: 0;
}
</style>

父组件使用

 <ValidCode ref="ValidCodeMail" v-bind="ValidCodeMailProps" />

data(){
          ValidCodeMailProps: {
                isCodeShow: true,
                title: "mailbox verification",
                desc: "Please enter your email verification code",
                mark: "mshiyonglife@gmail.com",
            },
}

// 获取code
const ValidCodeGoogleCode = this.$refs.ValidCodeGoogle.getCode();