Skip to content

新增拖拽组件#1463

Open
Xiabaiyou wants to merge 23 commits intoMoonofweisheng:masterfrom
Xiabaiyou:baiyou/dragSort
Open

新增拖拽组件#1463
Xiabaiyou wants to merge 23 commits intoMoonofweisheng:masterfrom
Xiabaiyou:baiyou/dragSort

Conversation

@Xiabaiyou
Copy link
Copy Markdown
Collaborator

@Xiabaiyou Xiabaiyou commented Feb 3, 2026

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

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

🔗 相关 Issue

💡 需求背景和解决方案

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

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

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

Summary by CodeRabbit

发布说明

  • 新功能

    • 新增Tour组件,为用户提供页面引导和交互漫游功能
    • 新增DragSort组件,支持拖拽排序、交换和网格布局等多种排序方式
    • ColPicker组件增强:支持本地和远程搜索、下拉刷新、上拉加载等功能
  • 文档

    • 新增Tour、DragSort组件及ColPicker高级功能的完整使用文档和代码示例
  • 多语言

    • 为新增功能添加15种语言的本地化支持

在 global.d.ts 文件中添加 WdGuide 组件的类型声明,确保 TypeScript 项目中可以正确识别该组件。

添加引导组件主逻辑
- 将 offset、borderRadius、padding、bottomSafetyOffset 和 topSafetyOffset 的类型从数组修改为 Number
- 这种更改提高了类型定义的准确性和一致性,确保了更好的类型检查和代码质量
- 将 border-radius、padding、bottom-safety-offset 和 top-safety-offset 属性的类型从 string / number 修改为 number
- 优化文档中属性类型的准确性,提高开发者体验
- 修改 highlightStyle 和 popoverStyle 计算属性,优化样式对象的创建方式
- 移除多余的空行和注释,提高代码整洁度
- 调整 updateElementInfo、getEffectiveBoundaries、checkScrollNeeds 等函数的结构,提升可读性
- 优化 scrollUp 和 scrollDown 函数中的变量赋值和注释
- 丰富了自定义功能,包括引导内容、高亮区域、按钮等
- 优化了引导步骤的控制逻辑,支持双向绑定当前步骤
- 调整了高亮区域和引导内容的样式,提升了用户体验
- 增加了组件的响应式处理,适应不同平台和屏幕尺寸
- 优化了代码结构,提高了组件的可维护性和可测试性
- 将 manifest.json 中的 appid 从 "wxf81518e7b11ea8d8" 更改为 "wx18107b71aa1672e2"
- 添加了向导组件的单元测试文件
- 实现了对向导组件各项功能的测试,包括基本渲染、隐藏状态、步骤内容渲染、按钮显示和功能、自定义文本和样式、事件触发等
- 模拟了 uni 对象和元素查询,以适应uni-app环境

wd-guide
去除多余的打印 修复menuButtonInfo的ts报错
- 移除了多处 console.log 调试日志,清理无用输出
- 为 menuButtonInfo 添加了更精确的类型定义
- 删除了未使用的计算属性 watch
- 提取公共的默认样式函数 getDefaultStyle
- 提取公共的高亮样式计算函数 calculateHighlightStyle
- 使用 computed 属性优化高亮样式和元素信息的计算
- 简化代码结构,提高可维护性和可读性
- 优化了引导组件的滚动逻辑,确保滚动到顶部后再显示引导步骤
- 添加了系统信息更新功能,提高元素定位的准确性
- 修复了某些场景下引导步骤显示异常的问题
- 优化了代码结构,提高了组件的可维护性
- 修复重复调用导致位置计算偏差问题
- 将 controlCurrent.value 的初始值从 6 修改为 2
- 优化了控制指南的起始步骤,提高了用户体验
- 将 guide 组件重命名为 tour 组件
- 更新了相关文件和代码中的 guide 引用,改为 tour
- 调整了部分样式和命名以适应新的组件名称
- 移除旧的引导组件代码和文档
- 更新类型定义,使用通用的类型构造函数
- 优化组件属性定义,使用统一的类型创建方法
- 删除冗余的属性类型和默认值定义
- 将 guide-show 动画名称改为 tour-show,统一动画命名风格
- 此更改不影响功能,仅用于提升代码一致性和可维护性
- 新增中英文文档侧边栏目录
- 修改演示组件入口位置,确保导航一致性
- 调整标题结构,将“介绍”合并为描述文字,“基本用法”更名为“基本使用”
- 统一代码块语言标识为 html,并更新 v-model 为 :model-value 以符合 Vue 3 写法
- 补充插槽、属性和方法的说明,优化自定义内容与样式的描述
- 规范化 props 类型标注,统一数值类属性类型为 number
- 更新事件参数格式,移除冗余字段说明
- 更新本地化标题文本,保持中英文一致
- 在 H5 环境中使用 useLockScroll 返回的 lock 和 unlock 方法以正确控制滚动锁定
- 为 popoverStyle 增加更精确的 TypeScript 类型定义,提升代码可维护性
- 新增对 useLockScroll 的 mock 实现,完善组件单元测试覆盖
该提交移除了 `src/subPages/tour/index.vue` 文件中的所有内容,包括模板、脚本和样式部分。这可能是因为该页面不再需要,或其功能已被迁移至其他位置。
- 新增 filterable 属性支持本地和远程搜索功能
- 新增 refresher-enabled 属性支持下拉刷新
- 新增 scroll-to-lower-enabled 属性支持上拉加载更多
- 新增 search、refresh、scroll-to-lower 等事件
- 新增 stopRefresh 等方法
- 新增搜索和空数据插槽
- 添加本地搜索和远程搜索示例文档
- 添加下拉刷新和上拉加载示例文档
- 添加综合案例展示搜索+刷新+加载功能
移除了 getCurrentColData 和 getAllColData 方法的文档说明,
这些方法已在组件中移除。
- 在文档导航中添加 DragSort 拖拽排序组件链接
- 添加英文和中文国际化配置
- 配置页面路由和导航标题
- 在首页组件列表中注册新组件
- 在全局类型定义中声明组件类型
- 在中文文档导航中添加DragSort拖拽排序组件链接
- 添加国际化词条:en-US.json和zh-CN.json中的dragSort翻译
- 配置pages.json注册dragSort页面路径和样式设置
- 在首页组件中注册dragSort组件入口
- 在全局类型定义中声明WdDragSort、WdDragSortItem和WdDragHandle组件类型
@netlify
Copy link
Copy Markdown

netlify bot commented Feb 3, 2026

Deploy Preview for wot-design-uni ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit b86ff06
🔍 Latest deploy log https://app.netlify.com/projects/wot-design-uni/deploys/69820fcdc8b9ff0008b619a1
😎 Deploy Preview https://deploy-preview-1463--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
Copy Markdown

coderabbitai bot commented Feb 3, 2026

