Skip to content

Conversation

@wangyong1997
Copy link

@wangyong1997 wangyong1997 commented Nov 28, 2025

Summary by CodeRabbit

发布说明

  • 新功能

    • 新增倒计时组件,支持多种时间格式(HH:mm:ss、DD天 HH时 mm分 ss秒等)
    • 支持自定义样式和插槽自定义渲染
    • 提供开始、暂停、重置等控制方法
    • 可配置自动开始和更新间隔
  • Chores

    • 版本号更新至 1.1.1

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

@wangyong1997 wangyong1997 requested a review from skiyee as a code owner November 28, 2025 09:24
@netlify
Copy link

netlify bot commented Nov 28, 2025

👷 Deploy request for skiyee-ui pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 89724f7

@coderabbitai
Copy link

coderabbitai bot commented Nov 28, 2025

总体概述

本 PR 新增了一个 Vue 3 倒计时组件(sk-count-down),支持自定义格式、暂停/恢复/重置控制。同时添加了四个演示页面展示基础用法、手动控制、自定义插槽样式和多种时间格式。所有包的版本从 1.1.0 升级至 1.1.1。

变更总览

类别 / 文件 变更说明
版本升级
package.json, examples/uni/package.json, packages/skiyee-uni-ui/package.json
将版本号从 1.1.0 升级到 1.1.1
核心倒计时组件
packages/skiyee-uni-ui/src/components/sk-count-down.vue
新增 Vue 3 单文件组件,包含倒计时逻辑、格式化、事件发射(change/finish)、公共方法(start/pause/reset)等功能。支持时间戳模式和自动启动。
演示页面
examples/uni/src/pages-feedback/count-down/base.vue, control.vue, custom.vue, format.vue
新增四个示例页面,展示倒计时组件的基础用法、手动控制、自定义插槽渲染和多种时间格式
路由及类型定义
examples/uni/src/pages.json, examples/uni/types/pages.d.ts, examples/uni/types/components.d.ts
新增四条倒计时相关路由和对应的全局组件类型声明(SkCountDown、SKCountDown)

代码审查工作量评估

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

需要重点关注的部分:

  • sk-count-down.vue 中的时间计算逻辑和定时器管理,特别是 remain 状态更新、endTime 计算以及暂停/重置的语义实现
  • 格式化函数的逻辑(TimeData 的天/小时/分钟/秒/毫秒的计算和进位行为)
  • 生命周期钩子中的定时器清理和 watch 监听确保资源正确释放

诗歌

🐰 倒计时的秒针在舞动,
从 1.1.0 跳跃到新版本,
格式化的魔法随心转变,
暂停、启动、重置在指尖—
一个精致的计时小精灵诞生了! ⏳✨

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 Pull request title clearly describes the main change: adding a new SkCountDown countdown component, which aligns with the extensive additions of countdown-related Vue components, type definitions, and configuration updates throughout the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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

🧹 Nitpick comments (1)
packages/skiyee-uni-ui/src/components/sk-count-down.vue (1)

149-187: 建议消除重复代码。

tick 函数中的两个分支(time 模式和 target 模式)包含几乎完全相同的逻辑。可以提取公共逻辑以提高代码可维护性。

