Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: picker #2990

Open
wants to merge 51 commits into
base: feat_v3.x
Choose a base branch
from
Open

refactor: picker #2990

wants to merge 51 commits into from

Conversation

irisSong
Copy link
Collaborator

@irisSong irisSong commented Feb 14, 2025

🤔 这个变动的性质是?

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

🔗 相关 Issue

💡 需求背景和解决方案

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

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

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

Summary by CodeRabbit

  • 新功能
    • 新增 PickerView 组件,为用户提供全新的选择视图体验。
  • 重构
    • 优化了数据录入、日期选择器和选择器组件的交互逻辑,统一了显示属性,增强了稳定性。
    • 移除了 PickerOption 接口,采用新的 PickerOptions 和 PickerValue 类型,提升了类型安全性。
  • 样式
    • 精简并优化了选择器组件的样式,实现了界面更现代、简洁的视觉效果。

Copy link

coderabbitai bot commented Feb 14, 2025

Walkthrough

这次更新主要围绕配置和类型重构展开。配置文件中将多个组件的“dd”属性由 false 改为 true,并更新了 Picker 组件的 author,同时新增了 PickerView。各 DatePicker、Picker 及表单相关模块全面替换旧的 PickerOption 类型为更明确的 PickerOptionItem 和 PickerValue,同时将属性名称从 text 调整为 label。组件测试、演示示例和样式文件也进行了相应修改,部分老旧组件如 PickerPanel 被移除,导出接口也做了相应精简和更新。

Changes

文件或目录 更改摘要
src/config.json 更新“dd”属性(false → true)及 Picker 组件的 author,更新增 PickerView 配置项。
src/packages/datepicker/** DatePicker 组件及测试中:将 PickerOption 替换为 PickerOptionItem,调整 onConfirm/onChange 等方法签名,属性由 text 改为 label。
src/packages/datepicker/demos/{h5,taro}/*.tsx 更新各 demo 中的数据类型及参数(PickerOption → PickerOptionItem,(string
src/packages/form/demos/{h5,taro}/demo7.tsx 修改 pickerOptions 结构,将 text 更名为 label,并调整选项映射及显示逻辑。
src/packages/picker/__tests__/*.tsx and demos 测试及 demo 文件中修改选项对象:移除 PickerOption,用 label 替换 text,更新事件回调签名和导入类型。
src/packages/picker/{index.taro.ts,index.ts} 移除 PickerOption 类型的导出,只保留 PickerProps 及默认组件。
src/packages/picker/picker.scss 大幅简化样式,移除大部分子组件规则,保留基础控制样式并新增 box-sizing。
src/packages/picker/{picker.taro.tsx,picker.tsx} 重构核心逻辑:更新 PickerProps 接口、状态管理及渲染逻辑,统一使用新类型;同时简化 onConfirm/onChange 事件处理。
src/packages/picker/pickerpanel.{taro.tsx,tsx} 删除了旧的 PickerPanel 组件实现。
src/packages/picker/types.ts 移除 PickerOption 接口,新增 PickerRef、PickerActions 和 ColumnsType 类型。

Sequence Diagram(s)

DatePicker onConfirm Flow

sequenceDiagram
    participant User as 用户
    participant DP as DatePicker 组件
    participant Parent as 父组件
    User->>DP: 选择日期(使用 label 显示选项)
    DP->>DP: 内部状态更新为 PickerValue[] 与 PickerOptionItem[]
    DP->>Parent: 调用 onConfirm(selectedOptions, selectedValue)
    Parent-->>User: 更新显示选择的日期
Loading

Picker onConfirm Flow

sequenceDiagram
    participant User as 用户
    participant P as Picker 组件
    participant Parent as 父组件
    User->>P: 选择 Picker 选项(新数据结构)
    P->>P: 更新内部分层状态与值(使用 label)
    P->>Parent: 触发 onConfirm(selectedOptions, selectedValue)
    Parent-->>User: 返回更新后的选项
Loading

Possibly related PRs

  • feat: pickerview #2986: 与本 PR 直接相关,涉及 PickerView 组件配置和功能更新,二者共同调整同一组件的行为。

Suggested reviewers

  • xiaoyatong
  • oasis-cloud

Poem

嘎嘣脆的代码跳跃在夜空,
我这小兔子唱着重构之歌;
PickerView新登场,类型更明晰,
从 text 变为 label,美妙无比;
更新与精简皆在此刻酝酿,
快乐代码,欢欣鼓舞! 🐰✨


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b458e92 and ffba77f.

📒 Files selected for processing (1)
  • src/config.json (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/config.json
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: test

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

🧹 Nitpick comments (65)
src/packages/picker/demos/taro/demo2.tsx (1)

11-11: 建议增强选项数组的类型安全性

虽然选项数组的结构改进很好,但建议添加明确的类型定义以提高代码的可维护性。

建议应用以下更改:

-  const options = [
+  const options: PickerOptions = [
     [
       { value: 1, label: '南京市' },
       // ... other options
     ],
   ]

Also applies to: 13-25

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

5-23: 建议优化数据加载的用户体验

当前实现存在以下可以改进的地方:

  1. 缺少加载状态的管理,用户无法知道数据正在加载
  2. 没有错误处理机制
  3. 使用固定的3秒延迟不够灵活

建议按照以下方式优化:

 const Demo7 = () => {
+  const [loading, setLoading] = useState(true)
   const [columnsList, setColumnsList] = useState([] as PickerOptions[])
+  const [error, setError] = useState<Error | null>(null)
 
   useEffect(() => {
+    setLoading(true)
+    setError(null)
     setTimeout(() => {
-      setColumnsList([
-        [
-          { value: 1, label: '南京市' },
-          // ...其他城市
-        ],
-      ])
+      try {
+        setColumnsList([
+          [
+            { value: 1, label: '南京市' },
+            // ...其他城市
+          ],
+        ])
+      } catch (err) {
+        setError(err instanceof Error ? err : new Error('未知错误'))
+      } finally {
+        setLoading(false)
+      }
     }, 3000)
   }, [])
src/packages/picker/picker.tsx (1)

35-35: 可定制标签渲染
通过 renderLabel 回调让用户自定义选项文案,扩展性很好。

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

87-87: 建议使用可选链运算符以提升可读性及避免潜在空引用问题。
当前通过 while (columnOptions && columnOptions.children) 判断可能被静态分析工具视为可以用可选链替换。虽然现有实现并不会导致严重错误,但可选链有助于提升代码可读性并减少潜在空值风险。

以下为可能的代码修改示例:

- while (columnOptions && columnOptions.children) {
+ while (columnOptions?.children) {
    const currentOptions: PickerOptions = columnOptions.children
    ...
}

110-110: 减少对 (touch as any) 的依赖。
在此处使用 (touch as any).isVertical 虽然可以工作,但会降低类型安全性。建议在 useTouch 自定义 Hook 中完善返回值的类型定义,以免在此处使用 any

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

5-6: 谨慎使用 ForwardRefRenderFunction 与不完整类型定义。
此处的泛型声明中 Partial<PickerRollerProps> 虽然更灵活,但也可能使得必传属性意外缺失。建议确保类型与业务使用场景一致,以避免后期对属性的错误调用。


31-31: 建议对滚动惯性与距离做进一步可配置。
INERTIA_TIME 与 INERTIA_DISTANCE 的常量对交互有较大影响,后续若需要针对多种 UI 或设备场景做细微调整,可将其提取并抽离成可配置项,以提升扩展性。


37-37: 考虑将 useRef<any> 替换为更明确的类型。
useRef<any> 也降低了类型检查的有效性,特别是对 DOM 对象和自定义对象的操作时,建议使用合适的类型或 HTMLDivElement | null 等更精确的定义,从而在编译期捕获潜在错误。

Also applies to: 86-86

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

8-8: 针对 Taro 环境的条件逻辑可进一步抽象。
通过 web() 判断来分支处理,可在后续多平台逻辑扩展时引发重复或分支增多。若后续需兼容更多平台,可将 platform-taro 重构为统一的多平台适配层,以减少散落于各处的判断语句。

Also applies to: 12-12


38-38: 建议避免过渡使用 any 并确保类型安全。
isMovingpickerRollerRefrollerRef 等均使用了 any,在多人协作或者日后维护时容易出现逻辑或属性访问错误。可适度扩充类型以提升可维护性。

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

1-11: 建议优化类型导出的代码复用

index.ts 和 index.taro.ts 之间存在类型导出的重复代码。建议考虑创建一个共享的类型导出文件,以减少代码重复并提高可维护性。

+ // src/packages/pickerview/shared-types.ts
+ export type {
+   PickerViewProps,
+   PickerOptionItem,
+   PickerRollerProps,
+   PickerValue,
+   PickerOptions,
+   PickerOnChangeCallbackParameter,
+ } from './types'

  // src/packages/pickerview/index.ts
  import PickerView from './pickerview'
- export type {
-   PickerViewProps,
-   PickerOptionItem,
-   PickerRollerProps,
-   PickerValue,
-   PickerOptions,
-   PickerOnChangeCallbackParameter,
- } from './types'
+ export * from './shared-types'
  export default PickerView

  // src/packages/pickerview/index.taro.ts
  import PickerView from './pickerview.taro'
- export type {
-   PickerViewProps,
-   PickerOptionItem,
-   PickerRollerProps,
-   PickerValue,
-   PickerOptions,
-   PickerOnChangeCallbackParameter,
- } from './types'
+ export * from './shared-types'
  export default PickerView
src/packages/pickerview/demos/h5/demo1.tsx (2)

5-17: 建议将测试数据移至单独文件

建议将城市列表数据提取到单独的数据文件中,以提高代码的可维护性和重用性。

+ // src/packages/pickerview/demos/h5/data.ts
+ export const cityList = [
+   [
+     { value: 1, label: '南京市' },
+     { value: 2, label: '无锡市' },
+     // ... other cities
+   ],
+ ]

  // src/packages/pickerview/demos/h5/demo1.tsx
- const listData = [/*...*/]
+ import { cityList } from './data'

