Skip to content

Comments

feat: pickerview#2986

Merged
oasis-cloud merged 26 commits intojdf2e:feat_v3.xfrom
irisSong:feat_v3.x_pickerview
Feb 14, 2025
Merged

feat: pickerview#2986
oasis-cloud merged 26 commits intojdf2e:feat_v3.xfrom
irisSong:feat_v3.x_pickerview

Conversation

@irisSong
Copy link
Collaborator

@irisSong irisSong commented Feb 12, 2025

🤔 这个变动的性质是?

  • 新特性提交
  • 日常 bug 修复
  • 站点、文档改进
  • 演示代码改进
  • 组件样式/交互改进
  • TypeScript 定义更新
  • 包体积优化
  • 性能优化
  • 功能增强
  • 国际化改进
  • 重构
  • 代码风格优化
  • 测试用例
  • 分支合并
  • 其他改动(是关于什么的改动?)

🔗 相关 Issue

💡 需求背景和解决方案

☑️ 请求合并前的自查清单

⚠️ 请自检并全部勾选全部选项⚠️

  • 文档已补充或无须补充
  • 代码演示已提供或无须提供
  • TypeScript 定义已补充或无须补充
  • fork仓库代码是否为最新避免文件冲突
  • Files changed 没有 package.json lock 等无关文件

Summary by CodeRabbit

  • 新功能

    • 新增“选择器视图”组件,支持多列、级联选择及异步数据更新,适用于数据录入场景。
    • 提供丰富的演示示例,涵盖基础用法、平铺布局、受控操作及跨平台(Taro)应用。
  • 文档

    • 新增详细的中、英、繁体多语言文档,全面说明组件用法、属性设置和主题定制方法。
  • 样式

    • 优化了组件样式和滚筒动画效果,提升了视觉表现和用户体验。

@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2025

Walkthrough

此次变更在配置文件中新增了 PickerView 组件条目,并为其引入了测试用例、演示组件(适用于 H5 和 Taro 平台)、文档、样式、类型定义以及工具函数。同时,还添加了支持触摸滚动效果的 PickerRoller 组件,并在入口文件中导出相应类型,使整个 PickerView 组件的功能得到完善和对外暴露。

Changes

文件/文件组 变更摘要
src/config.json 在“数据录入”部分新增 PickerView 组件配置条目
src/packages/pickerview/__test__/pickerview.spec.tsx 添加 PickerView 组件的测试用例
src/packages/pickerview/demo.taro.tsx, src/packages/pickerview/demo.tsx 添加 PickerViewDemo 组件演示(Taro 与 Web 版本)
src/packages/pickerview/demos/h5/demo*.tsx, src/packages/pickerview/demos/taro/demo*.tsx 添加多个示例组件(Demo1 至 Demo7,分别针对 H5 与 Taro)
src/packages/pickerview/doc*.md 新增 PickerView 组件文档(英文、中文、繁体及 Taro 版本)
src/packages/pickerview/index.taro.ts, src/packages/pickerview/index.ts 添加 PickerView 组件及相关类型的入口文件
src/packages/pickerview/pickerroller.taro.tsx, src/packages/pickerview/pickerroller.tsx 添加 PickerRoller 组件,支持触摸滚动和惯性效果
src/packages/pickerview/pickerview.taro.tsx, src/packages/pickerview/pickerview.tsx 添加 PickerView 组件实现,支持多列、级联等选择逻辑
src/packages/pickerview/types.ts 新增 PickerView 相关的类型和接口定义(如 PickerValue、PickerOptionItem 等)
src/packages/pickerview/pickerview.scss, src/styles/variables.scss 新增 PickerView 样式及更新 SCSS 变量(添加 $picker-item-height,调整 $picker-list-height)
src/packages/pickerview/utils.ts 新增 momentum 函数和 useStyles 工具函数,用于触摸事件与样式计算

Sequence Diagram(s)

sequenceDiagram
    participant User as 用户
    participant PV as PickerView
    participant PR as PickerRoller
    participant Callback as onChange

    User->>PV: 开始触摸与选择操作
    PV->>PR: 转发触摸事件
    PR->>PR: 处理滚动、惯性计算
    PR-->>PV: 更新组件选中值
    PV->>Callback: 触发 onChange 回调
Loading

Suggested reviewers

  • xiaoyatong
  • oasis-cloud
  • Alex-huxiyang

Poem

我是跳跃的小兔子,
代码花园春意盎然,
PickerView 与 Roller共舞,
每次触摸都写下奇迹,
更新中藏着灵感的甜蜜,
开发路上欢乐不停,
跟随我一起奔向新篇!


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added 3.x Target branch 3.x action:review This PR needs more reviews (less than 2 approvals) labels Feb 12, 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: 14

🔭 Outside diff range comments (1)
src/packages/pickerview/demos/taro/demo6.tsx (1)

1-81: 🛠️ Refactor suggestion

建议重构以减少代码重复

H5 和 Taro 版本的 Demo6 代码几乎完全相同,建议创建一个共享的基础组件来减少代码重复。

建议采用以下方式重构:

  1. 创建共享的数据文件:
+// src/packages/pickerview/demos/data/cities.ts
+export const citiesData = [...]
  1. 创建共享的基础组件:
+// src/packages/pickerview/demos/base/demo6.base.tsx
+import React from 'react'
+import { citiesData } from '../data/cities'
+
+export const createDemo6 = (PickerView, Cell) => {
+  return () => {
+    return (
+      <>
+        <Cell>
+          <PickerView
+            defaultValue={[1]}
+            options={citiesData}
+            onChange={({ value, selectedOptions }) => {
+              if (process.env.NODE_ENV === 'development') {
+                console.log('onChange', value, selectedOptions)
+              }
+            }}
+          />
+        </Cell>
+      </>
+    )
+  }
+}
  1. 在 H5 和 Taro 版本中复用:
+// H5 version
+import { PickerView, Cell } from '@nutui/nutui-react'
+import { createDemo6 } from '../base/demo6.base'
+
+export default createDemo6(PickerView, Cell)

+// Taro version
+import { PickerView, Cell } from '@nutui/nutui-react-taro'
+import { createDemo6 } from '../base/demo6.base'
+
+export default createDemo6(PickerView, Cell)
🧹 Nitpick comments (42)
src/packages/pickerview/pickerview.tsx (3)

82-83: 使用可选链精简条件判断
可以将 columnOptions && columnOptions.children 简化为可选链写法,增强可读性并避免潜在的空对象解引用。

-    while (columnOptions && columnOptions.children) {
+    while (columnOptions?.children) {
       ...
     }
🧰 Tools
🪛 Biome (1.9.4)

[error] 83-83: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


116-119: 避免对数组直接进行引用比较
此处的比较 props.options !== innerOptions 可能在引用不同而内容相同的场景下被误触发。可考虑使用深度比较或针对更新条件进行更精细的处理,以避免重复渲染。


127-166: 拆分级联和多列选择逻辑
handleSelect 方法中针对级联和多列的处理逻辑相对冗长,可将其中的级联部分提取为独立函数,减少分支内的嵌套。这样有助于后续维护与测试,也提高代码可读性。

src/packages/pickerview/pickerview.taro.tsx (3)

83-84: 考虑使用可选链优化循环条件
与非 Taro 版本类似,这里 columnOptions && columnOptions.children 建议改为可选链形式,以减少不必要的空值判断。

-    while (columnOptions && columnOptions.children) {
+    while (columnOptions?.children) {
       ...
     }
🧰 Tools
🪛 Biome (1.9.4)

[error] 84-84: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


116-119: 避免对数组直接进行引用比较
此处 props.options !== innerOptions 的比较条件,在 React 中可能带来额外的渲染开销。考虑改为使用深度比较或其他特定比较方案解决。


128-166: 简化级联与多列逻辑
handleSelect 方法容纳了两套逻辑,代码量较大,可将级联逻辑单独提取,以方便后续维护与单元测试。

src/packages/pickerview/pickerroller.tsx (2)

60-65: 通过减少远距离元素渲染提升性能
isHidden 方法仅对大于/小于当前索引 ±8 的条目返回 true。在渲染内容较多时,可进一步优化判断,加快组件渲染效率,例如通过分段渲染或虚拟列表技术来避免过长的 DOM 节点。


82-91: 校验滚动边界时提升可读性
updateMove 的边界判断虽然逻辑正确,但可考虑通过函数封装或命名常量来提升可读性,比如 minMovemaxMove 的定义,让阅读逻辑更直观明了。

src/packages/pickerview/pickerroller.taro.tsx (3)

29-39: 建议在 ref 与状态之间保持一致性,以便减少潜在的逻辑混淆。
目前对 movinglineSpacing 等变量混合使用 useRefuseState,需要检查它们在渲染周期里的更新频率和用法场景,以保证逻辑持续一致,避免意外的渲染或状态无法同步。


224-260: 每次渲染都重复添加与移除事件监听器,可能造成性能开销。
useEffect 没有指定依赖数组,组件每次重新渲染都会执行事件绑定与解绑;另外,使用 { once: true } 会在首次触发后移除监听器,需确认是否满足滚动需求。若需多次滚动事件,建议去除 once: true 并在依赖数组中仅执行一次绑定。

- const eventOptions = passiveSupported ? { passive: false, once: true } : false
+ const eventOptions = passiveSupported ? { passive: false } : false

useEffect(() => {
  ...
- })
+ }, [])

195-200: 可在惯性滚动结束后集中处理滚动结果。
stopMomentummoving 设置为 false 并调用 setChooseValue,逻辑合理。但若需额外处理收尾事件(如回调队列或埋点数据)可以在此处统一集中处理,减少各钩子之间的耦合。

src/packages/pickerview/demos/h5/demo1.tsx (3)

5-17: 建议将列表数据提取为共享常量

由于多个演示组件使用相同的数据结构,建议将 listData 提取到一个共享的常量文件中以提高代码复用性。

建议创建 src/packages/pickerview/demos/data.ts 文件:

export const cityList = [
  { value: 1, label: '南京市' },
  { value: 2, label: '无锡市' },
  // ...其他城市
]

export const defaultDemo = {
  options: [[...cityList]],
  defaultValue: [1]
}

25-27: 移除生产环境中的 console.log

在生产环境中使用 console.log 不是最佳实践。建议使用 proper 的日志系统或移除此代码。

-onChange={({ value, selectedOptions }) => {
-  console.log('onChange', value, selectedOptions)
-}}
+onChange={({ value, selectedOptions }) => {
+  // 在这里处理选择变更
+}}

22-28: 增加无障碍属性

为了提高组件的可访问性,建议添加 ARIA 属性。

 <PickerView
   defaultValue={[1]}
   options={listData}
+  aria-label="城市选择器"
+  role="listbox"
   onChange={({ value, selectedOptions }) => {
     console.log('onChange', value, selectedOptions)
   }}
 />
src/packages/pickerview/demos/taro/demo1.tsx (2)

5-17: 建议将列表数据提取为共享常量

Taro 和 H5 版本使用相同的数据结构,建议提取共享数据以确保跨平台一致性。

参考上述 H5 版本的数据提取建议。


25-27: 优化 onChange 处理函数

在 Taro 环境中,建议使用 Taro 的日志系统替代 console.log。

-onChange={({ value, selectedOptions }) => {
-  console.log('onChange', value, selectedOptions)
-}}
+onChange={({ value, selectedOptions }) => {
+  // 使用 Taro.showToast 或其他适合的反馈方式
+}}
src/packages/pickerview/demos/h5/demo2.tsx (1)

23-23: 建议将样式配置移至主题文件

内联样式建议移至主题配置文件中,以便统一管理和复用。

建议创建主题配置文件,将样式变量集中管理:

// src/packages/pickerview/styles/variables.ts
export const pickerViewVariables = {
  itemHeight: '28px',
  // 其他样式变量
}
src/packages/pickerview/demos/taro/demo2.tsx (2)

23-23: 建议使用主题变量而不是直接设置CSS变量

建议使用NutUI的主题系统来设置picker项的高度,这样可以保持与其他组件的一致性。

-          style={{ '--nutui-picker-item-height': '28px' }}
+          style={{ height: 'var(--nutui-picker-item-height, 28px)' }}

26-28: 建议增加错误处理

onChange事件处理器目前只打印日志,建议添加错误处理并通知用户选择结果。

           onChange={({ value, selectedOptions }) => {
-            console.log('onChange', value, selectedOptions)
+            try {
+              console.log('onChange', value, selectedOptions)
+              // 添加用户通知或状态更新
+            } catch (error) {
+              console.error('选择出错:', error)
+            }
           }}
src/packages/pickerview/demos/h5/demo5.tsx (1)

27-29: 建议增加事件处理的类型定义

onChange回调函数的参数建议添加类型定义,以提高代码的可维护性。

