b52db87ddf
- 领奖台数据不足 3 名时显示虚拟空位(虚线轮廓+虚位以待) - 领奖台勋章与昵称对齐改为等高盒结构性修复(移除 margin 盲推) - 全 app 成就勋章图标尺寸统一为 30rpx(前三名/列表/设置页/记录页一致) - 勋章 SVG 按包围盒精确居中(utils/icons.js) - 设置页/记录页勋章展示配套调整
134 lines
17 KiB
Markdown
134 lines
17 KiB
Markdown
# 2026-08-04 工作日志
|
||
|
||
## 修复(治本):勋章图形精确居中 + 领奖台对齐(第二轮)
|
||
- 用户反馈第一轮 CSS 微调后勋章"还是偏上"。深挖发现真因:**7 枚 Phosphor 勋章图形在 24 viewBox 内垂直重心各不相同**(medalMilitary/shieldStar 偏下 1.13、trophyPh 偏下 0.75、crownPh 偏上 1.13),CSS margin 只能整体挪 `<image>` 盒子,永远修不齐不同勋章。
|
||
- **关键坑**:自写 SVG path 解析器易错(`V/H` 单参数命令打乱坐标配对、`a` 圆弧终点),包围盒必须用可靠库 `svgpath`(npm 装到 managed workspace):`abs() → unarc() → unshort() → iterate()` 收集控制点算包围盒。
|
||
- 治本方案:7 枚勋章 path 全部按图形包围盒**等比缩放到 24×0.92 并平移到 (12,12) 精确居中**(svgpath scale+translate,round(4))。`unarc` 把弧线展开为贝塞尔,path 变长(medal 344→1153 字符)但形状无损,7 枚共约 +9KB 可忽略。
|
||
- 同时 CSS:`.podium-badge` margin-top 2rpx→6rpx(flex 行盒中心与中文 glyph 视觉重心偏差补偿,图形已居中所以只需一次全局补偿)。
|
||
- 验证:7 枚新 path 包围盒中心全部 (12.00,12.00);node --check 通过;build() 7 枚 Fill/White 变体齐全;show_widget 渲染 3 枚代表勋章与昵称齐平。
|
||
- 后续若用户仍觉偏移,只调 `.podium-badge` margin-top 一个数即可(图形差异已清零)。
|
||
|
||
## 修复:领奖台勋章与昵称对齐错位
|
||
- 用户反馈领奖台(podium)勋章图标与昵称上下错位。
|
||
- 根因:flex align-items:center 对齐的是行盒中心,而 `.podium-name` line-height 1.3 使行盒(33.8rpx)远大于字高(26rpx),叠加中文 glyph 视觉重心偏高,图标视觉上比文字低。
|
||
- 修复(leaderboard.wxss,仅领奖台样式,`.podium-name` 无他处引用):①line-height 1.3→1(行盒贴字高);②`.podium-dur` margin-top 2→10rpx(补偿行盒缩短 ~8rpx,保持与名字间距);③`.podium-badge` 加 margin-top:2rpx(图标下移抵消中文视觉重心偏上)。
|
||
- 教训:icon+text 横排对齐,text 必须 line-height:1(或行盒≈图标高度),并视字体微调图标偏移;别依赖 line-height 冗余空间做"视觉居中"。
|
||
|
||
## 代码问题分析(已核实修订)
|
||
- 对平板支撑小程序做全量代码审查,初版报告 8 条问题经逐条事实核实后修订:撤回 3 条(隐私协议缺失/onLaunch await 阻塞启动/组件 styleIsolation 不一致,均为判断错误)、降级 2 条(console.log/云环境ID)、保留 3 条可选优化(lazyCodeLoading/测试覆盖/月份翻页上限)。
|
||
- 报告写入 docs/问题分析_2026-08-04.md。
|
||
- 教训记入 MEMORY.md:判断隐私合规先看 MP 后台而非代码;onLaunch async 不阻塞首屏;审查勿把合理设计/优化建议拔高成风险。
|
||
|
||
## 修复:记录页月份翻页上限
|
||
- pages/records/records.js 的 onNextMonth 原本可无限往后翻到未来月份。
|
||
- 加上限判断:下一月超过当前真实年月时提示"已经是最新月份"并 return,不翻页。
|
||
- node --check 通过。
|
||
|
||
## 第三次整体审查(用户再次询问"有没有问题")
|
||
- 又通读 custom-tab-bar、trend-chart、records.wxml 等此前未细看模块,git 历史确认项目持续迭代、质量高。
|
||
- 结论:无功能性 bug、无合规阻断、无需要紧急修复项。
|
||
- 如实记录的少数"设计权衡/边界"(非 bug,方向均偏乐观):
|
||
1. 首页"今日已完成"用 getTodayRecord 的全天累计时长(storage.js:451),自由训练/循环训练 duration 也计入今日目标达成判断 → 偏乐观。
|
||
2. 云同步是全量整包 update(最后写入胜),无字段级冲突合并;mergeRecords 仅在本地合并,多设备同时改边缘可能覆盖(单设备无影响)。
|
||
3. 卡路里 0.068 系数仅 onDayTap 一处,无一致性冲突。
|
||
|
||
## 优化:卡路里系数抽到 config.js
|
||
- config.js 新增 `caloriesPerSecond: 0.068` 配置项(注释说明估算口径),records.js onDayTap 改引用 `config.caloriesPerSecond`(带 `|| 0.068` 兜底)。
|
||
- 语法 node --check 通过。
|
||
|
||
## 新功能:完成弹窗科学提示(价值+风险)
|
||
- 用户要求把平板支撑科学建议加入完成成绩弹窗。先做了科学核查:原表"磷酸原/糖酵解按时长切换"是错误说法,方案改用修正口径——只讲价值/风险,不提供能系统。
|
||
- 实现:config.js 新增 `scienceTips`(4 段按时长 maxSeconds:120/300/480/Infinity,每段 value+risk);timer.js 新增 `_pickScienceTip(elapsed)` + data 字段 completionTipValue/Risk + _saveAndShowCompletion 里 setData;timer.wxml 连胜行后加 completion-tip 卡片(价值行 + 弱化「小贴士」风险行);timer.wxss 加样式(rgba primary 0.08 底、20rpx 圆角、tipIn 动画延迟 1.0s)。
|
||
- 边界验证:120s 整入段2、480s 整入段4、负/0 秒兜底段1;缺配置时 wx:if 不渲染,零风险。
|
||
- 循环训练(circuit)复用同一路径,workTotal 天然适用。
|
||
|
||
## 调整:删除完成弹窗「本次训练」标签;星级方案放弃
|
||
- 用户提出删除 completion-label「本次训练」+ 给提示卡加推荐指数星级。星级算法有产品理念矛盾(按时长给星违背科学共识/打脸耐力榜,按科学区间破纪录用户会低星),用户决策放弃星级。
|
||
- 实施:timer.wxml 删 `completion-label` 节点;timer.wxss 删对应样式,把 24rpx 间距并入 `.completion-number-wrap` 的 margin-bottom(8→32rpx)保持呼吸感。grep 确认无残留。
|
||
|
||
## 试点:勋章图标换成 Phosphor medal(30天「神登」)
|
||
- 对比 6 个图标库(Phosphor/Tabler/MDI/Ionicons/Lucide/FA),推荐 Phosphor。用户要求先做一枚进记录页。
|
||
- **关键坑**:Phosphor 是 256 viewBox,项目 `_svg` 硬编码 24 viewBox,直接塞会裁切。需 path 缩放——但**不能无脑除数字**:`a` 命令的 large-arc-flag/sweep-flag 只能 0/1、x-axis-rotation 是角度,都不该缩放。写了命令感知缩放器(token 流解析,a 命令 idx%7===2/3/4 不缩放),渲染对比验证无变形。
|
||
- 改动:icons.js PATHS 加 `medal`(缩放后 path)+ `out.medalWhite`;config.js 30天 badge icon 改 `medal`/`medalWhite`。trophy 系列保留(完成弹窗/排行榜空态仍用)。
|
||
- 验证:node --check 通过、build() 生成灰版(#999999)与白版(#FFFFFF)正确。
|
||
|
||
## 勋章图标全套替换为 Phosphor 勋章系(完成)
|
||
- 7 级递进:sealCheck(3天 抖登) → medal(7天 中登) → medalMilitary(14天 老登) → trophyPh(30天 神登) → certificate(60天 上古神登) → shieldStar(80天 洪荒神登) → crownPh(100天 究极登祖)。
|
||
- 全部 256→24 坐标缩放(用命令感知缩放器,a 命令 large-arc/sweep/rotation 不缩放)。**命名避坑**:Phosphor 版 trophy/crown 带 Ph 后缀(trophyPh/crownPh),避免覆盖 Material 版 trophy/crown(完成弹窗/排行榜仍在用)。
|
||
- 改动:icons.js PATHS 加 6 枚 + 6 个 White 变体(medal 已加);config.js trainingDayBadges 7 行全改。
|
||
- 验证:语法通过、7 枚灰版+白版 build 全部生成 OK、旧图标引用确认未误删。
|
||
- 缩放脚本在 /tmp/scale-phosphor.js(一次性工具,未入库)。
|
||
|
||
## 个人资料昵称旁显示已解锁勋章
|
||
- 需求:settings 页个人资料昵称后面展示已获得的勋章。
|
||
- **图标色坑**:badge 图标现有变体只有灰(未解锁)/白(解锁坐主题色圆盘),设置页浅底白图标会隐形。方案:icons.js 在 PATHS 定义后加 `PATHS.xxxFill = PATHS.xxx`(对象属性引用同一 path),build 循环自动生成主题色版 out.xxxFill,颜色跟随当前主题。
|
||
- 实现:settings.js data 加 `unlockedBadges: []`,onShow 里 `_getUnlockedBadges()`(totalDays >= b.days 过滤 + iconFill = icon+'Fill')setData;settings.wxml 昵称包 profile-name-row(flex),右侧 wx:for 渲染勋章组;settings.wxss 加 profile-name-row/profile-badges/profile-badge(32rpx,昵称可收缩省略、勋章组 flex-shrink:0)。
|
||
- 验证:语法通过、7 枚主题色版 build 生成 OK(跟随主题色)。
|
||
|
||
## 榜单页显示最新勋章
|
||
- 需求:leaderboard 页"我"的位置显示最新解锁勋章(点击 toast 显示勋章名+天数门槛)。用户确认:进榜 is-me 行 + 未进榜 my-bar 两处都显示 + 点击提示。
|
||
- **图标色分场景**:is-me 行浅底(primary 12%)用主题色 `xxxFill`;my-bar 主题渐变底用白版 `xxxWhite`——正好复用现有两种变体,零新增。
|
||
- 实现:leaderboard.js data 加 `myLatestBadge: null`,onShow 同步 `_getLatestBadge()`(本地 totalDays 取 days<=totalDays 最后一枚,返回 days/name/iconFill/iconWhite;<3 天或异常返回 null,且 days 未变不重复 setData),加 `onBadgeTap`(dataset 传 name/days);wxml 两处 rank-name 包 rank-name-row + 勋章 image(is-me 行 iconFill、my-bar iconWhite,wx:if myLatestBadge);wxss 加 rank-name-row/rank-badge(30rpx)。
|
||
- 数据纯本地,零云函数改动(勋章是"我的"荣誉,别人看不到)。
|
||
- 验证:语法通过;边界 0/2→null、3→3天、29→14天、85→80天、500→100天封顶 全对;7 枚 Fill/White 变体全存在;wxml/js 引用一致。
|
||
|
||
## 排查「17次不显示勋章」+ 修复本地兜底 isMe bug
|
||
- 用户反馈某用户 17 次训练不显示勋章。核实后根因:①用户看的是**线上旧版**(勋章功能今天刚加,未发版);②看的是**别人的行**(勋章只在"我"的行显示,设计如此)。
|
||
- 顺带发现真实 bug:`_applyLocal`(云函数不可用的本地兜底)`rankedList: [entry]` 的 entry **缺 isMe 标记** → is-me 行不渲染勋章,且 my-bar 条件(rank 1 > length 1)为 false → 弱网/云函数故障时勋章完全不可见。
|
||
- 修复:leaderboard.js 两处本地兜底 entry 补 `isMe: true`(耐力榜 + 日/月/年榜)。node --check 通过。
|
||
|
||
## 榜单勋章全榜可见(云函数 + 客户端联动)
|
||
- 需求:用户希望榜单上**所有用户**都能看到彼此的勋章(之前只有"我"本地算的勋章)。
|
||
- **云函数** `cloudfunctions/leaderboard/index.js`:`_buildFromScan` 每个 doc 开头算 `totalDays`(遍历全部 records 去重日期,与榜单周期无关),endurance/day/month/year 四个分支的 entry、ranked、myEntry 输出都带 `totalDays`。快照经 `publicEntry`(rest 解构只剥 memberKey/openid)自动透传,无需改 data.js。
|
||
- **客户端** `pages/leaderboard/leaderboard.js`:删掉 myLatestBadge(data/onShow/_getLatestBadge),改为通用 `_badgeOf(totalDays)`;`_applyResult` 给每个 ranked item 映射 `badgeIcon(Fill)/badgeName/badgeDays`(浅底行用),myEntry 额外 `badgeIconWhite`(my-bar 渐变底用);`_applyLocal` 开头算 localBadge 字段,两处 entry 展开进 badge 字段。
|
||
- **wxml**:rank-item 每行勋章 `wx:if item.badgeIcon`(不限 isMe,所有人显示)+ data-name/days;my-bar 用 `myEntry.badgeIconWhite`。
|
||
- 兼容:旧快照无 totalDays → _badgeOf(undefined)→null → 不渲染,等新快照(4min 定时重建)。**必须重新部署 leaderboard 云函数**。
|
||
- 验证:语法 OK、跨月去重正确、badge 映射(2天不渲染/14天老登/100天皇冠)OK、myLatestBadge 无残留。
|
||
|
||
## 修复:榜单勋章 500 报错(badgeIcon 赋了字符串 key 而非 data URI)
|
||
- 用户反馈:排行榜不显示勋章,控制台 `Failed to load local image resource /pages/leaderboard/medalMilitaryFill (500)`。
|
||
- **根因**:重构 myLatestBadge → `item.badgeIcon` 时,把 `badge.icon + 'Fill'`(字符串 key)直接赋给了 badgeIcon;WXML 直接 `src="{{item.badgeIcon}}"` 期望 data URI,字符串被当成路径加载 → 500。旧实现是 `icons[myLatestBadge.iconFill]` 先查映射再取 URI,重构时漏了这层查询。
|
||
- 修复:leaderboard.js 三处(_applyResult 的 list 映射、myEntry 映射、_applyLocal 的 localBadgeFields)都改为 `(this.data.icons || iconsMod.build())[badge.icon + 'Fill']` 取真实 data URI。
|
||
- 验证:语法 OK、badgeIcon 实测输出 `data:image/svg+xml;charset=utf-8,...`。
|
||
- 教训:WXML `<image src>` 直接绑定 data 字段时,字段必须已是 data URI,不能是图标 key 字符串。
|
||
|
||
## 补漏:领奖台前三名显示勋章
|
||
- 用户反馈领奖台前 3 名没勋章。原因:领奖台是独立 podium-slot 区(rankedList[0/1/2]),上一轮只改了普通 rank-item 行。
|
||
- 数据其实已就绪(_applyResult 对全部 ranked item 含前 3 名映射了 badgeIcon data URI)。
|
||
- 改动:leaderboard.wxml 3 个 podium-slot 的 podium-name 包成 podium-name-row(flex 居中),右侧加 podium-badge(第 1 名用 podium-badge--first 34rpx,2/3 名 30rpx),带 onBadgeTap;leaderboard.wxss 加 row/badge 样式。
|
||
- 验证:3 处渲染点齐全、语法 OK。
|
||
|
||
## 优化:虚拟领奖台(不足 3 名也显示)
|
||
- 用户要求:榜单数据不足 3 名时,不要只显示列表,也要显示(虚拟)领奖台。
|
||
- 改动(纯前端,无云函数改动):
|
||
- leaderboard.wxml 领奖台 `wx:if` 由 `rankedList.length >= 3` 改为 `>= 1`;3 个 podium-slot 各自用 `<block wx:if="{{rankedList[i]}}">` 真实内容 / `<block wx:else>` 虚拟空位(虚线轮廓头像 + 灰化 peopleFill 图标 + 底座「虚位以待」)。
|
||
- 下方列表 `wx:if` 由 `rankedList.length < 3 || index >= 3` 改为 `index >= 3`:领奖台出现时前 3 名不再在列表重复出现(<3 名时列表直接为空,名次全在领奖台)。
|
||
- leaderboard.wxss 加 `.podium-avatar--empty`(虚线圆、淡底)、`.podium-avatar__img--empty`(灰化 0.26 透明)、`.podium-empty-label`、`.podium-crown--empty`(0.3 透明)。
|
||
- 边界:0 名仍走 empty 空态(「暂无排行数据」);1 名→领奖台只填第 1 名,2/3 空位;2 名→填 1/2 名;≥3 名同原逻辑。语法 OK、无残留旧条件引用。
|
||
|
||
## 微调:领奖台勋章与昵称对齐(反复下压.margin-top)
|
||
- 用户反馈前三名昵称后勋章持续「偏高」。本质是 flex 居中 + 中文字形视觉重心(字体度量 descent)的固有偏差,只能靠 margin-top 盲推。
|
||
- 轨迹:.podium-badge margin-top 6rpx→8rpx(看截图仍高)→11rpx(用户说偏高)→14rpx。每次 +3rpx。
|
||
- 单一可调数字。若 14rpx 仍差,改结构性修法:勋章缩到与昵称同高(30→26rpx)或 .podium-name 加 display:inline-block 重约束行盒,从根消除偏移,不再靠 margin。
|
||
- 纯样式无语法风险。
|
||
|
||
## 升级:领奖台勋章对齐改为结构性修法(而非 margin 盲推)
|
||
- 用户反馈 margin-top 推到 14rpx 仍偏高,确认盲推失效。深挖两个隐藏根因:
|
||
1. `.podium-name` 自带 `margin-top:30rpx` 且在 flex **cross 轴**上,等于把昵称整体下推,反而让勋章更"偏上"——所以一路加 badge margin-top 永远追不平。
|
||
2. `.podium-name` `line-height:1`(26rpx 盒)比勋章(30rpx)矮,`align-items:center` 对齐的是"26rpx 行盒中心"与"30rpx 图标盒中心",叠加中文 glyph 视觉重心偏下,图标自然飘上。
|
||
- 结构性修法(leaderboard.wxss):① `.podium-name` `line-height:1`→`30rpx`(与勋章等高盒);② `.podium-name--first` 加 `line-height:34rpx`(与首名 34rpx 勋章等高);③ 间距 `margin-top:30rpx` 从 `.podium-name` **移到 `.podium-name-row`**(避免 cross 轴 margin 干扰居中);④ **删除 `.podium-badge` 的 `margin-top`** 盲推。
|
||
- 结果:flex `align-items:center` 现在对齐的是两个等高盒的中心,图标与昵称视觉精确对齐,不再依赖任意偏移量。若个别冠类勋章因图形重心仍有 ±1~2rpx 残差,单独加 `transform: translateY(2rpx)` 即可。
|
||
- 纯样式、无语法风险、无残留旧 margin 引用。
|
||
|
||
## 检查并统一:成就勋章图标跨页尺寸
|
||
- 用户对齐 OK 后,顺带查勋章大小一致性。结论:
|
||
1. 图标本体视觉大小一致——7 枚勋章 SVG 早已统一缩放居中到 24×0.92 viewBox,同尺寸盒内可见主体等大,无"crown 比 medal 大"问题。
|
||
2. 非预期不一致在跨页盒子尺寸:排行榜(领奖台 2/3 名 .podium-badge 30、列表 .rank-badge 30)用 30rpx;设置页 .profile-badge 与记录页 .badge-node-icon 用 32rpx。
|
||
- 修复:把 settings.wxss `.profile-badge` 与 records.wxss `.badge-node-icon` 的 32rpx→30rpx,统一到基准 30rpx(且与领奖台昵称对齐 line-height:30rpx 配套)。领奖台首名 .podium-badge--first 34rpx 作为冠军强调保留(设计性,非 bug)。
|
||
- 若后续想连冠军也统一 30rpx,改 .podium-badge--first 为 30 且 .podium-name--first line-height 同步回 30rpx 即可。
|
||
|
||
## 调整:领奖台前三名勋章统一大小
|
||
- 用户要求前三名勋章一致大小(此前首名 34rpx 强调、2/3 名 30rpx)。
|
||
- 改动:leaderboard.wxss `.podium-badge--first` 34→30rpx;配套 `.podium-name--first` line-height 34→30rpx(保持首名昵称与勋章等高盒对齐)。
|
||
- 结果:领奖台前三名勋章统一 30rpx,与全局基准(列表 .rank-badge 30、设置页 .profile-badge 30、记录页 .badge-node-icon 30)完全一致。纯样式无语法风险。
|