领先的免费Web技术教程,涵盖HTML到ASP.NET

网站首页 > 知识剖析 正文

vue实现一个6个输入框的验证码输入组件

nixiaole 2024-12-31 13:42:27 知识剖析 23 ℃

作者:jsoncode

转发链接:https://segmentfault.com/a/1190000023041

前言

要实现的功能:

完全和单输入框一样的操作,甚至可以插入覆盖:

1,限制输入数字

2,正常输入

3,backspace删除

4,paste任意位置粘贴输入

5,光标选中一个数字,滚轮可以微调数字大小,限制0-9

6,123|456 自动覆盖光标后输入的字符,此时光标在3后,继续输入111,会得到123111,而不用手动删除456

7,封装成vue单文件组件,方便任意调用。

模板代码

<template>
    <div class="input-box">
        <div class="input-content" @keydown="keydown" @keyup="keyup" @paste="paste" @mousewheel="mousewheel"
                @input="inputEvent">
            <input max="9" min="0" maxlength="1" data-index="0" v-model.trim.number="input[0]" type="number"
                    ref="firstinput"/>
            <input max="9" min="0" maxlength="1" data-index="1" v-model.trim.number="input[1]" type="number"/>
            <input max="9" min="0" maxlength="1" data-index="2" v-model.trim.number="input[2]" type="number"/>
            <input 
            <input max="9" min="0" maxlength="1" data-index="4" v-model.trim.number="input[4]" type="number"/>
            <input max="9" min="0" maxlength="1" data-index="5" v-model.trim.number="input[5]" type="number"/>
        </div>
    </div>
</template>

实现了键盘的keydown/keyup/paste/input和鼠标滚轮mousewheel事件

使用了6个输入框的方案来实现。

样式部分:使用了scss模式

<style scoped lang="scss">
    .input-box {
        .input-content {
            width: 512px;
            height: 60px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            
            input {
                color: inherit;
                font-family: inherit;
                border: 0;
                outline: 0;
                border-bottom: 1px solid #919191;
                height: 60px;
                width: 60px;
                font-size: 44px;
                text-align: center;
            }
        }
        
        input::-webkit-outer-spin-button,
        input::-webkit-inner-spin-button {
            appearance: none;
            margin: 0;
        }
    }
</style>

具体实现逻辑:主要实现以上几个键盘事件操作。

<script>
    export default {
        data() {
            return {
                // 存放粘贴进来的数字
                pasteResult: [],
            };
        },
        props: ['code'],
        computed: {
            input() {
                // 计算输入框的值
                if (this.code && Array.isArray(this.code) && this.code.length === 6) {
                    return this.code;
                } else if (/^\d{6}$/.test(this.code.toString())) {
                    return this.code.toString().split('');
                } else if (this.pasteResult.length === 6) {
                    return this.pasteResult;
                } else {
                    return new Array(6);
                }
            }
        },
        methods: {
            // 解决一个输入框输入多个字符
            inputEvent(e) {
                var index = e.target.dataset.index * 1;
                var el = e.target;
                this.$set(this.input, index, el.value.slice(0, 1))
            },
            keydown(e) {
                var index = e.target.dataset.index * 1;
                var el = e.target;
                if (e.key === 'Backspace') {
                    if (this.input[index].length > 0) {
                        this.$set(this.input, index, '')
                    } else {
                        if (el.previousElementSibling) {
                            el.previousElementSibling.focus()
                            this.$set(this.input, index - 1, '')
                        }
                    }
                } else if (e.key === 'Delete') {
                    if (this.input[index].length > 0) {
                        this.$set(this.input, index, '')
                    } else {
                        if (el.nextElementSibling) {
                            this.$set(this.input, index = 1, '')
                        }
                    }
                    if (el.nextElementSibling) {
                        el.nextElementSibling.focus()
                    }
                } else if (e.key === 'Home') {
                    el.parentElement.children[0] && el.parentElement.children[0].focus()
                } else if (e.key === 'End') {
                    el.parentElement.children[this.input.length - 1] && el.parentElement.children[this.input.length - 1].focus()
                } else if (e.key === 'ArrowLeft') {
                    if (el.previousElementSibling) {
                        el.previousElementSibling.focus()
                    }
                } else if (e.key === 'ArrowRight') {
                    if (el.nextElementSibling) {
                        el.nextElementSibling.focus()
                    }
                } else if (e.key === 'ArrowUp') {
                    if (this.input[index] * 1 < 9) {
                        this.$set(this.input, index, (this.input[index] * 1 + 1).toString());
                    }
                } else if (e.key === 'ArrowDown') {
                    if (this.input[index] * 1 > 0) {
                        this.$set(this.input, index, (this.input[index] * 1 - 1).toString());
                    }
                }
            },
            keyup(e) {
                var index = e.target.dataset.index * 1;
                var el = e.target;
                // 解决输入e和无法删除e的问题 感谢一群网友 https://segmentfault.com/u/nanzk8fl 的指正
                el.value=el.value.replace(/1/g,'');
                if (/Digit|Numpad/i.test(e.code)) {
                    this.$set(this.input, index, e.code.replace(/Digit|Numpad/i, ''));
                    el.nextElementSibling && el.nextElementSibling.focus();
                    if (index === 5) {
                        if (this.input.join('').length === 6) {
                            document.activeElement.blur();
                            this.$emit('complete', this.input);
                        }
                    }
                } else {
                    if (this.input[index] === '') {
                        this.$set(this.input, index, '');
                    }
                }
            },
            mousewheel(e) {
                var index = e.target.dataset.index;
                if (e.wheelDelta > 0) {
                    if (this.input[index] * 1 < 9) {
                        this.$set(this.input, index, (this.input[index] * 1 + 1).toString());
                    }
                } else if (e.wheelDelta < 0) {
                    if (this.input[index] * 1 > 0) {
                        this.$set(this.input, index, (this.input[index] * 1 - 1).toString());
                    }
                } else if (e.key === 'Enter') {
                    if (this.input.join('').length === 6) {
                        document.activeElement.blur();
                        this.$emit('complete', this.input);
                    }
                }
            },
            paste(e) {
                // 当进行粘贴时
                e.clipboardData.items[0].getAsString(str => {
                    if (str.toString().length === 6) {
                        this.pasteResult = str.split('');
                        document.activeElement.blur();
                        this.$emit('complete', this.input);
                        // 恢复缓冲区
                        this.pasteResult = [];
                    }
                    else {
                        // 如果粘贴内容不合规,清除所有内容
                        this.input = new Array(6)
                    }
                })
            }
        },
        mounted() {
            // 等待dom渲染完成,在执行focus,否则无法获取到焦点
            this.$nextTick(() => {
                this.$refs.firstinput.focus()
            })
        },
    }
