vue-vben-admin
vue-vben-admin copied to clipboard
表单输入框设置blur验证失效
⚠️ 重要 ⚠️ 在进一步操作之前,请检查下列选项。如果您忽视此模板或者没有提供关键信息,您的 Issue 将直接被关闭
- [√] 已阅读 文档.
- [√] 确保您的代码已是最新或者所报告的 Bug 在最新版本中可以重现. (部分 Bug 可能已经在最近的代码中修复)
- [√] 已在 Issues 中搜索了相关的关键词
- [√] 不是 ant design vue 组件库的 Bug
描述 Bug
vben2.8版本中,表演输入框设置blur失去焦点进行校验,触发的是change事件校验,blur不生效
复现 Bug
如上图设置的是blur事件实际触发的是change事件
解决方案
1、为useForm.ts\useEvent.ts\form.ts中的validateFields方法添加ValidateOptions
import type { NamePath, RuleObject, ValidateOptions } from 'ant-design-vue/lib/form/interface';
validateFields: (nameList?: NamePath[], options?: ValidateOptions): Promise<any> {
...
unref(form).validateFields(nameList, options);
};
2、注释setFormModel的验证方法
3、将validateFields方法提供给FormItem组件
4、FormItem组件添加validateFields props属性
5、FormItem组件handleRules方法为校验规则添加默认的change事件
function handleRules(): ValidationRule[] {
...
rules.forEach(item => !item.trigger && (item.trigger = 'change'))
return rules;
}
5、FormItem组件renderComponent方法添加onBlur事件
function renderComponent() {
...
const on = {
[eventKey]: (...args: Nullable<Recordable>[]) => {
const [e] = args;
if (propsData[eventKey]) {
propsData[eventKey](...args);
}
const target = e ? e.target : null;
const value = target ? (isCheck ? target.checked : target.value) : e;
props.setFormModel(field, value);
props.validateFields([field], { triggerName: 'change' }).catch((_) => {});
},
onBlur: () => {
props.validateFields([field], { triggerName: 'blur' }).catch((_) => {});
},
};
}
...
}
系统信息
- 操作系统:
- Node 版本:
- 包管理器 (npm/yarn/pnpm) 及其版本:
阔以 很完美
perfect
坐等UPDATE
打补丁: 新建文本文件 后缀改成 diff 文本内容如下 ==> patch.diff,使用source tree 顶部=>动作=>应用补丁 使用这个补丁即可
diff --git a/src/components/Form/src/BasicForm.vue b/src/components/Form/src/BasicForm.vue
index d62d66c..ec97bd3 100644
--- a/src/components/Form/src/BasicForm.vue
+++ b/src/components/Form/src/BasicForm.vue
@@ -17,6 +17,7 @@
:allDefaultValues="defaultValueRef"
:formModel="formModel"
:setFormModel="setFormModel"
+ :validateFields="validateFields"
>
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
@@ -241,11 +242,11 @@
function setFormModel(key: string, value: any) {
formModel[key] = value;
- const { validateTrigger } = unref(getBindValue);
- if (!validateTrigger || validateTrigger === 'change') {
- validateFields([key]).catch((_) => {});
- }
- emit('field-value-change', key, value);
+ // const { validateTrigger } = unref(getBindValue);
+ // if (!validateTrigger || validateTrigger === 'change') {
+ // validateFields([key]).catch((_) => {});
+ // }
+ // emit('field-value-change', key, value);
}
function handleEnterPress(e: KeyboardEvent) {
diff --git a/src/components/Form/src/components/FormItem.vue b/src/components/Form/src/components/FormItem.vue
index d348dfc..dfa3bc7 100644
--- a/src/components/Form/src/components/FormItem.vue
+++ b/src/components/Form/src/components/FormItem.vue
@@ -13,6 +13,7 @@
import { cloneDeep, upperFirst } from 'lodash-es';
import { useItemLabelWidth } from '../hooks/useLabelWidth';
import { useI18n } from '/@/hooks/web/useI18n';
+ import { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
export default defineComponent({
name: 'BasicFormItem',
@@ -38,6 +39,12 @@
type: Function as PropType<(key: string, value: any) => void>,
default: null,
},
+ validateFields: {
+ type: Function as PropType<
+ (nameList?: NamePath[] | undefined, options?: ValidateOptions) => Promise<any>
+ >,
+ default: null,
+ },
tableAction: {
type: Object as PropType<TableActionType>,
},
@@ -226,6 +233,7 @@
rules[characterInx].message ||
t('component.form.maxTip', [rules[characterInx].max] as Recordable);
}
+ rules.forEach((item) => !item.trigger && (item.trigger = 'change'));
return rules;
}
@@ -252,6 +260,10 @@
const target = e ? e.target : null;
const value = target ? (isCheck ? target.checked : target.value) : e;
props.setFormModel(field, value);
+ props.validateFields([field], { triggerName: 'change' }).catch((_) => {});
+ },
+ onBlur: () => {
+ props.validateFields([field], { triggerName: 'blur' }).catch((_) => {});
},
};
const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;
diff --git a/src/components/Form/src/hooks/useForm.ts b/src/components/Form/src/hooks/useForm.ts
index d026d69..14ebc3e 100644
--- a/src/components/Form/src/hooks/useForm.ts
+++ b/src/components/Form/src/hooks/useForm.ts
@@ -1,12 +1,15 @@
import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form';
-import type { NamePath } from 'ant-design-vue/lib/form/interface';
+import type { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
import type { DynamicProps } from '/#/utils';
import { ref, onUnmounted, unref, nextTick, watch } from 'vue';
import { isProdMode } from '/@/utils/env';
import { error } from '/@/utils/log';
import { getDynamicProps } from '/@/utils';
-export declare type ValidateFields = (nameList?: NamePath[]) => Promise<Recordable>;
+export declare type ValidateFields = (
+ nameList?: NamePath[],
+ options?: ValidateOptions,
+) => Promise<Recordable>;
type Props = Partial<DynamicProps<FormProps>>;
@@ -112,9 +115,12 @@ export function useForm(props?: Props): UseFormReturnType {
return form.validate(nameList);
},
- validateFields: async (nameList?: NamePath[]): Promise<Recordable> => {
+ validateFields: async (
+ nameList?: NamePath[],
+ options?: ValidateOptions,
+ ): Promise<Recordable> => {
const form = await getForm();
- return form.validateFields(nameList);
+ return form.validateFields(nameList, options);
},
};
diff --git a/src/components/Form/src/hooks/useFormEvents.ts b/src/components/Form/src/hooks/useFormEvents.ts
index 531bb0d..906608a 100644
--- a/src/components/Form/src/hooks/useFormEvents.ts
+++ b/src/components/Form/src/hooks/useFormEvents.ts
@@ -1,6 +1,6 @@
import type { ComputedRef, Ref } from 'vue';
import type { FormProps, FormSchema, FormActionType } from '../types/form';
-import type { NamePath } from 'ant-design-vue/lib/form/interface';
+import type { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
import { unref, toRaw, nextTick } from 'vue';
import { isArray, isFunction, isObject, isString, isNullOrUnDef, isDef } from '/@/utils/is';
import { deepMerge } from '/@/utils';
@@ -258,8 +258,8 @@ export function useFormEvents({
});
}
- async function validateFields(nameList?: NamePath[] | undefined) {
- return unref(formElRef)?.validateFields(nameList);
+ async function validateFields(nameList?: NamePath[] | undefined, options?: ValidateOptions) {
+ return unref(formElRef)?.validateFields(nameList, options);
}
async function validate(nameList?: NamePath[] | undefined) {
diff --git a/src/components/Form/src/types/form.ts b/src/components/Form/src/types/form.ts
index 2c77228..8311236 100644
--- a/src/components/Form/src/types/form.ts
+++ b/src/components/Form/src/types/form.ts
@@ -1,4 +1,4 @@
-import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface';
+import type { NamePath, RuleObject, ValidateOptions } from 'ant-design-vue/lib/form/interface';
import type { VNode } from 'vue';
import type { ButtonProps as AntdButtonProps } from '/@/components/Button';
import type { FormItem } from './formItem';
@@ -39,7 +39,7 @@ export interface FormActionType {
prefixField: string | undefined,
first?: boolean | undefined,
) => Promise<void>;
- validateFields: (nameList?: NamePath[]) => Promise<any>;
+ validateFields: (nameList?: NamePath[], options?: ValidateOptions) => Promise<any>;
validate: (nameList?: NamePath[]) => Promise<any>;
scrollToField: (name: NamePath, options?: ScrollOptions) => Promise<void>;
}
最新版代码,根据大佬的说话修改的,不知道有没有错。。。
- form.ts
为validateFields方法添加ValidateOptions
import type { NamePath, RuleObject, ValidateOptions } from 'ant-design-vue/lib/form/interface';
validateFields: (nameList?: NamePath[], options?: ValidateOptions) => Promise
- useForm.ts
为validateFields方法添加ValidateOptions
import type { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface'; validateFields: async ( nameList?: NamePath[], options?: ValidateOptions ): Promise<Recordable> => { const form = await getForm(); return form.validateFields(nameList, options); },
- useFormEvents.ts
为validateFields方法添加ValidateOptions
import type { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface'; // 这个不知道是否也需要更改 export declare type ValidateFields = ( nameList?: NamePath[], options?: ValidateOptions, ) => Promise<Recordable>; async function validateFields(nameList?: NamePath[], options?: ValidateOptions | undefined) { return unref(formElRef)?.validateFields(nameList, options); }
- BasicForm.vue
FormItem中添加validateFields
<FormItem :tableAction="tableAction" :formActionType="formActionType" :schema="schema" :formProps="getProps" :allDefaultValues="defaultValueRef" :formModel="formModel" :setFormModel="setFormModel" :validateFields="validateFields" >
注释setFormModel
function setFormModel(key: string, value: any) { formModel[key] = value; // const { validateTrigger } = unref(getBindValue); // if (!validateTrigger || validateTrigger === 'change') { // validateFields([key]).catch((_) => {}); // } }
- FormItem.vue
props中添加方法validateFields
import type { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
validateFields: {
type: Function as PropType<
(nameList?: NamePath[], options?: ValidateOptions) => Promise
handleRules方法中添加
rules.forEach((item) => !item.trigger && (item.trigger = 'change')); return rules;
renderComponent方法中on添加onBlur方法
const on = { [eventKey]: (...args: Nullable<Recordable>[]) => { const [e] = args; if (propsData[eventKey]) { propsDataeventKey; } const target = e ? e.target : null; const value = target ? (isCheck ? target.checked : target.value) : e; props.setFormModel(field, value); // 这个也要添加 props.validateFields([field], { triggerName: 'change' }).catch(() => {}); }, onBlur: () => { props.validateFields([field], { triggerName: 'blur' }).catch(() => {}); }, };
为什么我用这个方法,会触发两次呢