输入框限制输入n位小数
<template> <el-input :model-value="displayValue" placeholder="请输入" :max="maxNumber" :min="minNumber" @input="handleInput" @change="handleInputChange" clearable /> </template> <script setup> // vue3 组合式API自定义限制小数位数字输入框组件 // <InputNumLimit v-model="num" :precision="2" /> import { computed, reactive, watch } from "vue"; const max = 9999999999; const min = -9999999999; const props = defineProps({ modelValue: { type: [Number, null, String], }, maxNumber: { type: Number, default: max, }, minNumber: { type: Number, default: min, }, isInterval: { type: Boolean, default: false, // 是否是区间范围 }, precision: { type: Number, default: 2, // 小数点位数 }, }); const emit = defineEmits(["update:modelValue"]); const data = reactive({ currentValue: props.modelValue, userInput: null, }); const displayValue = computed(() => { if (data.userInput !== null) { return data.userInput; } return data.currentValue; }); watch( () => props.modelValue, (value, oldValue) => { // console.log("value=", value, oldValue); let newVal = [null, undefined, ""].includes(value) ? value : Number(value); if (![null, undefined, ""].includes(newVal)) { if (isNaN(newVal)) { return; } newVal = toPrecision(newVal); } // if (newVal >= props.maxNumber) newVal = props.maxNumber; // if (newVal <= props.minNumber) newVal = props.minNumber; if (data.userInput === null && newVal !== oldValue) { data.currentValue = newVal; // 初始赋值 emit("update:modelValue", newVal); } }, { immediate: true } ); const toPrecision = (num) => { const { precision } = props; return parseFloat( Math.trunc((num * Math.pow(10, precision + 1)) / 10) / Math.pow(10, precision) ); }; function setCurrentValue(newVal) { const oldVal = data.currentValue; if (props.isInterval) { if (typeof newVal === "number") { newVal = toPrecision(newVal); } if (newVal >= props.maxNumber) newVal = props.maxNumber; if (newVal <= props.minNumber) newVal = props.minNumber; } if (oldVal === newVal) return; data.userInput = null; emit("update:modelValue", newVal); data.currentValue = newVal; } function handleInput(val) { if (isNaN(val) && val !== "-") { val = val.replace(/\,|\,/g, ""); // 复制时过滤千分符 if (isNaN(val)) { val = val.slice(0, -1); val = val.replace(/[^\-\d^\.]+/g, ""); } } if (val) { const valArr = val.split("."); if (props.precision === 0) { val = valArr[0]; } else if (valArr[1] && valArr[1].length > props.precision) { val = valArr[0] + "." + valArr[1].substr(0, props.precision); } } // 最大、最小值限制 - 区间范围数值不能在此限制,如查询时的金额范围 if (!props.isInterval) { if (val >= props.maxNumber) val = props.maxNumber; if (val <= props.minNumber) val = props.minNumber; } else { if (val >= max) val = max; if (val <= min) val = min; } data.userInput = val; } function handleInputChange(val) { const newVal = val === "" ? undefined : Number(val); if (!isNaN(newVal) || val === "") { setCurrentValue(newVal); } data.userInput = null; } </script>
<template> <div> <el-input ref="inputRef" :value="displayValue" :placeholder="placeholder" :max="maxNum" :min="minNum" @input="handleInput" @change="handleInputChange" clearable /> </div> </template> <script> // vue3 组合式API自定义输入框组件如何在父组件设置v-model // <CustomInputNumber v-model="inpNum" /> export default { // name: "CustomInputNumber", // 自定义数字输入框 props: { value: {}, // 表单输入元素上创建双向数据绑定,使用 emit 来触发事件更新这个值 precision: { type: Number, default: () => undefined, // 小数点位 }, }, data() { return { currentValue: null, userInput: null, maxNum: 1000000, minNum: -9999, placeholder: "请输入", }; }, computed: { displayValue() { if (this.userInput !== null) { return this.userInput; } return this.currentValue; }, }, watch: { value: { immediate: true, handler(value) { let newVal = [null, undefined].includes(value) ? value : Number(value); if (![null, undefined].includes(newVal)) { if (isNaN(newVal)) { return; } if (this.precision !== undefined) { newVal = this.toPrecision(newVal, this.precision); } } // if (newVal >= this.maxNum) newVal = this.maxNum; // if (newVal <= this.minNum) newVal = this.minNum; this.currentValue = newVal; // 初始赋值 this.userInput = null; this.$emit("input", newVal); }, }, }, methods: { toPrecision(num, precision = 2) { return parseFloat( Math.trunc((num * Math.pow(10, precision + 1)) / 10) / Math.pow(10, precision) ); }, setCurrentValue(newVal) { const oldVal = this.currentValue; if (typeof newVal === "number" && this.precision !== undefined) { newVal = this.toPrecision(newVal, this.precision); } if (newVal >= this.maxNum) newVal = this.maxNum; if (newVal <= this.minNum) newVal = this.minNum; if (oldVal === newVal) return; this.userInput = null; this.$emit("input", newVal); // this.$emit("change", newVal, oldVal); this.currentValue = newVal; }, handleInput(val) { if (isNaN(val) && val !== "-") { val = val.replace(/\,|\,/g, ""); // 复制时过滤千分符 if (isNaN(val)) { val = val.slice(0, -1); val = val.replace(/[^\-\d^\.]+/g, ""); } } if (this.precision !== undefined && val) { const valArr = val.split("."); if (this.precision === 0) { val = valArr[0]; } else if (valArr[1] && valArr[1].length > this.precision) { val = valArr[0] + "." + valArr[1].substr(0, this.precision); } } // 最大、最小值限制 if (val >= this.maxNum) val = this.maxNum; if (val <= this.minNum) val = this.minNum; this.userInput = val; }, handleInputChange(val) { const newVal = val === "" ? undefined : Number(val); if (!isNaN(newVal) || val === "") { this.setCurrentValue(newVal); } this.userInput = null; }, }, mounted() { // let innerInput = this.$refs.inputRef.$refs.input; // innerInput.setAttribute("role", "spinbutton"); // innerInput.setAttribute("aria-valuemax", this.maxNum); // innerInput.setAttribute("aria-valuemin", this.minNum); // innerInput.setAttribute("aria-valuenow", this.currentValue); // 输入时限制 }, updated() { // if (!this.$refs || !this.$refs.inputRef) return; // const innerInput = this.$refs.inputRef.$refs.input; // innerInput.setAttribute("aria-valuenow", this.currentValue); }, }; </script>
/** * instructions.js * * 配合 el-input-number 限制输入框只能输入n位小数 * <el-input-number v-model="num" :controls="false" v-input-number-limit="2" * @input.native="inputNumEvt" @change="handleNumberChange" :min="1" :max="10" :precision="2"></el-input-number> inputNumEvt(nVal) { // 如果有最大、最小数值限制,InputNumber计数器 实时计算需要配合 @change 使用 // console.log('nVal==', nVal.target.value) } handleNumberChange(value) { console.log(value); } */ export const inputNumberLimt = { bind(el, binding = {}) { const { value } = binding if (value === null || value === undefined) { return } const target = el instanceof HTMLInputElement ? el : el.querySelector("input"); target.addEventListener("input", e => { let val = e.target.value ? e.target.value.toString() : '' if (Number(val) != val && val !== '-') { val = val.slice(0,-1) val = val.replace(/[^\d^\.]+/g, '') } else { const decLen = typeof value === 'number' ? value : value.value // 小数点 - 长度限制 if (typeof decLen !== 'undefined' && val) { const valArr = val.split('.') if (decLen === 0) { val = valArr[0] } else if (valArr[1] && valArr[1].length > decLen) { val = valArr[0] + '.' + valArr[1].substr(0, decLen) } } } const max_num = value.maxNumber ? typeConversion(value.maxNumber, 'number') : 1000000000000 // const max_num = 1000000000000 // 最大值限制 if(val > max_num) { val = max_num } e.target.value = val; // 不能在此转Number类型,否则无法输入小数点 }) } }
/** * main.js 注入自定义指令 */ import * as instructions from '@/utils/instructions' // 小数点限制指令 Vue.directive('inputNumberLimit', instructions.inputNumberLimt)