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

8.7 KiB
Raw Blame History

项目长期记忆(wx_pbzc 平板支撑训练小程序)

关键技术陷阱

  • 小程序组件 style isolation:自定义组件默认 isolated。组件内 variant 拼出的类(gradient/in 等)在组件作用域,页面里写的 .组件类 <slot后代> 跨作用域选择器全部失效(如 .ui-card.gradient .streak-num)。唯一能跨边界传递的是 CSS 自定义属性(会沿 DOM 继承进 slot)。
    • 推论:渐变/主色容器内要反白的文字,必须用 var(--text)/var(--text-secondary)(容器在 .gradient 里重定义过即会变白),绝不能用 var(--primary)——否则橙色字画在橙色底上=看不见。
    • 进度条填充同理:用 var(--on-primary, <原渐变>) 让容器可覆盖为白,而非依赖失效的后代选择器。
  • 图标 SVG 颜色是烤死在 data URI 里的utils/icons.jsbuild()*Fillprimary 主题色、*(无 Fill)用灰 #999)。CSS 的 color/currentColor/变量都改不了 <image src> 这种图标。所以:
    • 放在主色/渐变底gradient hero 卡、primary 实心按钮)上的图标必须用白色变体hotWhite/formWhite/playWhite/trophyWhite 等),否则同色隐形。
    • 加新图标或新彩色容器时,先想清楚底色:浅色底用 *Fill(主题色),彩色/主色底用 *White 变体。
    • 主按钮 ui-btn--primary 背景是橙渐变,playFill(橙) 放上去即隐形——须用 playWhite。ghost/outline 按钮底色浅或灰,用 *Fill/灰色变体即可。
    • components/ui-btn 图标尺寸 1.3em(随按钮字号自适应),不要回到 1em 以免显得过小。
  • 深色模式有两套事实源,改配色前先统一:theme.json 的 dark 与 app.wxss/utils/theme.js 要一致(已统一到 #131316 系)。
  • 微信圆形头像/图片裁剪陷阱(用户基础库实测):本项目的圆形头像在用户机器上表现如下——
    • 单独给 <image>border-radius:50%(无 overflow 父层)→ 圆框里露方图,失效。
    • 外层 <view>overflow:hidden+border-radius:50% 包裹 → 能裁切 SVG 占位图,但裁不掉远程照片(cloud:// 真实头像),照片仍方。所以日榜(多为占位图)正常、月榜/年榜(真实照片)却方。
    • 唯一可靠写法:外层 overflow:hidden 父层 <image> 自身也加 border-radius:50%(双保险)。设置页 .avatar-btn(button,overflow:hidden)+.avatar-img(image,border-radius:50%) 即是此写法且实测正常。排行榜 .avatar/.avatar__img 已对齐这一写法。
    • 推论:以后做圆形头像,必须 image 自身 border-radius:50%,不能只靠父层 overflow。
  • 微信系统字体缩放导致文字溢出固定盒子(模拟器不重现、真机重现):微信会按用户「设置→通用→字体大小」自动放大所有文字(含 rpx 字号),但不放大 rpx 盒子尺寸width/height/padding)。症状:固定尺寸的圆形/方形容器里的文字在真机被放大、超出后被 overflow:hidden 裁掉,模拟器(标准档倍率 1.0)正常。
    • 获取缩放:wx.getAppBaseInfo().fontSizeScaleFactor(当前字号÷标准 17px);旧接口 getSystemInfoSync().fontSizeSettingpx,标准 17)。
    • 真机/模拟器不对称(关键坑)getAppBaseInfo().host.env 在模拟器是 'devtools'、真机是 'wechat'。模拟器会原样返回你手机的真实缩放倍率,但渲染时并不会把文字放大;真机才会真的放大。所以若不做区分,补偿把字号缩成 design/factor 后:真机放大回 design(正常),模拟器按缩小值渲染(数字变小=「不正常」)。→ 必须在 host.env === 'devtools' 时跳过补偿、直接用设计字号;仅在真机/PC 微信才补偿。首页 _readFontScale() 已加此判断。
    • 补偿法:把容器内的关键文字字号反向除以该倍率(designRpx / factor),并在 JS 里 clamp 到安全区间(如 48–96),使真机渲染成和模拟器一致的视觉大小、永不溢出。首页 .target-time 已用此法(index.js _readFontScale())。
    • 配套 CSSwhite-space:nowrap 防换行顶出圆圈。
    • 推论:以后凡把文字放进固定尺寸的圆形/方形容器且依赖 overflow:hidden 裁切,都必须考虑字体缩放补偿,否则真机大字体档必溢出。
  • 排行榜头像 1–2s 延迟根因(设计使然,非 bug)profile.avatarUrl 存的是 cloud:// 云存储 fileIDsettings._uploadAvatarwx.cloud.uploadFile 返回 fileID);云函数 leaderboard 直接透传,前端 <image src="cloud://...">setData 渲染时才把 fileID 解析成临时 https URL(等效一次 getTempFileURL 网络往返)再下载。文字(昵称/时长/排名)是本地字符串瞬时渲染 → 故"文字先出、头像后出",延迟正是头像专属的「云存储解析 + 原图下载」段,叠加云函数冷启动。
    • 占位图 peopleFill 是本地 SVG data URI 瞬时,所以日榜(多为占位)无感、月/年榜(真实照片)延迟明显。
    • 优化方向:①云函数返回前对 cloud://avatarUrl 批量 cloud.getTempFileURL 预解析成 https;②_uploadAvatar 上传前把 chooseAvatar 原图压缩/裁剪到最长边 ~200px、转 jpg/webp,体积降一个数量级;③云函数保活降冷启动。
  • 云函数数据库写操作必须 { data: {...} } 包裹(wx-server-sdk 关键坑)wx-server-sdk(本项目 2.6.3) 的 db.collection().doc(id).set(x) / .update(x) / .add(x) 写操作,参数必须是 { data: realObj } 包裹形式——SDK 内部读 parameter.data 当作要写入的文档。直接传裸对象 set(realObj) 会让 SDK 读 realObj.data === undefined,报 parameter.data should be object instead of undefined(读操作 get() 不受影响,裸对象/无参都行)。leaderboard 云函数 _persistSnapshot 踩过此坑:写快照全失败、集合一直空。
    • 修复:docRef.set({ data: { period, ranked, myOpenid, updatedAt } })。写入后文档内容即 realObj(data 外套被 SDK 解开),读取 doc.data 仍是 realObj,读逻辑无需改。
    • 推论:以后任何云函数写库代码,先写 { data: ... },别沿用客户端 SDK 的裸对象习惯;出错时先怀疑是不是漏了 data 包裹,而不是怀疑部署。

