Skip to content

Conversation

Passing-of-A-Dream
Copy link
Contributor

@Passing-of-A-Dream Passing-of-A-Dream commented Aug 20, 2025

fix: #6856 支持自定义日期选择器中年、月、日列的显示顺序

Summary by CodeRabbit

  • 新功能

    • DatePicker 新增 columns 属性与 DateColumnType,支持自定义列顺序与仅选择部分字段(缺失字段用当前/默认时间);首列可显示“现在”(TILL_NOW),启用后清空后续列。组件导出新增 YEAR/MONTH/DAY/HOUR/MINUTE/SECOND 常量。
  • 文档

    • 新增示例:自定义列顺序(月-日-年、时-分-日-月-年、仅时-分)。
  • 测试

    • 添加列控制单元测试,覆盖自定义顺序、部分列、精度限制与“至今”行为。
  • 注意

    • 若使用 columns,若干日期转换/生成接口新增 columns 与 precision 参数(向下兼容)。

Copy link

coderabbitai bot commented Aug 20, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

为 DatePicker 引入可配置列顺序与子集选择:新增 DateColumnType 常量与类型、DatePicker props 增加 columns?,并将该参数贯穿格式化/解析/列生成逻辑,按指定列序动态生成候选列并扩展 tillNow 首列行为;同步更新 demo 与测试。

Changes

Cohort / File(s) Change Summary
核心类型与动态列逻辑
src/components/date-picker/util.ts, src/components/date-picker/date-picker-date-utils.ts
新增列常量与导出类型 DateColumnType;添加 column→precision 映射与统一列排序/渲染逻辑;generateDatePickerColumns 新增 columns?: DateColumnType[] 参数并按指定列序生成候选列;convertStringArrayToDate 签名变更接收 columns?precision,处理缺失列以当前时间补全;为首列 TILL_NOW 支持追加“现在”选项并在选中时清空后续列;引入 dayjs isLeapYear 插件。
工具层透传与 API 改动
src/components/date-picker/date-picker-utils.ts
convertDateToStringArray / convertStringArrayToDate / generateDatePickerColumns 增加可选 columns 参数并在非 week/quarter 路径中向下透传;convertDateToStringArray 对空日期返回 [];按 columns 映射标准索引以提取子集。
组件层与导出
src/components/date-picker/date-picker.tsx, src/components/date-picker/index.ts
DatePickerProps 增加 columns?: DateColumnType[] 并把 columns 传递给相关工具与 Picker;默认导出通过 attachPropertiesToComponent 暴露 YEAR/MONTH/.../SECOND 等静态列常量并导出 DateColumnType 类型。
示例与测试
src/components/date-picker/demos/demo1.tsx, src/components/date-picker/tests/date-picker.test.tsx
新增“自定义列顺序”示例,展示不同列序与仅时分情形;新增测试覆盖自定义列顺序、部分列使用当前时间、精度限制与空 columns 的回退行为(使用假定时间控制)。
杂项导入/样式调整
src/components/date-picker/index.ts, src/components/date-picker/demos/demo1.tsx
调整部分 import 顺序与 CSS 引入位置;demo 中调整 React 导入顺序并解构列常量(仅代码组织/样式相关)。

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor 用户
  participant DatePicker
  participant Picker
  participant Utils as date-picker-utils
  participant DUtils as date-picker-date-utils

  用户->>DatePicker: 打开/确认 (precision, columns?, tillNow?)
  DatePicker->>Picker: 请求 columns 回调(selected, min, max, ..., columns)
  Picker->>Utils: generateDatePickerColumns(..., columns)
  Utils->>DUtils: 传入 columns -> 按列序/默认序生成候选列
  DUtils-->>Utils: 返回列数据(首列可能含 "now")
  Utils-->>Picker: 返回列数据
  用户->>Picker: 选择值 selected[]
  Picker->>Utils: convertStringArrayToDate(selected[], precision, columns)
  Utils->>DUtils: 按 columns 构造 Date(缺失列使用当前时间;首列 TILL_NOW 特殊处理)
  DUtils-->>Utils: 返回 Date
  Utils-->>Picker: 返回 Date
  Picker-->>DatePicker: 返回 Date
  DatePicker-->>用户: onConfirm(Date)

  rect rgba(200,230,201,0.12)
  note right of DUtils: 关键变化:支持自定义列序/子集;tillNow 在首列追加“现在”并能清空后续列
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

小兔排列又排行,
年月时分任意装,
若首列点下「现在」声,
其余列空随风扬,
demo 测试一起欢。 🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning 整体改动以实现列重排为主,但包含若干超出该功能目标或可能引发副作用的改动:多处对外/导出函数签名被修改(src/components/date-picker/date-picker-date-utils.ts 与 date-picker-utils.ts 中的 convert/generate 接口),全局引入并扩展了 dayjs 的 isLeapYear 插件,TILL_NOW 行为有增强,另有若干非功能性导入顺序调整,这些改动改变了公共接口或全局行为且未在描述中充分说明。 在合并前请把与“列重排”无直接关系或可独立验证的改动拆分为独立 PR(例如 dayjs 全局扩展、导入顺序或可选的行为增强),或者在本 PR 中明确说明这些改动的必要性并补充兼容性与迁移文档;对于已修改的公有函数签名应提供兼容层或恢复向后兼容调用路径,并在合并前完成完整回归测试与发布说明更新以降低对现有用户的影响。
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 标题“feat: date-picker 组件优化日期选择器,支持自定义日期字段顺序”简洁明确地描述了本次变更的核心意图(为 DatePicker 增加自定义字段顺序支持),符合 feat 前缀的约定且便于历史检索与审阅。
Linked Issues Check ✅ Passed 该 PR 直接实现了 issue #6856 的主要目标:新增 DateColumnType 与列常量、为 DatePicker 增加 columns prop,并把 columns 参数贯穿到数值转换与列生成逻辑(convert/parse/generate),同时补充了示例与测试来覆盖自定义列顺序与部分列场景,因此功能性要求已被满足。需要注意的是提交也修改了若干导出/公有函数的签名(如 generateDatePickerColumns、convertStringArrayToDate/convertDateToStringArray 等),这会影响兼容性,审查时应重点评估这些接口变更的影响范围。
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd37168 and 2b131b5.

📒 Files selected for processing (1)
  • src/components/date-picker/date-picker-date-utils.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/date-picker/date-picker-date-utils.ts
⏰ 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). (4)
  • GitHub Check: compressed-size
  • GitHub Check: size
  • GitHub Check: check
  • GitHub Check: build preview

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

github-actions bot commented Aug 20, 2025

Preview is ready

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Aug 20, 2025
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @Passing-of-A-Dream, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求为日期选择器组件引入了一项新功能,允许用户自定义年、月、日字段的显示顺序。这增强了组件的灵活性,使其能够适应不同的日期格式需求,从而提升了用户体验。

Highlights

  • 新功能: 为DatePicker和DatePickerView组件新增了fields属性。
  • 灵活性: 支持多种fields属性格式,包括数组形式(例如['month', 'day', 'year'])和字符串别名(如'YMD'、'MDY'、'DMY')。
  • 核心逻辑: 在date-picker-date-utils.ts中实现了重新排序日期列的核心逻辑。
  • 数据处理: 更新了日期转换工具函数(convertDateToStringArray和convertStringArrayToDate),以在自定义字段顺序下仍能正确解析和格式化日期。
  • 测试与示例: 新增了演示示例和单元测试,以验证此功能的正确性。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@dosubot dosubot bot added the feature label Aug 20, 2025
Copy link

pkg-pr-new bot commented Aug 20, 2025

npm i https://pkg.pr.new/ant-design/ant-design-mobile/antd-mobile@6940

commit: 3488ea9

Copy link

codecov bot commented Aug 20, 2025

Codecov Report

❌ Patch coverage is 98.83721% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.98%. Comparing base (44337a7) to head (3488ea9).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
...c/components/date-picker/date-picker-date-utils.ts 98.18% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6940      +/-   ##
==========================================
+ Coverage   92.90%   92.98%   +0.07%     
==========================================
  Files         336      336              
  Lines        7334     7357      +23     
  Branches     1847     1830      -17     
==========================================
+ Hits         6814     6841      +27     
- Misses        484      508      +24     
+ Partials       36        8      -28     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 优化了日期选择器,增加了对年、月、日列顺序的自定义支持,这是一个很棒的功能。代码整体实现得不错,并且补充了相应的演示和测试用例。

我发现了一些可以改进的地方,主要集中在 date-picker-date-utils.tsdate-picker-utils.ts 文件中。存在一些重复代码和一处潜在的逻辑缺陷。具体的建议请看我的评论。

Comment on lines 72 to 75
const safe = (i: number) => (Number.isFinite(i) && i >= 0 ? i : 0)
const yStr = selected[safe(idx.year)] ?? selected[0]
const mStr = selected[safe(idx.month)] ?? selected[1]
const dStr = selected[safe(idx.day)] ?? selected[2]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

这里的 safe 函数和后续的备用逻辑存在缺陷。当 fields 属性不包含某个日期单位时(例如 precision'month'fields['year']),idx.month 会是 -1safe(-1) 返回 0。这会导致 mStr 错误地取值为 selected[0],也就是年份的值。

为了更正这个逻辑并提高代码的清晰度,建议直接检查索引是否存在,并为 undefined 或未找到的情况提供明确的备用值。同时可以移除不再需要的 safe 函数。