建议重构为:

 function tick() {
-  if (props.time) {
-    // 倒计时模式(基于 duration)
-    // 注意:这种方式在暂停/恢复时可能需要调整,这里简化处理,每次 tick 减去 interval
-    // 更精确的做法是记录 startTime 和 pauseTime
-    // 但为了兼容 target 模式,我们统一使用 endTime 逻辑会更准,但 time 模式下 pause/start 需要重置 endTime
-
-    // 简单实现:直接减去 interval,可能受执行时间影响导致不准
-    // 优化实现:使用 endTime
-    const now = Date.now()
-    if (now >= endTime) {
-      remain.value = 0
-      pause()
-      emit('finish')
-    }
-    else {
-      remain.value = endTime - now
-      timer = setTimeout(() => {
-        tick()
-      }, props.interval)
-    }
-  }
-  else {
-    // 目标时间模式
-    const now = Date.now()
-    if (now >= endTime) {
-      remain.value = 0
-      pause()
-      emit('finish')
-    }
-    else {
-      remain.value = endTime - now
-      timer = setTimeout(() => {
-        tick()
-      }, props.interval)
-    }
-  }
+  const now = Date.now()
+  if (now >= endTime) {
+    remain.value = 0
+    pause()
+    emit('finish')
+  }
+  else {
+    remain.value = endTime - now
+    timer = setTimeout(() => {
+      tick()
+    }, props.interval)
+  }
   emit('change', timeData.value)
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1d5cd3 and 89724f7.

📒 Files selected for processing (11)
  • examples/uni/package.json (1 hunks)
  • examples/uni/src/pages-feedback/count-down/base.vue (1 hunks)
  • examples/uni/src/pages-feedback/count-down/control.vue (1 hunks)
  • examples/uni/src/pages-feedback/count-down/custom.vue (1 hunks)
  • examples/uni/src/pages-feedback/count-down/format.vue (1 hunks)
  • examples/uni/src/pages.json (2 hunks)
  • examples/uni/types/components.d.ts (1 hunks)
  • examples/uni/types/pages.d.ts (1 hunks)
  • package.json (1 hunks)
  • packages/skiyee-uni-ui/package.json (1 hunks)
  • packages/skiyee-uni-ui/src/components/sk-count-down.vue (1 hunks)
🧰 Additional context used
🪛 ESLint
examples/uni/src/pages-feedback/count-down/control.vue

[error] 2-2: Extra semicolon.

(style/semi)


[error] 4-4: Extra semicolon.

(style/semi)


[error] 5-5: Extra semicolon.

(style/semi)


[error] 8-8: Extra semicolon.

(style/semi)


[error] 12-12: Extra semicolon.

(style/semi)


[error] 16-16: Extra semicolon.

(style/semi)


[error] 23-23: Extra semicolon.

(style/semi)

examples/uni/src/pages-feedback/count-down/custom.vue

[error] 2-2: Extra semicolon.

(style/semi)


[error] 4-4: Extra semicolon.

(style/semi)

🔇 Additional comments (16)
packages/skiyee-uni-ui/package.json (1)

4-4: 版本号升级合理。

从 1.1.0 升级到 1.1.1,符合语义化版本规范,适用于新增 SkCountDown 组件的特性。

examples/uni/package.json (1)

4-4: 版本号升级一致。

示例项目的版本号与主包保持同步,版本管理一致。

examples/uni/src/pages.json (1)

640-655: 页面配置正确。

新增的 4 个倒计时示例页面配置结构正确,与现有页面格式保持一致。

package.json (1)

4-4: 根项目版本号同步更新。

工作区根项目版本号与子包保持同步,符合版本管理最佳实践。

examples/uni/types/pages.d.ts (1)

160-163: 路由类型定义正确。

新增的 4 个路由类型定义与 pages.json 中的页面配置保持一致,确保了类型安全的导航。

examples/uni/src/pages-feedback/count-down/custom.vue (1)

10-29: 自定义样式示例实现良好。

两个自定义样式示例正确使用了作用域插槽,展示了不同的 UI 模式,代码逻辑清晰。

examples/uni/src/pages-feedback/count-down/format.vue (1)

1-38: 格式化示例实现完整。

该页面全面展示了 SkCountDown 组件的多种时间格式选项,包括默认格式、自定义格式、中文格式以及毫秒精度格式,示例清晰且代码结构良好。

examples/uni/types/components.d.ts (1)

14-15: The review comment is based on outdated information that does not match the current file state.

The file examples/uni/types/components.d.ts currently contains 32 lines and shows no SkCountDown or SKCountDown declarations. Lines 14-15 contain SkDialog and SkField respectively. Since this is an auto-generated file (as indicated by the header comment "Generated by vite-plugin-uni-components"), the duplicate component declarations mentioned in the review either:

  • Have already been removed
  • Never existed in this specific file
  • Existed in a different version

No CountDown component variants exist anywhere in the current file, and all components consistently follow the Sk* naming pattern (SkBadge, SkButton, SkDialog, etc.).

examples/uni/src/pages-feedback/count-down/control.vue (1)

27-44: 代码实现正确!

模板部分展示了手动控制倒计时的用法,组件 API 调用正确,演示逻辑清晰。

examples/uni/src/pages-feedback/count-down/base.vue (2)

1-25: 代码实现正确!

脚本部分结构清晰,遵循 Vue 3 Composition API 最佳实践,控制函数实现简洁明了。


27-73: 示例代码完整全面!

模板部分展示了组件的多种使用场景,包括基础用法、自定义格式、手动控制和自定义样式,为用户提供了良好的参考示例。

packages/skiyee-uni-ui/src/components/sk-count-down.vue (5)

1-52: 类型定义完善!

组件的类型定义清晰完整,Props 接口文档详尽,TimeData 接口正确导出供外部使用。


130-147: 初始化逻辑实现正确!

初始化函数正确处理了 timetarget 两种模式,优先级设置合理,并使用 Math.max 防止负值,自动启动逻辑符合预期。


189-209: 控制方法实现正确!

startpausereset 方法的实现逻辑正确,正确处理了暂停/恢复场景中的 endTime 重新计算,防止了重复启动。


211-227: 生命周期管理完善!

组件正确处理了属性变化监听、初始化和清理逻辑,防止了内存泄漏,公共 API 暴露合理。


230-242: 模板设计灵活!

插槽设计提供了丰富的数据访问方式,既暴露了完整的时间数据对象,也提供了独立的属性访问,默认渲染逻辑合理。需要注意的是,此处的插槽数据会受到前面提到的 formatTime 参数突变问题的影响。

Comment on lines +1 to +25
<script setup lang="ts">
import { ref } from 'vue';

const countDown = ref();
const time = ref(5000);

function start() {
countDown.value?.start();
}

function pause() {
countDown.value?.pause();
}

function reset() {
countDown.value?.reset();
}

function onFinish() {
uni.showToast({
title: '倒计时结束',
icon: 'none'
});
}
</script>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

修复代码风格问题。

ESLint 检测到多处额外的分号。请移除这些分号以符合项目的代码风格规范。

建议应用以下修改:

-import { ref } from 'vue';
+import { ref } from 'vue'

-const countDown = ref();
-const time = ref(5000);
+const countDown = ref()
+const time = ref(5000)

 function start() {
-  countDown.value?.start();
+  countDown.value?.start()
 }

 function pause() {
-  countDown.value?.pause();
+  countDown.value?.pause()
 }

 function reset() {
-  countDown.value?.reset();
+  countDown.value?.reset()
 }

 function onFinish() {
   uni.showToast({
     title: '倒计时结束',
     icon: 'none'
-  });
+  })
 }
