Files
wx_pbzc/.workbuddy/memory/2026-08-04.md
T
lc 187c426243 chore: 上线前优化 — 清理调试日志/启用组件按需注入/补全组件声明
- 移除全项目 34 处调试 console.* 输出
- project.config.json: minified true、uploadWithSourceMap false、启用 lazyCodeLoading(requiredComponents 组件按需注入)
- pages/records/records.json 补全 ui-skeleton 组件声明(此前漏声明,WXML 已使用)
- 删除根目录残留 preview-buttons.html
2026-08-04 14:05:16 +08:00

19 KiB
Raw Blame History

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 圆弧终点),包围盒必须用可靠库 svgpathnpm 装到 managed workspace):abs() → unarc() → unshort() → iterate() 收集控制点算包围盒。
  • 治本方案:7 枚勋章 path 全部按图形包围盒等比缩放到 24×0.92 并平移到 (12,12) 精确居中svgpath scale+translateround(4))。unarc 把弧线展开为贝塞尔,path 变长(medal 344→1153 字符)但形状无损,7 枚共约 +9KB 可忽略。
  • 同时 CSS.podium-badge margin-top 2rpx→6rpxflex 行盒中心与中文 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 新增 scienceTips4 段按时长 maxSeconds:120/300/480/Infinity,每段 value+risk);timer.js 新增 _pickScienceTip(elapsed) + data 字段 completionTipValue/Risk + _saveAndShowCompletion 里 setDatatimer.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-bottom8→32rpx)保持呼吸感。grep 确认无残留。

试点:勋章图标换成 Phosphor medal30天「神登」)

  • 对比 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.medalWhiteconfig.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'setDatasettings.wxml 昵称包 profile-name-rowflex),右侧 wx:for 渲染勋章组;settings.wxss 加 profile-name-row/profile-badges/profile-badge32rpx,昵称可收缩省略、勋章组 flex-shrink:0)。
  • 验证:语法通过、7 枚主题色版 build 生成 OK(跟随主题色)。

榜单页显示最新勋章

  • 需求:leaderboard 页"我"的位置显示最新解锁勋章(点击 toast 显示勋章名+天数门槛)。用户确认:进榜 is-me 行 + 未进榜 my-bar 两处都显示 + 点击提示。
  • 图标色分场景is-me 行浅底(primary 12%)用主题色 xxxFillmy-bar 主题渐变底用白版 xxxWhite——正好复用现有两种变体,零新增。
  • 实现:leaderboard.js data 加 myLatestBadge: nullonShow 同步 _getLatestBadge()(本地 totalDays 取 days<=totalDays 最后一枚,返回 days/name/iconFill/iconWhite<3 天或异常返回 null,且 days 未变不重复 setData),加 onBadgeTapdataset 传 name/days);wxml 两处 rank-name 包 rank-name-row + 勋章 imageis-me 行 iconFill、my-bar iconWhitewx:if myLatestBadge);wxss 加 rank-name-row/rank-badge30rpx)。
  • 数据纯本地,零云函数改动(勋章是"我的"荣誉,别人看不到)。
  • 验证:语法通过;边界 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。快照经 publicEntryrest 解构只剥 memberKey/openid)自动透传,无需改 data.js。
  • 客户端 pages/leaderboard/leaderboard.js:删掉 myLatestBadgedata/onShow/_getLatestBadge),改为通用 _badgeOf(totalDays)_applyResult 给每个 ranked item 映射 badgeIcon(Fill)/badgeName/badgeDays(浅底行用),myEntry 额外 badgeIconWhitemy-bar 渐变底用);_applyLocal 开头算 localBadge 字段,两处 entry 展开进 badge 字段。
  • wxmlrank-item 每行勋章 wx:if item.badgeIcon(不限 isMe,所有人显示)+ data-name/daysmy-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)直接赋给了 badgeIconWXML 直接 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-rowflex 居中),右侧加 podium-badge(第 1 名用 podium-badge--first 34rpx2/3 名 30rpx),带 onBadgeTapleaderboard.wxss 加 row/badge 样式。
  • 验证:3 处渲染点齐全、语法 OK。

优化:虚拟领奖台(不足 3 名也显示)

  • 用户要求:榜单数据不足 3 名时,不要只显示列表,也要显示(虚拟)领奖台。
  • 改动(纯前端,无云函数改动):
    • leaderboard.wxml 领奖台 wx:ifrankedList.length >= 3 改为 >= 13 个 podium-slot 各自用 <block wx:if="{{rankedList[i]}}"> 真实内容 / <block wx:else> 虚拟空位(虚线轮廓头像 + 灰化 peopleFill 图标 + 底座「虚位以待」)。
    • 下方列表 wx:ifrankedList.length < 3 || index >= 3 改为 index >= 3:领奖台出现时前 3 名不再在列表重复出现(<3 名时列表直接为空,名次全在领奖台)。
    • leaderboard.wxss 加 .podium-avatar--empty(虚线圆、淡底)、.podium-avatar__img--empty(灰化 0.26 透明)、.podium-empty-label.podium-crown--empty0.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:130rpx(与勋章等高盒);② .podium-name--firstline-height:34rpx(与首名 34rpx 勋章等高);③ 间距 margin-top:30rpx.podium-name 移到 .podium-name-row(避免 cross 轴 margin 干扰居中);④ 删除 .podium-badgemargin-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)完全一致。纯样式无语法风险。

提交与推送

  • commit b52db8717 文件,+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 不进包。