mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-25 08:06:30 +08:00
feat: add scrollToFirstError to the form component
This commit is contained in:
@@ -48,13 +48,18 @@ const queryFormStyle = computed(() => {
|
||||
async function handleSubmit(e: Event) {
|
||||
e?.preventDefault();
|
||||
e?.stopPropagation();
|
||||
const { valid } = await form.validate();
|
||||
const props = unref(rootProps);
|
||||
if (!props.formApi) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { valid } = await props.formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const values = toRaw(await unref(rootProps).formApi?.getValues());
|
||||
await unref(rootProps).handleSubmit?.(values);
|
||||
const values = toRaw(await props.formApi.getValues());
|
||||
await props.handleSubmit?.(values);
|
||||
}
|
||||
|
||||
async function handleReset(e: Event) {
|
||||
|
@@ -39,6 +39,7 @@ function getDefaultState(): VbenFormProps {
|
||||
layout: 'horizontal',
|
||||
resetButtonOptions: {},
|
||||
schema: [],
|
||||
scrollToFirstError: false,
|
||||
showCollapseButton: false,
|
||||
showDefaultActions: true,
|
||||
submitButtonOptions: {},
|
||||
@@ -253,6 +254,41 @@ export class FormApi {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到第一个错误字段
|
||||
* @param errors 验证错误对象
|
||||
*/
|
||||
scrollToFirstError(errors: Record<string, any> | string) {
|
||||
// https://github.com/logaretm/vee-validate/discussions/3835
|
||||
const firstErrorFieldName =
|
||||
typeof errors === 'string' ? errors : Object.keys(errors)[0];
|
||||
|
||||
if (!firstErrorFieldName) {
|
||||
return;
|
||||
}
|
||||
|
||||
let el = document.querySelector(
|
||||
`[name="${firstErrorFieldName}"]`,
|
||||
) as HTMLElement;
|
||||
|
||||
// 如果通过 name 属性找不到,尝试通过组件引用查找, 正常情况下不会走到这,怕哪天 vee-validate 改了 name 属性有个兜底的
|
||||
if (!el) {
|
||||
const componentRef = this.getFieldComponentRef(firstErrorFieldName);
|
||||
if (componentRef && componentRef.$el instanceof HTMLElement) {
|
||||
el = componentRef.$el;
|
||||
}
|
||||
}
|
||||
|
||||
if (el) {
|
||||
// 滚动到错误字段,添加一些偏移量以确保字段完全可见
|
||||
el.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
inline: 'nearest',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async setFieldValue(field: string, value: any, shouldValidate?: boolean) {
|
||||
const form = await this.getForm();
|
||||
form.setFieldValue(field, value, shouldValidate);
|
||||
@@ -377,14 +413,21 @@ export class FormApi {
|
||||
|
||||
if (Object.keys(validateResult?.errors ?? {}).length > 0) {
|
||||
console.error('validate error', validateResult?.errors);
|
||||
|
||||
if (this.state?.scrollToFirstError) {
|
||||
this.scrollToFirstError(validateResult.errors);
|
||||
}
|
||||
}
|
||||
return validateResult;
|
||||
}
|
||||
|
||||
async validateAndSubmitForm() {
|
||||
const form = await this.getForm();
|
||||
const { valid } = await form.validate();
|
||||
const { valid, errors } = await form.validate();
|
||||
if (!valid) {
|
||||
if (this.state?.scrollToFirstError) {
|
||||
this.scrollToFirstError(errors);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return await this.submitForm();
|
||||
@@ -396,6 +439,10 @@ export class FormApi {
|
||||
|
||||
if (Object.keys(validateResult?.errors ?? {}).length > 0) {
|
||||
console.error('validate error', validateResult?.errors);
|
||||
|
||||
if (this.state?.scrollToFirstError) {
|
||||
this.scrollToFirstError(fieldName);
|
||||
}
|
||||
}
|
||||
return validateResult;
|
||||
}
|
||||
|
@@ -387,6 +387,12 @@ export interface VbenFormProps<
|
||||
*/
|
||||
resetButtonOptions?: ActionButtonOptions;
|
||||
|
||||
/**
|
||||
* 验证失败时是否自动滚动到第一个错误字段
|
||||
* @default true
|
||||
*/
|
||||
scrollToFirstError?: boolean;
|
||||
|
||||
/**
|
||||
* 是否显示默认操作按钮
|
||||
* @default true
|
||||
|
Reference in New Issue
Block a user