Files
frontend_v2/.workbuddy/memory/2026-08-24.md
toom1996 fbbac26903
Some checks failed
deploy / deploy (push) Has been cancelled
update
2026-08-24 21:52:47 +08:00

12 KiB
Raw Permalink Blame History

2026-08-24 工作日志

品牌弹窗「热门推荐」做成独立 HOT 字母 tab(19:52)

  • 用户要求:把品牌弹窗里的「热门推荐」单独做成一个 letter(字母条里的独立 tab),并且打开弹窗默认就选中这个热门字母。
  • 范围:仅品牌筛选弹窗的 A-Z 字母条(showsmodal-az),存在于 src/pages/cn/shows.astro 与 src/components/RunwayLooks.astro(en /en/runway-looks/)。en /shows/ 没这个弹窗(只有侧边栏),未动。
  • 改动(两文件一致):
    1. 字母条最前插入 <button class="az az-hot" @click="modalPickLetter('HOT')">HOT</button>;原 A-Z 不变。
    2. modalPickLetter(l) 顶部加 if (l === "HOT") { this.modalKeyword=""; await this.modalLoadHot(); return }。
    3. modalLoadHot() 开头设 this.modalLetter = "HOT"(让 HOT 按钮 active 高亮 + 提示「热门推荐」显示)。
    4. openBrandModal() 把 this.modalLetter = "" 改成 = "HOT",打开即默认选中热门。
    5. 提示文案 x-show="!modalKeyword && !modalLetter" 改为 && modalLetter === 'HOT'(只在 HOT 态显示「热门推荐」)。
    6. CSS 新增 .showsmodal-az .az-hot { w-auto px-2 font-sans font-bold tracking-[0.12em] text-[9px] }(HOT 是 3 字母,宽于 24px 方块)。
  • 验证:npm run build BUILD_EXIT=0;两文件 read_lints 0;dist 的 cn/shows 与 en/runway-looks index.html 均含 az-hot + modalPickLetter('HOT') 各 1 处;python -m http.server 8088 托管 dist/client 预览两页弹窗。
  • ⚠️ 假数据 id(hot-chanel 等)仍非真实 hashid,勾选假热门去筛仍返回空,等真实 SSG /api/ssg/brands/popular 接通即生效。

删除侧边栏 "Search designer" 搜索框(17:06)

  • 用户要求去掉 DESIGNERS 侧边栏的 "Search designer…" 搜索框及相关的 JS。
  • 范围:仅侧边栏那个输入框(用 keyword state 驱动)。品牌弹窗里的"搜索品牌名…"(modalKeyword/modalSearch)是另一套,保留不动。
  • 三文件统一改(src/pages/shows.astro en、src/pages/cn/shows.astro、src/components/RunwayLooks.astro):
    1. 删 <input class="showssearch" ... x-model.debounce.150ms="keyword" />。
    2. 删 keyword: "" state。
    3. visibleBrandItems getter 去掉 if (kw) {...} else {...} 的 keyword 分支,简化为只走字母分组展示(else 分支)。
    4. reset(clearAll/resetFilter 之类)里删 this.keyword = ""。
    5. shows.astro 额外把 x-show="hasMoreBrands && !keyword" 改回 x-show="hasMoreBrands"(Show all 链接不再因搜索态隐藏)。
    6. 删 .showssearch 死 CSS(该类仅被上述输入框使用)。
  • 验证:npm run build BUILD_EXIT=0;三文件 read_lints 0 错误;(?<![a-zA-Z])keyword(?![A-Za-z]) 全仓 0 残留(仅留 modalKeyword);cn/shows 与 shows 产物 Search designer/showssearch 各 0 处。

品牌筛选「最多只能选一个」修复

  • 用户反馈品牌筛选只能单选。排查发现:仓库里品牌筛选逻辑本就是多选(selectedBrands 数组 + toggleBrand 增删),但 DESIGNERS 侧边栏 <ul class="showschecks"> 漏了 x-for="item in visibleBrandItems",导致品牌勾选框一行都不渲染——侧边栏品牌列表完全失效,用户只能走弹窗。
  • cn 版 src/pages/cn/shows.astro 弹窗还硬卡了「最多 5 个」(modalMaxReached + :disabled + toggleModalBrand 内 >=5 return + 底部「已选 X / 最多 5 个」)。
  • 改动:
    • src/pages/cn/shows.astro 与 src/pages/shows.astro(en):补回 <ul> 上的 x-for="item in visibleBrandItems" :key="item.key",让 DESIGNERS 多选列表正常渲染(按字母分组 + checkbox + toggleBrand 多选)。
    • cn 版额外:删除 modalMaxReached 计算属性、toggleModalBrand 的 5 上限 guard、@change 上的 :disabled、底部文案改「已选 X 个品牌」——弹窗改为无上限纯多选。
  • en 版 shows.astro 没有品牌弹窗(只用侧边栏列表 + "Show all N designers" 展开),补 x-for 后即恢复正常多选。
  • 验证:npm run build BUILD_EXIT=0;产物 HTML 含 visibleBrandItems(cn/en 各 1)、modalMaxReached 残留 0、最多 5 个 残留 0。
  • 注意:去掉 5 上限后,若用户勾选大量品牌(如 50+),getArticles({ brand_ids: [...] }) 会带长数组请求后端;后端若用 IN (...) 应无碍,但极端多选需关注接口性能(用户暂未提,先不限制)。