Suggested change
const safe = (i: number) => (Number.isFinite(i) && i >= 0 ? i : 0)
const yStr = selected[safe(idx.year)] ?? selected[0]
const mStr = selected[safe(idx.month)] ?? selected[1]
const dStr = selected[safe(idx.day)] ?? selected[2]
const yStr = (idx.year > -1 ? selected[idx.year] : undefined) ?? min.getFullYear().toString()
const mStr = (idx.month > -1 ? selected[idx.month] : undefined) ?? '1'
const dStr = (idx.day > -1 ? selected[idx.day] : undefined) ?? '1'

Comment on lines 60 to 66
const order: ('year' | 'month' | 'day')[] = (() => {
if (!fields) return ['year', 'month', 'day']
if (Array.isArray(fields)) return fields
if (fields === 'MDY') return ['month', 'day', 'year']
if (fields === 'DMY') return ['day', 'month', 'year']
return ['year', 'month', 'day']
})()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

这部分用于解析 fields 属性的逻辑与 date-picker-utils.ts 文件中的 normalizeDateFieldsOrder 函数功能类似,但实现不完全一致(缺少了对 'YMD' 的处理)。同时,这段逻辑在本文件中的 170-176 行被重复。

为了代码的健壮性和可维护性,建议:

  1. 补全对 'YMD' 的处理,使其与 normalizeDateFieldsOrder 行为一致。
  2. 将此逻辑提取成一个局部函数,以消除文件内的重复代码。

考虑到循环依赖问题,暂时无法直接复用 normalizeDateFieldsOrder,但保持实现一致性很重要。

Comment on lines 170 to 176
const fieldsOrder: ('year' | 'month' | 'day')[] = (() => {
if (!fields) return ['year', 'month', 'day']
if (Array.isArray(fields)) return fields
if (fields === 'MDY') return ['month', 'day', 'year']
if (fields === 'DMY') return ['day', 'month', 'year']
return ['year', 'month', 'day']
})()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

这部分逻辑与本文件第 60-66 行的逻辑重复了。建议将它们统一起来,以提高代码的可维护性。

Comment on lines 69 to 96
// Support reordering for Y/M/D only; time units (hour/minute/second) keep original order after day
const order = normalizeDateFieldsOrder(fields)
if (length <= 3) {
const y = base[0]
const m = base[1]
const d = base[2]
const map: Record<'year' | 'month' | 'day', string> = {
year: y,
month: m,
day: d,
}
const reordered = order.map(k => map[k])
return reordered.slice(0, length)
}
// length > 3
const ymdReordered = (() => {
const y = base[0]
const m = base[1]
const d = base[2]
const map: Record<'year' | 'month' | 'day', string> = {
year: y,
month: m,
day: d,
}
return order.map(k => map[k])
})()
const rest = base.slice(3, length)
return [...ymdReordered, ...rest]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

这部分代码在 length <= 3length > 3 的分支中存在重复逻辑,即从 base 数组中提取年、月、日并根据 order 重排序的部分。

为了使代码更简洁、易于维护,可以提取这部分公共逻辑。

    // Support reordering for Y/M/D only; time units (hour/minute/second) keep original order after day
    const order = normalizeDateFieldsOrder(fields)
    const y = base[0]
    const m = base[1]
    const d = base[2]
    const map: Record<'year' | 'month' | 'day', string> = {
      year: y,
      month: m,
      day: d,
    }
    const ymdReordered = order.map(k => map[k])

    if (length <= 3) {
      return ymdReordered.slice(0, length)
    } else {
      const rest = base.slice(3, length)
      return [...ymdReordered, ...rest]
    }

Comment on lines 129 to 133
const indexInPresent = (key: 'year' | 'month' | 'day') =>
presentOrder.indexOf(key)
const yIdx = indexInPresent('year')
const mIdx = indexInPresent('month')
const dIdx = indexInPresent('day')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

这部分通过 indexOf 多次查找索引的逻辑可以稍微优化一下,使用 Map 可以让意图更清晰,代码也更具可读性。

    const keyToIndexMap = new Map(presentOrder.map((key, i) => [key, i]))
    const yIdx = keyToIndexMap.get('year') ?? -1
    const mIdx = keyToIndexMap.get('month') ?? -1
    const dIdx = keyToIndexMap.get('day') ?? -1

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/components/date-picker/date-picker-date-utils.ts (1)

170-188: 字段顺序处理逻辑存在重复

Lines 60-66 和 Lines 170-176 中存在几乎相同的字段顺序标准化逻辑。建议提取为一个共用的辅助函数以减少代码重复。

可以考虑提取如下的辅助函数:

+const normalizeFieldsOrder = (fields?: DateFieldsOrder): ('year' | 'month' | 'day')[] => {
+  if (!fields) return ['year', 'month', 'day']
+  if (Array.isArray(fields)) return fields
+  if (fields === 'MDY') return ['month', 'day', 'year']
+  if (fields === 'DMY') return ['day', 'month', 'year']
+  return ['year', 'month', 'day']
+}

-  const order: ('year' | 'month' | 'day')[] = (() => {
-    if (!fields) return ['year', 'month', 'day']
-    if (Array.isArray(fields)) return fields
-    if (fields === 'MDY') return ['month', 'day', 'year']
-    if (fields === 'DMY') return ['day', 'month', 'year']
-    return ['year', 'month', 'day']
-  })()
+  const order = normalizeFieldsOrder(fields)

-  const fieldsOrder: ('year' | 'month' | 'day')[] = (() => {
-    if (!fields) return ['year', 'month', 'day']
-    if (Array.isArray(fields)) return fields
-    if (fields === 'MDY') return ['month', 'day', 'year']
-    if (fields === 'DMY') return ['day', 'month', 'year']
-    return ['year', 'month', 'day']
-  })()
+  const fieldsOrder = normalizeFieldsOrder(fields)
src/components/date-picker/date-picker-utils.ts (1)

67-97: 日期转字符串数组的字段重排逻辑需要优化

实现中存在重复的代码块用于处理 Y/M/D 重排。可以提取公共逻辑以提高代码可维护性。

应用以下重构来消除重复代码:

export const convertDateToStringArray = (
  date: Date | undefined | null,
  precision: Precision,
  fields?: DateFieldsOrder
) => {
  if (precision.includes('week')) {
    return weekUtils.convertDateToStringArray(date)
  } else if (precision.includes('quarter')) {
    return quarterUtils.convertDateToStringArray(date)
  } else {
    const datePrecision = precision as DatePrecision
    const base = dateUtils.convertDateToStringArray(date)
    const length = precisionLengthRecord[datePrecision]
    // Support reordering for Y/M/D only; time units (hour/minute/second) keep original order after day
    const order = normalizeDateFieldsOrder(fields)
    
+   // Extract reordering logic
+   const reorderYMD = (base: string[], order: ('year' | 'month' | 'day')[]) => {
+     const y = base[0]
+     const m = base[1]
+     const d = base[2]
+     const map: Record<'year' | 'month' | 'day', string> = {
+       year: y,
+       month: m,
+       day: d,
+     }
+     return order.map(k => map[k])
+   }
    
    if (length <= 3) {
-     const y = base[0]
-     const m = base[1]
-     const d = base[2]
-     const map: Record<'year' | 'month' | 'day', string> = {
-       year: y,
-       month: m,
-       day: d,
-     }
-     const reordered = order.map(k => map[k])
+     const reordered = reorderYMD(base, order)
      return reordered.slice(0, length)
    }
    // length > 3
-   const ymdReordered = (() => {
-     const y = base[0]
-     const m = base[1]
-     const d = base[2]
-     const map: Record<'year' | 'month' | 'day', string> = {
-       year: y,
-       month: m,
-       day: d,
-     }
-     return order.map(k => map[k])
-   })()
+   const ymdReordered = reorderYMD(base, order)
    const rest = base.slice(3, length)
    return [...ymdReordered, ...rest]
  }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 44337a7 and 407d67d.

📒 Files selected for processing (6)
  • src/components/date-picker-view/date-picker-view.tsx (4 hunks)
  • src/components/date-picker/date-picker-date-utils.ts (5 hunks)
  • src/components/date-picker/date-picker-utils.ts (5 hunks)
  • src/components/date-picker/date-picker.tsx (5 hunks)
  • src/components/date-picker/demos/demo1.tsx (3 hunks)
  • src/components/date-picker/tests/date-picker.test.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
src/components/date-picker/tests/date-picker.test.tsx (2)
src/components/picker-view/columns-extend.tsx (1)
  • columns (34-36)
src/components/date-picker/date-picker.tsx (1)
  • DatePicker (78-178)
src/components/date-picker/demos/demo1.tsx (3)
src/components/space/space.tsx (1)
  • Space (30-55)
src/components/button/button.tsx (1)
  • Button (64-132)
src/components/date-picker/date-picker.tsx (1)
  • DatePicker (78-178)
src/components/date-picker/date-picker-date-utils.ts (2)
src/components/date-picker/date-picker-utils.ts (2)
  • DateFieldsOrder (25-29)
  • convertStringArrayToDate (100-143)
src/components/picker-view/picker-view.tsx (1)
  • PickerColumn (27-27)
src/components/date-picker/date-picker-utils.ts (1)
src/components/date-picker/date-picker-date-utils.ts (2)
  • DatePrecision (14-20)
  • convertStringArrayToDate (254-271)
src/components/date-picker-view/date-picker-view.tsx (5)
src/components/date-picker/date-picker-utils.ts (3)
  • DateFieldsOrder (25-29)
  • convertDateToStringArray (56-98)
  • convertStringArrayToDate (100-143)
src/components/date-picker/date-picker-date-utils.ts (2)
  • convertDateToStringArray (240-252)
  • convertStringArrayToDate (254-271)
src/components/date-picker/date-picker-week-utils.ts (2)
  • convertDateToStringArray (120-130)
  • convertStringArrayToDate (132-146)
src/components/date-picker/date-picker-quarter-utils.ts (2)
  • convertDateToStringArray (98-104)
  • convertStringArrayToDate (106-118)
src/components/picker-view/picker-view.tsx (1)
  • PickerValue (14-14)
src/components/date-picker/date-picker.tsx (3)
src/components/date-picker/date-picker-utils.ts (3)
  • DateFieldsOrder (25-29)
  • convertDateToStringArray (56-98)
  • convertStringArrayToDate (100-143)
src/components/date-picker/date-picker-date-utils.ts (2)
  • convertDateToStringArray (240-252)
  • convertStringArrayToDate (254-271)
src/components/date-picker/date-picker-week-utils.ts (2)
  • convertDateToStringArray (120-130)
  • convertStringArrayToDate (132-146)
⏰ 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). (4)
  • GitHub Check: size
  • GitHub Check: build preview
  • GitHub Check: check
  • GitHub Check: compressed-size
