diff --git a/pages/timer/timer.js b/pages/timer/timer.js index a5843e2..4f40ae6 100644 --- a/pages/timer/timer.js +++ b/pages/timer/timer.js @@ -25,6 +25,7 @@ Page({ confettiPieces: [], // Completion modal (full-screen summary after training ends) showCompletion: false, + showStopConfirm: false, // 自定义"结束训练"确认弹窗,替代 wx.showModal completionElapsed: 0, completionTarget: 0, completionOvertime: 0, @@ -260,13 +261,19 @@ Page({ }, onStop() { - wx.showModal({ - title: '结束训练', - content: '确定要结束本次训练吗?', - success: (res) => { - if (res.confirm) this.finishTraining() - } - }) + // 用自定义确认弹窗替代 wx.showModal: + // 系统原生 modal 在 WebView stacking context 下 hit-test 偶发不稳, + // 自定义 modal + catchtouchmove 与现有 completion-mask 同模式,行为更可靠。 + this.setData({ showStopConfirm: true }) + }, + + onStopCancel() { + this.setData({ showStopConfirm: false }) + }, + + onStopConfirm() { + this.setData({ showStopConfirm: false }) + this.finishTraining() }, finishTraining() { @@ -381,5 +388,10 @@ Page({ // Re-init countUp so the big number rolls again on next start _countUpTask: null }) - } + }, + + // No-op handler for catchtouchmove. Swallows move events that would + // otherwise bubble to underlying timer page controls (causing missed + // taps on the completion modal). + onNoop() { /* swallow */ } }) diff --git a/pages/timer/timer.wxml b/pages/timer/timer.wxml index 3dbc304..2e7ef82 100644 --- a/pages/timer/timer.wxml +++ b/pages/timer/timer.wxml @@ -25,6 +25,7 @@ wx:if="{{status !== 'completed'}}"> @@ -114,8 +116,21 @@ + + + + 结束训练 + 确定要结束本次训练吗? + + 取消 + 确定 + + + + - + {{completionLevel === 'first' ? '🎉' : completionLevel === 'record' ? '🏆' : completionLevel === 'milestone' ? '🔥' : '✨'}} diff --git a/pages/timer/timer.wxss b/pages/timer/timer.wxss index fe8f103..6e38461 100644 --- a/pages/timer/timer.wxss +++ b/pages/timer/timer.wxss @@ -70,8 +70,8 @@ bottom: 10rpx; left: 50%; transform: translateX(-50%); - background: rgba(76, 175, 80, 0.12); - border: 2rpx solid rgba(76, 175, 80, 0.3); + background: rgba(var(--success-rgb), 0.12); + border: 2rpx solid rgba(var(--success-rgb), 0.3); color: var(--success); padding: 8rpx 24rpx; border-radius: 24rpx; @@ -393,3 +393,84 @@ font-size: 26rpx; margin-top: 4rpx; } + +/* ---- stop confirm modal (替代 wx.showModal,自定义实现) ---- */ +.stop-confirm-mask { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.55); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + padding: 40rpx; + box-sizing: border-box; + animation: completionMaskIn 0.25s ease; +} + +.stop-confirm-card { + width: 100%; + max-width: 560rpx; + background: var(--card-bg); + border-radius: 24rpx; + padding: 48rpx 40rpx 24rpx; + display: flex; + flex-direction: column; + align-items: center; + box-shadow: 0 12rpx 36rpx rgba(0, 0, 0, 0.22); + animation: stopConfirmPop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +@keyframes stopConfirmPop { + 0% { transform: scale(0.85); opacity: 0; } + 100% { transform: scale(1); opacity: 1; } +} + +.stop-confirm-title { + font-size: 32rpx; + font-weight: 700; + color: var(--text); + margin-bottom: 20rpx; + line-height: 1.3; +} + +.stop-confirm-content { + font-size: 28rpx; + color: var(--text-secondary); + line-height: 1.5; + text-align: center; + margin-bottom: 40rpx; +} + +.stop-confirm-actions { + display: flex; + width: 100%; + gap: 16rpx; + border-top: 1rpx solid var(--border); + padding-top: 24rpx; +} + +.stop-confirm-btn { + flex: 1; + height: 80rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 30rpx; + font-weight: 600; + border-radius: 12rpx; + background: var(--bg-soft); + color: var(--text); + transition: transform 0.15s ease, opacity 0.15s ease, background 0.2s ease; +} + +.stop-confirm-btn:active { + transform: scale(0.96); + opacity: 0.85; +} + +.stop-confirm-btn--primary { + background: linear-gradient(135deg, var(--primary), var(--primary-light)); + color: #FFFFFF; + box-shadow: 0 6rpx 16rpx rgba(var(--primary-rgb), 0.3); +}