2026-06-28-template-list-query-pagination-design.md 8.2 KB

模板列表真实查询与分页设计

1. 背景

当前“我的发型模板”页面已经具备列表卡片、任务状态分流、批量删除和基础筛选 UI 骨架,但核心列表能力仍未落地:

  • 前端 TemplateListPage 目前仅在页面加载时调用一次 listTemplates(),查询项没有真正参与请求。
  • 后端 TemplatesService.list() 目前返回空列表,尚未提供真实数据。
  • 页面中仍保留“用途”筛选,但本轮需求明确要求先删除该项。
  • 分页控件仍是静态占位,不会随着真实数据变化。

本次设计目标是把模板列表补成可用的真实列表页,同时尽量保持现有卡片布局、任务分流和批量删除交互不变。

2. 目标

本轮交付后,模板列表页需要满足以下要求:

  • 使用真实后端数据展示模板列表。
  • 支持真实查询条件:
    • 搜索 q
    • 分类 category
    • 状态 status
  • 删除“用途”筛选,不再在前后端查询链路中使用该字段。
  • 支持真实分页:
    • 前端根据后端 total/page/pageSize 渲染页码
    • 查询条件变化时自动回到第一页
    • 即使只有一页,也保留分页控件区域
  • 保持现有模板跳转逻辑不变:
    • 未完成任务跳任务页
    • 已完成模板跳详情页

3. 非目标

本轮不处理以下内容:

  • 不新增“排序”真实能力,当前“排序:最近使用”继续保持占位态。
  • 不改造模板卡片视觉样式。
  • 不重构批量删除交互。
  • 不扩展“系统模板”或跨美发师共享列表。
  • 不修改模板详情页、模板编辑页和模板制作任务页的既有职责。

4. 方案选择

方案 A:最小闭环真实列表

做法:

  • 后端补齐 GET /templates 的真实查询与分页。
  • 前端将筛选项接入真实请求。
  • 删除“用途”筛选。
  • 保持现有 UI 结构和分页展示风格,仅把数据改为真实驱动。

优点:

  • 改动范围最小,能最快形成可联调的真实列表页。
  • 与当前模板任务流、详情页、编辑页职责兼容。
  • 风险低,适合当前 Case 节奏。

缺点:

  • “排序”仍然是占位态,列表能力不是最终形态。

方案 B:顺手一起做排序

做法:

  • 在方案 A 基础上,把排序也接成真实参数。

优点:

  • 列表能力更完整。

缺点:

  • 会扩大本轮范围。
  • 需要额外确认排序字段、默认顺序和文案。

方案 C:仅前端本地筛选

做法:

  • 后端仍返回全量列表,由前端本地做筛选与假分页。

优点:

  • 前端改动更快。

缺点:

  • 不是真实分页,不满足本轮要求。
  • 数据量变大后性能和一致性都会出问题。

结论

采用 方案 A:最小闭环真实列表。

5. 后端设计

5.1 接口

继续使用现有接口:

  • GET /api/v1/templates

查询参数调整为:

  • q?: string
  • category?: string
  • status?: string
  • page?: number
  • pageSize?: number

移除:

  • purpose

5.2 查询范围

只查询当前登录美发师自己的模板,并强制附加以下条件:

  • hairstylist_id = 当前登录用户
  • is_deleted = 0

5.3 筛选规则

  • q
    • 模糊匹配 template_name
    • 模糊匹配 tags
    • 模糊匹配 description
  • category
    • 精确匹配模板分类
  • status
    • 精确匹配模板状态

5.4 分页规则

  • 默认 page = 1
  • 默认 pageSize = 8
    • 选择 8 的原因:当前卡片网格在桌面端更容易形成稳定的两行展示,改动小
  • skip = (page - 1) * pageSize
  • take = pageSize

返回结构调整为:

{
  items: TemplateEntity[]
  total: number
  page: number
  pageSize: number
}

5.5 列表项字段

列表项继续返回前端当前卡片和分流逻辑所需字段,至少包括:

  • id
  • name
  • category
  • status
  • description
  • tags
  • image
  • usedCount
  • makeTaskId
  • makeTaskStatus
  • generationStatus

其中:

  • name 由 template_name 映射
  • image 优先取模板封面 cover_url
  • makeTaskId / makeTaskStatus 用于保持当前任务分流逻辑

5.6 排序规则

本轮不开放前端排序选择,但后端列表需要给出稳定默认顺序,避免翻页结果抖动:

  • 优先按 id desc

