8.8 KiB
门户前端调整工作记录
时间窗口:2026-04-19 ~ 2026-04-23 范围:sub2api 项目前端门户页(公开访问的 5 页:Landing / Docs / Login / Register / Pricing) 目标:完成多语言切换、新增 Pricing 页、消除导航栏抖动、清理对外不应展示的技术细节
阶段一:门户多语言 + Pricing 页
问题:门户 4 页(Landing / Docs / Login-narrative / Register-narrative)当时是 fidelity-port 自 Claude Design zip 的,约 100+ 处中文硬编码在 template 里。Pricing 页未实现。i18n 框架(vue-i18n)虽然到位,但门户页基本没用上。
做了什么:
- 新建
components/puro/PuroLocaleSwitcher.vue—— 深色科技风的语言切换器,复用setLocale()核心,与 puro.css 设计 token 对齐(区别于 admin 用的LocaleSwitcher.vue) - 抽取 4 页所有中文到 i18n key(
landing.*、docs.*、auth.narrative.*命名空间),同步生成英文翻译;总计约 130 个 leaf key - 新增
/pricing路由 +views/pricing/PricingView.vue(约 500 行)+components/puro/PricingCalculator.vue子组件 - Pricing 页从一开始就 i18n-native(约 100 个 key),含 4 档定价 tier、cost calculator、12 工具 grid、10 FAQ、final CTA
- 关键决策(写进 spec 锁定):
- 定价数字使用 zip 的
$9.9 / $29.9 / $99作为占位,配// preview · 最终定价以开售为准标签 - 未实现的功能(priority scheduling、zero-log mode)配
SOON灰标签,不删除展示 - Binding 卡片暂指
/register;联系商务用mailto:contact@puro.im - Cost calculator 算法照搬 zip JS(
puro = official × 0.3),配// estimated · 以实际计费为准标签 - 移除 zip 中的
注册送 $5 测试积分文案(Stage 1 决策)
- 定价数字使用 zip 的
执行方式:subagent-driven-development(Plan 文件 docs/superpowers/plans/2026-04-20-portal-i18n-pricing.md,10 个任务,3 个 subagent 实现 + 1 次回归修复)
已合并 PR:#2 → main 77bb69b2
阶段二:Nav 抖动修复
问题:用户报告点击导航栏「产品/定价/文档」切换时元素位置发生轻微抖动。
根因分析:
第一直觉是 active class 引起的字体 weight 变化。grep CSS 后发现并非如此 —— .active 只改 color 不影响布局。
真正原因是:当前 Landing/Docs/Pricing 三个 View 各自在 template 里写了一份完整的 nav(包括按钮文案)。Vue Router 切换路由时销毁整棵 view 组件树(含 nav),重新挂载下一个。虽然视觉上"看似同一个 nav",实际是三份独立拷贝被替换。
进一步发现两份拷贝间有差异:
- Docs 的注册 CTA 文案是
注册(2 字符);Landing/Pricing 是免费试用 →(5+1 字符)—— 按钮宽度不同 - Docs 的英文
Product(单数)vs 其他两页的Products(复数) - Pricing 的「定价」用
<a href="#" class="active">—— 点击会 scroll to top 且 URL 改成/pricing#
做了什么(分两步):
步骤 1(短期) —— 文案统一:把三份 nav 的内容对齐到完全一致
docs.nav.signup:注册→免费试用 →docs.nav.products(en):Product→Products- PricingView 的
<a href="#">→<router-link to="/pricing"> - commit
779005e1
步骤 2(结构性修复) —— 抽 PortalLayout:
- 新建
components/layout/PortalLayout.vue—— 包含 nav +<router-view />+ footer - 路由改为嵌套结构:
/、/docs、/pricing作为 PortalLayout 的子路由 - 三个 View 删掉各自的 nav / footer /
bg-glow/.puro-page包装 - 新增
portal.nav.*i18n 命名空间,删除原来的landing.nav.*/docs.nav.*/pricing.nav.*三份重复 router-link用active-class="active"prop 替代硬编码 classscrollBehavior加上 hash 锚点跳转(offset 80px绕 sticky nav)- commit
e7f3fe5b
结果:路由切换时 Nav 不再卸载重挂,真·SPA 行为。
已合并 PR:#3 → main 291e3bfe
阶段三:对外内容清理
Pass 1:删除 iShare 引用
- 用户要求:项目目前定位是独立运营,不要在前端展示 iShare 相关字样
- 影响:
docs.sections.getKey.note一个 key("未来通过 iShare 入口开放订阅购买")+ DocsView 对应的<p class="note"> - commit
623a7518,PR #4 → main5c4b2980
Pass 2:移除 footer 技术细节
- 用户要求:不要透露项目技术开发细节
- 删除:
更新日志链接(指向 Gitea commits)、git.puro.im链接、GitHub ↗链接、fork of Wei-Shaw/sub2api字样 - footer-meta 简化为
© 2026 puro.im - 同步删除
linkChangelogi18n key - commit
2b6b5fc6,PR #5 → main8be87883
关键文件清单
新增:
frontend/src/components/puro/PuroLocaleSwitcher.vuefrontend/src/components/puro/PricingCalculator.vuefrontend/src/components/layout/PortalLayout.vuefrontend/src/views/pricing/PricingView.vue
修改(重点):
frontend/src/router/index.ts—— 加/pricing、嵌套路由、scrollBehaviorfrontend/src/i18n/locales/{zh,en}.ts—— 新增 portal/landing/docs/pricing/auth.narrative 命名空间frontend/src/views/{landing,docs,pricing}/*View.vue—— 全量 i18n 化 + 移除各自 nav/footerfrontend/src/views/auth/{Login,Register}View.vue—— narrative slot i18n 化frontend/src/components/layout/AuthLayout.vue—— 添加右上角语言切换器槽位
commit / PR 索引
| 阶段 | commit | 描述 |
|---|---|---|
| 一 | e711a203 |
feat(i18n): add PuroLocaleSwitcher |
| 一 | 63288818 |
mount switcher in Landing/Docs/AuthLayout |
| 一 | fc7e2767 |
landing i18n keys + EN |
| 一 | 73b39807 |
docs + auth narrative i18n |
| 一 | 13bdd8f8 |
restore dashboard link in models.note |
| 一 | b989c503 |
PricingView + calculator |
| 一 | 77bb69b2 |
Merge PR #2 → main |
| 二 | 779005e1 |
unify signup CTA |
| 二 | e7f3fe5b |
extract PortalLayout |
| 二 | 291e3bfe |
Merge PR #3 → main |
| 三 | 623a7518 |
remove iShare mention |
| 三 | 5c4b2980 |
Merge PR #4 → main |
| 三 | 2b6b5fc6 |
footer remove tech details |
| 三 | 8be87883 |
Merge PR #5 → main |
部署链路
每次 merge 到 main 后:
- Gitea webhook 触发 Drone CI
- Drone 运行 docker build + 推镜像
- VPS 上 docker compose 拉取新镜像 + 重启
- 总耗时约 3–5 分钟
每次部署完成的验证手段(curl):
- 取
https://ai.puro.im/主 HTML,对比/assets/index-*.js文件名 hash 是否变化(Vite 内容寻址) - fetch 新 chunk grep 关键字符串确认新代码上线
- 5 个门户路由全部 200
遗留事项 / 下次可继续
门户层面
- 浏览器实测 nav 持久化无抖动(用户已确认部署,肉眼验证待做)
- 翻译效果打磨(用户浏览后发现的不自然处随时改)
- Pricing 数字 / 功能列表定稿(从 preview 转正式)
- 后端
SOON标记的功能落地:zero-log mode、priority scheduling - Pricing FAQ 中的 24 小时上线新模型 等承诺类文案核实
Admin / User 页面(之前搁置的两个决策)
- 决策 1:Admin reskin 深度(A 全局换肤 / B 深度 port 两页 / C 全量重写)
- 决策 2:User-facing 页面 vs Admin 页面优先级
- 推荐路径:Phase 2.1 全局换肤 → 2.2 user pages → 2.3 admin pages
仓库卫生
backend/config.prod.yaml、backend/sub2api-linux、LOCAL_SETUP_NOTES.md在某次意外的git add -A中被提交到 main。- 用户判断:因 Gitea 是私有的,先不处理;仅要求前端层面不展示敏感信息(已通过 footer 清理实现)。
- 建议:下次添加配置 / 二进制类文件前,往
.gitignore补规则(backend/config.*.yaml、backend/sub2api-*等),避免再次误提。
经验教训
- 不要
git add -A—— 主 worktree 里有累积的本地文件(生产配置、二进制、笔记),一次性 stage 会把这些都带进 commit。后续都用git add <具体文件>。 - 设计稿里的 nav = 3 份独立 HTML ≠ Vue 应该 3 份独立 template —— port 设计稿到 SPA 时,要把跨页共享的部分(nav、footer、layout shell)一开始就抽到 layout 组件。
- Subagent 报告"我做完了"≠ 真的做对了 ——
73b39807提交里 subagent 静默删掉了<router-link>退化成纯文本。事后 grep 读 commit diff 才发现。下次 subagent 处理 i18n 抽取时,prompt 里要明确"原始 HTML 标签如<a>/<code>/<router-link>必须保留,使用<i18n-t>命名 slot"。 - fidelity port 优先靠
<i18n-t>+ 命名 slot,不要把带行内标签的句子拍平成纯文本 —— 这点已经写进下次的 i18n 提示模板中。