前端 如何实现一个基于vue2.0的数字输入框指令

前言

前端项目中,输入框是常见的,数字输入框更是常见,我们也许用惯了UI框架或是第三方提供的数字输入框,其实我们内心也想拥有自己的一个数字输入框指令,进可以 (灵活使用),退可 以守(灵活扩展),一切尽在掌握之中,不尽于被动。

需求

最近用到了 数字输入框,需求需要满足:

  1. 设置输入的小数位数
  2. 设置是否支持输入符号
  3. 支持最值(最大值、最小值)
  4. 设置边界超出处理 (1、超出是否替换成最值 2、超出最值无法进一步输入)

首先来看下配置属性有哪些:

  • max - 输入数字最大值
  • min - 输入数字最小值
  • digit - 设置小数位数
  • negative 是否支持输入符号
  • isReplace - 超出最值 是采用替换成对应最值

使用

我们再来看下我们想如何使用:

以下需求为 允许输入符号,允许输入最大值为 99999999,允许输入最小值为 -99999999,小数保留位数 2

javascript 复制代码
<template>
  <div class="hello">
    <input type="text" v-number="{ negative: true, max: 99999999, min: -99999999 , digit:2 }">
  </div>
</template>

源码及示例

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue2 数字输入框指令</title>
</head>
<body>
    <div id="app">
        <div class="item">
            <div class="desc">
                默认状态
                <ul>
                    <li>输入的小数位数digit:2</li>
                    <li>输入数字最大值max:无限制</li>
                    <li>输入数字最小值min:无限制</li>
                    <li>是否允许输入负号negative:不允许</li>
                    <li>超出最值 是否允许采用替换成对应最值isReplace:不允许</li>
                </ul>
            </div>
            <div class="input-wrap"><input type="text" v-number></div>
        </div>
        <div class="item">
            <div class="desc">
                自定义状态1
                <ul>
                    <li>输入的小数位数digit:0</li>
                    <li>输入数字最大值max:999</li>
                    <li>输入数字最小值min:-999</li>
                    <li>是否允许输入负号negative:允许</li>
                    <li>超出最值 是否允许采用替换成对应最值isReplace:允许</li>
                </ul>
            </div>
            <div class="input-wrap"><input type="text" v-number="{digit:0,min:-999,max:999,negative:true,isReplace:true}"></div>
        </div>
        <div class="item">
            <div class="desc">
                自定义状态2
                <ul>
                    <li>输入的小数位数digit:0</li>
                    <li>输入数字最大值max:999</li>
                    <li>输入数字最小值min:0</li>
                    <li>是否允许输入负号negative:不允许</li>
                    <li>超出最值 是否允许采用替换成对应最值isReplace:不允许</li>
                </ul>
            </div>
            <div class="input-wrap"><input type="text" v-number="{digit:0,min:-999,max:999,negative:false,isReplace:false}"></div>
        </div>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    /**
     * 
        数字输入框,需求满足:

        设置输入的小数位数
        设置是否支持输入fushu负号
        支持最值(最大值、最小值)
        设置边界超出处理 (1、超出是否替换成最值 2、超出最值无法进一步输入)

        配置属性:

        max - 输入数字最大值
        min - 输入数字最小值
        digit - 设置小数位数
        negative 是否支持输入符号
        isReplace - 超出最值 是采用替换成对应最值
     * 
     * 
     */
    new Vue({
        el:'#app',
        data:{
        },
        directives:{
            number: {
                bind (el, binding) {
                     // input 输入框 元素 兼容
                    if (el.tagName !== 'INPUT') el = el.querySelector('input')
                    el.old = '' // 记录旧值
                    el.handler = function () {
                        if(el.lock)return
                        const { max, digit, min, negative, isReplace } = binding.value || {}
                         // 小数位数正则
                        const digitReg = new RegExp(`^\\d*(\\.?\\d{0,${digit === undefined ? 2 : digit}})`, 'g')
                        // 最新值是否是负数
                        const isNegative  = el.value.includes('-')
                        // 最新的输入框内的值
                        const val = el.value
                         // 其它非法值进行处理
                        let newValue = el.value.replace(/[^\d.]/, '')
                            .replace(/,/g, '')
                            .replace(/^0+(\d)/, '$1') // 第一位0开头,0后面为数字,则过滤掉,取后面的数字
                            .replace(/^\./, '0.') // 如果输入的第一位为小数点,则替换成 0. 实现自动补全
                            .match(digitReg)[0] || '' // 最终匹配得到结果 以数字开头,只有一个小数点,而且小数点后面只能有0到2位小数
                        // 负数 并且 允许输入符号
                        if (isNegative && negative) {
                            if (val.match(/-/g)?.length === 2) newValue = val.split('-').join('')
                            newValue = '-' + newValue
                        }

                        if (newValue.slice(-1) === '.' && digit === 0) {
                            newValue = Number(newValue)
                        }
                        // 输入值超出最值 , isReplace 为 true 就 替换,否则就还原上次输入的值
                        if (max !== undefined && newValue > max) {
                            newValue = isReplace ? max : String(newValue).slice(0, -1)
                        } else if (min !== undefined && newValue < min) {
                            newValue = isReplace ? min : String(newValue).slice(0, -1)
                        } else { // 输入值未超出最值
                            el.old = newValue
                        }
                        // 判断是否需要更新,避免进入死循环\
                        if (newValue !== el.value) {
                            el.value = newValue
                            el.dispatchEvent(new Event('input')) // 通知v-model更新
                        }
                    }
                    el.blurHander = function (e) {
                        const { digit } = binding.value || {}
                        const digitReg = new RegExp(`^\\d*(\\.?\\d{0,${digit === undefined ? 2 : digit}})`, 'g')
                        if (el.value === '-') {
                            el.value = ''
                            el.dispatchEvent(new Event('input')) // 通知v-model更新
                            return
                        }
                        let newValue = el.value.replace(/[^\d.]/, '')
                            .replace(/^0+(\d)/, '$1') // 第一位0开头,0后面为数字,则过滤掉,取后面的数字
                            .replace(/^\./, '0.') // 如果输入的第一位为小数点,则替换成 0. 实现自动补全
                            .match(digitReg)[0] || '' // 最终匹配得到结果 以数字开头,只有一个小数点,而且小数点后面只能有0到2位小数
                        if (newValue.slice(-1) === '.' && digit !== 0 && digit !== undefined) {
                            newValue = Number(newValue)
                            el.value = newValue
                            el.dispatchEvent(new Event('input')) // 通知v-model更新
                        }
                    }
                    el.compositionstart = function(e){
                        el.lock = true
                        e.preventDefault();
                        e.stopPropagation();
                    }
                    el.compositionend = function(e){
                        el.lock = false
                        el.value = parseFloat(el.value)
                        el.dispatchEvent(new Event('input'))
                        e.preventDefault();
                        e.stopPropagation();
                    }
                    el.addEventListener('input', el.handler)
                    el.addEventListener('blur', el.blurHander)
                    el.addEventListener('compositionstart', el.compositionstart)
                    el.addEventListener('compositionend', el.compositionend)
                },
                unbind (el) {
                    el.removeEventListener('input', el.handler)
                    el.removeEventListener('blur', el.blurHander)
                    el.removeEventListener('compositionstart', el.compositionstart)
                    el.removeEventListener('compositionend', el.compositionend)
                }
            }
        }
    })