这能保证新建模板优先显示,且在当前阶段实现最简单、最稳定。

6. 前端设计

6.1 查询区调整

保留并接入真实请求的筛选项:

  • 搜索框
  • 分类
  • 状态

删除:

  • 用途筛选 UI
  • 前端文案中的 purposeLabel 和 purposeOptions
  • listTemplates() 的 purpose 参数

6.2 页面状态

TemplateListPage 需要新增受控状态:

  • query
  • category
  • status
  • page
  • pageSize
  • total
  • loading

页面行为:

  • 首次进入时请求第一页。
  • 任一查询条件变化时:
    • 重置 page = 1
    • 重新发起请求
  • 点击分页按钮时:
    • 保留当前查询条件
    • 请求对应页码

6.3 列表展示

保留现有卡片样式与结构,不做视觉重构。

继续保留:

  • 批量删除按钮
  • 单项删除按钮
  • 任务状态徽标
  • 卡片点击跳转逻辑

6.4 分流逻辑

保持当前规则不变:

  • makeTaskId 存在,且 makeTaskStatus 属于 WAIT / PROCESSING / STEP1_SUCCESS / FAILED
    • 跳任务页 /templates/tasks/:taskId
  • 其他情况
    • 跳详情页 /templates/:templateId

6.5 分页展示

分页控件由真实分页数据驱动:

  • 总页数 totalPages = max(1, ceil(total / pageSize))
  • 即使 totalPages = 1,分页区域仍然显示
  • “上一页”在第一页禁用
  • “下一页”在最后一页禁用
  • 中间页码使用真实页码,不再硬编码 1 / 2

7. API 契约调整

7.1 前端 listTemplates

由:

listTemplates(params?: { q?: string; category?: string; purpose?: string; status?: string }): Promise<TemplateEntity[]>

调整为:

listTemplates(params?: {
  q?: string
  category?: string
  status?: string
  page?: number
  pageSize?: number
}): Promise<{
  items: TemplateEntity[]
  total: number
  page: number
  pageSize: number
}>

7.2 后端 DTO

ListTemplatesQueryDto 调整为:

  • 保留 q
  • 保留 category
  • 删除 purpose
  • 保留 status
  • 新增 page
  • 新增 pageSize

8. 错误处理

8.1 前端

  • 列表请求失败时,页面回退为空列表展示。
  • 分页区域仍保留,按 total = 0 处理。
  • 不新增复杂 toast,本轮保持当前页面风格。

8.2 后端

  • 非法分页参数使用默认值兜底:
    • page < 1 视为 1
    • pageSize < 1 使用默认值 8
  • 空结果返回:

    {
    items: [],
    total: 0,
    page,
    pageSize
    }
    

9. 测试设计

9.1 后端

补充或更新:

  • templates.service.spec.ts
    • 按 q/category/status 生成正确查询条件
    • 按 page/pageSize 生成正确分页参数
    • 返回 items/total/page/pageSize
  • templates.e2e-spec.ts
    • GET /templates 支持真实筛选
    • GET /templates 支持真实分页
    • 不再接受 purpose 作为有效查询链路的一部分

9.2 前端

更新 TemplateListPage.test.tsx,覆盖:

  • 删除“用途”筛选
  • 首次加载时携带真实分页参数请求第一页
  • 搜索、分类、状态变化时重新请求并重置页码
  • 分页按钮始终显示
  • 点击不同页码后请求对应页
  • 未完成模板仍跳任务页,已完成模板仍跳详情页

10. 实施边界

本次实现预计涉及:

  • 后端
    • templates.service.ts
    • templates.controller.ts
    • dto/list-templates-query.dto.ts
    • templates.service.spec.ts
    • templates.e2e-spec.ts
  • 前端
    • services/api/templates.ts
    • services/api/contracts.ts
    • pages/dashboard/templates/TemplateListPage.tsx
    • pages/dashboard/templates/__tests__/TemplateListPage.test.tsx
    • i18n/locales/zh/translation.json
    • i18n/locales/ja/translation.json

11. 验收标准

完成后需要满足:

  • “我的发型模板”页面展示真实后端数据
  • 查询项只保留:搜索、分类、状态
  • “用途”完全从页面和查询链路中移除
  • 分页为真实分页,且即使只有一页也显示分页控件
  • 列表卡片跳转分流逻辑保持正确
  • 前后端针对列表查询与分页的测试通过