Skip to content

Commit

Permalink
fix: 解决InputNumber数字输入框,去除掉值之后失焦/确认之后会变成数值0的问题。 (#1849)
Browse files Browse the repository at this point in the history
* 组件易用性优化,category-search组件唤出textInput时,input框自动聚焦 (#1842)

* fix(inputNumber): #1843,支持inputNumber可以允许输入空串值,即用户删除掉所有值之后有效

* feat(inputNumber): 为允许输入空值(返回null 同ag版本)提供开关参数:allowEmpty。
修复历史代码中为通过语法校验的语法错误。(formContext存在未被定义的情况无法通过ts语法校验)

* fix(lint): 预处理函数getPropsSlot无法通过ts校验,实际上这个函数无需校验。建议跳过校验或者定义成any类型

* feat: 新增允许为空验证的测试用例
  • Loading branch information
aolinpk committed Jun 3, 2024
1 parent ccebf06 commit b4a3289
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 24 deletions.
2 changes: 1 addition & 1 deletion packages/devui-vue/devui/breadcrumb/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export const getPropsSlot = (slots, props, prop = 'default') => {
export const getPropsSlot = (slots: any, props: any, prop = 'default') => {
return props[prop] ?? slots[prop]?.();
};
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,20 @@ describe('d-input-number', () => {
expect(selectFn).toBeCalledTimes(2);
});
});


it('allowEmpty', async () => {
const num = ref();
const wrapper = mount({
setup() {
return () => <DInputNumber v-model={num.value} allowEmpty={true} ></DInputNumber>;
},
});
num.value = undefined;
const inputInner = wrapper.find(ns.e('input-box'));
expect((inputInner.element as HTMLInputElement).value).toBeNull;
num.value = 51;
expect((inputInner.element as HTMLInputElement).value).toBe('51');
num.value = '';
expect((inputInner.element as HTMLInputElement).value).toBeNull;
});
2 changes: 1 addition & 1 deletion packages/devui-vue/devui/input-number/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export default {
category: '数据录入',
status: '50%',
install(app: App): void {
app.component(InputNumber.name, InputNumber);
app.component(InputNumber.name as string, InputNumber);
}
};
16 changes: 10 additions & 6 deletions packages/devui-vue/devui/input-number/src/input-number-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import type { PropType, ExtractPropTypes, ComputedRef, Ref, CSSProperties, Input
export type ISize = 'lg' | 'md' | 'sm';

export const inputNumberProps = {
modelValue: {
type: [Number, String] as PropType<number | string | null | undefined>,
},
placeholder: {
type: String,
},
Expand All @@ -25,9 +28,6 @@ export const inputNumberProps = {
size: {
type: String as PropType<ISize>,
},
modelValue: {
type: Number,
},
precision: {
type: Number,
},
Expand All @@ -39,13 +39,17 @@ export const inputNumberProps = {
type: Boolean,
default: true,
},
allowEmpty: {
type: Boolean,
default: false,
}
} as const;

export type InputNumberProps = ExtractPropTypes<typeof inputNumberProps>;

export interface IState {
currentValue: number | string | undefined;
userInputValue: number | string | undefined;
currentValue: number | string | undefined | null;
userInputValue: number | string | undefined | null;
}

export interface UseExpose {
Expand All @@ -62,7 +66,7 @@ export interface UseRender {
}

export interface UseEvent {
inputVal: ComputedRef<number | string | undefined>;
inputVal: ComputedRef<number | string | undefined | null>;
minDisabled: ComputedRef<boolean>;
maxDisabled: ComputedRef<boolean>;
onAdd: () => void;
Expand Down
28 changes: 13 additions & 15 deletions packages/devui-vue/devui/input-number/src/use-input-number.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { computed, reactive, toRefs, watch, ref, inject } from 'vue';
import { computed, reactive, toRefs, watch, ref, inject, InjectionKey } from 'vue';
import type { SetupContext, Ref, CSSProperties } from 'vue';
import { InputNumberProps, UseEvent, UseRender, IState, UseExpose } from './input-number-types';
import { useNamespace } from '../../shared/hooks/use-namespace';
import { isNumber, isUndefined } from '../../shared/utils';
import { FORM_TOKEN } from '../../form';
import { FORM_TOKEN, type FormProps } from '../../form';

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

export function useRender(props: InputNumberProps, ctx: SetupContext): UseRender {
const formContext = inject(FORM_TOKEN, undefined);
const formContext: FormProps | undefined | any = inject(FORM_TOKEN, undefined); // 修复ts语法错误组件不被d-from组件引用时,formContext未被定义
const { style, class: customClass, ...otherAttrs } = ctx.attrs;
const customStyle = { style: style as CSSProperties };

const inputNumberSize = computed(() => props.size || formContext?.size || 'md');

const wrapClass = computed(() => [
Expand Down Expand Up @@ -56,12 +55,12 @@ export function useExpose(ctx: SetupContext): UseExpose {
return { inputRef };
}

function getPrecision(pre: number | undefined): number {
function getPrecision(pre: string | number | undefined | null): number {
let precision = 0;
if (isUndefined(pre)) {
return precision;
}
const preString = pre.toString();
const preString = (pre as string).toString();
const dotIndex = preString.indexOf('.');
if (dotIndex !== -1) {
precision = preString.length - dotIndex - 1;
Expand Down Expand Up @@ -89,8 +88,8 @@ export function useEvent(props: InputNumberProps, ctx: SetupContext, inputRef: R
return state.userInputValue;
}
let currentValue = state.currentValue;
if (currentValue === '' || isUndefined(currentValue) || Number.isNaN(currentValue)) {
return '';
if (!currentValue && currentValue !== 0) {
return null;
}
if (isNumber(currentValue)) {
// todo 小数精度 确认是否应该以正则处理
Expand All @@ -111,17 +110,16 @@ export function useEvent(props: InputNumberProps, ctx: SetupContext, inputRef: R
};

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

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

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

const setCurrentValue = (value: number | string | undefined) => {
const setCurrentValue = (value: number | string | undefined | null) => {
const oldVal = state.currentValue;
const newVal = correctValue(value);

state.userInputValue = undefined;

// 0 可以被更新
if (newVal !== 0 && !newVal) {
// 0 和 '' 可以被更新
if (newVal !== 0 && newVal !== null && !newVal) {
ctx.emit('update:modelValue', oldVal);
return;
}
Expand Down
33 changes: 32 additions & 1 deletion packages/devui-vue/docs/components/input-number/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,36 @@ export default defineComponent({

:::

### 允许空值

:::demo 当 `allowEmpty``true` 的时候允许输入框的值为空,空值返回为 `null`,传入数据不为 `number` 类型且上一次输入没有值的时候都会返回null。

```vue
<template>
<div>
<d-input-number v-model="num" :allowEmpty="true" @change="onChange"></d-input-number>
</div>
</template>
<script>
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup(props) {
const num = ref(undefined);
const onChange = (newVal, oldVal) => {
console.log(newVal, oldVal);
};
return {
num,
onChange
};
}
})
</script>
```

:::

### InputNumber 参数

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

### InputNumber 事件
Expand Down

0 comments on commit b4a3289

Please sign in to comment.