-
Notifications
You must be signed in to change notification settings - Fork 8.2k
fix: form-api解析fieldMappingTime结果时支持多级路径转换 #6720
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
base: main
Are you sure you want to change the base?
Conversation
|
WalkthroughImplements nested path handling for time field mapping in form-api, replacing direct property mutations with helpers for set/delete on dot-notated keys. Adds guards and refactors formatting branches. Updates the playground example to include a second time range picker and corresponding fieldMappingTime entries. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Form as Form UI
participant API as form-api.processFieldMappingTime
participant Helpers as {setNestedValue, deleteNestedProperty}
participant Values as values
User->>Form: Submit
Form->>API: process(values, fieldMappingTime)
API->>API: Guard: field/startTimeKey/endTimeKey
API->>Values: timeRange = values[field]
alt timeRange is null
API->>Helpers: deleteNestedProperty(values, startTimeKey)
API->>Helpers: deleteNestedProperty(values, endTimeKey)
else timeRange falsy
API->>Helpers: deleteNestedProperty(values, field)
API-->>Form: return
else valid [startTime, endTime]
alt format === null
API->>Helpers: setNestedValue(values, startTimeKey, startTime)
API->>Helpers: setNestedValue(values, endTimeKey, endTime)
else format is function
API->>Helpers: setNestedValue(values, startTimeKey, format(startTime))
API->>Helpers: setNestedValue(values, endTimeKey, format(endTime))
else format is string/array
API->>Helpers: setNestedValue(values, startTimeKey, startFmt(startTime))
API->>Helpers: setNestedValue(values, endTimeKey, endFmt(endTime))
end
API->>Helpers: deleteNestedProperty(values, field)
end
API-->>Form: mapped values
Form-->>User: Submit payload
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
} | ||
const lastKey = keys[keys.length - 1]; | ||
if (lastKey !== undefined) { | ||
target[lastKey] = value; |
Check warning
Code scanning / CodeQL
Prototype-polluting function Medium
here
target
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.
Actionable comments posted: 1
🧹 Nitpick comments (4)
packages/@core/ui-kit/form-ui/src/form-api.ts (2)
613-623
: Avoid writing undefined; delete keys instead to keep payload cleanPrevents sending { startTime: undefined } / { endTime: undefined } to APIs.
- setNestedValue( - values, - startTimeKey, - startTime ? formatDate(startTime, startTimeFormat) : undefined, - ); - setNestedValue( - values, - endTimeKey, - endTime ? formatDate(endTime, endTimeFormat) : undefined, - ); + const startStr = + startTime != null ? formatDate(startTime, startTimeFormat) : undefined; + const endStr = + endTime != null ? formatDate(endTime, endTimeFormat) : undefined; + if (startStr === undefined) { + deleteNestedProperty(values, startTimeKey); + } else { + setNestedValue(values, startTimeKey, startStr); + } + if (endStr === undefined) { + deleteNestedProperty(values, endTimeKey); + } else { + setNestedValue(values, endTimeKey, endStr); + }
515-579
: Hoist helpers to avoid per-iteration redefinitionMinor perf/readability: define setNestedValue/deleteNestedProperty once within handleRangeTimeValue scope (or as private methods).
playground/src/views/examples/form/basic.vue (2)
46-46
: Improve readability of fieldMappingTimeSplit into multiple lines.
- fieldMappingTime: [['rangePicker', ['startTime', 'endTime'], 'YYYY-MM-DD'], ['rangePicker2', ['createTimeQuery.startTime', 'createTimeQuery.endTime'], 'YYYY-MM-DD HH:mm:ss']], + fieldMappingTime: [ + ['rangePicker', ['startTime', 'endTime'], 'YYYY-MM-DD'], + [ + 'rangePicker2', + ['createTimeQuery.startTime', 'createTimeQuery.endTime'], + 'YYYY-MM-DD HH:mm:ss', + ], + ],
406-432
: Optional: seed rangePicker2 in the demo to showcase nested mapping outputAdd an example value so submit output includes createTimeQuery.startTime/endTime.
// add alongside existing rangePicker seed rangePicker2: [dayjs('2022-01-03 08:00:00'), dayjs('2022-01-04 18:30:00')],
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/@core/ui-kit/form-ui/src/form-api.ts
(1 hunks)playground/src/views/examples/form/basic.vue
(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/@core/ui-kit/form-ui/src/form-api.ts (1)
packages/@core/base/shared/src/utils/date.ts (1)
formatDate
(3-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Check (windows-latest)
- GitHub Check: Check (ubuntu-latest)
- GitHub Check: Lint (windows-latest)
- GitHub Check: Test (windows-latest)
- GitHub Check: Lint (ubuntu-latest)
- GitHub Check: post-update (ubuntu-latest)
- GitHub Check: post-update (windows-latest)
🔇 Additional comments (4)
packages/@core/ui-kit/form-ui/src/form-api.ts (3)
581-585
: Good safety guard for required mapping partsThe early return on missing field/start/end keys is correct.
586-595
: Confirm null/undefined clearing semanticsYou clear target keys when values[field] === null, but for undefined you only remove the source field. Validate this matches API expectations; otherwise consider clearing start/end keys for both null and undefined.
596-599
: LGTM: robust shape checkArray and length check prevents partial ranges from leaking.
playground/src/views/examples/form/basic.vue (1)
285-294
: RangePicker2 example looks goodConfig aligns with nested mapping and formatting.
const setNestedValue = ( | ||
obj: Record<string, any>, | ||
path: string, | ||
value: any, | ||
): void => { | ||
const keys = path.split('.'); | ||
if (keys.length === 1) { | ||
// 单级路径直接赋值 | ||
obj[path] = value; | ||
} else { | ||
// 多级路径创建嵌套对象 | ||
let target: Record<string, any> = obj; | ||
for (let i = 0; i < keys.length - 1; i++) { | ||
const key = keys[i]; | ||
if (key !== undefined) { | ||
// 添加类型检查 | ||
if ( | ||
!(key in target) || | ||
typeof target[key] !== 'object' || | ||
target[key] === null | ||
) { | ||
target[key] = {}; | ||
} | ||
target = target[key] as Record<string, any>; | ||
} | ||
} | ||
const lastKey = keys[keys.length - 1]; | ||
if (lastKey !== undefined) { | ||
target[lastKey] = value; | ||
} | ||
} | ||
}; | ||
|
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.
Harden nested path helpers against prototype pollution and invalid segments
Guard against "proto", "prototype", "constructor" and empty segments. This also addresses the CodeQL warning.
Apply this diff:
- const setNestedValue = (
+ const setNestedValue = (
obj: Record<string, any>,
path: string,
value: any,
): void => {
- const keys = path.split('.');
+ const keys = path.split('.').filter(Boolean);
+ // reject unsafe or empty paths
+ if (
+ keys.length === 0 ||
+ keys.some((k) => k === '__proto__' || k === 'prototype' || k === 'constructor')
+ ) {
+ console.warn('[form-api] Ignoring unsafe path:', path);
+ return;
+ }
if (keys.length === 1) {
// 单级路径直接赋值
obj[path] = value;
} else {
// 多级路径创建嵌套对象
let target: Record<string, any> = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
if (key !== undefined) {
// 添加类型检查
- if (
- !(key in target) ||
- typeof target[key] !== 'object' ||
- target[key] === null
- ) {
+ if (
+ !(key in target) ||
+ typeof target[key] !== 'object' ||
+ target[key] === null ||
+ Array.isArray(target[key])
+ ) {
target[key] = {};
}
target = target[key] as Record<string, any>;
}
}
const lastKey = keys[keys.length - 1];
if (lastKey !== undefined) {
target[lastKey] = value;
}
}
};
// 处理多级路径的删除函数
- const deleteNestedProperty = (
+ const deleteNestedProperty = (
obj: Record<string, any>,
path: string,
): void => {
- const keys = path.split('.');
+ const keys = path.split('.').filter(Boolean);
+ // reject unsafe or empty paths
+ if (
+ keys.length === 0 ||
+ keys.some((k) => k === '__proto__' || k === 'prototype' || k === 'constructor')
+ ) {
+ console.warn('[form-api] Ignoring unsafe path:', path);
+ return;
+ }
if (keys.length === 1) {
// 单级路径直接删除
Reflect.deleteProperty(obj, path);
} else {
// 多级路径导航到目标对象并删除属性
let target: Record<string, any> = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
if (key !== undefined) {
// 添加类型检查
if (
!(key in target) ||
typeof target[key] !== 'object' ||
target[key] === null
) {
return;
}
target = target[key] as Record<string, any>;
}
}
const lastKey = keys[keys.length - 1];
if (lastKey !== undefined) {
Reflect.deleteProperty(target, lastKey);
}
}
};
Also applies to: 549-579
1、form-api解析fieldMappingTime结果时支持多级路径转换
2、增加demo示例
3、数据转换示例:
fieldMappingTime: [
[
'createTimeRange',
['createTimeQuery.beginTime', 'createTimeQuery.endTime'],
'YYYY-MM-DD HH:mm:ss',
]
] 转换示例 createTimeQuery: {
beginTime: '2025-09-24 13:41:30',
endTime: '2025-09-28 13:41:31',
}
Summary by CodeRabbit
New Features
Documentation