vue-vben-admin icon indicating copy to clipboard operation
vue-vben-admin copied to clipboard

表单输入框设置blur验证失效

Open leizhiyou opened this issue 3 years ago • 7 comments

⚠️ 重要 ⚠️ 在进一步操作之前,请检查下列选项。如果您忽视此模板或者没有提供关键信息,您的 Issue 将直接被关闭

  • [√] 已阅读 文档.
  • [√] 确保您的代码已是最新或者所报告的 Bug 在最新版本中可以重现. (部分 Bug 可能已经在最近的代码中修复)
  • [√] 已在 Issues 中搜索了相关的关键词
  • [√] 不是 ant design vue 组件库的 Bug

描述 Bug

vben2.8版本中,表演输入框设置blur失去焦点进行校验,触发的是change事件校验,blur不生效

复现 Bug

LNE E8Q_()7M%LS{AG5KW M Z({`4NCY}N%CE44XKOXSHL1 如上图设置的是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的验证方法 image 3、将validateFields方法提供给FormItem组件 image 4、FormItem组件添加validateFields props属性 image 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) 及其版本:

leizhiyou avatar Mar 06 '22 05:03 leizhiyou

阔以 很完美

DreamBuddy avatar May 31 '22 09:05 DreamBuddy

perfect

xavieryang2011 avatar Jun 21 '22 07:06 xavieryang2011

坐等UPDATE

waney avatar Jul 20 '22 07:07 waney

打补丁: 新建文本文件 后缀改成 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>;
 }

DreamBuddy avatar Jul 20 '22 12:07 DreamBuddy

最新版代码,根据大佬的说话修改的,不知道有没有错。。。

  1. form.ts

为validateFields方法添加ValidateOptions

import type { NamePath, RuleObject, ValidateOptions } from 'ant-design-vue/lib/form/interface'; validateFields: (nameList?: NamePath[], options?: ValidateOptions) => Promise;

  1. 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); },

  1. 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); }

  1. 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((_) => {}); // } }

  1. 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 >, default: null, },

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(() => {}); }, };

a531267910 avatar Aug 13 '22 08:08 a531267910

为什么我用这个方法,会触发两次呢

wuwenmei-tal avatar Sep 20 '23 10:09 wuwenmei-tal