The requirement is that the SMS verification code requires a grid input box, as shown in the picture
I found a case online and changed it. Just upload the code.
Structure
<template> <view class="verify-code"> <!-- Input box --> <input id="input" :value="code" class="input" :focus="isFocus" :type="inputType" :maxlength="itemSize" @input="onInput" @focus="inputFocus" @blur="inputBlur" /> <!-- Cursor --> <view id="cursor" v-if="cursorVisible & amp; & amp; type !== 'middle'" class="cursor" :style="{ left: codeCursorLeft[code.length] + 'px', height: cursorHeight + 'px', backgroundColor: cursorColor }"> </view> <!-- Input box - group --> <view id="input-ground" class="input-ground"> <view v-for="(item, index) in itemSize" :key="index" :style="{ borderColor: code.length === index & amp; & amp; cursorVisible ? boxActiveColor : boxNormalColor }" :class="['box', `box-${type + ''}`, `box::after`]"> <view :style="{ borderColor: boxActiveColor }" class="middle-line" v-if="type === 'middle' & amp; & amp; !code[index]"></view> <text class="code-text">{<!-- -->{<!-- --> code[index] | codeFormat(isPassword) }}</text> </view> </view> </view> </template> <script> /** * @description Enter verification code component * @property {string} type = [box|middle|bottom] - Display type Default: box -eg:bottom * @property {string} inputType = [text|number] - Input box type Default: number -eg:number * @property {number} size - Number of verification code input boxes Default: 6 -eg:6 * @property {boolean} isFocus - whether to focus immediately. Default: true * @property {boolean} isPassword - whether to display it in password form. Default false -eg: false * @property {string} cursorColor - Cursor color Default: #cccccc * @property {string} boxNormalColor - the color of the box where the cursor is not focused Default: #cccccc * @property {string} boxActiveColor - the color of the box where the cursor is focused Default: #000000 * @event {Function(data)} confirm - input completion callback function */ import {<!-- --> getElementRect } from './util.js'; export default {<!-- --> name: '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: '', // Entered verification code codeCursorLeft: [], // Array of distance moved to the left, itemSize: 6, getElement: getElementRect(this), isPatch: false }; }, created() {<!-- --> this.cursorVisible = this.isFocus; this.validatorSize(); }, mounted() {<!-- --> this.init(); }, methods: {<!-- --> /** * Set the number of verification code boxes */ validatorSize() {<!-- --> if (this.size > 0) {<!-- --> this.itemSize = Math.floor(this.size); } else {<!-- --> throw "methods of 'size' is integer"; } }, /** * @description initialization */ init() {<!-- --> this.getCodeCursorLeft(); this.setCursorHeight(); }, /** * @description Calculate the height of the cursor */ setCursorHeight() {<!-- --> this.getElement('.box', 'single', boxElm => {<!-- --> this.cursorHeight = boxElm.height * 0.6; }); }, /** * @description Get the left position of the cursor in each box */ getCodeCursorLeft() {<!-- --> // Get the position information of the parent box this.getElement('#input-ground', 'single', parentElm => {<!-- --> const parentLeft = parentElm.left; // Get information about each box this.getElement('.box', 'array', elms => {<!-- --> this.codeCursorLeft = []; elms.forEach(elm => {<!-- --> this.codeCursorLeft.push(elm.left - parentLeft + elm.width / 2); }); }); }); }, //Callback for input changes in the input box onInput(e) {<!-- --> let {<!-- --> value, keyCode } = e.detail; this.cursorVisible = value.length <this.itemSize; this.code = value; this.$emit('input', value); this.inputSuccess(value); }, // Input completion callback inputSuccess(value) {<!-- --> if (value.length === this.itemSize & amp; & amp; !this.isPatch) {<!-- --> this.$emit('confirm', value); } else {<!-- --> this.isPatch = false; } }, //Input focus inputFocus() {<!-- --> this.cursorVisible = this.code.length <this.itemSize; }, //Input loses focus inputBlur() {<!-- --> this.cursorVisible = false; } }, watch: {<!-- --> value(val) {<!-- --> if (val !== this.code) {<!-- --> this.code = val; } } }, filters: {<!-- --> codeFormat(val, isPassword) {<!-- --> return val ? (isPassword ? '*' : val) : ''; } } }; </script> <style scoped> .verify-code {<!-- --> position: relative; width: 100%; box-sizing: border-box; } .verify-code .input {<!-- --> height: 100%; width: 200%; position: absolute; left: -100%; z-index: 1; color: transparent; caret-color: transparent; background-color: rgba(0, 0, 0, 0); } .verify-code .cursor {<!-- --> position: absolute; top: 50%; transform: translateY(-50%); display: inline-block; width: 2px; animation-name: cursor; animation-duration: 0.8s; animation-iteration-count: infinite; } .verify-code .input-ground {<!-- --> display: flex; justify-content: space-between; align-items: center; width: 100%; box-sizing: border-box; } .verify-code .input-ground .box {<!-- --> position: relative; display: inline-block; width: 100rpx; height: 140rpx; } .verify-code .input-ground .box-bottom {<!-- --> border-bottom-width: 2px; border-bottom-style: solid; } .verify-code .input-ground .box-box {<!-- --> border-width: 2px; border-style: solid; } .verify-code .input-ground .box-middle {<!-- --> border: none; } .input-ground .box .middle-line {<!-- --> position: absolute; top: 50%; left: 50%; width: 50%; transform: translate(-50%, -50%); border-bottom-width: 2px; border-bottom-style: solid; } .input-ground .box .code-text {<!-- --> position: absolute; top: 50%; left: 50%; font-size: 80rpx; transform: translate(-50%, -50%); } @keyframes cursor {<!-- --> 0% {<!-- --> opacity: 1; } 100% {<!-- --> opacity: 0; } } </style>
util.js
/** * @description Get element node - size and other information * @param {string} elm - the id and class of the node are equivalent to the parameters of document.querySelect -eg: #id * @param {string} type = [single|array] - A single element gets multiple elements. The default is a single element. * @param {Function} callback - callback function * @param {object} that - context vue2: this, vue3: getCurrentInstance(); */ export const getElementRect = (that) => (elm, type = 'single', callback) => {<!-- --> // #ifndef H5 uni .createSelectorQuery() .in(that)[type === 'array' ? 'selectAll' : 'select'](elm) .boundingClientRect() .exec(data => {<!-- --> callback(data[0]); }); // #endif // #ifdef H5 let elmArr = []; const result = []; if (type === 'array') {<!-- --> elmArr = document.querySelectorAll(elm); } else {<!-- --> elmArr.push(document.querySelector(elm)); } for (let elm of elmArr) {<!-- --> result.push(elm.getBoundingClientRect()); } console.log('result', result) callback(type === 'array' ? result : result[0]); // #endif }