📝 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
<script setup lang="ts">
import { ref } from 'vue';
const countDown = ref();
const time = ref(5000);
function start() {
countDown.value?.start();
}
function pause() {
countDown.value?.pause();
}
function reset() {
countDown.value?.reset();
}
function onFinish() {
uni.showToast({
title: '倒计时结束',
icon: 'none'
});
}
</script>
<script setup lang="ts">
import { ref } from 'vue'
const countDown = ref()
const time = ref(5000)
function start() {
countDown.value?.start()
}
function pause() {
countDown.value?.pause()
}
function reset() {
countDown.value?.reset()
}
function onFinish() {
uni.showToast({
title: '倒计时结束',
icon: 'none'
})
}
</script>
🧰 Tools
🪛 ESLint

[error] 2-2: Extra semicolon.

(style/semi)


[error] 4-4: Extra semicolon.

(style/semi)


[error] 5-5: Extra semicolon.

(style/semi)


[error] 8-8: Extra semicolon.

(style/semi)


[error] 12-12: Extra semicolon.

(style/semi)


[error] 16-16: Extra semicolon.

(style/semi)


[error] 23-23: Extra semicolon.

(style/semi)

🤖 Prompt for AI Agents
In examples/uni/src/pages-feedback/count-down/control.vue around lines 1 to 25,
ESLint flagged extraneous semicolons; remove the unnecessary semicolons at the
ends of the import statement, ref declarations, and all function definitions so
the file follows project code-style (no trailing semicolons) while keeping code
structure and behavior unchanged.

