输入框限制输入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)

 

posted @ 2022-08-25 16:49  日升月恒  阅读(183)  评论(0)    收藏  举报