漫游

高阶概述

该PR引入了两个新组件(Tour和DragSort)以及一个辅助组件(DragHandle),同时显著扩展了ColPicker的功能,增加了搜索、刷新和无限滚动支持。包括完整的类型定义、样式、多语言支持、文档和演示页面。

变更

组件/文件 变更摘要
Tour 组件
src/uni_modules/wot-design-uni/components/wd-tour/types.ts, wd-tour.vue, index.scss, docs/component/tour.md, src/subPages/tour/Index.vue, tests/components/wd-tour.test.ts
新增完整的导览(Tour)组件实现,包括类型定义、组件逻辑、样式、文档和演示。支持步骤配置、遮罩高亮、自定义内容插槽、导航按钮定制,以及多种事件和方法暴露。
DragSort 组件及工具
src/uni_modules/wot-design-uni/components/wd-drag-sort/types.ts, wd-drag-sort.vue, index.scss, utils.ts, src/uni_modules/wot-design-uni/components/wd-drag-sort-item/types.ts, wd-drag-sort-item.vue, index.scss, src/uni_modules/wot-design-uni/components/wd-drag-handle/types.ts, wd-drag-handle.vue, docs/component/drag-sort.md, src/subPages/dragSort/Index.vue
新增拖拽排序组件系统,包括主组件、子项组件、拖拽句柄,支持移动/交换两种排序模式、自动滚动、长按触发、禁用项、实时模式等。包含完整的工具函数库(节流、距离计算、数组操作)、类型定义、样式和演示。
ColPicker 功能扩展
src/uni_modules/wot-design-uni/components/wd-col-picker/types.ts, wd-col-picker.vue, index.scss, docs/component/col-picker.md, src/subPages/colPicker/Index.vue
扩展ColPicker支持本地和远程搜索、下拉刷新、上拉加载等功能。新增多个属性(filterable、filterType、filterPlaceholder、refresherEnabled等)、事件(search、search-change、search-clear、refresh、scroll-to-lower)和方法(stopRefresh)。更新样式和文档以支持新功能。
全球组件声明
src/uni_modules/wot-design-uni/global.d.ts
在全球TypeScript组件声明中添加WdTour、WdDragSort、WdDragSortItem、WdDragHandle四个新组件。
多语言支持
src/locale/en-US.json, zh-CN.json, src/uni_modules/wot-design-uni/locale/lang/*.ts
添加Tour、DragSort、下拉加载等的多语言本地化条目,覆盖中、英、日、韩、德、法、西、葡、俄、土、泰、阿、越、维吾尔等多种语言,以及ColPicker搜索功能的本地化字符串。
导航配置
docs/.vitepress/locales/en-US.ts, zh-CN.ts, src/pages.json, src/pages/index/Index.vue
更新文档侧边栏导航和应用页面配置,添加Tour和DragSort的页面入口、导航项和本地化标题。
启动和构建配置
.hbuilderx/launch.json, src/manifest.json
添加HBuilderX启动配置以支持uni-app Android应用运行;调整manifest.json格式。
测试
tests/components/wd-col-picker.test.ts, wd-tour.test.ts
移除ColPicker的useLabelSlot属性测试;为Tour组件添加全面的测试套件,涵盖渲染、导航、事件、自定义插槽等场景。

序列图

sequenceDiagram
    participant User as 用户
    participant DragSort as DragSort<br/>主容器
    participant Item as DragSortItem<br/>拖拽项
    participant Handle as DragHandle<br/>拖拽句柄
    participant Utils as 工具函数<br/>布局计算

    User->>Item: 触摸项目
    Item->>Item: 识别是否需要<br/>长按或手柄触发
    
    alt 使用拖拽句柄
        User->>Handle: 触摸句柄
        Handle->>Item: onHandleTouch
    else 直接触摸项
        Item->>Item: 启动长按计时
    end
    
    Item->>DragSort: onDragStart事件
    DragSort->>Utils: dragInit布局<br/>计算所有项目位置
    Utils-->>DragSort: 返回items和slots
    DragSort->>DragSort: 显示占位符<br/>startAutoScroll
    
    User->>Item: 拖拽项目移动
    Item->>DragSort: onDragMove<br/>提供位置数据
    DragSort->>Utils: findClosestSlot<br/>找到最近的目标位置
    
    alt 实时模式
        DragSort->>DragSort: 立即排序<br/>更新modelValue
    else 延迟模式
        DragSort->>DragSort: 存储dragDelta<br/>延迟更新
    end
    
    DragSort->>DragSort: 更新currentSlotStyle<br/>占位符位置
    
    User->>Item: 释放触摸
    Item->>DragSort: onDragEnd事件
    DragSort->>Utils: 最终位置验证<br/>确定最终排序
    DragSort->>DragSort: 发出change和<br/>update:modelValue事件
    DragSort->>DragSort: 重新初始化布局
Loading
sequenceDiagram
    participant User as 用户
    participant ColPicker as ColPicker<br/>列选择器
    participant Search as 搜索栏<br/>wd-search
    participant API as 远程API/本地数据

    User->>ColPicker: 启用filterable=true
    ColPicker->>Search: 显示搜索输入框
    
    User->>Search: 输入搜索文本
    Search->>ColPicker: handleSearchChange事件
    
    alt 本地搜索(local)
    ColPicker->>ColPicker: 过滤当前列数据<br/>显示匹配项
    else 远程搜索(remote)
    ColPicker->>ColPicker: 发出search-change事件
    ColPicker->>API: 外部应用处理<br/>远程搜索请求
    API-->>ColPicker: 返回过滤结果
    ColPicker->>ColPicker: 更新列数据
    end
    
    User->>ColPicker: 下拉刷新(refresher)
    ColPicker->>ColPicker: 发出refresh事件
    API->>API: 重新加载数据
    API-->>ColPicker: 返回新数据
    
    User->>ColPicker: 上拉到底部
    ColPicker->>ColPicker: 发出scroll-to-lower事件
    API->>API: 加载更多数据<br/>(分页)
    API-->>ColPicker: 返回下一批数据
    ColPicker->>ColPicker: 追加到列表
Loading

代码审查工作量评估

🎯 4 (复杂) | ⏱️ ~70 分钟

可能相关的PR

  • PR #572: 修改 docs/component/col-picker.md,主PR增加了广泛的搜索/刷新/加载文档和API文档,两者对同一文件进行修改。
  • PR #1399: 直接涉及Tour(wd-tour)组件及其类型/文档/页面的修改,包括 src/uni_modules/wot-design-uni/components/wd-tour/types.tswd-tour.vuedocs/component/tour.mdsrc/subPages/tour/Index.vue
  • PR #1181: 两者都修改了wd-col-picker组件(类型和主组件文件),主PR添加了搜索/刷新属性和事件,该PR添加了marker相关功能。

建议的审阅者

  • Moonofweisheng
  • MonkeyCode-AI

诗歌

🐰 新的导览带领万千步,拖拽排序任意舞,
列表搜索刷新流,多语言覆盖天下路,
组件完整样式好,测试文档俱周到,
兔兔欢呼新功能添!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题'新增拖拽组件'准确反映了主要变更内容,与changeset中新增的DragSort组件及其相关功能(DragSortItem、DragHandle)完全对应。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 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
Copy Markdown

@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: 15

🤖 Fix all issues with AI agents
In `@docs/component/col-picker.md`:
- Around line 934-938: The methods table has inconsistent column counts: the
rows for "open" and "close" are missing the "最低版本" cell; update the table so
each row has four columns (方法名称, 说明, 参数, 最低版本) by adding the missing cell (e.g.
"-" or the correct version) for the "open" and "close" rows so they match the
"stopRefresh" row and keep the table aligned.
- Around line 892-900: The properties table contains duplicate rows for
refresher-background and refresher-default-style; remove the duplicated entries
so each property appears only once, keeping the single correct row for
refresher-default-style (with allowed values 'black' / 'white' / 'none' and
default 'white') and the single correct row for refresher-background (default
'transparent'), and ensure the remaining table rows are reordered/aligned
consistently with the other properties (use the existing property names
refresher-default-style and refresher-background to locate the duplicates).

In `@docs/component/drag-sort.md`:
- Around line 193-198: The scroll-lock flag is inverted: when pointerEvents uses
`canScrollY ? 'auto' : 'none'`, dragging should disable scrolling; update
handleDragStart to set canScrollY.value = false and handleDragEnd to set
canScrollY.value = true so dragging disables pointer scroll and releasing
restores it; locate the canScrollY variable and the
handleDragStart/handleDragEnd functions to make this swap.

In `@docs/component/tour.md`:
- Around line 186-187: Update the event parameter type for `isUp` in the tour
component docs: change `isUp: number` to `isUp: boolean` in both the `prev` and
`next` table rows (the two entries showing `{ oldCurrent: number, current:
number, total: number, isUp: number }`) so the event signature becomes `{
oldCurrent: number, current: number, total: number, isUp: boolean }`.
- Line 1: 文档标题与组件命名不一致:当前 docs/component/tour.md 标题为 "Guide 漫游组件",但组件名为
`wd-tour` 且其它资源(如 locale 文件)使用 "Tour 漫游"。请将文档标题统一为与组件名和 locale 一致的表述(例如 "Tour
漫游" 或 "Tour 漫游引导"),并在文件头部(当前标题行)替换为选定的统一名称,同时检查并同步任何引用 `wd-tour` 的文档段落或示例以保持一致性。

In `@src/pages.json`:
- Around line 1346-1357: The non-MP navigationBarTitleText for the "dragSort"
page is hardcoded and should be replaced with an i18n placeholder to match other
pages; update the style entry under "name": "dragSort" (the
"navigationBarTitleText" in the // `#ifndef` MP block) to the appropriate locale
key placeholder (e.g. use the same key pattern used elsewhere like
"pages.dragSort.title" or the project's existing key for DragSort) so the title
is resolved via the i18n system instead of a literal string.

In `@src/subPages/colPicker/Index.vue`:
- Around line 535-539: The uni.showLoading calls are passing an invalid icon
option (icon: 'none'); update every occurrence of uni.showLoading in Index.vue
to only pass supported options (title and mask) and remove the icon property, or
if you intended a toast with an icon use uni.showToast instead (look for the
uni.showLoading invocations to modify). Ensure all instances reported (the three
additional occurrences) are fixed consistently.
- Around line 623-627: The DB array declared inside getComplexMockData (const
DB: any[] = []) is never used and should be removed to avoid dead code; remove
the DB declaration (and any related unused TOTAL_COUNT if it's solely for DB)
and keep the existing logic that generates rows into fullTable, ensuring
getComplexMockData only defines and returns fullTable and any genuinely used
constants like TOTAL_COUNT if referenced elsewhere.

In `@src/uni_modules/wot-design-uni/components/wd-col-picker/types.ts`:
- Line 1: Remove the unused named import handleError from the vue import
statement in types.ts; update the import to only include the used symbols
(ComponentPublicInstance, ExtractPropTypes, PropType) and verify there are no
remaining references to handleError in the file before committing.

In `@src/uni_modules/wot-design-uni/components/wd-col-picker/wd-col-picker.vue`:
- Around line 642-645: The function stopRefresh currently contains a console.log
debug statement; remove the console.log call from stopRefresh (which sets
refreshTriggered.value = false) so no console output remains in production, or
replace it with a noop or a controlled logger if a trace is required (refer to
stopRefresh and refreshTriggered in wd-col-picker.vue).
- Around line 113-117: Replace the hardcoded Chinese string for the empty state
by calling the existing translate() function: update the wd-status-tip usage in
the template v-else to pass the translated value (replace tip="暂无内容" with a
translate call) and add the corresponding "empty" key to the component's locale
files so translations resolve; refer to the existing translate() usages in this
component to mirror how they call translate().

In
`@src/uni_modules/wot-design-uni/components/wd-drag-sort-item/wd-drag-sort-item.vue`:
- Line 61: The module-level variable isHandleTriggered causes shared state
across wd-drag-sort-item instances; move its declaration into the component
instance (e.g. inside setup() as a ref or inside data() as a reactive property)
and replace all uses of isHandleTriggered with the instance-scoped reactive
(using .value if you choose ref) so each wd-drag-sort-item has its own trigger
flag; update any handlers/readers in the component to reference the new
instance-scoped symbol instead of the top-level isHandleTriggered.

In `@src/uni_modules/wot-design-uni/components/wd-tour/types.ts`:
- Around line 1-2: 导出类型 TourProps 当前使用 typeof tourProps(取到的是 props
定义对象)会导致错误的类型提示;请将导出改为使用 Vue 的 ExtractPropTypes 来提取解析后的 props 类型:在此文件中替换现有
TourProps 导出,引用 tourProps(props 定义对象)并用 ExtractPropTypes<typeof tourProps>
作为导出类型,确保导出的类型名仍为 TourProps 以保持向后兼容性。

In `@src/uni_modules/wot-design-uni/components/wd-tour/wd-tour.vue`:
- Around line 416-429: The watch handler for props.modelValue sets
oldscrollTop.value = 0 which breaks subsequent position calculations; instead,
when the tour opens (inside the watch callback where updateSystemInfo(),
nextTick(), and updateElementInfo() are called), read the current viewport
scroll via uni.createSelectorQuery().selectViewport().scrollOffset() and assign
that value to oldscrollTop.value before calling updateElementInfo() and emitting
update:current; keep updateSystemInfo() as is but replace the hardcoded 0
assignment with the actual scrollTop so scrollDistance calculations
(oldscrollTop.value + res.top - props.padding) use the correct baseline.

In `@tests/components/wd-tour.test.ts`:
- Line 175: Remove the template placeholder comment "// ... existing code ..."
from the test file (the placeholder present in tests/components/wd-tour.test.ts)
— delete that line and either replace it with the intended test code or leave no
stray placeholder comments so the test file contains only real test logic and
assertions (look for the exact string "// ... existing code ..." to locate and
remove it).
🧹 Nitpick comments (20)
.hbuilderx/launch.json (1)

1-9: 考虑是否应将 IDE 配置文件纳入版本控制

配置文件本身格式正确且有效。但 .hbuilderx/ 目录是 HBuilderX IDE 的特定配置,通常这类 IDE 配置文件会被添加到 .gitignore 中,因为不同开发者可能有不同的 IDE 设置偏好。

不过,如果团队希望统一开发环境配置以提高一致性,提交此文件也是合理的。建议与团队确认项目约定。

src/subPages/dragSort/Index.vue (1)

391-396: 建议同步滚动状态,避免拖拽后跳动。

handleScroll 只更新 currentScrollTopscrollTop 仅在 onScroll 更新。若程序化滚动未触发 scroll 事件,scrollTop 可能不同步。建议在自动滚动时同步两者并钳制到非负。

♻️ 建议修改
 function onScroll(e: any) {
-  scrollTop.value = e.detail.scrollTop
+  const top = e.detail.scrollTop
+  scrollTop.value = top
+  currentScrollTop.value = top
 }
 function handleScroll({ dy }: { dx: number; dy: number }) {
-  currentScrollTop.value += dy
+  const next = Math.max(0, currentScrollTop.value + dy)
+  currentScrollTop.value = next
+  scrollTop.value = next
 }
src/locale/zh-CN.json (2)

416-416: 键名 dragSort 位置破坏了字母排序顺序。

该文件中的键名按字母顺序排列,dragSort 应该放在 d 开头的其他键附近(如 down-and-load-more 之后),而不是在 duo-xuan 之后。


971-971: 键名 remote_down-and-load-more 使用下划线,与其他键名的连字符命名约定不一致。

其他类似的键名如 down-and-load-moreremote-search 都使用连字符。建议改为 remote-down-and-load-more 以保持一致性。

tests/components/wd-tour.test.ts (2)

123-123: 考虑将超时魔数提取为常量。

多处使用 100150 等超时值,建议提取为常量以提高可维护性和 CI 环境下的稳定性:

const DOM_UPDATE_DELAY = 100
const EXTENDED_DOM_DELAY = 150

Also applies to: 142-142, 162-162, 192-192, 209-209, 225-225


59-81: beforeEach 中的 mock 实现与模块级定义重复。

mockUni.createSelectorQuery.mockImplementation 在 lines 14-40 已定义,beforeEach 中再次定义相同实现是冗余的。vi.clearAllMocks()mockClear() 已足够重置状态。

简化 beforeEach
 beforeEach(() => {
   vi.clearAllMocks()
-  // 重置模拟函数
-  mockUni.createSelectorQuery.mockClear()
-  mockUni.createSelectorQuery.mockImplementation(() => ({
-    select: vi.fn(() => ({
-      boundingClientRect: vi.fn((callback) => {
-        // 模拟元素位置信息
-        setTimeout(() => {
-          callback({
-            top: 100,
-            left: 50,
-            width: 200,
-            height: 100
-          })
-        }, 10)
-        return {
-          exec: vi.fn()
-        }
-      })
-    }))
-  }))
 })
src/uni_modules/wot-design-uni/components/wd-drag-sort/types.ts (2)

2-2: makeArrayProp 已导入但未使用。

建议移除未使用的导入以保持代码整洁。

移除未使用的导入
-import { baseProps, makeArrayProp, makeBooleanProp, makeNumberProp, makeRequiredProp, makeStringProp } from '../common/props'
+import { baseProps, makeBooleanProp, makeNumberProp, makeRequiredProp, makeStringProp } from '../common/props'

30-37: DragSortProvide 中的函数参数类型较宽松。

touch: anyhelper: any 等参数使用 any 类型,降低了类型安全性。如果有明确的 touch event 或 helper 接口定义,建议替换为具体类型。

src/subPages/tour/Index.vue (2)

182-182: watch 已导入但未使用。

建议移除未使用的导入。

移除未使用的导入
-import { ref, watch, nextTick } from 'vue'
+import { ref, nextTick } from 'vue'

315-338: 启动引导的函数对 scrollToTop() 的处理不一致。

startBasicTour 使用 await scrollToTop()nextTick,而其他函数(如 startMaskNextTourstartNoMaskTour 等)直接调用 scrollToTop() 但不等待其完成。这可能导致滚动未完成时引导就开始显示。

建议统一异步处理
-function startMaskNextTour() {
-  scrollToTop()
-  showClickMaskTour.value = true
-}
+async function startMaskNextTour() {
+  await scrollToTop()
+  nextTick(() => {
+    showClickMaskTour.value = true
+  })
+}

其他函数也应采用类似模式。

src/subPages/colPicker/Index.vue (1)

766-771: 存在两个 onMounted 钩子。

文件中有两处 onMounted(lines 129-139 和 766-771)。虽然 Vue 允许多个 onMounted,但合并到一处更易维护。

建议合并 onMounted 钩子
 onMounted(async () => {
   toast.loading(t('shu-ju-jia-zai-zhong'))
   await sleep()
   toast.close()
   value2.value = ['150000', '150100', '150121']

   getMockData(0, 1).then((data) => {
     columnsAsync.value = [data]
     pageMap.value[0] = 1
   })
+
+  // 初始化综合案例第一列数据
+  pageMapComplex.value[0] = 1
+  getComplexMockData(0, 1).then((data) => {
+    columnsComplex.value = [data]
+  })
 })
-
-// 初始化综合案例第一列数据
-onMounted(() => {
-  pageMapComplex.value[0] = 1
-  getComplexMockData(0, 1).then((data) => {
-    columnsComplex.value = [data]
-  })
-})
src/uni_modules/wot-design-uni/components/wd-drag-sort-item/wd-drag-sort-item.vue (3)

49-49: slots 变量声明后未使用。

useSlots() 被调用但返回值未被使用,建议移除。

移除未使用的代码
-const slots = useSlots()

同时从 import 中移除 useSlots

-import { ref, computed, watch, onMounted, getCurrentInstance, onBeforeUnmount, useSlots } from 'vue'
+import { ref, computed, watch, onMounted, getCurrentInstance, onBeforeUnmount } from 'vue'

178-183: init() 函数体为空,仅包含注释。

函数声明后实际没有执行任何操作,且 onMounted 中调用了它。如果不需要此函数,建议移除相关代码以避免混淆。


209-209: 存在注释掉的代码。

// let touchStartTime = 0 是死代码,如果确认不需要应该删除。

建议移除
 let longPressTimer: ReturnType<typeof setTimeout> | null = null
 let touchStartPosition = { x: 0, y: 0 }
-// let touchStartTime = 0
src/uni_modules/wot-design-uni/components/wd-drag-sort/utils.ts (3)

7-16: common/util.ts 中的 throttle 函数存在重复。

根据提供的代码片段,common/util.ts 中已经有一个更完整的 throttle 实现,支持尾部调用(trailing edge)。当前实现更简单,会丢弃延迟期间的最后一次调用。

建议考虑复用现有的 throttle 函数,或者在此处添加注释说明为什么需要这种简化的实现(例如:拖拽场景不需要尾部调用)。


78-84: swapArrayItem 函数的行为与注释描述不一致。

注释声明返回"同一引用"(返回同一引用),但函数实际上直接修改了传入的数组。这种原地修改可能会导致调用方意外地改变原始数据。

moveArrayItem(返回新数组)的行为不一致,建议统一为不可变操作:

♻️ 建议修改为不可变操作
 export const swapArrayItem = (arr: any[], fromIndex: number, toIndex: number) => {
-  if (fromIndex === toIndex) return arr // 同一个位置啥也不做
-  const temp = arr[fromIndex]
-  arr[fromIndex] = arr[toIndex]
-  arr[toIndex] = temp
-  return arr
+  if (fromIndex === toIndex) return [...arr]
+  const newArr = [...arr]
+  ;[newArr[fromIndex], newArr[toIndex]] = [newArr[toIndex], newArr[fromIndex]]
+  return newArr
 }

129-134: 使用了已弃用的 substr 方法。

String.prototype.substr 已被标记为弃用,建议使用 substringslice 替代。

♻️ 建议修改
 export const generateId = (prefix = 'ds') => {
   // 使用时间戳 + 随机数生成 ID,兼容小程序环境
   const timestamp = Date.now().toString(36)
-  const random = Math.random().toString(36).substr(2, 5)
+  const random = Math.random().toString(36).substring(2, 7)
   return `${prefix}_${timestamp}${random}`
 }
src/uni_modules/wot-design-uni/components/wd-drag-sort/wd-drag-sort.vue (3)

278-283: reset 函数已定义但从未使用。

此函数在组件内部没有被调用,也未通过 defineExpose 暴露给外部。如果不需要,建议移除以避免死代码。

🧹 如果不需要,建议移除
-/**
- * 重置状态,强制重新计算
- */
-const reset = () => {
-  isReady.value = false
-  for (const key in items) {
-    delete items[key]
-  }
-}

