六/多位验证码代码框-改进版-详细注释

效果展示

原始版本

本文是基于CSDN改进版本,可以从父组件传值直接使用,封装完整度相对更高些

父组件代码

模板代码

<template>
    <div class="show_wrap flex_row flex_center">
        <div class="show flex_row flex_center">
            <verificationCodeInput :options_verificationCode="options_verificationCode"
                :isRight="isRight" @SonValue="ValFromSon" />
                <!-- options_xxx:将样式、配置等基本信息传给子组件 -->
                <!-- isRight:传值给子组件,告诉子组件验证是否通过 -->
                <!-- @SonValue="ValFromSon":从子组件接收处理好后的字符串 -->
        </div>
    </div>
</template>

ts代码

<script setup lang="ts">
import verificationCodeInput from "@/components/verificationCodeInput/index.vue"
import { ref } from "vue"


/**
 * 一、验证码组件
 *  1. 从父组件传入样式、配置等基础数据
 *  2. 从子组件接收所获取到的验证码数字
 *  3. 对输入的验证码进行判断处理
 *  4. 将是否通过判断的状态值传给子组件,进行相应处理
 */
// 1. 从父组件传入样式、配置等基础数据
const options_verificationCode = ref({
    style: {
        wrap: {
            width: "50%",            // 整个容器高度
            height: "50%",           // 整个容器高度
            bgColoer: "#d0deaa",     // 整个容器的背景色
            borderRadius: "10px"     // 整个容器的圆角
        },
        title: {
            width: "160px",         // 标题宽度
            height: "36px",         // 标题高度
            top: "25%",             // 标题距离盒子顶端高度
            lineHeight: "36px",     // 标题行高,和标题高度相等时文字竖向居中
            textAlign: "center",    // 标题字体横向居中
            fontSize: "24px",       // 标题的字体大小
            // color: "green"       // 标题字体颜色
        },
        myInput: {
            wrapWidth: "60%",                             // 输入框总长度
            inputItemWidth: "30px",                       // 单个输入框宽长
            inputItemHeight: "30px",                      // 单个输入框高度
            inputItemBorderRadius: "5px",                 // 单个输入框圆角
            inputItemBgcolor: "#8abcd1",                  // 单个输入框背景颜色-未选中
            borderCheckedStyle: '1px solid #E8C2BF',      // input选中的边框样式
            // borderCheckedBackground: "#69a794",            // input选中的背景色
        }
    },
    config: {
        inputNumber: 6,         // 输入框的个数
        inputLength: "1",       // 输入框输入长度
        inputType: 'tel'        // 输入字符的类型
    }
})
// 2. 从子组件接收所获取到的验证码数字
const ValFromSon = (value: string) => {
    _handlingResult(value)          // 获取验证码数字后进行后续处理
}
// 3. 对输入的验证码进行判断处理
let isRight = ref(true)
const _handlingResult = (codeStr: String) => {
    // 此处是模拟正确验证码为123456
    let rightCode = '123456'
    if (codeStr === rightCode) {
        // 4. 将是否通过判断的状态值传给子组件
        isRight.value = true
        alert("验证通过")
    } else {
        // 4. 将是否通过判断的状态值传给子组件
        isRight.value = false
        alert("验证失败")
    }
}
</script>

子组件代码 

模板代码

<template>
    <!-- <div class="codeBox flex_col flex_center"> -->
    <div class="code_warp flex_col flex_center">
        <div class="secondTitle">请输入验证码</div>
        <div class="codeList flex_row flex_center">
            <div class="list_wrap flex_row flex_around" v-for="n in options_config.inputNumber" :key="n">
                <!-- v-for="n in options_config.inputNumber" 循环控制输入框个数 -->
                <input class="codeItem" :type="options_config.inputType" :maxlength="options_config.inputLength"
                    ref="inputDom" v-model="inputArr[n - 1]" @keyup="inputCode($event, n - 1)" />
                    <!-- :type="options_config.inputType" 接收来自父组件的样式和配置 -->
                    <!-- :maxlength="options_config.inputLength" 动态设置每个输入框能输入的最大值 -->
                    <!-- ref="inputDom" 获取input输入框的Dom -->
                    <!-- v-model="inputArr[n - 1]" 将input输入值存入数组inputArr,由于ref获取的inputDom在js代码中的处理是存入数组,为了将dom和存入的值一一绑定,可以以数组下标为标识,但是由于n是从1开始,因此需要减一 -->
                    <!-- @keyup="inputCode($event, n - 1)" 传入事件和当前值在数组中的下标 -->
            </div>
        </div>
    </div>
    <!-- </div> -->
</template>

css代码