19-32: 建议增加更多交互示例

当前示例较为基础,建议添加以下功能来展示组件的完整特性:

  1. 多列选择示例
  2. 级联选择示例
  3. 禁用选项示例
  4. 自定义样式示例
src/packages/pickerview/demos/taro/demo1.tsx (2)

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

建议为 listData 添加类型定义,以提高代码的可维护性和类型安全性。

+ type PickerOption = {
+   value: number
+   label: string
+ }
+
- const listData = [
+ const listData: PickerOption[][] = [
   [
     { value: 1, label: '南京市' },
     // ...
   ],
 ]

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

在生产环境中直接使用 console.log 不太合适。建议:

  1. 移除 console.log 或使用专门的日志服务
  2. 添加具体的业务处理逻辑
src/packages/pickerview/demos/h5/demo2.tsx (1)

23-23: 建议将样式配置抽离

建议将样式配置抽离到统一的样式配置文件中,以便于维护和复用。

+ // styles/picker.ts
+ export const PICKER_STYLES = {
+   itemHeight: '28px',
+ }

// 组件中使用
- style={{ '--nutui-picker-item-height': '28px' }}
+ style={{ '--nutui-picker-item-height': PICKER_STYLES.itemHeight }}
src/packages/pickerview/demos/h5/demo5.tsx (2)

22-30: 建议添加属性说明注释

为了提高代码可读性,建议添加对特殊属性的说明注释,特别是 threeDimensionalduration 的用途和影响。

  <PickerView
    defaultValue={[1]}
    options={listData}
+   // 是否开启3D效果
    threeDimensional={false}
+   // 动画持续时间(毫秒)
    duration={500}
    onChange={({ value, selectedOptions }) => {
      console.log('onChange', value, selectedOptions)
    }}
  />

27-29: 建议完善事件处理

与其他 demo 类似,建议改进事件处理方式:

  1. 移除 console.log
  2. 添加错误处理
  3. 添加具体的业务逻辑
src/packages/pickerview/demos/taro/demo5.tsx (2)

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

在生产环境中保留 console.log 语句可能会影响性能并暴露敏感信息。建议使用条件性日志记录或完全移除。

-            console.log('onChange', value, selectedOptions)
+            if (process.env.NODE_ENV !== 'production') {
+              console.log('onChange', value, selectedOptions)
+            }

22-30: 建议添加错误处理和加载状态

当前实现缺少错误处理和加载状态的处理机制。

建议添加以下功能:

  1. 错误边界处理选项加载失败的情况
  2. 添加加载状态指示器
  3. 处理空数据的情况
 <PickerView
   defaultValue={[1]}
   options={listData}
   threeDimensional={false}
   duration={500}
+  loading={isLoading}
+  onError={(error) => {
+    console.error('PickerView error:', error)
+  }}
   onChange={({ value, selectedOptions }) => {
src/packages/pickerview/demos/h5/demo4.tsx (2)

29-31: 建议优化条件更新逻辑并添加注释

当前的条件更新逻辑缺乏清晰的业务含义说明,可能会让其他开发者感到困惑。

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

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

当前的列表数据直接定义在组件内部,建议将其提取为外部常量以提高可重用性。

+const CITY_OPTIONS = [
+  [
+    { value: 1, label: '南京市' },
+    { value: 2, label: '无锡市' },
+    // ...
+  ],
+]

 const Demo4 = () => {
-  const listData = [
-    [
-      { value: 1, label: '南京市' },
-      // ...
-    ],
-  ]
+  const listData = CITY_OPTIONS
src/packages/pickerview/utils.ts (2)

1-4: 建议添加注释说明动量计算逻辑

momentum 函数中的魔法数字 0.003 缺乏说明,建议添加注释解释计算逻辑。

+/**
+ * 计算滑动动量
+ * @param distance 滑动距离
+ * @param duration 滑动持续时间
+ * @returns 根据方向调整的速度值
+ */
 export const momentum = (distance: number, duration: number) => {
   const speed = Math.abs(distance / duration)
+  // 0.003 是经验值,用于调整滑动的灵敏度
   return (speed / 0.003) * (distance < 0 ? -1 : 1)
 }

24-28: 建议优化 3D 变换计算的可读性

rollerStyle 函数中的 3D 变换计算较为复杂,建议提取常量并添加注释。

+  // 3D 效果的深度系数
+  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/datepicker/demos/h5/demo2.tsx (1)

16-19: 建议移除生产环境中的调试代码

console.log 语句通常用于开发调试,建议在提交前移除或使用条件性日志记录。

 const confirm = (values: PickerValue[], options: PickerOptionItem[]) => {
-  console.log('values', values, options)
   setDesc(options.map((option) => option.label).join('-'))
 }
src/packages/pickerview/demos/h5/demo7.tsx (2)

7-23: 建议改进异步数据加载的实现

当前实现存在以下可以改进的地方:

  1. 使用固定的延时可能不是最佳实践
  2. 缺少加载状态的处理
  3. 缺少错误处理机制

建议添加加载状态和错误处理。

 const Demo7 = () => {
   const [columnsList, setColumnsList] = useState([] as PickerOptions[])
+  const [loading, setLoading] = useState(true)
+  const [error, setError] = useState<Error | null>(null)

   useEffect(() => {
+    setLoading(true)
+    setError(null)
     setTimeout(() => {
-      setColumnsList([
-        [
-          { value: 1, label: '南京市' },
-          // ... other cities
-        ],
-      ])
+      try {
+        setColumnsList([
+          [
+            { value: 1, label: '南京市' },
+            // ... other cities
+          ],
+        ])
+      } catch (err) {
+        setError(err instanceof Error ? err : new Error('Unknown error'))
+      } finally {
+        setLoading(false)
+      }
     }, 3000)
   }, [])

30-32: 建议移除或优化控制台日志

在生产环境中应该移除 console.log 语句,或使用专门的日志工具。

 onChange={({ value, selectedOptions }) => {
-  console.log('onChange', value, selectedOptions)
 }}
src/packages/pickerview/demos/h5/demo3.tsx (1)

28-33: 建议优化值比较和更新逻辑

当前实现存在以下问题:

  1. 使用硬编码的值比较不利于维护
  2. 包含调试日志
  3. 值更新逻辑可能导致用户困惑

建议将比较值提取为常量,移除调试日志,并考虑添加状态更新的提示。

+const TRIGGER_VALUE = ['Tuesday', 'Afternoon']
+const NEW_VALUE = ['Monday', 'Evening']

 onChange={({ value, selectedOptions }) => {
-  console.log('onChange', value, selectedOptions)
-  if (isEqual(value, ['Tuesday', 'Afternoon'])) {
-    setValue(['Monday', 'Evening'])
+  if (isEqual(value, TRIGGER_VALUE)) {
+    setValue(NEW_VALUE)
+    // 可以添加提示信息
   }
 }}
src/packages/pickerview/demos/taro/demo3.tsx (1)

3-3: 建议优化条件判断逻辑

  1. isEqual 用于简单数组比较可能过于复杂,建议使用 Array.prototype.join() 进行比较。
  2. 硬编码的值更新逻辑(从 ['Tuesday', 'Afternoon']['Monday', 'Evening'])可能会让用户感到困惑,建议添加注释说明此逻辑的用途。
- import isEqual from 'react-fast-compare'
...
- if (isEqual(value, ['Tuesday', 'Afternoon'])) {
+ // 演示动态更新选中值的示例
+ if (value.join() === ['Tuesday', 'Afternoon'].join()) {

Also applies to: 30-32

src/packages/pickerview/types.ts (2)

3-9: 建议完善类型定义

  1. PickerValue 类型定义可以更严格,建议移除 null 类型,使用 undefined 代替。
  2. PickerOptionItem 接口的 children 属性应该明确定义为数组类型。
- export type PickerValue = string | number | null
+ export type PickerValue = string | number | undefined

export interface PickerOptionItem {
  label: string | number
  value: string | number
- children?: PickerOptionItem[]
+ children: PickerOptionItem[] | undefined
}

29-38: 建议添加属性注释

建议为 PickerViewProps 接口的关键属性添加 JSDoc 注释,以提供更好的开发体验。

export interface PickerViewProps extends BasicComponent {
+ /** 设置组件引用 */
  setRefs?: (ref: any) => any
+ /** 选项数据 */
  options: PickerOptions[]
+ /** 当前选中值 */
  value?: PickerValue[]
+ /** 默认选中值 */
  defaultValue?: PickerValue[]
+ /** 是否开启3D效果 */
  threeDimensional?: boolean
+ /** 动画时长,单位ms */
  duration?: number | string
+ /** 自定义选项展示 */
  renderLabel: (item: PickerOptionItem) => React.ReactNode
+ /** 选项改变回调 */
  onChange?: (arg0: PickerOnChangeCallbackParameter) => void
}
src/packages/datepicker/demos/taro/demo5.tsx (1)

17-20: 建议优化时间格式化处理

  1. 默认时间描述 10:10 建议使用常量定义。
  2. 日期字符串拼接可能存在安全隐患,建议使用更安全的方式构造日期。
+ const DEFAULT_TIME = '10:10'
- const [desc, setDesc] = useState('10:10')
+ const [desc, setDesc] = useState(DEFAULT_TIME)

const confirm = (options: PickerOptionItem[], values: PickerValue[]) => {
  setDesc(options.map((option) => option.label).join(':'))
}

- defaultValue={new Date(`${defaultDescription} ${desc}`)}
+ defaultValue={new Date(
+   defaultValue.getFullYear(),
+   defaultValue.getMonth(),
+   defaultValue.getDate(),
+   ...desc.split(':').map(Number)
+ )}

Also applies to: 28-28

src/packages/datepicker/demos/h5/demo7.tsx (1)

17-21: 建议优化命名和时间处理

  1. confirm6 函数名不够直观,建议改为更具描述性的名称。
  2. 时间字符串 10:10:00 的处理方式可以更安全。
+ const DEFAULT_TIME_WITH_SECONDS = '10:10:00'
- const [desc, setDesc] = useState('10:10:00')
+ const [desc, setDesc] = useState(DEFAULT_TIME_WITH_SECONDS)

- const confirm6 = (values: PickerValue[], options: PickerOptionItem[]) => {
+ const handleTimeConfirm = (values: PickerValue[], options: PickerOptionItem[]) => {
  setDesc(options.map((option) => option.label).join(':'))
}

- onConfirm={(options, values) => confirm6(values, options)}
+ onConfirm={(options, values) => handleTimeConfirm(values, options)}

Also applies to: 31-31

src/packages/picker/demos/h5/demo5.tsx (1)

23-25: 建议优化类型定义,避免使用 any

forEach 回调函数中使用 any 类型会降低代码的类型安全性。建议使用更具体的类型定义。

-    options.forEach((option: any) => {
+    options.forEach((option: { label: string; value: string | number }) => {
src/packages/picker/demos/h5/demo2.tsx (1)

26-28: 建议优化类型定义,避免使用 any

forEach 回调函数中使用 any 类型会降低代码的类型安全性。建议使用更具体的类型定义。

-    selectedOptions.forEach((option: any) => {
+    selectedOptions.forEach((option: { label: string; value: string | number }) => {
src/packages/picker/demos/taro/demo5.tsx (2)

2-7: 建议优化导入语句的组织方式

当前的多行导入方式可以更简洁。建议合并为一行,提高代码的可读性。

-import {
-  Picker,
-  Cell,
-  PickerOptions,
-  PickerValue,
-} from '@nutui/nutui-react-taro'
+import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react-taro'

28-30: 建议优化类型定义,避免使用 any

forEach 回调函数中使用 any 类型会降低代码的类型安全性。建议使用更具体的类型定义。

-    options.forEach((option: any) => {
+    options.forEach((option: { label: string; value: string | number }) => {
src/packages/picker/demos/h5/demo4.tsx (2)

28-28: 建议移除调试代码

生产环境代码中不应该包含 console.log 语句。

-    console.log('confirmPicker', selectedOptions)

30-32: 建议优化类型定义和空值处理

代码存在以下问题:

  1. 使用了 any 类型
  2. 可选链操作符使用不当,建议改用空值合并运算符
-    selectedOptions.forEach((option: any) => {
-      option?.label && (description += ` ${option.label}`)
+    selectedOptions.forEach((option: { label: string; value: string | number }) => {
+      description += ` ${option.label ?? ''}`
src/packages/picker/demos/taro/demo4.tsx (2)

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

在生产环境中保留 console.log 语句可能会影响性能和安全性。

建议使用以下方式处理:

-    console.log('confirmPicker', selectedOptions)
+    if (process.env.NODE_ENV !== 'production') {
+      console.log('confirmPicker', selectedOptions)
+    }

35-37: 建议优化类型定义和空值处理

当前的类型定义使用了 any,并且对空值的处理不够严谨。

建议修改为:

-    selectedOptions.forEach((option: any) => {
-      option?.label && (description += ` ${option.label}`)
+    selectedOptions.forEach((option: PickerOptions[number]) => {
+      if (option?.label) {
+        description = description ? `${description} ${option.label}` : option.label
+      }
src/packages/pickerview/demos/h5/demo6.tsx (2)

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

当前的数据结构直接定义在组件中,这降低了代码的可维护性和复用性。

建议创建单独的数据文件,例如 cityData.ts

// cityData.ts
export const listData = [
  [
    {
      value: 1,
      label: '北京',
      children: [
        // ... existing children data
      ],
    },
    // ... remaining data
  ],
]

然后在组件中导入:

+import { listData } from './cityData'
 
 const Demo6 = () => {
-  const listData = [...]

70-74: 建议添加错误边界处理

当前的 onChange 回调缺少错误处理机制。

建议添加 try-catch 块:

-          onChange={({ value, selectedOptions }) => {
-            console.log('onChange', value, selectedOptions)
-          }}
+          onChange={({ value, selectedOptions }) => {
+            try {
+              if (process.env.NODE_ENV !== 'production') {
+                console.log('onChange', value, selectedOptions)
+              }
+              // 在这里添加其他必要的数据处理逻辑
+            } catch (error) {
+              console.error('选择器数据处理错误:', error)
+            }
+          }}
src/packages/pickerview/demo.tsx (1)

12-40: 建议将翻译文本抽离到独立的语言文件中

当前的翻译直接定义在组件中,这不利于维护和扩展。

建议创建独立的语言文件:

// locales/zh-CN.ts
export default {
  title: '基础用法',
  adjustHeight: '自适应高度',
  // ... other translations
}

// locales/en-US.ts
export default {
  title: 'Basic Usage',
  adjustHeight: 'Adjust Height',
  // ... other translations
}

然后在组件中导入:

+import zhCN from './locales/zh-CN'
+import enUS from './locales/en-US'
+import zhTW from './locales/zh-TW'

 const PickerViewDemo = () => {
   const [translated] = useTranslate({
-    'zh-CN': { ... },
-    'en-US': { ... },
-    'zh-TW': { ... },
+    'zh-CN': zhCN,
+    'en-US': enUS,
+    'zh-TW': zhTW,
   })
src/packages/pickerview/demos/taro/demo6.tsx (1)

72-74: 建议添加选择值的类型验证

当前的 onChange 回调没有对选择值进行类型验证。

建议添加类型验证:

           onChange={({ value, selectedOptions }) => {
+            if (!Array.isArray(value) || value.some(v => typeof v !== 'number')) {
+              console.error('选择值类型错误')
+              return
+            }
             console.log('onChange', value, selectedOptions)
           }}
src/packages/picker/demos/h5/demo1.tsx (2)

14-26: 建议添加默认选中值

为了提升用户体验,建议添加 defaultValue 属性来设置默认选中的城市。

 const Demo1 = () => {
   const [visible, setVisible] = useState(false)
   const [baseDesc, setBaseDesc] = useState('')
+  const defaultValue = [1] // 默认选中南京市
   const options = [
     [
       { value: 1, label: '南京市' },
       // ...
     ],
   ]
   return (
     <Picker
       // ...
+      defaultValue={defaultValue}
       options={options}
     />
   )
 }

27-44: 优化字符串拼接方式

建议使用 Array.map()join() 来优化字符串拼接,使代码更简洁易读。

 const confirmPicker = (
   selectedOptions: PickerOptions,
   selectedValue: PickerValue[]
 ) => {
   console.log('confirmPicker', selectedOptions, selectedValue)
-  let description = ''
-  selectedOptions.forEach((option: PickerOptionItem) => {
-    description += ` ${option.label}`
-  })
+  const description = selectedOptions
+    .map((option: PickerOptionItem) => option.label)
+    .join(' ')
   setBaseDesc(description)
 }
src/packages/picker/demos/taro/demo1.tsx (1)

1-64: 建议提取共享代码

H5 和 Taro 的 demo 代码几乎完全相同,建议将共享逻辑提取到一个公共文件中,以减少代码重复。

建议创建一个共享的 hooks 文件:

// src/packages/picker/demos/shared/usePickerDemo.ts
import { useState } from 'react'
import type { PickerOptions, PickerValue, PickerOnChangeCallbackParameter } from '@nutui/nutui-react'

export const usePickerDemo = () => {
  const [visible, setVisible] = useState(false)
  const [baseDesc, setBaseDesc] = useState('')
  
  const options = [
    [
      { value: 1, label: '南京市' },
      // ...
    ],
  ]

  const changePicker = (params: PickerOnChangeCallbackParameter) => {
    console.log('changePicker', params)
  }

  const confirmPicker = (selectedOptions: PickerOptions, selectedValue: PickerValue[]) => {
    // ...
  }

  return {
    visible,
    setVisible,
    baseDesc,
    options,
    changePicker,
    confirmPicker,
  }
}

然后在 H5 和 Taro 的 demo 中复用这个 hook:

const Demo1 = () => {
  const {
    visible,
    setVisible,
    baseDesc,
    options,
    changePicker,
    confirmPicker,
  } = usePickerDemo()
  
  return (
    // ...
  )
}
src/packages/picker/demos/taro/demo8.tsx (1)

46-62: 建议提取主题配置

建议将主题配置提取到共享的常量文件中,以确保 H5 和 Taro 版本的样式保持一致。

// src/packages/picker/demos/shared/constants.ts
export const PICKER_THEME = {
  nutuiPickerItemHeight: '48px',
  nutuiPickerItemActiveLineBorder: '1px dashed var(--nutui-color-primary)',
  nutuiPickerItemTextColor: 'var(--nutui-color-primary)',
  '--nutui-picker-item-height': '28px',
} as const

// 在 demo 中使用
<ConfigProvider theme={PICKER_THEME}>
  <Picker
    title="请选择城市"
    // ...
  />
</ConfigProvider>
src/packages/datepicker/demos/h5/demo8.tsx (1)

28-46: 格式化函数命名可以改进

函数名 formatter1 不够具体,建议使用更有描述性的名称。

建议重命名为更具描述性的名称:

- const formatter1 = (type: string, option: PickerOptionItem) => {
+ const appendUnitFormatter = (type: string, option: PickerOptionItem) => {
src/packages/datepicker/demos/taro/demo8.tsx (1)

28-46: 建议优化 formatter 函数的实现

当前实现直接修改了传入的 option 对象。建议返回新对象以保持不可变性。

建议如下修改:

  const formatter = (type: string, option: PickerOptionItem) => {
+   const newOption = { ...option }
    switch (type) {
      case 'year':
-       option.label += `年`
+       newOption.label += `年`
        break
      case 'month':
-       option.label += `月`
+       newOption.label += `月`
        break
      case 'day':
-       option.label += `日`
+       newOption.label += `日`
        break
      case 'hour':
-       option.label += `时`
+       newOption.label += `时`
        break
      default:
-       option.label += ''
+       newOption.label += ''
    }
-   return option
+   return newOption
  }
src/packages/picker/demos/taro/demo3.tsx (1)

40-50: 建议优化条件判断逻辑

当前实现中硬编码了特定值的判断([3]),这可能导致维护性问题。建议将这些特殊情况的处理提取为配置或常量。

建议如下修改:

+ const SPECIAL_CITY = {
+   value: 3,
+   defaultValue: 1,
+   defaultLabel: '南京市'
+ }

  if (isEqual(selectedValue, [SPECIAL_CITY.value])) {
-   setValue([1])
-   setBaseDesc('南京市')
+   setValue([SPECIAL_CITY.defaultValue])
+   setBaseDesc(SPECIAL_CITY.defaultLabel)
  }
src/packages/datepicker/demos/taro/demo1.tsx (1)

24-27: 建议移除多余的空行

change 函数中存在不必要的空行,影响代码的紧凑性。

建议如下修改:

  const change = (options: PickerOptionItem[], values: PickerValue[]) => {
    const v = values.join('/')
-

    setValue(v)
    setDesc2(options.map((option) => option.label).join(' '))
  }
src/packages/datepicker/demos/h5/demo1.tsx (1)

48-48: 建议移除或优化调试日志

生产环境代码中不建议保留 console.log 语句。如果需要日志,建议使用正式的日志系统。

建议如下修改:

- console.log('onconfirm', values, options)
+ // 如果需要日志,请使用正式的日志系统
src/packages/pickerview/demo.taro.tsx (1)

6-12: 建议为 Demo 组件添加类型定义

为了增强代码的类型安全性,建议为导入的 Demo 组件添加明确的类型定义。这样可以在编译时捕获潜在的类型错误。

-import Demo1 from './demos/taro/demo1'
+import type { FC } from 'react'
+import Demo1 from './demos/taro/demo1'
+type DemoComponent = FC
src/packages/picker/demos/h5/demo6.tsx (1)

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

为了保持代码整洁和性能优化,建议在生产环境中移除 console.log 语句。可以使用环境变量或日志库来控制日志输出。

-    console.log('onconfirm', selectedOptions, selectedValue)
+    if (process.env.NODE_ENV === 'development') {
+      console.log('onconfirm', selectedOptions, selectedValue)
+    }

Also applies to: 93-95

src/packages/picker/demos/taro/demo7.tsx (1)

87-90: 建议添加类型注释

为了提高代码的可读性和维护性,建议为 selectedOptionsselectedValue 参数添加显式的类型注释。

  const setAsyncConfirm = (
-   selectedOptions: PickerOptions,
-   selectedValue: PickerValue[]
+   selectedOptions: PickerOptions,
+   selectedValue: PickerValue[],
  ) => {
src/packages/pickerview/__test__/pickerview.spec.tsx (1)

97-103: 基础渲染测试用例完整。

测试用例验证了组件的基本渲染功能和默认值的正确显示。建议添加对错误情况的测试,如无效的 defaultValue。

src/packages/datepicker/datepicker.tsx (1)

410-418: 回调函数模式可以优化。

使用可选链操作符可以简化回调函数的调用。

-          onConfirm={(selectedOptions, selectedValue) => onConfirm && onConfirm(selectedOptions, selectedValue)}
+          onConfirm={(selectedOptions, selectedValue) => onConfirm?.(selectedOptions, selectedValue)}
🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/packages/datepicker/datepicker.taro.tsx (2)

415-423: 回调函数实现需要优化。

与非 Taro 版本一样,这里也应该使用可选链操作符来简化代码。

-          onConfirm={(selectedOptions, selectedValue) => onConfirm && onConfirm(selectedOptions, selectedValue)}
+          onConfirm={(selectedOptions, selectedValue) => onConfirm?.(selectedOptions, selectedValue)}
🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


1-431: 代码实现与非 Taro 版本保持一致。

良好地保持了两个版本之间的一致性,便于维护。建议考虑提取共同逻辑到共享模块中。

🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

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

19-27: 建议:考虑添加浏览器前缀以提高兼容性

对于 background-imagetransform 属性,建议添加浏览器前缀以提高兼容性。

 &-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);
   transform: translateZ(0);
 }

55-65: 建议:优化 backface-visibility 的写法

可以使用简写方式来减少代码重复。

 &-roller-item {
   position: absolute;
   top: 0;
-  backface-visibility: hidden;
-  -moz-backface-visibility: hidden;
-  -webkit-backface-visibility: hidden;
+  backface-visibility: hidden;
+  -webkit-backface-visibility: hidden;
   &-hidden {
     visibility: hidden;
     opacity: 0;
   }
 }
src/packages/pickerview/doc.md (1)

1-95: 文档结构完整,内容清晰

文档包含了必要的所有部分:

  • 组件说明
  • 引入方式
  • 示例代码
  • Props 定义
  • 主题定制说明

建议补充以下内容以使文档更加完善:

  1. 添加在线预览链接
  2. 补充更多使用场景示例
  3. 添加常见问题(FAQ)部分
src/packages/pickerview/doc.zh-TW.md (1)

1-95: 繁體中文文檔翻譯準確,結構完整

文檔的繁體中文翻譯質量很好,與簡體中文版本保持了一致的結構和內容。建議:

  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 ec4c186 and dc14741.

⛔ Files ignored due to path filters (1)
  • src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (78)
  • src/config.json (4 hunks)
  • src/packages/datepicker/__test__/datepicker.spec.tsx (6 hunks)
  • src/packages/datepicker/datepicker.taro.tsx (9 hunks)
  • src/packages/datepicker/datepicker.tsx (9 hunks)
  • src/packages/datepicker/demos/h5/demo1.tsx (3 hunks)
  • src/packages/datepicker/demos/h5/demo2.tsx (2 hunks)
  • src/packages/datepicker/demos/h5/demo3.tsx (2 hunks)
  • src/packages/datepicker/demos/h5/demo4.tsx (2 hunks)
  • src/packages/datepicker/demos/h5/demo5.tsx (2 hunks)
  • src/packages/datepicker/demos/h5/demo6.tsx (2 hunks)
  • src/packages/datepicker/demos/h5/demo7.tsx (2 hunks)
  • src/packages/datepicker/demos/h5/demo8.tsx (2 hunks)
  • src/packages/datepicker/demos/taro/demo1.tsx (2 hunks)
  • src/packages/datepicker/demos/taro/demo2.tsx (1 hunks)
  • src/packages/datepicker/demos/taro/demo3.tsx (2 hunks)
  • src/packages/datepicker/demos/taro/demo4.tsx (2 hunks)
  • src/packages/datepicker/demos/taro/demo5.tsx (2 hunks)
  • src/packages/datepicker/demos/taro/demo6.tsx (2 hunks)
  • src/packages/datepicker/demos/taro/demo7.tsx (2 hunks)
  • src/packages/datepicker/demos/taro/demo8.tsx (2 hunks)
  • src/packages/form/demos/h5/demo7.tsx (2 hunks)
  • src/packages/form/demos/taro/demo7.tsx (2 hunks)
  • src/packages/picker/__tests__/picker.spec.tsx (6 hunks)
  • src/packages/picker/demos/h5/demo1.tsx (2 hunks)
  • src/packages/picker/demos/h5/demo2.tsx (1 hunks)
  • src/packages/picker/demos/h5/demo3.tsx (1 hunks)
  • src/packages/picker/demos/h5/demo4.tsx (2 hunks)
  • src/packages/picker/demos/h5/demo5.tsx (2 hunks)
  • src/packages/picker/demos/h5/demo6.tsx (1 hunks)
  • src/packages/picker/demos/h5/demo7.tsx (2 hunks)
  • src/packages/picker/demos/h5/demo8.tsx (2 hunks)
  • src/packages/picker/demos/taro/demo1.tsx (2 hunks)
  • src/packages/picker/demos/taro/demo2.tsx (1 hunks)
  • src/packages/picker/demos/taro/demo3.tsx (1 hunks)
  • src/packages/picker/demos/taro/demo4.tsx (1 hunks)
  • src/packages/picker/demos/taro/demo5.tsx (2 hunks)
  • src/packages/picker/demos/taro/demo6.tsx (1 hunks)
  • src/packages/picker/demos/taro/demo7.tsx (2 hunks)
  • src/packages/picker/demos/taro/demo8.tsx (2 hunks)
  • src/packages/picker/index.taro.ts (0 hunks)
  • src/packages/picker/index.ts (0 hunks)
  • src/packages/picker/picker.scss (1 hunks)
  • src/packages/picker/picker.taro.tsx (6 hunks)
  • src/packages/picker/picker.tsx (6 hunks)
  • src/packages/picker/pickerpanel.taro.tsx (0 hunks)
  • src/packages/picker/pickerpanel.tsx (0 hunks)
  • src/packages/picker/types.ts (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/h5/demo7.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/demos/taro/demo7.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/packages/pickerview/utils.ts (1 hunks)
  • src/styles/variables.scss (1 hunks)
💤 Files with no reviewable changes (4)
  • src/packages/picker/index.ts
  • src/packages/picker/index.taro.ts
  • src/packages/picker/pickerpanel.tsx
  • src/packages/picker/pickerpanel.taro.tsx
✅ Files skipped from review due to trivial changes (2)
  • src/packages/pickerview/doc.taro.md
  • src/packages/pickerview/doc.en-US.md
🧰 Additional context used
🪛 GitHub Actions: CI
src/packages/datepicker/__test__/datepicker.spec.tsx

[error] 45-45: AssertionError: expected +0 to be 3 // Object.is equality

🪛 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)

src/packages/datepicker/datepicker.tsx

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/packages/datepicker/datepicker.taro.tsx

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

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

2-7: 导入声明更新符合重构要求!

导入声明已更新为使用新的类型定义 PickerOptionItemPickerValue,这与整体重构方向保持一致。


18-19: 函数签名和实现更新正确!

  • 函数参数类型从 PickerOption[] 更新为 PickerOptionItem[]
  • 属性访问从 text 更改为 label
  • 实现逻辑保持不变,仅更新了类型和属性名

这些更改与组件重构的目标保持一致。

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

2-7: 导入声明看起来不错!

类型导入的更改提高了代码的类型安全性,结构化的导入方式也使代码更加清晰。


38-50: 组件渲染部分的改动很棒!

Cell 和 Picker 组件的属性更新简洁明了,保持了组件功能的同时提高了代码的可读性。

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

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

组件的声明和必要的导入都很规范。


38-38: 导出语句正确!

组件导出方式符合规范。

src/packages/datepicker/demos/h5/demo4.tsx (1)

2-7: 导入更改看起来不错!

新增的类型导入使代码更加类型安全,结构清晰。

src/packages/picker/picker.tsx (25)

9-19: 引入新依赖与类型定义的变更不错
这些 import 统一了 Picker 相关类型,并使用 react-fast-compare 做深度比较,有助于避免复杂对象不必要的重新渲染,整体设计看起来良好。


24-24: 补充导入 PickerActionsPickerRef
在同一处为外部使用场景提供类型推断,明确了组件的引用方式,合理。


29-31: 改用 PickerOptions[]PickerValue[] 类型
使用更准确的类型定义有助于代码的可读性和类型安全,符合预期的重构目标。


41-42: 确认回调中的数据类型
selectedOptions 声明为 PickerOptions,但内部可能是多列或级联数据,需确保类型定义与实际选项结构保持一致。


46-47: 关闭回调的参数保持一致
与 onConfirm 相同的参数结构,维持了统一性,无明显问题。


49-49: onChange 属性更符合语义
增加回调类型 PickerOnChangeCallbackParameter,可让外部准确获取变更信息,设计合理。


54-54: 默认标题为空字符串
允许在不提供标题时保持界面简洁,可行。


57-57: 默认 value 未定义
结合非受控用法,方便在无默认值时让组件自行管理状态,设计合理。


86-86: 使用 usePropsValue 管理受控/非受控
能统一处理 visible、value 等属性,简化外部与内部状态的同步,思路巧妙。


90-91: onChange 回调中触发 onConfirm 需确保交互合理
此处 onChange 直接调用 props.onConfirm,可能在尚未点击“确认”时就触发提交逻辑,建议确认该行为符合业务需求。


94-103: 关闭弹窗时自动触发 onClose
结合 usePropsValue 实现自动监听,逻辑清晰。


115-115: 通过解构初始值为新数组
能够避免引用陷阱,初始化内部状态正确。


116-116: 引入 innerValueRef
在回调中读取最新状态,无需重新渲染,常见且实用的模式。


117-117: 内部选项初始为空数组
确保组件首次渲染时有初始可用状态,逻辑简单。


118-118: 使用 selectedOptionsRef
可在内部存储选中的选项对象或列表,避免重复计算,实践良好。


119-119: 自定义 useRefs
满足同时跟踪多个滚动实例等需求,针对性强。


123-126: useEffect 中更新内部值与选项
innerVisible 为真时初始化状态,不存在直接逻辑问题,但需留意依赖中包含 innerOptions 是否导致多次渲染循环。


128-128: 重复依赖项
此处仅重复说明依赖范围,忽略即可。


129-145: 分发回调与内部状态同步
内置防抖逻辑 isEqual 避免重复调用,更新后再通知外部的做法可减少多余渲染。


147-147: 确认事件函数封装
通过 onConfirmEvent 有效分离业务逻辑,便于维护。


153-153: 防止滚轮动画时提前触发确认
校验 moving 并在滚动结束后再执行保存流程,交互体验更友好。


159-163: 取消事件恢复状态
取消时重置 innerValue 并隐藏弹窗,符合直觉的交互逻辑。


191-214: 封装渲染逻辑
将 Picker 视图单独提取到 renderPickerElement,有效提高可读性与可维护性。


218-230: 结合 Popup + SafeArea
移动端场景友好,自动适配底部安全区域,合理。


236-236: 最终导出 Ref
React.forwardRef 能为外部提供触发开关等功能,便于集成。

src/packages/pickerview/pickerview.tsx (15)

1-19: 新增组件的基础导入
全面引入 React Hooks、第三方库与自定义类型,有利于拆分逻辑;react-fast-compare 用于深度比较以减少无效更新,思路良好。


21-27: 默认属性设计
defaultValuerenderLabel 等默认为空或简单函数,使组件具有良好的开箱即用体验。


29-32: 前向引用支持
通过 React.forwardRef 统一管理组件实例,增加拓展性。


33-43: 解构与合并默认属性
使用 { ...defaultProps, ...props } 整合用户与默认配置,简洁直观,但要确认对所有字段都适用。


45-55: 内部状态管理和多列的初始值
利用 usePropsValue 同时支持受控/非受控模式,存储选中的各列值,实践规范。


57-70: 多列或级联的类型判断
columnsType 区分了两种类型的选项,写法直观利于后续处理。


72-107: formatCascadeOptions 处理级联数据
通过循环与 children 逐级展开选项,逻辑完善,返回多列结构便于渲染。

🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


109-117: formatOptions 综合处理多列或级联
结合 columnsType 动态生成最终选项结构,代码可维护性高。


119-125: 在依赖更新时重置内部选项
若外部传入新 options,则重新计算 innerOptions 保持组件同步,逻辑正确。


126-131: 跟踪并同步外部选中值
稳定地监听 selectedValue 变化,保证内部与外部状态一致。


132-173: handleSelect 分步更新逻辑
分别针对多列和级联编排选项与值,分支逻辑清晰,层次分明。


175-185: 推导已选 Options
利用 innerOptionsinnerValue 匹配得到已选项,提供给父组件更直观的数据。


187-193: 变化后通知外部
useEffect 内合并当前选择并调用 onChange,及时同步信息。


195-217: 核心渲染结构
循环生成多列 PickerRoller,并在有数据时渲染 mask、indicator,布局逻辑完整。


220-224: 最终导出 PickerView
通过 forwardRef 输出,方便外部控制滚动逻辑或获取内部引用,无可见问题。

src/packages/picker/picker.taro.tsx (22)

8-10: 引入 Taro 原生组件与比较函数
View 组件搭配 react-fast-compare 深度判断,为小程序环境提供合适支持。


11-20: 添加 Taro 端 Picker 相关依赖
@nutui/nutui-react-taro 中的类型统一,保证跨端一致性,值得肯定。


25-25: 补充导入 PickerActionsPickerRef
提高复用性并清晰标明组件可用方法,设计合理。


30-32: 将选项与选中值声明为泛型数组
为多列或级联选项打下类型基础,避免类型不匹配。


36-36: 自定义标签渲染
renderLabel 扩展性高,可自定义显示逻辑,方便满足多种需求。


42-43: onConfirm 类型定义
同样传入选项与选中的值,保持与 picker.tsx 一致性,使用起来更自然。


46-47: onClose 回调传入相同数据
可在关闭时获取当前状态,逻辑与 Web 端一致。


50-50: 变更回调参数同样统一
精准传递 PickerOnChangeCallbackParameter,增强可维护性。


58-58: 默认 valueundefined
方便区分受控和非受控模式,贯彻统一设计。


63-63: 前向引用的整体封装
确保小程序端也能调用外部方法,复用逻辑与 Web 端相同。


87-87: usePropsValue 同步管理
可减少手动维护特性分支,利于组件内外完整交互。


91-92: onChange 中直接触发 onConfirm
同 Web 端存在的类似疑惑,需确认该流程是否符合 UI 预期,避免过早提交数据。


99-103: 关闭回调触发
与 Web 端统一逻辑:组件关闭后可获取最终或取消状态,简单直观。


116-120: 内部状态与引用
保留 innerValueRefinnerOptionsselectedOptionsRef 等,便于滚动动画、级联更新。


124-127: 在弹窗可见时重置内部状态
与 Web 端做法一致,显式设置当前值与选项,避免旧数据残留。


129-145: onChangeItem 差异更新
仅当新旧值不同才触发更新与回调,能减少重复渲染或多余操作。


148-153: 确认事件检查滚动状态
等待滚动停止后再应用选中值,交互更自然,功能匹配 Web 端。


154-154: 无滚动时即更新外部状态并收起
与前端逻辑对齐,完整关闭流程合理。


159-163: 取消事件回退至原有值
整体用户体验可控,与 onCancel 语义对齐。


193-214: 分离核心渲染
renderPickerElement 函数封装布局逻辑,与业务事件分离,维护更轻松。


220-230: 窗口与底部安全区适配
小程序端同样使用 PopupSafeArea,满足跨端一致体验。


238-238: 最终通过 forwardRef 导出
呼应上方 InternalPicker,保持组件的可复用性和一致性。

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

79-79: 检查滚动计算与索引转换逻辑的正确性。
handleMove 函数中使用 Math.round(updatedMove / lineSpacing.current) 去确定索引时,要确保 lineSpacingcurrentIndex 的默认值和增量逻辑彼此配合良好,避免出现 off-by-one 错误或小数精度失误。当前实现看上去可行,但依赖 36px 行高及 “+1” 偏移,若将来视觉样式或需求改动,则需要同步修正本段逻辑。

Also applies to: 95-95

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

151-158: 通过 CSS 变量获取高度时,请注意异常场景。
若获取不到 --nutui-picker-item-height 或解析失败,代码会跳过对 lineSpacing.current 的更新,可能导致滚动行为异常。建议补充对获取不到或转换失败时的兼容处理,并记录告警日志。

src/packages/picker/types.ts (2)

1-6: 类型定义清晰且实用!

PickerRefPickerActions 类型的定义为组件提供了良好的编程接口,使开发者能够通过引用来控制选择器的开关状态。


6-6: 类型定义完善且具有扩展性!

ColumnsType 类型很好地定义了选择器的三种模式:单选、多选和级联,为后续功能扩展提供了良好的基础。

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

1-11: 导出结构规范且完整!

入口文件清晰地导出了 PickerView 组件及其相关类型,方便使用者按需引入。类型导出的组织方式符合最佳实践。

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

2-7: 导入声明更新符合新的类型系统

导入声明已更新为使用新的 PickerOptionItemPickerValue 类型,这与组件重构的方向一致。


15-17: 类型定义和属性访问更新正确

函数签名已更新为使用 PickerValuePickerOptionItem 类型,同时将 text 属性更改为 label,保持了与整体重构的一致性。

src/packages/datepicker/demos/taro/demo7.tsx (2)

2-7: 导入声明更新符合重构要求

导入声明已更新为使用新的类型定义,这与组件的重构保持一致。


19-20: 类型定义和实现更新正确

函数签名和实现已更新为使用 PickerValuePickerOptionItem 类型,并正确使用了 label 属性。

src/packages/datepicker/demos/taro/demo4.tsx (2)

2-7: 导入声明更新保持一致

导入声明的更新与其他演示文件保持一致,符合重构规范。


18-19: 类型和实现更新符合预期

confirm 函数的参数类型和实现更新正确,使用了新的类型定义和属性名称。

src/packages/datepicker/demos/h5/demo3.tsx (2)

2-7: H5版本导入声明更新正确

H5版本的导入声明已正确更新,使用了新的类型定义。


19-23: 日期时间处理逻辑保持不变

虽然更新了类型定义,但保持了原有的日期和时间处理逻辑,这是正确的做法。

src/packages/datepicker/demos/taro/demo3.tsx (2)

2-7: Taro版本导入声明更新正确

Taro版本的导入声明已正确更新,与H5版本保持一致性。


19-23: 日期时间处理逻辑与H5版本一致

更新后的实现保持了与H5版本相同的日期时间处理逻辑,确保了跨平台的一致性。

src/packages/picker/demos/h5/demo5.tsx (1)

38-40: 代码结构优化良好

  • 使用了新的类型定义
  • 属性从 text 更新为 label
  • 回调函数直接传递,避免了不必要的包装
src/packages/picker/demos/h5/demo2.tsx (1)

41-44: 代码结构优化良好

  • Props 结构清晰
  • 使用了新的类型定义
  • 回调函数直接传递,提高了代码可读性
src/packages/picker/demos/h5/demo4.tsx (1)

43-48: 代码结构优化良好

  • Props 结构清晰
  • 使用了新的类型定义
  • 状态管理逻辑合理
src/packages/picker/demos/h5/demo1.tsx (1)

2-9: 类型定义更新得当!

引入了更具体的类型定义(PickerOptionsPickerValuePickerOnChangeCallbackParameterPickerOptionItem),提高了代码的类型安全性。

src/packages/picker/demos/h5/demo8.tsx (1)

46-62: 注意样式优先级冲突

Picker 组件同时使用了 ConfigProvider 的主题配置和内联样式,这可能导致样式优先级冲突。建议统一使用 ConfigProvider 进行样式配置。

 <ConfigProvider
   theme={{
     nutuiPickerItemHeight: '48px',
     nutuiPickerItemActiveLineBorder:
       '1px dashed var(--nutui-color-primary)',
     nutuiPickerItemTextColor: 'var(--nutui-color-primary)',
+    '--nutui-picker-item-height': '28px',
   }}
 >
   <Picker
-    style={{ '--nutui-picker-item-height': '28px' }}
     title="请选择城市"
     // ...
   />
 </ConfigProvider>
src/packages/datepicker/demos/h5/demo6.tsx (3)

2-7: 导入类型声明看起来不错!

类型导入清晰明确,符合重构要求。


17-27: 确认函数的实现符合预期

函数参数类型和属性访问已正确更新,保持了一致性。


28-49: 格式化函数实现正确

格式化函数的类型定义和实现都已正确更新,保持了一致性。

src/packages/datepicker/demos/taro/demo6.tsx (1)

2-7: Taro版本的导入声明同样正确

类型导入和H5版本保持一致,符合重构要求。

src/packages/picker/demos/h5/demo3.tsx (2)

3-4: 新增的依赖引入合理

使用 react-fast-compare 进行值比较是个好的做法。


24-30: 新增的changePicker函数实现简单明了

函数实现清晰,参数类型定义准确。

src/packages/datepicker/demos/h5/demo8.tsx (2)

19-21: 确认函数实现简洁有效

使用map和join处理选项数组的方式很优雅。


22-27: 过滤函数实现正确且实用

过滤函数的实现符合业务需求,类型定义准确。

src/packages/datepicker/demos/taro/demo8.tsx (1)

19-21: 类型更新正确实施!

确认函数的类型签名已正确更新为 PickerValue[]PickerOptionItem[],并且正确使用了 label 属性。

src/packages/picker/demos/taro/demo7.tsx (3)

2-8: 导入类型定义增强了类型安全性!

新增的类型导入使代码更加类型安全,这是一个很好的改进。


12-13: 状态管理得到了改进!

使用 PickerValue[] 类型替代字符串类型,提高了类型安全性。


53-57: 回调函数参数使用了类型定义!

使用 PickerOnChangeCallbackParameter 类型定义回调函数参数,这是一个很好的实践。

src/packages/form/demos/h5/demo7.tsx (1)

20-27: 选项数据结构得到统一!

text 属性更改为 label 使数据结构更加统一和标准化。

src/packages/form/demos/taro/demo7.tsx (1)

21-28: 跨平台保持一致性!

Taro 版本的选项数据结构与 H5 版本保持一致,这是很好的实践。

src/packages/datepicker/__test__/datepicker.spec.tsx (1)

94-115: 格式化函数更新了属性名称!

格式化函数现在正确地使用 label 属性而不是 text,保持了一致性。

src/packages/pickerview/__test__/pickerview.spec.tsx (1)

7-19: 测试数据结构清晰且覆盖了常见场景。

测试数据包含了单列、多列和级联数据的情况,有助于全面测试组件功能。

src/packages/picker/__tests__/picker.spec.tsx (2)

12-18: 接口定义更新合理。

text 属性更新为 label 使接口更符合常见命名规范。


20-28: 测试数据结构优化。

simpleColumns 包装在数组中提高了数据结构的一致性,便于处理多列场景。

src/packages/datepicker/datepicker.tsx (1)

42-44: 类型定义更新提升了类型安全性。

formatterfilter 函数的参数类型更新为 PickerOptionItem 增强了类型检查。

src/packages/picker/picker.scss (1)

11-14: 优化:样式属性的更新提高了布局的可预测性

添加 box-sizing: border-box 是一个很好的改进,这样可以使元素的尺寸计算更加直观和可预测。同时使用 CSS 变量($popup-title-height$popup-title-font-size)提高了样式的可维护性。

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

1-9: 良好实践:使用 CSS 变量和合理的布局结构

组件样式结构清晰,使用 CSS 变量(--nutui-picker-item-height)便于主题定制,同时合理使用 position: relativeoverflow: hidden 控制布局。

src/config.json (1)

690-704: 配置更新符合重构要求

配置文件的更新合理:

  1. 将 Picker 组件的 dd 属性更新为 true
  2. 添加了新的 PickerView 组件配置
  3. 作者信息更新为 "songsong"

建议:

  • 考虑为 PickerView 组件添加更详细的描述信息
  • 确保版本号与重构范围相匹配
src/styles/variables.scss (1)

564-565: 优化:Picker 组件变量定义更加灵活!

通过引入 $picker-item-height 变量并基于此计算 $picker-list-height,使得组件更易维护和定制。这种方式:

  1. 确保了列表高度与项目高度的一致性
  2. 简化了后续对高度的调整

Comment on lines 26 to 35
const confirmPicker = (
options: PickerOption[],
values: (string | number)[]
selectedOptions: PickerOptions,
selectedValue: PickerValue[]
) => {
let description = ''
options.forEach((option: any) => {
description += ` ${option.text}`
selectedOptions.forEach((option: any) => {
description += ` ${option.label}`
})
setbaseDefault(description)
setBaseDesc(description)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

避免使用 any 类型

confirmPicker 函数的类型签名改进很好,但在 forEach 回调中使用 any 类型会降低类型安全性。

建议应用以下更改:

-    selectedOptions.forEach((option: any) => {
+    selectedOptions.forEach((option: { label: string }) => {
       description += ` ${option.label}`
     })
📝 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 confirmPicker = (
options: PickerOption[],
values: (string | number)[]
selectedOptions: PickerOptions,
selectedValue: PickerValue[]
) => {
let description = ''
options.forEach((option: any) => {
description += ` ${option.text}`
selectedOptions.forEach((option: any) => {
description += ` ${option.label}`
})
setbaseDefault(description)
setBaseDesc(description)
}
const confirmPicker = (
selectedOptions: PickerOptions,
selectedValue: PickerValue[]
) => {
let description = ''
selectedOptions.forEach((option: { label: string }) => {
description += ` ${option.label}`
})
setBaseDesc(description)
}

Comment on lines 24 to 37
return (
<>
<Cell>
<PickerView
defaultValue={[5]}
options={columnsList}
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. 控制台日志不应该在生产环境使用
  2. 需要添加加载状态和错误状态的展示

建议按照以下方式修改:

   return (
     <>
       <Cell>
+        {loading && <div>加载中...</div>}
+        {error && <div>错误:{error.message}</div>}
+        {!loading && !error && (
         <PickerView
           defaultValue={[5]}
           options={columnsList}
           onChange={({ value, selectedOptions }) => {
-            console.log('onChange', value, selectedOptions)
+            // 在这里处理选择变更
           }}
         />
+        )}
       </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
defaultValue={[5]}
options={columnsList}
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
}}
/>
</Cell>
</>
)
}
return (
<>
<Cell>
{loading && <div>加载中...</div>}
{error && <div>错误:{error.message}</div>}
{!loading && !error && (
<PickerView
defaultValue={[5]}
options={columnsList}
onChange={({ value, selectedOptions }) => {
// 在这里处理选择变更
}}
/>
)}
</Cell>
</>
)
}

Comment on lines +18 to +19
const confirm = (values: PickerValue[], options: PickerOptionItem[]) => {
setDesc(options.map((option) => option.label).join(':'))
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

函数签名和实现的更改符合重构方向!

参数类型的更新提高了类型安全性,使用 label 替代 text 使API更加统一。

建议验证其他使用此组件的地方是否都已更新到新的API:


🏁 Script executed:

#!/bin/bash
# 搜索可能还在使用旧的 text 属性的地方
rg "option\.text" src/

Length of output: 836


请注意:部分文件仍在使用旧 API (option.text)

当前文件 src/packages/datepicker/demos/h5/demo4.tsx 的修改正确地将属性由 text 更新为 label 且提升了类型安全性。但全局搜索显示,以下文件中仍有对 option.text 的引用:

  • src/packages/calendar/demos/h5/demo6.tsx
  • src/packages/calendar/demos/taro/demo6.tsx
  • src/packages/popover/demos/taro/demo4.tsx
  • src/packages/popover/demos/taro/demo4-1.tsx
  • src/packages/popover/demos/h5/demo4.tsx
  • src/packages/popover/demos/h5/demo4-1.tsx

建议检查并统一更新这些文件以确保 API 的一致性。可使用如下命令进一步确认相关引用:

#!/bin/bash
rg "option\.text" src/

Comment on lines 25 to 27
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
}}
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. 在生产环境中应移除 console.log
  onChange={({ value, selectedOptions }) => {
-   console.log('onChange', value, selectedOptions)
+   try {
+     // 处理选择变更
+     if (process.env.NODE_ENV === 'development') {
+       console.log('onChange', value, selectedOptions)
+     }
+   } catch (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
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
}}
onChange={({ value, selectedOptions }) => {
try {
// 处理选择变更
if (process.env.NODE_ENV === 'development') {
console.log('onChange', value, selectedOptions)
}
} catch (error) {
// 错误处理逻辑
}
}}

Comment on lines 1 to 34
import React from 'react'
import { PickerView, Cell } from '@nutui/nutui-react-taro'

const Demo2 = () => {
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: '乌鲁木齐市' },
],
]

return (
<>
<Cell>
<PickerView
style={{ '--nutui-picker-item-height': '28px' }}
options={listData}
defaultValue={[1]}
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
}}
/>
</Cell>
</>
)
}
export default Demo2
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议重构以减少代码重复

H5 和 Taro 版本的 Demo2 代码几乎完全相同,建议:

  1. 创建一个共享的基础组件
  2. 根据平台差异进行必要的封装
+ // shared/demos/picker-demo2.tsx
+ import React from 'react'
+ 
+ export const createPickerDemo2 = (PickerView: any, Cell: any) => {
+   return () => {
+     const listData = [/* ... */]
+     
+     return (
+       <Cell>
+         <PickerView
+           style={{ '--nutui-picker-item-height': '28px' }}
+           options={listData}
+           defaultValue={[1]}
+           onChange={/* ... */}
+         />
+       </Cell>
+     )
+   }
+ }

+ // h5/demo2.tsx
+ import { PickerView, Cell } from '@nutui/nutui-react'
+ import { createPickerDemo2 } from '../shared/demos/picker-demo2'
+ export default createPickerDemo2(PickerView, Cell)

+ // taro/demo2.tsx
+ import { PickerView, Cell } from '@nutui/nutui-react-taro'
+ import { createPickerDemo2 } from '../shared/demos/picker-demo2'
+ export default createPickerDemo2(PickerView, 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
import React from 'react'
import { PickerView, Cell } from '@nutui/nutui-react-taro'
const Demo2 = () => {
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: '乌鲁木齐市' },
],
]
return (
<>
<Cell>
<PickerView
style={{ '--nutui-picker-item-height': '28px' }}
options={listData}
defaultValue={[1]}
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
}}
/>
</Cell>
</>
)
}
export default Demo2
// shared/demos/picker-demo2.tsx
import React from 'react'
export const createPickerDemo2 = (PickerView: any, Cell: any) => {
return () => {
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: '乌鲁木齐市' },
],
]
return (
<Cell>
<PickerView
style={{ '--nutui-picker-item-height': '28px' }}
options={listData}
defaultValue={[1]}
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
}}
/>
</Cell>
)
}
}
Suggested change
import React from 'react'
import { PickerView, Cell } from '@nutui/nutui-react-taro'
const Demo2 = () => {
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: '乌鲁木齐市' },
],
]
return (
<>
<Cell>
<PickerView
style={{ '--nutui-picker-item-height': '28px' }}
options={listData}
defaultValue={[1]}
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
}}
/>
</Cell>
</>
)
}
export default Demo2
// src/packages/pickerview/demos/taro/demo2.tsx
import { PickerView, Cell } from '@nutui/nutui-react-taro'
import { createPickerDemo2 } from '../shared/demos/picker-demo2'
export default createPickerDemo2(PickerView, Cell)
Suggested change
import React from 'react'
import { PickerView, Cell } from '@nutui/nutui-react-taro'
const Demo2 = () => {
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: '乌鲁木齐市' },
],
]
return (
<>
<Cell>
<PickerView
style={{ '--nutui-picker-item-height': '28px' }}
options={listData}
defaultValue={[1]}
onChange={({ value, selectedOptions }) => {
console.log('onChange', value, selectedOptions)
}}
/>
</Cell>
</>
)
}
export default Demo2
// h5/demo2.tsx
import { PickerView, Cell } from '@nutui/nutui-react'
import { createPickerDemo2 } from '../shared/demos/picker-demo2'
export default createPickerDemo2(PickerView, Cell)

Comment on lines +46 to +48
selectedOptions.forEach((option: any) => {
description += ` ${option.label}`
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议改进类型安全性

在 forEach 循环中使用 any 类型不够安全。应该使用 PickerOptionItem 类型。

建议如下修改:

- selectedOptions.forEach((option: any) => {
+ selectedOptions.forEach((option: PickerOptionItem) => {
    description += ` ${option.label}`
  })
📝 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
selectedOptions.forEach((option: any) => {
description += ` ${option.label}`
})
selectedOptions.forEach((option: PickerOptionItem) => {
description += ` ${option.label}`
})

Comment on lines 13 to 72
},
{
value: 4,
text: '东城区',
},
{
value: 5,
text: '西城区',
},
{
value: 6,
text: '丰台区',
},
],
},
{
value: 2,
text: '上海',
children: [
{
value: 1,
text: '黄埔区',
},
{
value: 2,
text: '长宁区',
},
{
value: 3,
text: '普陀区',
},
{
value: 4,
text: '杨浦区',
},
{
value: 5,
text: '浦东新区',
},
],
},
[
{
value: 1,
label: '北京',
children: [
{
value: 1,
label: '朝阳区',
},
{
value: 2,
label: '海淀区',
},
{
value: 3,
label: '大兴区',
},
{
value: 4,
label: '东城区',
},
{
value: 5,
label: '西城区',
},
{
value: 6,
label: '丰台区',
},
],
},
{
value: 2,
label: '上海',
children: [
{
value: 1,
label: '黄埔区',
},
{
value: 2,
label: '长宁区',
},
{
value: 3,
label: '普陀区',
},
{
value: 4,
label: '杨浦区',
},
{
value: 5,
label: '浦东新区',
},
],
},
],
]
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议提取共享的数据和工具函数

h5 和 taro 版本的 demo 中存在大量重复的数据结构和逻辑。建议将这些共同部分提取到共享文件中。

建议创建一个共享的数据文件:

// shared/cityData.ts
export const customCityData = [
  [
    {
      value: 1,
      label: '北京',
      children: [
        // ... existing data
      ],
    },
    // ... remaining data
  ],
]

然后在两个 demo 文件中导入:

-const customCityData = [ ... ]
+import { customCityData } from '../shared/cityData'

Comment on lines +58 to +84
if (value[0] === 2 && asyncData[0]?.[1].children.length === 0) {
console.log('updateChooseValueCustmer', index, value, selectedOptions)
setTimeout(() => {
if (asyncData[1].children.length === 0) {
asyncData[1].children = [
{
value: 1,
text: '黄埔区',
},
{
value: 2,
text: '长宁区',
},
{
value: 3,
text: '普陀区',
},
{
value: 4,
text: '杨浦区',
},
{
value: 5,
text: '浦东新区',
},
]
setAsyncData([...asyncData])
}
}, 100)
asyncData[0][1].children = [
{
value: 1,
label: '黄埔区',
},
{
value: 2,
label: '长宁区',
},
{
value: 3,
label: '普陀区',
},
{
value: 4,
label: '杨浦区',
},
{
value: 5,
label: '浦东新区',
},
]
setAsyncData([...[...asyncData]])
}, 0)
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. 使用 setTimeout(fn, 0) 是一种反模式

建议使用不可变的方式更新状态:

-      asyncData[0][1].children = [
-        // ... data
-      ]
-      setAsyncData([...[...asyncData]])
+      setAsyncData((prevData) => {
+        const newData = JSON.parse(JSON.stringify(prevData))
+        newData[0][1].children = [
+          // ... data
+        ]
+        return newData
+      })

对于异步数据加载,建议:

  1. 使用 Promise 或 async/await
  2. 添加加载状态指示
  3. 实现错误处理机制
📝 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
if (value[0] === 2 && asyncData[0]?.[1].children.length === 0) {
console.log('updateChooseValueCustmer', index, value, selectedOptions)
setTimeout(() => {
if (asyncData[1].children.length === 0) {
asyncData[1].children = [
{
value: 1,
text: '黄埔区',
},
{
value: 2,
text: '长宁区',
},
{
value: 3,
text: '普陀区',
},
{
value: 4,
text: '杨浦区',
},
{
value: 5,
text: '浦东新区',
},
]
setAsyncData([...asyncData])
}
}, 100)
asyncData[0][1].children = [
{
value: 1,
label: '黄埔区',
},
{
value: 2,
label: '长宁区',
},
{
value: 3,
label: '普陀区',
},
{
value: 4,
label: '杨浦区',
},
{
value: 5,
label: '浦东新区',
},
]
setAsyncData([...[...asyncData]])
}, 0)
if (value[0] === 2 && asyncData[0]?.[1].children.length === 0) {
console.log('updateChooseValueCustmer', index, value, selectedOptions)
setTimeout(() => {
setAsyncData((prevData) => {
const newData = JSON.parse(JSON.stringify(prevData))
newData[0][1].children = [
{
value: 1,
label: '黄埔区',
},
{
value: 2,
label: '长宁区',
},
{
value: 3,
label: '普陀区',
},
{
value: 4,
label: '杨浦区',
},
{
value: 5,
label: '浦东新区',
},
]
return newData
})
}, 0)
}

Comment on lines 43 to 45
const columns = container.querySelectorAll('.nut-pickerview-list')[0]
const lists = columns.querySelectorAll('.nut-pickerview-roller-item-title')
expect(lists.length).toBe(3)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

修复测试失败!

CI 管道显示此处的断言失败。需要验证选择器和预期的列表长度是否正确。

- const columns = container.querySelectorAll('.nut-pickerview-list')[0]
- const lists = columns.querySelectorAll('.nut-pickerview-roller-item-title')
+ const columns = container.querySelectorAll('.nut-pickerview-list')
+ const lists = columns[0].querySelectorAll('.nut-pickerview-roller-item-title')
  expect(lists.length).toBe(3)
📝 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 columns = container.querySelectorAll('.nut-pickerview-list')[0]
const lists = columns.querySelectorAll('.nut-pickerview-roller-item-title')
expect(lists.length).toBe(3)
const columns = container.querySelectorAll('.nut-pickerview-list')
const lists = columns[0].querySelectorAll('.nut-pickerview-roller-item-title')
expect(lists.length).toBe(3)
🧰 Tools
🪛 GitHub Actions: CI

[error] 45-45: AssertionError: expected +0 to be 3 // Object.is equality

Comment on lines 134 to 166
test('should match onchange', async () => {
const PenderContent = () => {
const [value, setValue] = useState([])
const [options, setInnerOptions] = useState<PickerOptions[]>([])

useEffect(() => {
const timer = setTimeout(() => {
setInnerOptions(listData)
setValue([1])
}, 1000)

return () => clearTimeout(timer) // 清理定时器
}, [])

return (
<PickerView
value={value}
options={options}
onChange={({ value }) => {
if (value[0] === 1) {
setValue([3])
}
}}
/>
)
}

const { container } = render(<PenderContent />)

await waitFor(() => {
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

异步更新测试需要优化。

当前的异步测试使用了固定的延时时间(1000ms),这可能导致测试不稳定。建议使用 act 包装异步操作,并考虑使用更可靠的等待机制。

-      const timer = setTimeout(() => {
-        setInnerOptions(listData)
-        setValue([1])
-      }, 1000)
+      await act(async () => {
+        setInnerOptions(listData)
+        setValue([1])
+      })

Committable suggestion skipped: line range outside the PR's diff.

Copy link

codecov bot commented Feb 14, 2025

Codecov Report

Attention: Patch coverage is 95.83333% with 4 lines in your changes missing coverage. Please review.

Project coverage is 85.89%. Comparing base (86164bb) to head (ffba77f).

Files with missing lines Patch % Lines
src/packages/picker/picker.tsx 95.00% 4 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           feat_v3.x    #2990      +/-   ##
=============================================
+ Coverage      85.79%   85.89%   +0.09%     
=============================================
  Files            281      280       -1     
  Lines          18527    18189     -338     
  Branches        2810     2741      -69     
=============================================
- Hits           15896    15623     -273     
+ Misses          2626     2561      -65     
  Partials           5        5              

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

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