From 025050dde0143fd4f13a2a25743c62130922c9bc Mon Sep 17 00:00:00 2001 From: RiseForever Date: Sat, 29 Aug 2026 18:13:48 +0800 Subject: [PATCH] 1.0.1 - Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将时间判断方法改为时间戳判断,修复息屏后无法计时的问题。 --- .vscode/mcp.json | 45 +++++++++++ src/manifest.json | 4 +- src/pages/5/home/home.ux | 5 ++ src/pages/5/picker/picker.ux | 5 ++ src/pages/5/timer/timer.ux | 119 ++++++++++++++++++++++-------- src/pages/s4-picker/s4-picker.ux | 5 ++ src/pages/s4-timer/s4-timer.ux | 123 +++++++++++++++++++++++-------- src/pages/s4/s4.ux | 5 ++ src/pages/s5/home/home.ux | 5 ++ src/pages/s5/picker/picker.ux | 5 ++ src/pages/s5/timer/timer.ux | 118 +++++++++++++++++++++-------- 11 files changed, 348 insertions(+), 91 deletions(-) create mode 100644 .vscode/mcp.json diff --git a/.vscode/mcp.json b/.vscode/mcp.json new file mode 100644 index 0000000..c9e0283 --- /dev/null +++ b/.vscode/mcp.json @@ -0,0 +1,45 @@ +{ + "mcpServers": { + "velajs": { + "command": "npx", + "args": [ + "-y", + "velajs-mcp" + ], + "autoApprove": [ + "list_devices", + "select_devices", + "get_debug_status", + "start_debug_cli", + "start_debug_ide", + "stop_debug", + "close_all_emulators", + "take_screenshot", + "list_emulators", + "get_snapshot", + "get_element", + "get_console_logs", + "clear_console_logs", + "execute_script", + "get_network_requests", + "clear_network_requests", + "tap", + "double_tap", + "scroll", + "swipe", + "long_press", + "send_key", + "input_text", + "get_storage", + "get_project_info", + "build_project", + "get_build_settings", + "select_build_setting", + "get_output_logs", + "get_emulator_logs", + "get_device_log", + "navigate" + ] + } + } +} \ No newline at end of file diff --git a/src/manifest.json b/src/manifest.json index fba65e4..2a855a9 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,8 +1,8 @@ { "package": "cn.seedsoft.tmtclock", "name": "番茄钟", - "versionName": "1.0.0", - "versionCode": 1, + "versionName": "1.0.1", + "versionCode": 2, "minPlatformVersion": 1000, "icon": "/common/logo.png", "deviceTypeList": [ diff --git a/src/pages/5/home/home.ux b/src/pages/5/home/home.ux index 8869c30..2456fce 100644 --- a/src/pages/5/home/home.ux +++ b/src/pages/5/home/home.ux @@ -41,6 +41,11 @@ export default { } }, + onShow() { + // 息屏期间 setInterval 被挂起,亮屏后立刻补一次时间 + this.updateTime() + }, + updateTime() { const now = new Date() const pad = (value) => (value < 10 ? `0${value}` : `${value}`) diff --git a/src/pages/5/picker/picker.ux b/src/pages/5/picker/picker.ux index 5a54557..e91ed32 100644 --- a/src/pages/5/picker/picker.ux +++ b/src/pages/5/picker/picker.ux @@ -57,6 +57,11 @@ export default { } }, + onShow() { + // 息屏期间 setInterval 被挂起,亮屏后立刻补一次时间 + this.updateTime() + }, + updateTime() { const now = new Date() const pad = (value) => (value < 10 ? `0${value}` : `${value}`) diff --git a/src/pages/5/timer/timer.ux b/src/pages/5/timer/timer.ux index ac2a863..acc8ecb 100644 --- a/src/pages/5/timer/timer.ux +++ b/src/pages/5/timer/timer.ux @@ -43,8 +43,13 @@ export default { paused: false, isResting: false, focusDurationSeconds: 1200, - focusTotalSeconds: 0, - restTotalSeconds: 0, + restDurationSeconds: 300, + // 息屏后 setInterval 会被系统挂起,所有计时以时间戳为准 + phaseEndAt: 0, + lastCountedAt: 0, + lastPersistAt: 0, + focusTotalMs: 0, + restTotalMs: 0, focusTotalText: "00:00:00", restTotalText: "00:00:00" }, @@ -56,51 +61,88 @@ export default { this.clockTimer = setInterval(() => this.updateTime(), 1000) }, + onShow() { + // 亮屏后立刻按时间戳补齐息屏期间的进度 + this.updateTime() + this.sync() + }, + onDestroy() { clearInterval(this.clockTimer) clearInterval(this.countdownTimer) + this.countdownTimer = null + if (!this.paused) { + this.accumulate(Date.now()) + this.updateStats() + } + this.persistStats(true) }, startCountdown(seconds) { - clearInterval(this.countdownTimer) this.focusDurationSeconds = seconds > 0 ? seconds : 1200 - this.remaining = this.focusDurationSeconds - this.isResting = false - this.paused = false - this.updateCountdown() - this.countdownTimer = setInterval(() => this.tick(), 1000) + this.beginPhase(false, this.focusDurationSeconds, Date.now()) + this.ensureTicking() }, - startRest() { - this.remaining = 300 - this.isResting = true + startRest(startAt) { + this.beginPhase(true, this.restDurationSeconds, startAt) this.paused = true + }, + + beginPhase(isResting, durationSeconds, startAt) { + this.isResting = isResting + this.paused = false + this.phaseEndAt = startAt + durationSeconds * 1000 + this.lastCountedAt = startAt + this.remaining = durationSeconds this.updateCountdown() }, + ensureTicking() { + if (this.countdownTimer === null) { + this.countdownTimer = setInterval(() => this.sync(), 1000) + } + }, + updateTime() { const now = new Date() const pad = (value) => (value < 10 ? `0${value}` : `${value}`) this.time = `${pad(now.getHours())}:${pad(now.getMinutes())}` }, - tick() { - if (this.paused || this.remaining <= 0) return - this.remaining -= 1 - if (this.isResting) this.restTotalSeconds += 1 - else this.focusTotalSeconds += 1 - this.updateStats() - this.persistStats() - this.updateCountdown() - if (this.remaining === 0) { + // 把 lastCountedAt 到 untilAt 之间的时间计入当前阶段的累计值 + accumulate(untilAt) { + const delta = untilAt - this.lastCountedAt + if (delta <= 0) return + if (this.isResting) this.restTotalMs += delta + else this.focusTotalMs += delta + this.lastCountedAt = untilAt + }, + + sync() { + // phaseEndAt 为 0 表示还没开始计时 + if (this.paused || this.phaseEndAt === 0) return + const now = Date.now() + // 息屏期间可能已跨过一个或多个阶段,逐段结算 + while (now >= this.phaseEndAt) { + this.accumulate(this.phaseEndAt) + const endedAt = this.phaseEndAt if (this.isResting) { vibrator.vibrate({ mode: "short" }) - this.startCountdown(this.focusDurationSeconds) + this.beginPhase(false, this.focusDurationSeconds, endedAt) } else { vibrator.vibrate({ mode: "long" }) - this.startRest() + this.startRest(endedAt) + break } } + if (!this.paused) { + this.accumulate(now) + this.remaining = Math.max(0, Math.round((this.phaseEndAt - now) / 1000)) + this.updateCountdown() + } + this.updateStats() + this.persistStats(false) }, durationSeconds(config) { @@ -117,8 +159,8 @@ export default { }, updateStats() { - this.focusTotalText = this.formatDuration(this.focusTotalSeconds) - this.restTotalText = this.formatDuration(this.restTotalSeconds) + this.focusTotalText = this.formatDuration(Math.floor(this.focusTotalMs / 1000)) + this.restTotalText = this.formatDuration(Math.floor(this.restTotalMs / 1000)) }, formatDuration(totalSeconds) { @@ -136,8 +178,9 @@ export default { try { const stats = JSON.parse(data.text) if (stats.date === this.today()) { - this.focusTotalSeconds = parseInt(stats.focus, 10) || 0 - this.restTotalSeconds = parseInt(stats.rest, 10) || 0 + // 读取是异步的,期间可能已经累计了一点时间,用加法避免被覆盖 + this.focusTotalMs += (parseInt(stats.focus, 10) || 0) * 1000 + this.restTotalMs += (parseInt(stats.rest, 10) || 0) * 1000 } } catch (error) {} this.updateStats() @@ -146,13 +189,17 @@ export default { }) }, - persistStats() { + persistStats(force) { + const now = Date.now() + // 每秒写盘没有必要,节流到 5 秒;退出/暂停时强制落盘 + if (!force && now - this.lastPersistAt < 5000) return + this.lastPersistAt = now file.writeText({ uri: "internal://files/focus-stats.json", text: JSON.stringify({ date: this.today(), - focus: this.focusTotalSeconds, - rest: this.restTotalSeconds + focus: Math.floor(this.focusTotalMs / 1000), + rest: Math.floor(this.restTotalMs / 1000) }) }) }, @@ -164,7 +211,19 @@ export default { }, togglePause() { - this.paused = !this.paused + if (this.paused) { + // 恢复:把剩余秒数换算成新的结束时间戳 + const now = Date.now() + this.phaseEndAt = now + this.remaining * 1000 + this.lastCountedAt = now + this.paused = false + this.ensureTicking() + } else { + this.accumulate(Date.now()) + this.paused = true + this.updateStats() + this.persistStats(true) + } }, stopTimer() { diff --git a/src/pages/s4-picker/s4-picker.ux b/src/pages/s4-picker/s4-picker.ux index d9dcb21..67351a3 100644 --- a/src/pages/s4-picker/s4-picker.ux +++ b/src/pages/s4-picker/s4-picker.ux @@ -59,6 +59,11 @@ export default { } }, + onShow() { + // 息屏期间 setInterval 被挂起,亮屏后立刻补一次时间 + this.updateTime() + }, + updateTime() { const now = new Date() const pad = (value) => (value < 10 ? `0${value}` : `${value}`) diff --git a/src/pages/s4-timer/s4-timer.ux b/src/pages/s4-timer/s4-timer.ux index 0a604c9..20c2981 100644 --- a/src/pages/s4-timer/s4-timer.ux +++ b/src/pages/s4-timer/s4-timer.ux @@ -54,8 +54,13 @@ export default { paused: false, isResting: false, focusDurationSeconds: 1200, - focusTotalSeconds: 0, - restTotalSeconds: 0, + restDurationSeconds: 300, + // 息屏后 setInterval 会被系统挂起,所有计时以时间戳为准 + phaseEndAt: 0, + lastCountedAt: 0, + lastPersistAt: 0, + focusTotalMs: 0, + restTotalMs: 0, focusTotalText: "00:00:00", restTotalText: "00:00:00" }, @@ -72,9 +77,21 @@ export default { this.clockTimer = setInterval(() => this.updateTime(), 1000) }, + onShow() { + // 亮屏后立刻按时间戳补齐息屏期间的进度 + this.updateTime() + this.sync() + }, + onDestroy() { clearInterval(this.clockTimer) clearInterval(this.countdownTimer) + this.countdownTimer = null + if (!this.paused) { + this.accumulate(Date.now()) + this.updateStats() + } + this.persistStats(true) }, loadDuration() { @@ -86,54 +103,81 @@ export default { }, startCountdown(seconds) { - clearInterval(this.countdownTimer) this.focusDurationSeconds = seconds > 0 ? seconds : 1200 - this.remaining = this.focusDurationSeconds - this.isResting = false - this.paused = false - this.updateCountdown() - this.countdownTimer = setInterval(() => this.tick(), 1000) + this.beginPhase(false, this.focusDurationSeconds, Date.now()) + this.ensureTicking() }, - startRest() { - this.remaining = 5 * 60 - this.isResting = true + startRest(startAt) { + this.beginPhase(true, this.restDurationSeconds, startAt) this.paused = true + }, + + beginPhase(isResting, durationSeconds, startAt) { + this.isResting = isResting + this.paused = false + this.phaseEndAt = startAt + durationSeconds * 1000 + this.lastCountedAt = startAt + this.remaining = durationSeconds this.updateCountdown() }, + ensureTicking() { + if (this.countdownTimer === null) { + this.countdownTimer = setInterval(() => this.sync(), 1000) + } + }, + updateTime() { const now = new Date() const pad = (value) => (value < 10 ? `0${value}` : `${value}`) this.time = `${pad(now.getHours())}:${pad(now.getMinutes())}` }, - tick() { - if (this.paused || this.remaining <= 0) { + // 把 lastCountedAt 到 untilAt 之间的时间计入当前阶段的累计值 + accumulate(untilAt) { + const delta = untilAt - this.lastCountedAt + if (delta <= 0) { return } - this.remaining -= 1 if (this.isResting) { - this.restTotalSeconds += 1 + this.restTotalMs += delta } else { - this.focusTotalSeconds += 1 + this.focusTotalMs += delta } - this.updateStats() - this.persistStats() - this.updateCountdown() - if (this.remaining === 0) { + this.lastCountedAt = untilAt + }, + + sync() { + // phaseEndAt 为 0 表示时长还没读出来,尚未开始计时 + if (this.paused || this.phaseEndAt === 0) { + return + } + const now = Date.now() + // 息屏期间可能已跨过一个或多个阶段,逐段结算 + while (now >= this.phaseEndAt) { + this.accumulate(this.phaseEndAt) + const endedAt = this.phaseEndAt if (this.isResting) { vibrator.vibrate({ mode: "short" }) - this.startCountdown(this.focusDurationSeconds) + this.beginPhase(false, this.focusDurationSeconds, endedAt) } else { vibrator.vibrate({ mode: "long" }) - this.startRest() + this.startRest(endedAt) + break } } + if (!this.paused) { + this.accumulate(now) + this.remaining = Math.max(0, Math.round((this.phaseEndAt - now) / 1000)) + this.updateCountdown() + } + this.updateStats() + this.persistStats(false) }, updateCountdown() { @@ -154,8 +198,8 @@ export default { }, updateStats() { - this.focusTotalText = this.formatDuration(this.focusTotalSeconds) - this.restTotalText = this.formatDuration(this.restTotalSeconds) + this.focusTotalText = this.formatDuration(Math.floor(this.focusTotalMs / 1000)) + this.restTotalText = this.formatDuration(Math.floor(this.restTotalMs / 1000)) }, formatDuration(totalSeconds) { @@ -173,8 +217,9 @@ export default { try { const stats = JSON.parse(data.text) if (stats.date === this.today()) { - this.focusTotalSeconds = parseInt(stats.focus, 10) || 0 - this.restTotalSeconds = parseInt(stats.rest, 10) || 0 + // 读取是异步的,期间可能已经累计了一点时间,用加法避免被覆盖 + this.focusTotalMs += (parseInt(stats.focus, 10) || 0) * 1000 + this.restTotalMs += (parseInt(stats.rest, 10) || 0) * 1000 } this.updateStats() } catch (error) { @@ -185,13 +230,19 @@ export default { }) }, - persistStats() { + persistStats(force) { + const now = Date.now() + // 每秒写盘没有必要,节流到 5 秒;退出/切阶段时强制落盘 + if (!force && now - this.lastPersistAt < 5000) { + return + } + this.lastPersistAt = now file.writeText({ uri: "internal://files/focus-stats.json", text: JSON.stringify({ date: this.today(), - focus: this.focusTotalSeconds, - rest: this.restTotalSeconds + focus: Math.floor(this.focusTotalMs / 1000), + rest: Math.floor(this.restTotalMs / 1000) }) }) }, @@ -203,7 +254,19 @@ export default { }, togglePause() { - this.paused = !this.paused + if (this.paused) { + // 恢复:把剩余秒数换算成新的结束时间戳 + const now = Date.now() + this.phaseEndAt = now + this.remaining * 1000 + this.lastCountedAt = now + this.paused = false + this.ensureTicking() + } else { + this.accumulate(Date.now()) + this.paused = true + this.updateStats() + this.persistStats(true) + } }, stopTimer() { diff --git a/src/pages/s4/s4.ux b/src/pages/s4/s4.ux index bf40d36..3d4faa2 100644 --- a/src/pages/s4/s4.ux +++ b/src/pages/s4/s4.ux @@ -41,6 +41,11 @@ export default { } }, + onShow() { + // 息屏期间 setInterval 被挂起,亮屏后立刻补一次时间 + this.updateTime() + }, + updateTime() { const now = new Date() const pad = (value) => (value < 10 ? `0${value}` : `${value}`) diff --git a/src/pages/s5/home/home.ux b/src/pages/s5/home/home.ux index ff4c3ed..08efb12 100644 --- a/src/pages/s5/home/home.ux +++ b/src/pages/s5/home/home.ux @@ -41,6 +41,11 @@ export default { } }, + onShow() { + // 息屏期间 setInterval 被挂起,亮屏后立刻补一次时间 + this.updateTime() + }, + updateTime() { const now = new Date() const pad = (value) => (value < 10 ? `0${value}` : `${value}`) diff --git a/src/pages/s5/picker/picker.ux b/src/pages/s5/picker/picker.ux index c945a85..038be3a 100644 --- a/src/pages/s5/picker/picker.ux +++ b/src/pages/s5/picker/picker.ux @@ -57,6 +57,11 @@ export default { } }, + onShow() { + // 息屏期间 setInterval 被挂起,亮屏后立刻补一次时间 + this.updateTime() + }, + updateTime() { const now = new Date() const pad = (value) => (value < 10 ? `0${value}` : `${value}`) diff --git a/src/pages/s5/timer/timer.ux b/src/pages/s5/timer/timer.ux index 484a8d1..5ee589b 100644 --- a/src/pages/s5/timer/timer.ux +++ b/src/pages/s5/timer/timer.ux @@ -35,7 +35,10 @@ export default { private: { time: "00:00", countdown: "20:00", clockTimer: null, countdownTimer: null, remaining: 1200, paused: false, isResting: false, focusDurationSeconds: 1200, - focusTotalSeconds: 0, restTotalSeconds: 0, + restDurationSeconds: 300, + // 息屏后 setInterval 会被系统挂起,所有计时以时间戳为准 + phaseEndAt: 0, lastCountedAt: 0, lastPersistAt: 0, + focusTotalMs: 0, restTotalMs: 0, focusTotalText: "00:00:00", restTotalText: "00:00:00" }, @@ -47,51 +50,88 @@ export default { this.clockTimer = setInterval(() => this.updateTime(), 1000) }, + onShow() { + // 亮屏后立刻按时间戳补齐息屏期间的进度 + this.updateTime() + this.sync() + }, + onDestroy() { clearInterval(this.clockTimer) clearInterval(this.countdownTimer) + this.countdownTimer = null + if (!this.paused) { + this.accumulate(Date.now()) + this.updateStats() + } + this.persistStats(true) }, startCountdown(seconds) { - clearInterval(this.countdownTimer) this.focusDurationSeconds = seconds > 0 ? seconds : 1200 - this.remaining = this.focusDurationSeconds - this.isResting = false - this.paused = false - this.updateCountdown() - this.countdownTimer = setInterval(() => this.tick(), 1000) + this.beginPhase(false, this.focusDurationSeconds, Date.now()) + this.ensureTicking() }, - startRest() { - this.remaining = 300 - this.isResting = true + startRest(startAt) { + this.beginPhase(true, this.restDurationSeconds, startAt) this.paused = true + }, + + beginPhase(isResting, durationSeconds, startAt) { + this.isResting = isResting + this.paused = false + this.phaseEndAt = startAt + durationSeconds * 1000 + this.lastCountedAt = startAt + this.remaining = durationSeconds this.updateCountdown() }, + ensureTicking() { + if (this.countdownTimer === null) { + this.countdownTimer = setInterval(() => this.sync(), 1000) + } + }, + updateTime() { const now = new Date() const pad = (value) => (value < 10 ? `0${value}` : `${value}`) this.time = `${pad(now.getHours())}:${pad(now.getMinutes())}` }, - tick() { - if (this.paused || this.remaining <= 0) return - this.remaining -= 1 - if (this.isResting) this.restTotalSeconds += 1 - else this.focusTotalSeconds += 1 - this.updateStats() - this.persistStats() - this.updateCountdown() - if (this.remaining === 0) { + // 把 lastCountedAt 到 untilAt 之间的时间计入当前阶段的累计值 + accumulate(untilAt) { + const delta = untilAt - this.lastCountedAt + if (delta <= 0) return + if (this.isResting) this.restTotalMs += delta + else this.focusTotalMs += delta + this.lastCountedAt = untilAt + }, + + sync() { + // phaseEndAt 为 0 表示还没开始计时 + if (this.paused || this.phaseEndAt === 0) return + const now = Date.now() + // 息屏期间可能已跨过一个或多个阶段,逐段结算 + while (now >= this.phaseEndAt) { + this.accumulate(this.phaseEndAt) + const endedAt = this.phaseEndAt if (this.isResting) { vibrator.vibrate({ mode: "short" }) - this.startCountdown(this.focusDurationSeconds) + this.beginPhase(false, this.focusDurationSeconds, endedAt) } else { vibrator.vibrate({ mode: "long" }) - this.startRest() + this.startRest(endedAt) + break } } + if (!this.paused) { + this.accumulate(now) + this.remaining = Math.max(0, Math.round((this.phaseEndAt - now) / 1000)) + this.updateCountdown() + } + this.updateStats() + this.persistStats(false) }, durationSeconds(config) { @@ -108,8 +148,8 @@ export default { }, updateStats() { - this.focusTotalText = this.formatDuration(this.focusTotalSeconds) - this.restTotalText = this.formatDuration(this.restTotalSeconds) + this.focusTotalText = this.formatDuration(Math.floor(this.focusTotalMs / 1000)) + this.restTotalText = this.formatDuration(Math.floor(this.restTotalMs / 1000)) }, formatDuration(totalSeconds) { @@ -127,8 +167,9 @@ export default { try { const stats = JSON.parse(data.text) if (stats.date === this.today()) { - this.focusTotalSeconds = parseInt(stats.focus, 10) || 0 - this.restTotalSeconds = parseInt(stats.rest, 10) || 0 + // 读取是异步的,期间可能已经累计了一点时间,用加法避免被覆盖 + this.focusTotalMs += (parseInt(stats.focus, 10) || 0) * 1000 + this.restTotalMs += (parseInt(stats.rest, 10) || 0) * 1000 } } catch (error) {} this.updateStats() @@ -137,13 +178,17 @@ export default { }) }, - persistStats() { + persistStats(force) { + const now = Date.now() + // 每秒写盘没有必要,节流到 5 秒;退出/暂停时强制落盘 + if (!force && now - this.lastPersistAt < 5000) return + this.lastPersistAt = now file.writeText({ uri: "internal://files/focus-stats.json", text: JSON.stringify({ date: this.today(), - focus: this.focusTotalSeconds, - rest: this.restTotalSeconds + focus: Math.floor(this.focusTotalMs / 1000), + rest: Math.floor(this.restTotalMs / 1000) }) }) }, @@ -154,7 +199,22 @@ export default { return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}` }, - togglePause() { this.paused = !this.paused }, + togglePause() { + if (this.paused) { + // 恢复:把剩余秒数换算成新的结束时间戳 + const now = Date.now() + this.phaseEndAt = now + this.remaining * 1000 + this.lastCountedAt = now + this.paused = false + this.ensureTicking() + } else { + this.accumulate(Date.now()) + this.paused = true + this.updateStats() + this.persistStats(true) + } + }, + stopTimer() { router.back() } }