diff --git a/packages/@core/ui-kit/form-ui/__tests__/form-integration.test.ts b/packages/@core/ui-kit/form-ui/__tests__/form-integration.test.ts index bf4b2c520..34f7658d1 100644 --- a/packages/@core/ui-kit/form-ui/__tests__/form-integration.test.ts +++ b/packages/@core/ui-kit/form-ui/__tests__/form-integration.test.ts @@ -104,6 +104,37 @@ describe('useVbenForm integration', () => { }); }); + it('filters native zod formats from nested default extraction', async () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); + const [Form, formApi] = useVbenForm({ + schema: [ + { + component: TestInput, + fieldName: 'profile', + rules: z.object({ + email: z.email(), + address: z.object({ + ip: z.ipv4(), + backup: z.email().default('backup@example.com'), + }), + }), + }, + ], + showDefaultActions: false, + }); + const wrapper = mount(Form); + wrappers.push(wrapper); + await flushPromises(); + + expect(warn).not.toHaveBeenCalled(); + expect(await formApi.getValues()).toEqual({ + profile: { + address: { backup: 'backup@example.com', ip: '' }, + email: '', + }, + }); + }); + it('uses model updates as the primary channel and preserves empty strings', async () => { const validateValue = vi.fn(); const [Form, formApi] = useVbenForm({ diff --git a/packages/@core/ui-kit/form-ui/src/use-form-context.ts b/packages/@core/ui-kit/form-ui/src/use-form-context.ts index 7f2711c4c..48cd1d1f2 100644 --- a/packages/@core/ui-kit/form-ui/src/use-form-context.ts +++ b/packages/@core/ui-kit/form-ui/src/use-form-context.ts @@ -15,6 +15,7 @@ import { ZodNumber, ZodObject, ZodString, + string as zodString, ZodStringFormat, } from 'zod'; import { getDefaultsForSchema } from 'zod-defaults'; @@ -33,6 +34,42 @@ export const [injectFormProps, provideFormProps] = export const [injectComponentRefMap, provideComponentRefMap] = createContext>('ComponentRefMap'); +function normalizeSchemaForDefaults(rule: ZodType): ZodType { + const rawRule = toRaw(rule) as any; + + if (rawRule instanceof ZodStringFormat) { + return zodString(); + } + + if (rawRule instanceof ZodObject) { + const shape = Object.fromEntries( + Object.entries(rawRule.shape).map(([key, value]) => [ + key, + normalizeSchemaForDefaults(value as ZodType), + ]), + ); + return object(shape); + } + + if (rawRule instanceof ZodIntersection) { + const { left, right } = (rawRule as any).def; + return normalizeSchemaForDefaults(left as ZodType).and( + normalizeSchemaForDefaults(right as ZodType), + ); + } + + if (rawRule.constructor.name === 'ZodDefault') { + const inner = normalizeSchemaForDefaults(rawRule.unwrap()); + return inner.default(rawRule.def.defaultValue); + } + + if (rawRule.constructor.name === 'ZodPipe') { + return normalizeSchemaForDefaults(rawRule.in).pipe(rawRule.out); + } + + return rawRule; +} + export function useFormInitial( props: ComputedRef | VbenFormProps, ) { @@ -63,7 +100,7 @@ export function useFormInitial( // 检查规则是否适合提取默认值 const rawRules = toRaw(item.rules); const customDefaultValue = getCustomDefaultValue(rawRules); - zodObject[item.fieldName] = rawRules; + zodObject[item.fieldName] = normalizeSchemaForDefaults(rawRules); if (customDefaultValue !== undefined) { initialValues[item.fieldName] = customDefaultValue; } @@ -93,7 +130,7 @@ export function useFormInitial( } return defaultValues; } else if (rule instanceof ZodIntersection) { - return getDefaultsForSchema(rule); + return getDefaultsForSchema(normalizeSchemaForDefaults(rule) as any); } else { return undefined; // 其他类型不提供默认值 }