</script>
</html>

指令的大致实现过程就监听输入,根据输入值进行各种场景的处理,compositionstartcompositionend来处理 输入中文的特殊场景,经过自测和项目中的使用,基本满足需求,如果不满足你的需求,可以复制下来进行扩展,实现思路 可以用于React、Vue3等其他,万变不离其宗。

结语

我们项目中出现该场景的情况还是比较多的,希望对大家有用,如有疑问,欢迎留言,有更好的 idea,欢迎一起讨论,感谢

24年往期文章:
前端 同时多次调用同个接口,如何只触发一次而不是触发多次
前端 页面不同组件内调用相同接口导致重复调用,如何只触发一次而不是触发多次
前端 保存前繁琐的静态校验,如何化繁为简

相关推荐
tedcloud1234 小时前
UI-TARS-desktop部署教程:构建AI桌面自动化系统
服务器·前端·人工智能·ui·自动化·github
UXbot7 小时前
AI原型设计工具如何支持团队协作与快速迭代
前端·交互·个人开发·ai编程·原型模式
ZC跨境爬虫8 小时前
跟着MDN学HTML_day_48:(Node接口)
前端·javascript·ui·html·音视频
PieroPc9 小时前
CAMWATCH — 局域网摄像头监控系统 Fastapi + html
前端·python·html·fastapi·监控
巴巴博一10 小时前
2026 最新:Trae / Cursor 一键接入 taste-skill 完整教程(让 AI 前端告别“AI 味”)
前端·ai·ai编程
kyriewen10 小时前
半夜三点线上崩了,AI替我背了锅——用AI排错,五分钟定位三年老bug
前端·javascript·ai编程
kyriewen11 小时前
我让 AI 当了 24 小时全年无休的“毒舌考官”
前端·ci/cd·ai编程
hexu_blog11 小时前
vue+java实现图片批量压缩
java·前端·vue.js
IT_陈寒11 小时前
为什么你应该学习JavaScript?
前端·人工智能·后端