Skip to content

fix(wd col picker): 修复列选择器自动补全和数据同步问题#1412

Open
TAYUN wants to merge 2 commits intoMoonofweisheng:masterfrom
TAYUN:fix/wd-col-picker
Open

fix(wd col picker): 修复列选择器自动补全和数据同步问题#1412
TAYUN wants to merge 2 commits intoMoonofweisheng:masterfrom
TAYUN:fix/wd-col-picker

Conversation

@TAYUN
Copy link
Collaborator

@TAYUN TAYUN commented Dec 25, 2025

🤔 这个 PR 的性质是?(至少选择一个)

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

🔗 相关 Issue

#704
#357
#264

修复背景

当前分支修复了 col-picker 在处理多级异步数据加载时, loading 状态显示不准确以及 modelValue 变更后数据自动补全(Auto Complete)逻辑的竞态问题。

解决方案

  1. 修复加载状态管理问题

    • 引入 loadingCount 引用计数 :
      在 handleColChange 中,使用 loadingCount 替代单纯的 boolean 值来管理 loading 状态。这解决了当存在多个并发的列数据请求时(例如快速切换列),前一个请求结束会错误地关闭 loading 动画,导致后续请求仍在进行但界面不再显示 loading 的问题。
    • 统一 Loading 重置逻辑 :
      确保在 columnChange 的 resolve 、 finish 回调以及 beforeConfirm 校验流程中,正确地增减 loadingCount ,保证加载状态的准确性。
  2. 修复自动补全与数据同步问题

    • 优化 handleAutoComplete 逻辑 :
      重构了数据补全流程。当 modelValue 传入但对应的列数据缺失时,通过 diffColumns 递归调用 columnChange 来补齐缺失的列数据,确保深层级的数据能正确回显。
    • 解决竞态条件 (Race Condition) :
      引入 autoCompletePending 和 isCompleting 标志位。如果在补全过程中 modelValue 再次发生变化,会将新的补全任务挂起,待当前任务结束后再次触发,防止数据状态错乱。
    • 序列号校验 :
      在 handleColChange 和 diffColumns 中增加了 seq (序列号) 校验,防止过期的异步回调覆盖最新的状态。
  3. 其他优化

    • 标签缓存 ( labelCache ) :
      优化了 getSelectedItem 方法,利用 labelCache 缓存已知的 value-label 映射,确保在数据列表尚未完全加载或临时缺失时,仍能尽可能正确地回显文本。
    • watch 深度监听优化 :
      优化了对 modelValue 和 columns 的监听逻辑,减少不必要的重绘和计算。

风险:labelCache 内存泄漏风险

labelCache 的主要作用是 解决异步请求情况下的“回显”问题 。

代码中使用了一个 Map 来缓存 label:

// L134
const labelCache = new Map<string | number, string>()

这个 Cache 只要组件不销毁就会一直存在,并且:

  1. 只增不减 :每次加载新列数据、每次选中项,都会往里 set 。
  2. 没有清理机制 :如果这个组件在一个长列表页面的每个 Item 里都用,或者是一个常驻页面频繁切换数据源, labelCache 会越来越大。
    建议 :应该考虑在组件销毁时清空,或者设置最大容量。虽然对于普通页面级组件可能影响不大,但对于长期运行的 SPA 应用是个隐患。

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

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

  • 文档已补充或无须补充
  • 代码演示已提供或无须提供
  • TypeScript 定义已补充或无须补充

Summary by CodeRabbit

发布说明

  • 错误修复与改进
    • 改进了列选择器的自动完成机制,确保异步更新的准确性和顺序正确性
    • 优化了加载状态管理,提升用户界面在数据更新时的一致性
    • 增强了数据同步机制,防止过期更新对选择器状态的影响

✏️ Tip: You can customize this high-level summary in your review settings.

改进自动补全逻辑,增加会话管理和序列控制防止重复触发
添加标签缓存优化性能,确保数据同步时显示正确
修复模型值变化时未正确更新显示列表的问题
添加loadingCount变量来跟踪并发加载请求,确保loading状态正确更新
使用深拷贝替代浅拷贝来避免数据引用问题
@vercel
Copy link

vercel bot commented Dec 25, 2025

@TAYUN is attempting to deploy a commit to the weisheng's projects Team on Vercel.

A member of the Team first needs to authorize it.

@netlify
Copy link

netlify bot commented Dec 25, 2025

Deploy Preview for wot-design-uni ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 38a1bed
🔍 Latest deploy log https://app.netlify.com/projects/wot-design-uni/deploys/694d3c5fb8d794000841fb69
😎 Deploy Preview https://deploy-preview-1412--wot-design-uni.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Dec 25, 2025