@@ -0,0 +1,31 @@
<script setup lang="ts">
import { ref } from 'vue';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

修复代码风格问题。

ESLint 检测到多余的分号。为保持代码风格一致性,建议移除 import 语句末尾的分号。

应用以下修改:

-import { ref } from 'vue';
+import { ref } from 'vue'
📝 Committable suggestion

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

Suggested change
import { ref } from 'vue';
import { ref } from 'vue'
🧰 Tools
🪛 ESLint

[error] 2-2: Extra semicolon.

(style/semi)

🤖 Prompt for AI Agents
在 examples/uni/src/pages-feedback/count-down/custom.vue 第2行,import 语句末尾包含多余的分号导致
ESLint 报告代码风格问题;请删除该行结尾的分号以符合项目的代码风格规则并通过 lint 检查,保持文件中所有 import 语句一致性。

<script setup lang="ts">
import { ref } from 'vue';

const time = ref(30 * 60 * 1000); // 30 minutes
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

修复代码风格问题。

建议移除此行末尾的分号,与项目代码风格保持一致。

应用以下修改:

-const time = ref(30 * 60 * 1000); // 30 minutes
+const time = ref(30 * 60 * 1000) // 30 minutes
📝 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 time = ref(30 * 60 * 1000); // 30 minutes
const time = ref(30 * 60 * 1000) // 30 minutes
🧰 Tools
🪛 ESLint

[error] 4-4: Extra semicolon.

(style/semi)

🤖 Prompt for AI Agents
In examples/uni/src/pages-feedback/count-down/custom.vue around line 4, the
statement "const time = ref(30 * 60 * 1000);" has a trailing semicolon that
violates the project code style; remove the semicolon at the end of the line so
it reads without the semicolon and ensure the file follows the project's
no-semicolon style.