🔇 Additional comments (28)
src/components/date-picker/demos/demo1.tsx (3)

3-3: 导入顺序调整符合代码规范

调整了 React hooks 的导入顺序(从 useState, useCallbackuseCallback, useState),虽然这不影响功能,但保持了一致的代码风格。


273-313: 新增字段顺序演示功能实现良好

FieldsDemo 组件很好地展示了新的 fields 属性功能:

  • 使用数组格式 ['month', 'day', 'year'] 展示月-日-年顺序
  • 使用字符串格式 'DMY' 展示日-月-年顺序
  • 两个选择器都正确设置了最大值和确认回调

实现符合 PR 目标,为用户提供了直观的字段顺序自定义功能演示。


346-348: 演示模块集成正确

新的"自定义列顺序"演示块已正确集成到主演示页面中,为用户提供了清晰的功能展示入口。

src/components/date-picker/tests/date-picker.test.tsx (3)

267-282: 测试工具函数实现合理

getColumnValues 辅助函数能够正确提取指定列的文本内容,为验证字段顺序提供了可靠的测试基础。defaultDate 常量选择了具体的测试日期,有助于验证预期结果。


284-310: 数组格式字段顺序测试覆盖完整

测试正确验证了使用数组格式 ['month', 'day', 'year'] 时:

  • 各列内容顺序符合预期(月-日-年)
  • 确认后返回的日期对象正确

测试逻辑清晰,覆盖了核心功能点。


312-338: 字符串格式字段顺序测试实现正确

测试有效验证了使用字符串格式 'DMY' 时:

  • 各列内容按日-月-年顺序正确显示
  • 用户选择后的日期转换准确

与数组格式测试形成良好互补,确保了两种配置方式都能正常工作。

src/components/date-picker-view/date-picker-view.tsx (5)

7-7: 正确导入新类型定义

添加了 DateFieldsOrder 类型导入,为组件支持字段顺序配置提供了类型基础。


43-43: Props 接口扩展合理

DatePickerViewProps 添加了可选的 fields?: DateFieldsOrder 属性,保持了向后兼容性的同时扩展了功能。


70-71: 日期转换函数参数传递正确

convertDateToStringArray 调用中正确传递了 props.fields 参数,确保了日期到字符串数组的转换能够遵循指定的字段顺序。同时更新了依赖数组以包含 props.fields,保证了字段顺序变化时组件能够正确重新计算。


75-82: onChange 回调处理完整

convertStringArrayToDate 调用中正确传递了 props.fields 参数,确保字符串数组到日期的转换能够正确解析按自定义顺序排列的值。依赖数组也相应更新,保证了响应性。


95-96: 列生成函数参数传递完整

generateDatePickerColumns 函数调用中正确添加了 props.fields 参数,确保生成的选择器列能够按照指定的字段顺序排列。

src/components/date-picker/date-picker.tsx (7)

19-22: 类型导入新增合理

正确导入了 DateFieldsOrder 类型,为组件提供字段顺序配置的类型支持。


65-65: Props 接口扩展符合设计

DatePickerProps 添加了可选的 fields?: DateFieldsOrder 属性,保持了 API 的一致性和向后兼容性。


75-75: 默认值设置合理

fields 的默认值设置为 'YMD',保持了原有的年-月-日显示顺序,确保了向后兼容性。


106-106: 日期转换参数传递正确

pickerValue 的计算中,convertDateToStringArray 正确传递了 props.fields 参数,确保了选择器值的生成能够遵循指定的字段顺序。


111-118: 确认回调处理完整

onConfirm 回调中的 convertStringArrayToDate 调用正确传递了 props.fields 参数,确保了用户选择的值能够被正确解析为日期对象。依赖数组也相应更新以包含 props.fields


122-122: 选择回调处理正确

onSelect 回调中的日期转换同样传递了正确的 props.fields 参数,保证了选择过程中的数据一致性。


135-145: 列生成依赖更新完整

generateDatePickerColumns 调用中正确添加了 props.fields 参数,同时在依赖数组中也包含了该字段,确保字段顺序变化时能够重新生成列配置。

src/components/date-picker/date-picker-date-utils.ts (5)

7-7: 类型导入添加正确

正确导入了 DateFieldsOrder 类型,为函数签名提供了必要的类型支持。


38-40: 函数签名扩展合理

generateDatePickerColumns 函数添加了可选的 fields?: DateFieldsOrder 参数,保持了向后兼容性的同时扩展了功能。参数顺序安排合理。


59-76: 字段顺序映射逻辑实现正确

通过计算字段顺序并创建索引映射,然后安全地提取年、月、日字符串,这种实现方式:

  • 正确处理了不同的字段顺序格式(数组、字符串)
  • 通过 safe 函数提供了边界保护
  • 为后续的日期计算提供了正确的基础数据

逻辑清晰且健壮。


107-123: buildStringArrayFor 辅助函数设计良好

该函数根据不同的精度级别构建正确的字符串数组,使用了重新映射的年、月、日字符串,确保了过滤器能够基于正确的字段顺序进行工作。实现简洁且逻辑清晰。


138-188: 列生成和排序逻辑实现优雅

代码采用了先生成各个日期列(年、月、日),然后根据字段顺序重新排列的方式:

  • 避免了重复的顺序判断逻辑
  • 保持了原有的列生成逻辑完整性
  • 通过 available 数组和循环实现了灵活的列排序

这种设计既保持了代码的可维护性,又实现了功能需求。

src/components/date-picker/date-picker-utils.ts (5)

25-29: 类型定义设计良好,支持灵活配置

DateFieldsOrder 类型设计合理,同时支持数组形式和字符串缩写形式,为用户提供了灵活的配置选项。这种设计既满足了编程式配置的需求,也提供了便捷的预设选项。


31-45: 规范化函数实现正确

normalizeDateFieldsOrder 函数正确处理了各种输入格式,包括 undefined、数组和字符串格式。实现逻辑清晰,默认值处理合理。


119-142: 字符串数组转日期的逆向重排逻辑实现正确

逆向重排逻辑正确实现了从自定义顺序的字符串数组还原为标准的 Y/M/D 顺序,然后传递给底层的 dateUtils.convertStringArrayToDate 函数。索引映射逻辑清晰,时间单位的处理也很合理。


152-184: 参数传递和函数签名更新正确

generateDatePickerColumns 函数正确添加了 fields 参数并将其传递给底层的日期工具函数。参数顺序合理,保持了向后兼容性。


58-59: 验证完成:所有调用点已包含 fields 参数

已确认:

  • src/components/date-picker/date-picker.tsx 中,convertDateToStringArray(date, props.precision, props.fields)convertStringArrayToDate(val, props.precision, props.fields) 均已传入 props.fields
  • src/components/date-picker-view/date-picker-view.tsx 中,convertDateToStringArray(value, props.precision, props.fields)convertStringArrayToDate(val, props.precision, props.fields) 均已传入 props.fields
  • generateDatePickerColumns 的调用:
    • date-picker-view.tsx 中以完整参数(selected, min, max, precision, renderLabel, filter, tillNow, fields)调用。
    • date-picker.tsx 中以必选前五个参数调用(其余为可选,未传时使用默认值)。

无需进一步修改。

@coderabbitai coderabbitai bot requested a review from zombieJ August 20, 2025 06:54
@coderabbitai coderabbitai bot added lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files. labels Aug 20, 2025
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. size:L This PR changes 100-499 lines, ignoring generated files. labels Aug 20, 2025
@Passing-of-A-Dream Passing-of-A-Dream changed the title feat: 优化日期选择器,支持自定义日期字段顺序 feat: date-picker 组件优化日期选择器,支持自定义日期字段顺序 Aug 20, 2025
@coderabbitai coderabbitai bot requested a review from zombieJ August 20, 2025 09:09
@coderabbitai coderabbitai bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Aug 20, 2025
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. size:L This PR changes 100-499 lines, ignoring generated files. labels Aug 20, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/date-picker/date-picker.tsx (1)

96-107: useMemo 依赖缺失 columns,切换列顺序不会触发 pickerValue 重算

当外部动态变更 props.columns 时,pickerValue 不会更新,导致 UI 未按新顺序重排/同步选中值。需将 props.columns 加入依赖数组。

建议改动:

-    }, [value, props.precision, props.min, props.max])
+    }, [value, props.precision, props.min, props.max, props.columns])
🧹 Nitpick comments (8)
src/components/date-picker/index.ts (1)

