Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions docs/src/components/common-ui/vben-form.md
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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: {
Expand All @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ const fieldProps = computed(() => {
keepValue: true,
label,
...(rules ? { rules } : {}),
...formFieldProps,
...(formFieldProps as Record<string, any>),
};
});

Expand Down
13 changes: 11 additions & 2 deletions packages/@core/ui-kit/form-ui/src/types.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -148,7 +157,7 @@ export interface FormCommonConfig {
* 所有表单项的控件样式
* @default {}
*/
formFieldProps?: Partial<typeof Field>;
formFieldProps?: FormFieldOptions;
/**
* 所有表单项的栅格布局
* @default ""
Expand Down
41 changes: 41 additions & 0 deletions playground/src/views/examples/form/rules.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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;
},
anncwb marked this conversation as resolved.
Show resolved Hide resolved
{
message: '用户名已存在',
},
),
},
],
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
Expand Down