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

* feat: add examples of asynchronous form verification and verification time
pull/48/MERGE
Vben 2024-10-03 15:15:50 +08:00 committed by GitHub
parent 0cd865e211
commit f7016466ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 73 additions and 11 deletions

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import type { VbenButtonProps } from '@vben-core/shadcn-ui'; 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 { ZodTypeAny } from 'zod';
import type { FormApi } from './form-api'; import type { FormApi } from './form-api';
@ -33,6 +33,15 @@ export type FormItemClassType =
| (Record<never, never> & string) | (Record<never, never> & string)
| WrapperClassType; | WrapperClassType;
export type FormFieldOptions = Partial<
{
validateOnBlur?: boolean;
validateOnChange?: boolean;
validateOnInput?: boolean;
validateOnModelUpdate?: boolean;
} & FieldOptions
>;
export interface FormShape { export interface FormShape {
/** 默认值 */ /** 默认值 */
default?: any; default?: any;
@ -148,7 +157,7 @@ export interface FormCommonConfig {
* *
* @default {} * @default {}
*/ */
formFieldProps?: Partial<typeof Field>; formFieldProps?: FormFieldOptions;
/** /**
* *
* @default "" * @default ""

View File

@ -175,6 +175,47 @@ const [Form, formApi] = useVbenForm({
label: '密码', label: '密码',
rules: 'required', 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: '用户名已存在',
},
),
},
], ],
// 321 // 321
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3', wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',