7-8: 建议同时对外导出类型 DateColumnsOrder,方便 TS 场景复用

目前仅导出 DatePickerProps / DatePickerRef / DatePickerFilter。考虑把 DateColumnsOrder 也从 index.ts re-export,便于用户在类型层面声明列顺序变量(而不仅仅是通过常量)。

可以在本文件中追加如下导出:

 export type { DatePickerProps, DatePickerRef } from './date-picker'
-export type { DatePickerFilter } from './date-picker-utils'
+export type { DatePickerFilter, DateColumnsOrder } from './date-picker-utils'
src/components/date-picker/tests/date-picker.test.tsx (4)

267-267: 测试用例描述建议与新 API 名称保持一致

块描述为“DatePicker fields prop”,而实际属性名为 columns。建议统一为 columns,避免混淆。

-  describe('DatePicker fields prop', () => {
+  describe('DatePicker columns prop', () => {

268-269: 避免使用字符串日期构造,降低时区导致的用例不稳定风险

new Date('2025-8-20') 依赖实现细节/时区解析,极端情况下可能在不同环境变成前一天或后一天。建议改用数值构造。

-    const defaultDate = new Date('2025-8-20')
+    // 月份从 0 开始,这里 7 代表 8 月
+    const defaultDate = new Date(2025, 7, 20)

295-301: 断言更精确:校验“已选中”的列项而不是“包含某值”

当前 toContain('8') / toContain('20') 仅验证列内是否存在该数字,无法确保是当前选中项。建议断言选中项(如使用 .adm-picker-view-column-item-selected)的文本,更能确保列顺序与选中值正确。

例如:

-        expect(getColumnValues(baseElement, 0)).toContain('8')
-        expect(getColumnValues(baseElement, 1)).toContain('20')
+        const selectedOf = (col: number) =>
+          baseElement
+            .querySelectorAll('.adm-picker-view-column')
+            [col]?.querySelector('.adm-picker-view-column-item-selected')
+            ?.textContent
+        expect(selectedOf(0)).toBe('8')
+        expect(selectedOf(1)).toBe('20')

若实际选中类名不同,请替换为项目内真实类名。


284-311: 补充一条关于“部分列(如仅 day+month)”的行为用例

本 PR 支持自定义列顺序,同时允许 columns 只给出部分 Y/M/D。建议新增一例:columns 仅包含两列(例如 ['day', 'month'])时,确认:

  • 生成列顺序符合预期(是否会自动补齐 year,或使用当前 year 回填)。
  • 转换回 Date 时的年份默认策略(当前年)正确。

我可以补一条用例,您需要的话我来起草。

src/components/date-picker/demos/demo1.tsx (1)

275-316: “日-月”示例的列数与按钮文案可能不一致,请确认行为并保持一致

columns={[DAY_COLUMN, MONTH_COLUMN]} 若内部会通过 normalize 补齐 year(常见设计是补齐为 3 列以满足 day 精度计算),则实际显示可能为“日-月-年”。建议:

  • 若实际是 3 列:更新按钮文案为“日-月-年”,或显式传入 columns={[DAY_COLUMN, MONTH_COLUMN, YEAR_COLUMN]} 以避免误导。
  • 若实际就是 2 列:建议在文档/示例中说明“缺失的年份回退为当前年”的行为,并补充一条测试覆盖(参考上文测试建议)。

我可以根据确认结果提交相应的示例/测试修正。

src/components/date-picker/date-picker-date-utils.ts (1)

80-86: 仅在需要日列时再计算当月天数,避免无谓计算

firstDayInSelectedMonth 仅在 rank >= day 时参与日列上限计算。可推迟到该分支内生成,减少不必要的计算与依赖。

-  const firstDayInSelectedMonth = dayjs(
-    convertStringArrayToDate([yStr, mStr, '1'])
-  )
+  // 下方 day 分支内按需计算:
+  // const firstDayInSelectedMonth = dayjs(convertStringArrayToDate([yStr, mStr, '1']))

并在 day 分支内部按需计算:

const firstDayInSelectedMonth = dayjs(
  convertStringArrayToDate([yStr, mStr, '1'])
)
const upper = isInMaxMonth ? maxDay : firstDayInSelectedMonth.daysInMonth()
src/components/date-picker/date-picker-utils.ts (1)

85-131: 反序列化映射逻辑合理;可小幅收紧类型以避免断言

使用 Map 计算索引、对缺失字段用“当前日期”兜底,能在不同顺序下稳定还原。当前将 mapped 断言为 string[] 传入底层转换函数,虽运行时安全,但类型不够严谨。可在赋值时统一转为字符串,消除断言。

-    mapped[0] = yIdx !== -1 ? value[yIdx] : currentYear
-    mapped[1] = mIdx !== -1 ? value[mIdx] : currentMonth
-    mapped[2] = dIdx !== -1 ? value[dIdx] : currentDay
+    mapped[0] = (yIdx !== -1 ? value[yIdx] : currentYear).toString()
+    mapped[1] = (mIdx !== -1 ? value[mIdx] : currentMonth).toString()
+    mapped[2] = (dIdx !== -1 ? value[dIdx] : currentDay).toString()
     for (let i = presentOrder.length; i < value.length; i += 1) {
-      mapped[3 + (i - presentOrder.length)] = value[i]
+      mapped[3 + (i - presentOrder.length)] = String(value[i] ?? '')
     }
-    return dateUtils.convertStringArrayToDate(mapped as string[])
+    return dateUtils.convertStringArrayToDate(mapped as string[])
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1bc6cf1 and 8d09205.

📒 Files selected for processing (9)
  • src/components/date-picker-view/date-picker-view.tsx (4 hunks)
  • src/components/date-picker-view/demos/demo1.tsx (1 hunks)
  • src/components/date-picker/date-picker-date-utils.ts (5 hunks)
  • src/components/date-picker/date-picker-utils.ts (6 hunks)
  • src/components/date-picker/date-picker.tsx (5 hunks)
  • src/components/date-picker/demos/demo1.tsx (3 hunks)
  • src/components/date-picker/index.ts (1 hunks)
  • src/components/date-picker/tests/date-picker.test.tsx (1 hunks)
  • src/components/date-picker/util.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/components/date-picker-view/demos/demo1.tsx
🧰 Additional context used
🧬 Code Graph Analysis (7)
src/components/date-picker/index.ts (2)
src/components/date-picker/date-picker.tsx (1)
  • DatePicker (78-178)
src/components/date-picker/util.ts (1)
  • TILL_NOW (1-1)
src/components/date-picker/date-picker.tsx (1)
src/components/date-picker/date-picker-utils.ts (3)
  • DateColumnsOrder (25-25)
  • convertDateToStringArray (51-83)
  • convertStringArrayToDate (85-132)
src/components/date-picker-view/date-picker-view.tsx (2)
src/components/date-picker/date-picker-utils.ts (3)
  • DateColumnsOrder (25-25)
  • convertDateToStringArray (51-83)
  • convertStringArrayToDate (85-132)
src/components/picker-view/picker-view.tsx (1)
  • PickerValue (14-14)
src/components/date-picker/date-picker-date-utils.ts (3)
src/components/date-picker/date-picker-utils.ts (3)
  • DateColumnsOrder (25-25)
  • normalizeDateColumnsOrder (27-40)
  • convertStringArrayToDate (85-132)
src/components/date-picker/util.ts (3)
  • YEAR_COLUMN (7-7)
  • MONTH_COLUMN (9-9)
  • DAY_COLUMN (11-11)
src/components/picker-view/picker-view.tsx (1)
  • PickerColumn (27-27)
src/components/date-picker/date-picker-utils.ts (4)
src/components/date-picker/util.ts (3)
  • YEAR_COLUMN (7-7)
  • MONTH_COLUMN (9-9)
  • DAY_COLUMN (11-11)
src/components/date-picker/date-picker-date-utils.ts (2)
  • DatePrecision (18-24)
  • convertStringArrayToDate (251-268)
src/components/date-picker/date-picker-week-utils.ts (1)
  • convertStringArrayToDate (132-146)
src/components/date-picker/date-picker-quarter-utils.ts (1)
  • convertStringArrayToDate (106-118)
src/components/date-picker/demos/demo1.tsx (2)
src/components/date-picker/date-picker.tsx (1)
  • DatePicker (78-178)
src/components/date-picker/util.ts (3)
  • MONTH_COLUMN (9-9)
  • DAY_COLUMN (11-11)
  • YEAR_COLUMN (7-7)
src/components/date-picker/tests/date-picker.test.tsx (1)
src/components/date-picker/date-picker.tsx (1)
  • DatePicker (78-178)
⏰ 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). (3)
  • GitHub Check: compressed-size
  • GitHub Check: size
  • GitHub Check: build preview
🔇 Additional comments (18)
src/components/date-picker/util.ts (1)

7-11: 新增列常量命名清晰、类型安全,LGTM

以 string literal + as const 的方式导出 YEAR/MONTH/DAY 三个列键,非常适合在外部作为 columns 配置使用。

src/components/date-picker/index.ts (1)

10-16: 将列键常量挂到组件静态属性,易用性提升,LGTM

通过 attachPropertiesToComponent 暴露 YEAR_COLUMN/MONTH_COLUMN/DAY_COLUMN,便于示例和用户直接从 DatePicker 解构使用,符合现有 API 风格。

src/components/date-picker/tests/date-picker.test.tsx (1)

267-311: 测试块未重复定义,无需修改

经检索,describe('DatePicker fields prop', …)date-picker.test.tsx 文件中仅出现一次,未发现重复定义的测试块,请忽略此建议。

Likely an incorrect or invalid review comment.

src/components/date-picker/demos/demo1.tsx (1)

8-9: 从 DatePicker 解构列键常量用于示例,LGTM

提升了示例的可读性与可发现性,且与 index.ts 的静态导出设计一致。

src/components/date-picker-view/date-picker-view.tsx (4)

7-8: 为 DatePickerView 新增 columns?: DateColumnsOrder,类型与 DatePicker 对齐,LGTM

类型对齐且向下贯穿至转换与列生成,便于 DatePickerView 独立使用时也能自定义列顺序。


70-72: pickerValue 计算引入 columns 参与重排,依赖项同步更新,LGTM

已将 props.columns 纳入 useMemo 依赖,避免列序改变时视图不同步。


73-82: onChange 转换逻辑引入 columns,避免错位解析,LGTM

依赖项补充 props.columns,保证闭包内拿到最新的列顺序。


95-97: 列生成传递 columns,保持转换与渲染一致性,LGTM

generateDatePickerColumns 的入参与 DatePicker 对齐,减少双组件行为差异。

src/components/date-picker/date-picker.tsx (4)

65-76: 新增 columns Prop 与默认值设置合理

公开 columns?: DateColumnsOrder 并在 defaultProps 中默认 ['year','month','day'],与此次能力目标一致,类型与默认值选择均恰当。


111-119: onConfirm 回调透传 columns 正确;依赖项已包含 columns

convertStringArrayToDate(val, props.precision, props.columns) 的调用与依赖项维护一致,能正确按自定义顺序还原日期。


121-124: onSelect 动态使用最新 columns 与 precision 的实现可接受

这里用 useMemoizedFn 获取最新的 props,无需在依赖数组中声明,能保持引用稳定且拿到最新 precision/columns,实现合理。


126-146: 列生成逻辑已正确串联 tillNow 与 columns

generateDatePickerColumns(..., props.tillNow, props.columns) 的透传与依赖项维护完整,符合新增能力设计。

src/components/date-picker/date-picker-date-utils.ts (4)

110-126: 过滤器上下文构造正确,能与 columns 顺序解耦

通过 buildStringArrayFor 始终以标准 Y/M/D 顺序构造日期字符串数组供 convertStringArrayToDate 使用,确保 filter 逻辑不受列顺序影响,设计合理。


141-171: 年/月/日列生成逻辑与选中态计算合理

针对边界条件的上下限(min/max)与选中态 selectedXxx === v 的计算正确;结合上方越界修复后,可以避免 NaN 造成的选中态异常。


173-185: 按自定义顺序组装 Y/M/D 列的实现清晰

通过 normalizeDateColumnsOrderavailable 列表拼装输出顺序,语义明确,易维护。


220-232: “至今”选项固定追加到第一列,请确认与交互预期一致

当前将 TILL_NOW 追加到 ret[0],且 convertStringArrayToDate 仅在 value[0] === TILL_NOW 情况下处理“至今”。这意味着当自定义顺序为 MDY/DMY 时,“至今”会出现在“第一列”(可能是月/日列)。如产品期望“至今”始终显示在年份列,请确认是否需要在组装顺序前将“年份列”强制置于索引 0 的特殊位(或调整判定逻辑为“只要某列为 TILL_NOW 即视为至今”)。

src/components/date-picker/date-picker-utils.ts (2)

51-82: 按自定义顺序重排 Y/M/D 的实现正确且简洁

将 Y/M/D 重排后再拼接后续时间单位,满足不同精度下的列顺序配置需求,逻辑清晰。


134-175: 路由到不同精度的列生成函数并透传 columns 的更改正确

对 week/quarter/date 三种路径的签名统一、行为一致,透传 columns(以及 tillNow)保证渲染与反序列化一致。

@coderabbitai coderabbitai bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Sep 3, 2025
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. size:XL This PR changes 500-999 lines, ignoring generated files. labels Sep 3, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/components/date-picker/date-picker.tsx (2)

91-103: useMemo 依赖缺少 props.columns,切换列顺序时 pickerValue 不会更新

当外部变更 columns 时,pickerValue 仍基于旧顺序,导致初始选中错位。

建议补全依赖:

-    }, [value, props.precision, props.min, props.max])
+    }, [value, props.precision, props.min, props.max, props.columns])