<style scoped lang="scss">
// 在scss中,可以使用v-bind动态设置样式的值,且可以在()中使用表达式
.code_warp {
    width: v-bind("options_style.wrap.width ?? '60%'");
    // options_style.wrap.width是从父组件中传入的值,若没有值,则默认为60%
    // ?? 是ts中的或表达,类似于js中的||,但是不会像||一般将0判断为false
    height: v-bind("options_style.wrap.height ?? '60%'");
    background-color: v-bind("options_style.wrap.bgColoer ?? '#d0deaa'");
    border-radius: v-bind("options_style.wrap.borderRadius ?? '10px'");

    position: relative;

    .secondTitle {
        width: v-bind("options_style.title.width ?? '160px'");
        height: v-bind("options_style.title.height ?? '36px'");
        position: absolute;
        top: v-bind("options_style.title.top ?? '30%'");

        line-height: v-bind("options_style.title.lineHeight ?? '36px'");
        text-align: v-bind("options_style.title.textAlign ?? 'center'");
        font-size: v-bind("options_style.title.fontSize ?? '24px'");
        color: v-bind("options_style.title.color ?? '#000000'");
    }

    .codeList {
        width: v-bind("options_style.myInput.wrapWidth ?? '60%'");

        .list_wrap {
            width: 100%;
            height: 100%;
            position: relative;

            .codeItem {
                width: v-bind("options_style.myInput.inputItemWidth ?? '30px'");
                height: v-bind("options_style.myInput.inputItemHeight ?? '30px'");
                border-radius: v-bind("options_style.myInput.inputItemBorderRadius ?? '5px'");
                display: block;
                background-color: v-bind("options_style.myInput.inputItemBgcolor ?? '#8abcd1'");

                text-align: center;
            }

            //获取焦点时input输入框样式
            input:focus {
                // outline: 1px solid #E8C2BF; //边框不用border,用outline
                outline: v-bind("options_style.myInput.borderCheckedStyle ?? '1px solid #000000'"); //边框不用border,用outline
                background: v-bind("options_style.myInput.borderCheckedBackground ?? 'rgba(0,0,0,0)'");
                ; //背景色
            }
        }
    }
}
</style>

ts代码

<!-- 验证码输入框页 -->
<script setup lang="ts">
/**
 * 实现思想:
 *  声明一个用于记录input值的数组
 *  通过原生html代码获取input dom
 *  借用数组的index来区分所获取dom数组中dom的顺序,以此实现焦点的跳转
 *  由于全程都是用过直接跳转焦点,没有失去焦点的情况,因此不会出现多次调起并关闭键盘的情况
 */
import { ref, watch } from "vue";

// 从父组件获取相关初始配置
const props = defineProps({
    options_verificationCode: Object,
    isRight: Boolean
})
const options_style = props?.options_verificationCode?.style
const options_config = props?.options_verificationCode?.config

// 声明emit,将输入的验证码传送到父组件
const emit = defineEmits(["SonValue"])
const _sendData = (codeString: string) => {
    emit("SonValue", codeString)
}

// 监听是否通过判断,并做出相应反应
// 此处是若验证码错误,则将验证框清空,并且第一个验证狂获取焦点
watch(() => props.isRight, newData => {
    if (newData == false) {
        inputArr = []
        inputDom.value[0].focus()
    }
})

let inputArr: number[] = []
const inputDom = ref<any>([])
const inputCode = (e: any, index: number) => {
    // 由于input是通过inputList循环渲染出来的,因此其dom在数组中的顺序和inputList中值的顺序相同
    let currInput = inputDom.value[index];      // 当前input dom
    let nextInput = inputDom.value[index + 1];  // 下一个input dom
    let preInput = inputDom.value[index - 1];   // 上一个input dom

    if (e.keyCode != 8 && e.keyCode != 112 && e.keyCode != 37 && e.keyCode != 39) {   // 若输入的值不是删除键(8是苹果和电脑的键值,112是安卓的键值,37是键盘左键,39是键盘右键)
        // 若当前所在index小于最后一个input所在index,焦点则自动跳转到下一个input dom
        if (index < options_config.inputNumber - 1) {
            nextInput.focus();
        } else {
            // 当输入最后一个数字后,该input框失去焦点
            currInput.blur();
            // 将输入的数组进行字符串拼接
            let codeStr = inputArr.map(element => {
                return element
            }).join("")

            // 将输入的验证码传送到父组件
            _sendData(codeStr)
        }
    } else {
        // 若输入的值是删除键,在跳回第一个输入框前,每删除一个数字,焦点都自动跳到前一个input框
        if (index != 0) {
            preInput.focus();
        }
    }
};
</script>

git地址

        GitHub - Sheyla-leila/componentsGroup

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值