Skip to content

Comments

refactor: cascader#3007

Merged
xiaoyatong merged 12 commits intojdf2e:feat_v3.xfrom
oasis-cloud:refactor_3x_cascader
Mar 12, 2025
Merged

refactor: cascader#3007
xiaoyatong merged 12 commits intojdf2e:feat_v3.xfrom
oasis-cloud:refactor_3x_cascader

Conversation

@oasis-cloud
Copy link
Collaborator

@oasis-cloud oasis-cloud commented Feb 20, 2025

Summary by CodeRabbit

  • 新功能

    • 新增多个示例页面,展示组件在受控与非受控模式下的使用及异步数据加载效果。
  • 改进

    • 优化了状态管理、数据加载和选项更新逻辑,提高交互流畅性与响应速度。
    • 强化了类型安全和事件处理,改善面板切换和选项选择体验。
  • 文档更新

    • 完善了使用说明,新增“基础用法-非受控”与“部分数据动态加载”示例,帮助用户更好地理解组件特性。
    • 更新了属性签名,确保文档与代码一致。
  • 测试与配置

    • 精简测试策略,去除冗余测试,并更新配置确保组件功能稳定启用。

@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2025

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

161 files out of 368 files are above the max files limit of 200.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

此次 PR 修改涉及 Cascader 组件及相关测试、Demo、文档、类型和工具函数。测试文件中移除了 Tree 类相关测试,并将 onLoad 异步处理改为 Promise。组件文件更新了 Props 类型、事件回调签名及内部状态管理,同时增加了 onTabsChange。Demo 文件中新增 Demo7,并调整了状态变量和翻译键值。文档、类型定义和工具函数(如 isEmpty)均作了相应更新,同时 Address 组件接口改为必填。配置文件中启用 Cascader 模块。

Changes