121-141: columns 回调缺少 props.filter 依赖,动态过滤规则变更不生效

generateDatePickerColumns 使用了 props.filter,但未列入依赖,可能导致列候选集与 UI 不一致。

-      [
+      [
         props.min,
         props.max,
         props.precision,
         mergedRenderLabel,
         props.tillNow,
         props.columns,
+        props.filter,
       ]
src/components/date-picker/date-picker-date-utils.ts (1)

115-137: 过滤回调中构造 date 未传入 columns,且 prefix 位置计算与自定义顺序不符

当前用 precisionRankRecord[precision] 截取 prefix,在自定义列顺序下位置不对;且未把列顺序传给 convertStringArrayToDate,导致 filter 判断基于错误日期。

-    const prefix = selected.slice(0, precisionRankRecord[precision])
+    // 以渲染列顺序定位当前列在 selected 中的位置
+    const insertIndex = Math.max(
+      0,
+      renderedColumns.findIndex(ct => columnToPrecisionMap[ct] === precision)
+    )
+    const prefix = selected.slice(0, insertIndex)
@@
-            const stringArray = [...prefix, i.toString()]
-            return convertStringArrayToDate(stringArray)
+            const stringArray = [...prefix, i.toString()]
+            return convertStringArrayToDate(stringArray, renderedColumns)
♻️ Duplicate comments (1)
src/components/date-picker/date-picker-date-utils.ts (1)

146-218: switch case 内声明变量触发 Biome noSwitchDeclarations 错误

需为每个 case 加块级作用域,避免跨 case 变量泄露。

-      case YEAR_COLUMN:
-        const lower = minYear
-        const upper = maxYear
-        const years = generateColumn(lower, upper, 'year')
+      case YEAR_COLUMN: {
+        const lower = minYear
+        const upper = maxYear
+        const years = generateColumn(lower, upper, 'year')
         ret.push(
           years.map(v => ({
             label: renderLabel('year', v, { selected: selectedYear === v }),
             value: v.toString(),
           }))
         )
-        break
+        break
+      }
@@
-      case MONTH_COLUMN:
+      case MONTH_COLUMN: {
         const lowerMonth = isInMinYear ? minMonth : 1
         const upperMonth = isInMaxYear ? maxMonth : 12
         const months = generateColumn(lowerMonth, upperMonth, 'month')
         ret.push(
           months.map(v => ({
             label: renderLabel('month', v, { selected: selectedMonth === v }),
             value: v.toString(),
           }))
         )
-        break
+        break
+      }
@@
-      case DAY_COLUMN:
+      case DAY_COLUMN: {
         const lowerDay = isInMinMonth ? minDay : 1
         const upperDay = isInMaxMonth ? maxDay : firstDayInSelectedMonth.daysInMonth()
         const days = generateColumn(lowerDay, upperDay, 'day')
         ret.push(
           days.map(v => ({
             label: renderLabel('day', v, { selected: selectedDay === v }),
             value: v.toString(),
           }))
         )
-        break
+        break
+      }
@@
-      case HOUR_COLUMN:
+      case HOUR_COLUMN: {
         const lowerHour = isInMinDay ? minHour : 0
         const upperHour = isInMaxDay ? maxHour : 23
         const hours = generateColumn(lowerHour, upperHour, 'hour')
         ret.push(
           hours.map(v => ({
             label: renderLabel('hour', v, { selected: selectedHour === v }),
             value: v.toString(),
           }))
         )
-        break
+        break
+      }
@@
-      case MINUTE_COLUMN:
+      case MINUTE_COLUMN: {
         const lowerMinute = isInMinHour ? minMinute : 0
         const upperMinute = isInMaxHour ? maxMinute : 59
         const minutes = generateColumn(lowerMinute, upperMinute, 'minute')
         ret.push(
           minutes.map(v => ({
             label: renderLabel('minute', v, { selected: selectedMinute === v }),
             value: v.toString(),
           }))
         )
-        break
+        break
+      }
@@
-      case SECOND_COLUMN:
+      case SECOND_COLUMN: {
         const lowerSecond = isInMinMinute ? minSecond : 0
         const upperSecond = isInMaxMinute ? maxSecond : 59
         const seconds = generateColumn(lowerSecond, upperSecond, 'second')
         ret.push(
           seconds.map(v => ({
             label: renderLabel('second', v, { selected: selectedSecond === v }),
             value: v.toString(),
           }))
         )
-        break
+        break
+      }
🧹 Nitpick comments (1)
src/components/date-picker/date-picker-date-utils.ts (1)

94-100: 可选:对选中年份做二次夹取,增强稳健性

上游异常映射时可出现越界年份,建议再次 clamp 到 [minYear, maxYear]

-  const selectedYear = getValue(YEAR_COLUMN) ?? min.getFullYear()
+  let selectedYear = getValue(YEAR_COLUMN) ?? min.getFullYear()
+  selectedYear = Math.min(Math.max(selectedYear, minYear), maxYear)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a0d3b28 and 222386c.

📒 Files selected for processing (3)
  • src/components/date-picker/date-picker-date-utils.ts (5 hunks)
  • src/components/date-picker/date-picker-utils.ts (5 hunks)
  • src/components/date-picker/date-picker.tsx (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/components/date-picker/date-picker-date-utils.ts (3)
src/components/date-picker/util.ts (8)
  • DateColumnType (14-20)
  • YEAR_COLUMN (7-7)
  • MONTH_COLUMN (8-8)
  • DAY_COLUMN (9-9)
  • HOUR_COLUMN (10-10)
  • MINUTE_COLUMN (11-11)
  • SECOND_COLUMN (12-12)
  • TILL_NOW (1-1)
src/components/date-picker/date-picker-utils.ts (3)
  • generateDatePickerColumns (93-133)
  • DatePickerFilter (21-31)
  • convertStringArrayToDate (70-91)
src/components/date-picker-view/date-picker-view.tsx (1)
  • RenderLabel (21-27)
src/components/date-picker/date-picker-utils.ts (4)
src/components/date-picker/util.ts (7)
  • DateColumnType (14-20)
  • YEAR_COLUMN (7-7)
  • MONTH_COLUMN (8-8)
  • DAY_COLUMN (9-9)
  • HOUR_COLUMN (10-10)
  • MINUTE_COLUMN (11-11)
  • SECOND_COLUMN (12-12)
src/components/date-picker/date-picker-date-utils.ts (2)
  • DatePrecision (23-29)
  • convertStringArrayToDate (253-319)
src/components/date-picker/date-picker-week-utils.ts (1)
  • convertStringArrayToDate (132-146)
src/components/date-picker/date-picker-quarter-utils.ts (1)
  • convertStringArrayToDate (106-118)
src/components/date-picker/date-picker.tsx (2)
src/components/date-picker/util.ts (1)
  • DateColumnType (14-20)
src/components/date-picker/date-picker-utils.ts (2)
  • convertDateToStringArray (42-68)
  • convertStringArrayToDate (70-91)
🪛 Biome (2.1.2)
src/components/date-picker/date-picker-date-utils.ts

[error] 147-147: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 148-148: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 149-149: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 159-159: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 160-160: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 161-161: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 171-171: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 172-174: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 175-175: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 185-185: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 186-186: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 187-187: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 197-197: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 198-198: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 199-199: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 209-209: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 210-210: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 211-211: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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). (4)
  • GitHub Check: compressed-size
  • GitHub Check: build preview
  • GitHub Check: size
  • GitHub Check: check
🔇 Additional comments (1)
src/components/date-picker/date-picker-date-utils.ts (1)

253-309: convertStringArrayToDate 对 '0' 误判为 falsy,导致 0 时/分/秒回退默认值

应使用空值判断而非真值判断;并统一十进制解析。

-    columns.forEach((columnType, index) => {
-      const val = value[index]?.toString() ?? null
+    columns.forEach((columnType, index) => {
+      const raw = value[index]
+      const val = raw == null ? null : String(raw)
@@
-        case YEAR_COLUMN:
-          if (val) yearString = val
+        case YEAR_COLUMN:
+          if (val != null) yearString = val
           break
@@
-        case MONTH_COLUMN:
-          if (val) monthString = val
+        case MONTH_COLUMN:
+          if (val != null) monthString = val
           break
@@
-        case DAY_COLUMN:
-          if (val) dateString = val
+        case DAY_COLUMN:
+          if (val != null) dateString = val
           break
@@
-        case HOUR_COLUMN:
-          if (val) hourString = val
+        case HOUR_COLUMN:
+          if (val != null) hourString = val
           break
@@
-        case MINUTE_COLUMN:
-          if (val) minuteString = val
+        case MINUTE_COLUMN:
+          if (val != null) minuteString = val
           break
@@
-        case SECOND_COLUMN:
-          if (val) secondString = val
+        case SECOND_COLUMN:
+          if (val != null) secondString = val
           break

Likely an incorrect or invalid review comment.

@coderabbitai coderabbitai bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Sep 3, 2025
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. size:XL This PR changes 500-999 lines, ignoring generated files. labels Sep 3, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/components/date-picker/date-picker-utils.ts (1)

81-101: TILL_NOW 仅检查首列,和 UI 行为一致

与“将 Now 选项放在第一列”的实现对齐,判断逻辑合理。

src/components/date-picker/date-picker-date-utils.ts (1)

119-141: 过滤回调中日期反序列化未携带列顺序,重排/子集时会算错 date(需修复)

当前 convertStringArrayToDate(stringArray) 以默认 Y-M-D 顺序解析;当 columns 重排为如 [MONTH, DAY, YEAR] 或仅为子集时,prefix 的顺序与默认不一致,过滤得到的 date 错误,直接影响候选项。

同时,prefix 含 TILL_NOW 时会把 'TILL_NOW' 传给解析函数导致 NaN,应在此短路为 now。

建议:给 generateColumn 增加 columnType 参数,构造与 stringArray 一致的 usedColumns 顺序传入解析;并对 TILL_NOW 前缀短路。

-  const generateColumn = (
-    from: number,
-    to: number,
-    precision: DatePrecision
-  ) => {
+  const generateColumn = (
+    from: number,
+    to: number,
+    precision: DatePrecision,
+    columnType: DateColumnType
+  ) => {
     let column: number[] = []
     for (let i = from; i <= to; i++) {
       column.push(i)
     }
     const prefix = selected.slice(0, precisionRankRecord[precision])
     const currentFilter = filter?.[precision]
     if (currentFilter && typeof currentFilter === 'function') {
       column = column.filter(i =>
         currentFilter(i, {
           get date() {
-            const stringArray = [...prefix, i.toString()]
-            return convertStringArrayToDate(stringArray)
+            const stringArray = [...prefix, i.toString()]
+            // 与 stringArray 对齐的列顺序:先取已渲染的前缀列,再拼接当前列
+            const usedColumns = renderedColumns
+              .slice(0, precisionRankRecord[precision])
+              .concat(columnType)
+            // 若选择了“至今”,直接按当前时间短路
+            if (prefix.includes(TILL_NOW)) return new Date()
+            return convertStringArrayToDate(stringArray, usedColumns)
           },
         })
       )
     }
     return column
   }

并在下方 switch 分支调用处补上 columnType

-        const years = generateColumn(lower, upper, 'year')
+        const years = generateColumn(lower, upper, 'year', YEAR_COLUMN)
@@
-        const months = generateColumn(lowerMonth, upperMonth, 'month')
+        const months = generateColumn(lowerMonth, upperMonth, 'month', MONTH_COLUMN)
@@
-        const days = generateColumn(lowerDay, upperDay, 'day')
+        const days = generateColumn(lowerDay, upperDay, 'day', DAY_COLUMN)
@@
-        const hours = generateColumn(lowerHour, upperHour, 'hour')
+        const hours = generateColumn(lowerHour, upperHour, 'hour', HOUR_COLUMN)
@@
-        const minutes = generateColumn(lowerMinute, upperMinute, 'minute')
+        const minutes = generateColumn(lowerMinute, upperMinute, 'minute', MINUTE_COLUMN)
@@
-        const seconds = generateColumn(lowerSecond, upperSecond, 'second')
+        const seconds = generateColumn(lowerSecond, upperSecond, 'second', SECOND_COLUMN)
🧹 Nitpick comments (5)
src/components/date-picker/date-picker-utils.ts (1)

42-79: columns 未做去重/校验,可能导致重复列与值错位

当传入 columns 含重复项或非法项时,这里会按重复顺序返回值,易与渲染列数不一致。建议先去重并过滤仅允许的列,再按精度裁剪。

可在本函数内做轻量归一化,避免额外依赖:

   const map: Record<DateColumnType, string> = {
@@
   }
-  const order: DateColumnType[] = [
+  const order: DateColumnType[] = [
     YEAR_COLUMN,
     MONTH_COLUMN,
     DAY_COLUMN,
     HOUR_COLUMN,
     MINUTE_COLUMN,
     SECOND_COLUMN,
   ]
   const max = precisionLengthRecord[datePrecision]
-  return columns
-    .filter(col => order.indexOf(col) > -1 && order.indexOf(col) < max)
-    .map(col => map[col])
+  const allowed = new Set(order)
+  // 去重且仅保留允许列
+  const normalized = Array.from(new Set(columns)).filter(c => allowed.has(c))
+  return normalized
+    .filter(col => order.indexOf(col) < max)
+    .map(col => map[col])
src/components/date-picker/date-picker-date-utils.ts (4)

75-88: finalColumns 建议先去重,避免重复列渲染与 selected 映射错位

当外部传入包含重复列时,renderedColumns 会重复生成列,selected 顺序也会变得不稳定。建议在应用精度过滤前去重。

-  const finalColumns = columns?.length ? columns : defaultColumns
-  const renderedColumns = finalColumns.filter(columnType => {
+  const finalColumns = columns?.length ? columns : defaultColumns
+  const dedupedFinal = Array.from(new Set(finalColumns))
+  const renderedColumns = dedupedFinal.filter(columnType => {
     const columnPrecision = columnToPrecisionMap[columnType]
     return rank >= precisionRankRecord[columnPrecision]
   })

98-103: year 建议再次夹取到 [minYear, maxYear],防御异常输入

可避免异常 selected 导致边界判定错乱(如 isInMinYear/MaxYear)。

-  const selectedYear = getValue(YEAR_COLUMN) ?? min.getFullYear()
+  const selectedYearRaw = getValue(YEAR_COLUMN) ?? min.getFullYear()
+  const selectedYear = Math.min(Math.max(selectedYearRaw, minYear), maxYear)

223-234: Till Now 清空下游列的方式可提前短路以省开销(可选)

若首列已选 TILL_NOW,可在生成列前直接返回仅含首列的结果,减少无效计算。


253-319: 列映射反序列化对“包含但未给值”的列未做兜底,默认 1900/1/1/0:0:0 不理想

当 columns 包含某列但对应值缺失(例如过滤阶段或受限于精度),当前实现会保留初始的 1900/1/1/0:0:0。建议对“包含但未赋值”的列回退到 now(或约定默认),避免生成过早/异常日期。

-    columns.forEach((columnType, index) => {
-      const val = value[index]?.toString() ?? null
+    columns.forEach((columnType, index) => {
+      const val = value[index]?.toString() ?? null
       switch (columnType) {
         case YEAR_COLUMN:
-          if (val) yearString = val
+          yearString = val ?? now.getFullYear().toString()
           break
         case MONTH_COLUMN:
-          if (val) monthString = val
+          monthString = val ?? (now.getMonth() + 1).toString()
           break
         case DAY_COLUMN:
-          if (val) dateString = val
+          dateString = val ?? now.getDate().toString()
           break
         case HOUR_COLUMN:
-          if (val) hourString = val
+          hourString = val ?? now.getHours().toString()
           break
         case MINUTE_COLUMN:
-          if (val) minuteString = val
+          minuteString = val ?? now.getMinutes().toString()
           break
         case SECOND_COLUMN:
-          if (val) secondString = val
+          secondString = val ?? now.getSeconds().toString()
           break
       }
     })
-
-    if (!columns.includes(YEAR_COLUMN))
+    if (!columns.includes(YEAR_COLUMN))
       yearString = now.getFullYear().toString()
@@
-    if (!columns.includes(SECOND_COLUMN))
+    if (!columns.includes(SECOND_COLUMN))
       secondString = now.getSeconds().toString()

说明:保持“未包含的列回退 now”的现有语义,同时让“包含但未给值”的场景也安全回退。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 222386c and f652cf4.

📒 Files selected for processing (2)
  • src/components/date-picker/date-picker-date-utils.ts (5 hunks)
  • src/components/date-picker/date-picker-utils.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/components/date-picker/date-picker-date-utils.ts (3)
src/components/date-picker/util.ts (8)
  • DateColumnType (14-20)
  • YEAR_COLUMN (7-7)
  • MONTH_COLUMN (8-8)
  • DAY_COLUMN (9-9)
  • HOUR_COLUMN (10-10)
  • MINUTE_COLUMN (11-11)
  • SECOND_COLUMN (12-12)
  • TILL_NOW (1-1)
src/components/date-picker/date-picker-utils.ts (3)
  • generateDatePickerColumns (104-144)
  • DatePickerFilter (21-31)
  • convertStringArrayToDate (81-102)
src/components/date-picker-view/date-picker-view.tsx (1)
  • RenderLabel (21-27)
src/components/date-picker/date-picker-utils.ts (2)
src/components/date-picker/util.ts (7)
  • DateColumnType (14-20)
  • YEAR_COLUMN (7-7)
  • MONTH_COLUMN (8-8)
  • DAY_COLUMN (9-9)
  • HOUR_COLUMN (10-10)
  • MINUTE_COLUMN (11-11)
  • SECOND_COLUMN (12-12)
src/components/date-picker/date-picker-date-utils.ts (1)
  • convertStringArrayToDate (253-319)
⏰ 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). (4)
  • GitHub Check: build preview
  • GitHub Check: compressed-size
  • GitHub Check: check
  • GitHub Check: size
🔇 Additional comments (4)
src/components/date-picker/date-picker-utils.ts (2)

8-17: 导入常量与类型整合良好

与 util 解耦后更清晰,可读性 OK。


111-143: 透传 tillNow 与 columns 至底层实现

签名扩展与透传无副作用,保持向后兼容。

src/components/date-picker/date-picker-date-utils.ts (2)

8-17: 常量与类型来源统一到 util

消除了运行时循环依赖风险,方向正确。


40-47: 列到精度映射定义清晰

后续过滤/裁剪逻辑可直接复用该映射。

@coderabbitai coderabbitai bot requested a review from zombieJ September 8, 2025 07:04
@coderabbitai coderabbitai bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Sep 8, 2025
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. size:XL This PR changes 500-999 lines, ignoring generated files. labels Sep 8, 2025
@zombieJ
Copy link
Member

zombieJ commented Sep 9, 2025

@gemini-code-assist, review again. To check carefully do it has a break change.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

这个 PR 引入了一个很棒的功能,允许自定义 DatePicker 的列顺序。代码实现总体上是好的,并且包含了相应的文档和测试。

我发现了一个关于日期解析逻辑的潜在问题,当 columns 属性未提供时,代码实现比较脆弱。我提出了两个相关的修复建议,一个在 date-picker-utils.ts,另一个在 date-picker-date-utils.ts。这些修改将通过确保在解析日期字符串时始终使用正确的列顺序来增强代码的健壮性。

除此之外,代码看起来不错。

return quarterUtils.convertStringArrayToDate(value)
} else {
return dateUtils.convertStringArrayToDate(value)
return dateUtils.convertStringArrayToDate(value, columns)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

为了使日期解析更健壮,请将 precision 参数传递给 dateUtils.convertStringArrayToDate。当 columns 属性为空时,dateUtils.convertStringArrayToDate 需要 precision 来确定正确的默认列顺序。这将修复一个在默认列顺序改变时可能发生的潜在错误。

Suggested change
return dateUtils.convertStringArrayToDate(value, columns)
return dateUtils.convertStringArrayToDate(value, columns, precision as DatePrecision)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/components/date-picker/date-picker-date-utils.ts (2)

123-145: 过滤用的日期构造未按自定义列顺序映射,列重排/子集时会计算错误

generateColumnselected.slice(0, precisionRankRecord[precision]) 推断前缀,并在 convertStringArrayToDate 中不传 columns。当列被重排或为子集时,前缀与默认 Y→S 顺序不一致,extend.date 会被错误解析,进而导致 filter 结果不正确。应基于“实际渲染列顺序 renderedColumns”构造前缀与 columns 再传入转换函数。下面补丁同时将 columnType 传入,彻底消除错位问题。

-  const generateColumn = (
-    from: number,
-    to: number,
-    precision: DatePrecision
-  ) => {
+  const generateColumn = (
+    from: number,
+    to: number,
+    p: DatePrecision,
+    columnType: DateColumnType
+  ) => {
     let column: number[] = []
     for (let i = from; i <= to; i++) {
       column.push(i)
     }
-    const prefix = selected.slice(0, precisionRankRecord[precision])
-    const currentFilter = filter?.[precision]
+    // 以“实际渲染列顺序”定位前缀,避免列重排导致取值错位
+    const pos = Math.max(0, renderedColumns.indexOf(columnType))
+    const prefix = selected.slice(0, pos)
+    const partialColumns = renderedColumns.slice(0, pos).concat(columnType)
+    const currentFilter = filter?.[p]
     if (currentFilter && typeof currentFilter === 'function') {
       column = column.filter(i =>
         currentFilter(i, {
           get date() {
             const stringArray = [...prefix, i.toString()]
-            return convertStringArrayToDate(stringArray, undefined, precision)
+            return convertStringArrayToDate(stringArray, partialColumns, p)
           },
         })
       )
     }
     return column
   }
@@
-        const years = generateColumn(lower, upper, 'year')
+        const years = generateColumn(lower, upper, 'year', YEAR_COLUMN)
@@
-        const months = generateColumn(lowerMonth, upperMonth, 'month')
+        const months = generateColumn(lowerMonth, upperMonth, 'month', MONTH_COLUMN)
@@
-        const days = generateColumn(lowerDay, upperDay, 'day')
+        const days = generateColumn(lowerDay, upperDay, 'day', DAY_COLUMN)
@@
-        const hours = generateColumn(lowerHour, upperHour, 'hour')
+        const hours = generateColumn(lowerHour, upperHour, 'hour', HOUR_COLUMN)
@@
-        const minutes = generateColumn(lowerMinute, upperMinute, 'minute')
+        const minutes = generateColumn(lowerMinute, upperMinute, 'minute', MINUTE_COLUMN)
@@
-        const seconds = generateColumn(lowerSecond, upperSecond, 'second')
+        const seconds = generateColumn(lowerSecond, upperSecond, 'second', SECOND_COLUMN)

Also applies to: 149-222


139-141: 在 filter 的日期构造中缺少 columns(与上条合并修复)

这里未传入 columns 导致自定义顺序下解析错误,见上方补丁已一并修复为传 partialColumns

🧹 Nitpick comments (3)
src/components/date-picker/date-picker-date-utils.ts (2)

98-104: 对选中年份做一次夹取,防御异常输入

selectedYear 可能来自异常值(如解析错位或越界)。建议在使用前按 [minYear, maxYear] 再次夹取,避免后续边界判断漂移。

-  const selectedYear = getValue(YEAR_COLUMN) ?? min.getFullYear()
+  let selectedYear = getValue(YEAR_COLUMN) ?? min.getFullYear()
   const selectedMonth = getValue(MONTH_COLUMN) ?? 1
   const selectedDay = getValue(DAY_COLUMN) ?? 1
   const selectedHour = getValue(HOUR_COLUMN) ?? 0
   const selectedMinute = getValue(MINUTE_COLUMN) ?? 0
   const selectedSecond = getValue(SECOND_COLUMN) ?? 0
+  // 防御性夹取
+  selectedYear = Math.max(minYear, Math.min(maxYear, selectedYear))

293-299: parseInt 明确指定进制,保持一致性

为避免隐式进制和风格不一致,建议统一使用 parseInt(x, 10)

-    parseInt(dateParts[YEAR_COLUMN]),
-    parseInt(dateParts[MONTH_COLUMN]) - 1,
-    parseInt(dateParts[DAY_COLUMN]),
-    parseInt(dateParts[HOUR_COLUMN]),
-    parseInt(dateParts[MINUTE_COLUMN]),
-    parseInt(dateParts[SECOND_COLUMN])
+    parseInt(dateParts[YEAR_COLUMN], 10),
+    parseInt(dateParts[MONTH_COLUMN], 10) - 1,
+    parseInt(dateParts[DAY_COLUMN], 10),
+    parseInt(dateParts[HOUR_COLUMN], 10),
+    parseInt(dateParts[MINUTE_COLUMN], 10),
+    parseInt(dateParts[SECOND_COLUMN], 10)
src/components/date-picker/date-picker-utils.ts (1)

48-56: 判定分支建议统一 startsWith 语义,保持一致

同文件其它位置使用了 startsWith('week'/'quarter');这里用 includes 含义更宽(潜在误判)。建议统一为 startsWith

-  if (precision.includes('week')) {
+  if (precision.startsWith('week')) {
@@
-  } else if (precision.includes('quarter')) {
+  } else if (precision.startsWith('quarter')) {

Also applies to: 95-99

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f737b4 and 687940b.

📒 Files selected for processing (2)
  • src/components/date-picker/date-picker-date-utils.ts (5 hunks)
  • src/components/date-picker/date-picker-utils.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/components/date-picker/date-picker-date-utils.ts (3)
src/components/date-picker/util.ts (8)
  • DateColumnType (14-20)
  • YEAR_COLUMN (7-7)
  • MONTH_COLUMN (8-8)
  • DAY_COLUMN (9-9)
  • HOUR_COLUMN (10-10)
  • MINUTE_COLUMN (11-11)
  • SECOND_COLUMN (12-12)
  • TILL_NOW (1-1)
src/components/date-picker/date-picker-utils.ts (3)
  • generateDatePickerColumns (108-148)
  • DatePickerFilter (21-31)
  • convertStringArrayToDate (81-106)
src/components/date-picker-view/date-picker-view.tsx (1)
  • RenderLabel (21-27)
src/components/date-picker/date-picker-utils.ts (5)
src/components/picker-view/columns-extend.tsx (1)
  • columns (34-36)
src/components/date-picker/util.ts (7)
  • DateColumnType (14-20)
  • YEAR_COLUMN (7-7)
  • MONTH_COLUMN (8-8)
  • DAY_COLUMN (9-9)
  • HOUR_COLUMN (10-10)
  • MINUTE_COLUMN (11-11)
  • SECOND_COLUMN (12-12)
src/components/date-picker/date-picker-date-utils.ts (2)
  • DatePrecision (23-29)
  • convertStringArrayToDate (257-300)
src/components/date-picker/date-picker-week-utils.ts (1)
  • convertStringArrayToDate (132-146)
src/components/date-picker/date-picker-quarter-utils.ts (1)
  • convertStringArrayToDate (106-118)
⏰ 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). (4)
  • GitHub Check: build preview
  • GitHub Check: compressed-size
  • GitHub Check: check
  • GitHub Check: size
🔇 Additional comments (4)
src/components/date-picker/date-picker-date-utils.ts (1)

226-238: “至今”选项插入到首列且联动清空后续列的实现清晰可控,LGTM

交互符合预期:首列追加 TILL_NOW,命中后清空后续列。

src/components/date-picker/date-picker-utils.ts (3)

56-79: 按精度过滤后再按自定义列顺序映射初值,逻辑正确

当提供 columns 时先依据精度裁剪,再按自定义顺序映射,避免值长度与可见列不一致问题。


81-106: precisioncolumns 透传给底层解析,修正默认顺序依赖问题

这里把两个参数一并传递到 dateUtils.convertStringArrayToDate,可确保解析在列重排/子集时仍正确。


42-79: 确认:无需修改调用点

已核验仓内调用:convertDateToStringArray 在 src/components/date-picker-view/date-picker-view.tsx 与 src/components/date-picker/date-picker.tsx;generateDatePickerColumns 在 src/components/date-picker/date-picker.tsx 与 src/components/date-picker-view/date-picker-view.tsx。底层 date/quarter/week 模块保留单参实现并由顶层委托;columns 为可选参数,现有调用可通过编译,无需改动。

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/components/date-picker/date-picker-date-utils.ts (1)

154-246: 使用 forEach 和 switch 的实现可以简化

虽然当前实现功能正确,但可以考虑使用数组映射来减少重复代码。

可以创建一个配置对象来简化这些重复的 case 分支:

const columnConfigs = {
  [YEAR_COLUMN]: { 
    lower: minYear, 
    upper: maxYear, 
    selected: selectedYear, 
    precision: 'year' as DatePrecision 
  },
  [MONTH_COLUMN]: { 
    lower: isInMinYear ? minMonth : 1, 
    upper: isInMaxYear ? maxMonth : 12, 
    selected: selectedMonth, 
    precision: 'month' as DatePrecision 
  },
  // ... 其他配置
}

renderedColumns.forEach(columnType => {
  const config = columnConfigs[columnType]
  if (!config) return
  
  const column = generateColumn(config.lower, config.upper, config.precision, columnType)
  ret.push(
    column.map(v => ({
      label: renderLabel(config.precision, v, { selected: config.selected === v }),
      value: v.toString(),
    }))
  )
})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 687940b and fd37168.

📒 Files selected for processing (1)
  • src/components/date-picker/date-picker-date-utils.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/date-picker/date-picker-date-utils.ts (3)
src/components/date-picker/util.ts (8)
  • DateColumnType (14-20)
  • YEAR_COLUMN (7-7)
  • MONTH_COLUMN (8-8)
  • DAY_COLUMN (9-9)
  • HOUR_COLUMN (10-10)
  • MINUTE_COLUMN (11-11)
  • SECOND_COLUMN (12-12)
  • TILL_NOW (1-1)
src/components/date-picker/date-picker-utils.ts (3)
  • generateDatePickerColumns (108-148)
  • DatePickerFilter (21-31)
  • convertStringArrayToDate (81-106)
src/components/date-picker-view/date-picker-view.tsx (1)
  • RenderLabel (21-27)
⏰ 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). (4)
  • GitHub Check: size
  • GitHub Check: build preview
  • GitHub Check: compressed-size
  • GitHub Check: check
🔇 Additional comments (4)
src/components/date-picker/date-picker-date-utils.ts (4)

105-109: firstDayInSelectedMonth 计算逻辑正确

使用 convertStringArrayToDate 传递了正确的参数,包括 columnsprecision,确保了与新 API 的兼容性。


123-152: generateColumn 函数的 filter 回调需要完善

在 Line 142-146 中,convertStringArrayToDate 的调用看起来是正确的,传递了 partialColumnsprecision 参数。


249-260: tillNow 逻辑实现正确

正确检查了 ret.length > 0 并且只在第一列添加 "now" 选项,当选择了 TILL_NOW 时清空后续列的逻辑也是合理的。


279-322: convertStringArrayToDate 函数重构合理

新的实现正确支持了 columnsprecision 参数,使用 dateParts 映射的方式也很清晰,能够处理不同的列顺序组合。

@zombieJ
Copy link
Member

zombieJ commented Sep 16, 2025

@gemini-code-assist pls help to review again

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 为 DatePicker 组件增加了 columns 属性,支持自定义日期列的顺序和仅选择部分字段,是一个很棒的功能增强。代码实现上做了比较大的重构,特别是 generateDatePickerColumns 函数,使其更加模块化和可读。同时,也为新功能添加了相应的单元测试,值得称赞。

我发现了一个在自定义列顺序时可能导致日期计算错误的 bug,以及一处代码重复的问题。修复这些问题会让这个新功能更加健壮。

Comment on lines 105 to 109
convertStringArrayToDate(
[selectedYear, selectedMonth, '1'],
columns ?? defaultColumns,
precision
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

此处 convertStringArrayToDate 的使用方式存在问题。该函数期望的第一个参数 value 数组中的元素顺序与第二个参数 columns 数组的顺序严格对应。然而,这里硬编码了 [selectedYear, selectedMonth, '1'],它假定了列的顺序是年、月、日,当 columns prop 自定义顺序时(例如 ['month', 'day', 'year']),这会导致错误的日期计算,进而影响天数滚轮的范围。

由于 selectedYearselectedMonth 已经包含了正确的回退值,可以直接使用它们来构造日期,这样更清晰且能避免上述 bug。

    new Date(selectedYear, selectedMonth - 1, 1)

Comment on lines 76 to 82
const defaultColumns: DateColumnType[] = []
Object.keys(columnToPrecisionMap).forEach(columnType => {
const precision = columnToPrecisionMap[columnType as DateColumnType]
if (rank >= precisionRankRecord[precision]) {
defaultColumns.push(columnType as DateColumnType)
}
})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

这部分根据 precision 生成默认 columns 的逻辑,在 convertStringArrayToDate 函数(第 252-260 行)中也存在。代码重复会增加未来维护的成本。建议将这部分逻辑提取到一个独立的辅助函数中,例如 getDefaultColumns(precision),然后在两处都调用这个新函数,以遵循 DRY (Don't Repeat Yourself) 原则。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature lgtm This PR has been approved by a maintainer size:XL This PR changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DatePicker reorder columns
2 participants