From f5281f1e7ecf51a09becd4e7513d340a0b872d55 Mon Sep 17 00:00:00 2001 From: neverland Date: Thu, 24 Dec 2020 19:44:51 +0800 Subject: [PATCH 1/9] fix(CountDown): fix ssr memory leak (#7808) --- src/count-down/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/count-down/index.js b/src/count-down/index.js index c13d36779..ccda57817 100644 --- a/src/count-down/index.js +++ b/src/count-down/index.js @@ -1,4 +1,4 @@ -import { createNamespace } from '../utils'; +import { createNamespace, inBrowser } from '../utils'; import { raf, cancelRaf } from '../utils/dom/raf'; import { isSameSecond, parseTimeData, parseFormat } from './utils'; @@ -92,6 +92,12 @@ export default createComponent({ }, tick() { + // should not start counting in server + // see: https://github.com/youzan/vant/issues/7807 + if (!inBrowser) { + return; + } + if (this.millisecond) { this.microTick(); } else { From d92d29c885e3130b79ed5f9b18e0fdaf9752cc3b Mon Sep 17 00:00:00 2001 From: neverland Date: Thu, 24 Dec 2020 20:11:22 +0800 Subject: [PATCH 2/9] feat(Form): support valdiate multiple names (#7810) --- src/form/README.md | 2 +- src/form/README.zh-CN.md | 6 +++--- src/form/index.js | 37 ++++++++++++++++++++++------------- src/form/test/methods.spec.js | 23 ++++++++++++++++++++-- types/form.d.ts | 2 +- 5 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/form/README.md b/src/form/README.md index bbca7e1c9..e71665ea1 100644 --- a/src/form/README.md +++ b/src/form/README.md @@ -472,7 +472,7 @@ Use [ref](https://vuejs.org/v2/api/#ref) to get Form instance and call instance | Name | Description | Attribute | Return value | | --- | --- | --- | --- | | submit | Submit form | - | - | -| validate | Validate form | _name?: string_ | _Promise_ | +| validate | Validate form | _name?: string \| string[]_ | _Promise_ | | resetValidation | Reset validation | _name?: string_ | - | | scrollToField `v2.8.3` | Scroll to field | _name: string, alignToTop: boolean_ | - | diff --git a/src/form/README.zh-CN.md b/src/form/README.zh-CN.md index b89b80527..2285aa97f 100644 --- a/src/form/README.zh-CN.md +++ b/src/form/README.zh-CN.md @@ -482,7 +482,7 @@ export default { | message `v2.5.3` | 错误提示文案 | _string \| (value, rule) => string_ | | validator `v2.5.3` | 通过函数进行校验 | _(value, rule) => boolean \| Promise_ | | pattern `v2.5.3` | 通过正则表达式进行校验 | _RegExp_ | -| trigger `v2.5.2` | 本项规则的触发时机,可选值为`onChange`、`onBlur` | _string_ | +| trigger `v2.5.2` | 本项规则的触发时机,可选值为 `onChange`、`onBlur` | _string_ | | formatter `v2.5.3` | 格式化函数,将表单项的值转换后进行校验 | _(value, rule) => any_ | ### validate-trigger  可选值 @@ -509,8 +509,8 @@ export default { | 方法名 | 说明 | 参数 | 返回值 | | --- | --- | --- | --- | | submit | 提交表单,与点击提交按钮的效果等价 | - | - | -| validate | 验证表单,支持传入`name`来验证单个表单项 | _name?: string_ | _Promise_ | -| resetValidation | 重置表单项的验证提示,支持传入`name`来重置单个表单项 | _name?: string_ | - | +| validate | 验证表单,支持传入 `name` 来验证单个或多个表单项 | _name?: string \| string[]_ | _Promise_ | +| resetValidation | 重置表单项的验证提示,支持传入 `name` 来重置单个表单项 | _name?: string_ | - | | scrollToField `v2.8.3` | 滚动到对应表单项的位置,默认滚动到顶部,第二个参数传 false 可滚动至底部 | _name: string, alignToTop: boolean_ | - | ### Slots diff --git a/src/form/index.js b/src/form/index.js index 0a73f1858..363de6eda 100644 --- a/src/form/index.js +++ b/src/form/index.js @@ -43,11 +43,19 @@ export default createComponent({ }, methods: { - validateSeq() { + getFieldsByNames(names) { + if (names) { + return this.fields.filter((field) => names.indexOf(field.name) !== -1); + } + return this.fields; + }, + + validateSeq(names) { return new Promise((resolve, reject) => { const errors = []; + const fields = this.getFieldsByNames(names); - this.fields + fields .reduce( (promise, field) => promise.then(() => { @@ -71,28 +79,29 @@ export default createComponent({ }); }, - validateAll() { + validateFields(names) { return new Promise((resolve, reject) => { - Promise.all(this.fields.map((item) => item.validate())).then( - (errors) => { - errors = errors.filter((item) => item); + const fields = this.getFieldsByNames(names); + Promise.all(fields.map((item) => item.validate())).then((errors) => { + errors = errors.filter((item) => item); - if (errors.length) { - reject(errors); - } else { - resolve(); - } + if (errors.length) { + reject(errors); + } else { + resolve(); } - ); + }); }); }, // @exposed-api validate(name) { - if (name) { + if (name && !Array.isArray(name)) { return this.validateField(name); } - return this.validateFirst ? this.validateSeq() : this.validateAll(); + return this.validateFirst + ? this.validateSeq(name) + : this.validateFields(name); }, validateField(name) { diff --git a/src/form/test/methods.spec.js b/src/form/test/methods.spec.js index 5acb4549b..1e58b9937 100644 --- a/src/form/test/methods.spec.js +++ b/src/form/test/methods.spec.js @@ -37,7 +37,7 @@ test('validate method - validate all fields', (done) => { }); }); -test('validate method - validate one field and passed', (done) => { +test('validate method - validate one field and failed', (done) => { mountSimpleRulesForm({ mounted() { this.$refs.form.validate('A').catch((err) => { @@ -48,7 +48,7 @@ test('validate method - validate one field and passed', (done) => { }); }); -test('validate method - validate one field and failed', (done) => { +test('validate method - validate one field and passed', (done) => { mountForm({ template: ` @@ -64,6 +64,25 @@ test('validate method - validate one field and failed', (done) => { }); }); +test('validate method - validate two fields and failed', (done) => { + mountForm({ + template: ` + + + + + + `, + data: getSimpleRules, + mounted() { + this.$refs.form.validate(['A', 'B']).catch((error) => { + expect(error).toEqual([{ message: 'B failed', name: 'B' }]); + done(); + }); + }, + }); +}); + test('validate method - unexisted name', (done) => { mountSimpleRulesForm({ mounted() { diff --git a/types/form.d.ts b/types/form.d.ts index 3f11ee7fd..2584f24a8 100644 --- a/types/form.d.ts +++ b/types/form.d.ts @@ -3,7 +3,7 @@ import { VanComponent } from './component'; export class Form extends VanComponent { submit(): void; - validate(name?: string): Promise; + validate(name?: string | string[]): Promise; resetValidation(name?: string): void; From 1fac5c5f4f6619753e3312ed52f264e648927a9f Mon Sep 17 00:00:00 2001 From: neverland Date: Thu, 24 Dec 2020 20:19:00 +0800 Subject: [PATCH 3/9] feat(Form): resetValidation support multiple names (#7811) --- src/form/README.md | 2 +- src/form/README.zh-CN.md | 4 ++-- src/form/index.js | 11 +++++++---- src/form/test/methods.spec.js | 13 +++++++++++++ types/form.d.ts | 2 +- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/form/README.md b/src/form/README.md index e71665ea1..df0627506 100644 --- a/src/form/README.md +++ b/src/form/README.md @@ -473,7 +473,7 @@ Use [ref](https://vuejs.org/v2/api/#ref) to get Form instance and call instance | --- | --- | --- | --- | | submit | Submit form | - | - | | validate | Validate form | _name?: string \| string[]_ | _Promise_ | -| resetValidation | Reset validation | _name?: string_ | - | +| resetValidation | Reset validation | _name?: string \| string[]_ | - | | scrollToField `v2.8.3` | Scroll to field | _name: string, alignToTop: boolean_ | - | ### Slots diff --git a/src/form/README.zh-CN.md b/src/form/README.zh-CN.md index 2285aa97f..89491f4ae 100644 --- a/src/form/README.zh-CN.md +++ b/src/form/README.zh-CN.md @@ -509,8 +509,8 @@ export default { | 方法名 | 说明 | 参数 | 返回值 | | --- | --- | --- | --- | | submit | 提交表单,与点击提交按钮的效果等价 | - | - | -| validate | 验证表单,支持传入 `name` 来验证单个或多个表单项 | _name?: string \| string[]_ | _Promise_ | -| resetValidation | 重置表单项的验证提示,支持传入 `name` 来重置单个表单项 | _name?: string_ | - | +| validate | 验证表单,支持传入 `name` 来验证单个或部分表单项 | _name?: string \| string[]_ | _Promise_ | +| resetValidation | 重置表单项的验证提示,支持传入 `name` 来重置单个或部分表单项 | _name?: string \| string[]_ | - | | scrollToField `v2.8.3` | 滚动到对应表单项的位置,默认滚动到顶部,第二个参数传 false 可滚动至底部 | _name: string, alignToTop: boolean_ | - | ### Slots diff --git a/src/form/index.js b/src/form/index.js index 363de6eda..225b910a6 100644 --- a/src/form/index.js +++ b/src/form/index.js @@ -124,10 +124,13 @@ export default createComponent({ // @exposed-api resetValidation(name) { - this.fields.forEach((item) => { - if (!name || item.name === name) { - item.resetValidation(); - } + if (name && !Array.isArray(name)) { + name = [name]; + } + + const fields = this.getFieldsByNames(name); + fields.forEach((item) => { + item.resetValidation(); }); }, diff --git a/src/form/test/methods.spec.js b/src/form/test/methods.spec.js index 1e58b9937..a91700c9e 100644 --- a/src/form/test/methods.spec.js +++ b/src/form/test/methods.spec.js @@ -104,6 +104,19 @@ test('resetValidation method - reset all fields', (done) => { }); }); +test('resetValidation method - reset two fields', (done) => { + const wrapper = mountSimpleRulesForm({ + mounted() { + this.$refs.form.validate().catch(() => { + this.$refs.form.resetValidation(['A', 'B']); + const errors = wrapper.findAll('.van-field__error-message'); + expect(errors.length).toEqual(0); + done(); + }); + }, + }); +}); + test('resetValidation method - reset one field', (done) => { const wrapper = mountSimpleRulesForm({ mounted() { diff --git a/types/form.d.ts b/types/form.d.ts index 2584f24a8..53f5417e0 100644 --- a/types/form.d.ts +++ b/types/form.d.ts @@ -5,7 +5,7 @@ export class Form extends VanComponent { validate(name?: string | string[]): Promise; - resetValidation(name?: string): void; + resetValidation(name?: string | string[]): void; scrollToField(name: string, options?: boolean | ScrollIntoViewOptions): void; } From 97509a90b1f6888ca28a05b6974344de8f66abc1 Mon Sep 17 00:00:00 2001 From: neverland Date: Thu, 24 Dec 2020 20:23:32 +0800 Subject: [PATCH 4/9] feat(Stepper): add show-input prop (#7812) --- src/stepper/README.md | 1 + src/stepper/README.zh-CN.md | 1 + src/stepper/index.js | 5 +++++ src/stepper/test/index.spec.js | 10 ++++++++++ 4 files changed, 17 insertions(+) diff --git a/src/stepper/README.md b/src/stepper/README.md index ca8f308f0..7ee225d1d 100644 --- a/src/stepper/README.md +++ b/src/stepper/README.md @@ -129,6 +129,7 @@ export default { | async-change | Whether to enable async change | _boolean_ | `false` | - | | show-plus | Whether to show plus button | _boolean_ | `true` | | show-minus | Whether to show minus button | _boolean_ | `true` | +| show-input `v2.12.1` | Whether to show input | _boolean_ | `true` | | long-press `v2.4.3` | Whether to allow long press | _boolean_ | `true` | | allow-empty `v2.9.1` | Whether to allow the input to be empty | _boolean_ | `false` | diff --git a/src/stepper/README.zh-CN.md b/src/stepper/README.zh-CN.md index 6ddcbb535..5f083b2a7 100644 --- a/src/stepper/README.zh-CN.md +++ b/src/stepper/README.zh-CN.md @@ -154,6 +154,7 @@ export default { | async-change | 是否开启异步变更,开启后需要手动控制输入值 | _boolean_ | `false` | | show-plus | 是否显示增加按钮 | _boolean_ | `true` | | show-minus | 是否显示减少按钮 | _boolean_ | `true` | +| show-input `v2.12.1` | 是否显示输入框 | _boolean_ | `true` | | long-press `v2.4.3` | 是否开启长按手势 | _boolean_ | `true` | | allow-empty `v2.9.1` | 是否允许输入的值为空 | _boolean_ | `false` | diff --git a/src/stepper/index.js b/src/stepper/index.js index 606dc7972..29183bdbd 100644 --- a/src/stepper/index.js +++ b/src/stepper/index.js @@ -65,6 +65,10 @@ export default createComponent({ type: Boolean, default: true, }, + showInput: { + type: Boolean, + default: true, + }, longPress: { type: Boolean, default: true, @@ -306,6 +310,7 @@ export default createComponent({ {...createListeners('minus')} /> { expect(wrapper).toMatchSnapshot(); }); +test('should hide input when show-input prop is false', () => { + const wrapper = mount(Stepper, { + propsData: { + showInput: false, + }, + }); + + expect(wrapper.find('input').element.style.display).toEqual('none'); +}); + test('decimal-length prop', () => { const wrapper = mount(Stepper, { propsData: { From b89832c03e4b9cdf1bcf3fe367b59717fa8fe8ad Mon Sep 17 00:00:00 2001 From: neverland Date: Sat, 26 Dec 2020 15:09:57 +0800 Subject: [PATCH 5/9] fix(Stepper): change event emitted twice (#7820) --- src/stepper/index.js | 5 +++++ src/stepper/test/index.spec.js | 11 +++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/stepper/index.js b/src/stepper/index.js index 29183bdbd..ae60d071b 100644 --- a/src/stepper/index.js +++ b/src/stepper/index.js @@ -193,6 +193,11 @@ export default createComponent({ event.target.value = formatted; } + // perfer number type + if (formatted === String(+formatted)) { + formatted = +formatted; + } + this.emitChange(formatted); }, diff --git a/src/stepper/test/index.spec.js b/src/stepper/test/index.spec.js index 389c73af6..89428318a 100644 --- a/src/stepper/test/index.spec.js +++ b/src/stepper/test/index.spec.js @@ -125,7 +125,7 @@ test('filter value during user input', () => { input.element.value = '2'; input.trigger('input'); expect(input.element.value).toEqual('2'); - expect(wrapper.emitted('input')[1][0]).toEqual('2'); + expect(wrapper.emitted('input')[1][0]).toEqual(2); }); test('shoud watch value and format it', () => { @@ -149,12 +149,11 @@ test('only allow interger', () => { }); const input = wrapper.find('input'); - input.element.value = '1.2'; + input.element.value = '2.2'; input.trigger('input'); input.trigger('blur'); - expect(wrapper.emitted('input')[0][0]).toEqual('1'); - expect(wrapper.emitted('input')[1][0]).toEqual(1); + expect(wrapper.emitted('input')[0][0]).toEqual(2); }); test('input invalid value and blur', () => { @@ -246,8 +245,8 @@ test('async-change prop', () => { 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', () => { From 76aa0e5dd9ad121371179f5c44e620248eaa14e0 Mon Sep 17 00:00:00 2001 From: neverland Date: Sat, 26 Dec 2020 15:18:28 +0800 Subject: [PATCH 6/9] fix(Swipe): incorrect size during ssr (#7821) --- .../test/__snapshots__/index.spec.js.snap | 18 +++++++++--------- .../test/__snapshots__/demo.spec.js.snap | 8 ++++---- src/swipe-item/index.js | 4 +++- src/swipe/index.js | 16 ++++++++++------ src/swipe/test/__snapshots__/demo.spec.js.snap | 12 ++++++------ 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/image-preview/test/__snapshots__/index.spec.js.snap b/src/image-preview/test/__snapshots__/index.spec.js.snap index ea96f5ab7..fb3dfe278 100644 --- a/src/image-preview/test/__snapshots__/index.spec.js.snap +++ b/src/image-preview/test/__snapshots__/index.spec.js.snap @@ -4,7 +4,7 @@ exports[`close-icon prop 1`] = `
-
+
1 / 0
@@ -14,7 +14,7 @@ exports[`close-icon-position prop 1`] = `
-
+
1 / 0
@@ -23,7 +23,7 @@ exports[`close-icon-position prop 1`] = ` exports[`cover slot 1`] = `
-
+
1 / 0
Custom Cover Content
@@ -33,7 +33,7 @@ exports[`cover slot 1`] = ` exports[`index slot 1`] = `
-
+
Custom Index
@@ -42,10 +42,10 @@ exports[`index slot 1`] = ` exports[`render image 1`] = `
-
-
-
-
+
+
+
+
1 / 3
@@ -55,7 +55,7 @@ exports[`render image 1`] = ` exports[`set show-index prop to false 1`] = `
-
+
`; diff --git a/src/notice-bar/test/__snapshots__/demo.spec.js.snap b/src/notice-bar/test/__snapshots__/demo.spec.js.snap index f28b3b021..2806a899f 100644 --- a/src/notice-bar/test/__snapshots__/demo.spec.js.snap +++ b/src/notice-bar/test/__snapshots__/demo.spec.js.snap @@ -57,10 +57,10 @@ exports[`renders demo correctly 1`] = `
-
-
内容 1
-
内容 2
-
内容 3
+
+
内容 1
+
内容 2
+
内容 3
diff --git a/src/swipe-item/index.js b/src/swipe-item/index.js index 8039d0996..37fa21910 100644 --- a/src/swipe-item/index.js +++ b/src/swipe-item/index.js @@ -25,7 +25,9 @@ export default createComponent({ const style = {}; const { size, vertical } = this.parent; - style[vertical ? 'height' : 'width'] = `${size}px`; + if (size) { + style[vertical ? 'height' : 'width'] = `${size}px`; + } if (this.offset) { style.transform = `translate${vertical ? 'Y' : 'X'}(${this.offset}px)`; diff --git a/src/swipe/index.js b/src/swipe/index.js index 947516082..bd74a4843 100644 --- a/src/swipe/index.js +++ b/src/swipe/index.js @@ -124,15 +124,19 @@ export default createComponent({ }, trackStyle() { - const mainAxis = this.vertical ? 'height' : 'width'; - const crossAxis = this.vertical ? 'width' : 'height'; - - return { - [mainAxis]: `${this.trackSize}px`, - [crossAxis]: this[crossAxis] ? `${this[crossAxis]}px` : '', + const style = { transitionDuration: `${this.swiping ? 0 : this.duration}ms`, transform: `translate${this.vertical ? 'Y' : 'X'}(${this.offset}px)`, }; + + if (this.size) { + const mainAxis = this.vertical ? 'height' : 'width'; + const crossAxis = this.vertical ? 'width' : 'height'; + style[mainAxis] = `${this.trackSize}px`; + style[crossAxis] = this[crossAxis] ? `${this[crossAxis]}px` : ''; + } + + return style; }, indicatorStyle() { diff --git a/src/swipe/test/__snapshots__/demo.spec.js.snap b/src/swipe/test/__snapshots__/demo.spec.js.snap index 799f7af64..7c42a21af 100644 --- a/src/swipe/test/__snapshots__/demo.spec.js.snap +++ b/src/swipe/test/__snapshots__/demo.spec.js.snap @@ -4,7 +4,7 @@ exports[`renders demo correctly 1`] = `
-
+
1
2
3
@@ -15,7 +15,7 @@ exports[`renders demo correctly 1`] = `
-
+
@@ -26,7 +26,7 @@ exports[`renders demo correctly 1`] = `
-
+
1
2
3
@@ -37,7 +37,7 @@ exports[`renders demo correctly 1`] = `
-
+
1
2
3
@@ -48,7 +48,7 @@ exports[`renders demo correctly 1`] = `
-
+
1
2
3
@@ -59,7 +59,7 @@ exports[`renders demo correctly 1`] = `
-
+
1
2
3
From 67e4adb3894f436c800b7af514146f2ac6b82b30 Mon Sep 17 00:00:00 2001 From: neverland Date: Sat, 26 Dec 2020 15:29:46 +0800 Subject: [PATCH 7/9] fix(Image): mismatching warning during ssr (#7822) --- src/image/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/image/index.js b/src/image/index.js index 30993d464..d314d7063 100644 --- a/src/image/index.js +++ b/src/image/index.js @@ -114,7 +114,7 @@ export default createComponent({ }, genPlaceholder() { - if (this.loading && this.showLoading && inBrowser) { + if (this.loading && this.showLoading) { return (
{this.slots('loading') || ( From 8c07e7a95303bb94bc32a618a18042efc552049a Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Sat, 26 Dec 2020 15:31:59 +0800 Subject: [PATCH 8/9] chore: release 2.12.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c56a16635..acb54464f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vant", - "version": "2.12.0", + "version": "2.12.1", "description": "Mobile UI Components built on Vue", "main": "lib/index.js", "module": "es/index.js", From ed2eaf005c74c94e3eb40dda75b880d5846878cb Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Sat, 26 Dec 2020 15:35:02 +0800 Subject: [PATCH 9/9] docs(changelog): 2.12.1 --- docs/markdown/changelog.en-US.md | 17 +++++++++++++++++ docs/markdown/changelog.zh-CN.md | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/docs/markdown/changelog.en-US.md b/docs/markdown/changelog.en-US.md index 779f1858a..96ae5bd17 100644 --- a/docs/markdown/changelog.en-US.md +++ b/docs/markdown/changelog.en-US.md @@ -16,6 +16,23 @@ Vant follows [Semantic Versioning 2.0.0](https://semver.org/lang/zh-CN/). ## Details +### [v2.12.1](https://github.com/youzan/vant/compare/v2.12.0...v2.12.1) + +`2020-12-26` + +**Feature** + +- Form: support valdiate multiple names [#7810](https://github.com/youzan/vant/issues/7810) +- Form: resetValidation support multiple names [#7811](https://github.com/youzan/vant/issues/7811) +- Stepper: add show-input prop [#7812](https://github.com/youzan/vant/issues/7812) + +**Bug Fixes** + +- CountDown: fix ssr memory leak [#7808](https://github.com/youzan/vant/issues/7808) +- Image: mismatching warning during ssr [#7822](https://github.com/youzan/vant/issues/7822) +- Stepper: change event emitted twice [#7820](https://github.com/youzan/vant/issues/7820) +- Swipe: incorrect size during ssr [#7821](https://github.com/youzan/vant/issues/7821) + ### [v2.12.0](https://github.com/youzan/vant/compare/v2.11.3...v2.12.0) `2020-12-23` diff --git a/docs/markdown/changelog.zh-CN.md b/docs/markdown/changelog.zh-CN.md index 6439bead0..d72b26669 100644 --- a/docs/markdown/changelog.zh-CN.md +++ b/docs/markdown/changelog.zh-CN.md @@ -16,6 +16,23 @@ Vant 遵循 [Semver](https://semver.org/lang/zh-CN/) 语义化版本规范。 ## 更新内容 +### [v2.12.1](https://github.com/youzan/vant/compare/v2.12.0...v2.12.1) + +`2020-12-26` + +**Feature** + +- Form: valdiate 方法支持校验多个表单项 [#7810](https://github.com/youzan/vant/issues/7810) +- Form: resetValidation 方法支持重置多个表单项 [#7811](https://github.com/youzan/vant/issues/7811) +- Stepper: 新增 show-input 属性,用于控制是否显示输入框 [#7812](https://github.com/youzan/vant/issues/7812) + +**Bug Fixes** + +- CountDown: 修复 SSR 过程中内存泄露的问题 [#7808](https://github.com/youzan/vant/issues/7808) +- Image: 修复 SSR 时提示 DOM 不匹配的问题 [#7822](https://github.com/youzan/vant/issues/7822) +- Stepper: 修复 change 事件重复触发的问题 [#7820](https://github.com/youzan/vant/issues/7820) +- Swipe: 修复 SSR 样式不正确的问题 [#7821](https://github.com/youzan/vant/issues/7821) + ### [v2.12.0](https://github.com/youzan/vant/compare/v2.11.3...v2.12.0) `2020-12-23`