diff --git a/src/stepper/README.md b/src/stepper/README.md
index 237157048..edba40b68 100644
--- a/src/stepper/README.md
+++ b/src/stepper/README.md
@@ -51,6 +51,18 @@ export default {
```
+### Decimal Length
+
+```html
+
+```
+
+### Custom Size
+
+```html
+
+```
+
### Async Change
```html
@@ -82,16 +94,6 @@ export default {
}
```
-### Custom Size
-
-```html
-
-```
-
## API
### Props
@@ -110,6 +112,7 @@ export default {
| button-size | Button size | *string \| number* | `28px` | 2.0.5 |
| show-plus | Whether to show plus button | *boolean* | `true` | 2.1.2 |
| show-minus | Whether to show minus button | *boolean* | `true` | 2.1.2 |
+| decimal-length | Decimal length | *number* | - | 2.2.1 |
### Events
diff --git a/src/stepper/README.zh-CN.md b/src/stepper/README.zh-CN.md
index 9542d9cde..f6ba0e3a4 100644
--- a/src/stepper/README.zh-CN.md
+++ b/src/stepper/README.zh-CN.md
@@ -65,6 +65,22 @@ export default {
```
+### 固定小数位数
+
+通过设置`decimal-length`属性可以保留固定的小数位数
+
+```html
+
+```
+
+### 自定义大小
+
+通过`input-width`属性设置输入框宽度,通过`button-size`属性设置按钮大小和输入框高度
+
+```html
+
+```
+
### 异步变更
如果需要异步地修改输入框的值,可以设置`async-change`属性,并在`change`事件中手动修改`value`
@@ -100,25 +116,13 @@ export default {
}
```
-### 自定义大小
-
-通过`input-width`属性设置输入框宽度,通过`button-size`属性设置按钮大小和输入框高度
-
-```html
-
-```
-
## API
### Props
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|------|------|------|------|------|
-| v-model | 当前输入值 | *string \| number* | 最小值 | - |
+| v-model | 当前输入值 | *string \| number* | min | - |
| min | 最小值 | *string \| number* | `1` | - |
| max | 最大值 | *string \| number* | - | - |
| step | 步长 | *string \| number* | `1` | - |
@@ -127,9 +131,10 @@ export default {
| disable-input | 是否禁用输入框 | *boolean* | `false` | - |
| async-change | 是否开启异步变更,开启后需要手动控制输入值 | *boolean* | `false` | - |
| input-width | 输入框宽度,默认单位为`px` | *string \| number* | `32px` | - |
-| button-size | 按钮大小,默认单位为`px`,输入框高度会和按钮大小保持一致 | *string \| number* | `28px` | 2.0.5 |
+| button-size | 按钮大小以及输入框高度,默认单位为`px` | *string \| number* | `28px` | 2.0.5 |
| show-plus | 是否显示增加按钮 | *boolean* | `true` | 2.1.2 |
| show-minus | 是否显示减少按钮 | *boolean* | `true` | 2.1.2 |
+| decimal-length | 固定显示的小数位数 | *number* | - | 2.2.1 |
### Events
diff --git a/src/stepper/demo/index.vue b/src/stepper/demo/index.vue
index 0fdb0c88e..066c4b2f9 100644
--- a/src/stepper/demo/index.vue
+++ b/src/stepper/demo/index.vue
@@ -1,73 +1,35 @@
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
@@ -80,14 +42,16 @@ export default {
range: '限制输入范围',
integer: '限制输入整数',
asyncChange: '异步变更',
- customSize: '自定义大小'
+ customSize: '自定义大小',
+ decimalLength: '固定小数位数'
},
'en-US': {
step: 'Step',
range: 'Range',
integer: 'Integer',
asyncChange: 'Async Change',
- customSize: 'Custom Size'
+ customSize: 'Custom Size',
+ decimalLength: 'Decimal Length'
}
},
@@ -99,7 +63,8 @@ export default {
stepper4: 1,
stepper5: 1,
stepper6: 1,
- stepper7: 1
+ stepper7: 1,
+ stepper8: 1
};
},
diff --git a/src/stepper/index.js b/src/stepper/index.js
index 3c4dad747..14db7b9d4 100644
--- a/src/stepper/index.js
+++ b/src/stepper/index.js
@@ -7,6 +7,10 @@ const [createComponent, bem] = createNamespace('stepper');
const LONG_PRESS_START_TIME = 600;
const LONG_PRESS_INTERVAL = 200;
+function equal(value1, value2) {
+ return String(value1) === String(value2);
+}
+
export default createComponent({
props: {
value: null,
@@ -16,6 +20,7 @@ export default createComponent({
buttonSize: [Number, String],
asyncChange: Boolean,
disableInput: Boolean,
+ decimalLength: Number,
min: {
type: [Number, String],
default: 1
@@ -43,8 +48,10 @@ export default createComponent({
},
data() {
- const value = this.range(isDef(this.value) ? this.value : this.defaultValue);
- if (value !== +this.value) {
+ const defaultValue = isDef(this.value) ? this.value : this.defaultValue;
+ const value = this.format(defaultValue);
+
+ if (!equal(value, this.value)) {
this.$emit('input', value);
}
@@ -90,7 +97,7 @@ export default createComponent({
watch: {
value(val) {
- if (val !== this.currentValue) {
+ if (!equal(val, this.currentValue)) {
this.currentValue = this.format(val);
}
},
@@ -103,29 +110,54 @@ export default createComponent({
methods: {
// filter illegal characters
- format(value) {
+ filter(value) {
value = String(value).replace(/[^0-9.-]/g, '');
- return value === '' ? 0 : this.integer ? Math.floor(value) : +value;
+
+ if (this.integer && value.indexOf('.') !== -1) {
+ value = value.split('.')[0];
+ }
+
+ return value;
},
- // limit value range
- range(value) {
- return Math.max(Math.min(this.max, this.format(value)), this.min);
+ format(value) {
+ value = this.filter(value);
+
+ // format range
+ value = value === '' ? 0 : +value;
+ value = Math.max(Math.min(this.max, value), this.min);
+
+ // format decimal
+ if (isDef(this.decimalLength)) {
+ value = value.toFixed(this.decimalLength);
+ }
+
+ return value;
},
onInput(event) {
const { value } = event.target;
- const formatted = this.format(value);
+ // allow input to be empty
+ if (value === '') {
+ return;
+ }
+
+ const formatted = this.filter(value);
+
+ if (!equal(value, formatted)) {
+ event.target.value = formatted;
+ }
+
+ this.emitChange(formatted);
+ },
+
+ emitChange(value) {
if (this.asyncChange) {
- event.target.value = this.currentValue;
- this.$emit('input', formatted);
- this.$emit('change', formatted);
+ this.$emit('input', value);
+ this.$emit('change', value);
} else {
- if (+value !== formatted) {
- event.target.value = formatted;
- }
- this.currentValue = formatted;
+ this.currentValue = value;
}
},
@@ -138,15 +170,17 @@ export default createComponent({
}
const diff = type === 'minus' ? -this.step : +this.step;
- const value = Math.round((this.currentValue + diff) * 100) / 100;
- if (this.asyncChange) {
- this.$emit('input', value);
- this.$emit('change', value);
- } else {
- this.currentValue = this.range(value);
+ let value = +this.currentValue + diff;
+
+ // avoid float number
+ if (!isDef(this.decimalLength)) {
+ value = Math.round(value * 100) / 100;
}
+ value = this.format(value);
+
+ this.emitChange(value);
this.$emit(type);
},
@@ -155,14 +189,11 @@ export default createComponent({
},
onBlur(event) {
- this.currentValue = this.range(this.currentValue);
+ const value = this.format(event.target.value);
+ event.target.value = value;
+ this.currentValue = value;
this.$emit('blur', event);
- // fix edge case when input is empty and min is 0
- if (this.currentValue === 0) {
- event.target.value = this.currentValue;
- }
-
resetScroll();
},
diff --git a/src/stepper/test/__snapshots__/demo.spec.js.snap b/src/stepper/test/__snapshots__/demo.spec.js.snap
index d37bc1995..60d21c169 100644
--- a/src/stepper/test/__snapshots__/demo.spec.js.snap
+++ b/src/stepper/test/__snapshots__/demo.spec.js.snap
@@ -33,9 +33,9 @@ exports[`renders demo correctly 1`] = `
+
`;
diff --git a/src/stepper/test/__snapshots__/index.spec.js.snap b/src/stepper/test/__snapshots__/index.spec.js.snap
index 79cac6a84..906faebe1 100644
--- a/src/stepper/test/__snapshots__/index.spec.js.snap
+++ b/src/stepper/test/__snapshots__/index.spec.js.snap
@@ -4,6 +4,6 @@ exports[`disable stepper input 1`] = ``;
-exports[`input width 1`] = `
`;
+exports[`input-width prop 1`] = `
`;
exports[`show-plus & show-minus props 1`] = `
`;
diff --git a/src/stepper/test/index.spec.js b/src/stepper/test/index.spec.js
index 38c8d4a17..e6080371f 100644
--- a/src/stepper/test/index.spec.js
+++ b/src/stepper/test/index.spec.js
@@ -60,35 +60,45 @@ test('long press', async () => {
expect(wrapper.emitted('input')).toEqual([[2], [3], [4]]);
});
-test('correct value when value is not correct', () => {
+test('filter value during user input', () => {
const wrapper = mount(Stepper, {
propsData: {
- value: 50,
- max: 30,
- min: 10
+ value: 1
}
});
- const input = wrapper.find('input');
- input.element.value = 1;
- input.trigger('input');
- input.element.value = 'abc';
- input.trigger('input');
- wrapper.setData({ value: 'abc' });
- input.trigger('input');
- wrapper.setData({ value: '' });
- input.trigger('input');
- wrapper.setData({ value: 40 });
- input.trigger('input');
- wrapper.setData({ value: 30 });
+ const input = wrapper.find('.van-stepper__input');
+ input.element.value = '';
input.trigger('input');
+ expect(wrapper.emitted('input')).toBeFalsy();
- expect(wrapper.emitted('input')).toEqual([[30], [1], [0], [40], [30]]);
+ input.element.value = 'a';
+ input.trigger('input');
+ expect(input.element.value).toEqual('');
+ expect(wrapper.emitted('input')).toBeFalsy();
+
+ input.element.value = '2';
+ input.trigger('input');
+ expect(input.element.value).toEqual('2');
+ expect(wrapper.emitted('input')[0][0]).toEqual('2');
+});
+
+test('shoud watch value and format it', () => {
+ const wrapper = mount(Stepper, {
+ propsData: {
+ value: 1,
+ max: 5
+ }
+ });
+
+ wrapper.setData({ value: 10 });
+ expect(wrapper.emitted('input')[0][0]).toEqual(5);
});
test('only allow interger', () => {
const wrapper = mount(Stepper, {
propsData: {
+ value: 1,
integer: true
}
});
@@ -98,7 +108,8 @@ test('only allow interger', () => {
input.trigger('input');
input.trigger('blur');
- expect(wrapper.emitted('input')).toEqual([[1]]);
+ expect(wrapper.emitted('input')[0][0]).toEqual('1');
+ expect(wrapper.emitted('input')[1][0]).toEqual(1);
});
test('stepper focus', () => {
@@ -121,16 +132,15 @@ test('stepper blur', () => {
});
const input = wrapper.find('input');
- input.trigger('blur');
input.element.value = '';
input.trigger('input');
input.trigger('blur');
- expect(wrapper.emitted('input')).toEqual([[0], [3]]);
+ expect(wrapper.emitted('input')[0][0]).toEqual(3);
expect(wrapper.emitted('blur')).toBeTruthy();
});
-test('input width', () => {
+test('input-width prop', () => {
const wrapper = mount(Stepper, {
propsData: {
inputWidth: '10rem'
@@ -139,7 +149,7 @@ test('input width', () => {
expect(wrapper).toMatchSnapshot();
});
-test('async change', () => {
+test('async-change prop', () => {
const wrapper = mount(Stepper, {
propsData: {
value: 1,
@@ -157,8 +167,8 @@ test('async change', () => {
input.element.value = '3';
input.trigger('input');
- expect(wrapper.emitted('input')[1][0]).toEqual(3);
- expect(wrapper.emitted('change')[1][0]).toEqual(3);
+ expect(wrapper.emitted('input')[1][0]).toEqual('3');
+ expect(wrapper.emitted('change')[1][0]).toEqual('3');
});
test('min value is 0', () => {
@@ -174,7 +184,7 @@ test('min value is 0', () => {
input.trigger('input');
input.trigger('blur');
- expect(wrapper.emitted('input')[0][0]).toEqual(0);
+ expect(input.element.value).toEqual('0');
});
test('show-plus & show-minus props', () => {
@@ -187,3 +197,19 @@ test('show-plus & show-minus props', () => {
expect(wrapper).toMatchSnapshot();
});
+
+test('decimal-length prop', () => {
+ const wrapper = mount(Stepper, {
+ propsData: {
+ value: 1,
+ step: 0.2,
+ decimalLength: 2
+ }
+ });
+
+ expect(wrapper.emitted('input')[0][0]).toEqual('1.00');
+
+ const plus = wrapper.find('.van-stepper__plus');
+ plus.trigger('click');
+ expect(wrapper.emitted('input')[1][0]).toEqual('1.20');
+});