From b4a3289801d5a9dff6d5efa9abd981ac9ad7aa44 Mon Sep 17 00:00:00 2001 From: VRlie <2755561608@qq.com> Date: Mon, 3 Jun 2024 20:49:29 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=20=E8=A7=A3=E5=86=B3InputNumber?= =?UTF-8?q?=E6=95=B0=E5=AD=97=E8=BE=93=E5=85=A5=E6=A1=86=EF=BC=8C=E5=8E=BB?= =?UTF-8?q?=E9=99=A4=E6=8E=89=E5=80=BC=E4=B9=8B=E5=90=8E=E5=A4=B1=E7=84=A6?= =?UTF-8?q?/=E7=A1=AE=E8=AE=A4=E4=B9=8B=E5=90=8E=E4=BC=9A=E5=8F=98?= =?UTF-8?q?=E6=88=90=E6=95=B0=E5=80=BC0=E7=9A=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E3=80=82=20(#1849)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 组件易用性优化,category-search组件唤出textInput时,input框自动聚焦 (#1842) * fix(inputNumber): #1843,支持inputNumber可以允许输入空串值,即用户删除掉所有值之后有效 * feat(inputNumber): 为允许输入空值(返回null 同ag版本)提供开关参数:allowEmpty。 修复历史代码中为通过语法校验的语法错误。(formContext存在未被定义的情况无法通过ts语法校验) * fix(lint): 预处理函数getPropsSlot无法通过ts校验,实际上这个函数无需校验。建议跳过校验或者定义成any类型 * feat: 新增允许为空验证的测试用例 --- .../devui-vue/devui/breadcrumb/src/utils.ts | 2 +- .../__tests__/input-number.spec.tsx | 17 ++++++++++ .../devui-vue/devui/input-number/index.ts | 2 +- .../input-number/src/input-number-types.ts | 16 +++++---- .../input-number/src/use-input-number.ts | 28 ++++++++-------- .../docs/components/input-number/index.md | 33 ++++++++++++++++++- 6 files changed, 74 insertions(+), 24 deletions(-) diff --git a/packages/devui-vue/devui/breadcrumb/src/utils.ts b/packages/devui-vue/devui/breadcrumb/src/utils.ts index 1ae12867c9..a38470f7a9 100644 --- a/packages/devui-vue/devui/breadcrumb/src/utils.ts +++ b/packages/devui-vue/devui/breadcrumb/src/utils.ts @@ -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]?.(); }; diff --git a/packages/devui-vue/devui/input-number/__tests__/input-number.spec.tsx b/packages/devui-vue/devui/input-number/__tests__/input-number.spec.tsx index b562c29147..cc519dde5a 100644 --- a/packages/devui-vue/devui/input-number/__tests__/input-number.spec.tsx +++ b/packages/devui-vue/devui/input-number/__tests__/input-number.spec.tsx @@ -279,3 +279,20 @@ describe('d-input-number', () => { expect(selectFn).toBeCalledTimes(2); }); }); + + +it('allowEmpty', async () => { + const num = ref(); + const wrapper = mount({ + setup() { + return () => ; + }, + }); + 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; +}); diff --git a/packages/devui-vue/devui/input-number/index.ts b/packages/devui-vue/devui/input-number/index.ts index 5ff9dae71f..2c097498ac 100644 --- a/packages/devui-vue/devui/input-number/index.ts +++ b/packages/devui-vue/devui/input-number/index.ts @@ -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); } }; diff --git a/packages/devui-vue/devui/input-number/src/input-number-types.ts b/packages/devui-vue/devui/input-number/src/input-number-types.ts index d0e52a4566..6750a7de21 100644 --- a/packages/devui-vue/devui/input-number/src/input-number-types.ts +++ b/packages/devui-vue/devui/input-number/src/input-number-types.ts @@ -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, + }, placeholder: { type: String, }, @@ -25,9 +28,6 @@ export const inputNumberProps = { size: { type: String as PropType, }, - modelValue: { - type: Number, - }, precision: { type: Number, }, @@ -39,13 +39,17 @@ export const inputNumberProps = { type: Boolean, default: true, }, + allowEmpty: { + type: Boolean, + default: false, + } } as const; export type InputNumberProps = ExtractPropTypes; export interface IState { - currentValue: number | string | undefined; - userInputValue: number | string | undefined; + currentValue: number | string | undefined | null; + userInputValue: number | string | undefined | null; } export interface UseExpose { @@ -62,7 +66,7 @@ export interface UseRender { } export interface UseEvent { - inputVal: ComputedRef; + inputVal: ComputedRef; minDisabled: ComputedRef; maxDisabled: ComputedRef; onAdd: () => void; diff --git a/packages/devui-vue/devui/input-number/src/use-input-number.ts b/packages/devui-vue/devui/input-number/src/use-input-number.ts index 31c1244071..ea5a80621d 100644 --- a/packages/devui-vue/devui/input-number/src/use-input-number.ts +++ b/packages/devui-vue/devui/input-number/src/use-input-number.ts @@ -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(() => [ @@ -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; @@ -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 小数精度 确认是否应该以正则处理 @@ -111,6 +110,9 @@ 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))) { @@ -118,10 +120,6 @@ export function useEvent(props: InputNumberProps, ctx: SetupContext, inputRef: R } let newVal = Number(value); - // 不是0 是假值或者是NaN返回undefined - if (newVal !== 0 && (!Number(value) || Number.isNaN(newVal))) { - return undefined; - } // 精度限制存在才做转换 if (!isUndefined(props.precision)) { @@ -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; } diff --git a/packages/devui-vue/docs/components/input-number/index.md b/packages/devui-vue/docs/components/input-number/index.md index d6c6c29fd6..1b6abc4ae2 100644 --- a/packages/devui-vue/docs/components/input-number/index.md +++ b/packages/devui-vue/docs/components/input-number/index.md @@ -223,6 +223,36 @@ export default defineComponent({ ::: +### 允许空值 + +:::demo 当 `allowEmpty` 为 `true` 的时候允许输入框的值为空,空值返回为 `null`,传入数据不为 `number` 类型且上一次输入没有值的时候都会返回null。 + +```vue + + +``` + +::: + ### InputNumber 参数 | 参数名 | 类型 | 默认值 | 说明 | 跳转 Demo | @@ -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 事件