daoji_h5/components/xt-verify-code.vue

281 lines
6.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="xt__verify-code">
<!-- 输入框 -->
<input id="xt__input" :value="code" class="xt__input" :focus="isFocus" :password="isPassword" :type="inputType"
:maxlength="itemSize" @input="input" @focus="inputFocus" @blur="inputBlur" />
<!-- 光标 -->
<view id="xt__cursor" v-if="cursorVisible && type !== 'middle'" class="xt__cursor"
:style="{ left: codeCursorLeft[code.length] + 'px', height: cursorHeight + 'px', backgroundColor: cursorColor }">
</view>
<!-- 输入框 - 组 -->
<view id="xt__input-ground" class="xt__input-ground">
<template v-for="(item, index) in itemSize">
<view :key="index"
:style="{ borderColor: code.length === index && cursorVisible ? boxActiveColor : boxNormalColor }"
:class="['xt__box', `xt__box-${type + ''}`, `xt__box::after`]">
<view :style="{ borderColor: boxActiveColor }" class="xt__middle-line"
v-if="type === 'middle' && !code[index]"></view>
<text class="xt__code-text">{{ code[index] | codeFormat(isPassword) }}</text>
</view>
</template>
</view>
</view>
</template>
<script>
/**
* @description 输入验证码组件
* @property {string} type = [box|middle|bottom] - 显示类型 默认box -eg:bottom
* @property {string} inputType = [text|number] - 输入框类型 默认number -eg:number
* @property {number} size = [1|2|3|4|5|6] - 支持的验证码数量 默认6 -eg:6
* @property {boolean} isFocus - 是否立即聚焦 默认true
* @property {boolean} isPassword - 是否以密码形式显示 默认false -eg:false
* @property {string} cursorColor - 光标颜色 默认:#cccccc
* @property {string} boxNormalColor - 光标未聚焦到的框的颜色 默认:#f7f7f7
* @property {string} boxActiveColor - 光标聚焦到的框的颜色 默认:#009254
* @event {Function(data)} confirm - 输入完成
*/
export default {
name: 'xt-verify-code',
props: {
value: {
type: String,
default: () => ''
},
type: {
type: String,
default: () => 'box'
},
inputType: {
type: String,
default: () => 'number'
},
size: {
type: Number,
default: () => 6
},
isFocus: {
type: Boolean,
default: () => true
},
isPassword: {
type: Boolean,
default: () => false
},
cursorColor: {
type: String,
default: () => '#cccccc'
},
boxNormalColor: {
type: String,
default: () => '#f7f7f7'
},
boxActiveColor: {
type: String,
default: () => '#009254'
}
},
model: {
prop: 'value',
event: 'input'
},
data() {
return {
cursorVisible: false,
cursorHeight: 35,
code: '', // 输入的验证码
codeCursorLeft: [], // 向左移动的距离数组,
itemSize: 6
};
},
created() {
this.cursorVisible = this.isFocus;
this.validatorSize();
},
mounted() {
this.init();
},
methods: {
/**
*
*/
validatorSize() {
if (this.size <= 6 && this.size > 0) {
this.itemSize = Math.floor(this.size);
} else {
this.itemSize = 6;
}
},
/**
* @description 初始化
*/
init() {
this.getCodeCursorLeft();
this.setCursorHeight();
},
/**
* @description 获取元素节点
* @param {string} elm - 节点的id、class 相当于 document.querySelect的参数 -eg: #id
* @param {string} type = [single|array] - 单个元素获取多个元素 默认是单个元素
* @param {Function} callback - 回调函数
*/
getElement(elm, type = 'single', callback) {
uni
.createSelectorQuery()
.in(this)[type === 'array' ? 'selectAll' : 'select'](elm)
.boundingClientRect()
.exec(data => {
callback(data[0]);
});
},
/**
* @description 计算光标的高度
*/
setCursorHeight() {
this.getElement('.xt__box', 'single', boxElm => {
this.cursorHeight = boxElm.height * 0.6;
});
},
/**
* @description 获取光标在每一个box的left位置
*/
getCodeCursorLeft() {
// 获取父级框的位置信息
this.getElement('#xt__input-ground', 'single', parentElm => {
const parentLeft = parentElm.left;
// 获取各个box信息
this.getElement('.xt__box', 'array', elms => {
this.codeCursorLeft = [];
elms.forEach(elm => {
this.codeCursorLeft.push(elm.left - parentLeft + elm.width / 2);
});
});
});
},
// 输入框输入变化的回调
input(e) {
const value = e.detail.value;
this.cursorVisible = value.length !== this.itemSize;
this.$emit('input', value);
this.inputSuccess(value);
},
// 输入完成回调
inputSuccess(value) {
if (value.length === this.itemSize) {
this.$emit('confirm', value);
}
},
// 输入聚焦
inputFocus() {
this.cursorVisible = this.code.length !== this.itemSize;
},
// 输入失去焦点
inputBlur() {
this.cursorVisible = false;
}
},
watch: {
value(val) {
this.code = val;
}
},
filters: {
codeFormat(val, isPassword) {
let value = '';
if (val) {
value = isPassword ? '*' : val;
}
return value;
}
}
};
</script>
<style lang="scss" scoped>
.xt__verify-code {
position: relative;
width: 100%;
box-sizing: border-box;
.xt__input {
height: 100%;
width: 200%;
position: absolute;
left: -100%;
z-index: 1;
}
.xt__cursor {
position: absolute;
top: 50%;
transform: translateY(-50%);
display: inline-block;
width: 2px;
animation-name: cursor;
animation-duration: 0.8s;
animation-iteration-count: infinite;
}
.xt__input-ground {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
box-sizing: border-box;
.xt__box {
position: relative;
display: inline-block;
width: 88rpx;
height: 100rpx;
background: #F7F7F7;
border-radius: 12rpx;
margin-left: 20rpx;
&-bottom {
border-bottom: 1rpx solid #009254;
}
&-box {
border: 1rpx solid #009254;
}
&-middle {
border: none;
}
.xt__middle-line {
position: absolute;
top: 50%;
left: 50%;
width: 50%;
transform: translate(-50%, -50%);
border-bottom-width: 2px;
border-bottom-style: solid;
}
.xt__code-text {
position: absolute;
top: 50%;
left: 50%;
font-size: 58rpx;
transform: translate(-50%, -50%);
}
}
.xt__box:nth-child(1){
margin-left: 0;
}
}
}
@keyframes cursor {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
</style>