<template> 
 | 
    <view class="u-code-input"> 
 | 
        <view 
 | 
            class="u-code-input__item" 
 | 
            :style="[itemStyle(index)]" 
 | 
            v-for="(item, index) in codeLength" 
 | 
            :key="index" 
 | 
        > 
 | 
            <view 
 | 
                class="u-code-input__item__dot" 
 | 
                v-if="dot && codeArray.length > index" 
 | 
            ></view> 
 | 
            <text 
 | 
                v-else 
 | 
                :style="{ 
 | 
                    fontSize: $u.addUnit(fontSize), 
 | 
                    fontWeight: bold ? 'bold' : 'normal', 
 | 
                    color: color 
 | 
                }" 
 | 
            >{{codeArray[index]}}</text> 
 | 
            <view 
 | 
                class="u-code-input__item__line" 
 | 
                v-if="mode === 'line'" 
 | 
                :style="[lineStyle]" 
 | 
            ></view> 
 | 
            <!-- #ifndef APP-PLUS --> 
 | 
            <view v-if="isFocus && codeArray.length === index" :style="{backgroundColor: color}" class="u-code-input__item__cursor"></view> 
 | 
            <!-- #endif --> 
 | 
        </view> 
 | 
        <input 
 | 
            :disabled="disabledKeyboard" 
 | 
            type="number" 
 | 
            :focus="focus" 
 | 
            :value="inputValue" 
 | 
            :maxlength="maxlength" 
 | 
            :adjustPosition="adjustPosition" 
 | 
            class="u-code-input__input" 
 | 
            @input="inputHandler" 
 | 
            :style="{ 
 | 
                height: $u.addUnit(size)  
 | 
            }" 
 | 
            @focus="isFocus = true" 
 | 
            @blur="isFocus = false" 
 | 
        /> 
 | 
    </view> 
 | 
</template> 
 | 
  
 | 
