16 KiB
2026-07-21
代码审查:平板支撑训练小程序(wx_pbzc)
对微信小程序做了完整 code review,报告见 /Users/liubleed/Documents/wx_pbzc/code-review-report.md。
关键问题:
- 高#1:
utils/plan.js getPlanDay()用完整时间戳(r.date含时分秒)做new Set().size统计训练天数,同一天多次训练会多算天数、错误跳过计划进度。应改用dateOnly(r.date)。 - 高#2:系统深色模式切换时 index/records/leaderboard 三页不实时重绘——它们没定义
applyThemeToPage方法,而 app.jswatchDarkMode回调只在有该方法时才调用。只有 settings 页有该方法。修复:回调补兜底或给三页加方法。 - 中:排行榜完全信任客户端上传的 records.duration,可伪造;
admin-dedupe云函数无鉴权且具破坏性。 - 低:Timer 的
wx.onAppShow只注册不注销;ui-modal的 customStyle 是死属性;getTodayTarget空 days 会崩溃;darkMode.js 用了废弃的 getSystemInfoSync。
代码整体质量高(云同步去重/合并、降级处理都到位)。
修复记录(2026-07-21,两轮)
第一轮(高优)
utils/plan.js getPlanDay():改用dateOnly(r.date)去重,修同一天多次训练错误跳计划天数。app.js watchDarkMode回调:补兜底,对无applyThemeToPage方法的页面直接调themeMod.applyThemeToPage(p),并通知自定义 tabBarupdateTheme(),修系统深/浅色切换时三主页面不实时刷新。
第二轮("接着修")
cloudfunctions/admin-dedupe/index.js:入口加 openid 白名单闸门(读环境变量ADMIN_OPENIDS,未配置默认拒绝=失败安全),防任意用户调用批量删文档。部署时须配置该变量。utils/timer.js:抽_bindAppShow/_unbindAppShow,在stop()中wx.offAppShow,修每次进训练页累积 onAppShow 监听的内存泄漏。utils/plan.js getTodayTarget:空 days 数组返回 0,防plan.days[0].target崩溃。utils/darkMode.js:wx.getSystemInfoSync()废弃,改wx.getWindowInfo()。pages/timer/timer.js onTrainAgain:_countUpTask改为实例字段赋值,不再写进 setData。pages/index/index.js refresh():planProgress在 totalDays=0 返回 0,防 NaN。- 复核纠正两条误报(已写进报告):#3 排行榜 duration 实为服务端从 doc.records 重算,不读客户端,无需修;#8 ui-modal 的 customStyle 在 wxml 第3行已被
style="{{customStyle}}"使用,非死属性。 - 全部 6 文件
node --check通过;报告code-review-report.md已标修复状态并纠正误报。
UI 美化第一轮(2026-07-21)
基于 UI 分析(色彩层级单薄/排版无标尺/进度环太素/深色不一致),按约定先做见效最快的三项:
- 进度环(
components/progress-ring/progress-ring.js):canvas 分段绘制锥形渐变弧(primary→primaryLight,每段 0.06rad、上限 120 段),末端加shadowBlur发光圆点+白色内核,起点圆头补圆;primaryLightColor加入_drawobserver;环宽 timer 页 16→22rpx,时间字 80→88rpx/800 字重;呼吸环从描边圆改 radial-gradient 弥散光晕(timer.wxss)。 - 首页:
ui-card新增gradient变体(主色渐变底,局部重定义--text/--text-secondary/--border为白色系,后代自动反白);连续打卡卡用variant="gradient in",卡内 streak 数字/进度条/光点显式改白;今日目标环 200→260rpx、时间 64→112rpx、加外圈光晕。 - 背景分层+深色对齐:
theme.js getThemeStyle浅色背景渐变起点改用各主题primaryBg(背景随主题变色),深色统一为#131316系(bg/card#26262B/soft#343439);applyThemeToPage/applyWindowBg窗口背景同步;app.wxss冷启动变量、theme.json、timertrackColor(深色#3A3A40)、ui-skeleton、custom-tab-bar、records.wxss注释全部对齐新色值;ui-card深色下加深阴影+微亮描边防"卡片沉底"。
验证:node --check 通过;全库 grep 旧深色值 #0F0F12/#2C2C2E/#1C1C1E 仅剩 docs/ 历史文档(无需改)。
待办(用户确认后做):排行榜领奖台+period tab 改分段控件、Emoji→SVG 统一、排版/圆角标尺收敛、设置页主题色卡横滑、微交互(数字滚动/按钮回弹)。
UI 美化第二轮(2026-07-21)
- 排行榜:period-tabs(白底下划线)→
period-seg胶囊分段控件(呼应设置页 .segmented,sticky 保留);≥3 人时前三名上领奖台(冠军居中放大头像 128rpx+皇冠图标+渐变金牌,2/3 名银铜奖牌,阶梯底座,跳过列表前三);my-bar从半透明描边→主色渐变实色卡+白字+主色投影;rank-item 的 is-me 判空保护(myEntry 可为 null)。 - Emoji→SVG:
utils/icons.js新增 trophy/party/sparkle(+Fill)及白色语义变体trophyWhite/partyWhite/hotWhite/sparkleWhite/checkWhite;timer 完成弹窗 emoji 文本→SVG 图标(.completion-icon 80rpx),庆祝粒子 emoji→sparkle/crown/like 图片(.spark 改 44rpx 图片),streak 🔥→hotFill 图片;成就横幅文案去掉内嵌 emoji(渐变横幅自身够视觉)。排行榜奖牌 emoji 已在任务4随领奖台移除。 - 设置页:配色方案从纵向列表→横向渐变色卡(3 列,primary→primaryLight 渐变 swatch + 白色对勾,选中态放大 1.04+主色外描边,名称高亮);深色模式选择保留原行式(.theme-item 样式保留);计划选中项加左侧 6rpx 主色渐变竖条。 校验:全部改动文件 node --check 通过;grep 确认无残留界面 emoji(仅 settings 关于页 ❤️ 与文档,保留)。
UI 对比度修复(2026-07-21)
修首页渐变 hero 卡(训练首页顶部"连续打卡")数字/文案看不清:
- 根因:小程序组件默认
style isolation,.ui-card.gradient <slot后代>跨作用域选择器全部失效;hero 卡内.streak-num/.progress-fill用var(--primary)(橙)画在橙色渐变底上=隐形。 - 修复:
ui-card渐变变体新增--on-primary:#FFFFFF(自定义属性能跨隔离边界继承进 slot);.streak-num改var(--text)(渐变卡继承为白,普通卡为深);.progress-fill/.progress-glow改var(--on-primary, <原渐变/主色>);--text-secondary渐变卡内由 0.75 提到 0.85 提升小标签可读性;删除 index.wxss 里 4 条失效的.ui-card.gradient ...页面选择器。 - 全局扫描确认其余彩色/渐变底(按钮/横幅/完成弹窗/my-bar/主题色卡/奖牌/列表时长)均显式白字或位于浅色底,无同类问题。
UI 对比度审计(2026-07-21 下午)
用户反馈:训练首页(index)顶部"连续打卡"数字与渐变卡底几乎同色、看不清。
根因(关键项目陷阱):ui-card 自定义组件默认 style isolation(isolated),variant="gradient in" 产生的 gradient/in 类在组件作用域内,而卡内 slot 内容(.streak-num 等)的样式写在页面作用域。因此页面里的 .ui-card.gradient .streak-num{color:#FFF} 这类跨作用域后代选择器全部失效(运行时症状即证明)。唯一能跨边界生效的是 CSS 自定义属性——.ui-card.gradient 重定义的 --text/--text-secondary/--border 会向下继承进 slot,所以标签等能反白,但 .streak-num 用的 var(--primary) 没被重定义 → 仍是橙色画在橙色渐变上 = 看不见。
同类受损(同一机制):index 渐变 hero 卡内 .progress-fill/.progress-glow 用 var(--primary) 渐变,覆盖选择器同样失效 → 进度条橙色填充在橙色卡上不可见(细,用户未提但已确认)。
已确认安全(渐变/主色底 + 文字全部显式 #FFFFFF):ui-btn--primary、achievement-banner、completion-emoji-wrap(白色 SVG)、completion-btn--primary、stop-confirm-btn--primary、leaderboard my-bar(全显式白)、editor-action-save、records first-train-cta、settings 主题色卡(白对勾 + 名称在卡底)、rank-dur/podium-dur--first(主色字放在白卡/页面底)、target-time(主色字放在 0.10 浅色环)。
修复原则(待用户确认):渐变/主色容器内文字只用 var(--text)/var(--text-secondary)(随卡反白),绝不用 var(--primary);进度填充改 var(--on-primary, <原渐变>) 让容器可覆盖为白。删掉那些失效的 .ui-card.gradient ... 页面选择器。
UI 图标对比度修复(2026-07-21)
用户反馈:index 页几个图标颜色与背景基本一样、看不清(同 gradient hero 卡问题延续)。
根因:图标是 SVG data URI,颜色烤死在 URI 里(icons.js build() 中 *Fill 变体统一用 primary 主题色绘制),CSS 的 color/变量均无法改它。index 渐变 hero 卡上的两个图标因此仍是橙色,画在橙色渐变底上=隐形:
.streak-icon用icons.hotFill(橙)on 渐变卡.plan-badge-icon用icons.formFill(橙)on 渐变卡
修复:
utils/icons.js新增formWhite(白填色,与已有hotWhite并列,注释说明渐变卡必须用白填色)。pages/index/index.wxml:.streak-icon→icons.hotWhite、.plan-badge-icon→icons.formWhite(白色图标在橙卡上清晰)。- 校验 grep:index 页仅此两处用主色 Fill 变量,均已改;timer/settings/records 页的 hotFill/formFill 在深色/白卡背景,对比正常,不在本次范围。
结论:index 首页其余图标(targetFill 在白卡、time 灰在白卡、lightFill 在浅 soft 卡、successFill 在绿徽章)对比均正常。精力/问候图标(emojiFill 橙)在页面浅色底,橙主题下略低对比但可见,未改。
UI 图标尺寸 + 主按钮图标对比度 + 目标数字溢出(2026-07-21)
用户反馈:1) index「开始训练」前的图标太小也看不清;2) 今日目标数字太大超出圆环。并要求排查项目类似小图标。
排查(全项目 icon-src 审计):主按钮用 playFill(橙) 放在橙渐变背景上=隐形且 36rpx 太小,同病根的有 3 处——index 开始训练、timer 开始、timer 继续。ghost(浅底)/outline(灰底)/delete(灰) 按钮图标对比正常。
修复:
utils/icons.js新增playWhite(白色播放图标,与 hotWhite/formWhite 并列;注释强调主按钮必须白图标)。- 3 个主按钮
icon-src:playFill→playWhite(index.wxml:73、timer.wxml:90、timer.wxml:109)。 components/ui-btn/ui-btn.wxss:.ui-btn__icon尺寸1em→1.3em(xl 按钮 36→约 47rpx,随按钮字号自适应放大)。pages/index/index.wxss:.target-ring260→280rpx(box-shadow 16→14rpx 收外扩、加 overflow:hidden);.target-time112→84rpx(letter-spacing -1rpx、max-width:240rpx、text-align:center、overflow:hidden),3 位数不再溢出环。- 校验 node --check 通过;grep 确认全项目无残留
playFill。 - 项目 MEMORY.md 补充"图标烤色无法被 CSS 改 + 主按钮须用 *White 变体"陷阱。
配色方案 +1(2026-07-21)
用户反馈:设置页配色方案只有 5 个、3 列网格下不对称(2 行满+1 孤)。要求再加一个。
新增第 6 个主题 teal(湖青):utils/theme.js 的 THEMES 末尾追加
- id:'teal', name:'湖青', primary:'#00BCD4', primaryLight:'#4DD0E1', primaryBg:'#E0F7FA', primaryRgb:'0,188,212' 选青/湖蓝原因:区别于已有蓝/绿,且避开红色(项目 --danger 用红,主色不宜红)。 效果:设置页色卡变 6 个=2 行×3 列,对称。theme.js 单源、settings.js 直接读 themeMod.THEMES,无数量硬限制(grep 仅 cloudfunctions/admin-dedupe 有 slice(0,5),无关)。node --check 通过。
排行榜头像不显示 - 修复(2026-07-21)
根因:排行榜头像写死 icons.peopleFill(统一占位人像)。App 其实支持头像(settings 页 chooseAvatar→上传云存储 fileID→存 profile.avatarUrl,cloud.js:146 同步整份 profile 到云端),但 cloudfunctions/leaderboard/index.js 只读了 profile.nickname、返回的 ranked/myEntry 没有 avatarUrl 字段 → 前端无真头像可用。
修复:
cloudfunctions/leaderboard/index.js:userMap.set、ranked map、myEntry 三处均加avatarUrl: profile.avatarUrl || ''(后者用 item.avatarUrl)。pages/leaderboard/leaderboard.wxml:领奖台 3 处rankedList[0/1/2].avatarUrl || icons.peopleFill、列表项item.avatarUrl || icons.peopleFill、my-barmyEntry.avatarUrl || icons.peopleFill(my-bar 原无头像,补上)。共 5 处。leaderboard.wxss:my-bar 头像.my-bar__avataropacity:1 + 白底白边(渐变条上占位图更协调)。leaderboard.js._applyResult用...item透传,avatarUrl 自动带入,无需改。- 校验:云函数 node --check 通过;grep 确认 5 处头像均改、占位图保留为兜底。
- ⚠️ 需重新部署 leaderboard 云函数(
wx cloud deploy或开发者工具上传)后头像才会在真机/线上生效;本地仅前端改动预览看不到他人头像(本地 fallback 也只取 profile.nickname)。
排行榜头像方形未裁圆(2026-07-21)
用户验收:头像已显示,但是方形图套在圆框里(border-radius:50% 加在 上,部分微信基础库不裁切图片内容)。
修复(微信可靠做法:圆角 包裹 让父层裁切):
leaderboard.wxss:删除.podium-avatar/.podium-avatar--first/.rank-avatar,新增统一.avatar(圆角+overflow:hidden+背景)、.avatar__img(100%填充 block)、.avatar--sm/md/lg三档尺寸;.podium-avatar-wrap去掉 margin-bottom(改由 avatar--md/lg 自带)。leaderboard.wxml:5 处头像<image>改为<view class="avatar avatar--sm/md/lg [my-bar__avatar]"><image class="avatar__img" .../></view>(md/lg 用于领奖台 2/3 名与冠军放大;sm 用于列表项与 my-bar)。.my-bar__avatar覆盖(opacity:1+白底白边)仍作用于新元素。- 校验 grep:无悬空旧类
podium-avatar/rank-avatar(仅.podium-avatar-wrap定位容器保留);5 处均改新结构。 - 此坑记入 MEMORY.md(微信圆形头像裁剪陷阱)。
月榜/年榜头像仍方(仅日榜圆)二次修复(2026-07-21)
用户验收:日榜正常,月榜/年榜头像还是方的。
根因确认:三档周期共用同一 .avatar 结构(WXML/WXSS 完全一致),差异只在图片内容——
- 日榜其他用户多未设头像 → 显示
peopleFill占位 SVG → 被外层 view 的 overflow:hidden 裁圆; - 月榜/年榜出现真实照片头像(cloud://)→ 该基础库下,外层 view 的 overflow:hidden 裁不掉远程照片的原生图片层,照片露方。
修复:给 .avatar__img 自身补 border-radius:50%(双保险,对齐设置页 .avatar-btn+.avatar-img 的实测可用写法),与父层 overflow:hidden 并存。
- 设置页
settings.wxml:10的"我的头像"正是此写法(button overflow:hidden + image border-radius:50%)且用户未投诉,证明该组合在用户机器上可靠。 - 已更新 MEMORY.md 圆形头像陷阱条目为更精确结论:必须 image 自身 border-radius,不能只靠父层 overflow。
月/年榜头像"大圆套小圆"缩小问题(2026-07-21)
用户验收:圆形有了,但真实照片偏小,像大圆里套小圆,不像设置页头像。
根因:.avatar--md/lg/sm 自带 padding:20/26/0 rpx 留白——占位 SVG 是"图标居中+外圈"(OK),但真实照片被这圈留白缩成小图 → 大圆套小圆。
修复(对齐设置页"有头像就填满"的写法):
leaderboard.wxml5 处头像加条件has-avatar类({{x.avatarUrl ? 'has-avatar' : ''}}),且<image>的mode改为{{x.avatarUrl ? 'aspectFill' : 'aspectFit'}}(真实照片填满圆、占位图仍 fit 居中)。leaderboard.wxss新增.avatar.has-avatar { padding: 0; opacity: 1; }(双类选择器特异性高于.avatar--md/lg/sm,覆盖留白与淡透明度),照片填满圆且不发虚。- 未设头像的占位图仍保留 padding 环(设计预期),仅真实照片受影响。
- 仅前端改动,结构与设置页完全一致。