diff --git a/docs/markdown/migrate-from-v2.zh-CN.md b/docs/markdown/migrate-from-v2.zh-CN.md index ceabf40f5..5955f6ced 100644 --- a/docs/markdown/migrate-from-v2.zh-CN.md +++ b/docs/markdown/migrate-from-v2.zh-CN.md @@ -201,6 +201,10 @@ Vue 3.0 中增加了 `Teleport` 组件,提供将组件渲染到任意 DOM 位 - `trigger` 属性的默认值调整为 `click` +#### Stepper + +- `async-change` 属性重命名为 `before-change`,并调整使用方法 + #### SwipeCell - `open` 事件的 `detail` 参数重命名为 `name` diff --git a/src/stepper/README.md b/src/stepper/README.md index 9358fe55a..cd3290802 100644 --- a/src/stepper/README.md +++ b/src/stepper/README.md @@ -71,10 +71,10 @@ export default { ``` -### Async Change +### Before Change ```html - + ``` ```js @@ -85,22 +85,22 @@ export default { setup() { const value = ref(1); - let timer; - const onChange = (newValue) => { - if (newValue === value.value) { - return; - } - + const beforeChange = (value) => { Toast.loading({ forbidClick: true }); - clearTimeout(this.timer); - timer = setTimeout(() => { - Toast.clear(); - value.value = newValue; - }, 500); + return new Promise((resolve) => { + setTimeout(() => { + Toast.clear(); + // resolve 'true' or 'false' + resolve(true); + }, 500); + }); }; - return { value }; + return { + value, + beforeChange, + }; }, }; ``` @@ -116,7 +116,7 @@ export default { ### Props | Attribute | Description | Type | Default | -| --- | --- | --- | --- | --- | +| --- | --- | --- | --- | | v-model | Current value | _number \| string_ | - | | min | Min value | _number \| string_ | `1` | | max | Max value | _number \| string_ | - | @@ -133,7 +133,7 @@ export default { | disable-plus | Whether to disable plus button | _boolean_ | `false` | | disable-minus | Whether to disable minus button | _boolean_ | `false` | | disable-input | Whether to disable input | _boolean_ | `false` | -| async-change | Whether to enable async change | _boolean_ | `false` | - | +| before-change | Callback function before changing,return `false` to prevent change,support return Promise | _(value) => boolean \| Promise_ | `false` | | show-plus | Whether to show plus button | _boolean_ | `true` | | show-minus | Whether to show minus button | _boolean_ | `true` | | long-press `v2.4.3` | Whether to allow long press | _boolean_ | `true` | diff --git a/src/stepper/README.zh-CN.md b/src/stepper/README.zh-CN.md index b57bcbf14..e324a14bc 100644 --- a/src/stepper/README.zh-CN.md +++ b/src/stepper/README.zh-CN.md @@ -93,10 +93,10 @@ export default { ### 异步变更 -如果需要异步地修改输入框的值,可以设置 `async-change` 属性,并在 `change` 事件中手动修改 `value`。 +通过 `before-change` 属性可以在 ```html - + ``` ```js @@ -107,22 +107,22 @@ export default { setup() { const value = ref(1); - let timer; - const onChange = (newValue) => { - if (newValue === value.value) { - return; - } - + const beforeChange = (value) => { Toast.loading({ forbidClick: true }); - clearTimeout(this.timer); - timer = setTimeout(() => { - Toast.clear(); - value.value = newValue; - }, 500); + return new Promise((resolve) => { + setTimeout(() => { + Toast.clear(); + // 在 resolve 函数中返回 true 或 false + resolve(true); + }, 500); + }); }; - return { value }; + return { + value, + beforeChange, + }; }, }; ``` @@ -157,7 +157,7 @@ export default { | disable-plus | 是否禁用增加按钮 | _boolean_ | `false` | | disable-minus | 是否禁用减少按钮 | _boolean_ | `false` | | disable-input | 是否禁用输入框 | _boolean_ | `false` | -| async-change | 是否开启异步变更,开启后需要手动控制输入值 | _boolean_ | `false` | +| before-change | 输入值变化前的回调函数,返回 `false` 可阻止输入,支持返回 Promise | _(value) => boolean \| Promise_ | `false` | | show-plus | 是否显示增加按钮 | _boolean_ | `true` | | show-minus | 是否显示减少按钮 | _boolean_ | `true` | | long-press `v2.4.3` | 是否开启长按手势 | _boolean_ | `true` | diff --git a/src/stepper/demo/index.vue b/src/stepper/demo/index.vue index ac46f0445..8059dc6e6 100644 --- a/src/stepper/demo/index.vue +++ b/src/stepper/demo/index.vue @@ -31,8 +31,8 @@ - - + + @@ -56,8 +56,8 @@ export default { range: '限制输入范围', integer: '限制输入整数', roundTheme: '圆角风格', - asyncChange: '异步变更', customSize: '自定义大小', + beforeChange: '异步变更', disableInput: '禁用输入框', decimalLength: '固定小数位数', }, @@ -66,8 +66,8 @@ export default { range: 'Range', integer: 'Integer', roundTheme: 'Round Theme', - asyncChange: 'Async Change', customSize: 'Custom Size', + beforeChange: 'Before Change', disableInput: 'Disable Input', decimalLength: 'Decimal Length', }, @@ -87,24 +87,20 @@ export default { disabledInput: 1, }); - let timer; - const onChange = (newValue) => { - if (newValue === state.stepper6) { - return; - } - + const beforeChange = () => { Toast.loading({ forbidClick: true }); - clearTimeout(timer); - timer = setTimeout(() => { - state.stepper6 = newValue; - Toast.clear(); - }, 500); + return new Promise((resolve) => { + setTimeout(() => { + Toast.clear(); + resolve(true); + }, 500); + }); }; return { ...toRefs(state), - onChange, + beforeChange, }; }, }; diff --git a/src/stepper/index.tsx b/src/stepper/index.tsx index 38d184e70..2ab190e56 100644 --- a/src/stepper/index.tsx +++ b/src/stepper/index.tsx @@ -1,4 +1,4 @@ -import { ref, watch, computed } from 'vue'; +import { ref, watch, computed, PropType } from 'vue'; // Utils import { isNaN } from '../utils/validate/number'; @@ -14,6 +14,7 @@ import { // Composition import { useLinkField } from '../composables/use-link-field'; +import { Interceptor, callInterceptor } from '../utils/interceptor'; const [createComponent, bem] = createNamespace('stepper'); @@ -39,11 +40,11 @@ export default createComponent({ modelValue: [Number, String], inputWidth: [Number, String], buttonSize: [Number, String], - asyncChange: Boolean, placeholder: String, disablePlus: Boolean, disableMinus: Boolean, disableInput: Boolean, + beforeChange: Function as PropType, decimalLength: [Number, String], name: { type: [Number, String], @@ -147,10 +148,15 @@ export default createComponent({ } }; - const emitChange = (value: string | number) => { - if (props.asyncChange) { - emit('update:modelValue', value); - emit('change', value, { name: props.name }); + const setValue = (value: string | number) => { + if (props.beforeChange) { + callInterceptor({ + args: [value], + interceptor: props.beforeChange, + done() { + current.value = value; + }, + }); } else { current.value = value; } @@ -168,7 +174,7 @@ export default createComponent({ const diff = actionType === 'minus' ? -props.step : +props.step; const value = format(add(+current.value, diff)); - emitChange(value); + setValue(value); emit(actionType); }; @@ -185,11 +191,13 @@ export default createComponent({ formatted = `${pair[0]}.${pair[1].slice(0, +decimalLength)}`; } - if (!equal(value, formatted)) { + if (props.beforeChange) { + input.value = String(current.value); + } else if (!equal(value, formatted)) { input.value = formatted; } - emitChange(formatted); + setValue(formatted); }; const onFocus = (event: Event) => { diff --git a/src/utils/interceptor.ts b/src/utils/interceptor.ts index 916109f1f..be79b952a 100644 --- a/src/utils/interceptor.ts +++ b/src/utils/interceptor.ts @@ -1,7 +1,9 @@ import { isPromise, noop } from '.'; +export type Interceptor = (...args: any[]) => Promise | boolean; + export function callInterceptor(options: { - interceptor?: (...args: any[]) => Promise | boolean; + interceptor?: Interceptor; args?: any[]; done: () => void; canceled?: () => void;