</script>

如果你发现了bug,或者有优化空间,欢迎你的指正和建议。我会随时更新到源代码当中,分享给大家。

推荐Vue学习资料文章:

一用惊人的Vue实践技巧「值得推荐」

Vue常见的面试知识点汇总(上)「附答案」

Vue常见的面试知识点汇总(下)「附答案」

Kbone原理详解与小程序技术选型

为什么我不再用Vue,改用React?

让Jenkins自动部署你的Vue项目「实践」

20个免费的设计资源 UI套件背景图标CSS框架

Deno将停止使用TypeScript,并公布五项具体理由

前端骨架屏都是如何生成的

Vue原来可以这样写开发效率杠杠的

用vue简单写一个音乐播放组件「附源码」

为什么Vue3.0不再使用defineProperty实现数据监听?

「干货」学会这些Vue小技巧,可以早点下班和女神约会

探索 Vue-Multiselect

细品30张脑图带你从零开始学Vue

Vue后台项目中遇到的技术难点以及解决方案

手把手教你Electron + Vue实战教程(五)

手把手教你Electron + Vue实战教程(四)

手把手教你Electron + Vue实战教程(三)

手把手教你Electron + Vue实战教程(二)

手把手教你Electron + Vue实战教程(一)

收集22种开源Vue模板和主题框架「干货」

如何写出优秀后台管理系统?11个经典模版拿去不谢「干货」

手把手教你实现一个Vue自定义指令懒加载

基于 Vue 和高德地图实现地图组件「实践」

一个由 Vue 作者尤雨溪开发的 web 开发工具—vite

是什么让我爱上了Vue.js

1.1万字深入细品Vue3.0源码响应式系统笔记「上」

1.1万字深入细品Vue3.0源码响应式系统笔记「下」

「实践」Vue 数据更新7 种情况汇总及延伸解决总结

尤大大细说Vue3 的诞生之路「译」

提高10倍打包速度工具Snowpack 2.0正式发布,再也不需要打包器

大厂Code Review总结Vue开发规范经验「值得学习」

Vue3 插件开发详解尝鲜版「值得收藏」

带你五步学会Vue SSR

记一次Vue3.0技术干货分享会

Vue 3.x 如何有惊无险地快速入门「进阶篇」

「干货」微信支付前后端流程整理(Vue+Node)

带你了解 vue-next(Vue 3.0)之 炉火纯青「实践」

「干货」Vue+高德地图实现页面点击绘制多边形及多边形切割拆分

「干货」Vue+Element前端导入导出Excel

「实践」Deno bytes 模块全解析

细品pdf.js实践解决含水印、电子签章问题「Vue篇」

