| <template> | 
|   <div | 
|     @dragstart.prevent | 
|     :class="[ | 
|       'el-input-number', | 
|       inputNumberSize ? 'el-input-number--' + inputNumberSize : '', | 
|       { 'is-disabled': inputNumberDisabled }, | 
|       { 'is-without-controls': !controls }, | 
|       { 'is-controls-right': controlsAtRight } | 
|     ]"> | 
|     <span | 
|       class="el-input-number__decrease" | 
|       role="button" | 
|       v-if="controls" | 
|       v-repeat-click="decrease" | 
|       :class="{'is-disabled': minDisabled}" | 
|       @keydown.enter="decrease"> | 
|       <i :class="`el-icon-${controlsAtRight ? 'arrow-down' : 'minus'}`"></i> | 
|     </span> | 
|     <span | 
|       class="el-input-number__increase" | 
|       role="button" | 
|       v-if="controls" | 
|       v-repeat-click="increase" | 
|       :class="{'is-disabled': maxDisabled}" | 
|       @keydown.enter="increase"> | 
|       <i :class="`el-icon-${controlsAtRight ? 'arrow-up' : 'plus'}`"></i> | 
|     </span> | 
|     <el-input | 
|       ref="input" | 
|       :value="displayValue" | 
|       :placeholder="placeholder" | 
|       :disabled="inputNumberDisabled" | 
|       :size="inputNumberSize" | 
|       :max="max" | 
|       :min="min" | 
|       :name="name" | 
|       :label="label" | 
|       @keydown.up.native.prevent="increase" | 
|       @keydown.down.native.prevent="decrease" | 
|       @blur="handleBlur" | 
|       @focus="handleFocus" | 
|       @input="handleInput" | 
|       @change="handleInputChange"> | 
|     </el-input> | 
|   </div> | 
| </template> | 
| <script> | 
|   import ElInput from 'element-ui/packages/input'; | 
|   import Focus from 'element-ui/src/mixins/focus'; | 
|   import RepeatClick from 'element-ui/src/directives/repeat-click'; | 
|   | 
|   export default { | 
|     name: 'ElInputNumber', | 
|     mixins: [Focus('input')], | 
|     inject: { | 
|       elForm: { | 
|         default: '' | 
|       }, | 
|       elFormItem: { | 
|         default: '' | 
|       } | 
|     }, | 
|     directives: { | 
|       repeatClick: RepeatClick | 
|     }, | 
|     components: { | 
|       ElInput | 
|     }, | 
|     props: { | 
|       step: { | 
|         type: Number, | 
|         default: 1 | 
|       }, | 
|       stepStrictly: { | 
|         type: Boolean, | 
|         default: false | 
|       }, | 
|       max: { | 
|         type: Number, | 
|         default: Infinity | 
|       }, | 
|       min: { | 
|         type: Number, | 
|         default: -Infinity | 
|       }, | 
|       value: {}, | 
|       disabled: Boolean, | 
|       size: String, | 
|       controls: { | 
|         type: Boolean, | 
|         default: true | 
|       }, | 
|       controlsPosition: { | 
|         type: String, | 
|         default: '' | 
|       }, | 
|       name: String, | 
|       label: String, | 
|       placeholder: String, | 
|       precision: { | 
|         type: Number, | 
|         validator(val) { | 
|           return val >= 0 && val === parseInt(val, 10); | 
|         } | 
|       } | 
|     }, | 
|     data() { | 
|       return { | 
|         currentValue: 0, | 
|         userInput: null | 
|       }; | 
|     }, | 
|     watch: { | 
|       value: { | 
|         immediate: true, | 
|         handler(value) { | 
|           let newVal = value === undefined ? value : Number(value); | 
|           if (newVal !== undefined) { | 
|             if (isNaN(newVal)) { | 
|               return; | 
|             } | 
|   | 
|             if (this.stepStrictly) { | 
|               const stepPrecision = this.getPrecision(this.step); | 
|               const precisionFactor = Math.pow(10, stepPrecision); | 
|               newVal = Math.round(newVal / this.step) * precisionFactor * this.step / precisionFactor; | 
|             } | 
|   | 
|             if (this.precision !== undefined) { | 
|               newVal = this.toPrecision(newVal, this.precision); | 
|             } | 
|           } | 
|           if (newVal >= this.max) newVal = this.max; | 
|           if (newVal <= this.min) newVal = this.min; | 
|           this.currentValue = newVal; | 
|           this.userInput = null; | 
|           this.$emit('input', newVal); | 
|         } | 
|       } | 
|     }, | 
|     computed: { | 
|       minDisabled() { | 
|         return this._decrease(this.value, this.step) < this.min; | 
|       }, | 
|       maxDisabled() { | 
|         return this._increase(this.value, this.step) > this.max; | 
|       }, | 
|       numPrecision() { | 
|         const { value, step, getPrecision, precision } = this; | 
|         const stepPrecision = getPrecision(step); | 
|         if (precision !== undefined) { | 
|           if (stepPrecision > precision) { | 
|             console.warn('[Element Warn][InputNumber]precision should not be less than the decimal places of step'); | 
|           } | 
|           return precision; | 
|         } else { | 
|           return Math.max(getPrecision(value), stepPrecision); | 
|         } | 
|       }, | 
|       controlsAtRight() { | 
|         return this.controls && this.controlsPosition === 'right'; | 
|       }, | 
|       _elFormItemSize() { | 
|         return (this.elFormItem || {}).elFormItemSize; | 
|       }, | 
|       inputNumberSize() { | 
|         return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size; | 
|       }, | 
|       inputNumberDisabled() { | 
|         return this.disabled || !!(this.elForm || {}).disabled; | 
|       }, | 
|       displayValue() { | 
|         if (this.userInput !== null) { | 
|           return this.userInput; | 
|         } | 
|   | 
|         let currentValue = this.currentValue; | 
|   | 
|         if (typeof currentValue === 'number') { | 
|           if (this.stepStrictly) { | 
|             const stepPrecision = this.getPrecision(this.step); | 
|             const precisionFactor = Math.pow(10, stepPrecision); | 
|             currentValue = Math.round(currentValue / this.step) * precisionFactor * this.step / precisionFactor; | 
|           } | 
|   | 
|           if (this.precision !== undefined) { | 
|             currentValue = currentValue.toFixed(this.precision); | 
|           } | 
|         } | 
|   | 
|         return currentValue; | 
|       } | 
|     }, | 
|     methods: { | 
|       toPrecision(num, precision) { | 
|         if (precision === undefined) precision = this.numPrecision; | 
|         return parseFloat(Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision)); | 
|       }, | 
|       getPrecision(value) { | 
|         if (value === undefined) return 0; | 
|         const valueString = value.toString(); | 
|         const dotPosition = valueString.indexOf('.'); | 
|         let precision = 0; | 
|         if (dotPosition !== -1) { | 
|           precision = valueString.length - dotPosition - 1; | 
|         } | 
|         return precision; | 
|       }, | 
|       _increase(val, step) { | 
|         if (typeof val !== 'number' && val !== undefined) return this.currentValue; | 
|   | 
|         const precisionFactor = Math.pow(10, this.numPrecision); | 
|         // Solve the accuracy problem of JS decimal calculation by converting the value to integer. | 
|         return this.toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor); | 
|       }, | 
|       _decrease(val, step) { | 
|         if (typeof val !== 'number' && val !== undefined) return this.currentValue; | 
|   | 
|         const precisionFactor = Math.pow(10, this.numPrecision); | 
|   | 
|         return this.toPrecision((precisionFactor * val - precisionFactor * step) / precisionFactor); | 
|       }, | 
|       increase() { | 
|         if (this.inputNumberDisabled || this.maxDisabled) return; | 
|         const value = this.value || 0; | 
|         const newVal = this._increase(value, this.step); | 
|         this.setCurrentValue(newVal); | 
|       }, | 
|       decrease() { | 
|         if (this.inputNumberDisabled || this.minDisabled) return; | 
|         const value = this.value || 0; | 
|         const newVal = this._decrease(value, this.step); | 
|         this.setCurrentValue(newVal); | 
|       }, | 
|       handleBlur(event) { | 
|         this.$emit('blur', event); | 
|       }, | 
|       handleFocus(event) { | 
|         this.$emit('focus', event); | 
|       }, | 
|       setCurrentValue(newVal) { | 
|         const oldVal = this.currentValue; | 
|         if (typeof newVal === 'number' && this.precision !== undefined) { | 
|           newVal = this.toPrecision(newVal, this.precision); | 
|         } | 
|         if (newVal >= this.max) newVal = this.max; | 
|         if (newVal <= this.min) newVal = this.min; | 
|         if (oldVal === newVal) return; | 
|         this.userInput = null; | 
|         this.$emit('input', newVal); | 
|         this.$emit('change', newVal, oldVal); | 
|         this.currentValue = newVal; | 
|       }, | 
|       handleInput(value) { | 
|         this.userInput = value; | 
|       }, | 
|       handleInputChange(value) { | 
|         const newVal = value === '' ? undefined : Number(value); | 
|         if (!isNaN(newVal) || value === '') { | 
|           this.setCurrentValue(newVal); | 
|         } | 
|         this.userInput = null; | 
|       }, | 
|       select() { | 
|         this.$refs.input.select(); | 
|       } | 
|     }, | 
|     mounted() { | 
|       let innerInput = this.$refs.input.$refs.input; | 
|       innerInput.setAttribute('role', 'spinbutton'); | 
|       innerInput.setAttribute('aria-valuemax', this.max); | 
|       innerInput.setAttribute('aria-valuemin', this.min); | 
|       innerInput.setAttribute('aria-valuenow', this.currentValue); | 
|       innerInput.setAttribute('aria-disabled', this.inputNumberDisabled); | 
|     }, | 
|     updated() { | 
|       if (!this.$refs || !this.$refs.input) return; | 
|       const innerInput = this.$refs.input.$refs.input; | 
|       innerInput.setAttribute('aria-valuenow', this.currentValue); | 
|     } | 
|   }; | 
| </script> |