排行榜架构(2026-07-28 落地 P0/P1/P2 后)

  • 预计算快照cloudfunctions/leaderboardexports.main 现在区分两种调用——
    • 定时触发器config.jsontriggerssnapshotTimer / 0 */4 * * * * *,每 4 分钟)进入 _rebuildSnapshots():扫描一次 plank_data_fetchLatestByOpenid,走 updatedAt 索引+投影),复用 _buildFromScan() 算 day/month/year 三榜,各自写进 leaderboard_snapshot 集合(doc._id=周期,字段 period/ranked/myOpenid/updatedAt;首次写自动建集合)。
    • 客户端请求:默认走 _serveFromSnapshot()1 次 get ≈ 50ms),快照缺失/过期(>6min)才 _computeBoard() 实时重算并回写。首开/冷启动已从 2-3s 降到 ≈50ms。
  • force 语义:仅训练后那一次(客户端 onShow_lb_force_refreshforce=true)走 _computeBoard(force:true,persist:true) 实时重算+回写快照;其他 99% 请求读快照,已满足"force 不再绕过云缓存"诉求。
  • P1 启动预热app.js onLaunchcloud.init() 后 fire-and-forget 调一次 leaderboard(day),提前暖云实例/快照。
  • 头像延迟:云函数内 getTempFileURL 预解析已落地(在 _buildFromScan 里),前端不再懒加载,故"文字先出头像后出"的 1-2s 延迟已大幅消除;快照每 4min 重写,临时 URL 始终新鲜。
  • 改动注意:要生效必须重新部署 leaderboard 云函数(定时器随部署注册)。plank_data.updatedAt 单字段索引需在控制台已建(用户已建)。响应结构 {period,ranked,myOpenid,myEntry,updatedAt} 不变,客户端 leaderboard.js 无需改逻辑(仅注释更新)。

约定

  • UI 动词说法:index=首页/训练首页,timer=训练页,leaderboard=排行榜,records=记录,settings=设置。
  • 美化按"见效快优先"分批做,每轮 node --check + grep 残留再交付。
  • git 推送需走 sandbox 外:本机 Bash 工具默认 sandbox 隔离网络,git push 到 github.com 会 SSL connection timeout(约 5 分钟才报错)。必须用 dangerouslyDisableSandbox: true 在沙箱外执行推送(或 run_in_background + 沙箱外)才能连上。无 SSH keyremote 仅 HTTPS。