品牌筛选「改成单选」纠正(en 版)

  • 纠正:上一笔把品牌筛选做成"多选"是理解反了。用户原话"最多只能选一个"的真实意图是要单选(最多 1 个),且只改 en 版(src/pages/shows.astro)。
  • 改动(仅 en):toggleBrand(id) 由增删数组改为「已选则清空、否则替换为单元素数组」this.selectedBrands = [id] / = []。selectedBrands 仍为数组(接口 / pills / :checked 绑定都依赖数组形态),只是长度恒 ≤ 1,checkbox 视觉上等同 radio。
  • cn 版保持多选(用户说"你就改en的就行"),即 cn/shows.astro 仍是补回 x-for + 去 5 上限的多选状态。⚠️ 现状 en 单选 / cn 多选,两端行为不一致,是用户明确要求的。
  • 验证:npm run build BUILD_EXIT=0;read_lints 0 错误;预览 http://localhost:8090/shows/。

品牌筛选「cn 也改单选」纠正(14:36)

  • 用户又提"现在品牌的筛选不是最多 5 个吗,我希望最多只能选一个"。说明他认知里 cn 仍是旧"最多 5 个"弹窗(实际上一条我已把 cn 改成无上限多选)。最终意图 = 品牌筛选全局单选(max 1)。
  • 这次把 cn 版也改成单选:src/pages/cn/shows.astro 的 toggleBrand(侧边栏)+ toggleModalBrand(弹窗)都由增删数组改为「已选则清空、否则置单元素数组」;弹窗底部计数文案由「已选 X 个品牌」改为「已选 X / 1 个品牌」明示上限。
  • 现状:en + cn 均已单选(max 1),两端一致。en 在上一笔已完成,cn 本笔补齐。
  • 验证:npm run build BUILD_EXIT=0;read_lints 0 错误;预览 http://localhost:8090/cn/shows/。

年份筛选改为前端写死(不再调后端 getYears)

  • 用户要求年份/季节这类筛选"改成品牌那种的数据,直接写死就好了,因为不会特别多"。
  • 排查:Collection(selectedCt: rtw/menswear/couture/resort/pre_fall) 与 Season(selectedSeason: spring/fall) 本就是前端写死的静态 <li> checkbox;只有 Year 是动态的——frontmatter 调 getYears(30) 从后端拉年份、后端挂了才回落 yearSeed=[2026..2021]。
  • 改动(3 文件一致:src/pages/shows.astro en、src/pages/cn/shows.astro、src/components/RunwayLooks.astro):
    • 删除 import { ... getYears ... }、删除 let yearChips / yearSeed / getYears try-catch 整段。
    • frontmatter 新增 const yearOptions = [2026, 2025, 2024, 2023, 2022, 2021, 2020, 2019, 2018, 2017](写死,2026→2017 共 10 个)。
    • 渲染 {yearChips.map} → {yearOptions.map},其余(checkbox / x-model=selectedYear / refreshFilter)不变,年份仍为多选。
  • 验证:npm run build BUILD_EXIT=0;getYears|yearChips 全仓 0 残留;cn 产物含 >2017</>2026</value="2026"。
  • ⚠️ 取舍:写死后2005–2016 这批老秀场将无法按年份筛选(网格仍展示,只是筛不出)。当前 yearOptions 是一行可编辑数组,要补老年份直接往里加即可。用户未提,先保持 10 年窗口。
  • 现状:Collection/Season/Year 三套筛选均为前端写死静态数据;仅 DESIGNERS(品牌)仍由后端 getBrands() 拉取(用户未要求改品牌来源)。

