feat: add examples of asynchronous form verification and verification time (#4559)
* feat: add examples of asynchronous form verification and verification timepull/48/MERGE
parent
0cd865e211
commit
f7016466ee
|
@ -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',
|
||||||
|
|
|
@ -191,7 +191,7 @@ const fieldProps = computed(() => {
|
||||||
keepValue: true,
|
keepValue: true,
|
||||||
label,
|
label,
|
||||||
...(rules ? { rules } : {}),
|
...(rules ? { rules } : {}),
|
||||||
...formFieldProps,
|
...(formFieldProps as Record<string, any>),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
|
@ -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: '用户名已存在',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||||
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
|
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
|
||||||
|
|
Loading…
Reference in New Issue