This commit is contained in:
admin
2026-04-11 20:46:55 +08:00
parent e6c2d76238
commit e04405d0bc
70 changed files with 10438 additions and 332 deletions

View File

@@ -0,0 +1,189 @@
# 用户手册详情页实施计划
**目标**:将访客端原有详情弹窗替换为基于 `slug` 的独立详情页,使 `description` 可以长期作为 Markdown 用户手册展示并补齐直达访问、空内容、404 和长文阅读体验。
**架构**:本次为单一功能子系统改造,不创建纲领文件。后端先补齐按 `slug` 查询已发布工具的公开详情能力,再由前端新增公开详情页路由和页面组件,最后替换首页详情入口并完善长文阅读样式与测试。前端测试基建当前缺失,因此计划第一步先补齐 `Vitest + Vue Test Utils + jsdom`,确保后续页面改造遵循测试优先。
**技术栈**Vue 3、vue-router、Vite、Vitest、Vue Test Utils、NestJS、Prisma、Jest、Supertest、marked、DOMPurify
---
## 文件结构
### 新建文件
- `client/vitest.config.js` - 前端测试配置,提供 Vue SFC 和 `jsdom` 运行环境
注释需求:低优先级,配置项仅在非直观处补简短说明
复杂度:低
- `client/src/test/setup.js` - 前端测试初始化,挂载全局匹配器、滚动和路由相关基础 mock
注释需求:中优先级,需要说明全局 mock 的用途
复杂度:中
- `client/src/utils/markdown-outline.js` - 提取 Markdown 标题、生成稳定锚点和目录结构
注释需求:高优先级,需要为重复标题去重和锚点规范化逻辑添加说明
复杂度:中
- `client/src/utils/markdown-outline.spec.js` - 验证目录提取、标题去重和无标题场景
注释需求:低优先级
复杂度:中
- `client/src/pages/ToolDetailPage.vue` - 公开详情页,负责按 `slug` 拉取详情、渲染摘要和 Markdown 手册、处理空状态/404/返回入口
注释需求:高优先级,需要在状态流转和 launch 复用入口处补说明
复杂度:高
- `client/src/pages/ToolDetailPage.spec.js` - 详情页组件测试覆盖加载态、404、空内容、目录和主操作按钮
注释需求:低优先级
复杂度:高
- `client/src/App.spec.js` - 首页交互测试,覆盖详情按钮跳转和旧弹窗移除
注释需求:低优先级
复杂度:中
- `server/src/modules/tools/tools.service.spec.ts` - 后端服务单测,覆盖按 `slug` 查询详情的业务规则
注释需求:低优先级
复杂度:中
- `server/test/tools-detail.e2e-spec.ts` - 公开详情接口 e2e覆盖成功命中和 404
注释需求:中优先级,需要说明测试数据准备和清理
复杂度:中
### 修改文件
- `client/package.json` - 增加前端测试脚本和测试依赖声明
注释需求:低优先级
复杂度:低
- `client/package-lock.json` - 锁定前端测试依赖版本
注释需求:无
复杂度:低
- `client/src/api.js` - 新增按 `slug` 获取详情的方法,复用现有错误和 launch 行为
注释需求:中优先级,需要说明新接口和旧按 `id` 接口的职责差异
复杂度:中
- `client/src/admin/router.js` - 增加公开详情页路由 `/tools/:slug`
注释需求:低优先级
复杂度:低
- `client/src/App.vue` - 移除详情弹窗状态和模板,将详情入口改为路由跳转
注释需求:中优先级,需要说明首页仅负责列表和跳转,不再持有详情状态
复杂度:高
- `client/src/style.scss` - 增加详情页布局、目录、空状态、404 和长文阅读样式
注释需求:低优先级
复杂度:高
- `client/src/admin/components/ToolFormDialog.vue` - 微调“工具简介”提示文案,明确其可承载 Markdown 手册内容
注释需求:低优先级
复杂度:低
- `server/src/modules/tools/tools.service.ts` - 抽出详情映射逻辑,新增按 `slug` 查询已发布工具详情的方法
注释需求:高优先级,需要说明 `id``slug` 两套公开查询入口的职责分层
复杂度:中
- `server/src/modules/tools/tools.controller.ts` - 新增按 `slug` 获取公开详情的接口
注释需求:低优先级
复杂度:低
### 测试文件
- `client/src/utils/markdown-outline.spec.js` - 验证 Markdown 目录提取和锚点生成规则
- `client/src/pages/ToolDetailPage.spec.js` - 验证详情页数据状态和长文阅读体验
- `client/src/App.spec.js` - 验证首页详情入口从弹窗切为路由跳转
- `server/src/modules/tools/tools.service.spec.ts` - 验证按 `slug` 查询的业务规则
- `server/test/tools-detail.e2e-spec.ts` - 验证公开详情接口路由行为
---
## 任务列表
### 任务 1建立前端测试基建和 Markdown 目录工具
**文件**
- 创建:`client/vitest.config.js`
- 创建:`client/src/test/setup.js`
- 创建:`client/src/utils/markdown-outline.js`
- 测试:`client/src/utils/markdown-outline.spec.js`
- 修改:`client/package.json`
- 修改:`client/package-lock.json`
- [ ] **步骤 1**:在 `client/package.json` 中增加 `test``test:run` 脚本和 `vitest``@vue/test-utils``jsdom` 等依赖声明;创建 `client/vitest.config.js``client/src/test/setup.js`;编写 `client/src/utils/markdown-outline.spec.js`,先描述标题提取、重复标题去重、无标题返回空目录的失败用例。
- [ ] **步骤 2**:运行 `npm --prefix client install`;随后运行 `npm --prefix client run test:run -- client/src/utils/markdown-outline.spec.js`,预期输出为测试失败,原因是 `markdown-outline.js` 尚未实现或断言不满足。
- [ ] **步骤 3**:创建 `client/src/utils/markdown-outline.js`,实现 `extractMarkdownOutline` 和稳定锚点生成逻辑,保证只提取正文标题并对重复标题追加序号。
- [ ] **步骤 4**:再次运行 `npm --prefix client run test:run -- client/src/utils/markdown-outline.spec.js`,预期输出 `1 passed`,前端测试基建可用。
### 任务 2实现后端按 `slug` 查询详情的服务层能力
**文件**
- 创建:`server/src/modules/tools/tools.service.spec.ts`
- 修改:`server/src/modules/tools/tools.service.ts`
- [ ] **步骤 1**:编写 `server/src/modules/tools/tools.service.spec.ts`,覆盖已发布工具可按 `slug` 查询、未发布工具不可见、缺失工具抛出 404、详情映射字段与现有按 `id` 详情保持一致的失败测试。
- [ ] **步骤 2**:运行 `npm --prefix server test -- --runTestsByPath src/modules/tools/tools.service.spec.ts`,预期输出为编译失败或测试失败,因为 `getToolDetailBySlug` 及共享映射尚未存在。
- [ ] **步骤 3**:在 `server/src/modules/tools/tools.service.ts` 中抽出详情映射方法,新增 `getToolDetailBySlug(slug: string)`,查询条件限定为 `status=published``isDeleted=false`,并复用现有详情返回结构。
- [ ] **步骤 4**:再次运行 `npm --prefix server test -- --runTestsByPath src/modules/tools/tools.service.spec.ts`,预期输出 `Test Suites: 1 passed`
### 任务 3补齐公开详情 `slug` 接口和后端 e2e
**文件**
- 创建:`server/test/tools-detail.e2e-spec.ts`
- 修改:`server/src/modules/tools/tools.controller.ts`
- [ ] **步骤 1**:编写 `server/test/tools-detail.e2e-spec.ts`,准备一条已发布工具测试数据,定义 `GET /tools/slug/:slug` 成功返回详情和未知 `slug` 返回 404 的失败测试。
- [ ] **步骤 2**:运行 `npm --prefix server run test:e2e -- --runTestsByPath test/tools-detail.e2e-spec.ts`,预期输出为 404 或断言失败,因为控制器路由尚未暴露该接口。
- [ ] **步骤 3**:在 `server/src/modules/tools/tools.controller.ts` 中新增 `GET /tools/slug/:slug` 路由,调用 `toolsService.getToolDetailBySlug`;按需要调整 e2e 中的测试数据清理,避免污染现有数据。
- [ ] **步骤 4**:再次运行 `npm --prefix server run test:e2e -- --runTestsByPath test/tools-detail.e2e-spec.ts`,预期输出 `1 passed``2 passed`,接口直达行为稳定。
### 任务 4新增公开详情页路由、API 接入和页面状态
**文件**
- 创建:`client/src/pages/ToolDetailPage.vue`
- 测试:`client/src/pages/ToolDetailPage.spec.js`
- 修改:`client/src/api.js`
- 修改:`client/src/admin/router.js`
- [ ] **步骤 1**:编写 `client/src/pages/ToolDetailPage.spec.js`,覆盖按路由 `slug` 拉取详情、加载态、404 提示、空内容提示、返回首页入口以及主操作按钮展示的失败测试。
- [ ] **步骤 2**:运行 `npm --prefix client run test:run -- client/src/pages/ToolDetailPage.spec.js`,预期输出为模块不存在或多个断言失败,因为详情页组件、路由和新 API 方法尚未实现。
- [ ] **步骤 3**:在 `client/src/api.js` 中新增 `fetchToolDetailBySlug(slug)`;在 `client/src/admin/router.js` 中增加公开路由 `/tools/:slug`;创建 `client/src/pages/ToolDetailPage.vue`,完成按 `slug` 加载数据、页面级加载/错误/404/空内容状态和返回入口,并复用现有 launch 行为。
- [ ] **步骤 4**:再次运行 `npm --prefix client run test:run -- client/src/pages/ToolDetailPage.spec.js`,预期输出 `1 passed`,详情页基础行为成立。
### 任务 5替换首页详情弹窗为路由跳转
**文件**
- 创建:`client/src/App.spec.js`
- 修改:`client/src/App.vue`
- [ ] **步骤 1**:编写 `client/src/App.spec.js`,覆盖“详情”按钮改为跳转到 `/tools/:slug`、旧详情弹窗 DOM 不再渲染、首页仍保留概览弹窗的失败测试。
- [ ] **步骤 2**:运行 `npm --prefix client run test:run -- client/src/App.spec.js`,预期输出为断言失败,因为首页仍然保留 `openDetailModal` 流程和旧弹窗模板。
- [ ] **步骤 3**:修改 `client/src/App.vue`,移除 `detailModalOpen/detailLoading/detailError/detail` 等状态及弹窗模板,将详情按钮改为 `router.push``RouterLink` 跳转,并保留列表、筛选和概览弹窗逻辑不变。
- [ ] **步骤 4**:再次运行 `npm --prefix client run test:run -- client/src/App.spec.js client/src/pages/ToolDetailPage.spec.js`,预期输出全部通过,首页到详情页的主链路打通。
### 任务 6完善长文阅读体验、后台提示文案和整体验证
**文件**
- 修改:`client/src/style.scss`
- 修改:`client/src/pages/ToolDetailPage.vue`
- 修改:`client/src/pages/ToolDetailPage.spec.js`
- 修改:`client/src/admin/components/ToolFormDialog.vue`
- [ ] **步骤 1**:扩展 `client/src/pages/ToolDetailPage.spec.js`,新增目录渲染、无标题时不显示目录、长文时保留清晰段落层级和返回入口的失败测试。
- [ ] **步骤 2**:运行 `npm --prefix client run test:run -- client/src/pages/ToolDetailPage.spec.js`,预期输出为目录相关断言失败,因为页面尚未接入目录工具和阅读态样式。
- [ ] **步骤 3**:修改 `client/src/pages/ToolDetailPage.vue``client/src/style.scss`,接入 `extractMarkdownOutline`、渲染目录导航、优化正文宽度/标题层级/锚点区块;同步修改 `client/src/admin/components/ToolFormDialog.vue` 的简介提示文案,明确其支持 Markdown 手册内容。
- [ ] **步骤 4**:依次运行 `npm --prefix client run test:run -- client/src/utils/markdown-outline.spec.js client/src/pages/ToolDetailPage.spec.js client/src/App.spec.js``npm --prefix client run build``npm --prefix server test -- --runTestsByPath src/modules/tools/tools.service.spec.ts``npm --prefix server run test:e2e -- --runTestsByPath test/tools-detail.e2e-spec.ts`,预期输出均为通过,且客户端构建成功无报错。
---
## 执行顺序说明
1. 先补前端测试基建,否则后续页面任务无法按 TDD 执行。
2. 后端先做服务层,再做控制器和 e2e可减少路由级调试成本。
3. 前端先完成详情页自身状态,再替换首页入口,避免“按钮先改了但页面未就绪”。
4. 长文阅读体验放在最后收口,避免样式和目录逻辑反复返工。
## 风险提示
- 前端当前没有测试依赖,任务 1 会同时引入测试脚手架和锁文件变更。
- `App.vue` 体量较大,任务 5 需要谨慎删除旧弹窗状态,避免误伤概览弹窗和列表筛选逻辑。
- `slug` 接口与按 `id` 接口将同时存在,任务 2 和任务 3 需要确保两者返回结构一致,避免前端出现双标准。
- e2e 测试需要自行准备和清理工具数据,避免依赖现有 seed 内容导致测试不稳定。