Преглед изворни кода

feat: add examples of asynchronous form verification and verification time (#4559)

* feat: add examples of asynchronous form verification and verification time
Vben пре 6 месеци
родитељ
комит
f7016466ee

+ 20 - 8
docs/src/components/common-ui/vben-form.md

@@ -31,6 +31,7 @@ import type {
   VbenFormProps,
 } from '@vben/common-ui';
 
+import type { Component, SetupContext } from 'vue';
 import { h } from 'vue';
 
 import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
@@ -84,6 +85,16 @@ export type FormComponentType =
   | 'Upload'
   | BaseFormComponentType;
 
+const withDefaultPlaceholder = <T extends Component>(
+  component: T,
+  type: 'input' | 'select',
+) => {
+  return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
+    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
+    return h(component, { ...props, ...attrs, placeholder }, slots);
+  };
+};
+
 // 初始化表单组件,并注册到form组件内部
 setupVbenForm<FormComponentType>({
   components: {
@@ -100,26 +111,27 @@ setupVbenForm<FormComponentType>({
       return h(Button, { ...props, attrs, type: 'primary' }, slots);
     },
     Divider,
-    Input,
-    InputNumber,
-    InputPassword,
-    Mentions,
+    Input: withDefaultPlaceholder(Input, 'input'),
+    InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
+    InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
+    Mentions: withDefaultPlaceholder(Mentions, 'input'),
     Radio,
     RadioGroup,
     RangePicker,
     Rate,
-    Select,
+    Select: withDefaultPlaceholder(Select, 'select'),
     Space,
     Switch,
-    Textarea,
+    Textarea: withDefaultPlaceholder(Textarea, 'input'),
     TimePicker,
-    TreeSelect,
+    TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
     Upload,
   },
   config: {
+    // 是否禁用onChange事件监听,naive ui组件库默认不需要监听onChange事件,否则会在控制台报错
+    disabledOnChangeListener: true,
     // ant design vue组件库默认都是 v-model:value
     baseModelPropName: 'value',
-
     // 一些组件是 v-model:checked 或者 v-model:fileList
     modelPropNameMap: {
       Checkbox: 'checked',

+ 1 - 1
packages/@core/ui-kit/form-ui/src/form-render/form-field.vue

@@ -191,7 +191,7 @@ const fieldProps = computed(() => {
     keepValue: true,
     label,
     ...(rules ? { rules } : {}),
-    ...formFieldProps,
+    ...(formFieldProps as Record<string, any>),
   };
 });
 

+ 11 - 2
packages/@core/ui-kit/form-ui/src/types.ts

@@ -1,5 +1,5 @@
 import type { VbenButtonProps } from '@vben-core/shadcn-ui';
-import type { Field, FormContext, GenericObject } from 'vee-validate';
+import type { FieldOptions, FormContext, GenericObject } from 'vee-validate';
 import type { ZodTypeAny } from 'zod';
 
 import type { FormApi } from './form-api';
@@ -33,6 +33,15 @@ export type FormItemClassType =
   | (Record<never, never> & string)
   | WrapperClassType;
 
+export type FormFieldOptions = Partial<
+  {
+    validateOnBlur?: boolean;
+    validateOnChange?: boolean;
+    validateOnInput?: boolean;
+    validateOnModelUpdate?: boolean;
+  } & FieldOptions
+>;
+
 export interface FormShape {
   /** 默认值 */
   default?: any;
@@ -148,7 +157,7 @@ export interface FormCommonConfig {
    * 所有表单项的控件样式
    * @default {}
    */
-  formFieldProps?: Partial<typeof Field>;
+  formFieldProps?: FormFieldOptions;
   /**
    * 所有表单项的栅格布局
    * @default ""

+ 41 - 0
playground/src/views/examples/form/rules.vue

@@ -175,6 +175,47 @@ const [Form, formApi] = useVbenForm({
       label: '密码',
       rules: 'required',
     },
+    {
+      component: 'Input',
+      componentProps: {
+        placeholder: '请输入',
+      },
+      fieldName: 'input-blur',
+      formFieldProps: {
+        validateOnChange: false,
+        validateOnModelUpdate: false,
+      },
+      help: 'blur时才会触发校验',
+      label: 'blur触发',
+      rules: 'required',
+    },
+    {
+      component: 'Input',
+      componentProps: {
+        placeholder: '请输入',
+      },
+      fieldName: 'input-async',
+      label: '异步校验',
+      rules: z
+        .string()
+        .min(3, '用户名至少需要3个字符')
+        .refine(
+          async (username) => {
+            // 假设这是一个异步函数,模拟检查用户名是否已存在
+            const checkUsernameExists = async (
+              username: string,
+            ): Promise<boolean> => {
+              await new Promise((resolve) => setTimeout(resolve, 1000));
+              return username === 'existingUser';
+            };
+            const exists = await checkUsernameExists(username);
+            return !exists;
+          },
+          {
+            message: '用户名已存在',
+          },
+        ),
+    },
   ],
   // 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
   wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',