基于vue + element的后台管理系统解决方案

Vue仿蘑菇街商城项目(vue+koa+mongodb)

基于 electron-vue 开发的音乐播放器「实践」

「实践」Vue项目中标配编辑器插件Vue-Quill-Editor

基于 Vue 技术栈的微前端方案实践

消息队列助你成为高薪 Node.js 工程师

Node.js 中的 stream 模块详解

「干货」Deno TCP Echo Server 是怎么运行的?

「干货」了不起的 Deno 实战教程

「干货」通俗易懂的Deno 入门教程

Deno 正式发布,彻底弄明白和 node 的区别

「实践」基于Apify+node+react/vue搭建一个有点意思的爬虫平台

「实践」深入对比 Vue 3.0 Composition API 和 React Hooks

前端网红框架的插件机制全梳理(axios、koa、redux、vuex)

深入Vue 必学高阶组件 HOC「进阶篇」

深入学习Vue的data、computed、watch来实现最精简响应式系统

10个实例小练习,快速入门熟练 Vue3 核心新特性(一)

10个实例小练习,快速入门熟练 Vue3 核心新特性(二)

教你部署搭建一个Vue-cli4+Webpack移动端框架「实践」

2020前端就业Vue框架篇「实践」

详解Vue3中 router 带来了哪些变化?

Vue项目部署及性能优化指导篇「实践」

Vue高性能渲染大数据Tree组件「实践」

尤大大细品VuePress搭建技术网站与个人博客「实践」

10个Vue开发技巧「实践」

是什么导致尤大大选择放弃Webpack?【vite 原理解析】

带你了解 vue-next(Vue 3.0)之 小试牛刀【实践】

带你了解 vue-next(Vue 3.0)之 初入茅庐【实践】

实践Vue 3.0做JSX(TSX)风格的组件开发

一篇文章教你并列比较React.js和Vue.js的语法【实践】

手拉手带你开启Vue3世界的鬼斧神工【实践】

深入浅出通过vue-cli3构建一个SSR应用程序【实践】

怎样为你的 Vue.js 单页应用提速

聊聊昨晚尤雨溪现场针对Vue3.0 Beta版本新特性知识点汇总

【新消息】Vue 3.0 Beta 版本发布,你还学的动么?

Vue真是太好了 壹万多字的Vue知识点 超详细!

Vue + Koa从零打造一个H5页面可视化编辑器——Quark-h5

深入浅出Vue3 跟着尤雨溪学 TypeScript 之 Ref 【实践】

手把手教你深入浅出vue-cli3升级vue-cli4的方法

Vue 3.0 Beta 和React 开发者分别杠上了

手把手教你用vue drag chart 实现一个可以拖动 / 缩放的图表组件

Vue3 尝鲜

总结Vue组件的通信

Vue 开源项目 TOP45

2020 年,Vue 受欢迎程度是否会超过 React?

尤雨溪:Vue 3.0的设计原则

使用vue实现HTML页面生成图片

实现全栈收银系统(Node+Vue)(上)

实现全栈收银系统(Node+Vue)(下)

vue引入原生高德地图

Vue合理配置WebSocket并实现群聊

多年vue项目实战经验汇总

vue之将echart封装为组件

基于 Vue 的两层吸顶踩坑总结

Vue插件总结【前端开发必备】

Vue 开发必须知道的 36 个技巧【近1W字】

构建大型 Vue.js 项目的10条建议

深入理解vue中的slot与slot-scope

手把手教你Vue解析pdf(base64)转图片【实践】

使用vue+node搭建前端异常监控系统

推荐 8 个漂亮的 vue.js 进度条组件

基于Vue实现拖拽升级(九宫格拖拽)

手摸手,带你用vue撸后台 系列二(登录权限篇)

手摸手,带你用vue撸后台 系列三(实战篇)

前端框架用vue还是react?清晰对比两者差异

Vue组件间通信几种方式,你用哪种?【实践】

浅析 React / Vue 跨端渲染原理与实现

10个Vue开发技巧助力成为更好的工程师

手把手教你Vue之父子组件间通信实践讲解【props、$ref 、$emit】

1W字长文+多图,带你了解vue的双向数据绑定源码实现

深入浅出Vue3 的响应式和以前的区别到底在哪里?【实践】

干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)

基于Vue/VueRouter/Vuex/Axios登录路由和接口级拦截原理与实现

手把手教你D3.js 实现数据可视化极速上手到Vue应用

吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【上】

吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【中】

吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【下】

Vue3.0权限管理实现流程【实践】

后台管理系统,前端Vue根据角色动态设置菜单栏和路由

作者:jsoncode

转发链接:https://segmentfault.com/a/1190000023041401

Tags:

最近发表
标签列表