187c426243
- 移除全项目 34 处调试 console.* 输出 - project.config.json: minified true、uploadWithSourceMap false、启用 lazyCodeLoading(requiredComponents 组件按需注入) - pages/records/records.json 补全 ui-skeleton 组件声明(此前漏声明,WXML 已使用) - 删除根目录残留 preview-buttons.html
19 KiB
19 KiB
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-badgemargin-top 2rpx→6rpx(flex 行盒中心与中文 glyph 视觉重心偏差补偿,图形已居中所以只需一次全局补偿)。 - 验证:7 枚新 path 包围盒中心全部 (12.00,12.00);node --check 通过;build() 7 枚 Fill/White 变体齐全;show_widget 渲染 3 枚代表勋章与昵称齐平。
- 后续若用户仍觉偏移,只调
.podium-badgemargin-top 一个数即可(图形差异已清零)。
修复:领奖台勋章与昵称对齐错位
- 用户反馈领奖台(podium)勋章图标与昵称上下错位。
- 根因:flex align-items:center 对齐的是行盒中心,而
.podium-nameline-height 1.3 使行盒(33.8rpx)远大于字高(26rpx),叠加中文 glyph 视觉重心偏高,图标视觉上比文字低。 - 修复(leaderboard.wxss,仅领奖台样式,
.podium-name无他处引用):①line-height 1.3→1(行盒贴字高);②.podium-durmargin-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,方向均偏乐观):
- 首页"今日已完成"用 getTodayRecord 的全天累计时长(storage.js:451),自由训练/循环训练 duration 也计入今日目标达成判断 → 偏乐观。
- 云同步是全量整包 update(最后写入胜),无字段级冲突合并;mergeRecords 仅在本地合并,多设备同时改边缘可能覆盖(单设备无影响)。
- 卡路里 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 透明)。
- leaderboard.wxml 领奖台
- 边界: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 仍偏高,确认盲推失效。深挖两个隐藏根因:
.podium-name自带margin-top:30rpx且在 flex cross 轴上,等于把昵称整体下推,反而让勋章更"偏上"——所以一路加 badge margin-top 永远追不平。.podium-nameline-height:1(26rpx 盒)比勋章(30rpx)矮,align-items:center对齐的是"26rpx 行盒中心"与"30rpx 图标盒中心",叠加中文 glyph 视觉重心偏下,图标自然飘上。
- 结构性修法(leaderboard.wxss):①
.podium-nameline-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 后,顺带查勋章大小一致性。结论:
- 图标本体视觉大小一致——7 枚勋章 SVG 早已统一缩放居中到 24×0.92 viewBox,同尺寸盒内可见主体等大,无"crown 比 medal 大"问题。
- 非预期不一致在跨页盒子尺寸:排行榜(领奖台 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--first34→30rpx;配套.podium-name--firstline-height 34→30rpx(保持首名昵称与勋章等高盒对齐)。 - 结果:领奖台前三名勋章统一 30rpx,与全局基准(列表 .rank-badge 30、设置页 .profile-badge 30、记录页 .badge-node-icon 30)完全一致。纯样式无语法风险。
提交与推送
- commit
b52db87(17 文件,+690/-67)包含今天整轮(虚拟领奖台+勋章对齐结构修复+全 app 勋章 30rpx 统一)。 - 已推
origin(github.com/cnliucheng/wx_pbzc) + 新增远程soao(https://git.soao.net/lc/wx_pbzc.git) 并推main。两个远程同步于 b52db87。 - 提醒:cloudfunctions/leaderboard 若含线上逻辑改动需开发者工具「上传并部署」才生效(本次主要改前端)。
上线前审查(launch readiness)
- 主包 540KB(<<2MB,无需分包);网络全走 wx.cloud(免 request 域名白名单);无硬编码 http;隐私接口仅 chooseAvatar(后台已配)。合规面干净。
- 🔴 阻断项:①云函数 git 提交≠部署——
leaderboard(本次b52db87改 17 行 totalDays/全榜勋章)、tts(8-03 改)需重新部署;②tts环境变量 TTS_SECRET_ID/KEY/REGION 须在云函数控制台配好(否则语音失败)。 - 🟡 建议项:清理调试 console.log(cloud.js 14 处等)、project.config.json 上线开关(minified→true/uploadWithSourceMap→false)、加 wx.onNeedPrivacyAuthorize 自定义隐私弹窗、加全局错误上报、清理残留(cloudfunctions/shared 空死目录/根 preview-buttons.html/.claude/.codegraph)。
- 核实:leaderboard 实际 require('./data') 非 '../shared',空 shared 目录无害;
data.js/cache.js同目录存在,部署含之;无分包;无 wx.onError。 - 报告:.workbuddy/launch-readiness-review.md。
执行:上线前建议项清理(用户确认"需要")
- 清调试 console.*:脚本 .workbuddy/clean-console.js 批量删(独立 console 行 + 箭头函数体
=> console→=> {}),删 34 处;漏 2 处if(){console.log();return}行内形式手动改;Grep 复扫全项目 .js 已无 console. 残留。涉及 cloud.js/storage.js/voice.js/theme.js/app.js/leaderboard.js/timer.js/settings.js/cloudfunctions/leaderboard/index.js,均 node --check 通过。 - project.config.json:minified false→true、uploadWithSourceMap true→false(上线防源码泄露+减小包体)。
- 清残留:删 preview-buttons.html + rmdir cloudfunctions/shared(空死目录,重构遗留);.claude/.codegraph 已被 packOptions.ignore 的
.!*忽略,不动。 - 未做:全局错误上报(wx.onError)、自定义隐私弹窗(用户仅确认做这三个清理项)。
- 改动未提交(git status 显示 M/D 文件)。clean-console.js 为一性工具,留 .workbuddy 不进包。