12 KiB
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 /en200,输出含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 链接),加小号副标
Loading
。 - 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 从 Layout head 移入组件(Astro 自动 hoist 到 head)。
- Layout.astro:引入组件、用 替换内联标记、删除底部 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 转义为 && >)。登录后门禁消失、分页器出现、显示全部。
未登录预览门禁后端硬截断 + 重启踩坑(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/r0042h1mxSSR 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/cnitem/[id].astro把imageTotal传给<Item>/<StreetSnap>。 - 详情组件
Item.astro/StreetSnap.astro增imageTotalprop,算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 && 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 弄反导致重复插入门禁块,须先读文件确认再一次性删除所有重复块;改文件先想清楚替换方向再提交。