Skip to content

Commit 927bf46

Browse files
authored
feat(Stepper): add decimal-length prop (youzan#4443)
1 parent f14043c commit 927bf46

7 files changed

Lines changed: 172 additions & 136 deletions

File tree

src/stepper/README.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ export default {
5151
<van-stepper v-model="value" disabled />
5252
```
5353

54+
### Decimal Length
55+
56+
```html
57+
<van-stepper v-model="value" step="0.2" :decimal-length="1" />
58+
```
59+
60+
### Custom Size
61+
62+
```html
63+
<van-stepper v-model="value" input-width="40px" button-size="32px" />
64+
```
65+
5466
### Async Change
5567

5668
```html
@@ -82,16 +94,6 @@ export default {
8294
}
8395
```
8496

85-
### Custom Size
86-
87-
```html
88-
<van-stepper
89-
v-model="value"
90-
input-width="40px"
91-
button-size="32px"
92-
/>
93-
```
94-
9597
## API
9698

9799
### Props
@@ -110,6 +112,7 @@ export default {
110112
| button-size | Button size | *string \| number* | `28px` | 2.0.5 |
111113
| show-plus | Whether to show plus button | *boolean* | `true` | 2.1.2 |
112114
| show-minus | Whether to show minus button | *boolean* | `true` | 2.1.2 |
115+
| decimal-length | Decimal length | *number* | - | 2.2.1 |
113116

114117
### Events
115118

src/stepper/README.zh-CN.md

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,22 @@ export default {
6565
<van-stepper v-model="value" disabled />
6666
```
6767

68+
### 固定小数位数
69+
70+
通过设置`decimal-length`属性可以保留固定的小数位数
71+
72+
```html
73+
<van-stepper v-model="value" step="0.2" :decimal-length="1" />
74+
```
75+
76+
### 自定义大小
77+
78+
通过`input-width`属性设置输入框宽度,通过`button-size`属性设置按钮大小和输入框高度
79+
80+
```html
81+
<van-stepper v-model="value" input-width="40px" button-size="32px" />
82+
```
83+
6884
### 异步变更
6985

7086
如果需要异步地修改输入框的值,可以设置`async-change`属性,并在`change`事件中手动修改`value`
@@ -100,25 +116,13 @@ export default {
100116
}
101117
```
102118

103-
### 自定义大小
104-
105-
通过`input-width`属性设置输入框宽度,通过`button-size`属性设置按钮大小和输入框高度
106-
107-
```html
108-
<van-stepper
109-
v-model="value"
110-
input-width="40px"
111-
button-size="32px"
112-
/>
113-
```
114-
115119
## API
116120

117121
### Props
118122

119123
| 参数 | 说明 | 类型 | 默认值 | 版本 |
120124
|------|------|------|------|------|
121-
| v-model | 当前输入值 | *string \| number* | 最小值 | - |
125+
| v-model | 当前输入值 | *string \| number* | min | - |
122126
| min | 最小值 | *string \| number* | `1` | - |
123127
| max | 最大值 | *string \| number* | - | - |
124128
| step | 步长 | *string \| number* | `1` | - |
@@ -127,9 +131,10 @@ export default {
127131
| disable-input | 是否禁用输入框 | *boolean* | `false` | - |
128132
| async-change | 是否开启异步变更,开启后需要手动控制输入值 | *boolean* | `false` | - |
129133
| input-width | 输入框宽度,默认单位为`px` | *string \| number* | `32px` | - |
130-
| button-size | 按钮大小,默认单位为`px`,输入框高度会和按钮大小保持一致 | *string \| number* | `28px` | 2.0.5 |
134+
| button-size | 按钮大小以及输入框高度,默认单位为`px` | *string \| number* | `28px` | 2.0.5 |
131135
| show-plus | 是否显示增加按钮 | *boolean* | `true` | 2.1.2 |
132136
| show-minus | 是否显示减少按钮 | *boolean* | `true` | 2.1.2 |
137+
| decimal-length | 固定显示的小数位数 | *number* | - | 2.2.1 |
133138

134139
### Events
135140

src/stepper/demo/index.vue

Lines changed: 23 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,35 @@
11
<template>
22
<demo-section>
3-
<van-cell
4-
center
5-
:title="$t('basicUsage')"
6-
>
3+
<van-cell center :title="$t('basicUsage')">
74
<van-stepper v-model="stepper1" />
85
</van-cell>
96

10-
<van-cell
11-
center
12-
:title="$t('step')"
13-
>
14-
<van-stepper
15-
v-model="stepper2"
16-
step="2"
17-
/>
7+
<van-cell center :title="$t('step')">
8+
<van-stepper v-model="stepper2" step="2" />
189
</van-cell>
1910

20-
<van-cell
21-
center
22-
:title="$t('range')"
23-
>
24-
<van-stepper
25-
v-model="stepper3"
26-
:min="5"
27-
:max="8"
28-
/>
11+
<van-cell center :title="$t('range')">
12+
<van-stepper v-model="stepper3" :min="5" :max="8" />
2913
</van-cell>
3014

31-
<van-cell
32-
center
33-
:title="$t('integer')"
34-
>
35-
<van-stepper
36-
v-model="stepper4"
37-
integer
38-
/>
15+
<van-cell center :title="$t('integer')">
16+
<van-stepper v-model="stepper4" integer />
3917
</van-cell>
4018

41-
<van-cell
42-
center
43-
:title="$t('disabled')"
44-
>
45-
<van-stepper
46-
v-model="stepper5"
47-
disabled
48-
/>
19+
<van-cell center :title="$t('disabled')">
20+
<van-stepper v-model="stepper5" disabled />
4921
</van-cell>
5022

51-
<van-cell
52-
center
53-
:title="$t('asyncChange')"
54-
>
55-
<van-stepper
56-
:value="stepper6"
57-
async-change
58-
@change="onChange"
59-
/>
23+
<van-cell center :title="$t('decimalLength')">
24+
<van-stepper v-model="stepper8" :decimal-length="1" step="0.2" />
6025
</van-cell>
6126

62-
<van-cell
63-
center
64-
:title="$t('customSize')"
65-
>
66-
<van-stepper
67-
v-model="stepper7"
68-
button-size="32px"
69-
input-width="40px"
70-
/>
27+
<van-cell center :title="$t('customSize')">
28+
<van-stepper v-model="stepper7" button-size="32px" input-width="40px" />
29+
</van-cell>
30+
31+
<van-cell center :title="$t('asyncChange')">
32+
<van-stepper :value="stepper6" async-change @change="onChange" />
7133
</van-cell>
7234
</demo-section>
7335
</template>
@@ -80,14 +42,16 @@ export default {
8042
range: '限制输入范围',
8143
integer: '限制输入整数',
8244
asyncChange: '异步变更',
83-
customSize: '自定义大小'
45+
customSize: '自定义大小',
46+
decimalLength: '固定小数位数'
8447
},
8548
'en-US': {
8649
step: 'Step',
8750
range: 'Range',
8851
integer: 'Integer',
8952
asyncChange: 'Async Change',
90-
customSize: 'Custom Size'
53+
customSize: 'Custom Size',
54+
decimalLength: 'Decimal Length'
9155
}
9256
},
9357
@@ -99,7 +63,8 @@ export default {
9963
stepper4: 1,
10064
stepper5: 1,
10165
stepper6: 1,
102-
stepper7: 1
66+
stepper7: 1,
67+
stepper8: 1
10368
};
10469
},
10570

src/stepper/index.js

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ const [createComponent, bem] = createNamespace('stepper');
77
const LONG_PRESS_START_TIME = 600;
88
const LONG_PRESS_INTERVAL = 200;
99

10+
function equal(value1, value2) {
11+
return String(value1) === String(value2);
12+
}
13+
1014
export default createComponent({
1115
props: {
1216
value: null,
@@ -16,6 +20,7 @@ export default createComponent({
1620
buttonSize: [Number, String],
1721
asyncChange: Boolean,
1822
disableInput: Boolean,
23+
decimalLength: Number,
1924
min: {
2025
type: [Number, String],
2126
default: 1
@@ -43,8 +48,10 @@ export default createComponent({
4348
},
4449

4550
data() {
46-
const value = this.range(isDef(this.value) ? this.value : this.defaultValue);
47-
if (value !== +this.value) {
51+
const defaultValue = isDef(this.value) ? this.value : this.defaultValue;
52+
const value = this.format(defaultValue);
53+
54+
if (!equal(value, this.value)) {
4855
this.$emit('input', value);
4956
}
5057

@@ -90,7 +97,7 @@ export default createComponent({
9097

9198
watch: {
9299
value(val) {
93-
if (val !== this.currentValue) {
100+
if (!equal(val, this.currentValue)) {
94101
this.currentValue = this.format(val);
95102
}
96103
},
@@ -103,29 +110,54 @@ export default createComponent({
103110

104111
methods: {
105112
// filter illegal characters
106-
format(value) {
113+
filter(value) {
107114
value = String(value).replace(/[^0-9.-]/g, '');
108-
return value === '' ? 0 : this.integer ? Math.floor(value) : +value;
115+
116+
if (this.integer && value.indexOf('.') !== -1) {
117+
value = value.split('.')[0];
118+
}
119+
120+
return value;
109121
},
110122

111-
// limit value range
112-
range(value) {
113-
return Math.max(Math.min(this.max, this.format(value)), this.min);
123+
format(value) {
124+
value = this.filter(value);
125+
126+
// format range
127+
value = value === '' ? 0 : +value;
128+
value = Math.max(Math.min(this.max, value), this.min);
129+
130+
// format decimal
131+
if (isDef(this.decimalLength)) {
132+
value = value.toFixed(this.decimalLength);
133+
}
134+
135+
return value;
114136
},
115137

116138
onInput(event) {
117139
const { value } = event.target;
118-
const formatted = this.format(value);
119140

141+
// allow input to be empty
142+
if (value === '') {
143+
return;
144+
}
145+
146+
const formatted = this.filter(value);
147+
148+
if (!equal(value, formatted)) {
149+
event.target.value = formatted;
150+
}
151+
152+
this.emitChange(formatted);
153+
},
154+
155+
emitChange(value) {
120156
if (this.asyncChange) {
121-
event.target.value = this.currentValue;
122-
this.$emit('input', formatted);
123-
this.$emit('change', formatted);
157+
this.$emit('input', value);
158+
this.$emit('change', value);
124159
} else {
125-
if (+value !== formatted) {
126-
event.target.value = formatted;
127-
}
128-
this.currentValue = formatted;
160+
this.currentValue = value;
129161
}
130162
},
131163

@@ -138,15 +170,17 @@ export default createComponent({
138170
}
139171

140172
const diff = type === 'minus' ? -this.step : +this.step;
141-
const value = Math.round((this.currentValue + diff) * 100) / 100;
142173

143-
if (this.asyncChange) {
144-
this.$emit('input', value);
145-
this.$emit('change', value);
146-
} else {
147-
this.currentValue = this.range(value);
174+
let value = +this.currentValue + diff;
175+
176+
// avoid float number
177+
if (!isDef(this.decimalLength)) {
178+
value = Math.round(value * 100) / 100;
148179
}
149180

181+
value = this.format(value);
182+
183+
this.emitChange(value);
150184
this.$emit(type);
151185
},
152186

@@ -155,14 +189,11 @@ export default createComponent({
155189
},
156190

157191
onBlur(event) {
158-
this.currentValue = this.range(this.currentValue);
192+
const value = this.format(event.target.value);
193+
event.target.value = value;
194+
this.currentValue = value;
159195
this.$emit('blur', event);
160196

161-
// fix edge case when input is empty and min is 0
162-
if (this.currentValue === 0) {
163-
event.target.value = this.currentValue;
164-
}
165-
166197
resetScroll();
167198
},
168199

0 commit comments

Comments
 (0)