update
Some checks failed
deploy / deploy (push) Has been cancelled

This commit is contained in:
toom1996
2026-08-24 21:52:47 +08:00
parent 3814cf40ed
commit fbbac26903
10 changed files with 721 additions and 551 deletions

View File

@ -0,0 +1,81 @@
# 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` 据此),实际等同单选——属既有状态,未动。

View File

@ -28,6 +28,11 @@
- **正确做法**:用 `w-full`(强制 100% 父容器,常配 `max-w-[xxx]`),或 `w-full mx-auto`(保留大屏居中)。
- 适用范围:所有 Layout 内的页内根容器(包括 showsroot / blogs / about 等),**避免 `m-auto` 单独使用**。
## Astro frontmatter 与 `<script>` 作用域隔离(通用坑)
- **frontmatter 里的 `import` / 变量,在 `<script>`(客户端打包脚本)里拿不到**。两者是分别打包的:frontmatter 是服务端渲染期,`<script>` 是独立客户端 bundle。脚本里直接用 frontmatter 引的 `ROUTES` / `clientHref` 等,esbuild 不报错(当全局变量),但 **TS 语言服务报 `ts(2304) 找不到名称`**,运行时更是 `ReferenceError`。
- **正确做法**:脚本里要用什么,就在该 `<script>` 顶部重新 `import`。例:`import { clientHref, ROUTES } from "@/lib/routes"`。
- 排查方法:在 `<script>` 块里 `import` 行 grep,确认用到的标识符都已在脚本作用域内 import。本仓库曾在 RunwayLooks.astro、pages/shows.astro、pages/cn/shows.astro 三处脚本用了 `clientHref`/`ROUTES` 却只在 frontmatter import,统一补 import 修复(2026-08-24)。
## i18n 脚本内翻译陷阱(2026-08-23 踩坑)
- **Astro 对「打包型 `<script>`(module)不做 `{...}` 模板插值**。若在前端 `<script>` 里写 `slot.innerHTML = \`...{t('Hi')}...\``,构建后 `t('Hi')` 会原样留存在打包 JS 中 → 运行时 `t is not defined` 报错、脚本中断。
- **正确做法**:在 frontmatter 用 `const hiLabel = t('Hi')` 预翻译,再用 `<script define:vars={{ hiLabel, logoutLabel }}>` 把字符串注入脚本,脚本内用 `${hiLabel}` 引用。define:vars 会把 `const hiLabel="..."` 注入到该 page 的脚本作用域(en 得英文、cn 得中文)。