Browse Source

InputNumber: add precision attribute (#11281)

hetech 7 years ago
parent
commit
bcfb1d3c71

+ 26 - 0
examples/docs/en-US/input-number.md

@@ -99,6 +99,31 @@ Allows you to define incremental steps.
 ```
 :::
 
+### Precision
+
+:::demo Add `precision` attribute to set the precision of input value.
+
+```html
+<template>
+  <el-input-number v-model="num9" :precision="2" :step="0.1" :max="10"></el-input-number>
+</template>
+<script>
+  export default {
+    data() {
+      return {
+        num9: 1
+      }
+    }
+  };
+</script>
+```
+
+:::
+
+:::tip
+The value of `precision` must be a positive integer and should not be less than the decimal places of `step`.
+:::
+
 ### Size
 
 Use attribute `size` to set additional sizes with `medium`, `small` or `mini`.
@@ -159,6 +184,7 @@ Use attribute `size` to set additional sizes with `medium`, `small` or `mini`.
 |min | the minimum allowed value | number | — | `-Infinity` |
 |max | the maximum allowed value | number | — | `Infinity` |
 |step | incremental step | number | — | 1 |
+|precision | precision of input value | number | — | — |
 |size | size of the component | string | large/small| — |
 |disabled| whether the component is disabled | boolean | — | false |
 |controls| whether to enable the control buttons | boolean | — | true |

+ 26 - 0
examples/docs/es/input-number.md

@@ -99,6 +99,31 @@ Le permite definir el nivel de incremento de los saltos.
 ```
 :::
 
+### Precision
+
+:::demo Add `precision` attribute to set the precision of input value.
+
+```html
+<template>
+  <el-input-number v-model="num9" :precision="2" :step="0.1" :max="10"></el-input-number>
+</template>
+<script>
+  export default {
+    data() {
+      return {
+        num9: 1
+      }
+    }
+  };
+</script>
+```
+
+:::
+
+:::tip
+The value of `precision` must be a positive integer and should not be less than the decimal places of `step`.
+:::
+
 ### Tamaño
 
 Utilice el atributo `size` para establecer tamaños adicionales con `medium`, `small` o `mini`.
@@ -160,6 +185,7 @@ Utilice el atributo `size` para establecer tamaños adicionales con `medium`, `s
 | min               | el valor mínimo permitido                | number  | —                 | `-Infinity`  |
 | max               | el valor maximo permitido                | number  | —                 | `Infinity`  |
 | step              | incremento (salto)                       | number  | —                 | 1           |
+| precision         | precision of input value                 | number  | —                 | —           |
 | size              | tamaño del componente                    | string  | large/small       | —           |
 | disabled          | si el componente esta deshabilitado      | boolean | —                 | false       |
 | controls          | si se activan los botones de control     | boolean | —                 | true        |

+ 28 - 1
examples/docs/zh-CN/input-number.md

@@ -9,7 +9,8 @@
         num5: 1,
         num6: 1,
         num7: 1,
-        num8: 1
+        num8: 1,
+        num9: 1
       }
     },
     methods: {
@@ -97,6 +98,31 @@
 ```
 :::
 
+### 精度
+
+:::demo 设置 `precision` 属性可以控制数值精度,接收一个 `Number`。
+
+```html
+<template>
+  <el-input-number v-model="num9" :precision="2" :step="0.1" :max="10"></el-input-number>
+</template>
+<script>
+  export default {
+    data() {
+      return {
+        num9: 1
+      }
+    }
+  };
+</script>
+```
+
+:::
+
+:::tip
+`precision` 的值必须是一个正整数,并且不能小于 `step` 的小数位数。
+:::
+
 ### 尺寸
 
 额外提供了 `medium`、`small`、`mini` 三种尺寸的数字输入框
@@ -156,6 +182,7 @@
 | min      | 设置计数器允许的最小值 | number | — | -Infinity |
 | max      | 设置计数器允许的最大值 | number | — | Infinity |
 | step     | 计数器步长           | number   | — | 1 |
+| precision| 数值精度             | number   | — | — |
 | size     | 计数器尺寸           | string   | large, small | — |
 | disabled | 是否禁用计数器        | boolean | — | false |
 | controls | 是否使用控制按钮        | boolean | — | true |

+ 43 - 11
packages/input-number/src/input-number.vue

@@ -28,7 +28,7 @@
     </span>
     <el-input
       ref="input"
-      :value="currentValue"
+      :value="currentInputValue"
       :disabled="inputNumberDisabled"
       :size="inputNumberSize"
       :max="max"
@@ -96,7 +96,13 @@
         default: ''
       },
       name: String,