Comment on lines +93 to +128
function formatTime(format: string | ((time: TimeData) => string), time: TimeData): string {
if (typeof format === 'function') {
return format(time)
}

let text = format

if (text.includes('DD')) {
text = text.replace('DD', fixedZero(time.days))
}
else {
// 如果没有 DD,将天数转换为小时
time.hours += time.days * 24
}

if (text.includes('HH')) {
text = text.replace('HH', fixedZero(time.hours))
}
if (text.includes('mm')) {
text = text.replace('mm', fixedZero(time.minutes))
}
if (text.includes('ss')) {
text = text.replace('ss', fixedZero(time.seconds))
}
if (text.includes('SSS')) {
text = text.replace('SSS', fixedZero(time.milliseconds).padStart(3, '0'))
}
if (text.includes('SS')) {
text = text.replace('SS', fixedZero(Math.floor(time.milliseconds / 10)))
}
if (text.includes('S')) {
text = text.replace('S', `${Math.floor(time.milliseconds / 100)}`)
}

return text
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

修复参数对象突变导致的数据不一致问题。

formatTime 函数在第 105 行修改了传入的 time 对象(time.hours += time.days * 24)。由于 timeData 是一个 computed 属性,其值会被 Vue 缓存,当 formatTime 修改缓存的对象后,会影响模板中插槽接收到的数据。

示例问题场景:

  • 当格式为 'HH:mm:ss'(不包含 DD)且剩余 2 天 3 小时时
  • formatTime 会将 time.hours 修改为 51(3 + 2*24)
  • 插槽会接收到 { days: 2, hours: 51 },这在语义上是不一致的
  • 用户在自定义插槽中可能会显示 "2 天 51 小时",造成困惑

建议使用局部变量而不是修改参数对象:

 function formatTime(format: string | ((time: TimeData) => string), time: TimeData): string {
   if (typeof format === 'function') {
     return format(time)
   }
 
   let text = format
+  let { days, hours, minutes, seconds, milliseconds } = time
 
   if (text.includes('DD')) {
-    text = text.replace('DD', fixedZero(time.days))
+    text = text.replace('DD', fixedZero(days))
   }
   else {
     // 如果没有 DD,将天数转换为小时
-    time.hours += time.days * 24
+    hours = hours + days * 24
   }
 
   if (text.includes('HH')) {
-    text = text.replace('HH', fixedZero(time.hours))
+    text = text.replace('HH', fixedZero(hours))
   }
   if (text.includes('mm')) {
-    text = text.replace('mm', fixedZero(time.minutes))
+    text = text.replace('mm', fixedZero(minutes))
   }
   if (text.includes('ss')) {
-    text = text.replace('ss', fixedZero(time.seconds))
+    text = text.replace('ss', fixedZero(seconds))
   }
   if (text.includes('SSS')) {
-    text = text.replace('SSS', fixedZero(time.milliseconds).padStart(3, '0'))
+    text = text.replace('SSS', fixedZero(milliseconds).padStart(3, '0'))
   }
   if (text.includes('SS')) {
-    text = text.replace('SS', fixedZero(Math.floor(time.milliseconds / 10)))
+    text = text.replace('SS', fixedZero(Math.floor(milliseconds / 10)))
   }
   if (text.includes('S')) {
-    text = text.replace('S', `${Math.floor(time.milliseconds / 100)}`)
+    text = text.replace('S', `${Math.floor(milliseconds / 100)}`)
   }
 
   return text
 }
📝 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 formatTime(format: string | ((time: TimeData) => string), time: TimeData): string {
if (typeof format === 'function') {
return format(time)
}
let text = format
if (text.includes('DD')) {
text = text.replace('DD', fixedZero(time.days))
}
else {
// 如果没有 DD,将天数转换为小时
time.hours += time.days * 24
}
if (text.includes('HH')) {
text = text.replace('HH', fixedZero(time.hours))
}
if (text.includes('mm')) {
text = text.replace('mm', fixedZero(time.minutes))
}
if (text.includes('ss')) {
text = text.replace('ss', fixedZero(time.seconds))
}
if (text.includes('SSS')) {
text = text.replace('SSS', fixedZero(time.milliseconds).padStart(3, '0'))
}
if (text.includes('SS')) {
text = text.replace('SS', fixedZero(Math.floor(time.milliseconds / 10)))
}
if (text.includes('S')) {
text = text.replace('S', `${Math.floor(time.milliseconds / 100)}`)
}
return text
}
function formatTime(format: string | ((time: TimeData) => string), time: TimeData): string {
if (typeof format === 'function') {
return format(time)
}
let text = format
let { days, hours, minutes, seconds, milliseconds } = time
if (text.includes('DD')) {
text = text.replace('DD', fixedZero(days))
}
else {
// 如果没有 DD,将天数转换为小时
hours = hours + days * 24
}
if (text.includes('HH')) {
text = text.replace('HH', fixedZero(hours))
}
if (text.includes('mm')) {
text = text.replace('mm', fixedZero(minutes))
}
if (text.includes('ss')) {
text = text.replace('ss', fixedZero(seconds))
}
if (text.includes('SSS')) {
text = text.replace('SSS', fixedZero(milliseconds).padStart(3, '0'))
}
if (text.includes('SS')) {
text = text.replace('SS', fixedZero(Math.floor(milliseconds / 10)))
}
if (text.includes('S')) {
text = text.replace('S', `${Math.floor(milliseconds / 100)}`)
}
return text
}
🤖 Prompt for AI Agents
In packages/skiyee-uni-ui/src/components/sk-count-down.vue around lines 93 to
128, formatTime currently mutates the incoming time object at line ~105
(time.hours += time.days * 24) which causes Vue computed-cached data to change
and leads to inconsistent values downstream; fix by not mutating the parameter:
create local copies (e.g., const days = time.days, const hours = time.hours +
(text.includes('DD') ? 0 : days * 24) or compute a local hours variable when DD
is absent) and use those locals for replacements (replace HH with
fixedZero(hours)) while leaving the original time object untouched; ensure
millisecond formatting still uses the same local values when computing S/SS/SSS.

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