From f7016466eeaefee95e93195b5de88043578b6a86 Mon Sep 17 00:00:00 2001 From: Vben Date: Thu, 3 Oct 2024 15:15:50 +0800 Subject: [PATCH] feat: add examples of asynchronous form verification and verification time (#4559) * feat: add examples of asynchronous form verification and verification time --- docs/src/components/common-ui/vben-form.md | 28 +++++++++---- .../form-ui/src/form-render/form-field.vue | 2 +- packages/@core/ui-kit/form-ui/src/types.ts | 13 +++++- playground/src/views/examples/form/rules.vue | 41 +++++++++++++++++++ 4 files changed, 73 insertions(+), 11 deletions(-) diff --git a/docs/src/components/common-ui/vben-form.md b/docs/src/components/common-ui/vben-form.md index 0206dbee..5b4c12ce 100644 --- a/docs/src/components/common-ui/vben-form.md +++ b/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 = ( + component: T, + type: 'input' | 'select', +) => { + return (props: any, { attrs, slots }: Omit) => { + const placeholder = props?.placeholder || $t(`placeholder.${type}`); + return h(component, { ...props, ...attrs, placeholder }, slots); + }; +}; + // 初始化表单组件,并注册到form组件内部 setupVbenForm({ components: { @@ -100,26 +111,27 @@ setupVbenForm({ 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', diff --git a/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue b/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue index 53ceff61..c116d49c 100644 --- a/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue +++ b/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), }; }); diff --git a/packages/@core/ui-kit/form-ui/src/types.ts b/packages/@core/ui-kit/form-ui/src/types.ts index d3ba250d..3d5fb10a 100644 --- a/packages/@core/ui-kit/form-ui/src/types.ts +++ b/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 & 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; + formFieldProps?: FormFieldOptions; /** * 所有表单项的栅格布局 * @default "" diff --git a/playground/src/views/examples/form/rules.vue b/playground/src/views/examples/form/rules.vue index 20dd88ee..f4558613 100644 --- a/playground/src/views/examples/form/rules.vue +++ b/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 => { + 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',