-      label: String
+      label: String,
+      precision: {
+        type: Number,
+        validator(val) {
+          return val >= 0 && val === parseInt(val, 10);
+        }
+      }
     },
     data() {
       return {
@@ -108,7 +114,14 @@
         immediate: true,
         handler(value) {
           let newVal = value === undefined ? value : Number(value);
-          if (newVal !== undefined && isNaN(newVal)) return;
+          if (newVal !== undefined) {
+            if (isNaN(newVal)) {
+              return;
+            }
+            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;
@@ -123,9 +136,17 @@
       maxDisabled() {
         return this._increase(this.value, this.step) > this.max;
       },
-      precision() {
-        const { value, step, getPrecision } = this;
-        return Math.max(getPrecision(value), getPrecision(step));
+      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.controlsPosition === 'right';
@@ -138,11 +159,19 @@
       },
       inputNumberDisabled() {
         return this.disabled || (this.elForm || {}).disabled;
+      },
+      currentInputValue() {
+        const currentValue = this.currentValue;
+        if (typeof currentValue === 'number' && this.precision !== undefined) {
+          return currentValue.toFixed(this.precision);
+        } else {
+          return currentValue;
+        }
       }
     },
     methods: {
       toPrecision(num, precision) {
-        if (precision === undefined) precision = this.precision;
+        if (precision === undefined) precision = this.numPrecision;
         return parseFloat(parseFloat(Number(num).toFixed(precision)));
       },
       getPrecision(value) {
@@ -158,14 +187,14 @@
       _increase(val, step) {
         if (typeof val !== 'number' && val !== undefined) return this.currentValue;
 
-        const precisionFactor = Math.pow(10, this.precision);
+        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.precision);
+        const precisionFactor = Math.pow(10, this.numPrecision);
 
         return this.toPrecision((precisionFactor * val - precisionFactor * step) / precisionFactor);
       },
@@ -183,17 +212,20 @@
       },
       handleBlur(event) {
         this.$emit('blur', event);
-        this.$refs.input.setCurrentValue(this.currentValue);
+        this.$refs.input.setCurrentValue(this.currentInputValue);
       },
       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) {
-          this.$refs.input.setCurrentValue(this.currentValue);
+          this.$refs.input.setCurrentValue(this.currentInputValue);
           return;
         }
         this.$emit('input', newVal);

+ 45 - 0
test/unit/specs/input-number.spec.js

@@ -220,6 +220,51 @@ describe('InputNumber', () => {
       done();
     });
   });
+  describe('precision', () => {
+    it('precision is 2', () => {
+      vm = createVue({
+        template: `
+          <el-input-number v-model="value" :max="8" :precision="2">
+          </el-input-number>
+        `,
+        data() {
+          return {
+            value: 6.999
+          };
+        }
+      }, true);
+      expect(vm.value === 7);
+      expect(vm.$el.querySelector('input').value).to.be.equal('7.00');
+    });
+
+    it('precision greater than the precision of step', done => {
+      vm = createVue({
+        template: `
+          <el-input-number v-model="value" :max="8" :precision="0" :step="0.1">
+          </el-input-number>
+        `,
+        data() {
+          return {
+            value: 6.999
+          };
+        }
+      }, true);
+      const input = vm.$el.querySelector('input');
+      const btnIncrease = vm.$el.querySelector('.el-input-number__increase');
+
+      expect(vm.value === 7);
+      expect(input.value).to.be.equal('7');
+
+      triggerEvent(btnIncrease, 'mousedown');
+      triggerClick(document, 'mouseup');
+
+      vm.$nextTick(_ => {
+        expect(vm.value).to.be.equal(7);
+        expect(input.value).to.be.equal('7');
+        done();
+      });
+    });
+  });
   it('controls', () => {
     vm = createVue({
       template: `

+ 3 - 0
types/input-number.d.ts

@@ -34,6 +34,9 @@ export declare class ElInputNumber extends ElementUIComponent {
   /** Same as name in native input */
   name: string
 
+  /** Precision of input value */
+  precision: Number
+
   /**
    * Focus the Input component
    */