Skip to content

Commit b4a3289

Browse files
authored
fix: 解决InputNumber数字输入框,去除掉值之后失焦/确认之后会变成数值0的问题。 (#1849)
* 组件易用性优化,category-search组件唤出textInput时,input框自动聚焦 (#1842) * fix(inputNumber): #1843,支持inputNumber可以允许输入空串值,即用户删除掉所有值之后有效 * feat(inputNumber): 为允许输入空值(返回null 同ag版本)提供开关参数:allowEmpty。 修复历史代码中为通过语法校验的语法错误。(formContext存在未被定义的情况无法通过ts语法校验) * fix(lint): 预处理函数getPropsSlot无法通过ts校验,实际上这个函数无需校验。建议跳过校验或者定义成any类型 * feat: 新增允许为空验证的测试用例
1 parent ccebf06 commit b4a3289

File tree

6 files changed

+74
-24
lines changed

6 files changed

+74
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export const getPropsSlot = (slots, props, prop = 'default') => {
1+
export const getPropsSlot = (slots: any, props: any, prop = 'default') => {
22
return props[prop] ?? slots[prop]?.();
33
};

packages/devui-vue/devui/input-number/__tests__/input-number.spec.tsx

+17
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,20 @@ describe('d-input-number', () => {
279279
expect(selectFn).toBeCalledTimes(2);
280280
});
281281
});
282+
283+
284+
it('allowEmpty', async () => {
285+
const num = ref();
286+
const wrapper = mount({
287+
setup() {
288+
return () => <DInputNumber v-model={num.value} allowEmpty={true} ></DInputNumber>;
289+
},
290+
});
291+
num.value = undefined;
292+
const inputInner = wrapper.find(ns.e('input-box'));
293+
expect((inputInner.element as HTMLInputElement).value).toBeNull;
294+
num.value = 51;
295+
expect((inputInner.element as HTMLInputElement).value).toBe('51');
296+
num.value = '';
297+
expect((inputInner.element as HTMLInputElement).value).toBeNull;
298+
});

packages/devui-vue/devui/input-number/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ export default {
88
category: '数据录入',
99
status: '50%',
1010
install(app: App): void {
11-
app.component(InputNumber.name, InputNumber);
11+
app.component(InputNumber.name as string, InputNumber);
1212
}
1313
};

packages/devui-vue/devui/input-number/src/input-number-types.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import type { PropType, ExtractPropTypes, ComputedRef, Ref, CSSProperties, Input
33
export type ISize = 'lg' | 'md' | 'sm';
44

55
export const inputNumberProps = {
6+
modelValue: {
7+
type: [Number, String] as PropType<number | string | null | undefined>,
8+
},
69
placeholder: {
710
type: String,
811
},
@@ -25,9 +28,6 @@ export const inputNumberProps = {
2528
size: {
2629
type: String as PropType<ISize>,
2730
},
28-
modelValue: {
29-
type: Number,
30-
},
3131
precision: {
3232
type: Number,
3333
},
@@ -39,13 +39,17 @@ export const inputNumberProps = {
3939
type: Boolean,
4040
default: true,
4141
},
42+
allowEmpty: {
43+
type: Boolean,
44+
default: false,
45+
}
4246
} as const;
4347

4448
export type InputNumberProps = ExtractPropTypes<typeof inputNumberProps>;
4549

4650
export interface IState {
47-
currentValue: number | string | undefined;
48-
userInputValue: number | string | undefined;
51+
currentValue: number | string | undefined | null;
52+
userInputValue: number | string | undefined | null;
4953
}
5054

5155
export interface UseExpose {
@@ -62,7 +66,7 @@ export interface UseRender {
6266
}
6367

6468
export interface UseEvent {
65-
inputVal: ComputedRef<number | string | undefined>;
69+
inputVal: ComputedRef<number | string | undefined | null>;
6670
minDisabled: ComputedRef<boolean>;
6771
maxDisabled: ComputedRef<boolean>;
6872
onAdd: () => void;

packages/devui-vue/devui/input-number/src/use-input-number.ts

+13-15
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
import { computed, reactive, toRefs, watch, ref, inject } from 'vue';
1+
import { computed, reactive, toRefs, watch, ref, inject, InjectionKey } from 'vue';
22
import type { SetupContext, Ref, CSSProperties } from 'vue';
33
import { InputNumberProps, UseEvent, UseRender, IState, UseExpose } from './input-number-types';
44
import { useNamespace } from '../../shared/hooks/use-namespace';
55
import { isNumber, isUndefined } from '../../shared/utils';
6-
import { FORM_TOKEN } from '../../form';
6+
import { FORM_TOKEN, type FormProps } from '../../form';
77

88
const ns = useNamespace('input-number');
99

1010
export function useRender(props: InputNumberProps, ctx: SetupContext): UseRender {
11-
const formContext = inject(FORM_TOKEN, undefined);
11+
const formContext: FormProps | undefined | any = inject(FORM_TOKEN, undefined); // 修复ts语法错误组件不被d-from组件引用时,formContext未被定义
1212
const { style, class: customClass, ...otherAttrs } = ctx.attrs;
1313
const customStyle = { style: style as CSSProperties };
14-
1514
const inputNumberSize = computed(() => props.size || formContext?.size || 'md');
1615

1716
const wrapClass = computed(() => [
@@ -56,12 +55,12 @@ export function useExpose(ctx: SetupContext): UseExpose {
5655
return { inputRef };
5756
}
5857

59-
function getPrecision(pre: number | undefined): number {
58+
function getPrecision(pre: string | number | undefined | null): number {
6059
let precision = 0;
6160
if (isUndefined(pre)) {
6261
return precision;
6362
}
64-
const preString = pre.toString();
63+
const preString = (pre as string).toString();
6564
const dotIndex = preString.indexOf('.');
6665
if (dotIndex !== -1) {
6766
precision = preString.length - dotIndex - 1;
@@ -89,8 +88,8 @@ export function useEvent(props: InputNumberProps, ctx: SetupContext, inputRef: R
8988
return state.userInputValue;
9089
}
9190
let currentValue = state.currentValue;
92-
if (currentValue === '' || isUndefined(currentValue) || Number.isNaN(currentValue)) {
93-
return '';
91+
if (!currentValue && currentValue !== 0) {
92+
return null;
9493
}
9594
if (isNumber(currentValue)) {
9695
// todo 小数精度 确认是否应该以正则处理
@@ -111,17 +110,16 @@ export function useEvent(props: InputNumberProps, ctx: SetupContext, inputRef: R
111110
};
112111

113112
const correctValue = (value: number | string | undefined | null) => {
113+
if ((!value && value !== 0) && props.allowEmpty) { // 当用户开始允许空值时 value不为0的false全返回null(即'',null,undefined,NaN都会反回null设计与dev_ui_ag版本一致)
114+
return null;
115+
}
114116
// 校验正则
115117
const valueStr = value + '';
116118
if (props.reg && !valueStr.match(new RegExp(props.reg))) {
117119
return undefined;
118120
}
119121

120122
let newVal = Number(value);
121-
// 不是0 是假值或者是NaN返回undefined
122-
if (newVal !== 0 && (!Number(value) || Number.isNaN(newVal))) {
123-
return undefined;
124-
}
125123

126124
// 精度限制存在才做转换
127125
if (!isUndefined(props.precision)) {
@@ -135,14 +133,14 @@ export function useEvent(props: InputNumberProps, ctx: SetupContext, inputRef: R
135133
return newVal;
136134
};
137135

138-
const setCurrentValue = (value: number | string | undefined) => {
136+
const setCurrentValue = (value: number | string | undefined | null) => {
139137
const oldVal = state.currentValue;
140138
const newVal = correctValue(value);
141139

142140
state.userInputValue = undefined;
143141

144-
// 0 可以被更新
145-
if (newVal !== 0 && !newVal) {
142+
// 0 和 '' 可以被更新
143+
if (newVal !== 0 && newVal !== null && !newVal) {
146144
ctx.emit('update:modelValue', oldVal);
147145
return;
148146
}

packages/devui-vue/docs/components/input-number/index.md

+32-1
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,36 @@ export default defineComponent({
223223

224224
:::
225225

226+
### 允许空值
227+
228+
:::demo 当 `allowEmpty``true` 的时候允许输入框的值为空,空值返回为 `null`,传入数据不为 `number` 类型且上一次输入没有值的时候都会返回null。
229+
230+
```vue
231+
<template>
232+
<div>
233+
<d-input-number v-model="num" :allowEmpty="true" @change="onChange"></d-input-number>
234+
</div>
235+
</template>
236+
<script>
237+
import { defineComponent, ref } from 'vue';
238+
239+
export default defineComponent({
240+
setup(props) {
241+
const num = ref(undefined);
242+
const onChange = (newVal, oldVal) => {
243+
console.log(newVal, oldVal);
244+
};
245+
return {
246+
num,
247+
onChange
248+
};
249+
}
250+
})
251+
</script>
252+
```
253+
254+
:::
255+
226256
### InputNumber 参数
227257

228258
| 参数名 | 类型 | 默认值 | 说明 | 跳转 Demo |
@@ -235,7 +265,8 @@ export default defineComponent({
235265
| disabled | `boolean` | false | 可选,文本框是否被禁用 | [禁用状态](#禁用状态) |
236266
| precision | `number` | -- | 可选,数值精度 | [精度](#精度) |
237267
| size | [ISize](#isize) | 'md' | 可选,文本框尺寸 | [尺寸](#尺寸) |
238-
| reg | `RegExp\| string` | -- | 可选,用于限制输入的正则或正则字符串 | [正则限制](#正则限制)|
268+
| reg | `RegExp \| string` | -- | 可选,用于限制输入的正则或正则字符串 | [正则限制](#正则限制)|
269+
| allowEmpty | `boolean \| false` | -- | 可选,是否允许值为空 允许空值 | [允许空值](#允许空值) |
239270
|show-glow-style|`boolean`|true|可选,是否展示悬浮发光效果||
240271

241272
### InputNumber 事件

0 commit comments

Comments
 (0)