123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- <template>
- <view class="xt__verify-code">
- <!-- 输入框 -->
- <input
- id="xt__input"
- :value="code"
- class="xt__input"
- :focus="isFocus"
- :password="isPassword"
- :type="inputType"
- :maxlength="size"
- @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 size">
- <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 = [4|6] - 支持的验证码数量 默认:6 -eg:6
- * @property {boolean} isFocus - 是否立即聚焦 默认:true
- * @property {boolean} isPassword - 是否以密码形式显示 默认false -eg:false
- * @property {string} cursorColor - 光标颜色 默认:#cccccc
- * @property {string} boxNormalColor - 光标未聚焦到的框的颜色 默认:#cccccc
- * @property {string} boxActiveColor - 光标聚焦到的框的颜色 默认:#000000
- * @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: () => '#cccccc'
- },
- boxActiveColor: {
- type: String,
- default: () => '#000000'
- }
- },
- model: {
- prop: 'value',
- event: 'input'
- },
- data() {
- return {
- cursorVisible: false,
- cursorHeight: 35,
- code: '', // 输入的验证码
- codeCursorLeft: [] // 向左移动的距离数组
- };
- },
- created() {
- this.cursorVisible = this.isFocus;
- },
- mounted() {
- this.init();
- },
- methods: {
- /**
- * @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.size;
- this.$emit('input', value);
- this.inputSuccess(value);
- },
- // 输入完成回调
- inputSuccess(value) {
- if (value.length === this.size) {
- this.$emit('confirm', value);
- }
- },
- // 输入聚焦
- inputFocus() {
- this.cursorVisible = this.code.length !== this.size;
- },
- // 输入失去焦点
- 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: 76rpx;
- height: 112rpx;
- &-bottom {
- border-bottom-width: 2px;
- border-bottom-style: solid;
- }
- &-box {
- border-width: 2px;
- border-style: solid;
- }
- &-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:52rpx;
- transform: translate(-50%, -50%);
- }
- }
- }
- }
- @keyframes cursor {
- 0% {
- opacity: 1;
- }
- 100% {
- opacity: 0;
- }
- }
- </style>
|