概览

此变更为列选择器组件添加了自动完成机制和异步更新支持,通过引入加载计数、序列控制和标签缓存来追踪在途操作,防止过时更新,并用深拷贝替代浅拷贝以避免引用共享bug。

变更项

内聚/文件(s) 变更说明
自动完成与异步流程控制
src/uni_modules/wot-design-uni/components/wd-col-picker/wd-col-picker.vue
添加 loadingCount、autoCompletePending、autoCompleteSeq、colChangeSeq、pickerSession 和 labelCache 等状态变量用于追踪在途操作和缓存标签;在 handleColChange 和 diffColumns 中引入序列和会话防护以防止乱序更新;使用深相等检查 (isEqual) 替代引用相等检查;用加载计数器驱动加载状态;缓存列项标签以提高查询性能。

预估代码审查工作量

🎯 3 (中等) | ⏱️ ~20 分钟

建议审查者

  • Moonofweisheng

诗歌

🐰 列选器轻轻翻飞,
异步序列护驾同行,
深拷贝防范悄悄影,
缓存标签闪闪亮晶,
加载计数稳如磐石,
乱序更新无处藏身!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题清晰地总结了主要变更内容,涵盖了自动补全和数据同步问题的修复,与代码改动的核心目标完全相符。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@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

Caution

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

⚠️ Outside diff range comments (1)
src/uni_modules/wot-design-uni/components/wd-col-picker/wd-col-picker.vue (1)

367-460: 防止 resolvefinish 回调重复调用导致的 loadingCount 双重递减。

该函数通过 session 和 sequence 序列号防止过期操作的副作用,但未防止同一操作中用户代码同时调用 resolvefinish 回调。根据 API 文档和示例(如 if (areaData) { resolve(...) } else { finish() }),用户应仅调用其中之一,但代码未强制这一约束。若用户代码错误地同时调用两个回调,loadingCount 会被双重递减,导致 loading 状态不准确。

建议添加状态标志来防止重复调用:

 resolve: (nextColumn: Record<string, any>[]) => {
   if (pickerSession !== session) return
   if (colChangeSeq[colIndex] !== seq) return
+  if (resolved || finished) return
+  resolved = true
   // ... rest
 },
 finish: (isOk?: boolean) => {
   if (pickerSession !== session) return
   if (colChangeSeq[colIndex] !== seq) return
+  if (finished || resolved) return
+  finished = true
   // ... rest
 }

其中 let resolved = false, finished = false 在函数开始处声明。

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d7572d and 38a1bed.

📒 Files selected for processing (1)
  • src/uni_modules/wot-design-uni/components/wd-col-picker/wd-col-picker.vue
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-24T06:12:44.418Z
Learnt from: Moonofweisheng
Repo: Moonofweisheng/wot-design-uni PR: 733
File: src/uni_modules/wot-design-uni/components/wd-tabs/wd-tabs.vue:327-327
Timestamp: 2024-11-24T06:12:44.418Z
Learning: 在 `src/uni_modules/wot-design-uni/components/wd-tabs/wd-tabs.vue` 中,由于 `title` 可能会发生变化,涉及到 `title` 的优化不太容易实现,需谨慎处理。

Applied to files:

  • src/uni_modules/wot-design-uni/components/wd-col-picker/wd-col-picker.vue
📚 Learning: 2024-12-05T15:55:35.371Z
Learnt from: Moonofweisheng
Repo: Moonofweisheng/wot-design-uni PR: 761
File: src/uni_modules/wot-design-uni/components/wd-message-box/wd-message-box.vue:166-182
Timestamp: 2024-12-05T15:55:35.371Z
Learning: 在文件 `wd-message-box.vue` 的 `toggleModal` 方法中,当 `isPass` 为 `false` 时,应阻止执行 `confirm`,但不触发 `cancel` 操作。

Applied to files:

  • src/uni_modules/wot-design-uni/components/wd-col-picker/wd-col-picker.vue
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test Components (wd-col-picker)
  • GitHub Check: Redirect rules - wot-design-uni
  • GitHub Check: Header rules - wot-design-uni
  • GitHub Check: Pages changed - wot-design-uni
🔇 Additional comments (10)
src/uni_modules/wot-design-uni/components/wd-col-picker/wd-col-picker.vue (10)

106-106: 导入 isEqual 工具函数正确。

该函数用于深度相等性比较,有助于准确检测 modelValue 的变化。


173-188: 使用 isEqual 进行深度比较可以避免不必要的更新。

