Files
lc ca1793b4c0 feat: 个人最佳趋势改为折线图 + 底部最佳趋势日期 + 修复
- 趋势组件 trend-chart 新增 chartType(line/bar),line 模式用内联 SVG 渲染折线+渐变面积(无 canvas)
- 记录页个人最佳趋势改用折线图,峰值信息移到图表下方对称高亮块显示最佳趋势产生日期
- 修复1: linePoints 漏存绝对坐标 y 导致运行时 toFixed 崩溃
- 修复2: 折线模式漏渲染数据点数值(valueText),现圆点上方显示且 0 值不显示
2026-08-04 15:46:18 +08:00

24 KiB
Raw Permalink 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 不进包。

上线前优化续:组件按需注入 + records 补声明 + 提交推送

  • 微信开发者工具「代码质量」扫描显示「组件:启用组件按需注入 — 未通过」。
  • 根因:project.config.json 缺 lazyCodeLoading 配置项。已加 "lazyCodeLoading": "requiredComponents"(只加载页面 usingComponents 声明且模板实际用到的组件)。
  • 核实:7 组件均静态写死在 WXML,无动态 <component is=/抽象节点/custom-tab-bar 不受影响 → 无负面影响;基础库 <2.10.1 用户自动回退全量加载(无害)。
  • 顺带发现真 bug:pages/records/records.json 漏声明 ui-skeleton(WXML 第 4/6 行已用)→ 已补;5 页面声明与 WXML 实际使用现已完全一致。
  • 提交:commit 187c426(14 文件,+100/-199,含之前的 console 清理+project.config 开关+删 preview-buttons.html+lazyCodeLoading+records 补声明)。clean-console.js 一次性工具未入库。
  • 推送:soao(https://git.soao.net/lc/wx_pbzc.git)成功(b52db87..187c426);origin(github)当前环境到 github.com:443 SSL 连接被重置(HTTP2 framing/SSL_ERROR_SYSCALL 交替),多次重试失败——环境网络抖动,非代码问题。本地 commit 安全,待网络恢复后重试 git push origin main

功能:个人最佳趋势改为折线图 + 底部加最佳趋势产生日期

  • 用户要求:记录页「个人最佳趋势」(bestTrendData)从柱状图改折线图,并在底部加「最佳趋势产生日期」(像「最高训练日」那样)。
  • 实现:趋势组件 trend-chartchartType(bar|line,默认 bar,柱状图完全不动)+ lineColor 属性。line 模式用内联 SVG(data URI)→<image> 渲染折线+半透明面积(无 canvas,和组件 no-canvas 理念一致);圆点与横轴标签用 WXML 绝对定位(按 xPercent/yPercent),保证清晰且能吃主题色(因 SVG 在 image 内读不到 CSS 变量,颜色由页面传 hex)。
  • 几何:viewBox=640×H(H=peak?170:200),mode=widthFix 让 SVG 宽高比与容器 rpx 一致→stroke 均匀不拉伸;x 按 (i+0.5)*640/N 均分,y 用 topPad/bottomPad 留头尾余量;零值点落到接近底部。
  • 数据层不动(getBestTrendData 仍返回 label=周一/7月 等周期标签,符合"就像最高训练日"的粒度)。
  • records 页:best 趋势 <trend-chart chart-type="line" line-color="{{bestLineColor}}">;原在 header 的 best-trend-best 文本移到图表下方,复用 .trend-highlight 对称块(icons.crownFill + bestTrendSummary.peakTitle + peakText);新增 _buildBestSummary 把 peakTitle 覆盖为「个人最佳日/月」(通用 getTrendSummary 原给"最高训练日",不对);bestLineColor=theme.primary(peak 实际用主题主色,非金)。
  • 验证:node --check 通过;tests/trend-state.test.js 5/5 通过(数据层未变);SVG 字符串模拟确认无 NaN、含 stroke/fill-opacity/width/height。删了 records.wxss 里已不用的 .best-trend-best 样式。
  • 改动未提交(git status 有 M)。待用户审阅后可提交(soao 国内远程稳定;origin 仍待网络恢复推)。

BUG 修复:折线图运行时崩溃(toFixed of undefined)

  • 现象:记录页切换 trend 模式报 Cannot read property 'toFixed' of undefined(trend-chart.js:90, _buildLineSvg 内 p.y.toFixed)。
  • 根因:_compute 生成 linePoints 时只存了 x(绝对)与 yPercent(百分比),漏存绝对坐标 y;_buildLineSvg 画 polyline/area 用的是 viewBox 绝对坐标 p.x/p.y,p.y 为 undefined → 崩。WXML 圆点/标签用 xPercent/yPercent 定位(无误)。
  • 修复:linePoints 返回对象补 y(与 x 同为 viewBox 坐标);_buildLineSvg 加防御 valid = points.filter(p => p && typeof p.x==='number' && typeof p.y==='number'),first/last/area/polyline 全改走 valid,空则返 ''。
  • 验证:node --check 通过;用真实 getBestTrendData 输出实跑 _compute(week/month)均生成 lineSvg 不再崩;稀疏数组也安全。临时 repro 脚本已删。改动未提交。

BUG 修复:折线图数据点未显示数值

  • 现象:折线模式每个数据点旁没有数值文字(柱状图有,折线没有)。
  • 根因:line 模式 WXML 只渲染了圆点(trend-line-dot)与横轴 label,漏渲染 valueText;且 linePoints 返回对象未透传 value 字段,无法做 value>0 判断。
  • 修复:trend-chart.js 的 linePoints 返回补 value: b.value(bars 项第57行本就有 value);WXML 圆点内加 <text wx:if="{{item.value>0}}" class="trend-line-value">{{item.valueText}}</text>(仿柱状图,0 值不显示);WXSS 加 .trend-line-value(绝对定位在 dot 上方居中,16rpx,高亮点用主色加粗),并把 .trend-chart.line 的 padding-top 16rpx→30rpx 给最高点数值留缓冲防裁切。
  • 验证:node --check 通过;bars.value 字段确认存在。改动未提交。