或者,如果此函数有公共用途,可以将其添加到 defineExpose 中:

 defineExpose({
-  init: dragInit
+  init: dragInit,
+  reset
 })

139-145: 重试计数器附加在函数对象上的模式不够清晰。

当前将 retryCount 附加到 dragInit 函数对象上,虽然能工作,但可读性较差且需要类型断言。建议使用 ref 管理重试计数。

♻️ 建议使用 ref 管理重试计数
+const retryCount = ref(0)
+
 const retryLayout = () => {
-  if (!(dragInit as any).retryCount) (dragInit as any).retryCount = 0
-  if ((dragInit as any).retryCount < 3) {
-    ;(dragInit as any).retryCount++
+  if (retryCount.value < 3) {
+    retryCount.value++
     setTimeout(() => dragInit(), 50)
   }
 }

同时在 handleLayout 成功时重置:

-  ;(dragInit as any).retryCount = 0 // 成功后重置重试计数
+  retryCount.value = 0 // 成功后重置重试计数

56-71: linkChildren 中的 register/unregister 为空函数。

注释说明 useChildren 不需要这两个方法,但仍然提供了空实现。如果确实不需要,建议移除以简化代码;如果是为了接口兼容,建议添加更详细的注释说明。

Comment on lines +892 to +900
| filterable | 是否开启搜索功能(本地/远程) | boolean | - | false | - |
| filter-type | 搜索类型:'local' (本地过滤) 或 'remote' (远程搜索) | string | - | 'local' | - |
| filter-placeholder | 搜索输入框占位符 | string | - | - | - |
| refresher-enabled | 是否开启下拉刷新 | boolean | - | false | - |
| refresher-default-style| 下拉刷新默认样式 | string | 'black' / 'white' / 'none' | 'white' | - |
| refresher-background | 下拉刷新区域背景颜色 | string | - | transparent | - |
| scroll-to-lower-enabled| 是否开启上拉加载更多 | boolean | - | false | - |
| refresher-background | 下拉刷新区域背景颜色 | string | - | transparent | - |
| refresher-default-style| 下拉刷新默认样式 | string | 'black' / 'white' / 'none' | 'white' | - |
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

属性表重复行会导致文档歧义。

refresher-backgroundrefresher-default-style 在表内重复出现,请保留一组即可。

🛠️ 建议修改
-| refresher-background   | 下拉刷新区域背景颜色                                                                                                          | string            | -      | transparent | -    |
-| refresher-default-style| 下拉刷新默认样式                                                                                                              | string            | 'black' / 'white' / 'none' | 'white' | - |
📝 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
| filterable | 是否开启搜索功能(本地/远程) | boolean | - | false | - |
| filter-type | 搜索类型:'local' (本地过滤) 或 'remote' (远程搜索) | string | - | 'local' | - |
| filter-placeholder | 搜索输入框占位符 | string | - | - | - |
| refresher-enabled | 是否开启下拉刷新 | boolean | - | false | - |
| refresher-default-style| 下拉刷新默认样式 | string | 'black' / 'white' / 'none' | 'white' | - |
| refresher-background | 下拉刷新区域背景颜色 | string | - | transparent | - |
| scroll-to-lower-enabled| 是否开启上拉加载更多 | boolean | - | false | - |
| refresher-background | 下拉刷新区域背景颜色 | string | - | transparent | - |
| refresher-default-style| 下拉刷新默认样式 | string | 'black' / 'white' / 'none' | 'white' | - |
| filterable | 是否开启搜索功能(本地/远程) | boolean | - | false | - |
| filter-type | 搜索类型:'local' (本地过滤) 或 'remote' (远程搜索) | string | - | 'local' | - |
| filter-placeholder | 搜索输入框占位符 | string | - | - | - |
| refresher-enabled | 是否开启下拉刷新 | boolean | - | false | - |
| refresher-default-style| 下拉刷新默认样式 | string | 'black' / 'white' / 'none' | 'white' | - |
| refresher-background | 下拉刷新区域背景颜色 | string | - | transparent | - |
| scroll-to-lower-enabled| 是否开启上拉加载更多 | boolean | - | false | - |
🤖 Prompt for AI Agents
In `@docs/component/col-picker.md` around lines 892 - 900, The properties table
contains duplicate rows for refresher-background and refresher-default-style;
remove the duplicated entries so each property appears only once, keeping the
single correct row for refresher-default-style (with allowed values 'black' /
'white' / 'none' and default 'white') and the single correct row for
refresher-background (default 'transparent'), and ensure the remaining table
rows are reordered/aligned consistently with the other properties (use the
existing property names refresher-default-style and refresher-background to
locate the duplicates).

Comment on lines 934 to +938
| 方法名称 | 说明 | 参数 | 最低版本 |
| -------- | ---------------- | ---- | -------- |
| open | 打开 picker 弹框 | - |
| close | 关闭 picker 弹框 | - |
| stopRefresh | 停止下拉刷新 | - | - |
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Methods 表格列数不一致。

open/close 行缺少“最低版本”列,导致表格列数不一致。

🛠️ 建议修改
-| open     | 打开 picker 弹框 | -    |
-| close    | 关闭 picker 弹框 | -    |
+| open     | 打开 picker 弹框 | -    | - |
+| close    | 关闭 picker 弹框 | -    | - |
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 936-936: Table column count
Expected: 4; Actual: 3; Too few cells, row will be missing data

(MD056, table-column-count)


[warning] 937-937: Table column count
Expected: 4; Actual: 3; Too few cells, row will be missing data

(MD056, table-column-count)

🤖 Prompt for AI Agents
In `@docs/component/col-picker.md` around lines 934 - 938, The methods table has
inconsistent column counts: the rows for "open" and "close" are missing the
"最低版本" cell; update the table so each row has four columns (方法名称, 说明, 参数, 最低版本)
by adding the missing cell (e.g. "-" or the correct version) for the "open" and
"close" rows so they match the "stopRefresh" row and keep the table aligned.

Comment on lines +193 to +198
const handleDragStart = (index) => {
canScrollY.value = true
}
const handleDragEnd = (index) => {
canScrollY.value = false
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

滚动锁定示例的 canScrollY 取值反了。

pointerEvents: canScrollY ? 'auto' : 'none' 时,拖拽开始应禁用滚动(false),结束后恢复(true)。

🛠️ 建议修改
 const handleDragStart = (index) => {
-	canScrollY.value = true
+	canScrollY.value = false
 }
 const handleDragEnd = (index) => {
-	canScrollY.value = false
+	canScrollY.value = true
 }
📝 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 handleDragStart = (index) => {
canScrollY.value = true
}
const handleDragEnd = (index) => {
canScrollY.value = false
}
const handleDragStart = (index) => {
canScrollY.value = false
}
const handleDragEnd = (index) => {
canScrollY.value = true
}
🤖 Prompt for AI Agents
In `@docs/component/drag-sort.md` around lines 193 - 198, The scroll-lock flag is
inverted: when pointerEvents uses `canScrollY ? 'auto' : 'none'`, dragging
should disable scrolling; update handleDragStart to set canScrollY.value = false
and handleDragEnd to set canScrollY.value = true so dragging disables pointer
scroll and releasing restores it; locate the canScrollY variable and the
handleDragStart/handleDragEnd functions to make this swap.

@@ -0,0 +1,254 @@
# Guide 漫游组件
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

标题命名不一致。

文档标题为 "Guide 漫游组件",但组件名为 wd-tour,其他地方(如 locale 文件)使用 "Tour 漫游"。建议统一为 "Tour 漫游" 或 "Tour 漫游引导"。

建议修复
-# Guide 漫游组件
+# Tour 漫游引导
📝 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
# Guide 漫游组件
# Tour 漫游引导
🤖 Prompt for AI Agents
In `@docs/component/tour.md` at line 1, 文档标题与组件命名不一致:当前 docs/component/tour.md 标题为
"Guide 漫游组件",但组件名为 `wd-tour` 且其它资源(如 locale 文件)使用 "Tour 漫游"。请将文档标题统一为与组件名和
locale 一致的表述(例如 "Tour 漫游" 或 "Tour 漫游引导"),并在文件头部(当前标题行)替换为选定的统一名称,同时检查并同步任何引用
`wd-tour` 的文档段落或示例以保持一致性。

Comment on lines +186 to +187
| prev | 点击上一步按钮时触发 | `{ oldCurrent: number, current: number, total: number, isUp: number }` |
| next | 点击下一步按钮时触发 | `{ oldCurrent: number, current: number, total: number, isUp: number }` |
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

事件参数 isUp 的类型应为 boolean 而非 number

根据语义,isUp 表示方向(向上/向下),应该是布尔值类型。

建议修复
-| prev | 点击上一步按钮时触发 | `{ oldCurrent: number, current: number, total: number, isUp: number }` |
-| next | 点击下一步按钮时触发 | `{ oldCurrent: number, current: number, total: number, isUp: number }` |
+| prev | 点击上一步按钮时触发 | `{ oldCurrent: number, current: number, total: number, isUp: boolean }` |
+| next | 点击下一步按钮时触发 | `{ oldCurrent: number, current: number, total: number, isUp: boolean }` |
📝 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
| prev | 点击上一步按钮时触发 | `{ oldCurrent: number, current: number, total: number, isUp: number }` |
| next | 点击下一步按钮时触发 | `{ oldCurrent: number, current: number, total: number, isUp: number }` |
| prev | 点击上一步按钮时触发 | `{ oldCurrent: number, current: number, total: number, isUp: boolean }` |
| next | 点击下一步按钮时触发 | `{ oldCurrent: number, current: number, total: number, isUp: boolean }` |
🤖 Prompt for AI Agents
In `@docs/component/tour.md` around lines 186 - 187, Update the event parameter
type for `isUp` in the tour component docs: change `isUp: number` to `isUp:
boolean` in both the `prev` and `next` table rows (the two entries showing `{
oldCurrent: number, current: number, total: number, isUp: number }`) so the
event signature becomes `{ oldCurrent: number, current: number, total: number,
isUp: boolean }`.

Comment on lines +642 to +645
function stopRefresh() {
refreshTriggered.value = false
console.log('[wd-col-picker] 停止刷新状态')
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

移除调试日志语句。

console.log 不应出现在生产代码中。

🧹 建议移除
 function stopRefresh() {
   refreshTriggered.value = false
-  console.log('[wd-col-picker] 停止刷新状态')
 }
📝 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
function stopRefresh() {
refreshTriggered.value = false
console.log('[wd-col-picker] 停止刷新状态')
}
function stopRefresh() {
refreshTriggered.value = false
}
🤖 Prompt for AI Agents
In `@src/uni_modules/wot-design-uni/components/wd-col-picker/wd-col-picker.vue`
around lines 642 - 645, The function stopRefresh currently contains a
console.log debug statement; remove the console.log call from stopRefresh (which
sets refreshTriggered.value = false) so no console output remains in production,
or replace it with a noop or a controlled logger if a trace is required (refer
to stopRefresh and refreshTriggered in wd-col-picker.vue).

// 同步的样式
const syncedStyle = ref({})
// 是否由 handle 触发拖拽
let isHandleTriggered = false
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

isHandleTriggered 作为模块级变量可能导致多实例共享状态问题。

let isHandleTriggered = false 不是响应式的,且在模块级别声明。如果页面上有多个 wd-drag-sort-item 实例,它们会共享这个变量,可能导致拖拽行为异常。

建议改为 ref 或在组件实例范围内声明。

建议修复
-// 是否由 handle 触发拖拽
-let isHandleTriggered = false
+// 是否由 handle 触发拖拽
+const isHandleTriggered = ref(false)

然后更新使用处:

-  if (parent && parent.props.useDragHandle && !isHandleTriggered) {
+  if (parent && parent.props.useDragHandle && !isHandleTriggered.value) {
     return
   }
   // 重置标志位
-  isHandleTriggered = false
+  isHandleTriggered.value = false
 const onHandleTouch = (e: any) => {
   if (parent && parent.props.useDragHandle) {
-    isHandleTriggered = true
+    isHandleTriggered.value = true
     startDragLogic(e)
📝 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
let isHandleTriggered = false
// 是否由 handle 触发拖拽
const isHandleTriggered = ref(false)
🤖 Prompt for AI Agents
In
`@src/uni_modules/wot-design-uni/components/wd-drag-sort-item/wd-drag-sort-item.vue`
at line 61, The module-level variable isHandleTriggered causes shared state
across wd-drag-sort-item instances; move its declaration into the component
instance (e.g. inside setup() as a ref or inside data() as a reactive property)
and replace all uses of isHandleTriggered with the instance-scoped reactive
(using .value if you choose ref) so each wd-drag-sort-item has its own trigger
flag; update any handlers/readers in the component to reference the new
instance-scoped symbol instead of the top-level isHandleTriggered.

Comment on lines +1 to +2
import type { PropType } from 'vue'
import { baseProps, makeBooleanProp, makeNumberProp, makeStringProp, makeArrayProp } from '../common/props'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's read the types.ts file to see the current state
cat -n "src/uni_modules/wot-design-uni/components/wd-tour/types.ts"

Repository: Moonofweisheng/wot-design-uni

Length of output: 3350


🌐 Web query:

Vue 3 ExtractPropTypes vs typeof props type extraction best practices

💡 Result:

What each approach is for

ExtractPropTypes<typeof propsOptions>

Use this when you declare runtime props as an options object and you want a reusable type derived from that object. Vue documents ExtractPropTypes as internal-facing / resolved props (e.g., booleans and props with defaults are treated as always defined). Use ExtractPublicPropTypes when you want the public-facing type (what parents are allowed to pass). [1]

import type { ExtractPropTypes, ExtractPublicPropTypes } from 'vue'

export const propsOptions = {
  foo: String,
  bar: Boolean,
  qux: { type: Number, default: 1 },
  baz: { type: Number, required: true },
} as const

export type MyPropsResolved = ExtractPropTypes<typeof propsOptions>
export type MyPropsPublic   = ExtractPublicPropTypes<typeof propsOptions>

typeof props (where const props = defineProps(...))

Use this when you already have the props variable and you just need the type locally (or to derive helper types), e.g.:

const props = defineProps<Props>()
type LocalProps = typeof props

This is usually not the best “exported API” type, because it ties the type to a particular variable and (depending on how you declared props) represents the component’s resolved props shape rather than “what a parent can pass”.

Best-practice recommendations (Vue 3)

  1. If you’re using type-based props (defineProps<Props>()): export Props directly (or import it where needed). You don’t need extraction utilities in this case. [8]
  2. If you’re using runtime props options (defineProps(propsOptions) / props: { ... }):
    • use ExtractPublicPropTypes<typeof propsOptions> for “parent can pass” types, and
    • use ExtractPropTypes<typeof propsOptions> for “component receives” (resolved) types. [1]
  3. Don’t mix runtime and type-based declarations at the same time—Vue explicitly calls out that you must choose one or the other. [8]
  4. Prefer ExtractPublicPropTypes when documenting/typing a component’s external interface, especially when defaults/boolean props are involved (because ExtractPropTypes intentionally models them as always-defined). [1]

Rule of thumb:

  • “I want the component’s public props contract” → ExtractPublicPropTypes from a propsOptions object. [1]
  • “I’m inside the component and want the resolved props” → ExtractPropTypes (or just rely on props inference). [1]
  • “I used defineProps<Props>()” → export/use Props (don’t extract). [8]

Sources: Vue Utility Types (ExtractPropTypes / ExtractPublicPropTypes) [1], Vue TypeScript + Composition API guide (choose runtime OR type-based; exporting/importing Props) [8].


修正 TourProps 类型导出以提供正确的 TypeScript 类型提示。

第 153 行使用 typeof tourProps 导出的是 props 定义对象的类型,而非组件实际接收的已解析 props 类型。这会导致 TypeScript 使用方获得不准确的类型提示。应改为 ExtractPropTypes<typeof tourProps>,这符合 Vue 3 官方推荐的最佳实践。

修复方案
-import type { PropType } from 'vue'
+import type { PropType, ExtractPropTypes } from 'vue'
-export type TourProps = typeof tourProps
+export type TourProps = ExtractPropTypes<typeof tourProps>
🤖 Prompt for AI Agents
In `@src/uni_modules/wot-design-uni/components/wd-tour/types.ts` around lines 1 -
2, 导出类型 TourProps 当前使用 typeof tourProps(取到的是 props 定义对象)会导致错误的类型提示;请将导出改为使用 Vue
的 ExtractPropTypes 来提取解析后的 props 类型:在此文件中替换现有 TourProps 导出,引用 tourProps(props
定义对象)并用 ExtractPropTypes<typeof tourProps> 作为导出类型,确保导出的类型名仍为 TourProps 以保持向后兼容性。

Comment on lines +416 to +429
// 监听 modelValue 变化,当组件显示时更新系统信息
watch(
() => props.modelValue,
(newVal) => {
if (newVal) {
// 组件显示时重置滚动位置并更新系统信息
oldscrollTop.value = 0
updateSystemInfo()
nextTick(() => {
setTimeout(() => {
updateElementInfo()
emit('update:current', currentIndex.value)
}, 50)
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the wd-tour.vue file
find . -name "wd-tour.vue" -type f

Repository: Moonofweisheng/wot-design-uni

Length of output: 136


🏁 Script executed:

# List the repository structure to understand the project layout
ls -la | head -20

Repository: Moonofweisheng/wot-design-uni

Length of output: 1246


🏁 Script executed:

# Get the file size to determine how to read it
wc -l src/uni_modules/wot-design-uni/components/wd-tour/wd-tour.vue 2>/dev/null || find . -name "wd-tour.vue" -exec wc -l {} \;

Repository: Moonofweisheng/wot-design-uni

Length of output: 138


🏁 Script executed:

# Read the relevant section around lines 416-429
sed -n '410,440p' src/uni_modules/wot-design-uni/components/wd-tour/wd-tour.vue

Repository: Moonofweisheng/wot-design-uni

Length of output: 559


🏁 Script executed:

# Search for all occurrences of oldscrollTop and scrollOffset in the file
rg -n 'oldscrollTop|scrollOffset|pageScrollTo' src/uni_modules/wot-design-uni/components/wd-tour/wd-tour.vue

Repository: Moonofweisheng/wot-design-uni

Length of output: 534


🏁 Script executed:

# Read more context around the pageScrollTo calls to understand the flow
sed -n '275,315p' src/uni_modules/wot-design-uni/components/wd-tour/wd-tour.vue

Repository: Moonofweisheng/wot-design-uni

Length of output: 1392


🏁 Script executed:

# Search for selectViewport usage in the codebase
rg -n 'selectViewport' src/uni_modules/wot-design-uni/

Repository: Moonofweisheng/wot-design-uni

Length of output: 55


🏁 Script executed:

# Check if there are scroll event listeners
rg -n 'onPageScroll|pageScroll|scroll' src/uni_modules/wot-design-uni/components/wd-tour/wd-tour.vue

Repository: Moonofweisheng/wot-design-uni

Length of output: 1189


🌐 Web query:

uni-app selectViewport scrollOffset API documentation

💡 Result:

Official uni-app docs for selectViewport and scrollOffset are under API → UI → Node Info (uni.createSelectorQuery):

  • selectorQuery.selectViewport(): selects the viewport (visible area) so you can query its size/scroll info. [1]
  • nodesRef.scrollOffset(cb): queries a node’s scroll position (scrollLeft, scrollTop); the node must be a scroll-view or viewport. [1]
  • Alternative: nodesRef.fields({ scrollOffset: true }, cb) to include scrollLeft/scrollTop in a fields() result. [1][2]

Example from the docs:

uni.createSelectorQuery()
  .selectViewport()
  .scrollOffset((res) => {
    // res.scrollTop / res.scrollLeft
  })
  .exec();

[1]

Doc pages (official):

https://en.uniapp.dcloud.io/api/ui/nodes-info.html
https://uniapp.dcloud.net.cn/api/ui/nodes-info.html

[1][2]

Sources:
[1] uni-app (EN) Node Info / createSelectorQuery docs
[2] uni-app (CN) Node Info / createSelectorQuery docs


🌐 Web query:

uni-app pageScrollTo how to get current scroll position

💡 Result:

To get the current scroll position in uni-app (when using uni.pageScrollTo), you don’t “read it back” from pageScrollTo. Instead, track it via the page scroll event:

1) Page (window) scroll position

Use onPageScroll, which provides e.scrollTop (pixels scrolled vertically). (en.uniapp.dcloud.io)

Options API

export default {
  data() {
    return { scrollTop: 0 }
  },
  onPageScroll(e) {
    this.scrollTop = e.scrollTop
  },
  methods: {
    toTop() {
      uni.pageScrollTo({ scrollTop: 0, duration: 300 })
    }
  }
}

<script setup>

import { ref } from 'vue'
import { onPageScroll } from '@dcloudio/uni-app'

const scrollTop = ref(0)
onPageScroll((e) => {
  scrollTop.value = e.scrollTop
})

2) If you’re scrolling inside a scroll-view (not the page)

Listen to the scroll-view @scroll event, or query its scroll offset via createSelectorQuery().select(...).scrollOffset(...) (works for scroll-view/viewport). (en.uniapp.dcloud.io)

Citations:


打开已滚动页面时引导位置定位偏差。

当 tour 在已滚动的页面上打开时,设置 oldscrollTop.value = 0 会导致后续位置计算错误。由于 scrollDistance 计算依赖 oldscrollTop 作为基准(如 oldscrollTop.value + res.top - props.padding),若页面已滚动但 oldscrollTop 为 0,计算结果将偏离实际滚动距离,导致引导高亮或弹窗位置错位。

建议在打开时通过 uni.createSelectorQuery().selectViewport().scrollOffset() 读取当前滚动位置后再执行 updateElementInfo()

🔧 建议在打开引导时读取当前 scrollTop
watch(
  () => props.modelValue,
  (newVal) => {
    if (newVal) {
-      // 组件显示时重置滚动位置并更新系统信息
-      oldscrollTop.value = 0
-      updateSystemInfo()
-      nextTick(() => {
-        setTimeout(() => {
-          updateElementInfo()
-          emit('update:current', currentIndex.value)
-        }, 50)
-      })
+      // 组件显示时先读取当前滚动位置再更新引导
+      updateSystemInfo()
+      uni.createSelectorQuery()
+        .selectViewport()
+        .scrollOffset((res: { scrollTop?: number }) => {
+          oldscrollTop.value = res?.scrollTop ?? 0
+          nextTick(() => {
+            setTimeout(() => {
+              updateElementInfo()
+              emit('update:current', currentIndex.value)
+            }, 50)
+          })
+        })
+        .exec()
    }
  },
  {
    immediate: true
  }
)
🤖 Prompt for AI Agents
In `@src/uni_modules/wot-design-uni/components/wd-tour/wd-tour.vue` around lines
416 - 429, The watch handler for props.modelValue sets oldscrollTop.value = 0
which breaks subsequent position calculations; instead, when the tour opens
(inside the watch callback where updateSystemInfo(), nextTick(), and
updateElementInfo() are called), read the current viewport scroll via
uni.createSelectorQuery().selectViewport().scrollOffset() and assign that value
to oldscrollTop.value before calling updateElementInfo() and emitting
update:current; keep updateSystemInfo() as is but replace the hardcoded 0
assignment with the actual scrollTop so scrollDistance calculations
(oldscrollTop.value + res.top - props.padding) use the correct baseline.

expect(wrapper.find('.wd-tour__next').exists()).toBe(false)
expect(wrapper.find('.wd-tour__skip').exists()).toBe(true)
})
// ... existing code ...
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

移除占位符注释。

// ... existing code ... 看起来是模板占位符,不应出现在最终代码中。

建议修复
-  // ... existing code ...
-
   // 测试自定义按钮文字
🤖 Prompt for AI Agents
In `@tests/components/wd-tour.test.ts` at line 175, Remove the template
placeholder comment "// ... existing code ..." from the test file (the
placeholder present in tests/components/wd-tour.test.ts) — delete that line and
either replace it with the intended test code or leave no stray placeholder
comments so the test file contains only real test logic and assertions (look for
the exact string "// ... existing code ..." to locate and remove it).

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 3, 2026

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

A member of the Team first needs to authorize it.

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