这个改进能够正确检测 modelValue 的实际变化,而不是仅仅依赖引用相等性。这样可以减少不必要的重新渲染和自动补全触发。


202-211: 缓存标签有助于提高显示一致性。

在列数据变化时填充 labelCache 可以确保即使在异步数据加载过程中也能正确显示标签。类型守卫确保只缓存有效的键值对。


289-295: 会话管理机制能有效防止过期回调。

通过递增 pickerSession 并重置加载状态,这段代码确保了关闭选择器后,之前的异步操作不会影响新的会话。


333-337: 标签缓存回退提升了用户体验。

当在当前 selectList 中找不到项目时,使用缓存的标签可以确保在数据异步加载期间仍然能显示有意义的文本。


340-365: 用户交互时正确取消自动补全操作。

当用户手动选择项目时,重置 autoCompletePendingisCompleting 标志,并递增序列号,确保之前的自动补全操作不会干扰用户的选择。同时缓存标签以供后续使用。


461-474: 确认时正确重置状态并使用浅拷贝发送数据。

重置 loadingCount 和其他状态变量确保了干净的状态。使用 slice(0) 创建浅拷贝来发送事件可以防止外部代码意外修改内部状态。


547-575: 自动补全逻辑重构提升了健壮性。

新的实现正确处理了:

  • 通过 isCompletingautoCompletePending 防止重入
  • 通过序列号防止过期操作
  • 查找第一个缺失或不匹配的列
  • 从缺失位置开始递归加载数据

这些改进有效解决了多级异步数据加载和 modelValue 变化时的同步问题。

注意:此逻辑依赖于 diffColumns 中的递归,该函数存在 off-by-one 错误(已在其他评论中指出)。


135-135: 由于无法访问代码仓库,我无法直接验证关于 labelCache 的内存泄漏声明。为了提供准确的反馈,需要手动验证以下几点:

  1. 检查组件是否已有 onUnmounted 钩子用于清理缓存
  2. 确认 labelCache 的使用方式和键值的生成逻辑
  3. 验证缓存键的数量是否真的无限增长,还是有实际上界
  4. 查看是否存在其他的缓存大小限制机制

300-300: 改为使用项目已有的 deepClone 工具函数。

项目的 util.ts 已经提供了 deepClone 函数(第 500 行),支持循环引用处理。由于 uni-app 需要跨多个平台运行(H5、小程序、原生应用),而 structuredClone 在许多小程序引擎中并不可用,建议使用既存的 deepClone 工具函数,这样既能保证跨平台兼容性,又能获得更好的性能和对循环引用的支持。

建议的修改:

在组件文件顶部导入 deepClone

import { deepClone } from '../common/util'

然后替换:

- selectList.value = JSON.parse(JSON.stringify(lastSelectList.value))
+ selectList.value = deepClone(lastSelectList.value)
- lastSelectList.value = JSON.parse(JSON.stringify(selectList.value))
+ lastSelectList.value = deepClone(selectList.value)

Likely an incorrect or invalid review comment.

Comment on lines +536 to +539
if (nextIndex < modelValue.length - 1) {
diffColumns(nextIndex, seq)
return
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

递归停止条件存在 off-by-one 错误。

Line 536 的条件 nextIndex < modelValue.length - 1 会导致最后一列的子列数据不被加载。

例如,当 modelValue = [1, 2, 3] 时:

  • diffColumns(0) 加载 selectList[1](基于 modelValue[0]
  • diffColumns(1) 加载 selectList[2](基于 modelValue[1]
  • 递归停止,但 selectList[3](基于 modelValue[2])未被加载

这会导致用户看不到最后一个选择项之后的列,与用户手动点击的行为不一致。

🔎 建议的修复
     if (seq !== autoCompleteSeq) return
     const nextIndex = colIndex + 1
-    if (nextIndex < modelValue.length - 1) {
+    if (nextIndex < modelValue.length) {
       diffColumns(nextIndex, seq)
       return
     }
📝 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 (nextIndex < modelValue.length - 1) {
diffColumns(nextIndex, seq)
return
}
if (nextIndex < modelValue.length) {
diffColumns(nextIndex, seq)
return
}
🤖 Prompt for AI Agents
In src/uni_modules/wot-design-uni/components/wd-col-picker/wd-col-picker.vue
around lines 536-539, the recursive stop condition `nextIndex <
modelValue.length - 1` is off-by-one and prevents the last column's child data
from being loaded; change the condition to `nextIndex < modelValue.length` so
the recursion includes the final selected index's child column, then run the
component behavior/test to confirm the last column is loaded correctly after
selection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant