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,83 @@
# 项目上下文
## 基本信息
| 字段 | 内容 |
|------|------|
| **项目名称** | Tools Show |
| **技术栈** | Vue 3、vue-router、Vite、Vitest、NestJS、Prisma、Jest、SQLite |
| **创建日期** | 2026-04-11 |
| **最后更新** | 2026-04-11 |
| **项目阶段** | 开发中 |
## 已完成功能
| 功能 | 描述 | 完成日期 | 状态 |
|------|------|---------|------|
| 访客端工具列表与筛选 | 首页支持搜索、分类、排序、分页和站点概览弹窗 | 2026-04-11 | 稳定 |
| 工具用户手册详情页 | 新增基于 `slug` 的访客端详情页,支持 Markdown 手册、空状态、404、返回入口和长文目录导航 | 2026-04-11 | 稳定 |
| 公开详情 `slug` 接口 | 服务端新增按 `slug` 查询已发布工具详情的公开接口,并保留原有按 `id` 查询能力 | 2026-04-11 | 稳定 |
| 前端测试基建 | 客户端补充 `Vitest + Vue Test Utils + jsdom`,覆盖首页跳转、详情页和 Markdown 目录工具 | 2026-04-11 | 稳定 |
## 项目结构
### 目录结构
```text
/
├── client/ # Vue 前端与管理端
├── server/ # NestJS 后端
├── docs/ # 项目文档
├── design/ # 需求、规格、计划与上下文
└── docker/ # 运行环境脚本
```
### 模块划分
| 模块 | 职责 | 依赖关系 |
|------|------|---------|
| `client/src/App.vue` | 访客端工具列表、筛选和概览入口 | `client/src/api.js` |
| `client/src/pages/ToolDetailPage.vue` | 访客端工具详情与 Markdown 手册阅读 | `client/src/api.js`, `client/src/utils/markdown.js`, `client/src/utils/markdown-outline.js` |
| `client/src/admin/*` | 后台工具、分类和审计管理 | `client/src/admin/api.js` |
| `server/src/modules/tools/*` | 公开工具列表与详情查询 | Prisma |
| `server/src/modules/access/*` | 网页打开与下载行为 | `server/src/modules/tools/*` |
## 架构决策记录
| 日期 | 决策 | 理由 | 影响 |
|------|------|------|------|
| 2026-04-11 | 访客端详情页使用 `slug` 路由 | 链接可读、适合长期分享和直达访问 | 前后端同步支持 `/tools/:slug` |
| 2026-04-11 | 继续复用 `Tool.description` 作为用户手册源 | 避免新增数据模型和后台复杂度 | 后台只需优化说明文案 |
| 2026-04-11 | 保留按 `id` 获取详情接口,同时新增按 `slug` 接口 | 降低回归风险,兼容既有调用 | `ToolsService` 维护共享详情映射 |
| 2026-04-11 | 客户端引入 `Vitest + Vue Test Utils + jsdom` | 详情页和路由改造需要前端自动化回归 | `client/package.json` 和锁文件更新 |
## 依赖管理
### 项目依赖
| 依赖名称 | 版本 | 用途 | 来源 |
|---------|------|------|------|
| `vitest` | `^3.2.4` | 客户端单元测试 | npm |
| `@vue/test-utils` | `^2.4.6` | Vue 组件测试挂载 | npm |
| `jsdom` | `^26.1.0` | 客户端测试 DOM 环境 | npm |
| `marked` | `^17.0.5` | Markdown 解析 | npm |
| `dompurify` | `^3.3.3` | Markdown HTML 安全净化 | npm |
### 外部服务
| 服务 | 用途 | 配置位置 |
|------|------|---------|
| SQLite | 工具、分类与访问数据存储 | `server/prisma/schema.prisma` |
## 技术债务
| 债务 | 影响 | 优先级 | 备注 |
|------|------|-------|------|
| 客户端构建产物单 chunk 体积较大 | Vite 构建有 chunk size warning | 中 | 后续可考虑按公开页/管理页拆分 chunk |
| 首页与详情页存在重复的 launch 行为逻辑 | 后续维护动作文案或追踪逻辑时需双处同步 | 中 | 可考虑抽成共享 composable |
## 更新历史
| 日期 | 更新内容 | 触发来源 |
|------|---------|---------|
| 2026-04-11 | 初始创建,记录访客端详情页与前端测试基建落地情况 | executing-plans 完成 |

View File

@@ -0,0 +1,56 @@
# 用户画像
## 基本信息
| 字段 | 内容 | 更新日期 |
|------|------|---------|
| 用户定位 | 高级开发者 | 2026-04-11 |
| 技术背景 | Vue / Node.js 全栈开发,能直接指定页面与交互改动 | 2026-04-11 |
| 首次记录 | 2026-04-11 | 2026-04-11 |
| 最后更新 | 2026-04-11 | 2026-04-11 |
## 技术偏好
| 类型 | 偏好 | 备注 |
|------|------|------|
| 前端框架 | Vue | 当前项目直接基于 Vue 页面提出功能修改 |
| 后端语言 | Node.js | 当前项目服务端为 NestJS |
| 代码风格 | 简洁直接 | 偏向明确需求,不需要冗长解释 |
## 决策倾向
| 场景 | 倾向 | 表现 |
|------|------|------|
| 常规开发 | 偏质量优先 | 在可控范围内倾向补齐完整体验,如空状态和 404 |
| 紧急情况 | 效率优先 | 倾向直接说明要加什么功能 |
| 重构场景 | 中步迭代 | 先做增量功能,而不是整体推翻 |
## 风险偏好
- **技术选型**:适中
- **重构决策**:中步迭代
- **依赖引入**:最小依赖
## 交互风格
| 方面 | 偏好 | 说明 |
|------|------|------|
| 详细程度 | 简洁 | 更偏向短句和明确结论 |
| 反馈频率 | 关键节点 | 适合在决策点确认 |
| 解释深度 | 概要说明 | 不需要铺垫式解释 |
| 问题形式 | 选择题 | 当前对话对数字选项响应直接 |
## 能力评估
| 能力领域 | 自评等级 | 备注 |
|---------|---------|------|
| 前端开发 | 高级 | 能明确提出页面形态与交互需求 |
| 后端开发 | 中级 | 当前尚未直接表达更强偏好 |
| 架构设计 | 中级 | 关注功能增量落地 |
| 性能优化 | 中级 | 当前未体现特殊关注点 |
## 更新历史
| 日期 | 更新内容 | 触发场景 |
|------|---------|---------|
| 2026-04-11 | 初始创建,记录为高级开发者,偏好简洁和选择题沟通 | brainstorming 初次激活 |

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 内容导致测试不稳定。

44
design/session-state.md Normal file
View File

@@ -0,0 +1,44 @@
# 会话状态
## 基本信息
- **技能**: executing-plans
- **主题**: 用户手册详情页(支持 Markdown
- **开始时间**: 2026-04-11 10:26
- **最后更新**: 2026-04-11 11:28
## 当前状态
- **阶段**: 阶段 3完成与报告
- **上一步**: 已完成全部 6 个任务,并通过前端测试、客户端构建、服务端单测、服务端 e2e 与服务端构建。
- **下一步**: 输出完成报告并结束本轮执行。
## 已确认内容
- 2026-04-11 10:26-需求目标是增加一个新的详情页,可显示 Markdown 格式的用户手册。
- 2026-04-11 10:26-主站当前没有独立详情页,详情内容以弹窗形式展示。
- 2026-04-11 10:26-前端已存在 Markdown 渲染工具,可作为后续方案基础。
- 2026-04-11 10:27-详情页主要用户为普通访客。
- 2026-04-11 10:31-详情页交互方式为站内路由跳转。
- 2026-04-11 10:32-用户手册内容来源先复用现有 `description` 字段承载 Markdown。
- 2026-04-11 10:33-旧的详情弹窗将被新详情页替换。
- 2026-04-11 10:37-本次实现范围包含详情页空状态、404 状态与返回入口体验。
- 2026-04-11 10:39-验收标准为长期可用,要求详情页具备清晰信息结构。
- 2026-04-11 10:42-边界情况需覆盖直达路由、空内容、404 及超长 Markdown 阅读体验。
- 2026-04-11 10:58-最终方案为基于 `slug` 的详情页路由与详情接口。
- 2026-04-11 11:02-规格文档已获批准,开始编写实施计划。
- 2026-04-11 11:05-实施计划已保存,包含 6 个按 TDD 拆分的执行任务。
- 2026-04-11 11:08-执行前检查发现当前分支为 `master`,按技能规则需用户确认后才能继续。
- 2026-04-11 11:09-用户已确认允许在 `master` 分支执行计划,任务 1 开始。
- 2026-04-11 11:12-任务 1 完成:前端测试基建建立,`markdown-outline` 工具和测试通过。
- 2026-04-11 11:17-任务 2 和任务 3 完成:后端 `slug` 详情服务单测和控制器 e2e 通过。
- 2026-04-11 11:28-任务 4、5、6 完成:公开详情页、首页路由跳转、目录阅读体验和后台文案已落地并完成最终验证。
## 待处理问题
- [x] 任务 1前端测试基建和 Markdown 目录工具。
- [x] 任务 2后端按 `slug` 查询详情的服务层能力。
- [x] 任务 3公开详情 `slug` 接口和后端 e2e。
- [x] 任务 4公开详情页路由、API 接入和页面状态。
- [x] 任务 5首页详情弹窗替换为路由跳转。
- [x] 任务 6长文阅读体验、后台提示文案和整体验证。

View File

@@ -0,0 +1,100 @@
# 用户手册详情页设计规格
## 概述
当前访客端首页通过弹窗展示工具详情,适合快速浏览,但不适合作为稳定的用户手册阅读入口。现有工具数据中的 `description` 字段已经支持 Markdown 渲染,因此可以直接复用它承载用户手册内容,无需新增数据库字段。
本次设计目标是在现有公开站点中增加一个正式的详情页入口,替换原有弹窗。详情页使用工具的 `slug` 作为公开访问标识,支持站内路由直达、长篇 Markdown 阅读、空内容状态、404 状态和明确的返回入口,使其可以长期承担“工具介绍 + 用户手册”的双重职责。
## 目标定位
- **主要用户**:普通访客
- **使用场景**:访客从工具列表点击“详情”进入独立页面,阅读工具说明、能力介绍和 Markdown 格式的用户手册;也支持用户直接访问详情页链接
## 约束条件
- **技术限制**:前端基于 Vue 3 + vue-router主站与管理端共用同一路由实例后端基于 NestJS + PrismaMarkdown 渲染复用现有 `marked + DOMPurify` 能力;用户手册内容先复用 `Tool.description`
- **时间限制**:本轮以增量改造为主,不引入新的内容模型,不重做管理端工具编辑流程
- **资源限制**:仅在现有项目代码结构内完成,避免增加新依赖或引入第二套公开站点入口
## 成功标准
- [ ] 访客在首页点击“详情”后进入新的站内详情页,不再弹出旧弹窗
- [ ] 详情页路由使用 `slug`,可通过 URL 直达访问
- [ ] 详情页能够稳定展示 `description` Markdown、基础元信息和功能点适合作为长期手册页面
- [ ] 当工具不存在时展示明确的 404 状态;当 `description` 为空时展示清晰的空内容提示
- [ ] 对于较长 Markdown 内容,页面具备目录感和良好的阅读体验,用户可以快速回到列表或继续执行“打开网页/下载”操作
## 架构设计
### 系统结构
本次改造保持单一公开站点结构不变,在现有 `vue-router` 中新增公开详情路由。前端首页仍负责列表、筛选和跳转;详情页负责根据 `slug` 拉取数据并渲染完整内容。后端在保留现有按 `id` 获取详情接口的前提下,新增按 `slug` 查询的公开详情接口,避免影响既有逻辑,并为长期公开链接提供稳定入口。
详情页信息结构分为三层:
1. 页面顶部:返回列表、工具名称、分类、更新时间、主操作按钮
2. 信息摘要区:评分、访问方式、下载/访问次数、版本、标签、核心能力
3. 手册正文区:将 `description` 作为 Markdown 正文渲染,并为长文提供标题导航或可跳转的章节锚点
### 组件划分
| 组件 | 职责 | 依赖 |
|------|------|------|
| `client/src/App.vue` | 保留首页列表、筛选和分页;将“详情”按钮改为路由跳转;移除旧详情弹窗状态与模板 | `vue-router`, `client/src/api.js` |
| `client/src/pages/ToolDetailPage.vue` | 详情页容器;根据 `slug` 拉取详情数据处理加载、404、空内容、长文阅读与返回入口 | `vue-router`, `client/src/api.js`, `client/src/utils/markdown.js` |
| `client/src/api.js` | 新增按 `slug` 获取详情的方法;复用已有错误处理与启动行为 | Axios |
| `client/src/utils/markdown.js` | 继续负责安全渲染 Markdown可扩展标题提取或锚点处理支持长文目录体验 | `marked`, `DOMPurify` |
| `client/src/admin/components/ToolFormDialog.vue` | 保持数据录入入口不变;若实施时顺手优化文案,可将简介提示改为更明确的“支持手册 Markdown” | Element Plus |
| `server/src/modules/tools/tools.controller.ts` | 新增按 `slug` 获取公开详情的接口 | `ToolsService` |
| `server/src/modules/tools/tools.service.ts` | 新增按 `slug` 查询已发布工具详情的方法,并复用现有详情映射逻辑 | Prisma |
## 数据流
首页进入详情页的数据流如下:
1. 首页加载工具列表,列表项已带有 `id``slug`
2. 用户点击“详情”按钮
3. 前端通过路由跳转到 `/tools/:slug`
4. 详情页组件从路由参数读取 `slug`
5. 前端调用新的公开接口,例如 `GET /api/v1/tools/slug/:slug`
6. 后端按 `slug + status=published + isDeleted=false` 查询工具
7. 查询成功后返回详情数据,包括 `description``features`、分类、评分、访问方式、访问量/下载量、版本等
8. 前端渲染详情页摘要区和 Markdown 正文;若正文存在标题,则生成目录锚点或章节导航
9. 用户可从详情页返回首页,或继续点击“打开网页/下载”执行现有 launch 流程
长文阅读的数据处理建议如下:
1. 详情页获取 Markdown 原文
2. 使用现有 Markdown 解析能力渲染 HTML
3. 在渲染前或渲染时提取标题层级,生成页面内目录
4. 为标题生成稳定锚点,支持目录点击跳转
5. 若正文无标题,则不显示目录,保留清晰的文章排版和回顶能力
## 错误处理
| 错误类型 | 处理方式 |
|----------|----------|
| 详情接口加载中 | 展示页面级骨架或加载态,不显示旧弹窗 |
| `slug` 对应工具不存在 | 展示 404 状态页,包含返回首页入口 |
| 工具存在但 `description` 为空 | 展示“暂未提供用户手册”空状态,同时仍保留工具摘要信息和主操作按钮 |
| 接口请求失败 | 展示页面级错误提示,提供重试操作和返回首页入口 |
| Markdown 内容很长 | 启用目录/锚点导航、限制正文宽度、保证标题层级和段落间距,避免整页难以浏览 |
| 从详情页执行打开或下载失败 | 复用现有 `launchTool` 和错误提示逻辑,不在详情页定义第二套行为 |
## 测试策略
- **单元测试**:校验按 `slug` 查询详情的服务方法;校验 Markdown 标题提取或目录生成逻辑;校验空内容和 404 的状态判断
- **集成测试**:覆盖公开详情接口按 `slug` 返回已发布工具;覆盖不存在 `slug` 时返回 404覆盖详情页直达访问、列表跳转访问和返回行为
- **验收标准**:从首页点击“详情”可稳定进入 `/tools/:slug`;直达链接可用;长篇 Markdown 有清晰阅读结构空内容、404、请求失败均有明确页面反馈旧详情弹窗已移除
## 决策记录
| 决策 | 理由 | 影响 |
|------|------|------|
| 详情页采用站内路由而不是新标签页 | 用户明确要求站内跳转,且可形成连续阅读路径 | 前端需新增公开详情路由 |
| 详情页公开地址使用 `slug` | `slug` 已存在且唯一,适合长期公开链接和分享 | 后端需新增按 `slug` 查询接口,前端详情页按 `slug` 加载 |
| 用户手册先复用 `description` 字段 | 当前已支持 Markdown避免新增数据库字段和后台复杂度 | 本轮不做数据模型迁移 |
| 旧弹窗详情被新详情页替换 | 用户希望详情入口统一,避免双入口维护 | 首页需移除弹窗模板和相关状态 |
| 长文阅读加入目录感设计 | 用户明确要求处理超长 Markdown 的阅读体验 | 详情页和 Markdown 工具层需要支持标题导航或锚点 |
| 现有按 `id` 的详情接口保留,新增长期用的 `slug` 接口 | 兼容已有内部调用,减少回归风险 | 后端存在两个公开详情查询入口,但职责清晰 |