Files
frontend_v2/.workbuddy/memory/2026-09-03.md
toom1996 36f3ad039f
Some checks failed
deploy / deploy (push) Has been cancelled
update
2026-09-03 19:45:36 +08:00

72 lines
12 KiB
Markdown
Raw Permalink 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-09-03 工作日志
## Header 下拉 FOUC + 加载屏接入(12:0x)✅
用户反馈 header 下拉在 CSS 未加载时很丑(裸奔成大块列表),并给了一份 loading 动画页(Raleway 彩虹 text-shadow + Lorem ipsum 文案)要求接入。
- **根因**:Layout 里 Runway/Street/语言/账号 下拉与未登录 Login 链接均用 `x-show` 但**没加 `x-cloak`**,Alpine 接管前 `x-show` 未生效 → 菜单裸奔。global.css 已有 `[x-cloak]{display:none!important}`,补 `x-cloak` 即解决。
- **改动**:
- `src/layouts/Layout.astro`:head 引入 Raleway(`<link>` preconnect+stylesheet);body 顶部插入 `#loading-screen` 动画屏(用户给的 HTML/CSS 还原,h1 文案保留 Lorem ipsum);所有 `x-show` 下拉补 `x-cloak`(含 login.astro 语言 ul);footer 后加 `is:inline` 淡出脚本(`window.load` 后淡出,最少 500ms / 最多 3s 兜底)。
- `src/styles/global.css`:追加**作用域隔离**的 loading 样式(全部在 `#loading-screen` 内;`@keyframes` 改名 `ls-text-shadow`;`prefers-reduced-motion` 仅限加载屏;避免 `#fdf9fd` 背景/`Raleway`/`a` 颜色污染整站黑白调性)。
- **验证**:lint 0;`GET /en` 200,输出含 `id="loading-screen"`、`x-cloak`(×13)、淡出脚本、Raleway link 均到位。
- **提醒点**:loading 屏用彩色(与站点纯黑白调性冲突),是用户明确要求;若要统一风格可改 `#loading-screen` 背景为白/黑。h1 文案是占位 Lorem ipsum,用户可能想换成品牌名。
## 加载屏黑白化 + 文案替换(12:1x)✅
用户要求加载屏改成黑白(与站点纯黑白调性一致)并换文案。改动:
- Layout.astro 加载屏 h1 Lorem ipsum dolor sit amet. → Portfolio Studio(取自 siteTitle 品牌名),删掉 demo 残留(Hover over text to pause / Inspired by Tomas Brunsdon dribbble 链接),加小号副标 <p class=ls-sub>Loading</p>。
- global.css 加载屏配色:背景 #fdf9fd→#fff、文字 #011a32→#0a0a0a、链接 #024794→#0a0a0a;动画 keyframes 彩色 shadow(#0c2ffb/#2cfcfd/#fb203b/#fefc4b)改灰阶(#000/#3a3a3a/#777/#bbb),保留偏移运动;h1 字号 5em→clamp(2.5rem,8vw,5rem) 防移动端溢出;新增 .ls-sub 等宽大写灰字样式。
- 验证:GET /en 200,输出含 Portfolio Studio + ls-sub Loading,已无 Lorem/dribbble;CSS 黑白校验全通过(无 fdf9fd/0c2ffb/024794/011a32)。lint 0。
## 加载屏重构:is:inline → 独立组件 + 打包脚本(12:2x)✅
用户认为 is:inline 不合理,要求优化(Alpine 或组件化)。结论:抽 src/components/LoadingScreen.astro 组件,最合理——不需为小事引入 Alpine(且有 window.load 竞态),普通 Astro 打包 <script> 是 module/defer,在 load 前执行无竞态,比 is:inline 更工程化。改动:
- 新建 LoadingScreen.astro:标记(h1 Portfolio Studio + p.ls-sub Loading) + <style is:global>(原 global.css 加载屏 CSS 整段移入) + 打包 <script> 隐藏逻辑(原 is:inline IIFE 照搬,去掉 is:inline)。Raleway <link> 从 Layout head 移入组件(Astro 自动 hoist 到 head)。
- Layout.astro:引入组件、用 <LoadingScreen /> 替换内联标记、删除底部 is:inline 脚本、删 head Raleway 链。
- global.css:删除加载屏 CSS 块,仅留 @import tailwindcss + [x-cloak]。
- 验证:lint 0;GET /en 200,loading-screen 标记在、无 is:inline、旧 IIFE 已移除、Raleway 仍在。教训:Astro 中全局一次性 UI(loading/overlay)应封装为组件而非散在 Layout+global.css。
## 未登录预览门禁:列表仅显前5张+登录提示(14:2x)✅
需求:未登录用户只能看图集前5张图片,其余提示登录。实现:RunwayLooks/StreetSnaps 两列表页统一加未登录预览门禁。
- 新增 i18n 键 login to view all photos(cn: 登录查看全部图片)。
- 脚本加常量 PREVIEW_LIMIT=5;Alpine data 加 getter visibleItems(getUser() 返回全部,否则 slice(0,5))、showLoginGate(!getUser(); items.length>5)。
- 模板 x-for 源 items→visibleItems;卡片列表后插门禁块(x-show=showLoginGate,含 Preview 标题+loginGateText+登录按钮开 showLoginModal);分页器 x-show 加 getUser() 条件。
- 验证:lint 0;GET /en/runway-looks 与 /en/street-snaps 均 200,含 visibleItems x-for、showLoginGate div、x-text=loginGateText、pager x-show=getUser(); lastPage>1(HTML 转义为 &amp;&amp; &gt;)。登录后门禁消失、分页器出现、显示全部。
## 未登录预览门禁后端硬截断 + 重启踩坑(15:0x)✅
需求:前端门禁只是障眼法,接口一抓全露馅——后端也得改。改动:
- 后端新增 dto.PreviewLimit=5;response.PagePreview 支持 preview 标记;runway/street 列表 handler 未登录时强制 Page=1,Size=5,且 total>5 才回 preview:true。
- 关键修复 PublicFirstPageAuth:原仅 page>1 解析 token,导致 page=1 已登录用户无 Context、被误判匿名全截 5 条;改为始终解析 Bearer(有效则写入 Context),仅 page>1 强制。新增 isAnon(c) 辅助(同包 handler)。
- 前端:getArticles/getStreetSnaps 透传 preview;Alpine loadPage 存 this.preview,showLoginGate 改用 !getUser() && (preview===true || items.length>PREVIEW_LIMIT),visibleItems 仍 slice(0,5) 防御。
- 验证(node 脚本):anon runway/street 各 5 条+preview:true、page=2→401;root 登录回 24/12 条无 preview。前端 /en/runway-looks、/en/street-snaps 均 200,api.ts lint 0。
- 重启大坑(重要):go build -o server-bin 在 Windows 产出无扩展名 server-bin,但运行的是 server-bin.exe(旧)。Start-Process server-bin 被 Windows 解析成旧 .exe,导致首轮没生效。修正:改用 go build -o server-bin.exe 覆盖 .exe 并启 .exe。已记 MEMORY。
## 预览门禁纠正:列表页限制 → 详情页限制(16:3x)✅
用户澄清:之前把"未登录最多看 5 张"做成了列表页限制(RunwayLooks/StreetSnaps 列表只显 5 个图集)是误读。真实意图是**列表页照常展示全部图集,限制发生在点进图集详情后——未登录仅看前 5 张图片**。改动:
- 前端列表页回退:StreetSnaps.astro 删 visibleItems/showLoginGate/preview 字段 + 删门禁块 + x-for 改回 items + 清 PREVIEW_LIMIT/loginGateText 残留常量;RunwayLooks.astro 清 PREVIEW_LIMIT/loginGateText 残留常量(列表逻辑上一轮已回退)。lint 0。
- 详情页(Item.astro runway / StreetSnap.astro street)本就已是「前5张 SSR + extraImages 登录后补拉 + 登录门禁」结构,无需改。
- 后端:源码早已把截断从列表接口移到详情接口(列表 response.Page 全量;详情未登录截断 Images[:5]+preview:true)。但**运行中的二进制是旧的列表截断版**(验证显示列表只回 5 条、详情回全量 6 张且无 preview)→ 重编译 `go build -o server-bin.exe`、杀旧 PID 18912、启新 .exe。
- 验证(node+SSR):anon 列表全量无 preview;anon 详情 5 张+preview:true;root 登录详情完整 6 张无 preview;`/en/item/s000zAwwQ` 与 `/en/item/r0042h1mx` SSR 200、`data-preview="true"`、画廊 5 张图、含 Preview 门禁块;两个列表页已无 visibleItems/showLoginGate。
## 详情页「剩余 N 张」占位遮罩(17:0x)✅
用户补充:详情页未登录只显前 5 张,但用户会以为图集就 5 张——要求对剩余图片做空图遮罩并写"登录后查看"之类提示。改动:
- 后端 `response.DataPreview` 新增参数 `imageTotal int`,响应包写入 `image_total`(截断前的图片真实总数);走秀/街拍两详情 handler 在截断前 `imageTotal:=len(detail.Images)` 透传。前端 `getSsrArticle`/`getSsrStreetSnap` 读 `json.image_total`,`SsrArticleResult`/`SsrStreetSnapResult` 增 `imageTotal?` 字段;en/cn `item/[id].astro` 把 `imageTotal` 传给 `<Item>`/`<StreetSnap>`。
- 详情组件 `Item.astro`/`StreetSnap.astro` 增 `imageTotal` prop,算 `lockedCount=preview?max(0,imageTotal-previewImages.length):0`;画廊在 previewImages 之后、extraImages 之前插入 `lockedCount` 个空图 `<figure>`(锁图标 + "login to view" + "+N photos",`x-show="!getUser()&&preview"`+`x-cloak`,点击 `goLogin()`)。已登录 `getUser()` 为真 → 遮罩隐藏、由 `extraImages` 真实图替补。
- i18n 新增 `login to view`(cn 登录查看)/`photos`(cn 张照片)。
- 后端重编译 `go build -o server-bin.exe`、杀旧 PID 32508、启新 .exe(PID 29468);`Invoke-RestMethod` 验证 anon runway image_total=8 preview=true 返5、street image_total=6 preview=true 返5。
- 验证(node 解析 SSR HTML):走秀页画廊 5 张真实图 + 3 个"login to view +3 photos"遮罩 + 底部 Preview 门禁块;街拍页 5 张 + 1 个"login to view +1 photos"遮罩。功能对任意 image_total 通用(含用户举例的 20→15)。
## 遮罩运行时不显示:Alpine 表达式作用域坑(18:4x)🔧
用户反馈"还是只展示 5 张图"——SSR 静态 HTML 确有遮罩,但浏览器运行时看不到。排查:
- 后端 verified:`GET /api/v1/public/runway-looks/r0042h1mx`(anon)返 `image_total=8, preview:true` 且 images 截断 5 张;已登录(带 Bearer + 前端签名)返 `image_total=8, images.length=8, preview=undefined` → 后端全量正常。
- **根因**:遮罩与底部门禁用 `x-show="!getUser() && preview"`,但 `getUser` 是 `.astro` `<script>` 里 `import` 进来的模块符号——Alpine 属性表达式作用域访问不到它(只认组件 data 属性 / `$magic` / global),求值抛 ReferenceError → 绑定失效、元素卡在 `x-cloak` 的 `display:none`。**上一条"验证"只看 SSR 静态 HTML,没跑 Alpine,所以误判成功**。列表分页器 `StreetSnaps.astro:215` / `RunwayLooks.astro:253` 的 `x-show="getUser() && lastPage>1"` 同病。
- **修复**:详情组件 `Item.astro`/`StreetSnap.astro` 与列表组件 `RunwayLooks.astro`/`StreetSnaps.astro` 各增响应式 data 属性 `isAuthed:false`;`init()` 用 `this.isAuthed = !!getUser()` 初始化、`auth:login` 事件置 `this.isAuthed = true`;所有相关 `x-show` 改用 `isAuthed`(遮罩 `!isAuthed && preview`,分页器 `isAuthed && lastPage>1`)。`getUser()` 仍在组件 method 内调用(作用域OK)。
- 验证:`fetch` 详情页 HTML 确认 `x-show="!isAuthed &amp;&amp; preview"` 出现 4 次(3 遮罩+1 门禁)、`getUser` 在 x-show 中 0 次。dev server (astro dev, :4321) 已热更新新代码。
## 遮罩与真实图不等高:遮罩缺 figcaption 行(18:5x)✅
用户反馈未登录时遮罩图高度与正常图不一样。根因:正常图 `<figure>` = `img aspect-[3/4]` + 底部 `figcaption`(图片 name 如 look01/img01 灰字一行,实测数据每张图都有 name);而遮罩 figure 直接把 `aspect-[3/4]` 套在 figure 上、无 figcaption → 总高比正常图矮一行 caption 高度,grid stretch 行为也不一致。修复:`Item.astro`/`StreetSnap.astro` 遮罩重构为与正常图**同构**——figure 不再带 aspect,内部先放 `div.w-full aspect-[3/4] flex items-center justify-center`(锁图标+login to view 居中的"图位"),再放与正常 figcaption 同 class 的 `figcaption`(灰字 `+N photos`,替代原图内那行 +N 小字,保证 caption 行高一致)。验证:SSR figcaption 顺序 = look01..look05 5 个正常 + "+3 photos"×3 遮罩、遮罩 figure 含 aspect-[3/4] 图位 div(×3),结构已同构。dev server 热更新生效。
## 去掉底部 Preview 大框门禁(19:1x)✅
用户觉得画廊后整块「Preview / login to view all photos + Login 按钮」门禁意义不大(画廊遮罩已有提示),要求去掉。改动:
- `Item.astro`/`StreetSnap.astro`:删除画廊后 `x-show="!isAuthed && preview"` 整块门禁 div(font-serif "Preview" + 副文案 + Login 按钮)及顶部仅供该块使用的 `loginGateText`/`loginModalSubmitLabel` 常量(列表组件 RunwayLooks/StreetSnaps 的 loginModalSubmitLabel 用于内嵌登录弹窗,保留)。
- `dictionary.ts`:删 `'login to view all photos'` key(无引用)。
- 验证:两详情页 SSR 无 gateText/gateDiv、画廊遮罩 x-show 仍在(走秀 3/街拍 1);lint 0。
- **编辑教训**:删除整块时两次把 replace_in_file 的 old/new_str 弄反导致重复插入门禁块,须先读文件确认再一次性删除所有重复块;改文件先想清楚替换方向再提交。