uniapp+vue3移动端实现输入验证码

在这里插入图片描述

ios+安卓+ uniapp+vue3+ 微信小程序端

<template><view class="verification-code"><view class="verification-code__display"><block v-for="i in numberArr" :key="i"><view:class="['verification-code__digit',codes.length === i && isFocus ? 'verification-code__digit--active' : '',codes[i] ? 'verification-code__digit--filled' : '',]">{{ codes[i] || '' }}</view></block></view><view class="verification-code__input-wrapper"><inputv-model="value":focus="true"@focus="onFocus"@blur="onBlur"type="number"@input="onChange":maxlength="maxLength"class="verification-code__input"/></view></view>
</template><script setup lang="ts">
import { ref, onMounted, defineProps, defineEmits } from 'vue'const props = defineProps({maxLength: {type: Number,default: 6,},
})const emit = defineEmits(['complete'])const numberArr = ref<number[]>([])
const codes = ref<string[]>([])
const value = ref('')
const isFocus = ref(false)// 初始化数组
onMounted(() => {numberArr.value = Array.from({ length: props.maxLength }, (_, i) => i)
})// 处理焦点
const onFocus = () => {isFocus.value = true
}const onBlur = () => {isFocus.value = false
}// 处理输入变化
const onChange = (e: { detail: { value: string } }) => {const str = e.detail.valueif (str) {codes.value = String(str).split('')if (codes.value.length === props.maxLength) {emit('complete', str)uni.hideKeyboard()}} else {codes.value = []}
}
</script><style lang="scss">
.verification-code {position: relative;overflow: hidden;text-align: unset;margin-bottom: 80rpx;&__display {display: flex;justify-content: space-between;align-items: center;gap: 20rpx;}&__digit {box-sizing: border-box;width: 80rpx;height: 80rpx;border-bottom: 1px solid #ddd;display: flex;align-items: center;justify-content: center;transition: all 0.3s ease;&--active {border-bottom: 2rpx solid #000;animation: blink 600ms infinite;}&--filled {border-bottom: 2rpx solid #333;}}&__input-wrapper {position: absolute;z-index: 99;left: 0;top: 0;height: 80rpx;opacity: 0;text-align: unset;}&__input {height: 80rpx;text-align: unset;width: 9999rpx;/* #ifdef MP-WEIXIN */position: relative;left: -999rpx;/* #endif */}
}@keyframes blink {0% {opacity: 0.1;}100% {opacity: 1;}
}
</style>

代码实现如上效果(不能灵活删除)

<template><view class="uni-flex uni-flex-center vcode-input-body"><viewclass="vcode-input-item vcode-input-line"v-for="(v, index) in sum":key="index":style="getStyle(index)">{{ text[index] ? text[index] : '' }}<view v-if="focus && text.length === index" class="cursor"></view></view><inputref="VcodeInput"type="number"class="hidden-input":focus="focus":maxlength="sum"@input="inputVal"@blur="setBlur":password="isPassword"placeholder="验证码"/></view>
</template><script>
export default {name: 'VcodeInput',props: {sum: {type: Number,default: 6,},isBorderLine: {type: Boolean,default: false,},borderColor: {type: String,default: '#DADADA',},borderValueColor: {type: String,default: '#424456',},borderActiveColor: {type: String,default: '#000000',},isAutoComplete: {type: Boolean,default: true,},isPassword: {type: Boolean,default: false,},},data() {return {focus: false,text: [],currentIndex: 0,}},created() {setTimeout(() => {this.focus = true}, 300)},methods: {getStyle(index) {let style = {}style.borderColor = this.borderColorif (this.text.length > index) {style.borderColor = this.borderValueColorstyle.color = this.borderValueColor}if (this.currentIndex === index) {style.borderColor = this.borderActiveColor}return style},setBlur() {this.focus = false},setFocus(index) {this.focus = truethis.currentIndex = index// 如果点击的位置在已输入字符之后,则移动到最后一个字符的位置if (index > this.text.length) {this.currentIndex = this.text.length}// 设置输入框的值,允许修改当前位置的内容const input = this.$refs.VcodeInputif (input) {input.value = this.text.join('')// 设置光标位置setTimeout(() => {input.setSelectionRange(this.currentIndex, this.currentIndex)}, 0)}},inputVal(e) {let value = e.detail.valueif (this.isAutoComplete) {if (value.length >= this.sum) {this.focus = falsethis.$emit('vcodeInput', value)}} else {this.$emit('vcodeInput', value)}if (this.isPassword) {let val = ''for (let i = 0; i < value.length; i++) {val += '●'}this.text = val} else {this.text = value}// 更新光标位置到输入位置this.currentIndex = value.length},},
}
</script><style lang="scss" scoped>
.vcode-input-body {width: 100%;position: relative;overflow: hidden;display: flex;flex-direction: row;justify-content: center;margin: 0 auto;.vcode-input-item {width: 76rpx;height: 76rpx;margin-left: 12rpx;margin-right: 12rpx;line-height: 76rpx;text-align: center;font-weight: 500;position: relative;}.vcode-input-border {border-style: solid;border-width: 2rpx;border-color: $uni-border-color;border-radius: 4rpx;}.vcode-input-line {border-bottom-style: solid;border-bottom-width: 2rpx;border-color: $uni-border-color;}.hidden-input {width: 1px;height: 1px;position: absolute;left: -1px;top: -1px;}.cursor {position: absolute;left: 30%;bottom: 18rpx;width: 2rpx;height: 30rpx;background-color: #000000;transform: translateX(-50%);animation: blink 1s infinite;}
}@keyframes blink {0%,100% {opacity: 1;}50% {opacity: 0;}
}
</style>

灵活删除代码实现(区分ios和安卓端)

<template><view v-if="isIos"><inputref="codeInputRef"class="hidden-input"type="number":focus="isFocused":maxlength="maxLength"v-model="verificationCode"@input="handleCodeInput"@focus="isFocused = true"@blur="isFocused = false"/><view class="verification-code-container"><view class="code-display"><viewv-for="(_, index) in maxLength":key="index"class="code-cell":class="{active: isFocused && index === verificationCode.length,filled: index < verificationCode.length,}"@click="isFocused = true">{{ verificationCode[index] || '' }}</view></view></view></view><view class="code-input-container" v-if="!isIos"><view v-for="(digit, index) in props.maxLength" :key="index" class="code-input"><inputtype="number"maxlength="1"class="phone-code-input"v-model="passwordDigits[index]":focus="index === focusIndex"@input="handleInput(index)"@paste="handlePaste"/></view></view>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits, watch, onMounted, onUnmounted } from 'vue'
const props = defineProps({maxLength: {type: Number,default: 0,},
})
const emit = defineEmits(['complete'])
const verificationCode = ref('')
const codeInputRef = ref()
const isFocused = ref(true)const handleCodeInput = (e) => {const value = e.detail.value.replace(/\D/g, '') // 只保留数字verificationCode.value = value.slice(0, props.maxLength) // 限制位数// 输入完成if (value.length === props.maxLength) {onCodeComplete()}
}
// 验证码输入完成
const onCodeComplete = () => {// submitForm()emit('complete', verificationCode.value)uni.hideKeyboard()
}// 设备类型
const isIos = ref(uni.getSystemInfoSync().platform === 'ios')// 密码数组,初始化为空
const passwordDigits = ref(Array(props.maxLength).fill('')) // 处理输入// 当前聚焦的输入框索引
const focusIndex = ref(0)// 处理输入事件
const handleInput = (index: number) => {// 如果当前输入框有值,并且不是最后一个输入框,则自动聚焦到下一个输入框if (passwordDigits.value[index] !== '' && index < props.maxLength - 1) {focusIndex.value = index + 1}if (passwordDigits.value[index] === '' && index > 0) {focusIndex.value = index - 1}
}// 键盘删除事件
const handleKeyUp = (event: PlusKeyKeyEvent) => {if (event.keyCode === 67 &&passwordDigits.value[focusIndex.value] === '' &&focusIndex.value > 0) {focusIndex.value -= 1}
}
onMounted(() => {if (!isIos.value) {plus.key.addEventListener('keyup', handleKeyUp)}
})onUnmounted(() => {if (!isIos.value) {plus.key.removeEventListener('keyup', handleKeyUp)}
})const handlePaste = (event: Event) => {// 获取剪贴板内容(在某些平台上可能无法直接访问 clipboardData)if (uni.getClipboardData) {uni.getClipboardData({success: (res) => {// 提取粘贴内容中的所有数字const pasteContent = res.data.trim() // 去除可能的空格const extractedNumbers = pasteContent.replace(/\D/g, '') // 只保留数字,替换掉非数字字符// 如果提取的数字是6位,继续处理if (extractedNumbers.length === props.maxLength) {console.log('extractedNumbers', extractedNumbers)// 填充验证码数组passwordDigits.value = extractedNumbers.split('')onCodeComplete()}},fail: (err) => {console.log('获取剪贴板数据失败', err)},})} else {console.log('当前平台不支持获取剪贴板数据')}
}// 监听密码输入完成
watch(passwordDigits,(newValue) => {// 检查是否所有数字都已输入const isAllFilled = newValue.every((digit) => digit !== '')if (isAllFilled) {// 短暂延迟以确保用户看到最后一个数字setTimeout(() => {emit('complete', passwordDigits.value.join(''))}, 200)}},{ deep: true },
)
</script>
<style lang="scss" scoped>
.verification-code-container {position: relative;padding: 20rpx;
}.hidden-input {position: absolute;left: -9999rpx;opacity: 0;width: 0;height: 0;
}.code-display {display: flex;justify-content: center;
}.code-cell {width: 80rpx;height: 80rpx;margin: 0 10rpx;display: flex;justify-content: center;align-items: center;font-size: 40rpx;border-bottom: 2rpx solid #ddd;position: relative;
}.code-cell.filled {border-bottom-color: #007aff;
}.code-cell.active::after {content: '';position: absolute;bottom: 5rpx;left: 50%;transform: translateX(-50%);width: 40rpx;height: 4rpx;background-color: #007aff;animation: blink 1s infinite;
}@keyframes blink {0%,100% {opacity: 1;}50% {opacity: 0;}
}.code-input-container {display: flex;justify-content: space-between;position: relative;margin-bottom: 40rpx;
}.code-input {width: 80rpx;height: 100rpx;text-align: center;font-size: 40rpx;outline: none;background: transparent;
}.phone-code-input {width: 80rpx;height: 100rpx;text-align: center;font-size: 40rpx;outline: none;background: transparent;border-bottom: 2rpx solid #ccc;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80757.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何选择游戏支付平台呢?

如果要选择一个游戏支付平台的话&#xff0c;那么你可以考虑一下这个平台&#xff1a;功能非常多&#xff0c;支付模式很高效&#xff0c;功能很全&#xff0c;服务很贴心&#xff0c;资金安全靠得住&#xff0c;安全认证模式也很可靠。 第二&#xff0c;结算方法也很多&#x…

前端如何获取文件的 Hash 值?多种方式详解、对比与实践指南

文章目录 前言一、Hash 值为何重要&#xff1f;二、Hash 值基础知识2.1 什么是 Hash&#xff1f;2.2 Hash 在前端的应用场景2.3 常见的 Hash 算法&#xff08;MD5、SHA 系列&#xff09; 三、前端获取文件 Hash 的常用方式3.1 使用 SparkMD5 计算 MD5 值3.2 使用 Web Crypto AP…

【Java学习笔记】类与对象

类与对象 什么是类&#xff1f; 知识迁移&#xff1a;类比 C 语言中的结构体 类的描述 类是一个对象的抽象&#xff0c;从字面意思就表示一个类的事物&#xff0c;类具有属性和方法&#xff08;行为&#xff09;&#xff0c;对象是类的一个具体表现 总结&#xff1a;类是对象…

如何对极狐GitLab 议题进行过滤和排序?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 排序和议题列表排序 (BASIC ALL) 您可以通过多种方式对议题列表进行排序&#xff0c;可用的排序选项可以根据列表的上下文进…

k8s中资源的介绍及标准资源namespaces实践

文章目录 第1章 k8s中的资源(resources)介绍1.1 k8s中资源(resouces)的分类1.2 k8s中资源(resources)的级别1.3 k8s中资源(resources)的API规范1.4 k8s中资源(resources)的manifests 第2章 k8s中的标准资源之namespaces的实践2.1 基本介绍2.2 编写相关ns资源对象的manifests2.3…

优化uniappx页面性能,处理页面滑动卡顿问题

问题&#xff1a;在页面遇到滑动特别卡的情况就是在页面使用了动态样式或者动态类&#xff0c;做切换的时候页面重新渲染导致页面滑动卡顿 解决&#xff1a;把动态样式和动态类做的样式切换改为通过获取元素修改样式属性值 循环修改样式示例 bannerList.forEach((_, index)…

DeepSeek赋能Nuclei:打造网络安全检测的“超级助手”

引言 各位少侠&#xff0c;周末快乐&#xff0c;幸会幸会&#xff01; 今天唠一个超酷的技术组合——用AI大模型给Nuclei开挂&#xff0c;提升漏洞检测能力&#xff01; 想象一下&#xff0c;当出现新漏洞时&#xff0c;少侠们经常需要根据Nuclei模板&#xff0c;手动扒漏洞文章…

leetcode - 字符串

字符串 466. 统计重复个数 题目 定义 str [s, n] 表示 str 由 n 个字符串 s 连接构成。 例如&#xff0c;str ["abc", 3] "abcabcabc" 。 如果可以从 s2( )中删除某些字符使其变为 s1&#xff0c;则称字符串 s1( )可以从字符串 s2 获得。 例如&#xf…

Java求职者面试:从Spring Boot到微服务的技术深度探索

场景&#xff1a;互联网大厂Java求职者面试 角色介绍&#xff1a; 面试官&#xff1a;技术精湛&#xff0c;负责把控面试质量。谢飞机&#xff1a;搞笑的程序员&#xff0c;偶尔能答对问题。 第一轮&#xff1a;基础知识 面试官&#xff1a;谢飞机&#xff0c;你能简要介绍…

榕壹云国际版短剧系统:基于Spring Boot+MySQL+UniApp的全球短剧创作平台

一、项目背景与简介 在短视频行业高速发展的今天&#xff0c;短剧内容已成为全球用户娱乐消费的新宠。为满足市场对高质量、多样化短剧的需求&#xff0c;我们基于Spring Boot MySQL UniApp技术栈开发了榕壹云国际版短剧系统&#xff0c;这是一款面向全球市场的短剧创作与分…

资料分享!瑞芯微RK3506(3核ARM+Cortex-A7 + ARM Cortex-M0)工业评估板硬件资料

前 言 本文主要介绍TL3506-EVM评估板硬件接口资源以及设计注意事项等内容。 RK3506J/RK3506B处理器的IO电平标准一般为1.8V、3.3V,上拉电源一般不超过3.3V或1.8V,当外接信号电平与IO电平不匹配时,中间需增加电平转换芯片或信号隔离芯片。按键或接口需考虑ESD设计,ESD器…

C#通过NTP服务器获取NTP时间

C#通过NTP服务器获取NTP时间 注意事项&#xff1a; 如果NTP服务器地址是域名&#xff0c;如阿里云的NTP服务器地址。需要DNS解析。NTP使用UDP通讯&#xff0c;默认端口是123NTP经过很多年的发展&#xff0c;有4个版本号&#xff0c;目前常用的3和4。NTP区分客户端和服务端&am…

使用cmd来创建数据库和数据库表-简洁步骤

创建数据库和表&#xff1a; 1. 按WinR打开“运行”&#xff0c;输入cmd&#xff0c;回车 2. 登录数据库&#xff1a;mysql -u root -p 然后输入密码 3. 创建数据库create database myblog; myblog为数据库名(自定义你的数据库名) &#xff01;注意分号不要漏了&#xff01; …

java工具类

LocalDateTime LocalDateTime可以获取当前时间&#xff1a; LocalDateTime now LocalDateTime.now(); 同时他也可以获取指定时间&#xff1a; LocalDateTime dateTime LocalDateTime.of(2023, 5, 15, 10, 30) 若我们时间值超出了我们的实际情况值&#xff0c;我们将会出现…

02_java的运行机制以及JDKJREJVM基本介绍

1、运行机制 2、JDK&JRE&JVM JDK 基本介绍 &#xff08;1&#xff09; JDK 的全称(Java Development Kit Java开发工具包) JDK JRE java的开发工具 [ java, javac, javadoc, javap等 ] &#xff08;2&#xff09;JDK是提供给Java开发人员使用的&#xff0c;其…

【文心快码】确实有点东西!

这里写自定义目录标题 背景 Electron 开发 Markdown 编辑器全记录提问1&#xff1a;提问2&#xff1a;提问3&#xff1a;提问4&#xff1a;完整项目结构总结 背景 前两天百度在2025 百度AI开发者大会"如何驾驭Coding Agent分会场"上发布了文心快码&#xff0c;注册试…

AI心理健康服务平台项目面试实战

AI心理健康服务平台项目面试实战 第一轮提问&#xff1a; 面试官&#xff1a; 请简要介绍一下AI心理健康服务平台的核心技术架构。在AI领域&#xff0c;心理健康服务的机遇主要体现在哪些方面&#xff1f;如何利用NLP技术提升用户与AI的心理健康对话体验&#xff1f; 马架构…

Win10安装 P104-100 驱动

安装完之后总结一下, 之前做了不少功课, 在网上搜了很多教程, 视频的文字的, 但是很多已经比较陈旧了. 最后发现的这个 GitHub 项目 NVIDIA-patcher 是最有用的, 因为这是现在这些魔改驱动的来源. NVIDIA-patcher 仓库地址: https://github.com/dartraiden/NVIDIA-patcher 安…

把一个 PyTorch 的图像张量转换成 NumPy 格式,并按照正确的维度顺序显示出来

示例代码&#xff1a; plt.imshow(np.transpose(tensor_denorm.numpy(), (1, 2, 0)))它的作用是&#xff1a;把一个 PyTorch 的图像张量转换成 NumPy 格式&#xff0c;并按照正确的维度顺序显示出来。 &#x1f680; 一步步解释&#xff1a; ✅ tensor_denorm 这是一个形状为…

【速写】conda安装(linux)

序言 昨天叶凯浩空降&#xff08;全马241&#xff09;&#xff0c;降维打击&#xff0c;10分24秒断层夺冠。 夏潇阳10分53秒绝杀小崔10分54秒第2&#xff0c;小崔第3&#xff0c;均配都在3’30"以内&#xff0c;即便我是去年巅峰期也很难跑出这种水平。我就知道他去年大…