Files
wx_pbzc/.workbuddy/memory/2026-07-21.md
T
2026-07-29 11:05:03 +08:00

140 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.js `watchDarkMode` 回调只在有该方法时才调用。只有 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)`,并通知自定义 tabBar `updateTheme()`,修系统深/浅色切换时三主页面不实时刷新。
**第二轮("接着修"**
- `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 分析(色彩层级单薄/排版无标尺/进度环太素/深色不一致),按约定先做见效最快的三项:
1. **进度环**`components/progress-ring/progress-ring.js`):canvas 分段绘制锥形渐变弧(primary→primaryLight,每段 0.06rad、上限 120 段),末端加 `shadowBlur` 发光圆点+白色内核,起点圆头补圆;`primaryLightColor` 加入 `_draw` observer;环宽 timer 页 16→22rpx,时间字 80→88rpx/800 字重;呼吸环从描边圆改 radial-gradient 弥散光晕(`timer.wxss`)。
2. **首页**`ui-card` 新增 `gradient` 变体(主色渐变底,局部重定义 `--text/--text-secondary/--border` 为白色系,后代自动反白);连续打卡卡用 `variant="gradient in"`,卡内 streak 数字/进度条/光点显式改白;今日目标环 200→260rpx、时间 64→112rpx、加外圈光晕。
3. **背景分层+深色对齐**`theme.js getThemeStyle` 浅色背景渐变起点改用各主题 `primaryBg`(背景随主题变色),深色统一为 `#131316` 系(bg/card `#26262B`/soft `#343439`);`applyThemeToPage/applyWindowBg` 窗口背景同步;`app.wxss` 冷启动变量、`theme.json`、timer `trackColor`(深色 `#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
4. **排行榜**period-tabs(白底下划线)→ `period-seg` 胶囊分段控件(呼应设置页 .segmented,sticky 保留);≥3 人时前三名上**领奖台**(冠军居中放大头像 128rpx+皇冠图标+渐变金牌,2/3 名银铜奖牌,阶梯底座,跳过列表前三);`my-bar` 从半透明描边→主色渐变实色卡+白字+主色投影;rank-item 的 is-me 判空保护(myEntry 可为 null)。
5. **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随领奖台移除。
6. **设置页**:配色方案从纵向列表→**横向渐变色卡**(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 isolationisolated),`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`(白色图标在橙卡上清晰)。
- 校验 grepindex 页仅此两处用主色 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-ring` 260→280rpxbox-shadow 16→14rpx 收外扩、加 overflow:hidden);`.target-time` 112→84rpxletter-spacing -1rpx、max-width:240rpx、text-align:center、overflow:hidden),3 位数不再溢出环。
- 校验 node --check 通过;grep 确认全项目无残留 `playFill`
- 项目 MEMORY.md 补充"图标烤色无法被 CSS 改 + 主按钮须用 *White 变体"陷阱。
## 配色方案 +12026-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.avatarUrlcloud.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-bar `myEntry.avatarUrl || icons.peopleFill`(my-bar 原无头像,补上)。共 5 处。
- `leaderboard.wxss`my-bar 头像 `.my-bar__avatar` opacity:1 + 白底白边(渐变条上占位图更协调)。
- `leaderboard.js._applyResult``...item` 透传,avatarUrl 自动带入,无需改。
- 校验:云函数 node --check 通过;grep 确认 5 处头像均改、占位图保留为兜底。
- ⚠️ 需重新部署 leaderboard 云函数(`wx cloud deploy` 或开发者工具上传)后头像才会在真机/线上生效;本地仅前端改动预览看不到他人头像(本地 fallback 也只取 profile.nickname)。
## 排行榜头像方形未裁圆(2026-07-21)
用户验收:头像已显示,但是方形图套在圆框里(border-radius:50% 加在 <image> 上,部分微信基础库不裁切图片内容)。
修复(微信可靠做法:圆角 <view overflow:hidden> 包裹 <image> 让父层裁切):
- `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.wxml` 5 处头像加条件 `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 环(设计预期),仅真实照片受影响。
- 仅前端改动,结构与设置页完全一致。