<script> 
 | 
    import props from './props.js'; 
 | 
    /** 
 | 
     * CodeInput 验证码输入 
 | 
     * @description 该组件一般用于验证用户短信验证码的场景,也可以结合uView的键盘组件使用 
 | 
     * @tutorial https://www.uviewui.com/components/codeInput.html 
 | 
     * @property {String | Number}    maxlength            最大输入长度 (默认 6 ) 
 | 
     * @property {Boolean}            dot                    是否用圆点填充 (默认 false ) 
 | 
     * @property {String}            mode                显示模式,box-盒子模式,line-底部横线模式 (默认 'box' ) 
 | 
     * @property {Boolean}            hairline            是否细边框 (默认 false ) 
 | 
     * @property {String | Number}    space                字符间的距离 (默认 10 ) 
 | 
     * @property {String | Number}    value                预置值 
 | 
     * @property {Boolean}            focus                是否自动获取焦点 (默认 false ) 
 | 
     * @property {Boolean}            bold                字体和输入横线是否加粗 (默认 false ) 
 | 
     * @property {String}            color                字体颜色 (默认 '#606266' ) 
 | 
     * @property {String | Number}    fontSize            字体大小,单位px (默认 18 ) 
 | 
     * @property {String | Number}    size                输入框的大小,宽等于高 (默认 35 ) 
 | 
     * @property {Boolean}            disabledKeyboard    是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true (默认 false ) 
 | 
     * @property {String}            borderColor            边框和线条颜色 (默认 '#c9cacc' ) 
 | 
     * @property {Boolean}            disabledDot            是否禁止输入"."符号 (默认 true ) 
 | 
     *  
 | 
     * @event {Function}    change    输入内容发生改变时触发,具体见上方说明            value:当前输入的值 
 | 
     * @event {Function}    finish    输入字符个数达maxlength值时触发,见上方说明    value:当前输入的值 
 | 
     * @example    <u-code-input v-model="value4" :focus="true"></u-code-input> 
 | 
     */ 
 | 
    export default { 
 | 
        name: 'u-code-input', 
 | 
        mixins: [uni.$u.mpMixin, uni.$u.mixin, props], 
 | 
        data() { 
 | 
            return { 
 | 
                inputValue: '', 
 | 
                isFocus: this.focus 
 | 
            } 
 | 
        }, 
 | 
        watch: { 
 | 
            value: { 
 | 
                immediate: true, 
 | 
                handler(val) { 
 | 
                    // 转为字符串,超出部分截掉 
 | 
                    this.inputValue = String(val).substring(0, this.maxlength) 
 | 
                } 
 | 
            }, 
 | 
        }, 
 | 
        computed: { 
 | 
            // 根据长度,循环输入框的个数,因为头条小程序数值不能用于v-for 
 | 
            codeLength() { 
 | 
                return new Array(Number(this.maxlength)) 
 | 
            }, 
 | 
            // 循环item的样式 
 | 
            itemStyle() { 
 | 
                return index => { 
 | 
                    const addUnit = uni.$u.addUnit 
 | 
                    const style = { 
 | 
                        width: addUnit(this.size), 
 | 
                        height: addUnit(this.size) 
 | 
                    } 
 | 
                    // 盒子模式下,需要额外进行处理 
 | 
                    if (this.mode === 'box') { 
 | 
                        // 设置盒子的边框,如果是细边框,则设置为0.5px宽度 
 | 
                        style.border = `${this.hairline ? 0.5 : 1}px solid ${this.borderColor}` 
 | 
                        // 如果盒子间距为0的话 
 | 
                        if (uni.$u.getPx(this.space) === 0) { 
 | 
                            // 给第一和最后一个盒子设置圆角 
 | 
                            if (index === 0) { 
 | 
                                style.borderTopLeftRadius = '3px' 
 | 
                                style.borderBottomLeftRadius = '3px' 
 | 
                            } 
 | 
                            if (index === this.codeLength.length - 1) { 
 | 
                                style.borderTopRightRadius = '3px' 
 | 
                                style.borderBottomRightRadius = '3px' 
 | 
                            } 
 | 
                            // 最后一个盒子的右边框需要保留 
 | 
                            if (index !== this.codeLength.length - 1) { 
 | 
                                style.borderRight = 'none' 
 | 
                            } 
 | 
                        } 
 | 
                    } 
 | 
                    if (index !== this.codeLength.length - 1) { 
 | 
                        // 设置验证码字符之间的距离,通过margin-right设置,最后一个字符,无需右边框 
 | 
                        style.marginRight = addUnit(this.space) 
 | 
                    } else { 
 | 
                        // 最后一个盒子的有边框需要保留 
 | 
                        style.marginRight = 0 
 | 
                    } 
 | 
  
 | 
                    return style 
 | 
                } 
 | 
            }, 
 | 
            // 将输入的值,转为数组,给item历遍时,根据当前的索引显示数组的元素 
 | 
            codeArray() { 
 | 
                return String(this.inputValue).split('') 
 | 
            }, 
 | 
            // 下划线模式下,横线的样式 
 | 
            lineStyle() { 
 | 
                const style = {} 
 | 
                style.height = this.hairline ? '2px' : '4px' 
 | 
                style.width = uni.$u.addUnit(this.size) 
 | 
                // 线条模式下,背景色即为边框颜色 
 | 
                style.backgroundColor = this.borderColor 
 | 
                return style 
 | 
            } 
 | 
        }, 
 | 
        methods: { 
 | 
            // 监听输入框的值发生变化 
 | 
            inputHandler(e) { 
 | 
                const value = e.detail.value 
 | 
                this.inputValue = value 
 | 
                // 是否允许输入“.”符号 
 | 
                if(this.disabledDot) { 
 | 
                    this.$nextTick(() => { 
 | 
                        this.inputValue = value.replace('.', '') 
 | 
                    }) 
 | 
                } 
 | 
                // 未达到maxlength之前,发送change事件,达到后发送finish事件 
 | 
                this.$emit('change', value) 
 | 
                // 修改通过v-model双向绑定的值 
 | 
                this.$emit('input', value) 
 | 
                // 达到用户指定输入长度时,发出完成事件 
 | 
                if (String(value).length >= Number(this.maxlength)) { 
 | 
                    this.$emit('finish', value) 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
</script> 
 | 
  
 | 
<style lang="scss" scoped> 
 | 
    @import "../../libs/css/components.scss"; 
 | 
    $u-code-input-cursor-width: 1px; 
 | 
    $u-code-input-cursor-height: 40%; 
 | 
    $u-code-input-cursor-animation-duration: 1s; 
 | 
    $u-code-input-cursor-animation-name: u-cursor-flicker; 
 | 
  
 | 
    .u-code-input { 
 | 
        @include flex; 
 | 
        position: relative; 
 | 
        overflow: hidden; 
 | 
  
 | 
        &__item { 
 | 
            @include flex; 
 | 
            justify-content: center; 
 | 
            align-items: center; 
 | 
            position: relative; 
 | 
  
 | 
            &__text { 
 | 
                font-size: 15px; 
 | 
                color: $u-content-color; 
 | 
            } 
 | 
  
 | 
            &__dot { 
 | 
                width: 7px; 
 | 
                height: 7px; 
 | 
                border-radius: 100px; 
 | 
                background-color: $u-content-color; 
 | 
            } 
 | 
  
 | 
            &__line { 
 | 
                position: absolute; 
 | 
                bottom: 0; 
 | 
                height: 4px; 
 | 
                border-radius: 100px; 
 | 
                width: 40px; 
 | 
                background-color: $u-content-color; 
 | 
            } 
 | 
            /* #ifndef APP-PLUS */ 
 | 
            &__cursor { 
 | 
                position: absolute; 
 | 
                top: 50%; 
 | 
                left: 50%; 
 | 
                transform: translate(-50%,-50%); 
 | 
                width: $u-code-input-cursor-width; 
 | 
                height: $u-code-input-cursor-height; 
 | 
                animation: $u-code-input-cursor-animation-duration u-cursor-flicker infinite; 
 | 
            } 
 | 
            /* #endif */ 
 | 
             
 | 
        } 
 | 
  
 | 
        &__input { 
 | 
            // 之所以需要input输入框,是因为有它才能唤起键盘 
 | 
            // 这里将它设置为两倍的屏幕宽度,再将左边的一半移出屏幕,为了不让用户看到输入的内容 
 | 
            position: absolute; 
 | 
            left: -750rpx; 
 | 
            width: 1500rpx; 
 | 
            top: 0; 
 | 
            background-color: transparent; 
 | 
            text-align: left; 
 | 
        } 
 | 
    } 
 | 
     
 | 
    /* #ifndef APP-PLUS */ 
 | 
    @keyframes u-cursor-flicker { 
 | 
        0% { 
 | 
            opacity: 0; 
 | 
        } 
 | 
        50% { 
 | 
            opacity: 1; 
 | 
        } 
 | 
        100% { 
 | 
            opacity: 0; 
 | 
        } 
 | 
    } 
 | 
    /* #endif */ 
 | 
  
 | 
</style> 
 |