89d93c8155
Bug fixes: - #1 Remove duplicate month label from calendar-heatmap (shown twice in records) - #2 Extract shared .modal-overlay to app.wxss (was duplicated in index/records) UX improvements: - #3 Add long-press hint to history list in records page - #4 Add drag handle + catchtouchmove to picker modal - #5 Extend timer auto-navigate-back from 1.5s to 3s - #6 Add clear data option in settings with confirmation dialog Visual/code polish: - #7 Remove 4 unused @keyframes (shimmer, spin) and 5 unused .icon classes - #8 Tab bar inactive colors: hardcoded #888/#666 -> var(--text-secondary) - #10 Split progress-ring observer to avoid redundant setData on size/color changes - #12 Remove meaningless Math.min(planDay, 99) cap
111 lines
2.9 KiB
JavaScript
111 lines
2.9 KiB
JavaScript
const Timer = require('../../utils/timer')
|
|
const storage = require('../../utils/storage')
|
|
const planMod = require('../../utils/plan')
|
|
const themeMod = require('../../utils/theme')
|
|
|
|
Page({
|
|
data: {
|
|
theme: { primary: '#FF6B35', primaryLight: '#FF8C5A', primaryBg: '#FFF3ED', primaryRgb: '255,107,53' },
|
|
themeStyle: '',
|
|
duration: 60,
|
|
remaining: 60,
|
|
status: 'idle',
|
|
isRunning: false,
|
|
isPaused: false,
|
|
isCompleted: false,
|
|
overtime: 0,
|
|
todayTarget: 0,
|
|
planDay: 1,
|
|
isFreeMode: false
|
|
},
|
|
|
|
onLoad(options) {
|
|
themeMod.applyThemeToPage(this)
|
|
|
|
let target
|
|
let planDay = 1
|
|
let isFreeMode = false
|
|
|
|
if (options && options.free) {
|
|
target = parseInt(options.free) || 60
|
|
isFreeMode = true
|
|
} else {
|
|
const settings = storage.getSettings()
|
|
const allRecords = Object.values(storage.getRecords()).flat()
|
|
const plan = planMod.getPlan(settings.planId)
|
|
planDay = planMod.getPlanDay(settings.planId, allRecords)
|
|
target = planMod.getTodayTarget(settings.planId, Math.min(planDay, plan.totalDays))
|
|
}
|
|
|
|
this.setData({
|
|
duration: target,
|
|
remaining: target,
|
|
todayTarget: target,
|
|
planDay: isFreeMode ? 0 : planDay,
|
|
isFreeMode
|
|
})
|
|
|
|
this._timer = new Timer({
|
|
onTick: (tick) => {
|
|
this.setData({
|
|
remaining: tick.remaining > 0 ? tick.remaining : 0,
|
|
overtime: tick.remaining <= 0 ? tick.elapsed - this.data.duration : 0
|
|
})
|
|
},
|
|
onComplete: () => {
|
|
this.setData({ status: 'completed', isCompleted: true })
|
|
if (storage.getSettings().vibrate) {
|
|
try { wx.vibrateLong() } catch (e) {}
|
|
}
|
|
wx.showToast({ title: '目标完成! 太棒了!', icon: 'success', duration: 2000 })
|
|
}
|
|
})
|
|
},
|
|
|
|
onUnload() {
|
|
if (this._timer) this._timer.stop()
|
|
},
|
|
|
|
onStart() {
|
|
if (this.data.isPaused) {
|
|
this._timer.resume()
|
|
this.setData({ isRunning: true, isPaused: false, status: 'running' })
|
|
return
|
|
}
|
|
if (this.data.isRunning) return
|
|
this._timer.start(this.data.duration)
|
|
this.setData({ isRunning: true, status: 'running' })
|
|
},
|
|
|
|
onPause() {
|
|
if (!this.data.isRunning || this.data.isPaused) return
|
|
this._timer.pause()
|
|
this.setData({ isPaused: true, status: 'paused' })
|
|
},
|
|
|
|
onStop() {
|
|
wx.showModal({
|
|
title: '结束训练',
|
|
content: '确定要结束本次训练吗?',
|
|
success: (res) => {
|
|
if (res.confirm) this.finishTraining()
|
|
}
|
|
})
|
|
},
|
|
|
|
finishTraining() {
|
|
const elapsed = this._timer.stop()
|
|
const today = storage.getToday()
|
|
storage.saveRecord({
|
|
date: today,
|
|
duration: elapsed,
|
|
planId: storage.getSettings().planId,
|
|
day: this.data.planDay
|
|
})
|
|
storage.updateStreak(today)
|
|
|
|
wx.showToast({ title: `已记录 ${elapsed}秒`, icon: 'none', duration: 1500 })
|
|
setTimeout(() => { wx.navigateBack() }, 3000)
|
|
}
|
|
})
|