You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

7.0 KiB

设计:公开主页预览 + 完整列表子页(文章 / 时光机 / 阅读)

日期:2026-04-18
状态:已定稿(与产品对话一致)

1. 背景与目标

公开主页 /@:publicSlug「展示」「阅读」 两种布局下,文章、时光机、阅读(RSS 公开项) 目前或拉全量、或在「阅读」模式主栏内分页,首页信息密度与首包体积都不理想。

目标

  • 首页每类仅展示 最新 5 条 预览,并显示 总数
  • total > 5 时提供 「查看全部」 入口,进入 独立子页面;列表 每页 10 条,分页体现在 URL?page=),便于分享与刷新。
  • 瘦身 聚合接口:首页只拉预览 + 总数;完整列表仅由 分页 API 提供。
  • 在现有 Nuxt UI 与绿色主色体系内 统一预览与列表的视觉层级(卡片、时间轴、阅读行),不做换肤级重构。

2. 产品规则(已确认)

维度 规则
布局范围 「展示」与「阅读」两种模式都做,三块行为一致:预览 5 条 + 条件「查看全部」。
预览条数 三类 统一5 条(PUBLIC_PREVIEW_LIMIT = 5)。
列表每页条数 10 条(PUBLIC_LIST_PAGE_SIZE = 10),仅服务端生效;请求中的 pageSize 忽略或拒绝,防止任意大页。
「查看全部」 仅当 total > 5 时显示;total === 0 不展示该区块;1 ≤ total ≤ 5 只展示预览,不显示「查看全部」。
子页路由 /@:publicSlug/posts/@:publicSlug/timeline/@:publicSlug/reading(与界面「阅读」一致,路径不用 rss)。
聚合接口 GET /api/public/profile/:publicSlug 改为每类 { items, total }破坏性变更;当前仅 app/pages/@[publicSlug]/index.vue 消费该聚合响应,改版面时一并更新类型。
分页接口 三类独立 GET.../posts.../timeline.../reading,Query page(默认 1,非法或 <1 规范为 1)。
空页 页码超出末页时 items: []对空页返回 404total 与规范化后的 page 仍返回)。
可见性 visibility === public 的内容;unlisted / share 链路不在本次范围。

3. 非目标

  • 管理后台(/me/*)列表与分页改造。
  • RSS 同步策略、抓取频率、私有订阅源管理。
  • 全文搜索、标签筛选、按日期归档视图。
  • 为列表页单独做 SSR HTML 的复杂 SEO(仅轻量 title/meta 即可)。

4. API 契约

4.1 GET /api/public/profile/:publicSlug(变更)

响应中 posts / timeline / rssItems 从数组改为对象(字段名保持与现有前端语义一致,减少无谓重命名):

posts: {
  items: Array<{ title, excerpt, slug, publishedAt, coverUrl }> // 长度 ≤ 5
  total: number
}
timeline: {
  items: Array<{ /* 与现 listPublicTimelineBySlug 公开字段一致 */ }>
  total: number
}
rssItems: {
  items: Array<{ /* 与现 listPublicRssItemsBySlug 公开字段一致 */ }>
  total: number
}

userbiolinks 保持现状。total 必须通过 数据库计数(或与分页等价的稳定查询)得到,禁止拉全表后在内存计算。

4.2 新增分页接口

方法 路径 Query
GET /api/public/profile/:publicSlug/posts page(可选,默认 1)
GET /api/public/profile/:publicSlug/timeline 同上
GET /api/public/profile/:publicSlug/reading 同上

与已有 GET .../posts/:postSlug 不冲突:列表与单篇分文件注册即可。

成功响应(三端统一形状):

{
  items: [...]   // 当前页,最多 10 条
  total: number
  page: number   // 规范化后的页码
  pageSize: 10   // 固定常量,与 PUBLIC_LIST_PAGE_SIZE 一致
}

404publicSlug 对应用户不存在或非 active,与现 profile 行为一致。

4.3 服务层职责

#server/service/poststimelinerss 中新增或重构为:

  • 每类:预览items 上限 5 + total)、分页pageitems + total + 规范化 page + pageSize)。
  • 现有无 limit 的 listPublic* 应改为复用上述逻辑或内部 helper,避免重复 SQL;RSS 现有 limit(200) 的公开列表实现应由 正式分页 + count 替代,不再依赖硬编码 200 作为「全量」。

5. 前端:路由与数据流

5.1 新增页面

文件 路由
app/pages/@[publicSlug]/posts/index.vue /@:publicSlug/posts
app/pages/@[publicSlug]/timeline/index.vue /@:publicSlug/timeline
app/pages/@[publicSlug]/reading/index.vue /@:publicSlug/reading

均使用 layout: 'public'。列表数据来自对应分页 GETpageroute.query.page 同步;翻页优先使用 router.replace 更新 query,减少历史栈噪音。

5.2 主页 @[publicSlug]/index.vue

  • 仅请求 GET .../profile/:slug,使用返回的 items / total
  • 删除「阅读」模式下主栏内对全量数组的 slice + UPagination;主栏三块与「展示」模式一致:预览 + 条件「查看全部」
  • 侧栏数字改为 total

5.3 分页 UI

使用 UPaginationtotal 为接口 totalitems-per-page 固定 10。首屏从 URL 读取 page;非法字符串是否在客户端先规范为 1 由实现计划写明,并与服务端规范化一致。

6. 体验与美化

  • 文章预览:展示 日期(沿用 formatPublishedDateOnlyoccurredOnToIsoAttr 等工具,与旧 detailed 列表信息层级一致)。
  • 时光机预览:保留 圆点 + 竖线 时间轴;与现 showcase 块级卡片协调。
  • 阅读预览:从纯链接改为 块级行(标题 + hostname 次要文案),hover 边框/背景 微变化,对齐旧 detailed 「阅读」列表气质。
  • 「查看全部」UButton outlineghost + 右箭头;位置在区块标题行或区块底部统一,两种布局模式同一套位置
  • 列表页:分隔线/密度对齐现 detailed 列表;UEmptyUAlert(404)、加载态与现站一致。

7. 测试与验收

  • 手工:主页预览条数、total 与侧栏、按钮显隐(total > 5);子页翻页、URL、刷新、超大 page 空列表;单篇文章 /@/posts/:slug 仍可访问。
  • 自动化(可选):若项目已有 service 层测试模式,可为 计数 + limit/offset 增加用例;API 路由可做轻量集成测试(视仓库惯例)。

8. 自检记录(定稿前)

  • 占位符:无 TBD。
  • 一致性:预览 5、列表 10、按钮仅 total > 5、空页不 404,与对话一致。
  • 范围:仅公开 profile 与三列表页;不含后台。
  • 歧义rssItems 键名保留在 profile 响应中;公开分页路径使用 /reading,与页面路由一致。