fix: 修复头像存储、日历详情数据与弹窗定位问题

- settings: 补上缺失的 util 导入,onChooseAvatar 不再抛
  ReferenceError,头像走 data URI 永久存储主路径
- records: 日历详情改用 dateOnly 比较,修复详情永远显示 0 次/0 时长
- records: 日历详情弹窗从 ui-modal 改为普通 view + fixed,
  修复被底部 dock 遮挡/不居中;补暗黑阴影描边与滚动穿透处理

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 08:46:17 +08:00
parent ac0db6118d
commit 1d13b49682
6 changed files with 91 additions and 30 deletions
+3 -3
View File
@@ -4,14 +4,14 @@
*/ */
module.exports = { module.exports = {
/** 应用版本号,外显在设置页「关于」 */ /** 应用版本号,外显在设置页「关于」 */
version: 'v2.0', version: 'v2.5',
/** 最后更新日期,外显在设置页「关于」 */ /** 最后更新日期,外显在设置页「关于」 */
updatedAt: '2026-07-08', updatedAt: '2026-07-09',
/** 开发者名称 / 微信号,设置页点击可复制 */ /** 开发者名称 / 微信号,设置页点击可复制 */
developer: '刘承', developer: '刘承',
/** 排行榜最大显示人数 */ /** 排行榜最大显示人数 */
leaderboardMaxRank: 15 leaderboardMaxRank: 20
} }
+6 -1
View File
@@ -108,7 +108,9 @@ Page({
const { year, month, day } = e.detail const { year, month, day } = e.detail
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}` const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`
const allRecords = Object.values(storage.getRecords()).flat() const allRecords = Object.values(storage.getRecords()).flat()
const sessions = allRecords.filter(r => r.date === dateStr) // record.date 是 "YYYY-MM-DD HH:MM:SS"(formatDate 生成),dateStr 只有
// 日期部分,直接 === 永远不匹配,必须用 dateOnly 截取后再比。
const sessions = allRecords.filter(r => util.dateOnly(r.date) === dateStr)
const totalDur = sessions.reduce((sum, r) => sum + r.duration, 0) const totalDur = sessions.reduce((sum, r) => sum + r.duration, 0)
const calories = Math.round(totalDur * 0.068) const calories = Math.round(totalDur * 0.068)
this.setData({ this.setData({
@@ -127,6 +129,9 @@ Page({
this.setData({ showDayDetail: false }) this.setData({ showDayDetail: false })
}, },
// Swallow touchmove on the popup so the background page doesn't scroll.
onNoop() { /* swallow */ },
onLongPressDelete(e) { onLongPressDelete(e) {
this.onDeleteRecord(e) this.onDeleteRecord(e)
}, },
+1 -2
View File
@@ -1,8 +1,7 @@
{ {
"usingComponents": { "usingComponents": {
"calendar-heatmap": "/components/calendar-heatmap/calendar-heatmap", "calendar-heatmap": "/components/calendar-heatmap/calendar-heatmap",
"ui-card": "/components/ui-card/ui-card", "ui-card": "/components/ui-card/ui-card"
"ui-modal": "/components/ui-modal/ui-modal"
}, },
"navigationBarTitleText": "训练记录" "navigationBarTitleText": "训练记录"
} }
+29 -23
View File
@@ -113,31 +113,37 @@
</view> </view>
<!-- /hidden wrapper --> <!-- /hidden wrapper -->
<!-- 日期详情弹窗 --> <!-- 日期详情弹窗:普通 view 实现,不用 ui-modal。
<ui-modal visible="{{showDayDetail}}" position="center" bind:close="onCloseDayDetail"> 自定义组件根元素的 position: fixed 会被小程序运行时吞掉,叠加
<view class="detail-date-row"> .page-enter 的 transform 会让 fixed 降级相对 .container,导致弹窗
<image class="detail-icon" src="{{icons.calendarFill}}" mode="aspectFit"></image> 落到容器末尾而非视口居中。普通 view 的 fixed 100% 生效。 -->
<text class="detail-date">{{dayDetail.date}}</text> <view wx:if="{{showDayDetail}}" class="day-detail-mask" catchtouchmove="onNoop">
</view> <view class="day-detail-mask__bg" bindtap="onCloseDayDetail"></view>
<view class="detail-grid"> <view class="day-detail-body" catchtap="onNoop">
<view class="detail-item"> <view class="detail-date-row">
<image class="detail-item-icon" src="{{icons.rankFill}}" mode="aspectFit"></image> <image class="detail-icon" src="{{icons.calendarFill}}" mode="aspectFit"></image>
<text class="detail-num">{{dayDetail.sessions}}</text> <text class="detail-date">{{dayDetail.date}}</text>
<text class="detail-label">训练次数</text>
</view> </view>
<view class="detail-item"> <view class="detail-grid">
<image class="detail-item-icon" src="{{icons.timeFill}}" mode="aspectFit"></image> <view class="detail-item">
<text class="detail-num">{{dayDetail.durationText}}</text> <image class="detail-item-icon" src="{{icons.rankFill}}" mode="aspectFit"></image>
<text class="detail-label">总时长</text> <text class="detail-num">{{dayDetail.sessions}}次</text>
<text class="detail-label">训练次数</text>
</view>
<view class="detail-item">
<image class="detail-item-icon" src="{{icons.timeFill}}" mode="aspectFit"></image>
<text class="detail-num">{{dayDetail.durationText}}</text>
<text class="detail-label">总时长</text>
</view>
<view class="detail-item">
<image class="detail-item-icon" src="{{icons.hotFill}}" mode="aspectFit"></image>
<text class="detail-num">{{dayDetail.calories}}</text>
<text class="detail-label">约消耗 (千卡)</text>
</view>
</view> </view>
<view class="detail-item"> <view class="detail-close" bindtap="onCloseDayDetail">
<image class="detail-item-icon" src="{{icons.hotFill}}" mode="aspectFit"></image> <text>关闭</text>
<text class="detail-num">{{dayDetail.calories}}</text>
<text class="detail-label">约消耗 (千卡)</text>
</view> </view>
</view> </view>
<view class="detail-close" bindtap="onCloseDayDetail"> </view>
<text>关闭</text>
</view>
</ui-modal>
</view> </view>
+51 -1
View File
@@ -221,7 +221,57 @@
line-height: 1; line-height: 1;
} }
/* ---- day detail popup ---- */ /* ---- day detail popup ----
普通 view 实现,不用 ui-modal:自定义组件根元素的 position: fixed
会被小程序运行时吞掉(见 settings.wxss plan-editor 注释),叠加
.page-enter 的 transform 会让 fixed 降级相对 .container,导致弹窗
落到容器末尾而非视口居中。普通 view 的 fixed 100% 生效。 */
.day-detail-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
}
.day-detail-mask__bg {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.45);
animation: dayDetailFade 0.25s ease;
}
.day-detail-body {
position: relative;
width: 560rpx;
border-radius: 28rpx;
padding: 48rpx 40rpx 36rpx;
background: var(--card-bg);
box-sizing: border-box;
text-align: center;
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.25);
animation: dayDetailPop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@media (prefers-color-scheme: dark) {
.day-detail-body {
/* 暗黑下 body(#1C1C1E)与页面(#0F0F12)都偏深,加深阴影 + 微亮描边拉开层次 */
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.6), 0 0 0 1rpx rgba(255, 255, 255, 0.06);
}
}
@keyframes dayDetailFade {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes dayDetailPop {
from { transform: scale(0); opacity: 0; }
60% { transform: scale(1.05); }
to { transform: scale(1); opacity: 1; }
}
.detail-date-row { .detail-date-row {
display: flex; display: flex;
align-items: center; align-items: center;
+1
View File
@@ -5,6 +5,7 @@ const iconsMod = require('../../utils/icons')
const cloud = require('../../utils/cloud') const cloud = require('../../utils/cloud')
const config = require('../../config') const config = require('../../config')
const darkMod = require('../../utils/darkMode') const darkMod = require('../../utils/darkMode')
const util = require('../../utils/util')
/** /**
* Build the simplified plans list shown in the settings UI by overlaying * Build the simplified plans list shown in the settings UI by overlaying