1. 把NumericInput.vue文件添加至src/compontents目录下(NumericInput.vue是 自己封装的一个组件) 内容如下

<!-- 
  Originally copied from Vue-Numeric (MIT licensed)
  Using el-input instead to make el-form validation available
  (using input does not work)
-->
<template>
  <el-input
    :placeholder="placeholder"
    @blur="onBlurHandler"
    @input="onInputHandler"
    @focus="onFocusHandler"
    ref="numeric"
    type="tel"
    v-model="amount"
    v-if="!readOnly"
  ></el-input>
  <span
    v-else
    ref="readOnly"
  >{{ amount }}</span>
</template>

<script>
import accounting from 'accounting-js'

export default {
  name: 'NumericInput',

  props: {
    /**
     * Currency symbol.
     */
    currency: {
      type: String,
      default: '',
      required: false
    },

    /**
     * Maximum value allowed.
     */
    max: {
      type: Number,
      default: Number.MAX_SAFE_INTEGER || 9007199254740991,
      required: false,
    },

    /**
     * Minimum value allowed.
     */
    min: {
      type: Number,
      default: Number.MIN_SAFE_INTEGER || -9007199254740991,
      required: false
    },

    /**
     * Enable/Disable minus value.
     */
    minus: {
      type: Boolean,
      default: false,
      required: false
    },

    /**
     * Input placeholder.
     */
    placeholder: {
      type: String,
      default: '',
      required: false
    },

    /**
     * Value when the input is empty
     */
    emptyValue: {
      type: [Number, String],
      default: '',
      required: false
    },

    /**
     * Number of decimals.
     * Decimals symbol are the opposite of separator symbol.
     */
    precision: {
      type: Number,
      default: 0,
      required: false
    },

    /**
     * Thousand separator type.
     * Separator props accept either . or , (default).
     */
    separator: {
      type: String,
      default: ',',
      required: false
    },

    /**
     * Forced thousand separator.
     * Accepts any string.
     */
    thousandSeparator: {
      default: undefined,
      required: false,
      type: String
    },

    /**
     * Forced decimal separator.
     * Accepts any string.
     */
    decimalSeparator: {
      default: undefined,
      required: false,
      type: String
    },

    /**
      * The output type used for v-model.
      * It can either be String or Number (default).
      */
    outputType: {
      required: false,
        type: String,
        default: 'Number'
    },

    /**
     * v-model value.
     */
    value: {
      type: [Number, String],
      default: 0,
      required: true
    },

    /**
     * Hide input and show value in text only.
     */
    readOnly: {
      type: Boolean,
      default: false,
      required: false
    },

    /**
     * Class for the span tag when readOnly props is true.
     */
    readOnlyClass: {
      type: String,
      default: '',
      required: false
    },

    /**
     * Position of currency symbol
     * Symbol position props accept either 'suffix' or 'prefix' (default).
     */
    currencySymbolPosition: {
      type: String,
      default: 'prefix',
      required: false
    }
  },

  data: () => ({
    amount: ''
  }),

  computed: {
    /**
     * Number type of formatted value.
     * @return {Number}
     */
    amountNumber () {
      return this.unformat(this.amount)
    },

    /**
     * Number type of value props.
     * @return {Number}
     */
    valueNumber () {
      return this.unformat(this.value)
    },

    /**
     * Define decimal separator based on separator props.
     * @return {String} '.' or ','
     */
    decimalSeparatorSymbol () {
      if (typeof this.decimalSeparator !== 'undefined') return this.decimalSeparator
      if (this.separator === ',') return '.'
      return ','
    },

    /**
     * Define thousand separator based on separator props.
     * @return {String} '.' or ','
     */
    thousandSeparatorSymbol () {
      if (typeof this.thousandSeparator !== 'undefined') return this.thousandSeparator
      if (this.separator === '.') return '.'
      if (this.separator === 'space') return ' '
      return ','
    },

    /**
     * Define format position for currency symbol and value.
     * @return {String} format
     */
    symbolPosition () {
      if (!this.currency) return '%v'
      return this.currencySymbolPosition === 'suffix' ? '%v %s' : '%s %v'
    }
  },

  watch: {
    /**
     * Watch for value change from other input with same v-model.
     * @param {Number} newValue
     */
    // valueNumber (newValue) {
    //   if (this.$refs.numeric !== document.activeElement) {
    //     this.amount = this.format(newValue)
    //   }
    // },

    /**
     * When readOnly is true, replace the span tag class.
     * @param {Boolean} newValue
     * @param {Boolean} oldValue
     */
    readOnly (newValue, oldValue) {
      if (oldValue === false && newValue === true) {
        this.$nextTick(() => {
          this.$refs.readOnly.className = this.readOnlyClass
        })
      }
    },

    /**
     * Immediately reflect separator changes
     */
    separator () {
      this.process(this.valueNumber)
      this.amount = this.format(this.valueNumber)
    },

    /**
     * Immediately reflect currency changes
     */
    currency () {
      this.process(this.valueNumber)
      this.amount = this.format(this.valueNumber)
    },

    /**
     * Immediately reflect precision changes
     */
    precision () {
      this.process(this.valueNumber)
      this.amount = this.format(this.valueNumber)
    }
  },

  mounted () {
    // Set default value props when placeholder undefined.
    if (!this.placeholder) {
      this.process(this.valueNumber)
      this.amount = this.format(this.valueNumber)

      // In case of delayed props value.
      setTimeout(() => {
        this.process(this.valueNumber)
        this.amount = this.format(this.valueNumber)
      }, 500)
    }

    // Set read-only span element's class
    if (this.readOnly) this.$refs.readOnly.className = this.readOnlyClass;
    (function($) {
        $.fn.inputFilter = function(inputFilter) {
          return this.on("input keydown keyup", function() {
            if (inputFilter(this.value)) {
                this.oldValue = this.value;
                this.oldSelectionStart = this.selectionStart;
                this.oldSelectionEnd = this.selectionEnd;
            } else if (this.hasOwnProperty("oldValue")) {
                this.value = this.oldValue;
                this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd);
            }
          });
        };
      }($));
      $(this.$el.children[0]).inputFilter(function(value) {
          return /^-?\d*[.,]?\d{0,2}$/.test(value);
      });
  },

  methods: {
    /**
     * Handle blur event.
     * @param {Object} e
     */
    onBlurHandler (e) {
      this.$emit('blur', e)
      this.amount = this.format(this.valueNumber)
    },

    /**
     * Handle focus event.
     * @param {Object} e
     */
    onFocusHandler (e) {
      this.$emit('focus', e)
      if (this.valueNumber === 0) {
        this.amount = null
      } else {
        this.amount = accounting.formatMoney(this.valueNumber, {
          symbol: '',
          format: '%v',
          thousand: '',
          decimal: this.decimalSeparatorSymbol,
          precision: Number(this.precision)
        })
      }
    },

    /**
     * Handle input event.
     */
    onInputHandler () {
      this.process(this.amountNumber)
    },

    /**
     * Validate value before update the component.
     * @param {Number} value
     */
    process (value) {
      if (value >= this.max) this.update(this.max)
      if (value <= this.min) this.update(this.min)
      if (value > this.min && value < this.max) this.update(value)
      if (!this.minus && value < 0) this.min >= 0 ? this.update(this.min) : this.update(0)
    },

    /**
     * Update parent component model value.
     * @param {Number} value
     */
    update (value) {
      const fixedValue = accounting.toFixed(value, this.precision)
      const output = this.outputType.toLowerCase() === 'string' ? fixedValue : Number(fixedValue)
      this.$emit('input', output)
    },

    /**
     * Format value using symbol and separator.
     * @param {Number} value
     * @return {String}
     */
    format (value) {
      return accounting.formatMoney(value, {
        symbol: this.currency,
        format: this.symbolPosition,
        precision: Number(this.precision),
        decimal: this.decimalSeparatorSymbol,
        thousand: this.thousandSeparatorSymbol
      })
    },

    /**
     * Remove symbol and separator.
     * @param {Number} value
     * @return {Number}
     */
    unformat (value) {
      const toUnformat = typeof value === 'string' && value === '' ? this.emptyValue : value
      return accounting.unformat(toUnformat, this.decimalSeparatorSymbol)
    },

    init(){
      this.amount = '';
    }
  }
}
</script>

2. 安装依赖 npm install accounting-js   (原因是NumericInput.vue中引入了accounting-js
3. 在xxx.vue中引入并声明组件

 <script>
import numericInput from '../components/NumericInput'
export default {
   components: {
      numericInput
   },
}
</script>

5. 使用方法

1)把<input>替换为<numeric-input></numeric-input>

2)precision属性加上即可

      <numeric-input v-model="price" :precision="2"></numeric-input>

 element-ui 替换<el-input>

 <el-form-item label="价钱" prop="price">
    // 替换之后的 precision 表示保留几位小数
    <numeric-input v-model="ruleForm2.price" :precision="2"></numeric-input>
     // 之前的
    <!-- <el-input v-model="ruleForm2.price"></el-input> -->
 </el-form-item>

 

 

 

Logo

前往低代码交流专区

更多推荐