# 模板列表真实查询与分页设计 ## 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` 返回结构调整为: ```ts { 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` 由: ```ts listTemplates(params?: { q?: string; category?: string; purpose?: string; status?: string }): Promise ``` 调整为: ```ts 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` - 空结果返回: ```ts { 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. 验收标准 完成后需要满足: - “我的发型模板”页面展示真实后端数据 - 查询项只保留:搜索、分类、状态 - “用途”完全从页面和查询链路中移除 - 分页为真实分页,且即使只有一页也显示分页控件 - 列表卡片跳转分流逻辑保持正确 - 前后端针对列表查询与分页的测试通过