-
Notifications
You must be signed in to change notification settings - Fork 8.9k
fix(form-ui): 动态表单定义下重置时恢复错误的默认值 #8351
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
Open
xueyitt
wants to merge
1
commit into
vbenjs:main
Choose a base branch
from
xueyitt:feat/2026090101
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+128
−55
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import type { ZodType } from 'zod'; | ||
|
|
||
| import type { FormSchemaRuleType } from './types'; | ||
|
|
||
| import { toRaw } from 'vue'; | ||
|
|
||
| import { isString, mergeWithArrayOverride, set } from '@vben-core/shared/utils'; | ||
|
|
||
| import { object, ZodIntersection, ZodNumber, ZodObject, ZodString } from 'zod'; | ||
| import { getDefaultsForSchema } from 'zod-defaults'; | ||
|
|
||
| /** 仅依赖计算默认值所需的最小字段结构,兼容任意泛型表单定义 */ | ||
| interface SchemaLike { | ||
| defaultValue?: any; | ||
| fieldName: string; | ||
| rules?: FormSchemaRuleType; | ||
| } | ||
|
|
||
| /** | ||
| * 根据表单定义计算默认值。 | ||
| * | ||
| * 优先取字段显式声明的默认值;未声明时,尝试从校验规则中推断。 | ||
| * 动态表单在定义变化后需重新计算,否则重置时会恢复到挂载时的旧默认值 | ||
| */ | ||
| export function generateSchemaDefaultValues( | ||
| schema: readonly SchemaLike[] = [], | ||
| ): Record<string, any> { | ||
| const initialValues: Record<string, any> = {}; | ||
|
|
||
| const zodObject: Record<string, ZodType> = {}; | ||
| (schema || []).forEach((item) => { | ||
| if (Reflect.has(item, 'defaultValue')) { | ||
| set(initialValues, item.fieldName, item.defaultValue); | ||
| } else if (item.rules && !isString(item.rules)) { | ||
| // 检查规则是否适合提取默认值 | ||
| const rawRules = toRaw(item.rules); | ||
| const customDefaultValue = getCustomDefaultValue(rawRules); | ||
| zodObject[item.fieldName] = rawRules; | ||
| if (customDefaultValue !== undefined) { | ||
| initialValues[item.fieldName] = customDefaultValue; | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| const schemaInitialValues = getDefaultsForSchema(object(zodObject)); | ||
|
|
||
| const zodDefaults: Record<string, any> = {}; | ||
| for (const key in schemaInitialValues) { | ||
| set(zodDefaults, key, schemaInitialValues[key]); | ||
| } | ||
| return mergeWithArrayOverride(initialValues, zodDefaults); | ||
| } | ||
|
|
||
| /** 从校验规则中推断默认值 */ | ||
| function getCustomDefaultValue(rule: any): any { | ||
| rule = toRaw(rule); | ||
| if (rule instanceof ZodString) { | ||
| return ''; // 默认为空字符串 | ||
| } else if (rule instanceof ZodNumber) { | ||
| return null; // 默认为 null(避免显示 0) | ||
| } else if (rule instanceof ZodObject) { | ||
| // 递归提取嵌套对象的默认值 | ||
| const defaultValues: Record<string, any> = {}; | ||
| for (const [key, valueSchema] of Object.entries(rule.shape)) { | ||
| defaultValues[key] = getCustomDefaultValue(valueSchema); | ||
| } | ||
| return defaultValues; | ||
| } else if (rule instanceof ZodIntersection) { | ||
| return getDefaultsForSchema(rule); | ||
| } else { | ||
| return undefined; // 其他类型不提供默认值 | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: vbenjs/vue-vben-admin
Length of output: 50378
🏁 Script executed:
Repository: vbenjs/vue-vben-admin
Length of output: 16479
🏁 Script executed:
Repository: vbenjs/vue-vben-admin
Length of output: 9251
🌐 Web query:
defu createDefu source null undefined first defined value merge precedence💡 Result:
In defu, null and undefined values in the source object are explicitly skipped during the merge process [1][2][3]. This means that if a source property is null or undefined, defu will ignore it and retain the existing value from the defaults object [3][4]. This behavior is hardcoded into the library's core algorithm: before performing any custom merging or standard assignment, defu checks if the source value is nullish and, if so, immediately continues to the next key [3][5]. Because of this design, you cannot use createDefu to "opt-in" to processing null or undefined values, as the nullish check occurs before the merger callback is ever invoked [3]. If your requirements involve preserving or explicitly setting null or undefined values, the official documentation recommends using alternatives such as defaults-deep, omit-deep, or lodash.defaultsdeep [1][6][4].
Citations:
🏁 Script executed:
Repository: vbenjs/vue-vben-admin
Length of output: 1422
🌐 Web query:
zod-defaults getDefaultsForSchema z.string().default source💡 Result:
Zod does not provide a built-in, public API to retrieve default values from a schema [1][2]. The library maintainers have intentionally avoided adding this feature, suggesting that users can achieve this by calling schema.parse(undefined) if the schema supports it, or by using community-provided utility patterns [3][4][1]. For scenarios where you need to extract defaults programmatically, you must access Zod's internal properties. While Zod's internal structure can change, the community-standard approach—often used in packages like zod-defaults or
@zod-utils/core—involves checking if a field is an instance of z.ZodDefault and accessing its underlying definition [5][6][3][2]. An example implementation for a flat Zod object schema is as follows: function getDefaults(schema) { return Object.fromEntries( Object.entries(schema.shape).map(([key, value]) => { // Accessing internal _def to retrieve the default value if (value instanceof z.ZodDefault) { return [key, typeof value._def.defaultValue === 'function'? value._def.defaultValue: value._def.defaultValue]; } return [key, undefined]; })); } Important Considerations: 1. Stability: Accessing _def relies on Zod's internal structure, which is not guaranteed to remain stable across versions [2]. 2. Complexity: If you need to handle nested objects, optional fields, or unions, the logic must be expanded recursively, which is why many developers prefer using established third-party libraries like@zod-utils/coreor zod-defaults rather than maintaining their own implementation [6][4][2][7]. 3. Zod 4 Changes: Note that Zod 4 introduced changes to how.default works for objects compared to Zod 3, which may impact how your recursive extraction logic needs to be structured [4].Citations:
🏁 Script executed:
Repository: vbenjs/vue-vben-admin
Length of output: 6931
Preserve explicit
nullvalues and prioritize Zod defaults over inferred fallbacks.mergeWithArrayOverrideusesdefu6.1.7, which skips nullish values and preserves the first defined value. Thus,defaultValue: nullis omitted, and the inferredZodStringfallback''overrides a Zod.default()value during initialization or reset.Use this precedence: explicit
defaultValue, Zod.default(), then inferred type fallback. Add regressions for both cases.🤖 Prompt for AI Agents