This commit is contained in:
xingyu4j
2025-11-24 11:47:31 +08:00
185 changed files with 2651 additions and 939 deletions

View File

@@ -47,7 +47,7 @@ async function handleSubmit(e: Event) {
return;
}
const values = toRaw(await props.formApi.getValues());
const values = toRaw(await props.formApi.getValues()) ?? {};
await props.handleSubmit?.(values);
}
@@ -56,7 +56,7 @@ async function handleReset(e: Event) {
e?.stopPropagation();
const props = unref(rootProps);
const values = toRaw(await props.formApi?.getValues());
const values = toRaw(await props.formApi?.getValues()) ?? {};
if (isFunction(props.handleReset)) {
await props.handleReset?.(values);

View File

@@ -36,6 +36,7 @@ function getDefaultState(): VbenFormProps {
handleReset: undefined,
handleSubmit: undefined,
handleValuesChange: undefined,
handleCollapsedChange: undefined,
layout: 'horizontal',
resetButtonOptions: {},
schema: [],

View File

@@ -341,7 +341,7 @@ onUnmounted(() => {
:is="FieldComponent"
ref="fieldComponentRef"
:class="{
'border-destructive focus:border-destructive hover:border-destructive/80 focus:shadow-[0_0_0_2px_rgba(255,38,5,0.06)]':
'border-destructive hover:border-destructive/80 focus:border-destructive focus:shadow-[0_0_0_2px_rgba(255,38,5,0.06)]':
isInValid,
}"
v-bind="createComponentProps(slotProps)"
@@ -369,7 +369,7 @@ onUnmounted(() => {
<CircleAlert
:class="
cn(
'text-foreground/80 hover:text-foreground inline-flex size-5 cursor-pointer',
'inline-flex size-5 cursor-pointer text-foreground/80 hover:text-foreground',
)
"
/>

View File

@@ -21,7 +21,7 @@ const props = defineProps<Props>();
<template>
<FormLabel :class="cn('flex items-center', props.class)">
<span v-if="required" class="text-destructive mr-[2px]">*</span>
<span v-if="required" class="mr-[2px] text-destructive">*</span>
<slot></slot>
<VbenHelpTooltip v-if="help" trigger-class="size-3.5 ml-1">
<VbenRenderContent :content="help" />

View File

@@ -381,6 +381,10 @@ export interface VbenFormProps<
* 表单字段映射
*/
fieldMappingTime?: FieldMappingTime;
/**
* 表单收起展开状态变化回调
*/
handleCollapsedChange?: (collapsed: boolean) => void;
/**
* 表单重置回调
*/

View File

@@ -13,7 +13,7 @@ import { useForm } from 'vee-validate';
import { object, ZodIntersection, ZodNumber, ZodObject, ZodString } from 'zod';
import { getDefaultsForSchema } from 'zod-defaults';
type ExtendFormProps = VbenFormProps & { formApi: ExtendedFormApi };
type ExtendFormProps = VbenFormProps & { formApi?: ExtendedFormApi };
export const [injectFormProps, provideFormProps] =
createContext<[ComputedRef<ExtendFormProps> | ExtendFormProps, FormActions]>(

View File

@@ -40,7 +40,9 @@ const { delegatedSlots, form } = useFormInitial(props);
provideFormProps([props, form]);
const handleUpdateCollapsed = (value: boolean) => {
currentCollapsed.value = !!value;
currentCollapsed.value = value;
// 触发收起展开状态变化回调
props.handleCollapsedChange?.(value);
};
watchEffect(() => {

View File

@@ -25,7 +25,7 @@ import {
} from './use-form-context';
// 通过 extends 会导致热更新卡死,所以重复写了一遍
interface Props extends VbenFormProps {
formApi: ExtendedFormApi;
formApi?: ExtendedFormApi;
}
const props = defineProps<Props>();
@@ -44,11 +44,13 @@ provideComponentRefMap(componentRefMap);
props.formApi?.mount?.(form, componentRefMap);
const handleUpdateCollapsed = (value: boolean) => {
props.formApi?.setState({ collapsed: !!value });
props.formApi?.setState({ collapsed: value });
// 触发收起展开状态变化回调
forward.value.handleCollapsedChange?.(value);
};
function handleKeyDownEnter(event: KeyboardEvent) {
if (!state.value.submitOnEnter || !forward.value.formApi?.isMounted) {
if (!state?.value.submitOnEnter || !forward.value.formApi?.isMounted) {
return;
}
// 如果是 textarea 不阻止默认行为,否则会导致无法换行。
@@ -58,11 +60,11 @@ function handleKeyDownEnter(event: KeyboardEvent) {
}
event.preventDefault();
forward.value.formApi.validateAndSubmitForm();
forward.value.formApi?.validateAndSubmitForm();
}
const handleValuesChangeDebounced = useDebounceFn(async () => {
state.value.submitOnChange && forward.value.formApi?.validateAndSubmitForm();
state?.value.submitOnChange && forward.value.formApi?.validateAndSubmitForm();
}, 300);
const valuesCache: Recordable<any> = {};
@@ -74,7 +76,7 @@ onMounted(async () => {
() => form.values,
async (newVal) => {
if (forward.value.handleValuesChange) {
const fields = state.value.schema?.map((item) => {
const fields = state?.value.schema?.map((item) => {
return item.fieldName;
});
@@ -91,8 +93,9 @@ onMounted(async () => {
if (changedFields.length > 0) {
// 调用handleValuesChange回调传入所有表单值的深拷贝和变更的字段列表
const values = await forward.value.formApi?.getValues();
forward.value.handleValuesChange(
cloneDeep(await forward.value.formApi.getValues()),
cloneDeep(values ?? {}) as Record<string, any>,
changedFields,
);
}
@@ -109,7 +112,7 @@ onMounted(async () => {
<Form
@keydown.enter="handleKeyDownEnter"
v-bind="forward"
:collapsed="state.collapsed"
:collapsed="state?.collapsed"
:component-bind-event-map="COMPONENT_BIND_EVENT_MAP"
:component-map="COMPONENT_MAP"
:form="form"
@@ -126,7 +129,7 @@ onMounted(async () => {
<slot v-bind="slotProps">
<FormActions
v-if="forward.showDefaultActions"
:model-value="state.collapsed"
:model-value="state?.collapsed"
@update:model-value="handleUpdateCollapsed"
>
<template #reset-before="resetSlotProps">