年份区间改为"当今年份往前 15 年 + X年前",Collection/Season 换写死示例数据

  • 用户纠正:年份应为 2026 往前推 15 年 → 到 2011(之前误写成 2001),并加末项「2011年前」。
  • 年份改为动态生成:const currentYear = new Date().getFullYear(); const yearFloor = currentYear - 15; 循环 push yearFloor..currentYear,末项 push {value:'before'+yearFloor, label: yearFloor+'年前'}。SSR 构建时 new Date() 取构建年(现 2026),故产物固定 2026→2011 + 「2011年前」共 17 项,selectedYear 为 string[](含 "before2011" 哨兵)。
  • Collection / Season 改为写死示例(假)数据数组 collectionOptions / seasonOptions({value,label}),模板 map 渲染(与 Year 同套路)。Collection 现 8 项(rtw/menswear/womenswear/couture/resort/pre_fall/bridal/swimwear),Season 现 4 项(spring/fall/cruise/holiday)。ctLabel() 的 map 在 shows.astro / cn/shows.astro 两处补了 womenswear/bridal/swimwear 标签(RunwayLooks 无 ctLabel)。
  • 仍内联渲染(用户 earlier:「数量<10 不用弹窗」):Collection(8)/Season(4) 均 <10 内联;Year 虽 17 项但用户要求「都展示出来」故也内联。
  • 验证:npm run build BUILD_EXIT=0;read_lints 0;cn 产物含 2011年前/value="2011"/value="2026"/Womenswear/Cruise/before2011。
  • ⚠️ 假数据提醒:Collection 里 womenswear/bridal/swimwear、Season 里 cruise/holiday 是示例占位,后端 collection_type/season 未必识别 → 勾这些可能筛不到结果。确认后直接改 collectionOptions/seasonOptions 数组即可(已加注释说明)。before2011 同理需后端支持才能真正筛「2011 年前」,目前仅展示。

其他(同日)

  • 上午修 RunwayLooks.astro / shows.astro / cn/shows.astro 三处 <script> 块 ROUTES/clientHref ts(2304):frontmatter 的 import 无法在客户端 <script> 作用域使用,需在各自脚本块内重新 import(见 MEMORY.md「Astro frontmatter 与 script 作用域隔离」)。

新增「热门品牌 / HOT」区(SSG 接口 + 假数据回落)

  • 用户要求:选择品牌时加一个热门品牌区,进来最先展示热门品牌;数据走 SSG 接口获取,先写假数据看效果。
  • 关键架构点:SSG 接口 getSsgPopularBrands()(src/lib/api.ts,调 /api/ssg/brands/popular,用 BASE_API_SSG 8091 内部端口,仅 Node 构建期可用)不能在浏览器端调。所以改为 frontmatter 构建期调 getSsgPopularBrands(12),映射成 {id,name,article_count},拿不到就回落前端写死的 FAKE_HOT_BRANDS(12 个时装屋:Chanel/Dior/Gucci/.../Bottega Veneta),烤进 <script id="showshotbrands-data"> JSON,Alpine 在 alpine:init 里 getElementById 读出 hotBrands。
  • 三文件改动一致(src/pages/shows.astro en、src/pages/cn/shows.astro、src/components/RunwayLooks.astro):
    • import 加 getSsgPopularBrands;frontmatter 加 FAKE_HOT_BRANDS 常量 + hotBrands 抓取/回落;markup 加 showshotbrands-data JSON 脚本(RunwayLooks 额外补了缺失的 showsbrands-data 脚本——它之前只靠运行时 getBrands 撑弹窗,allBrands 实际为空)。
    • Alpine:alpine:init 读 hotBrands 入组件;pills 的 findName 兼容 hotBrands(假 id 也能显示名)。
    • 侧边栏 HOT 区:shows.astro / cn/shows.astro 的 DESIGNERS showsg-b 顶部插入「HOT · Popular / 热门品牌 · HOT」分组,渲染 hotBrands 为 chip(checkbox 绑 toggleBrand/selectedBrands,含 has-[:checked] 选中高亮,纯 Tailwind 工具类,无需改 <style>)。RunwayLooks 侧边栏品牌列表是注释掉的,故只改弹窗、不插图侧栏 HOT。
    • 弹窗热门:modalLoadHot() 由「getBrands({featured:1})」改为「this.hotBrands 映射成 modalResults」,进入弹窗即先展示热门("热门推荐" 标题原已存在);搜索/选字母时仍走 getBrands 拉全量。
  • 验证:npm run build BUILD_EXIT=0;三文件 read_lints 0;dist 三页(shows/cn/shows/en/runway-looks)均含 showshotbrands-data + Chanel/Bottega Veneta。
  • ⚠️ 假数据取舍:FAKE id 形如 hot-chanel,非真实 hashid → 勾选假热门品牌去筛选会返回空(后端不认)。等真实 SSG /api/ssg/brands/popular 接通后,返回真实 hashid,筛选即生效。RunwayLooks 弹窗 modalMaxReached 返回 >=1(且 :disabled 据此),实际等同单选——属既有状态,未动。