文件 变更摘要
src/packages/cascader/__tests__/cascader.spec.tsx 移除 Tree 相关测试与无用依赖,更新 onLoad 异步实现
src/packages/cascader/cascader.taro.tsx
src/packages/cascader/cascader.tsx
更新 Props 类型(继承 CascaderPopupProps),修改 onLoad、onChange、onPathChange 签名,优化状态管理
src/packages/cascader/demo.taro.tsx
src/packages/cascader/demo.tsx
添加 Demo7 组件,更新翻译键(customStyle → title5)及展示顺序
src/packages/cascader/demos/h5/*.tsx Demo1–6 重构状态变量命名、更新事件处理及异步加载;新增 Demo7 展示 Cascader 使用
src/packages/cascader/demos/taro/*.tsx 同步 H5 Demo 改动,重构状态管理,更新 onLoad 和 onChange 实现,新增 Demo7
src/packages/cascader/doc*.md 各版本文档更新基础用法、动态加载说明及事件签名
src/packages/cascader/types.ts 移除 CascaderOption 中 level 属性,新增 index 签名及 CascaderActions 类型
src/packages/cascader/utils.ts
src/utils/is-empty.ts
重构选项归一化函数(formatTree → normalizeOptions、convertListToOptions → normalizeListOptions),新增 isEmpty 函数
src/packages/address/*.(taro.)tsx AddressProps 接口中 value 与 defaultValue 由可选改为必填,同时调整 defaultProps
src/config.json 将 Cascader 配置中的 "dd" 属性值由 false 更新为 true

Sequence Diagram(s)

sequenceDiagram
    participant U as 用户
    participant C as Cascader组件
    participant L as onLoad函数
    participant D as 数据返回

    U->>C: 点击触发级联选择
    C->>L: 调用 onLoad(节点, level)
    L-->>C: 返回 Promise[选项数组]
    C->>C: 更新状态并渲染选项
    C->>U: 显示更新后的选项
Loading

Possibly related PRs

Suggested reviewers

  • xiaoyatong
  • irisSong

Poem

我是一只快乐的小兔子,
跳跃在代码的森林间,
测试精简不再迷茫,
异步加载似春风拂面,
组件更稳健,文档更新明晰,
让我们欢呼,在代码中畅泳!
🐇💻🌟


🪧 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.
  • @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 20, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🔭 Outside diff range comments (3)
src/packages/cascader/demos/h5/demo6.tsx (1)

89-94: 🛠️ Refactor suggestion

建议改进事件处理函数的类型安全性

事件处理函数使用了 any 类型,这降低了代码的类型安全性。

-  const onChange = (value: any, path: any) => {
+  const onChange = (value: CascaderOption['value'][], path: CascaderOption[]) => {
     setValue(value)
   }
-  const onPathChange = (value: any, path: any) => {
+  const onPathChange = (value: CascaderOption['value'][], path: CascaderOption[]) => {
     console.log('onPathChange', value, path)
   }
src/packages/cascader/demos/taro/demo4.tsx (1)

1-70: 🛠️ Refactor suggestion

建议提取共享逻辑

此文件与 h5/demo4.tsx 存在大量重复代码。建议提取共享逻辑到一个公共文件中,减少代码重复。

建议:

  1. 创建一个共享的 hooks 文件
  2. 将状态管理和异步加载逻辑移至该文件
  3. 在 H5 和 Taro 版本中复用该逻辑

示例实现:

// hooks/useCascaderDemo.ts
export function useCascaderDemo() {
  const [visible, setVisible] = useState(false)
  const [value, setValue] = useState<string[]>([])
  const [options, setOptions] = useState<CascaderOption[]>([])

  useEffect(() => {
    // 初始化逻辑
  }, [])

  const lazyLoad = async (node: CascaderOption, level: number) => {
    // 异步加载逻辑
  }

  return {
    visible,
    setVisible,
    value,
    setValue,
    options,
    lazyLoad,
  }
}
src/packages/cascader/__tests__/cascader.spec.tsx (1)

142-143: 💡 Verification agent

🧩 Analysis chain

快照测试需要更新

测试用例的快照不匹配,这可能是由于组件的有意更改导致的。请检查更改是否符合预期,然后更新快照。

运行以下命令更新快照:


🏁 Script executed:

#!/bin/bash
# 更新失败的快照测试
npm test -- -u

Length of output: 867


快照更新失败:配置问题需要解决

目前快照测试因预期的组件更改而不匹配,但在执行更新快照命令时(npm test -- -u)出现了配置错误——在加载 vite.config.mts 时缺少依赖 @vitejs/plugin-react。请按以下步骤操作:

  • 确认组件更改:确认快照不匹配是预期的变更结果。
  • 检查并修复依赖配置:请检查项目的 Vite 配置文件以及相关依赖项,确保已正确安装 @vitejs/plugin-react 或调整配置以匹配当前项目需要。
  • 重新更新快照:配置问题解决后,再次运行 npm test -- -u 来更新快照。

涉及文件:

  • src/packages/cascader/__tests__/cascader.spec.tsx(第142-143行处)
  • vite.config.mts(配置文件中涉及依赖的加载)
🧰 Tools
🪛 GitHub Actions: CI

[error] 143-143: Snapshot Cascader > visible true 1 mismatched

🧹 Nitpick comments (33)
src/packages/cascader/demos/h5/demo6.tsx (2)

19-21: 建议改进 value 状态的类型定义

当前 value 的类型定义为 string[],建议使用更具体的类型来增强类型安全性。

-  const [value, setValue] = useState<string[]>(['浙江', '温州', '鹿城区'])
+  const [value, setValue] = useState<CascaderOption['value'][]>(['浙江', '温州', '鹿城区'])

22-88: 建议添加加载状态管理

异步加载数据时没有处理加载状态,可能会影响用户体验。建议添加 loading 状态来优化交互。

+  const [loading, setLoading] = useState(true)
   useEffect(() => {
+    setLoading(true)
     setTimeout(() => {
       setOptions([
         // ... options ...
       ])
+      setLoading(false)
     }, 300)
   }, [])

然后在 Cascader 组件中使用:

   <Cascader
     visible={visible}
     activeColor="#3768FA"
     value={value}
     title="选择地址"
     options={options}
+    loading={loading}
     // ... other props ...
   />
src/packages/cascader/demos/taro/demo2.tsx (1)

75-77: 建议改进类型定义

onChange 处理函数存在以下问题:

  1. 使用了 any 类型,降低了类型安全性
  2. path 参数未被使用,但仍被声明

建议按如下方式优化:

- const onChange = (value: any, path: any) => {
+ const onChange = (value: string[]) => {
    setValue(value)
  }
src/packages/cascader/utils.ts (1)

3-22: 递归地规范化选项结构时,应注意对 children 的类型检查。
当前对 children 的处理默认假设其为数组,若后续外部传参出现非数组类型,可能导致 normalizeOptions 抛出错误。可在递归调用前确保对 children 进行判空或类型判断,以增强健壮性。

- children: normalizeOptions(children, keyMap),
+ children: Array.isArray(children)
+   ? normalizeOptions(children, keyMap)
+   : undefined,
src/packages/cascader/demos/h5/demo1.tsx (3)

5-11: 使用 State 管理 visiblevalueoptions 与回调函数 onChange
命名清晰,逻辑简洁。但若有异步需求,可在 onChange 内部添加防抖或节流逻辑,以应对频繁操作。


12-77: 通过定时器模拟异步设置 options 的做法缺少错误处理。
在真实环境中或需调用接口获取数据,可额外捕获错误或取消请求。若只做演示,可在注释中标明示例用途,方便后期维护。


83-85: Celldescription 字段与新状态 value 对应正确。
value 存在多级选中,可考虑在 UI 上进行拼接或展示完整层级路径,提高易用性。

src/packages/cascader/cascader.tsx (4)

2-18: React Hooks 与组件依赖的引入顺序清晰。
这里引入了部分 NutUI 图标组件与内部自定义 Hook,建议定期审视依赖关系,若有重复引用或未使用的依赖应及时清理。


55-74: CascaderProps 接口新增与合并的字段较多,注意字段含义和可选性的一致性。
onLoadonChangeonPathChange 等返回值、参数类型都需要与真实使用场景相符,否则可能引起调用端类型断言失效或业务逻辑混乱。


76-92: defaultProps 使用类型断言时需注意与接口字段保持一致。
如果后续对 CascaderProps 有更新,应同步修正这里的默认值,否则会增大维护成本。


266-343: 渲染级联条目与 Tabs 切换部分在交互可用性上值得优化。

  1. renderCascaderItem 中针对 disabledactiveloading 设置了较好的状态区分。
  2. 如需在移动端适配,可在 className 或样式上增加更细的触控区域提示。
  3. Popup 关闭前,可添加一些过渡动画或二次确认逻辑(根据业务场景),并与 Tabs 的切换逻辑保持一致。
src/packages/cascader/cascader.taro.tsx (4)

2-14: 简化组件依赖的导入顺序有助于阅读与维护。
组件依赖较为分散,建议在后续合并或归类导入,以提高可维护性。


55-74: CascaderProps 接口拓展,支持更多自定义回调与弹窗配置。

  1. onLoadonChangeonPathChangeonTabsChange 函数签名明确,提高可扩展性。
  2. 建议为 valuedefaultValuevisible 等属性添加更详细的 JSDoc 注释,提高团队协作时的可理解度。

236-264: chooseItem 异步加载分支完善,需检查竞态与错误处理。

  • onLoad 调用返回较慢或发生异常时,当前逻辑仅在 try/catch 之后简单打印错误或置空 loading,可考虑增加更多提示或重试机制。
  • 对比 pane.leafpane.children 的判断逻辑,如后续出现伪空数据或字段缺失,可能出现意外分支。

324-343: 切换是否显示 Popup 的条件式渲染处理直观,注意控制底层动画与交互一致。

  • 建议在 Popup 关闭后回调里能清楚区分点击遮罩与点击关闭图标等情况,以便进行更丰富的业务处理。
src/utils/is-empty.ts (1)

1-6: isEmpty 工具函数实现简洁,请谨慎处理特殊类型边界。
例如:若传入 new Date()NaN、或类似无可枚举属性的对象,需要防范意外判定。可考虑补充更多单元测试覆盖场景。

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

15-16: 使用索引签名 [key: string]: any 扩展 CascaderOption 的灵活性。
在不一致的团队协作环境下,需明确约定可能添加的额外字段含义,避免导致数据结构不规范。


40-43: 新增 CascaderActions 类型,开放 open()close() 方法。
该设计便于父级组件或外部管理弹窗状态,但可考虑是否需要更多状态同步回调来满足复杂业务场景。

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

25-27: 建议改进 onChange 的类型定义

当前 onChange 的参数使用了 any 类型,建议使用更具体的类型定义以提高类型安全性。

-  const onChange = (value: any, path: any) => {
+  const onChange = (value: string[], path: CascaderOption[]) => {
     setValue(value)
   }

8-24: 优化异步加载逻辑

loadCascaderItemData 函数有以下几点建议:

  1. 将 500ms 的延迟时间提取为常量
  2. leaf 判断条件 (level >= 2) 建议添加注释说明原因
+  const LOAD_DELAY = 500
   const loadCascaderItemData = (
     node: CascaderOption,
     level: number
   ): Promise<CascaderOption[]> => {
     return new Promise((resolve) => {
-      setTimeout(() => {
+      setTimeout(() => {
         const { value } = node
         const text = value?.toString().substring(0, 1)
         const value1 = `${text}${level + 1}1`
         const value2 = `${text}${level + 1}2`
         resolve([
+          // 当层级达到2时设置为叶子节点
           { value: value1, text: value1, leaf: level >= 2 },
           { value: value2, text: value2, leaf: level >= 2 },
         ])
-      }, 500)
+      }, LOAD_DELAY)
     })
   }
src/packages/cascader/demos/taro/demo3.tsx (1)

25-27: 建议改进 onChange 的类型定义

与 H5 版本相同,建议改进类型定义以提高代码质量。

-  const onChange = (value: any, path: any) => {
+  const onChange = (value: string[], path: CascaderOption[]) => {
     setValue(value)
   }
src/packages/cascader/demos/h5/demo5.tsx (2)

14-27: 建议优化初始化逻辑

useEffect 中的延迟初始化有以下建议:

  1. 将 300ms 延迟时间提取为常量
  2. 考虑添加加载状态指示器
  3. 建议添加错误处理机制
+  const INIT_DELAY = 300
+  const [loading, setLoading] = useState(false)

   useEffect(() => {
+    setLoading(true)
     setTimeout(() => {
       setValue(['广东省', '广州市'])
       setOptions([
         // ... options
       ])
+      setLoading(false)
-    }, 300)
+    }, INIT_DELAY)
   }, [])

29-31: 改进 onChange 的类型安全性

建议使用更具体的类型定义替代 any。

-  const onChange = (value: any, path: any) => {
+  const onChange = (value: string[], path: CascaderOption[]) => {
     setValue(value)
   }
src/packages/cascader/demos/h5/demo4.tsx (2)

25-28: 建议改进类型注解

node: any 的类型注解过于宽松,建议使用更具体的类型。

-  node: any,
+  node: CascaderOption,

42-44: 建议补充类型注解

change4 函数的参数类型应该更明确。

- const change4 = (value: any, path: any) => {
+ const change4 = (value: string[], path: CascaderOption[]) => {
src/packages/cascader/demos/taro/demo1.tsx (2)

8-10: 建议改进类型安全性

建议为 onChange 函数参数添加更具体的类型定义,避免使用 any

-  const onChange = (value: any, path: any) => {
+  const onChange = (value: string[], path: CascaderOption[]) => {
     setValue(value)
   }

11-77: 建议优化异步加载逻辑

  1. 建议将超时时间抽取为常量或配置项
  2. 建议添加错误处理和加载状态
+  const [loading, setLoading] = useState(false)
+  const LOAD_TIMEOUT = 300
   useEffect(() => {
+    setLoading(true)
     setTimeout(() => {
       setOptions([...])
+      setLoading(false)
-    }, 300)
+    }, LOAD_TIMEOUT)
+    return () => setLoading(false)
   }, [])
src/packages/cascader/demos/h5/demo7.tsx (2)

8-11: 建议移除生产环境的调试代码

建议移除 console.log 语句,或使用环境变量控制调试输出。

   const onChange = (value: any, path: any) => {
-    console.log('onchange', value, path)
     setValue(value)
   }
   // ...
   onPathChange={(value, path) => {
-    console.log(value, path)
   }}

Also applies to: 102-104


15-77: 建议抽取共享的选项数据

建议将重复的选项数据抽取到共享的配置文件中。

+// cascaderOptions.ts
+export const defaultOptions = [
+  {
+    value: '浙江',
+    text: '浙江',
+    children: [...]
+  },
+  // ...
+]

// demo7.tsx
+import { defaultOptions } from './cascaderOptions'
useEffect(() => {
  setTimeout(() => {
-    setOptions([...])
+    setOptions(defaultOptions)
  }, 300)
}, [])
src/packages/cascader/demos/taro/demo6.tsx (2)

22-88: 建议将超时时间提取为常量

建议将 useEffect 中的超时时间 300 提取为一个有意义的常量,以提高代码的可维护性。

+ const LOADING_DELAY = 300
  useEffect(() => {
    setTimeout(() => {
      setOptions([
        // ...options
-     ], 300)
+     ], LOADING_DELAY)
  }, [])

89-91: 建议添加类型注解

onChange 处理函数的参数类型应该更明确。

- const onChange = (value: any, path: any) => {
+ const onChange = (value: string[], path: CascaderOption[]) => {
src/packages/cascader/doc.zh-TW.md (1)

45-46: 建议改进繁体中文表述

建议将"透過"改为"通過",使表述更符合繁体中文的使用习惯。

- Cascader 內部透過 `value` 和 `onLoad` 實作了自動載入資料的邏輯
+ Cascader 內部通過 `value` 和 `onLoad` 實作了自動載入資料的邏輯
🧰 Tools
🪛 LanguageTool

[uncategorized] ~45-~45: 您的意思是“"不"透”?
Context: ...## 動態加載 lazy 屬性表示開啟資料的自動加載,Cascader 內部透過 valueonLoad 實作了自動載入資料的邏輯。 `laz...

(BU)

src/packages/cascader/doc.en-US.md (1)

23-27: 新增 Uncontrolled 示例说明
新增的“Basic Usage - Uncontroled”部分展示了组件在非受控模式下的用法,建议修正标题中的拼写错误(“Uncontroled” → “Uncontrolled”),以保证英文表达的准确性。

提供修改建议:

-### Basic Usage - Uncontroled
+### Basic Usage - Uncontrolled
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a5014bf and 201e966.

⛔ Files ignored due to path filters (1)
  • src/packages/cascader/__tests__/__snapshots__/cascader.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (26)
  • src/packages/cascader/__tests__/cascader.spec.tsx (4 hunks)
  • src/packages/cascader/cascader.taro.tsx (1 hunks)
  • src/packages/cascader/cascader.tsx (1 hunks)
  • src/packages/cascader/demo.taro.tsx (1 hunks)
  • src/packages/cascader/demo.tsx (3 hunks)
  • src/packages/cascader/demos/h5/demo1.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo2.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo3.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo4.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo5.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo6.tsx (3 hunks)
  • src/packages/cascader/demos/h5/demo7.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo1.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo2.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo3.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo4.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo5.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo6.tsx (3 hunks)
  • src/packages/cascader/demos/taro/demo7.tsx (1 hunks)
  • src/packages/cascader/doc.en-US.md (4 hunks)
  • src/packages/cascader/doc.md (4 hunks)
  • src/packages/cascader/doc.taro.md (4 hunks)
  • src/packages/cascader/doc.zh-TW.md (4 hunks)
  • src/packages/cascader/types.ts (2 hunks)
  • src/packages/cascader/utils.ts (1 hunks)
  • src/utils/is-empty.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: CI
src/packages/cascader/__tests__/cascader.spec.tsx

[error] 143-143: Snapshot Cascader > visible true 1 mismatched

🪛 LanguageTool
src/packages/cascader/doc.zh-TW.md

[uncategorized] ~45-~45: 您的意思是“"不"透”?
Context: ...## 動態加載 lazy 屬性表示開啟資料的自動加載,Cascader 內部透過 valueonLoad 實作了自動載入資料的邏輯。 `laz...

(BU)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (34)
src/packages/cascader/demos/h5/demo6.tsx (3)

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

导入了必要的 React hooks 和类型定义,结构清晰。


24-86: 注意选项结构中的潜在用户体验问题

  1. 在不同省份中存在重复的城市名称(如"温州"),这可能会导致用户混淆。
  2. 部分选项被禁用(disabled)但缺少视觉提示,可能影响用户理解。

建议:

  1. 为重复的地名添加更多上下文信息
  2. 为禁用选项添加提示信息

96-122: 渲染结构组织合理!

组件结构清晰,主题配置得当,props 传递正确。

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

1-7: 代码结构优化得当!

状态变量的命名更加规范,类型注解的添加提高了代码的类型安全性。


79-106: 渲染逻辑实现合理!

组件属性与状态管理保持一致,接口实现完整。

src/packages/cascader/utils.ts (2)

1-2: 导入类型定义保持整体可读性良好。
这些类型定义能提高可维护性,但请确保与其他文件的类型引用保持一致,避免重复或冲突。


24-60: 基于父子关系构建层级时,应关注顶层 ID 未匹配的情况。
当前仅返回 map[topId],若 topId 在来源数据中不存在,结果将是 undefined。若业务方需要在此场景下返回空数组,请在返回结果前加一次非空判断或兜底处理。

src/packages/cascader/demos/h5/demo1.tsx (2)

1-2: 引入方式和依赖组件在示例中正常。
导入 useStateuseEffect 并结合 CascaderCell 的使用无明显问题。


100-103: 使用 onPathChange 打印路径时可作为调试信息。
如生产环境注重性能与安全,可移除或限制日志输出,避免跟踪信息过多。

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

32-43: CascaderPopupProps 补充 Popup 属性的做法可提高可扩展性。
请确保与 PopupProps 的字段保持同步更新,避免后续出现功能不一致的风险。

src/packages/cascader/cascader.taro.tsx (5)

16-29: 引入本地工具函数与合并 Props 的逻辑值得肯定。
使用 normalizeListOptionsnormalizeOptionsmergeProps 等方法,能提高代码可读性与可重用性。


32-54: 新增的 CascaderPopupPropsCascaderSupportPopupProps 类型定义层次清晰。
通过 PickOmit 组合出弹窗相关的部分属性,结构更灵活,可读性更强。


94-118: 初始化合并 Props 与基础状态管理逻辑清晰,可读性较好。

  • forwardRef 便于父级直接调用开放的 CascaderActions
  • 使用自定义 Hook (useRefState) 与 getRefValue 管理 options,思路明确。

120-140: 基于 usePropsValue 管理受控与非受控的混合状态,提升灵活度。

  • 请确认注释掉的 onPathChange 调用是否为刻意保留,若不再使用可移除,避免他人误解。
  • isEmpty 判断与 normalizeListOptionsnormalizeOptions 配合,确保容错性。

144-169: 使用 useMemo 动态计算级联层级的逻辑值得肯定,但需留意 end 的计算分支。
若仅依赖 opt?.children 判断是否结束,部分情况下(如空数组、缺失字段)可能导致逻辑分支遗漏。建议完善数据校验,确保下游使用安全。

src/packages/cascader/demos/taro/demo5.tsx (2)

14-27: 建议优化初始化逻辑

与 H5 版本相同,建议改进初始化逻辑:

  1. 提取延迟时间常量
  2. 添加加载状态
  3. 添加错误处理

29-31: 改进 onChange 的类型安全性

与 H5 版本保持一致,建议改进类型定义。

src/packages/cascader/demo.tsx (3)

2-2: 导入路径优化和新增演示组件

导入路径的修改和新增 Demo7 组件的集成都很合理。

Also applies to: 9-9


15-15: 翻译键命名优化

翻译键的重命名和新增都保持了良好的一致性,使代码更易维护。

Also applies to: 20-20, 29-29, 33-33, 38-38


47-48: 新增非受控模式演示

新增的非受控模式演示补充了组件的使用场景。

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

47-62:

Details

❓ Verification inconclusive

请验证 HTML 元素在 Taro 环境中的兼容性

将 Taro 组件替换为 HTML 元素(div, h2)可能会影响跨平台兼容性。建议验证在各端的表现。


🏁 Script executed:

#!/bin/bash
# 搜索项目中 Taro 组件的使用模式
rg -A 2 "ScrollView|View" --type tsx

Length of output: 66


检查 Taro 环境下 HTML 元素兼容性

建议在非 WEB 平台(例如小程序)上验证 demo.taro.tsx 中直接使用 HTML 标签(div, h2)的效果,确认其兼容性。当前代码将 Taro 提供的组件替换为普通 HTML 元素,可能导致部分平台显示或功能异常。请参考 Taro 官方文档,确保在所有目标平台上均无问题。如有兼容性问题,建议将相关 HTML 标签替换为 Taro 组件(例如 View、Text 等)。

  • 文件:src/packages/cascader/demo.taro.tsx(第47-62行)
  • 检查点:各目标平台(如微信小程序、支付宝小程序等)的元素渲染与交互表现
src/packages/cascader/demos/h5/demo4.tsx (1)

5-7: 状态管理优化

状态变量的命名更加统一和语义化,使用 useState 和类型注解的方式也更加规范。

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

8-11: 移除生产环境的调试代码

与 H5 版本存在相同的问题,建议移除或控制调试输出。

Also applies to: 102-104


15-77: 抽取共享的选项数据

与 H5 版本存在相同的问题,建议将选项数据抽取到共享配置中。

src/packages/cascader/__tests__/cascader.spec.tsx (1)

215-221: 异步测试实现优化

异步加载测试的实现已优化,使用 Promise 和 setTimeout 提供了更清晰的异步行为。

src/packages/cascader/doc.md (2)

23-31: 文档结构清晰完整

新增的非受控用法部分,有助于用户更好地理解组件的不同使用方式。


54-58: 文档说明准确且实用

关于部分数据动态加载的说明非常清晰,特别是强调了无需设置 lazy 属性这一点,可以避免用户混淆。

src/packages/cascader/doc.taro.md (4)

23-27: 新增非受控示例说明
该部分为文档新增了“基础用法-非受控”示例,展示在非受控模式下如何通过传入 options 列表使用 Cascader。示例结构清晰、易于理解。


45-46: 更新动态加载说明
文档中更新了 lazy 属性的描述,明确指出该属性必须与 onLoad 同时设置,并且声明了 onLoad 方法的返回类型为 CascaderOption[]。这有助于开发者正确使用自动加载逻辑。


56-57: 新增部分数据动态加载说明
此处新增说明针对已设置初始 options 的场景,详细描述了如何通过 onLoad 方法实现部分数据的动态加载,并特别注明无需设置 lazy 属性,内容表达清晰。


105-107: 更新事件回调签名说明
Props 表中对 onLoadonChange 以及 onPathChange 的签名进行了更新,确保文档描述与组件实现保持一致,同时增强了类型安全性。

src/packages/cascader/doc.en-US.md (3)

45-46: 更新动态加载说明
文档中对 lazy 属性与 onLoad 回调的描述进行了更新,明确说明二者需要同时设置,并指出了 onLoad 方法返回数据类型为 CascaderOption[]。该说明有助于用户正确理解自动加载的逻辑。


56-57: 更新部分数据动态加载说明
这一部分详细解释了在设置初始 options 后如何通过 onLoad 方法加载数据,并明确告知用户在此场景下无需设置 lazy 属性,描述清晰明了。


105-107: 更新事件回调签名说明
Props 表中对 onLoadonChangeonPathChange 的签名已更新,确保文档和组件实现之间的一致性,增强了类型安全性,文档说明完整。

Comment on lines 93 to 264
export const Cascader = forwardRef((props: Partial<CascaderProps>, ref) => {
const classPrefix = 'nut-cascader'
const classPane = `${classPrefix}-pane`
const {
className,
style,
activeColor,
activeIcon,
popup,
popupProps = {},
visible,
options,
value,
defaultValue,
visible: outerVisible,
options: outerOptions,
value: outerValue,
defaultValue: outerDefaultValue,
optionKey,
format,
closeable,
closeIconPosition,
closeIcon,
lazy,
title,
left,
onLoad,
onClose,
onChange,
onPathChange,
} = { ...defaultProps, ...props }

const [tabvalue, setTabvalue] = useState('c1')
const [optionsData, setOptionsData] = useState<CascaderPane[]>([])
const isLazy = () => state.configs.lazy && Boolean(state.configs.onLoad)
} = mergeProps(defaultProps, props)
const { locale } = useConfig()

const [innerValue, setInnerValue] = usePropsValue<CascaderValue>({
value,
defaultValue,
finalValue: defaultValue,
const [tabActiveIndex, setTabActiveIndex] = useState(0)
const [optionsRef, setInnerOptions] = useRefState(outerOptions)
const innerOptions = getRefValue(optionsRef)
const [loading, setLoading] = useState<{ [key: string]: any }>({})

const [value, setValue] = usePropsValue({
value: outerValue,
defaultValue: outerDefaultValue,
finalValue: [],
onChange: (value) => {
props.onChange?.(value, pathNodes.current)
// props.onPathChange?.(value, pathNodes.current)
},
})
const [innerVisible, setInnerVisible] = usePropsValue<boolean>({
value: visible,

const [innerValue, setInnerValue] = useState(value)

const options = useMemo(() => {
if (!isEmpty(format)) {
return normalizeListOptions(innerOptions, format)
}
if (!isEmpty(optionKey)) {
return normalizeOptions(innerOptions, optionKey)
}
return innerOptions
}, [innerOptions, optionKey, format, innerValue])

const pathNodes = useRef<CascaderOption[]>([])

const levels: any[] = useMemo(() => {
const next = []
let end = false
let currentOptions = options
for (const [index, val] of innerValue.entries()) {
const opt = currentOptions?.find((o: CascaderOption) => o.value === val)
next.push({
selected: val,
pane: currentOptions,
})
pathNodes.current[index] = opt
if (opt?.children) {
currentOptions = opt.children
} else {
end = true
break
}
}
if (!end) {
next.push({
selected: null,
pane: currentOptions,
})
}
return next
}, [innerValue, options, innerOptions])

const [visible, setVisible] = usePropsValue({
value: outerVisible,
defaultValue: undefined,
finalValue: false,
onChange: (value) => {
if (value === false) {
props.onClose?.()
}
},
})
const actions: CascaderActions = {
open: () => {
setInnerVisible(true)
setVisible(true)
},
close: () => {
setInnerVisible(false)
setVisible(false)
},
}
useImperativeHandle(ref, () => actions)

const [state] = useState({
optionsData: [] as any,
panes: [
{
nodes: [] as any,
selectedNode: [] as CascaderOption | null,
paneKey: '',
},
],
tree: new Tree([], {}),
tabsCursor: 0, // 选中的tab项
initLoading: false,
currentProcessNode: [] as CascaderOption | null,
configs: {
lazy,
onLoad,
optionKey,
format,
},
lazyLoadMap: new Map(),
})

const classPrefix = classNames(`nut-cascader`)
const classesPane = classNames({
[`${classPrefix}-pane`]: true,
})

useEffect(() => {
initData()
}, [options, format])

useEffect(() => {
syncValue()
}, [value])

const initData = async () => {
// 初始化开始处理数据
state.lazyLoadMap.clear()
if (format && Object.keys(format).length > 0) {
state.optionsData = convertListToOptions(
options as CascaderOption[],
format as CascaderFormat
)
} else {
state.optionsData = options
if (!visible) {
setInnerValue(value)
}
state.tree = new Tree(state.optionsData as CascaderOption[], {
value: state.configs.optionKey.valueKey,
text: state.configs.optionKey.textKey,
children: state.configs.optionKey.childrenKey,
})
if (isLazy() && !state.tree.nodes.length) {
await invokeLazyLoad({
root: true,
loading: true,
text: '',
value: '',
})
}
state.panes = [
{
nodes: state.tree.nodes,
selectedNode: null,
paneKey: 'c1',
},
]
syncValue()
setOptionsData(state.panes)
}
// 处理有默认值时的数据
const syncValue = async () => {
const currentValue = innerValue
}, [visible, value])

if (
currentValue === undefined ||
![defaultValue, value].includes(currentValue) ||
!state.tree.nodes.length
) {
return
}
useEffect(() => {
setInnerOptions(outerOptions)
}, [outerOptions])

if (currentValue.length === 0) {
state.tabsCursor = 0
return
useEffect(() => {
setTabActiveIndex(levels.length - 1)
}, [innerValue, innerOptions, outerOptions])
useEffect(() => {
const max = levels.length - 1
if (tabActiveIndex > max) {
setTabActiveIndex(max)
}

let needToSync = currentValue

if (isLazy() && Array.isArray(currentValue) && currentValue.length) {
needToSync = []
const parent: any = state.tree.nodes.find(
(node) => node.value === currentValue[0]
)

if (parent) {
needToSync = [parent.value]
state.initLoading = true

const last = await currentValue
.slice(1)
.reduce(async (p: Promise<CascaderOption | void>, value) => {
const parent = await p
await invokeLazyLoad(parent)
const node: any = parent?.children?.find(
(item: any) => item.value === value
)
if (node) {
needToSync.push(value)
}
}, [tabActiveIndex, levels, innerOptions, outerOptions])
useEffect(() => {
const load = async () => {
const parent = { children: [] }
try {
await innerValue.reduce(async (promise: Promise<any>, val, key) => {
const pane = await onLoad({ value: val }, key)
const parent = await promise
parent.children = pane
if (key === innerValue.length - 1) {
return Promise.resolve(parent)
}
if (pane) {
const node = pane.find((p) => p.value === val)
return Promise.resolve(node)
}, Promise.resolve(parent))
await invokeLazyLoad(last)
state.initLoading = false
}
}

if (needToSync.length && [defaultValue, value].includes(currentValue)) {
const pathNodes = state.tree.getPathNodesByValue(needToSync)
pathNodes.forEach((node, index) => {
state.tabsCursor = index
// 当有默认值时,不触发 chooseItem 里的 emit 事件
chooseItem(node, true)
})
}
}

const invokeLazyLoad = async (node?: CascaderOption | void) => {
if (!node) {
return
}

if (!state.configs.onLoad) {
node.leaf = true
return
}

if (
state.tree.isLeaf(node, isLazy()) ||
state.tree.hasChildren(node, isLazy())
) {
return
}

node.loading = true
}
}, Promise.resolve(parent))

const parent = node.root ? null : node
let lazyLoadPromise = state.lazyLoadMap.get(node)

if (!lazyLoadPromise) {
lazyLoadPromise = new Promise((resolve) => {
// 外部必须resolve
state.configs.onLoad?.(node, resolve)
})
state.lazyLoadMap.set(node, lazyLoadPromise)
}

const nodes: CascaderOption[] | void = await lazyLoadPromise

if (Array.isArray(nodes) && nodes.length > 0) {
state.tree.updateChildren(nodes, parent)
} else {
// 如果加载完成后没有提供子节点,作为叶子节点处理
node.leaf = true
}
node.loading = false
state.lazyLoadMap.delete(node)
}

const close = () => {
setInnerVisible(false)
onClose && onClose()
}

const closePopup = () => {
close()
}

/* type: 是否是静默模式,是的话不触发事件
tabsCursor: tab的索引 */
const chooseItem = async (node: CascaderOption, type: boolean) => {
if ((!type && node.disabled) || !state.panes[state.tabsCursor]) {
return
}
// 如果没有子节点
if (state.tree.isLeaf(node, isLazy())) {
node.leaf = true
state.panes[state.tabsCursor].selectedNode = node
state.panes = state.panes.slice(0, (node.level as number) + 1)
if (!type) {
const pathNodes = state.panes.map((item) => item.selectedNode)
const optionParams = pathNodes.map((item: any) => item.value)
onChange(optionParams, pathNodes)
onPathChange?.(optionParams, pathNodes)
setInnerValue(optionParams)
// 如果需要处理最终结果,可以在这里使用 last
setInnerOptions(parent.children)
} catch (error) {
console.error('Error loading data:', error)
}
setOptionsData(state.panes)
close()
return
}
// 如果有子节点,滑到下一个
if (state.tree.hasChildren(node, isLazy())) {
const level = (node.level as number) + 1

state.panes[state.tabsCursor].selectedNode = node
state.panes = state.panes.slice(0, level)
state.tabsCursor = level
state.panes.push({
nodes: node.children || [],
selectedNode: null,
paneKey: `c${state.tabsCursor + 1}`,
})
setOptionsData(state.panes)
setTabvalue(`c${state.tabsCursor + 1}`)

if (!type) {
const pathNodes = state.panes.map((item) => item.selectedNode)
const optionParams = pathNodes.map((item: any) => item?.value)
onPathChange?.(optionParams, pathNodes)
if (lazy) load()
}, [lazy])

const chooseItem = async (pane: CascaderOption, levelIndex: number) => {
if (pane.disabled) return
const nextValue = innerValue.slice(0, levelIndex)
const nextPathNodes = pathNodes.current.slice(0, levelIndex)
if (pane.value) {
setLoading(!!onLoad && { [levelIndex]: pane.value })
nextValue[levelIndex] = pane.value
nextPathNodes[levelIndex] = pane
pathNodes.current = nextPathNodes
props?.onPathChange?.(nextValue, pathNodes.current)
}
if (onLoad) {
// 叶子节点不操作
if (!pane.leaf) {
const asyncOptions = await onLoad(pane, levelIndex)
// 修改 options 触发渲染逻辑
if (asyncOptions) pane.children = asyncOptions
} else {
setVisible(false)
setValue(nextValue)
}
return
}
state.currentProcessNode = node
if (node.loading) {
return
if (!pane.children && !onLoad) {
setVisible(false)
setValue(nextValue)
}

await invokeLazyLoad(node)
if (state.currentProcessNode === node) {
state.panes[state.tabsCursor].selectedNode = node
chooseItem(node, type)
}
setOptionsData(state.panes)
setInnerValue(nextValue)
setLoading({})
}
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. usePropsValueuseRefState 配合可减少不必要的 re-render,但需明确每次数据更新路径,以免覆盖外部传入的 props。
  2. onLoad 处理的异步结果,如出现异常时,目前仅简单地打印日志,建议提供更明确的错误回调或 UI 提示信息。
  3. 当懒加载时,如后续层级数据量较大,onLoad 频繁调用可能带来性能开销,可考虑加缓存策略或分页处理。

@codecov
Copy link

codecov bot commented Feb 20, 2025

Codecov Report

Attention: Patch coverage is 91.91489% with 19 lines in your changes missing coverage. Please review.

Project coverage is 86.14%. Comparing base (fc2e7a5) to head (ce2dcda).
Report is 8 commits behind head on feat_v3.x.

Files with missing lines Patch % Lines
src/packages/cascader/cascader.tsx 89.72% 19 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           feat_v3.x    #3007      +/-   ##
=============================================
+ Coverage      86.10%   86.14%   +0.03%     
=============================================
  Files            277      278       +1     
  Lines          18408    18198     -210     
  Branches        2807     2771      -36     
=============================================
- Hits           15851    15677     -174     
+ Misses          2552     2516      -36     
  Partials           5        5              

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

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

Copy link

@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 (4)
src/packages/address/customRender.tsx (1)

14-15: 建议为 value 属性提供默认值

接口中将 valuedefaultValue 设置为必需属性是一个好的改进,但建议在 defaultProps 中也为 value 提供默认值,以确保组件在未传入该属性时的行为一致性。

 const defaultProps = {
   ...ComponentDefaults,
+  value: [],
   visible: false,
   type: 'custom',
   options: [],
src/packages/address/customRender.taro.tsx (1)

14-15: 建议为 value 属性提供默认值

接口中将 valuedefaultValue 设置为必需属性是一个好的改进,但建议在 defaultProps 中也为 value 提供默认值,以确保组件在未传入该属性时的行为一致性。

 const defaultProps = {
   ...ComponentDefaults,
+  value: [],
   visible: false,
   type: 'custom',
   options: [],
src/packages/address/address.tsx (1)

30-31: 建议为 value 属性提供默认值

接口中将 valuedefaultValue 设置为必需属性是一个好的改进,但建议在 defaultProps 中也为 value 提供默认值,以确保组件在未传入该属性时的行为一致性。

 const defaultProps = {
   ...ComponentDefaults,
+  value: [],
   defaultValue: [],
   type: 'custom',
   options: [],
src/packages/address/address.taro.tsx (1)

31-32: 建议为 value 属性提供默认值

接口中将 valuedefaultValue 设置为必需属性是一个好的改进,但建议在 defaultProps 中也为 value 提供默认值,以确保组件在未传入该属性时的行为一致性。

 const defaultProps = {
   ...ComponentDefaults,
+  value: [],
   defaultValue: [],
   type: 'custom',
   options: [],
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 201e966 and c59b8be.

⛔ Files ignored due to path filters (2)
  • src/packages/address/__test__/__snapshots__/address.spec.tsx.snap is excluded by !**/*.snap
  • src/packages/cascader/__tests__/__snapshots__/cascader.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • src/packages/address/address.taro.tsx (1 hunks)
  • src/packages/address/address.tsx (1 hunks)
  • src/packages/address/customRender.taro.tsx (1 hunks)
  • src/packages/address/customRender.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (2)
src/packages/address/address.tsx (1)

154-170: 验证 CustomRender 组件的 value 属性传递

注意到 CustomRender 组件没有传递 value 属性,这可能会导致类型错误或未定义的行为。建议检查并确保正确传递该属性。

 <CustomRender
   visible={innerVisible}
   closeable
   title={title || locale.address.selectRegion}
   left={renderLeftOnCustomSwitch()}
+  value={value}
   defaultValue={defaultValue}
   closeIcon={closeIcon}
   options={options}
src/packages/address/address.taro.tsx (1)

156-172: 验证 CustomRender 组件的 value 属性传递

注意到 CustomRender 组件没有传递 value 属性,这可能会导致类型错误或未定义的行为。建议检查并确保正确传递该属性。

 <CustomRender
   visible={innerVisible}
   closeable
   title={title || locale.address.selectRegion}
   left={renderLeftOnCustomSwitch()}
+  value={value}
   defaultValue={defaultValue}
   closeIcon={closeIcon}
   options={options}

# Conflicts:
#	src/packages/overlay/overlay.taro.tsx
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 (6)
src/packages/cascader/demos/taro/demo5.tsx (2)

14-27: 异步数据初始化实现

使用useEffect实现异步数据初始化是一个良好的实践。延时设置初始值避免了组件渲染过程中的闪烁。数据结构中添加了sortKey字段增强了选项的排序能力。

不过,可以考虑从以下几个方面进一步优化:

  • 提取延时加载的逻辑到一个自定义Hook中,以便在其他组件中复用
  • 为异步操作添加加载状态指示器
  • 考虑添加错误处理机制

29-31: 简化的事件处理函数

onChange函数实现更加简洁,直接更新状态而不进行复杂处理,符合React的数据流理念。函数参数path虽然接收了但未使用,建议移除未使用的参数或添加注释说明保留原因。

-const onChange = (value: any, path: any) => {
+const onChange = (value: any) => {
  setValue(value)
}
src/packages/cascader/cascader.taro.tsx (4)

77-93: 默认属性优化

默认属性设置更加清晰,使用空对象{}初始化optionKey而不是undefined,避免了潜在的空值检查问题。

不过,注意到类型断言as unknown as CascaderProps可能会掩盖类型错误。考虑使用部分类型Partial<CascaderProps>或完善默认值以匹配接口定义。

-const defaultProps: CascaderProps = {
+const defaultProps = {
  ...ComponentDefaults,
  activeColor: '',
  activeIcon: 'checklist',
  popup: true,
  options: [],
  optionKey: {},
  format: {},
  closeable: false,
  closeIconPosition: 'top-right',
  closeIcon: 'close',
  lazy: false,
  onClose: () => {},
  onChange: () => {},
  onPathChange: () => {},
-} as unknown as CascaderProps
+}

121-129: 值管理与回调处理

使用usePropsValue统一管理内部状态和外部传入的值,实现了受控和非受控模式的兼容。注意到有一个被注释掉的onPathChange调用,建议移除注释代码或添加说明其保留原因的注释。

  const [value, setValue] = usePropsValue({
    value: outerValue,
    defaultValue: outerDefaultValue,
    finalValue: [],
    onChange: (value) => {
      props.onChange?.(value, pathNodes.current)
-      // props.onPathChange?.(value, pathNodes.current)
    },
  })

145-170: 层级计算逻辑优化

使用useMemo缓存层级计算结果,提高了性能。算法实现清晰:

  • 遍历当前值数组构建层级
  • 维护pathNodes引用存储路径节点
  • 处理边界情况(如未结束的路径)

不过,这个函数有些复杂,可以考虑提取子函数或添加更多注释来提高可读性。


210-235: 异步数据加载逻辑

将异步加载逻辑重构为使用async/await和Promise,代码更加现代化和可读。使用try/catch处理异步错误,增强了代码的健壮性。

注意到这里有一个中文注释"如果需要处理最终结果...",建议保持注释语言一致性,或者在国际化版本中使用英文注释。

-        // 如果需要处理最终结果,可以在这里使用 last
+        // If there's a need to process the final result, it can be done here
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between df3c570 and d4fc3a6.

📒 Files selected for processing (16)
  • src/config.json (1 hunks)
  • src/packages/cascader/cascader.taro.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo1.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo2.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo3.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo4.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo5.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo6.tsx (3 hunks)
  • src/packages/cascader/demos/h5/demo7.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo1.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo2.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo3.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo4.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo5.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo6.tsx (3 hunks)
  • src/packages/cascader/demos/taro/demo7.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/config.json
  • src/packages/cascader/demos/taro/demo2.tsx
  • src/packages/cascader/demos/taro/demo6.tsx
  • src/packages/cascader/demos/h5/demo5.tsx
  • src/packages/cascader/demos/h5/demo7.tsx
  • src/packages/cascader/demos/taro/demo7.tsx
  • src/packages/cascader/demos/h5/demo6.tsx
  • src/packages/cascader/demos/taro/demo4.tsx
  • src/packages/cascader/demos/h5/demo2.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (41)
src/packages/cascader/demos/taro/demo3.tsx (6)

2-2: 导入了更明确的类型定义

将 CascaderOption 类型从 @nutui/nutui-react-taro 包中导入,提高了代码类型安全性。


5-6: 状态变量命名优化

将特定的状态变量名(如 isVisibleDemo3 和 value3)重命名为更通用的名称(visible 和 value),提高了代码的可读性和可维护性。同时更新了初始值。


8-24: 异步加载优化

将回调式的 lazyLoadDemo3 函数重构为返回 Promise 的 loadCascaderItemData 函数,符合现代 JavaScript 异步编程实践。同时:

  • 添加了明确的类型定义
  • 减少了数据加载的模拟延时(从 2000ms 减少到 500ms)
  • 将叶节点条件从 level >= 6 修改为 level >= 2,简化了演示层级

25-27: 事件处理命名优化

将 change3 函数重命名为 onChange,使命名更符合 React 组件事件处理惯例,提高代码可读性。


33-36: 使用更新后的状态变量

更新了 Cell 组件的 description 属性和 onClick 处理函数以使用新的状态变量名。


39-48: Cascader 组件属性更新

更新了 Cascader 组件的属性以使用新的状态变量和函数名,使代码更加一致。特别是:

  • visible 状态控制
  • defaultValue 的使用
  • onChange 事件处理
  • onLoad 异步加载函数
src/packages/cascader/demos/h5/demo3.tsx (6)

2-2: 导入了更明确的类型定义

将 CascaderOption 类型从 @nutui/nutui-react 包中导入,提高了代码类型安全性。


5-6: 状态变量命名优化

将特定的状态变量名(如 isVisibleDemo3 和 value3)重命名为更通用的名称(visible 和 value),提高了代码的可读性和可维护性。同时更新了初始值。


8-24: 异步加载优化

将回调式的 lazyLoadDemo3 函数重构为返回 Promise 的 loadCascaderItemData 函数,符合现代 JavaScript 异步编程实践。同时:

  • 添加了明确的类型定义
  • 减少了数据加载的模拟延时(从 2000ms 减少到 500ms)
  • 将叶节点条件从 level >= 6 修改为 level >= 2,简化了演示层级

25-27: 事件处理命名优化

将 change3 函数重命名为 onChange,使命名更符合 React 组件事件处理惯例,提高代码可读性。


33-36: 使用更新后的状态变量

更新了 Cell 组件的 description 属性和 onClick 处理函数以使用新的状态变量名。


39-48: Cascader 组件属性更新

更新了 Cascader 组件的属性以使用新的状态变量和函数名,使代码更加一致。特别是:

  • visible 状态控制
  • defaultValue 的使用
  • onChange 事件处理
  • onLoad 异步加载函数
src/packages/cascader/demos/h5/demo1.tsx (6)

1-2: 导入优化

添加了 useEffect hook 导入和 CascaderOption 类型导入,支持新的状态管理和类型安全。


5-7: 状态管理优化

将特定的状态变量名重命名为更通用的名称,并使用 useState 初始化 options 为空数组,采用了更现代的状态管理方式。


8-11: 标准化的事件处理

添加了 onChange 处理函数,记录变更并更新状态,符合 React 组件事件处理惯例。


12-78: 数据加载优化

使用 useEffect 和 setTimeout 实现异步加载选项数据,更符合 React 组件的最佳实践。同时,重构了选项数据结构,提供了更丰富的层级和选项,更好地展示级联选择器的功能。


83-86: 使用更新后的状态变量

更新了 Cell 组件的 description 属性和 onClick 处理函数以使用新的状态变量名。


92-103: Cascader 组件属性更新

更新了 Cascader 组件的属性以使用新的状态变量和函数名,并添加了 onPathChange 事件处理,增强了组件的交互功能。

src/packages/cascader/demos/taro/demo1.tsx (6)

1-2: 导入优化

添加了 useEffect hook 导入和 CascaderOption 类型导入,支持新的状态管理和类型安全。


5-7: 状态管理优化

将特定的状态变量名重命名为更通用的名称,并使用 useState 初始化 options 为空数组,采用了更现代的状态管理方式。


8-10: 标准化的事件处理

添加了 onChange 处理函数,更新状态,符合 React 组件事件处理惯例。


11-77: 数据加载优化

使用 useEffect 和 setTimeout 实现异步加载选项数据,更符合 React 组件的最佳实践。同时,重构了选项数据结构,提供了更丰富的层级和选项,更好地展示级联选择器的功能。


82-85: 使用更新后的状态变量

更新了 Cell 组件的 description 属性和 onClick 处理函数以使用新的状态变量名。


91-99: Cascader 组件属性更新

更新了 Cascader 组件的属性以使用新的状态变量和函数名,使代码更加一致。

src/packages/cascader/demos/taro/demo5.tsx (4)

5-7: 状态管理优化

状态变量命名更加简洁明了,从特定的命名(如isVisibleDemo5)改为通用的visiblevalueoptions,提高了代码的可读性和可维护性。使用更明确的类型定义string[]CascaderOption[]有助于类型检查。


37-38: 描述文本的条件渲染优化

使用三元运算符实现条件渲染,代码简洁易读。值得注意的是,使用value.join(',')前先检查了value.length,避免了空数组处理可能引发的问题。


43-45: 属性对齐

visiblevalue属性直接使用对应的状态变量,保持了prop和state的一致性,符合React的数据流向。


46-53: 事件处理和属性设置

使用新的状态变量名称更新了所有相关属性和回调函数,保持了一致性。onChange回调使用了新定义的函数,简化了代码结构。

src/packages/cascader/demos/h5/demo4.tsx (6)

5-7: 状态管理重构

状态变量命名从特定(如isVisibleDemo4)改为通用的visiblevalueoptions,提高了代码复用性和可维护性。为options添加了明确的类型定义CascaderOption[],有助于开发过程中的类型检查。


8-23: 异步数据加载实现

使用useEffectsetTimeout模拟异步数据加载,符合React的最佳实践。初始化options为空数组并在useEffect中设置值,这种方式使组件状态变化更加可预测。


25-41: Promise化的懒加载函数

将回调风格的lazyLoadDemo4函数重构为返回Promise的异步函数,使代码更加现代化和可读。参数命名从noderesolve改为更具描述性的nodelevel,增强了函数接口的自描述性。

这种基于Promise的实现更符合现代JavaScript的异步处理模式,便于与其他异步操作(如API调用)集成。


50-51: 优化的UI渲染逻辑

使用三元运算符条件渲染描述文本,代码简洁且易于理解。检查value.length避免了对空数组调用join方法可能引发的问题。


64-66: 事件处理函数更新

保持了onChange使用change4函数,同时更新了onLoad使用新的Promise风格的lazyLoadDemo4函数。代码结构清晰,事件处理逻辑一致。


56-58:

Details

✅ Verification successful

属性更新

更新了Cascader组件的属性以匹配新的状态变量名称。使用visible代替特定名称的变量提高了代码一致性。注意到defaultValue使用了value状态,这可能需要检查是否符合预期行为。

请验证使用value作为defaultValue的属性值是否符合预期:


🏁 Script executed:

#!/bin/bash
# 检查Cascader组件的属性使用模式
rg -A 5 "defaultValue\s*=\s*\{.*value" --type=tsx

Length of output: 79


注意:请验证 defaultValue 的赋值逻辑

更新后的 Cascader 组件现在使用了 visible 替代原有状态变量,同时将 defaultValue 的值设置为 value 状态。请确保这种赋值方式符合业务预期,避免在组件初始化时出现不符合预期的行为。建议在 TSX 文件中重新搜索“defaultValue={...value”模式,以确认其他相关用法是否正确。使用上面的新命令执行验证。


---

🏁 Script executed:

```shell

#!/bin/bash
# 由于 tsx 文件类型未被识别,我们使用通配符进行搜索:
rg -g '*.tsx' -A 5 "defaultValue\s*=\s*\{.*value"

Length of output: 9784


属性更新验证通过

经过验证,多个 demo 文件(例如:

  • src/packages/cascader/demos/h5/demo4.tsx
  • src/packages/cascader/demos/taro/demo2.tsx
  • 以及其他相关 cascader demos)均使用了 defaultValue={value} 的写法,显示出当前修改的一致性。请确认在组件初始化时 value 状态的赋值符合业务预期,无需进一步调整。
src/packages/cascader/cascader.taro.tsx (7)

33-54: 类型定义优化

新增的CascaderPopupPropsCascaderSupportPopupProps类型定义提高了代码的可维护性和类型安全性。使用TypeScript的PickOmit工具类型精确控制属性的暴露,既提供了类型约束又保持了灵活性。

这种类型定义方式遵循了组件封装的最佳实践,使API设计更加清晰和一致。


56-75: 接口优化与功能扩展

CascaderProps接口通过继承CascaderPopupProps实现了更好的代码复用。方法签名更新增强了类型安全:

  • onLoad现在返回Promise,支持异步数据加载
  • onChangeonPathChange添加了pathNodes参数提供更多上下文信息
  • 新增的onTabsChange支持标签切换事件

这些变更使组件接口更加符合现代React组件的设计模式,提高了组件的可用性和灵活性。


116-120: 状态管理优化

使用useRefState管理options状态是一个很好的改进,解决了在异步上下文中访问最新状态的问题。loading状态使用对象结构存储,支持多级加载状态的独立控制。


133-141: 选项处理逻辑优化

使用useMemo优化选项处理逻辑,避免了不必要的重复计算。通过检查formatoptionKey的非空性来决定使用哪种标准化方法,逻辑清晰。

使用isEmpty工具函数替代直接的空检查,提高了代码的可读性和一致性。依赖数组包含了innerValue,确保在值变化时重新计算选项。


237-265: 选择项目逻辑优化

chooseItem函数改为异步函数,更好地支持异步数据加载。逻辑结构清晰:

  • 处理禁用状态
  • 计算新的值和路径节点
  • 调用onPathChange回调
  • 异步加载子选项
  • 处理叶子节点的特殊情况

代码组织良好,各个分支处理清晰。


267-302: 渲染逻辑优化

抽取renderCascaderItem函数使渲染逻辑更加模块化和可维护。加载状态指示器的实现简洁明了,通过loading对象控制显示状态。

使用条件渲染处理活动图标,支持自定义图标和默认图标。样式处理统一使用className和内联样式结合的方式。


328-346: 条件渲染优化

使用三元运算符实现基于popup属性的条件渲染,代码简洁清晰。当popup为true时,将内容包裹在Popup组件中,否则直接渲染内容。

传递所有必要的属性到Popup组件,包括扩展popupProps对象,保持了API的灵活性和可配置性。

Copy link
Collaborator

@xiaoyatong xiaoyatong left a comment

Choose a reason for hiding this comment

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

1、这里建议颜色一致;image
2、鸿蒙下 icon 缺失。

>
<div
class="nut-cascader "
class="nut-cascader undefined"
Copy link
Collaborator

Choose a reason for hiding this comment

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

这个为什么有个 undefined

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

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

210-235: 🛠️ Refactor suggestion

异步数据加载逻辑改进,但错误处理仍有优化空间。

与非 Taro 版本类似,使用 async/await 和 Promise 重构了数据加载逻辑,提高了代码可读性。但当前错误处理仅限于控制台日志记录,没有提供用户反馈机制。

建议增强错误处理,为用户提供加载失败的反馈,与非 Taro 版本保持一致实现。

🧹 Nitpick comments (3)
src/packages/cascader/cascader.tsx (2)

116-141: 状态管理逻辑优化,但可以考虑进一步改进。

使用 useRefStateuseMemo 避免了不必要的重渲染,优化了性能。当处理 formatoptionKey 时正确使用了 isEmpty 工具函数。

考虑将 innerValue 依赖从 options 的 useMemo 依赖数组中移除,因为它可能导致不必要的重新计算。options 的计算主要依赖于 innerOptionsoptionKeyformat,而与 innerValue 无直接关系。

  const options = useMemo(() => {
    if (!isEmpty(format)) {
      return normalizeListOptions(innerOptions, format)
    }
    if (!isEmpty(optionKey)) {
      return normalizeOptions(innerOptions, optionKey)
    }
    return innerOptions
-  }, [innerOptions, optionKey, format, innerValue])
+  }, [innerOptions, optionKey, format])

236-265: 选择项处理逻辑进行了适当的重构,但仍需更好的注释说明。

异步操作的处理更加清晰,数据流向更加明确。不过,代码缺乏足够的注释来解释各个分支的处理逻辑和数据流向。

建议添加更详细的注释,解释不同条件分支的处理逻辑:

  const chooseItem = async (pane: CascaderOption, levelIndex: number) => {
    if (pane.disabled) return
    const nextValue = innerValue.slice(0, levelIndex)
    const nextPathNodes = pathNodes.current.slice(0, levelIndex)
+   // 处理有效值选择
    if (pane.value) {
      setLoading(!!onLoad && { [levelIndex]: pane.value })
      nextValue[levelIndex] = pane.value
      nextPathNodes[levelIndex] = pane
      pathNodes.current = nextPathNodes
      props?.onPathChange?.(nextValue, pathNodes.current)
    }
+   // 处理异步加载
    if (onLoad) {
      // 叶子节点不操作
      if (!pane.leaf) {
+       // 加载当前节点的子节点
        const asyncOptions = await onLoad(pane, levelIndex)
        // 修改 options 触发渲染逻辑
        if (asyncOptions) pane.children = asyncOptions
      } else {
+       // 叶子节点选择,关闭弹窗并提交结果
        setVisible(false)
        setValue(nextValue)
      }
    }
+   // 处理非异步加载叶子节点的选择
    if (!pane.children && !onLoad) {
      setVisible(false)
      setValue(nextValue)
    }
    setInnerValue(nextValue)
    setLoading({})
  }
src/packages/cascader/cascader.taro.tsx (1)

298-298: 检查图标颜色硬编码,与非 Taro 版本不一致。

Taro 版本的 Check 组件硬编码了颜色值 "#ff0f23",而非 Taro 版本没有指定颜色,可能导致两个平台上的视觉表现不一致。

建议将颜色值统一处理,可以考虑以下方案:

- <Check className={`${classPrefix}-icon-check`} color="#ff0f23" />
+ <Check className={`${classPrefix}-icon-check`} />

或者在两个版本中都使用动态颜色值:

- <Check className={`${classPrefix}-icon-check`} color="#ff0f23" />
+ <Check className={`${classPrefix}-icon-check`} color={activeColor || "#ff0f23"} />
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d4fc3a6 and 65c3efe.

⛔ Files ignored due to path filters (1)
  • src/packages/cascader/__tests__/__snapshots__/cascader.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (2)
  • src/packages/cascader/cascader.taro.tsx (1 hunks)
  • src/packages/cascader/cascader.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (8)
src/packages/cascader/cascader.tsx (5)

32-53: 类型定义结构优化,提高了代码的可维护性。

通过创建 CascaderPopupPropsCascaderSupportPopupProps 类型,将弹出框相关的属性与级联选择器的核心属性清晰分离,使得类型结构更加合理。


67-74: 方法签名优化提高了类型安全性和代码可读性。

onLoad 改为返回 Promise 的实现更符合现代异步编程范式,同时 onChangeonPathChange 添加 pathNodes 参数提供了更多上下文信息,有助于外部更灵活地处理状态变化。新增的 onTabsChange 回调增强了组件的可定制性。


266-302: 抽取 renderCascaderItem 提高了代码的可读性和可维护性。

将级联项渲染逻辑提取为独立函数,代码结构更加清晰,可读性更好。


304-325: Tab 渲染逻辑优化和回调处理增强。

重命名为 renderTab 使命名更准确,同时添加了对 onTabsChange 的支持,增强了组件的扩展性。


327-345: 条件渲染逻辑简化,提高了代码的可读性。

使用三元运算符进行条件渲染,代码结构更加简洁明了。同时正确处理了弹窗的属性传递和关闭事件。

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

33-54: 类型定义结构优化,提高了代码的可维护性。

通过创建 CascaderPopupPropsCascaderSupportPopupProps 类型,将弹出框相关的属性与级联选择器的核心属性清晰分离,使得类型结构更加合理。


68-75: 方法签名优化提高了类型安全性和代码可读性。

onLoad 改为返回 Promise 的实现更符合现代异步编程范式,同时 onChangeonPathChange 添加 pathNodes 参数提供了更多上下文信息,有助于外部更灵活地处理状态变化。新增的 onTabsChange 回调增强了组件的可定制性。


312-312: Tabs 组件属性不一致问题。

Taro 版本的 Tabs 组件设置了 align="left" 属性,而非 Taro 版本没有设置此属性,可能导致两个平台上的布局表现不一致。

请确认是否有意为之,如果两个平台需要保持一致的表现,可以考虑以下修改:

- <Tabs
-   align="left"
-   value={tabActiveIndex}
+ <Tabs
+   value={tabActiveIndex}

或在非 Taro 版本也添加相同属性:

<Tabs
  align="left"
  value={tabActiveIndex}

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/cascader/cascader.taro.tsx (2)

210-235: 优化异步数据加载实现

使用 async/await 与 Promise 链改善了代码可读性和错误处理。不过,依赖数组只包含 [lazy],这可能导致 innerValue 变化时不会触发重新加载。

考虑将 innerValue 添加到依赖数组中,确保值变化时重新加载数据:

-  }, [lazy])
+  }, [lazy, innerValue])

267-306: 抽象组件渲染逻辑

将级联项渲染逻辑抽象为单独的 renderCascaderItem 函数提高了代码可维护性。加载指示器的集成也很好地提升了用户体验。

加载图标颜色值 "#969799" 是硬编码的,考虑使用主题变量或使其可配置,以提高定制性:

-            <Loading
-              color="#969799"
-              className="nut-cascader-item-icon-loading"
-            />
+            <Loading
+              color={props.loadingColor || "#969799"}
+              className="nut-cascader-item-icon-loading"
+            />
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 65c3efe and ce15f65.

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

33-43: 类型定义结构优化

通过从 PopupProps 中提取特定字段创建 CascaderPopupProps 类型,使代码结构更加清晰。这种方式比直接在组件属性中列出所有字段更易于维护。


67-74: 回调函数签名改进

回调函数签名的更新很好:

  • onLoad 现在返回 Promise,符合异步加载的最佳实践
  • onChangeonPathChange 增加了 pathNodes 参数提供更多上下文
  • 新增的 onTabsChange 回调增强了组件交互能力

这些变更提高了API的灵活性和可用性。


117-119: 状态管理优化

使用 useRefState 管理选项状态是一个很好的改进,可以避免闭包陷阱并确保始终访问最新的状态值。


133-141: 使用 useMemo 优化渲染性能

很好地使用了 useMemo 来优化选项的计算,避免了不必要的重新计算。依赖数组包含了所有相关依赖,确保选项在必要时才会重新计算。


242-242: 改进加载状态设置逻辑

使用 !!onLoad && { [levelIndex]: pane.value } 的方式设置加载状态很巧妙,确保只有在提供 onLoad 函数时才会显示加载状态。


334-352: 改进条件渲染

根据 popup 属性条件渲染 Popup 组件是一个很好的改进,提高了组件的灵活性和可用性。这使得组件可以在弹出模式和内联模式之间轻松切换。

# Conflicts:
#	src/packages/address/address.taro.tsx
#	src/packages/address/address.tsx
#	src/packages/cascader/cascader.taro.tsx
#	src/packages/cascader/cascader.tsx
#	src/packages/cascader/types.ts
#	src/packages/cascader/utils.ts
@oasis-cloud oasis-cloud requested a review from xiaoyatong March 11, 2025 06:02
@oasis-cloud
Copy link
Collaborator Author

1、这里建议颜色一致;image 2、鸿蒙下 icon 缺失。

已修复
image
鸿蒙采用 css 变量,等待 css 变量支持

@xiaoyatong xiaoyatong merged commit f74b3c1 into jdf2e:feat_v3.x Mar 12, 2025
8 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Apr 29, 2025
20 tasks
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.

3 participants