-          onChange={({ value, selectedOptions }) => {
+          onChange={({ value, selectedOptions }: { value: number[], selectedOptions: Array<{value: number, label: string}> }) => {
             console.log('onChange', value, selectedOptions)
           }}
src/packages/pickerview/demos/h5/demo4.tsx (2)

29-31: 建议优化选择值变更的逻辑

当选择值为3时直接设置为1的逻辑不够清晰,建议添加注释说明业务逻辑,并考虑使用更具描述性的条件判断。

-            if (value[0] === 3) {
-              setValue([1])
-            }
+            // 当选择"海北藏族自治区"时,自动切换到"南京市"
+            const AUTO_SWITCH_FROM_VALUE = 3
+            const DEFAULT_FALLBACK_VALUE = 1
+            if (value[0] === AUTO_SWITCH_FROM_VALUE) {
+              setValue([DEFAULT_FALLBACK_VALUE])
+            }

19-19: 建议添加初始值的类型定义

useState的初始值建议添加明确的类型定义,以提高代码的可维护性。

-  const [value, setValue] = useState([2])
+  const [value, setValue] = useState<number[]>([2])
src/packages/pickerview/demos/taro/demo4.tsx (2)

19-19: 建议使用常量定义初始值

避免在 useState 中使用魔法数字。建议将初始值定义为常量,提高代码可维护性。

+ const DEFAULT_CITY_VALUE = 2
- const [value, setValue] = useState([2])
+ const [value, setValue] = useState([DEFAULT_CITY_VALUE])

27-32: 建议优化 onChange 处理逻辑

  1. 生产环境代码中不应该保留 console.log
  2. 建议将硬编码的条件值提取为常量
  3. 建议添加注释说明值变更的业务逻辑
+ const TRIGGER_VALUE = 3
+ const RESET_VALUE = 1
  onChange={({ value, selectedOptions }) => {
-   console.log('onChange', value, selectedOptions)
-   if (value[0] === 3) {
-     setValue([1])
+   // 当选择海北藏族自治区时重置为南京市
+   if (value[0] === TRIGGER_VALUE) {
+     setValue([RESET_VALUE])
    }
  }}
src/packages/pickerview/demos/h5/demo3.tsx (1)

28-33: 建议优化 onChange 回调函数

  1. 移除生产环境中的 console.log
  2. 建议将条件判断值提取为常量,增加代码可读性
  3. 建议添加注释说明此处的业务逻辑
+ const TRIGGER_VALUES = ['Tuesday', 'Afternoon']
+ const RESET_VALUES = ['Monday', 'Evening']
  onChange={({ value, selectedOptions }) => {
-   console.log('onChange', value, selectedOptions)
-   if (isEqual(value, ['Tuesday', 'Afternoon'])) {
-     setValue(['Monday', 'Evening'])
+   // 当选择周二下午时重置为周一晚上
+   if (isEqual(value, TRIGGER_VALUES)) {
+     setValue(RESET_VALUES)
    }
  }}
src/packages/pickerview/types.ts (3)

30-30: 建议优化 setRefs 的类型定义

当前 setRefs 使用 any 类型,建议使用更具体的类型定义以提供更好的类型安全性。

- setRefs?: (ref: any) => any
+ setRefs?: (ref: HTMLElement | null) => void

5-11: 建议增强 PickerOptionItem 接口定义

  1. 建议添加 readonly 修饰符防止意外修改
  2. 建议为接口添加 JSDoc 注释说明用途
+ /**
+  * PickerView 选项项接口
+  */
 export interface PickerOptionItem {
-  label: string | number
-  value: string | number
-  children?: PickerOptionItem[]
+  readonly label: string | number
+  readonly value: string | number
+  readonly children?: readonly PickerOptionItem[]
 }

23-27: 建议完善回调参数接口的类型定义

建议为回调参数接口添加详细的 JSDoc 注释,说明每个字段的用途。

+ /**
+  * PickerView onChange 回调参数接口
+  * @property value - 当前选中的值数组
+  * @property index - 当前改变的列索引
+  * @property selectedOptions - 当前选中的选项数组
+  */
 export interface PickerOnChangeCallbackParameter {
   value: PickerValue[]
   index: number
   selectedOptions: PickerOptionItem[]
 }
src/packages/pickerview/demo.tsx (1)

38-51: 建议优化 Demo 展示顺序

当前的 Demo 展示顺序(基础用法 -> 受控 -> 自适应高度 -> 多列 -> 平铺 -> 级联)可以调整以提供更好的学习曲线。建议按照以下顺序排列:

  1. 基础用法
  2. 自适应高度
  3. 多列
  4. 级联
  5. 平铺
  6. 受控

这样的顺序从简单到复杂,更有利于用户理解组件的功能。

src/packages/pickerview/demos/h5/demo6.tsx (2)

5-64: 建议将数据结构抽离到单独的文件中

当前的城市和区域数据直接定义在组件中,建议将其抽离到单独的数据文件中以提高代码的可维护性和复用性。

+// src/packages/pickerview/demos/data/cities.ts
+export const citiesData = [
+  [
+    {
+      value: 1,
+      label: '北京',
+      children: [
+        // ... existing children
+      ],
+    },
+    // ... existing cities
+  ],
+]

-const listData = [...]
+import { citiesData } from '../data/cities'
+const listData = citiesData

72-74: 移除生产环境中的 console.log

在生产环境中应该移除 console.log 语句。建议使用条件编译或开发工具来处理这个问题。

-onChange={({ value, selectedOptions }) => {
-  console.log('onChange', value, selectedOptions)
-}}
+onChange={({ value, selectedOptions }) => {
+  if (process.env.NODE_ENV === 'development') {
+    console.log('onChange', value, selectedOptions)
+  }
+}}
src/packages/pickerview/pickerview.scss (4)

1-9: 布局结构合理,但建议优化变量复用

布局结构使用 flex 布局合理,但建议将重复使用的高度计算提取为变量以提高可维护性。

+$picker-list-height: calc($picker-item-height * 7);
 .nut-pickerview {
   --nutui-picker-item-height: 36px;
   position: relative;
   display: flex;
   width: 100%;
-  height: $picker-list-height;
+  height: $picker-list-height;
   $pickerview-top: calc(($picker-list-height - $picker-item-height) / 2);
   overflow: hidden;

10-27: 遮罩层实现合理,建议添加浏览器前缀

遮罩层和指示器的定位和层级设置合理,但建议为 transform 属性添加浏览器前缀以提高兼容性。

   &-mask {
     top: 0;
     bottom: 0;
     background-image: $picker-mask-background;
     background-position: top, bottom;
     background-size: 100% $pickerview-top;
     background-repeat: no-repeat;
+    -webkit-transform: translateZ(0);
+    -moz-transform: translateZ(0);
     transform: translateZ(0);
   }

46-65: 3D 效果实现完善,建议优化性能

3D 转换和背面可见性的处理合理,但建议添加 will-change 属性以优化动画性能。

   &-roller {
     position: absolute;
     top: $pickerview-top;
     width: 100%;
     height: $picker-item-height;
     z-index: 1;
     transform-style: preserve-3d;
+    will-change: transform;
   }

67-78: 文本样式处理合理,建议增加多语言支持

文本溢出和对齐的处理合理,但建议考虑为不同语言的文本长度添加自适应处理。

   &-roller-item,
   &-roller-item-title {
     width: 100%;
     height: $picker-item-height;
     line-height: $picker-item-height;
     color: $picker-item-text-color;
     font-size: $picker-item-text-font-size;
     text-align: center;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
+    word-break: break-word;
   }
src/packages/pickerview/doc.md (3)

1-60: 文档结构完整,建议补充更多使用场景

文档包含了基础用法、受控模式等示例,建议补充更多实际应用场景的示例代码。

建议添加以下场景的示例:

  • 自定义选项高亮样式
  • 动态加载选项
  • 异步更新数据
  • 错误处理

61-80: API 文档完善,建议增加事件说明

Props 的描述清晰,但建议补充事件触发时机和参数的详细说明。

建议在 onChange 事件中补充:

  • 参数的详细类型定义
  • 触发时机说明
  • 返回值示例

81-94: 主题定制说明完整,建议补充设计指南

CSS 变量的说明完整,建议补充设计规范和最佳实践指南。

建议添加:

  • 设计规范说明
  • 自定义主题示例
  • 常见定制场景
src/config.json (1)

693-705: 配置完整,建议补充依赖说明

组件配置包含了必要的元数据,但建议补充组件依赖关系说明。

建议在配置中补充:

 {
   "version": "3.0.0",
   "name": "PickerView",
   "type": "component",
   "cName": "选择器视图",
   "desc": "PickerView 是 Picker 的内容区域。",
   "sort": 15,
   "show": true,
   "taro": true,
   "v15": false,
   "dd": true,
-  "author": "songsong"
+  "author": "songsong",
+  "dependencies": ["Picker"]
 }
src/styles/variables.scss (1)

562-582: 变量定义合理,建议补充暗黑模式支持

变量定义和默认值设置合理,但建议补充暗黑模式下的变量值。

建议添加以下变量:

 $picker-item-height: var(--nutui-picker-item-height, 36px) !default;
 $picker-list-height: calc($picker-item-height * 7) !default;
+$picker-item-text-color-dark: var(--nutui-picker-item-text-color-dark, $color-text-dark) !default;
+$picker-item-active-line-border-dark: var(--nutui-picker-item-active-line-border-dark, 1px solid $color-border-dark) !default;
+$picker-mask-background-dark: var(--picker-mask-background-dark, linear-gradient(180deg, var(--nutui-black-12), var(--nutui-black-7))) !default;
src/packages/pickerview/doc.taro.md (1)

67-72: Props 文档需要补充说明

建议为以下属性添加更详细的说明:

  1. options 的具体数据结构示例
  2. valueselectedOptions 的具体数据格式
  3. onChange 回调函数的参数类型定义
src/packages/pickerview/doc.zh-TW.md (1)

89-93: 部分繁體中文術語翻譯建議優化

以下術語建議調整:

  1. "面闆" 應改為 "面板"
  2. "數據" 可考慮改為 "資料"
  3. "字色" 建議改為 "文字顏色"
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2836f0a and 7a27934.

📒 Files selected for processing (29)
  • src/config.json (1 hunks)
  • src/packages/pickerview/__test__/pickerview.spec.tsx (1 hunks)
  • src/packages/pickerview/demo.taro.tsx (1 hunks)
  • src/packages/pickerview/demo.tsx (1 hunks)
  • src/packages/pickerview/demos/h5/demo1.tsx (1 hunks)
  • src/packages/pickerview/demos/h5/demo2.tsx (1 hunks)
  • src/packages/pickerview/demos/h5/demo3.tsx (1 hunks)
  • src/packages/pickerview/demos/h5/demo4.tsx (1 hunks)
  • src/packages/pickerview/demos/h5/demo5.tsx (1 hunks)
  • src/packages/pickerview/demos/h5/demo6.tsx (1 hunks)
  • src/packages/pickerview/demos/taro/demo1.tsx (1 hunks)
  • src/packages/pickerview/demos/taro/demo2.tsx (1 hunks)
  • src/packages/pickerview/demos/taro/demo3.tsx (1 hunks)
  • src/packages/pickerview/demos/taro/demo4.tsx (1 hunks)
  • src/packages/pickerview/demos/taro/demo5.tsx (1 hunks)
  • src/packages/pickerview/demos/taro/demo6.tsx (1 hunks)
  • src/packages/pickerview/doc.en-US.md (1 hunks)
  • src/packages/pickerview/doc.md (1 hunks)
  • src/packages/pickerview/doc.taro.md (1 hunks)
  • src/packages/pickerview/doc.zh-TW.md (1 hunks)
  • src/packages/pickerview/index.taro.ts (1 hunks)
  • src/packages/pickerview/index.ts (1 hunks)
  • src/packages/pickerview/pickerroller.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerroller.tsx (1 hunks)
  • src/packages/pickerview/pickerview.scss (1 hunks)
  • src/packages/pickerview/pickerview.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerview.tsx (1 hunks)
  • src/packages/pickerview/types.ts (1 hunks)
  • src/styles/variables.scss (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/packages/pickerview/doc.en-US.md
🧰 Additional context used
🪛 GitHub Actions: CI
src/packages/pickerview/__test__/pickerview.spec.tsx

[error] 61-61: TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator)).

🪛 Biome (1.9.4)
src/packages/pickerview/pickerview.tsx

[error] 83-83: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/packages/pickerview/pickerview.taro.tsx

[error] 84-84: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 markdownlint-cli2 (0.17.2)
src/packages/pickerview/doc.taro.md

85-85: Link fragments should be valid
null

(MD051, link-fragments)

src/packages/pickerview/doc.zh-TW.md

85-85: Link fragments should be valid
null

(MD051, link-fragments)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (4)
src/packages/pickerview/pickerroller.tsx (1)

212-216: 在修改选中值后,补充边界保护
modifyStatus(false) 内部如果传入的 props.value 未匹配到任意 options,会将索引重置为 1,可能需要额外处理当 options 本身为空或传入值不在其索引范围时的场景。

src/packages/pickerview/index.ts (1)

1-10: 导出文件结构清晰,类型和主组件分离可读性好。
当前的导出方式有助于用户在使用时快速定位 PickerView 及相关类型,整体实现合理。

src/packages/pickerview/index.taro.ts (1)

1-10: 与标准版保持一致的导出结构,方便跨平台使用。
这种做法与普通 index.ts 相同,让 Taro 版本的组件保持一致的使用方式,很恰当。

src/packages/pickerview/demo.taro.tsx (1)

43-56: 建议优化 Demo 展示顺序

与 H5 版本相同,建议调整 Demo 的展示顺序以提供更好的学习曲线,从基础功能逐步过渡到高级特性。

Comment on lines 62 to 67
const [firstColumn] = props.options as PickerOptions[]
if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) {
return 'cascade'
}
return 'multiple'
}, [props.options])
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

在判定级联类型时增加空数组保护逻辑
如果外部传递的 options 为空数组,firstColumn[0] 会抛出错误,建议在访问之前增加数组为空的判断或使用可选链,提升健壮性。

import React from 'react'
import { PickerView, Cell } from '@nutui/nutui-react'

const Demo1 = () => {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

组件命名需要修正

文件名为 demo2.tsx 但组件名为 Demo1,这可能导致混淆。

-const Demo1 = () => {
+const Demo2 = () => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const Demo1 = () => {
const Demo2 = () => {


### 样式变量

组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

ConfigProvider 组件链接需要更新

链接片段 #/zh-CN/component/configprovider 格式不正确,这可能导致导航失败。

建议修改为:

-[ConfigProvider 组件](#/zh-CN/component/configprovider)
+[ConfigProvider 组件](/zh-CN/component/configprovider)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)
组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](/zh-CN/component/configprovider)
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

85-85: Link fragments should be valid
null

(MD051, link-fragments)


### 樣式變量

組件提供了下列 CSS 變量,可用於自定義樣式,使用方法請參考 [ConfigProvider 組件](#/zh-CN/component/configprovider)。
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

ConfigProvider 組件鏈接需要更新

鏈接片段 #/zh-CN/component/configprovider 格式不正確,這可能導致導航失敗。

建議修改為:

-[ConfigProvider 組件](#/zh-CN/component/configprovider)
+[ConfigProvider 組件](/zh-CN/component/configprovider)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
組件提供了下列 CSS 變量,可用於自定義樣式,使用方法請參考 [ConfigProvider 組件](#/zh-CN/component/configprovider)
組件提供了下列 CSS 變量,可用於自定義樣式,使用方法請參考 [ConfigProvider 組件](/zh-CN/component/configprovider)
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

85-85: Link fragments should be valid
null

(MD051, link-fragments)

@oasis-cloud oasis-cloud self-requested a review February 12, 2025 03:30
@github-actions github-actions bot added action:review This PR needs more reviews (less than 2 approvals) and removed action:review This PR needs more reviews (less than 2 approvals) labels Feb 12, 2025
@codecov
Copy link

codecov bot commented Feb 12, 2025

Codecov Report

Attention: Patch coverage is 87.52998% with 52 lines in your changes missing coverage. Please review.

Project coverage is 85.79%. Comparing base (9485fe0) to head (38296d3).
Report is 3 commits behind head on feat_v3.x.

Files with missing lines Patch % Lines
src/packages/pickerview/pickerroller.tsx 82.52% 36 Missing ⚠️
src/packages/pickerview/pickerview.tsx 92.89% 13 Missing ⚠️
src/packages/pickerview/utils.ts 88.46% 3 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           feat_v3.x    #2986      +/-   ##
=============================================
+ Coverage      85.75%   85.79%   +0.03%     
=============================================
  Files            277      281       +4     
  Lines          18110    18527     +417     
  Branches        2735     2810      +75     
=============================================
+ Hits           15531    15896     +365     
- Misses          2574     2626      +52     
  Partials           5        5              

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

@github-actions github-actions bot added action:review This PR needs more reviews (less than 2 approvals) and removed action:review This PR needs more reviews (less than 2 approvals) labels Feb 12, 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: 1

♻️ Duplicate comments (2)
src/packages/pickerview/pickerroller.tsx (1)

223-233: ⚠️ Potential issue

建议修复事件监听器的内存泄漏问题

当前的事件监听器实现存在以下问题:

  1. useEffect 缺少必要的依赖项
  2. 可能导致重复绑定事件
  3. 清理函数可能在组件卸载时无法正确执行
 useEffect(() => {
   const options = passiveSupported ? { passive: false } : false
+  const currentRef = pickerRollerRef.current
+  
-  pickerRollerRef.current?.addEventListener('touchstart', touchStart, options)
-  pickerRollerRef.current?.addEventListener('touchmove', touchMove, options)
-  pickerRollerRef.current?.addEventListener('touchend', touchEnd, options)
+  if (currentRef) {
+    currentRef.addEventListener('touchstart', touchStart, options)
+    currentRef.addEventListener('touchmove', touchMove, options)
+    currentRef.addEventListener('touchend', touchEnd, options)
+  }
+  
   return () => {
-    pickerRollerRef.current?.removeEventListener('touchstart', touchStart)
-    pickerRollerRef.current?.removeEventListener('touchmove', touchMove)
-    pickerRollerRef.current?.removeEventListener('touchend', touchEnd)
+    if (currentRef) {
+      currentRef.removeEventListener('touchstart', touchStart)
+      currentRef.removeEventListener('touchmove', touchMove)
+      currentRef.removeEventListener('touchend', touchEnd)
+    }
   }
-}, [])
+}, [touchStart, touchMove, touchEnd])
src/packages/pickerview/pickerroller.taro.tsx (1)

92-94: ⚠️ Potential issue

需要处理空数组边界情况

options.length 为 0 时,计算边界值可能会产生意外结果。建议添加空数组检查。

+      if (options.length === 0) {
+        updateMove = 0;
+        return;
+      }
       if (updateMove < -(options.length - 1) * lineSpacing.current) {
         updateMove = -(options.length - 1) * lineSpacing.current
       }
🧹 Nitpick comments (12)
src/packages/pickerview/__test__/pickerview.spec.tsx (2)

6-18: 测试数据建议使用更具代表性的示例

当前测试数据中的城市列表过于简单,建议:

  1. 添加不同长度的城市名称
  2. 包含特殊字符的城市名称
  3. 添加空值和边界值测试用例
 const listData = [
   [
+    { value: '', label: '' },  // 空值测试
     { value: 1, label: '南京市' },
     { value: 2, label: '无锡市' },
     { value: 3, label: '海北藏族自治区' },
+    { value: 4, label: '北京市(首都)' },  // 特殊字符
-    { value: 4, label: '北京市' },
     { value: 5, label: '连云港市' },
     { value: 8, label: '大庆市' },
     { value: 9, label: '绥化市' },
     { value: 10, label: '潍坊市' },
     { value: 12, label: '乌鲁木齐市' },
+    { value: null, label: '测试市' },  // null值测试
   ],
 ]

27-36: 建议完善平铺模式的测试用例

当前平铺模式的测试用例仅验证了渲染,建议增加:

  1. 样式验证
  2. 布局验证
  3. 交互验证
src/packages/pickerview/pickerview.tsx (2)

132-171: 建议优化 handleSelect 函数的复杂度

当前 handleSelect 函数逻辑较为复杂,建议:

  1. 拆分为多个子函数
  2. 添加详细的注释
  3. 增加错误处理
 const handleSelect = useCallback(
   (option: PickerOptionItem, index: number) => {
     const newValue = option?.value
     if (!newValue || innerValue[index] === newValue) return
     changeIndex.current = index
+    
+    // 处理多列选择模式
     if (columnsType === 'multiple') {
-      setInnerValue((prev) => {
-        const next = [...prev]
-        next[index] = newValue
-        return next
-      })
+      handleMultipleSelect(newValue, index)
     } else {
-      const startIndex = index
-      const values: PickerValue[] = []
-      values[index] = option.value
-      while (option?.children?.[0]) {
-        values[index + 1] = option.children[0].value
-        index++
-        option = option.children[0]
-      }
-      if (option?.children?.length) {
-        values[index + 1] = ''
-      }
-      const combineResult = [
-        ...innerValue.slice(0, startIndex),
-        ...values.splice(startIndex),
-      ]
-      setInnerValue([...combineResult])
+      handleCascadeSelect(option, index)
     }
   },
   [innerValue, props.options, columnsType, innerOptions]
 )

+ // 处理多列选择
+ const handleMultipleSelect = (newValue: PickerValue, index: number) => {
+   setInnerValue((prev) => {
+     const next = [...prev]
+     next[index] = newValue
+     return next
+   })
+ }
+
+ // 处理级联选择
+ const handleCascadeSelect = (option: PickerOptionItem, index: number) => {
+   try {
+     const startIndex = index
+     const values: PickerValue[] = []
+     values[index] = option.value
+     
+     // 递归获取子选项的值
+     while (option?.children?.[0]) {
+       values[index + 1] = option.children[0].value
+       index++
+       option = option.children[0]
+     }
+     
+     // 处理空子选项
+     if (option?.children?.length) {
+       values[index + 1] = ''
+     }
+     
+     const combineResult = [
+       ...innerValue.slice(0, startIndex),
+       ...values.splice(startIndex),
+     ]
+     setInnerValue([...combineResult])
+   } catch (error) {
+     console.error('级联选择处理失败:', error)
+   }
+ }

187-193: 建议优化 onChange 回调的依赖项

当前 useEffect 的依赖项可能导致不必要的重渲染,建议:

  1. 使用 useCallback 包装 onChange 回调
  2. 考虑使用 useMemo 缓存回调参数
+const callbackParams = useMemo(() => ({
+  value: innerValue,
+  index: changeIndex.current,
+  selectedOptions,
+}), [innerValue, selectedOptions])
+
 useEffect(() => {
-  onChange?.({
-    value: innerValue,
-    index: changeIndex.current,
-    selectedOptions,
-  })
+  onChange?.(callbackParams)
 }, [innerValue, selectedOptions, onChange])
src/packages/pickerview/pickerview.taro.tsx (1)

195-213: 建议增加 Taro 平台特定的处理

当前实现与 React 版本过于相似,建议:

  1. 添加 Taro 平台特有的性能优化
  2. 处理小程序的触摸事件差异
  3. 考虑小程序的渲染限制
 return (
   <View className={cls} style={style}>
+    {/* 使用 Taro 的虚拟列表优化性能 */}
+    <VirtualList
+      height={300}
+      itemData={innerOptions}
+      itemCount={innerOptions.length}
+      itemSize={44}
+    >
-    {innerOptions.map((item, index) => (
       <PickerRoller
         ref={props?.setRefs?.(index)}
         key={index}
         keyIndex={index}
         value={innerValue[index] as PickerValue}
         options={item}
         renderLabel={renderLabel}
         onSelect={handleSelect}
         duration={duration}
         threeDimensional={threeDimensional}
       />
-    ))}
+    </VirtualList>
     <View className="nut-pickerview-mask" />
     <View className="nut-pickerview-indicator" />
   </View>
 )
src/packages/pickerview/pickerroller.tsx (1)

130-138: 建议优化触摸事件处理逻辑

当前的触摸事件处理逻辑可以改进:

  1. 添加防抖处理
  2. 优化性能
  3. 增加错误处理
+const debouncedMove = useCallback(
+  debounce((move: number) => {
+    setMove(move)
+  }, 16),
+  []
+)
+
 const touchMove = (event: React.TouchEvent<HTMLDivElement>) => {
+  try {
     touch.move(event)
     if ((touch as any).isVertical) {
       moving.current = true
       preventDefault(event, true)
     }
     const move = touch.deltaY.current - startY
-    setMove(move)
+    debouncedMove(move)
+  } catch (error) {
+    console.error('触摸事件处理失败:', error)
+  }
 }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 131-138: src/packages/pickerview/pickerroller.tsx#L131-L138
Added lines #L131 - L138 were not covered by tests

src/packages/pickerview/pickerroller.taro.tsx (3)

20-20: 建议为 options 添加更具体的类型定义

当前 options = [] 的类型推断过于宽泛。建议明确指定类型以提高代码的可维护性。

-    options = [],
+    options: PickerOptionItem[] = [],

170-193: 建议优化值查找逻辑以提高性能

当前实现中存在多次数组遍历,可以通过单次遍历优化性能。

 const modifyStatus = (type?: boolean, val?: string | number) => {
   const value = val || props.value
-  let index = -1
-  if (value) {
-    options.some((item, idx) => {
-      if (item.value === value) {
-        index = idx
-        return true
-      }
-      return false
-    })
-  } else {
-    options.forEach((item, i) => {
-      if (item.value === props.value) {
-        index = i
-      }
-    })
-  }
+  const index = options.findIndex(item => 
+    item.value === (value || props.value)
+  )
   setCurrIndex(index === -1 ? 1 : index + 1)
   const move = index * lineSpacing.current
   type && setChooseValue(-move)
   setMove(-move)
 }

224-260: 建议优化事件监听器的清理逻辑

可以将事件监听器的添加和移除逻辑抽象为一个辅助函数,以减少代码重复并提高可维护性。

+ const setupEventListener = (
+   element: HTMLElement,
+   eventName: string,
+   handler: (event: TouchEvent) => void,
+   options: boolean | AddEventListenerOptions
+ ) => {
+   element?.addEventListener(eventName, handler, options)
+   return () => element?.removeEventListener(eventName, handler, options)
+ }

 useEffect(() => {
   const eventOptions = passiveSupported ? { passive: false, once: true } : false
-  pickerRollerRef.current?.addEventListener('touchstart', touchStart, eventOptions)
-  pickerRollerRef.current?.addEventListener('touchmove', touchMove, eventOptions)
-  pickerRollerRef.current?.addEventListener('touchend', touchEnd, eventOptions)
+  const cleanupFns = [
+    setupEventListener(pickerRollerRef.current, 'touchstart', touchStart, eventOptions),
+    setupEventListener(pickerRollerRef.current, 'touchmove', touchMove, eventOptions),
+    setupEventListener(pickerRollerRef.current, 'touchend', touchEnd, eventOptions)
+  ]
   return () => {
-    pickerRollerRef.current?.removeEventListener('touchstart', touchStart, eventOptions)
-    pickerRollerRef.current?.removeEventListener('touchmove', touchMove, eventOptions)
-    pickerRollerRef.current?.removeEventListener('touchend', touchEnd, eventOptions)
+    cleanupFns.forEach(cleanup => cleanup())
   }
 }, [])
src/packages/pickerview/doc.md (1)

85-85: ConfigProvider 组件链接需要更新

当前链接格式不正确,可能导致导航失败。

-[ConfigProvider 组件](#/zh-CN/component/configprovider)
+[ConfigProvider 组件](/zh-CN/component/configprovider)
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

85-85: Link fragments should be valid
null

(MD051, link-fragments)

src/packages/pickerview/doc.taro.md (1)

67-72: 建议完善 Props 类型说明

为了提高文档的准确性和可用性,建议补充以下内容:

  1. options 的具体类型定义
  2. valuedefaultValue 数组元素的类型说明
  3. onChange 回调参数的详细类型定义

建议修改为:

-| options | 列表数据 | `Array` | `[]` |
+| options | 列表数据 | `Array<{label: string \| number, value: string \| number, children?: Array}>` | `[]` |
-| value | 选中值,受控 | `Array` | `[]` |
+| value | 选中值,受控 | `Array<string \| number>` | `[]` |
-| defaultValue | 默认选中 | `Array` | `[]` |
+| defaultValue | 默认选中 | `Array<string \| number>` | `[]` |
src/packages/pickerview/doc.zh-TW.md (1)

67-72: 建議完善 Props 類型說明

為了提高文檔的準確性和可用性,建議補充以下內容:

  1. options 的具體類型定義
  2. valuedefaultValue 數組元素的類型說明
  3. onChange 回調參數的詳細類型定義

建議修改為:

-| options | 列錶數據 | `Array` | `[]` |
+| options | 列錶數據 | `Array<{label: string \| number, value: string \| number, children?: Array}>` | `[]` |
-| value | 選中值,受控 | `Array` | `[]` |
+| value | 選中值,受控 | `Array<string \| number>` | `[]` |
-| defaultValue | 默認選中 | `Array` | `[]` |
+| defaultValue | 默認選中 | `Array<string \| number>` | `[]` |
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7a27934 and f436c51.

⛔ Files ignored due to path filters (1)
  • src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (9)
  • src/packages/pickerview/__test__/pickerview.spec.tsx (1 hunks)
  • src/packages/pickerview/demo.taro.tsx (1 hunks)
  • src/packages/pickerview/doc.md (1 hunks)
  • src/packages/pickerview/doc.taro.md (1 hunks)
  • src/packages/pickerview/doc.zh-TW.md (1 hunks)
  • src/packages/pickerview/pickerroller.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerroller.tsx (1 hunks)
  • src/packages/pickerview/pickerview.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerview.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/packages/pickerview/demo.taro.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
src/packages/pickerview/pickerview.tsx

[error] 87-87: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/packages/pickerview/pickerview.taro.tsx

[error] 88-88: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 GitHub Check: codecov/patch
src/packages/pickerview/pickerview.tsx

[warning] 67-68: src/packages/pickerview/pickerview.tsx#L67-L68
Added lines #L67 - L68 were not covered by tests


[warning] 73-76: src/packages/pickerview/pickerview.tsx#L73-L76
Added lines #L73 - L76 were not covered by tests


[warning] 78-83: src/packages/pickerview/pickerview.tsx#L78-L83
Added lines #L78 - L83 were not covered by tests


[warning] 85-88: src/packages/pickerview/pickerview.tsx#L85-L88
Added lines #L85 - L88 were not covered by tests


[warning] 90-91: src/packages/pickerview/pickerview.tsx#L90-L91
Added lines #L90 - L91 were not covered by tests


[warning] 93-94: src/packages/pickerview/pickerview.tsx#L93-L94
Added lines #L93 - L94 were not covered by tests


[warning] 96-102: src/packages/pickerview/pickerview.tsx#L96-L102
Added lines #L96 - L102 were not covered by tests


[warning] 104-107: src/packages/pickerview/pickerview.tsx#L104-L107
Added lines #L104 - L107 were not covered by tests


[warning] 111-115: src/packages/pickerview/pickerview.tsx#L111-L115
Added lines #L111 - L115 were not covered by tests

src/packages/pickerview/pickerroller.tsx

[warning] 23-24: src/packages/pickerview/pickerroller.tsx#L23-L24
Added lines #L23 - L24 were not covered by tests


[warning] 68-80: src/packages/pickerview/pickerroller.tsx#L68-L80
Added lines #L68 - L80 were not covered by tests


[warning] 86-91: src/packages/pickerview/pickerroller.tsx#L86-L91
Added lines #L86 - L91 were not covered by tests


[warning] 94-98: src/packages/pickerview/pickerroller.tsx#L94-L98
Added lines #L94 - L98 were not covered by tests


[warning] 100-101: src/packages/pickerview/pickerroller.tsx#L100-L101
Added lines #L100 - L101 were not covered by tests


[warning] 112-114: src/packages/pickerview/pickerroller.tsx#L112-L114
Added lines #L112 - L114 were not covered by tests


[warning] 119-120: src/packages/pickerview/pickerroller.tsx#L119-L120
Added lines #L119 - L120 were not covered by tests


[warning] 124-128: src/packages/pickerview/pickerroller.tsx#L124-L128
Added lines #L124 - L128 were not covered by tests


[warning] 131-138: src/packages/pickerview/pickerroller.tsx#L131-L138
Added lines #L131 - L138 were not covered by tests


[warning] 141-143: src/packages/pickerview/pickerroller.tsx#L141-L143
Added lines #L141 - L143 were not covered by tests


[warning] 145-145: src/packages/pickerview/pickerroller.tsx#L145
Added line #L145 was not covered by tests


[warning] 147-155: src/packages/pickerview/pickerroller.tsx#L147-L155
Added lines #L147 - L155 were not covered by tests


[warning] 159-159: src/packages/pickerview/pickerroller.tsx#L159
Added line #L159 was not covered by tests


[warning] 161-161: src/packages/pickerview/pickerroller.tsx#L161
Added line #L161 was not covered by tests


[warning] 163-165: src/packages/pickerview/pickerroller.tsx#L163-L165
Added lines #L163 - L165 were not covered by tests


[warning] 176-176: src/packages/pickerview/pickerroller.tsx#L176
Added line #L176 was not covered by tests


[warning] 179-184: src/packages/pickerview/pickerroller.tsx#L179-L184
Added lines #L179 - L184 were not covered by tests


[warning] 194-197: src/packages/pickerview/pickerroller.tsx#L194-L197
Added lines #L194 - L197 were not covered by tests


[warning] 200-205: src/packages/pickerview/pickerroller.tsx#L200-L205
Added lines #L200 - L205 were not covered by tests


[warning] 207-210: src/packages/pickerview/pickerroller.tsx#L207-L210
Added lines #L207 - L210 were not covered by tests


[warning] 219-220: src/packages/pickerview/pickerroller.tsx#L219-L220
Added lines #L219 - L220 were not covered by tests

🪛 markdownlint-cli2 (0.17.2)
src/packages/pickerview/doc.taro.md

85-85: Link fragments should be valid
null

(MD051, link-fragments)

src/packages/pickerview/doc.md

85-85: Link fragments should be valid
null

(MD051, link-fragments)

src/packages/pickerview/doc.zh-TW.md

85-85: Link fragments should be valid
null

(MD051, link-fragments)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: test
  • GitHub Check: build
🔇 Additional comments (2)
src/packages/pickerview/doc.taro.md (1)

85-85: ConfigProvider 组件链接需要更新

链接格式不正确,建议修改为相对路径格式。

-[ConfigProvider 组件](#/zh-CN/component/configprovider)
+[ConfigProvider 组件](/zh-CN/component/configprovider)
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

85-85: Link fragments should be valid
null

(MD051, link-fragments)

src/packages/pickerview/doc.zh-TW.md (1)

85-85: ConfigProvider 組件鏈接需要更新

鏈接格式不正確,建議修改為相對路徑格式。

-[ConfigProvider 組件](#/zh-CN/component/configprovider)
+[ConfigProvider 組件](/zh-CN/component/configprovider)
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

85-85: Link fragments should be valid
null

(MD051, link-fragments)

Comment on lines 20 to 25
test('should match snapshot', () => {
const { container } = render(
<PickerView defaultValue={[1]} options={listData} />
)
expect(container).toMatchSnapshot()
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议增加更多测试场景

当前仅有快照测试,建议添加以下测试场景:

  1. 选项切换时的回调测试
  2. 默认值渲染测试
  3. 错误处理测试
  4. 用户交互测试
describe('PickerView', () => {
  it('应该正确触发 onChange 事件', () => {
    const onChange = jest.fn()
    const { container } = render(
      <PickerView
        defaultValue={[1]}
        options={listData}
        onChange={onChange}
      />
    )
    // 模拟选择操作
    // 验证回调是否被正确触发
  })

  it('应该正确处理无效的默认值', () => {
    const { container } = render(
      <PickerView
        defaultValue={[999]}  // 无效值
        options={listData}
      />
    )
    // 验证是否回退到第一个选项
  })
})

@github-actions github-actions bot added action:review This PR needs more reviews (less than 2 approvals) and removed action:review This PR needs more reviews (less than 2 approvals) labels Feb 12, 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: 1

♻️ Duplicate comments (2)
src/packages/pickerview/pickerroller.taro.tsx (2)

85-119: ⚠️ Potential issue

建议增加边界处理和简化计算逻辑

当前代码在处理空选项时可能出现问题,且计算逻辑可以简化:

  const setMove = (move: number, type?: string, time?: number) => {
+   if (options.length === 0) {
+     return
+   }
    let updateMove = move + transformY.current
    if (type === 'end') {
      // 限定滚动距离
      if (updateMove > 0) {
        updateMove = 0
      }
-     if (updateMove < -(options.length - 1) * lineSpacing.current) {
-       updateMove = -(options.length - 1) * lineSpacing.current
-     }
+     const maxMove = -(options.length - 1) * lineSpacing.current
+     updateMove = Math.max(updateMove, maxMove)

      // 设置滚动距离为lineSpacing.current的倍数值
-     const endMove =
-       Math.round(updateMove / lineSpacing.current) * lineSpacing.current
+     const endMove = Math.round(updateMove / lineSpacing.current) * lineSpacing.current

121-123: ⚠️ Potential issue

建议增加空值处理

在设置选中值时需要处理空值情况:

  const setChooseValue = (move: number) => {
+   if (options.length === 0) return
+   const selectedIndex = Math.round(-move / lineSpacing.current)
+   if (selectedIndex < 0 || selectedIndex >= options.length) return
-   onSelect?.(options?.[Math.round(-move / lineSpacing.current)], keyIndex)
+   onSelect?.(options[selectedIndex], keyIndex)
  }
🧹 Nitpick comments (9)
src/packages/pickerview/pickerroller.tsx (5)

27-47: 建议优化状态管理和常量定义

  1. 建议将相关状态组合成对象,减少 useState 的使用次数
  2. 建议将魔法数字(如 36、20)提取为命名常量,提高代码可维护性
+const CONSTANTS = {
+  LINE_SPACING: 36,
+  ROTATION_ANGLE: 20,
+  DEFAULT_DURATION: 200,
+  INERTIA_TIME: 300,
+  INERTIA_DISTANCE: 15,
+} as const
+
+interface TouchState {
+  time: number;
+  deg: string;
+  distance: number;
+}
+
-const lineSpacing = useRef(36)
-const [touchTime, setTouchTime] = useState(0)
-const [touchDeg, setTouchDeg] = useState('0deg')
-const rotation = 20
+const lineSpacing = useRef(CONSTANTS.LINE_SPACING)
+const [touchState, setTouchState] = useState<TouchState>({
+  time: 0,
+  deg: '0deg',
+  distance: 0
+})
+const rotation = CONSTANTS.ROTATION_ANGLE

122-156: 触摸事件处理逻辑缺少测试覆盖

触摸事件处理是组件的核心功能,但目前缺少测试覆盖。建议添加以下测试场景:

  1. 基本的触摸事件(开始、移动、结束)
  2. 惯性滚动触发条件
  3. 边界条件处理

需要我帮您生成这些测试用例的实现代码吗?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 124-128: src/packages/pickerview/pickerroller.tsx#L124-L128
Added lines #L124 - L128 were not covered by tests


[warning] 131-138: src/packages/pickerview/pickerroller.tsx#L131-L138
Added lines #L131 - L138 were not covered by tests


[warning] 141-143: src/packages/pickerview/pickerroller.tsx#L141-L143
Added lines #L141 - L143 were not covered by tests


[warning] 145-145: src/packages/pickerview/pickerroller.tsx#L145
Added line #L145 was not covered by tests


[warning] 147-155: src/packages/pickerview/pickerroller.tsx#L147-L155
Added lines #L147 - L155 were not covered by tests


157-165: 建议优化惯性滚动计算逻辑

  1. 建议将惯性系数 0.003 提取为可配置参数
  2. 建议添加注释说明惯性计算的物理原理
  3. 考虑添加减速度因子,使滚动更自然
+interface MomentumConfig {
+  coefficient: number;
+  deceleration: number;
+}
+
+const DEFAULT_MOMENTUM_CONFIG: MomentumConfig = {
+  coefficient: 0.003,
+  deceleration: 0.98,
+};
+
-const momentum = (distance: number, duration: number) => {
+// 基于物理模型计算惯性滚动距离
+// distance: 滑动距离
+// duration: 滑动时间
+// config: 惯性参数配置
+const momentum = (
+  distance: number,
+  duration: number,
+  config: MomentumConfig = DEFAULT_MOMENTUM_CONFIG
+) => {
   let nDistance = distance
   const speed = Math.abs(nDistance / duration)
-  nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1)
+  nDistance = (speed / config.coefficient) * 
+    (nDistance < 0 ? -1 : 1) * 
+    config.deceleration
   return nDistance
 }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 159-159: src/packages/pickerview/pickerroller.tsx#L159
Added line #L159 was not covered by tests


[warning] 161-161: src/packages/pickerview/pickerroller.tsx#L161
Added line #L161 was not covered by tests


[warning] 163-165: src/packages/pickerview/pickerroller.tsx#L163-L165
Added lines #L163 - L165 were not covered by tests


167-190: 建议简化值更新逻辑

当前值查找和更新逻辑较为复杂,建议重构以提高可读性:

 const modifyStatus = (type?: boolean, val?: string | number) => {
   const value = val || props.value
-  let index = -1
-  if (value) {
-    options.some((item, idx) => {
-      if (item.value === value) {
-        index = idx
-        return true
-      }
-      return false
-    })
-  } else {
-    options.forEach((item, i) => {
-      if (item.value === props.value) {
-        index = i
-      }
-    })
-  }
+  const index = options.findIndex(
+    (item) => item.value === (value || props.value)
+  )
+  const normalizedIndex = index === -1 ? 1 : index + 1
 
-  setCurrIndex(index === -1 ? 1 : index + 1)
+  setCurrIndex(normalizedIndex)
   const move = index * lineSpacing.current
   type && setChooseValue(-move)
   setMove(-move)
 }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 176-176: src/packages/pickerview/pickerroller.tsx#L176
Added line #L176 was not covered by tests


[warning] 179-184: src/packages/pickerview/pickerroller.tsx#L179-L184
Added lines #L179 - L184 were not covered by tests


256-292: 建议优化渲染性能

  1. 建议使用 useMemo 缓存样式计算结果:
+const memoizedRollerStyle = useMemo(
+  () => touchRollerStyle(),
+  [touchTime, touchDeg]
+)
+
+const memoizedTileStyle = useMemo(
+  () => touchTileStyle(),
+  [touchTime, scrollDistance]
+)

 return (
   <div className="nut-pickerview-list" ref={pickerRollerRef}>
     <div
       className="nut-pickerview-roller"
       ref={rollerRef}
-      style={threeDimensional ? touchRollerStyle() : touchTileStyle()}
+      style={threeDimensional ? memoizedRollerStyle : memoizedTileStyle}
  1. 建议使用 React.memo 优化选项渲染:
+const PickerItem = React.memo(
+  ({ item, index, isHidden, rollerStyle, renderLabel }) => (
+    <div
+      className={`nut-pickerview-roller-item ${
+        isHidden(index + 1) && 'nut-pickerview-roller-item-hidden'
+      }`}
+      style={rollerStyle(index)}
+      key={item.value ?? index}
+    >
+      {renderLabel(item)}
+    </div>
+  )
+)
src/packages/pickerview/pickerroller.taro.tsx (4)

14-28: 建议加强类型定义

为了提高代码的类型安全性,建议:

  1. keyIndex 添加更具体的类型约束
  2. duration 添加最小值限制
  3. renderLabel 的返回值添加明确的类型注解
const InternalPickerRoller: ForwardRefRenderFunction<
  { stopMomentum: () => void; moving: boolean },
  Partial<PickerRollerProps>
> = (props, ref) => {
  const {
-   keyIndex = 0,
+   keyIndex = 0 as const,
-   duration = 1000,
+   duration = Math.max(1000, 0),
    options = [],
    threeDimensional = true,
-   renderLabel = (item: PickerOptionItem) => {
+   renderLabel = (item: PickerOptionItem): string => {
      return item.label
    },
  } = props

29-49: 建议优化状态管理

当前状态变量较为分散,建议:

  1. 使用 reducer 统一管理相关状态
  2. 将魔法数字提取为命名常量
  3. 考虑使用 TypeScript 的 const assertions
+ const ROTATION_ANGLE = 20
+ const DEFAULT_CURR_INDEX = 1
+ const LINE_SPACING = 36
+ const touchState = {
+   time: 0,
+   deg: '0deg',
+   distance: 0,
+ }

- const [currIndex, setCurrIndex] = useState(1)
- const lineSpacing = useRef(36)
- const [touchTime, setTouchTime] = useState(0)
- const [touchDeg, setTouchDeg] = useState('0deg')
- const rotation = 20
+ const [currIndex, setCurrIndex] = useState(DEFAULT_CURR_INDEX)
+ const lineSpacing = useRef(LINE_SPACING)
+ const rotation = ROTATION_ANGLE as const

224-260: 建议优化事件处理

当前事件处理的实现可以通过以下方式优化:

  1. 使用 useCallback 缓存事件处理函数
  2. 优化 useEffect 的依赖数组
  3. 提取事件选项配置
+ const eventOptions = useMemo(
+   () => (passiveSupported ? { passive: false, once: true } : false),
+   []
+ )

+ const handleTouchStart = useCallback((event: React.TouchEvent<HTMLDivElement>) => {
+   touchStart(event)
+ }, [touchStart])

+ const handleTouchMove = useCallback((event: React.TouchEvent<HTMLDivElement>) => {
+   touchMove(event)
+ }, [touchMove])

+ const handleTouchEnd = useCallback(() => {
+   touchEnd()
+ }, [touchEnd])

  useEffect(() => {
-   const eventOptions = passiveSupported
-     ? { passive: false, once: true }
-     : false
    pickerRollerRef.current?.addEventListener(
      'touchstart',
-     touchStart,
+     handleTouchStart,
      eventOptions
    )
    // ... 其他事件监听器
-  }, [pickerRollerRef.current, touchStart, touchMove, touchEnd])
+  }, [handleTouchStart, handleTouchMove, handleTouchEnd, eventOptions])

262-315: 建议优化样式计算和渲染性能

当前样式计算和渲染逻辑可以通过以下方式优化:

  1. 使用 useMemo 缓存样式计算
  2. 优化 key 的生成
  3. 提取样式常量
+ const touchRollerStyle = useMemo(() => ({
+   transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`,
+   transform: `rotate3d(1, 0, 0, ${touchDeg})`,
+ }), [touchTime, touchDeg])

+ const getItemKey = useCallback((item: PickerOptionItem, index: number) => (
+   item.value?.toString() ?? `index-${index}`
+ ), [])

  {threeDimensional &&
    options.map((item, index) => (
      <View
        className={`nut-pickerview-roller-item ${
          isHidden(index + 1) && 'nut-pickerview-roller-item-hidden'
        }`}
        style={rollerStyle(index)}
-       key={item.value ?? index}
+       key={getItemKey(item, index)}
      >
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f436c51 and 6a6bc39.

📒 Files selected for processing (2)
  • src/packages/pickerview/pickerroller.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerroller.tsx (1 hunks)
🧰 Additional context used
🪛 GitHub Check: codecov/patch
src/packages/pickerview/pickerroller.tsx

[warning] 23-24: src/packages/pickerview/pickerroller.tsx#L23-L24
Added lines #L23 - L24 were not covered by tests


[warning] 68-80: src/packages/pickerview/pickerroller.tsx#L68-L80
Added lines #L68 - L80 were not covered by tests


[warning] 86-91: src/packages/pickerview/pickerroller.tsx#L86-L91
Added lines #L86 - L91 were not covered by tests


[warning] 94-98: src/packages/pickerview/pickerroller.tsx#L94-L98
Added lines #L94 - L98 were not covered by tests


[warning] 100-101: src/packages/pickerview/pickerroller.tsx#L100-L101
Added lines #L100 - L101 were not covered by tests


[warning] 112-114: src/packages/pickerview/pickerroller.tsx#L112-L114
Added lines #L112 - L114 were not covered by tests


[warning] 119-120: src/packages/pickerview/pickerroller.tsx#L119-L120
Added lines #L119 - L120 were not covered by tests


[warning] 124-128: src/packages/pickerview/pickerroller.tsx#L124-L128
Added lines #L124 - L128 were not covered by tests


[warning] 131-138: src/packages/pickerview/pickerroller.tsx#L131-L138
Added lines #L131 - L138 were not covered by tests


[warning] 141-143: src/packages/pickerview/pickerroller.tsx#L141-L143
Added lines #L141 - L143 were not covered by tests


[warning] 145-145: src/packages/pickerview/pickerroller.tsx#L145
Added line #L145 was not covered by tests


[warning] 147-155: src/packages/pickerview/pickerroller.tsx#L147-L155
Added lines #L147 - L155 were not covered by tests


[warning] 159-159: src/packages/pickerview/pickerroller.tsx#L159
Added line #L159 was not covered by tests


[warning] 161-161: src/packages/pickerview/pickerroller.tsx#L161
Added line #L161 was not covered by tests


[warning] 163-165: src/packages/pickerview/pickerroller.tsx#L163-L165
Added lines #L163 - L165 were not covered by tests


[warning] 176-176: src/packages/pickerview/pickerroller.tsx#L176
Added line #L176 was not covered by tests


[warning] 179-184: src/packages/pickerview/pickerroller.tsx#L179-L184
Added lines #L179 - L184 were not covered by tests


[warning] 194-197: src/packages/pickerview/pickerroller.tsx#L194-L197
Added lines #L194 - L197 were not covered by tests


[warning] 200-205: src/packages/pickerview/pickerroller.tsx#L200-L205
Added lines #L200 - L205 were not covered by tests


[warning] 207-210: src/packages/pickerview/pickerroller.tsx#L207-L210
Added lines #L207 - L210 were not covered by tests


[warning] 219-220: src/packages/pickerview/pickerroller.tsx#L219-L220
Added lines #L219 - L220 were not covered by tests

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (3)
src/packages/pickerview/pickerroller.tsx (2)

1-26: 组件结构设计合理!

组件使用 ForwardRef 转发引用,props 接口定义清晰,默认值设置合理。特别赞赏 renderLabel 的灵活设计,允许自定义标签渲染。

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 23-24: src/packages/pickerview/pickerroller.tsx#L23-L24
Added lines #L23 - L24 were not covered by tests


223-233: 事件监听器依赖项问题仍然存在

之前的审查意见仍然有效,当前 useEffect 的依赖数组包含了可能导致无限重渲染的依赖项。

src/packages/pickerview/pickerroller.taro.tsx (1)

321-326: 组件导出实现正确

组件导出使用了 forwardRef 包装,符合 React 最佳实践。

Comment on lines 51 to 61
useEffect(() => {
const element = pickerRollerRef.current
if (element && web()) {
const computedStyle = getComputedStyle(element)
const currentLineSpacing = computedStyle.getPropertyValue(
'--nutui-picker-item-height'
)
currentLineSpacing &&
(lineSpacing.current = parseFloat(currentLineSpacing))
}
}, [pickerRollerRef.current])
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议增加错误处理和优化依赖

当前代码在获取 CSS 变量时缺少错误处理,且 useEffect 的依赖数组可以优化:

  useEffect(() => {
    const element = pickerRollerRef.current
    if (element && web()) {
      const computedStyle = getComputedStyle(element)
      const currentLineSpacing = computedStyle.getPropertyValue(
        '--nutui-picker-item-height'
      )
-     currentLineSpacing &&
-       (lineSpacing.current = parseFloat(currentLineSpacing))
+     if (!currentLineSpacing) {
+       console.warn('未找到 --nutui-picker-item-height CSS 变量')
+       return
+     }
+     const parsedValue = parseFloat(currentLineSpacing)
+     if (isNaN(parsedValue)) {
+       console.warn('CSS 变量值格式无效')
+       return
+     }
+     lineSpacing.current = parsedValue
    }
-  }, [pickerRollerRef.current])
+  }, [])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
const element = pickerRollerRef.current
if (element && web()) {
const computedStyle = getComputedStyle(element)
const currentLineSpacing = computedStyle.getPropertyValue(
'--nutui-picker-item-height'
)
currentLineSpacing &&
(lineSpacing.current = parseFloat(currentLineSpacing))
}
}, [pickerRollerRef.current])
useEffect(() => {
const element = pickerRollerRef.current
if (element && web()) {
const computedStyle = getComputedStyle(element)
const currentLineSpacing = computedStyle.getPropertyValue(
'--nutui-picker-item-height'
)
if (!currentLineSpacing) {
console.warn('未找到 --nutui-picker-item-height CSS 变量')
return
}
const parsedValue = parseFloat(currentLineSpacing)
if (isNaN(parsedValue)) {
console.warn('CSS 变量值格式无效')
return
}
lineSpacing.current = parsedValue
}
}, [])

@github-actions github-actions bot added action:review This PR needs more reviews (less than 2 approvals) and removed action:review This PR needs more reviews (less than 2 approvals) labels Feb 12, 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

🧹 Nitpick comments (13)
src/packages/pickerview/demos/h5/demo5.tsx (1)

27-29: 建议改进事件处理方式

当前 onChange 事件仅打印日志,建议:

  1. 在生产环境中移除 console.log
  2. 添加实际的数据处理逻辑
-          onChange={({ value, selectedOptions }) => {
-            console.log('onChange', value, selectedOptions)
-          }}
+          onChange={({ value, selectedOptions }) => {
+            // 添加实际的数据处理逻辑
+            handleValueChange(value, selectedOptions)
+          }}
src/packages/pickerview/demos/taro/demo5.tsx (2)

5-17: 建议将数据提取到常量文件

建议将城市列表数据移至单独的常量文件中:

  1. 提高代码可维护性
  2. 方便数据复用
  3. 便于后续数据更新和管理
+// src/packages/pickerview/demos/constants.ts
+export const CITY_LIST = [
+  [
+    { value: 1, label: '南京市' },
+    { value: 2, label: '无锡市' },
+    // ... 其他城市
+  ],
+]

-const listData = [
-  [
-    { value: 1, label: '南京市' },
-    // ... 其他城市
-  ],
-]
+import { CITY_LIST } from '../constants'

19-34: 优化组件实现

  1. 建议移除生产环境中的 console.log
  2. 建议添加错误处理
  3. 考虑添加 onError 回调处理选择异常情况
 <PickerView
   defaultValue={[1]}
   options={listData}
   threeDimensional={false}
   duration={500}
   onChange={({ value, selectedOptions }) => {
-    console.log('onChange', value, selectedOptions)
+    try {
+      // 处理选择变更
+      handleChange(value, selectedOptions)
+    } catch (error) {
+      console.error('选择处理失败:', error)
+    }
   }}
+  onError={(error) => {
+    console.error('选择器错误:', error)
+  }}
 />
src/packages/pickerview/demos/taro/demo2.tsx (3)

5-17: 建议优化数据结构的设计

当前的数据结构有以下几点可以改进:

  1. value 值的序列不连续,建议使用连续的值以便维护
  2. 可以考虑将城市数据抽离到单独的配置文件中,提高代码的可维护性
  3. 可以为城市对象添加更多有用的元数据,如城市代码、所属省份等

建议重构数据结构如下:

// cities.ts
export const cityList = [
  { value: 1, label: '南京市', code: '320100', province: '江苏省' },
  { value: 2, label: '无锡市', code: '320200', province: '江苏省' },
  // ... 其他城市
]

22-29: 需要移除生产环境中的 console.log

onChange 事件处理器中的 console.log 应该仅用于开发调试,建议在生产环境中移除。

  onChange={({ value, selectedOptions }) => {
-   console.log('onChange', value, selectedOptions)
+   // 在这里处理选中值的变化
  }}

23-23: 建议使用 TypeScript 常量定义样式变量

为了提高代码的可维护性和类型安全性,建议将 CSS 自定义属性定义为常量。

const PICKER_ITEM_HEIGHT = '28px';

// 在组件中使用
style={{ '--nutui-picker-item-height': PICKER_ITEM_HEIGHT }}
src/packages/pickerview/demos/h5/demo4.tsx (3)

5-17: 建议优化数据结构的组织方式

为了提高代码的可维护性和复用性,建议:

  1. 将城市数据抽离到单独的常量文件中
  2. 考虑添加更多的城市信息(如区号、邮编等)以支持未来可能的需求

建议的代码结构如下:

+// src/packages/pickerview/demos/h5/constants.ts
+export const CITY_OPTIONS = [
+  [
+    { value: 1, label: '南京市', areaCode: '025' },
+    // ... 其他城市
+  ]
+]

+// src/packages/pickerview/demos/h5/demo4.tsx
-const listData = [
-  [
-    { value: 1, label: '南京市' },
-    // ... 其他城市
-  ],
-]
+import { CITY_OPTIONS } from './constants'
+const listData = CITY_OPTIONS

28-28: 建议移除生产环境中的 console.log

在生产环境中保留 console.log 语句可能会影响性能,建议使用适当的日志系统或在生产构建时移除。

-console.log('onChange', value, selectedOptions)
+// 使用适当的日志系统或条件性日志
+if (process.env.NODE_ENV === 'development') {
+  console.log('onChange', value, selectedOptions)
+}

29-31: 建议优化选择逻辑,提升用户体验

当用户选择"海北藏族自治区"时直接切换到"南京市"的逻辑可能会让用户感到困惑。建议:

  1. 添加适当的用户提示
  2. 考虑使用更平滑的过渡效果
 if (value[0] === 3) {
+  Toast.show('由于业务限制,已自动切换到南京市')
   setValue([1])
 }
src/packages/pickerview/demos/h5/demo2.tsx (1)

5-17: 建议优化数据结构设计

  1. 当前数据结构使用嵌套数组,但实际只使用单列数据,建议简化为单层数组。
  2. value 值不连续(1-12之间有跳跃),建议使用连续的值以便于维护和扩展。

建议修改为:

 const listData = [
-  [
     { value: 1, label: '南京市' },
     { value: 2, label: '无锡市' },
     { value: 3, label: '海北藏族自治区' },
     { value: 4, label: '北京市' },
     { value: 5, label: '连云港市' },
-    { value: 8, label: '大庆市' },
-    { value: 9, label: '绥化市' },
-    { value: 10, label: '潍坊市' },
-    { value: 12, label: '乌鲁木齐市' },
+    { value: 6, label: '大庆市' },
+    { value: 7, label: '绥化市' },
+    { value: 8, label: '潍坊市' },
+    { value: 9, label: '乌鲁木齐市' },
-  ],
 ]
src/packages/pickerview/demos/taro/demo4.tsx (3)

1-4: 建议优化组件命名以更好地反映其用途

当前的组件名称 Demo4 过于简单,建议改为更具描述性的名称,例如 PickerViewCityDemoCityPickerViewExample,以便更好地表达组件的功能和用途。

-const Demo4 = () => {
+const PickerViewCityDemo = () => {

5-17: 建议优化数据结构定义

当前的数据结构可以通过以下方式改进:

  1. 添加 TypeScript 接口定义以增强类型安全性
  2. 对城市数据进行更有意义的分组(如按省份分组)

建议添加以下类型定义:

interface CityOption {
  value: number;
  label: string;
}

const listData: CityOption[][] = [
  [
    { value: 1, label: '南京市' },
    // ... 其他城市
  ],
]

19-19: 建议优化状态管理

当前的状态变量命名和初始值设置可以更清晰:

  1. 建议使用更具描述性的状态变量名称
  2. 添加注释说明初始值的选择原因
-const [value, setValue] = useState([2])
+// 默认选中"无锡市"
+const [selectedCityValue, setSelectedCityValue] = useState([2])
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6a6bc39 and 10be5cd.

📒 Files selected for processing (6)
  • src/packages/pickerview/demos/h5/demo2.tsx (1 hunks)
  • src/packages/pickerview/demos/h5/demo4.tsx (1 hunks)
  • src/packages/pickerview/demos/h5/demo5.tsx (1 hunks)
  • src/packages/pickerview/demos/taro/demo2.tsx (1 hunks)
  • src/packages/pickerview/demos/taro/demo4.tsx (1 hunks)
  • src/packages/pickerview/demos/taro/demo5.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (11)
src/packages/pickerview/demos/h5/demo5.tsx (3)

1-4: 代码结构清晰,导入规范!

组件的导入和结构定义符合 React 最佳实践。


5-17: 建议将城市数据提取为共享常量

为保持代码的可维护性,建议将 listData 移至单独的常量文件中。


35-35: 导出语句规范!

组件的导出方式符合 React 组件的标准做法。

src/packages/pickerview/demos/taro/demo5.tsx (1)

1-4: 建议重构以减少代码重复

当前的实现方式可能导致H5和Taro版本之间存在重复代码。建议参考之前的建议,创建一个共享的基础组件。

src/packages/pickerview/demos/taro/demo2.tsx (2)

1-2: 导入语句看起来没有问题!

导入了必要的组件和依赖项。


34-34: 导出语句正确!

组件的默认导出符合常见的最佳实践。

src/packages/pickerview/demos/h5/demo4.tsx (3)

1-4: 代码结构清晰,导入合理!

组件的导入和声明遵循了 React 最佳实践。


19-19: 状态管理实现恰当!

使用 useState 管理选中值,初始值设置合理。


38-38: 导出语句正确!

组件导出方式符合规范。

src/packages/pickerview/demos/h5/demo2.tsx (2)

1-4: 代码结构清晰,命名规范!

组件命名与文件名保持一致,导入声明简洁明确。


34-34: 导出语句规范!

默认导出符合 React 组件的常见实践。

Comment on lines +19 to +33
return (
<>
<Cell>
<PickerView
style={{ '--nutui-picker-item-height': '28px' }}
options={listData}
defaultValue={[1]}
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
}}
/>
</Cell>
</>
)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议增强组件的健壮性和可访问性

  1. 生产环境代码中不应该保留 console.log
  2. 缺少错误处理机制
  3. 需要增加无障碍访问属性

建议修改为:

 return (
   <>
     <Cell>
       <PickerView
         style={{ '--nutui-picker-item-height': '28px' }}
         options={listData}
         defaultValue={[1]}
+        aria-label="城市选择器"
+        role="listbox"
         onChange={({ value, selectedOptions }) => {
-          console.log('onChange', value, selectedOptions)
+          try {
+            // 处理选择变更
+            onCityChange?.(value, selectedOptions)
+          } catch (error) {
+            console.error('城市选择发生错误:', error)
+          }
         }}
       />
     </Cell>
   </>
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<>
<Cell>
<PickerView
style={{ '--nutui-picker-item-height': '28px' }}
options={listData}
defaultValue={[1]}
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
}}
/>
</Cell>
</>
)
}
return (
<>
<Cell>
<PickerView
style={{ '--nutui-picker-item-height': '28px' }}
options={listData}
defaultValue={[1]}
aria-label="城市选择器"
role="listbox"
onChange={({ value, selectedOptions }) => {
try {
// 处理选择变更
onCityChange?.(value, selectedOptions)
} catch (error) {
console.error('城市选择发生错误:', error)
}
}}
/>
</Cell>
</>
)
}

Comment on lines +21 to +37
return (
<>
<Cell>
<PickerView
value={value}
options={listData}
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
if (value[0] === 3) {
setValue([1])
}
}}
/>
</Cell>
</>
)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议优化事件处理逻辑

当前的事件处理逻辑存在以下可改进之处:

  1. 生产环境代码中不应该保留 console.log
  2. 特殊情况处理(value[0] === 3)需要添加注释说明原因
  3. 建议添加错误处理

建议按如下方式修改:

 <PickerView
-  value={value}
+  value={selectedCityValue}
   options={listData}
   onChange={({ value, selectedOptions }) => {
-    console.log('onChange', value, selectedOptions)
+    try {
+      // 当选择"海北藏族自治区"时,自动切换到"南京市"
+      if (value[0] === 3) {
+        setSelectedCityValue([1])
+      } else {
+        setSelectedCityValue(value)
+      }
+    } catch (error) {
+      console.error('选择城市时发生错误:', error)
+    }
   }}
 />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<>
<Cell>
<PickerView
value={value}
options={listData}
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
if (value[0] === 3) {
setValue([1])
}
}}
/>
</Cell>
</>
)
}
return (
<>
<Cell>
<PickerView
value={selectedCityValue}
options={listData}
onChange={({ value, selectedOptions }) => {
try {
// 当选择"海北藏族自治区"时,自动切换到"南京市"
if (value[0] === 3) {
setSelectedCityValue([1])
} else {
setSelectedCityValue(value)
}
} catch (error) {
console.error('选择城市时发生错误:', error)
}
}}
/>
</Cell>
</>
)
}

@github-actions github-actions bot added action:review This PR needs more reviews (less than 2 approvals) and removed action:review This PR needs more reviews (less than 2 approvals) labels Feb 12, 2025
@github-actions github-actions bot added action:review This PR needs more reviews (less than 2 approvals) and removed action:review This PR needs more reviews (less than 2 approvals) labels Feb 13, 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: 4

🧹 Nitpick comments (8)
src/packages/pickerview/pickerroller.tsx (2)

124-167: 建议增加触摸事件的错误处理

当前触摸事件处理逻辑实现完整,但建议增加以下改进:

  1. 添加 try-catch 块处理可能的事件异常
  2. 处理触摸事件可能的空值情况
  3. 添加适当的错误日志

建议参考以下示例添加错误处理:

 const touchStart = (event: React.TouchEvent<HTMLDivElement>) => {
+  try {
     touch.start(event)
     setStartY(touch.deltaY.current)
     setStartTime(Date.now())
     transformY.current = scrollDistance
+  } catch (error) {
+    console.error('触摸事件处理失败:', error)
+  }
 }

258-293: 建议优化渲染性能

当前渲染逻辑可以通过以下方式优化:

  1. 使用 useMemo 缓存 options 的映射结果
  2. 优化 key 的选择,避免使用索引作为后备值
  3. 考虑使用虚拟滚动处理大量选项

建议参考以下示例优化渲染逻辑:

+const renderedOptions = useMemo(() => 
+  options.map((item, index) => (
+    <div
+      className={`nut-pickerview-roller-item ${
+        isHidden(index + 1) && 'nut-pickerview-roller-item-hidden'
+      }`}
+      style={rollerStyle(index)}
+      key={item.value}
+    >
+      {renderLabel(item)}
+    </div>
+  )), [options, isHidden, rollerStyle, renderLabel])

 return (
   <div className="nut-pickerview-list" ref={pickerRollerRef}>
     {/* ... */}
-    {threeDimensional && options.map((item, index) => (/* ... */))}
+    {threeDimensional && renderedOptions}
     {/* ... */}
   </div>
 )
src/packages/pickerview/doc.en-US.md (3)

1-4: 建议增强组件概述部分

当前概述比较简单,建议补充以下内容:

  • 组件的主要用途和应用场景
  • 与 Picker 组件的关系和区别
  • 核心特性和优势

13-59: 建议为每个示例添加功能说明

为了帮助用户更好地理解每个示例的用途,建议在每个示例前添加简要说明:

  • 基础用法:说明最基本的使用方式
  • 受控组件:解释如何通过外部状态控制组件
  • 高度调整:说明如何自定义面板高度
  • 多列展示:展示多列数据的配置方式
  • 平铺展示:说明平铺模式的特点
  • 级联选择:解释级联数据的使用场景

63-80: 建议完善属性文档

为了提供更完整的使用指导,建议:

  1. 为每个属性添加示例值
  2. 补充 onChange 回调参数的详细类型说明
  3. 说明 valuedefaultValue 的数据格式要求
  4. PickerOptionItem 添加示例代码

示例格式:

interface PickerOptionItem {
  label: string | number; // 例如:'选项1' 或 1
  value: string | number; // 例如:'value1' 或 1
  children?: PickerOptionItem[]; // 用于级联数据,例如:[{label: '子选项1', value: 'child1'}]
}
src/packages/pickerview/pickerroller.taro.tsx (3)

14-28: 建议加强类型安全性

当前的类型定义可以进一步优化:

  1. keyIndex 可以使用更具体的类型而不是默认的 number
  2. renderLabel 的返回值类型应该显式声明
  3. 建议为 PickerOptionItem 添加更严格的类型约束

建议按如下方式优化类型定义:

+ type KeyIndex = number;
+ type RenderLabelReturn = string | JSX.Element;
  const InternalPickerRoller: ForwardRefRenderFunction<
    { stopMomentum: () => void; moving: boolean },
    Partial<PickerRollerProps>
  > = (props, ref) => {
    const {
-     keyIndex = 0,
+     keyIndex: KeyIndex = 0,
      options = [],
      threeDimensional = true,
      duration = 1000,
      onSelect,
-     renderLabel = (item: PickerOptionItem) => {
+     renderLabel = (item: PickerOptionItem): RenderLabelReturn => {
        return item.label
      },
    } = props

161-168: 优化惯性滚动实现

当前的惯性滚动实现可以进行以下优化:

  1. 添加最大速度限制,防止滚动过快
  2. 考虑设备性能,添加帧率控制
  3. 增加边界情况处理

建议按如下方式优化实现:

  const momentum = (distance: number, duration: number) => {
    let nDistance = distance
    // 惯性滚动的速度
    const speed = Math.abs(nDistance / duration)
+   // 限制最大速度
+   const MAX_SPEED = 2.5
+   const limitedSpeed = Math.min(speed, MAX_SPEED)
    // 惯性滚动的距离
-   nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1)
+   nDistance = (limitedSpeed / 0.003) * (nDistance < 0 ? -1 : 1)
+   // 确保不超出边界
+   const maxDistance = lineSpacing.current * (options.length - 1)
+   return Math.max(Math.min(nDistance, maxDistance), -maxDistance)
    return nDistance
  }

283-318: 优化渲染性能

当前的渲染实现可以进行以下优化:

  1. 使用 useMemo 缓存样式计算
  2. 优化条件渲染逻辑
  3. 减少不必要的重渲染

建议按如下方式优化:

+ const memoizedRollerStyle = useMemo(() => 
+   threeDimensional ? touchRollerStyle() : touchTileStyle(),
+   [threeDimensional, touchTime, touchDeg, scrollDistance]
+ )

+ const memoizedOptions = useMemo(() => 
+   options.map((item, index) => ({
+     ...item,
+     style: rollerStyle(index),
+     hidden: isHidden(index + 1)
+   })),
+   [options, rotation, lineSpacing.current]
+ )

  return (
    <View className="nut-pickerview-list" ref={pickerRollerRef}>
      <View
        className="nut-pickerview-roller"
        ref={rollerRef}
-       style={threeDimensional ? touchRollerStyle() : touchTileStyle()}
+       style={memoizedRollerStyle}
        onTransitionEnd={stopMomentum}
      >
        {threeDimensional ? (
-         options.map((item, index) => (
+         memoizedOptions.map(({ style, hidden, ...item }, index) => (
            <View
              className={`nut-pickerview-roller-item ${
-               isHidden(index + 1) && 'nut-pickerview-roller-item-hidden'
+               hidden && 'nut-pickerview-roller-item-hidden'
              }`}
-             style={rollerStyle(index)}
+             style={style}
              key={item.value ?? index}
            >
              {renderLabel(item)}
            </View>
          ))
        ) : (
          // 平铺模式保持不变...
        )}
      </View>
    </View>
  )
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a787f7d and c10fcc7.

⛔ Files ignored due to path filters (1)
  • src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • src/packages/pickerview/__test__/pickerview.spec.tsx (1 hunks)
  • src/packages/pickerview/doc.en-US.md (1 hunks)
  • src/packages/pickerview/pickerroller.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerroller.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/packages/pickerview/test/pickerview.spec.tsx
🧰 Additional context used
🪛 GitHub Check: build
src/packages/pickerview/pickerroller.tsx

[failure] 8-8:
Cannot find module '@/utils/use-touch' or its corresponding type declarations.

src/packages/pickerview/pickerroller.taro.tsx

[failure] 9-9:
Cannot find module '@/utils/use-touch' or its corresponding type declarations.

🪛 GitHub Actions: E2E Tests
src/packages/pickerview/pickerroller.tsx

[error] 19-19: Failed to resolve import '@/utils/use-touch'. Does the file exist?

🪛 markdownlint-cli2 (0.17.2)
src/packages/pickerview/doc.en-US.md

86-86: Link fragments should be valid
null

(MD051, link-fragments)

🪛 GitHub Actions: CI
src/packages/pickerview/pickerroller.taro.tsx

[error] 9-9: Cannot find module '@/utils/use-touch' or its corresponding type declarations.

🔇 Additional comments (2)
src/packages/pickerview/pickerroller.tsx (2)

12-26: 代码结构清晰,类型定义完善

组件初始化的实现很规范:

  • 使用 ForwardRefRenderFunction 正确处理 ref 转发
  • 默认属性设置合理
  • Props 接口设计完整
🧰 Tools
🪛 GitHub Actions: E2E Tests

[error] 19-19: Failed to resolve import '@/utils/use-touch'. Does the file exist?


225-235: 事件监听器管理优化到位

useEffect 的实现已经得到改进:

  • 正确设置了依赖数组
  • 清理函数移除了所有事件监听器
  • 避免了重复绑定事件的问题

@github-actions github-actions bot added action:review This PR needs more reviews (less than 2 approvals) and removed action:review This PR needs more reviews (less than 2 approvals) labels Feb 13, 2025
@github-actions github-actions bot added action:review This PR needs more reviews (less than 2 approvals) and removed action:review This PR needs more reviews (less than 2 approvals) labels Feb 13, 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: 8

🧹 Nitpick comments (4)
src/packages/pickerview/pickerview.tsx (1)

182-182: 清理注释代码

建议移除被注释的代码 // return selectedOption || columnOptions[0],如果这是一个重要的备选方案,建议添加说明注释解释为什么不使用这个方案。

src/packages/pickerview/pickerview.taro.tsx (1)

224-260: 事件监听设置合理,但可以优化

事件监听的设置和清理实现得当,使用了 passive 事件监听器提升性能。建议考虑:

  1. 将事件选项对象提取为常量,避免重复创建
  2. 考虑使用 Taro 的事件系统而不是原生事件
+ const EVENT_OPTIONS = passiveSupported ? { passive: false, once: true } : false;

  useEffect(() => {
    pickerRollerRef.current?.addEventListener(
      'touchstart',
      touchStart,
-     eventOptions
+     EVENT_OPTIONS
    )
    // ... 其他事件监听器同样更新
  }, [pickerRollerRef.current, touchStart, touchMove, touchEnd])
src/packages/pickerview/pickerroller.tsx (1)

170-193: 简化 modifyStatus 方法的实现

当前实现包含重复的查找逻辑和复杂的条件判断。建议使用数组方法简化:

 const modifyStatus = (type?: boolean, val?: string | number) => {
   const value = val || props.value
-  let index = -1
-  if (value) {
-    options.some((item, idx) => {
-      if (item.value === value) {
-        index = idx
-        return true
-      }
-      return false
-    })
-  } else {
-    options.forEach((item, i) => {
-      if (item.value === props.value) {
-        index = i
-      }
-    })
-  }
+  const index = options.findIndex(item => 
+    item.value === (value || props.value)
+  )
   setCurrIndex(index === -1 ? 1 : index + 1)
   const move = index * lineSpacing.current
   type && setChooseValue(-move)
   setMove(-move)
 }
src/packages/pickerview/pickerroller.taro.tsx (1)

8-12: 确保 Taro 平台兼容性

当前实现使用了 Taro 特定的组件和工具:

  1. 使用 @tarojs/componentsView 组件
  2. 引入 platform-taroweb 方法
  3. 使用平台特定的钩子和工具

建议添加以下测试和验证:

  1. 在不同小程序平台上的兼容性测试
  2. 触摸事件在各平台的表现验证
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c10fcc7 and f539b83.

📒 Files selected for processing (4)
  • src/packages/pickerview/pickerroller.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerroller.tsx (1 hunks)
  • src/packages/pickerview/pickerview.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerview.tsx (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/packages/pickerview/pickerview.taro.tsx

[error] 88-88: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/packages/pickerview/pickerview.tsx

[error] 87-87: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 GitHub Check: build
src/packages/pickerview/pickerview.taro.tsx

[failure] 13-13:
Cannot find module '@/utils/use-props-value' or its corresponding type declarations.


[failure] 139-139:
Parameter 'prev' implicitly has an 'any' type.

src/packages/pickerview/pickerview.tsx

[failure] 12-12:
Cannot find module '@/utils/use-props-value' or its corresponding type declarations.


[failure] 138-138:
Parameter 'prev' implicitly has an 'any' type.

🪛 GitHub Actions: CI
src/packages/pickerview/pickerview.taro.tsx

[error] 13-13: Cannot find module '@/utils/use-props-value' or its corresponding type declarations.

src/packages/pickerview/pickerview.tsx

[error] 12-12: Failed to resolve import '@/utils/use-props-value'. Does the file exist?

🪛 GitHub Actions: E2E Tests
src/packages/pickerview/pickerview.tsx

[error] 12-12: Failed to resolve import '@/utils/use-props-value'. Does the file exist?


[warning] 12-12: Invalid deprecation 'global-builtin'.


[warning] 12-12: Future import deprecation is not yet active, so silencing it is unnecessary.

🔇 Additional comments (2)
src/packages/pickerview/pickerview.tsx (1)

60-70: 代码实现正确且健壮

已正确实现了空数组保护,避免了潜在的运行时错误。同时类型判断逻辑清晰。

src/packages/pickerview/pickerroller.tsx (1)

8-8:

Details

✅ Verification successful

验证模块导入路径

建议确认 @/hooks/use-touch 模块的导入路径是否正确配置。需要检查:

  1. 项目的路径别名配置
  2. 模块是否存在于正确位置
  3. 类型声明文件是否完整

🏁 Script executed:

#!/bin/bash
# 验证模块是否存在
fd -t f "use-touch.{ts,tsx}" hooks/

Length of output: 131


请验证模块导入路径配置

当前在尝试寻找 @/hooks/use-touch 模块时,之前的搜索定位到了错误路径(即使用了 hooks/ 而非 src/hooks/)。请手动确认以下事项:

  • 是否在 src/hooks 目录下存在文件名为 use-touch.tsuse-touch.tsx 的模块;
  • 项目的路径别名 @ 是否正确映射到 src 目录;
  • 类型声明文件是否完整无误。

请开发者执行上面的 shell 脚本并验证文件是否存在,然后根据检测结果调整导入路径或别名配置。


🏁 Script executed:

[run_scripts]
#!/bin/bash
# 查找 use-touch 模块文件在 src/hooks 目录下
fd -t f -e ts -e tsx "use-touch" src/hooks/

Length of output: 188


模块导入路径已验证

经确认,项目中确实存在文件 src/hooks/use-touch.ts,因此当前的模块导入路径 @/hooks/use-touch 是有效的。不过,为确保项目整体配置一致,请检查以下内容:

  • 确认路径别名 @ 是否在 tsconfig.json(或相关构建配置)中正确映射至 src 目录。
  • 检查该模块的类型声明文件是否完善,满足项目的使用需求。

如果上述配置都正常,则无需进一步修改导入路径。

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 (1)
src/packages/pickerview/pickerview.taro.tsx (1)

61-71: 🛠️ Refactor suggestion

建议增加空值保护

在判断列类型时,建议增加更完善的空值检查:

  const columnsType = useMemo(() => {
-   const firstColumn = (props.options as PickerOptions[])[0] || []
+   const firstColumn = (props.options as PickerOptions[])?.at(0) ?? []
    if (
      Array.isArray(firstColumn) &&
      firstColumn.length > 0 &&
-     'children' in firstColumn[0]
+     firstColumn[0] && 'children' in firstColumn[0]
    ) {
      return 'cascade'
    }
    return 'multiple'
  }, [props.options])
🧹 Nitpick comments (2)
src/packages/pickerview/pickerview.taro.tsx (2)

73-108: 优化级联选项格式化逻辑

建议对 formatCascadeOptions 函数进行以下优化:

  1. 使用可选链简化空值判断
  2. 添加类型断言提升类型安全性
  const formatCascadeOptions = (
    options: PickerOptions,
    value: PickerValue[]
  ) => {
    if (!options.length) return []

    const formatted: PickerOptions[] = []
    let columnOptions: PickerOptionItem = {
      label: '',
      value: '',
      children: options,
    }

    let columnIndex = 0
-   while (columnOptions && columnOptions.children) {
+   while (columnOptions?.children) {
      const currentOptions: PickerOptions = columnOptions.children
      formatted.push(currentOptions)

      const currentValue = value?.[columnIndex]
      if (currentValue === 0) {
        columnOptions = currentOptions[0]
      } else if (currentValue) {
        const index = currentOptions.findIndex(
          (columnItem) => columnItem.value === currentValue
        )
        columnOptions = currentOptions[index === -1 ? 0 : index]
      } else {
        break
      }

      columnIndex++
    }
    return formatted
  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 88-88: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


133-174: 建议拆分选择处理逻辑

当前 handleSelect 函数同时处理多列和级联两种情况,建议拆分为独立函数以提高可维护性:

+ const handleMultipleSelect = (
+   option: PickerOptionItem,
+   index: number,
+   prev: PickerValue[]
+ ) => {
+   const next = [...prev]
+   next[index] = option.value
+   return next
+ }
+
+ const handleCascadeSelect = (
+   option: PickerOptionItem,
+   index: number,
+   innerValue: PickerValue[]
+ ) => {
+   const values: PickerValue[] = []
+   let currentOption = option
+   values[index] = option.value
+   
+   while (currentOption?.children?.[0]) {
+     values[index + 1] = currentOption.children[0].value
+     index++
+     currentOption = currentOption.children[0]
+   }
+   
+   if (currentOption?.children?.length) {
+     values[index + 1] = ''
+   }
+   
+   return [...innerValue.slice(0, index), ...values.splice(index)]
+ }

  const handleSelect = useCallback(
    (option: PickerOptionItem, index: number) => {
      const newValue = option?.value
      if (!newValue || innerValue[index] === newValue) return
      changeIndex.current = index
      
      if (columnsType === 'multiple') {
-       setInnerValue((prev) => {
-         const next = [...prev]
-         next[index] = newValue
-         return next
-       })
+       setInnerValue((prev) => handleMultipleSelect(option, index, prev))
      } else {
-       const startIndex = index
-       const values: PickerValue[] = []
-       // ... existing cascade logic ...
+       const combineResult = handleCascadeSelect(option, index, innerValue)
        setInnerValue(combineResult)
        
        const optionFirst = props?.options?.[0] as PickerOptionItem[]
        if (!isEqual(
          formatCascadeOptions(optionFirst, combineResult),
          innerOptions
        )) {
          setInnerOptions(formatCascadeOptions(optionFirst, combineResult))
        }
      }
    },
    [innerValue, props.options, columnsType, innerOptions]
  )
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f539b83 and f06ec00.

📒 Files selected for processing (1)
  • src/packages/pickerview/pickerview.taro.tsx (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/packages/pickerview/pickerview.taro.tsx

[error] 88-88: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (3)
src/packages/pickerview/pickerview.taro.tsx (3)

9-13: 请确保所有导入路径正确配置

以下导入路径需要验证:

  1. @/utils/typings
  2. @/hooks/use-props-value

请确保:

  1. 这些模块在正确的路径下
  2. Taro 项目配置中的路径别名设置正确

22-29: 默认属性设置合理

默认属性的设置遵循了最佳实践,包括了必要的默认值和类型定义。


196-219: 渲染逻辑实现正确

已正确使用 Taro 的 View 组件替代原生 div,渲染逻辑清晰且实现合理。

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: 1

🧹 Nitpick comments (4)
src/packages/pickerview/doc.md (3)

1-4: 建议补充组件介绍内容

当前介绍过于简单,建议补充以下内容:

  • 组件的主要使用场景
  • 与 Picker 组件的关系和区别
  • 核心功能特点

13-59: 建议为每个示例添加说明文字

为了帮助开发者更好地理解每个示例的用途,建议在每个示例代码块前添加简要说明,描述该示例的具体使用场景和实现效果。


72-72: 建议完善 onChange 事件说明

建议补充 onChange 回调函数参数的详细说明:

  • value: 当前选中项的值数组
  • index: 当前变更列的索引
  • selectedOptions: 当前选中项的完整选项数据
src/packages/pickerview/pickerview.tsx (1)

147-151: 优化代码安全性

建议使用可选链操作符来优化代码安全性。

-        while (option?.children?.[0]) {
-          values[index + 1] = option.children[0].value
+        while (option?.children?.[0]?.value) {
+          values[index + 1] = option.children?.[0].value
           index++
-          option = option.children[0]
+          option = option.children?.[0]
         }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f06ec00 and 0c448c8.

📒 Files selected for processing (7)
  • src/packages/pickerview/doc.en-US.md (1 hunks)
  • src/packages/pickerview/doc.md (1 hunks)
  • src/packages/pickerview/doc.taro.md (1 hunks)
  • src/packages/pickerview/doc.zh-TW.md (1 hunks)
  • src/packages/pickerview/pickerroller.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerroller.tsx (1 hunks)
  • src/packages/pickerview/pickerview.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/packages/pickerview/doc.en-US.md
  • src/packages/pickerview/doc.zh-TW.md
  • src/packages/pickerview/pickerroller.taro.tsx
  • src/packages/pickerview/doc.taro.md
  • src/packages/pickerview/pickerroller.tsx
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
src/packages/pickerview/doc.md

86-86: Link fragments should be valid
null

(MD051, link-fragments)

🪛 Biome (1.9.4)
src/packages/pickerview/pickerview.tsx

[error] 87-87: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: test
🔇 Additional comments (6)
src/packages/pickerview/pickerview.tsx (6)

11-12: 修复模块导入路径

建议检查并修正模块导入路径,确保与项目的路径别名配置一致。

建议执行以下操作:

  1. 确认 @/utils/typings@/hooks/use-props-value 模块存在
  2. 检查项目中 @ 别名的配置是否正确
  3. 验证模块是否包含必要的类型声明文件

21-28: 默认属性设置合理

默认属性的设置符合最佳实践,提供了必要的默认值。


60-70: 列类型判断逻辑完善

已经添加了空数组保护,避免了访问 firstColumn[0] 时可能出现的错误。


138-138: 增加类型安全性

prev 参数需要显式声明类型。

-        setInnerValue((prev) => {
+        setInnerValue((prev: PickerValue[]) => {

175-185: 确认默认选项处理逻辑

当前实现移除了默认选择第一个选项的逻辑(第182行的注释)。请确认:

  1. 是否需要保留默认选择第一个选项的逻辑
  2. 当前实现是否会导致未选择状态下的异常

195-217: 渲染逻辑清晰合理

组件渲染逻辑结构清晰,包含了必要的遮罩层和指示器元素。


### 样式变量

组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

修复 ConfigProvider 组件链接

当前文档中的 ConfigProvider 组件链接 #/zh-CN/component/configprovider 无效,请更新为正确的链接地址。

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

86-86: Link fragments should be valid
null

(MD051, link-fragments)

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 (7)
src/packages/pickerview/utils.ts (1)

1-4: 建议添加参数验证和单元测试

  1. 建议添加参数验证以处理边缘情况
  2. 魔术数字 0.003 应该提取为命名常量
  3. 需要添加单元测试以验证功能正确性

建议修改如下:

+const MOMENTUM_FACTOR = 0.003
+
 export const momentum = (distance: number, duration: number) => {
+  if (duration === 0) return 0
   const speed = Math.abs(distance / duration)
-  return (speed / 0.003) * (distance < 0 ? -1 : 1)
+  return (speed / MOMENTUM_FACTOR) * (distance < 0 ? -1 : 1)
 }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 2-4: src/packages/pickerview/utils.ts#L2-L4
Added lines #L2 - L4 were not covered by tests

src/packages/pickerview/hooks/useStyles.ts (2)

8-11: 建议优化过渡样式函数的类型安全性

建议为 transformValue 参数添加类型定义,并提取过渡时间曲线为常量。

+const TRANSITION_TIMING = 'cubic-bezier(0.17, 0.89, 0.45, 1)'
+
-const getTransitionStyle = (transformValue: string) => ({
+const getTransitionStyle = (transformValue: `rotate3d(${number}, ${number}, ${number}, ${string})` | `translate3d(${string}, ${string}, ${string})`) => ({
-  transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`,
+  transition: `transform ${touchTime}ms ${TRANSITION_TIMING}`,
   transform: transformValue,
 })

19-23: 建议提取魔术数字为命名常量

rollerStyle 函数中,3.2 是一个魔术数字,应该提取为有意义的命名常量。

+const DEPTH_FACTOR = 3.2
+
 const rollerStyle = (index: number) => ({
   transform: `rotate3d(1, 0, 0, ${-rotation * (index + 1)}deg) translate3d(0px, 0px, ${Math.round(
-    lineSpacing.current * 3.2
+    lineSpacing.current * DEPTH_FACTOR
   )}px)`,
 })
src/packages/pickerview/pickerroller.tsx (2)

28-31: 建议提取魔术数字为命名常量

这些数值应该定义为有意义的常量,并考虑通过 props 允许自定义。

-const DEFAULT_DURATION = 200
-const INERTIA_TIME = 300
-const INERTIA_DISTANCE = 15
-const ROTATION = 20
+const ANIMATION_CONSTANTS = {
+  DEFAULT_DURATION: 200,
+  INERTIA_TIME: 300,
+  INERTIA_DISTANCE: 15,
+  ROTATION: 20
+} as const

67-94: 建议添加错误边界处理

handleMove 函数缺少错误处理机制,建议添加 try-catch 块来捕获可能的计算错误。

 const handleMove = (move: number, type?: string, time?: number) => {
+  try {
     let updatedMove = move + transformY.current
     if (type === 'end') {
       // ... existing code ...
     } else {
       // ... existing code ...
     }
+  } catch (error) {
+    console.error('移动计算错误:', error)
+    // 重置到安全状态
+    setScrollDistance(0)
+    setTouchDeg('0deg')
+  }
 }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 70-73: src/packages/pickerview/pickerroller.tsx#L70-L73
Added lines #L70 - L73 were not covered by tests


[warning] 76-80: src/packages/pickerview/pickerroller.tsx#L76-L80
Added lines #L76 - L80 were not covered by tests

src/packages/pickerview/pickerview.scss (2)

58-61: 建议使用 Autoprefixer 处理浏览器前缀

不建议手动添加浏览器前缀,应该使用 Autoprefixer 自动处理。

 backface-visibility: hidden;
-    -moz-backface-visibility: hidden;
-    -webkit-backface-visibility: hidden;

6-7: 建议优化 CSS 计算逻辑

建议将复杂的计算提取为独立的 CSS 变量,以提高可维护性。

+  --nutui-picker-list-height: #{$picker-list-height};
+  --nutui-picker-top: calc((var(--nutui-picker-list-height) - var(--nutui-picker-item-height)) / 2);
   height: $picker-list-height;
-  $pickerview-top: calc(($picker-list-height - $picker-item-height) / 2);
+  $pickerview-top: var(--nutui-picker-top);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0c448c8 and 654d1e0.

⛔ Files ignored due to path filters (1)
  • src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • src/packages/pickerview/hooks/useStyles.ts (1 hunks)
  • src/packages/pickerview/pickerroller.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerroller.tsx (1 hunks)
  • src/packages/pickerview/pickerview.scss (1 hunks)
  • src/packages/pickerview/utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/packages/pickerview/pickerroller.taro.tsx
🧰 Additional context used
🪛 GitHub Check: codecov/patch
src/packages/pickerview/utils.ts

[warning] 2-4: src/packages/pickerview/utils.ts#L2-L4
Added lines #L2 - L4 were not covered by tests

src/packages/pickerview/pickerroller.tsx

[warning] 70-73: src/packages/pickerview/pickerroller.tsx#L70-L73
Added lines #L70 - L73 were not covered by tests


[warning] 76-80: src/packages/pickerview/pickerroller.tsx#L76-L80
Added lines #L76 - L80 were not covered by tests


[warning] 101-105: src/packages/pickerview/pickerroller.tsx#L101-L105
Added lines #L101 - L105 were not covered by tests


[warning] 108-115: src/packages/pickerview/pickerroller.tsx#L108-L115
Added lines #L108 - L115 were not covered by tests


[warning] 118-130: src/packages/pickerview/pickerroller.tsx#L118-L130
Added lines #L118 - L130 were not covered by tests


[warning] 156-156: src/packages/pickerview/pickerroller.tsx#L156
Added line #L156 was not covered by tests

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (2)
src/packages/pickerview/pickerroller.tsx (2)

148-158: 优化 CSS 变量获取的错误处理

当前获取 CSS 变量的逻辑缺少适当的错误处理机制。

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 156-156: src/packages/pickerview/pickerroller.tsx#L156
Added line #L156 was not covered by tests


172-202: 修正重复绑定事件监听的副作用

当前 useEffect 未设置依赖数组,会在组件每次渲染时反复绑定/解绑事件。

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: 1

♻️ Duplicate comments (4)
src/packages/pickerview/pickerroller.tsx (2)

147-157: 🛠️ Refactor suggestion

优化 CSS 变量获取逻辑

当前 CSS 变量获取逻辑缺少适当的错误处理。建议:

  1. 添加 CSS 变量不存在的警告
  2. 处理解析失败的情况
  3. 提供默认值保障
 useEffect(() => {
   const element = pickerRollerRef.current
   if (element) {
     const computedStyle = getComputedStyle(element)
     const currentLineSpacing = computedStyle.getPropertyValue(
       '--nutui-picker-item-height'
     )
-    !!currentLineSpacing &&
-      (lineSpacing.current = parseFloat(currentLineSpacing))
+    if (!currentLineSpacing) {
+      console.warn('未找到 --nutui-picker-item-height CSS 变量')
+      return
+    }
+    const parsedValue = parseFloat(currentLineSpacing)
+    if (isNaN(parsedValue)) {
+      console.warn('CSS 变量值格式无效')
+      return
+    }
+    lineSpacing.current = parsedValue
   }
 }, [])
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 155-155: src/packages/pickerview/pickerroller.tsx#L155
Added line #L155 was not covered by tests


171-201: 🛠️ Refactor suggestion

优化事件监听器的设置

当前实现在每次渲染时都会重新绑定事件监听器,这可能导致性能问题。建议:

  1. 优化依赖数组
  2. 使用 useCallback 包装事件处理函数
+const memoizedTouchStart = useCallback(handleTouchStart, [touch, setStartY, setStartTime])
+const memoizedTouchMove = useCallback(handleTouchMove, [touch, moving, setMove])
+const memoizedTouchEnd = useCallback(handleTouchEnd, [moving, touch, setMove])

 useEffect(() => {
   const options = passiveSupported ? { passive: false } : false
   pickerRollerRef.current?.addEventListener('touchstart', handleTouchStart, options)
   pickerRollerRef.current?.addEventListener('touchmove', handleTouchMove, options)
   pickerRollerRef.current?.addEventListener('touchend', handleTouchEnd, options)
   return () => {
     pickerRollerRef.current?.removeEventListener('touchstart', handleTouchStart)
     pickerRollerRef.current?.removeEventListener('touchmove', handleTouchMove)
     pickerRollerRef.current?.removeEventListener('touchend', handleTouchEnd)
   }
-}, [pickerRollerRef.current, handleTouchStart, handleTouchMove, handleTouchEnd])
+}, [memoizedTouchStart, memoizedTouchMove, memoizedTouchEnd])
src/packages/pickerview/pickerroller.taro.tsx (2)

174-176: ⚠️ Potential issue

修复事件选项配置

当前实现在 Taro 版本中添加了 once: true 选项,这会导致事件监听器在触发一次后被移除。建议移除该选项,因为这些事件需要持续监听。

 const eventOptions = passiveSupported
-  ? { passive: false, once: true }
+  ? { passive: false }
   : false

149-159: 🛠️ Refactor suggestion

优化平台特定的 CSS 变量处理

当前实现在 Taro 环境下的 CSS 变量获取逻辑需要更健壮的错误处理。建议添加平台特定的错误处理和默认值。

 useEffect(() => {
   const element = pickerRollerRef.current
   if (element && web()) {
     const computedStyle = getComputedStyle(element)
     const currentLineSpacing = computedStyle.getPropertyValue(
       '--nutui-picker-item-height'
     )
-    !!currentLineSpacing &&
-      (lineSpacing.current = parseFloat(currentLineSpacing))
+    if (!currentLineSpacing) {
+      console.warn('在 Taro 环境中未找到 --nutui-picker-item-height CSS 变量')
+      return
+    }
+    const parsedValue = parseFloat(currentLineSpacing)
+    if (isNaN(parsedValue)) {
+      console.warn('CSS 变量值格式无效')
+      return
+    }
+    lineSpacing.current = parsedValue
   }
-}, [pickerRollerRef.current])
+}, [])
🧹 Nitpick comments (3)
src/packages/pickerview/utils.ts (2)

1-4: 建议添加单元测试覆盖

momentum 函数缺少单元测试覆盖。该函数计算滚动动量,对于滚动体验至关重要,应该确保其行为符合预期。

需要我为这个函数生成单元测试代码吗?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 2-4: src/packages/pickerview/utils.ts#L2-L4
Added lines #L2 - L4 were not covered by tests


6-31: 建议优化样式生成函数

useStyles 函数可以通过以下方式优化:

  1. 使用 useMemo 缓存样式对象,避免不必要的重新计算
  2. 为 CSS 变量添加类型定义,提高类型安全性

建议按如下方式重构:

-export const useStyles = (
+export const useStyles = React.useMemo((
   touchTime: number,
   touchDeg: string,
   scrollDistance: number,
   lineSpacing: React.MutableRefObject<number>,
   rotation: number
-) => {
+) => ({
   const getTransitionStyle = (transformValue: string) => ({
     transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`,
     transform: transformValue,
   })

   const touchRollerStyle = () =>
     getTransitionStyle(`rotate3d(1, 0, 0, ${touchDeg})`)

   const touchTiledStyle = () =>
     getTransitionStyle(`translate3d(0, ${scrollDistance}px, 0)`)

   const rollerStyle = (index: number) => ({
     transform: `rotate3d(1, 0, 0, ${-rotation * (index + 1)}deg) translate3d(0px, 0px, ${Math.round(
       lineSpacing.current * 3.2
     )}px)`,
   })

   return { touchRollerStyle, touchTiledStyle, rollerStyle }
-}
+}), [touchTime, touchDeg, scrollDistance, lineSpacing.current, rotation])
src/packages/pickerview/pickerroller.tsx (1)

37-41: 建议优化 ref 类型定义

当前 ref 使用 any 类型,这降低了类型安全性。建议定义具体的类型:

-const rollerRef = useRef<any>(null)
-const pickerRollerRef = useRef<any>(null)
+const rollerRef = useRef<HTMLDivElement>(null)
+const pickerRollerRef = useRef<HTMLDivElement>(null)
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 654d1e0 and d9628e7.

📒 Files selected for processing (3)
  • src/packages/pickerview/pickerroller.taro.tsx (1 hunks)
  • src/packages/pickerview/pickerroller.tsx (1 hunks)
  • src/packages/pickerview/utils.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Check: codecov/patch
src/packages/pickerview/utils.ts

[warning] 2-4: src/packages/pickerview/utils.ts#L2-L4
Added lines #L2 - L4 were not covered by tests

src/packages/pickerview/pickerroller.tsx

[warning] 69-72: src/packages/pickerview/pickerroller.tsx#L69-L72
Added lines #L69 - L72 were not covered by tests


[warning] 75-79: src/packages/pickerview/pickerroller.tsx#L75-L79
Added lines #L75 - L79 were not covered by tests


[warning] 100-104: src/packages/pickerview/pickerroller.tsx#L100-L104
Added lines #L100 - L104 were not covered by tests


[warning] 107-114: src/packages/pickerview/pickerroller.tsx#L107-L114
Added lines #L107 - L114 were not covered by tests


[warning] 117-129: src/packages/pickerview/pickerroller.tsx#L117-L129
Added lines #L117 - L129 were not covered by tests


[warning] 155-155: src/packages/pickerview/pickerroller.tsx#L155
Added line #L155 was not covered by tests

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test

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/packages/pickerview/pickerroller.taro.tsx (2)

39-40: 优化 ref 类型定义

useRef 使用 any 类型可能导致类型安全问题。

建议添加具体的类型定义:

-  const rollerRef = useRef<any>(null)
-  const pickerRollerRef = useRef<any>(null)
+  const rollerRef = useRef<HTMLDivElement>(null)
+  const pickerRollerRef = useRef<HTMLDivElement>(null)

1-260: 建议提升组件的可测试性

组件的复杂度较高,建议:

  1. 将动画和触摸处理逻辑抽离为独立的 hooks
  2. 添加单元测试覆盖关键功能
  3. 考虑使用状态机管理复杂的交互状态
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d9628e7 and 38296d3.

📒 Files selected for processing (1)
  • src/packages/pickerview/pickerroller.taro.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (5)
src/packages/pickerview/pickerroller.taro.tsx (5)

112-112: 增强事件预防默认行为的处理

此问题在之前的评审中已提出,建议仍然需要增加对事件可取消状态的检查。


149-159: 优化 useEffect 依赖项

之前的评审中已指出,useEffect 的依赖数组包含 pickerRollerRef.current 可能导致不必要的重渲染。


174-176: 检查事件选项的影响

之前的评审中已指出,使用 once: true 选项可能导致事件监听器在触发一次后被移除。


232-232: 修复重复的 key 值

之前的评审中已指出,使用 item.value ?? index 作为 key 可能导致重复值的问题。

Also applies to: 243-243


9-9:

Details

✅ Verification successful

修复模块导入路径

导入路径已从 @/utils/use-touch 更新为 @/hooks/use-touch,但仍需验证此路径是否正确配置。

请运行以下脚本验证模块路径:


🏁 Script executed:

#!/bin/bash
# 验证 hooks/use-touch 模块是否存在
fd -t f "use-touch.(ts|tsx)" "src/hooks"

Length of output: 63


修复模块导入路径

经过验证,src/hooks/use-touch.ts 文件存在,因此更新后的导入路径 @/hooks/use-touch 已正确配置,不再需要修改。请继续关注此模块在对应平台上的使用情况。

@oasis-cloud oasis-cloud merged commit 6c89ee9 into jdf2e:feat_v3.x Feb 14, 2025
8 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 14, 2025
20 tasks
oasis-cloud pushed a commit to oasis-cloud/nutui-react that referenced this pull request Feb 20, 2025
@coderabbitai coderabbitai bot mentioned this pull request Mar 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.x Target branch 3.x action:review This PR needs more reviews (less than 2 approvals) size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants