gwy-exam/docs/checks/task-04.md
2026-08-31 16:50:38 +08:00

69 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 任务 04 检查记录:前端 API 自动生成与基础视觉壳层
日期2026-08-31
## 交付内容
### OpenAPI 代码生成管线
- `client/scripts/generate-api.mjs`:从后端运行时端点 `http://localhost:3000/api/openapi.json`(可用 `OPENAPI_URL` 覆盖)拉取并生成类型;获取失败/生成失败时 exit 1 并提示先启动后端。
- `client/package.json`:新增 `api:generate`,并接入 `dev``build` 前置(`npm run api:generate && vite`OpenAPI 无法获取或生成失败时命令直接失败。
-`package.json``pnpm api:generate` = client `api:generate`(需后端服务运行中)。
- `client/src/api/generated/schema.d.ts`:由 openapi-typescript 生成,已在 `.gitignore` 中忽略、禁止手工修改。
### 请求客户端与领域 API
- `client/src/api/client.ts``openapi-fetch``createClient<paths>`,统一 baseUrl默认相对路径 `/api`,开发环境经 Vite 代理、JSON headers、15s 超时AbortController与错误解包`ApiError` 统一异常。
- `client/src/api/index.ts`按域暴露类型化方法dashboard/practice/review/plans/mock/news/questions/ai/profile/settings请求/响应类型由 `BodyOf`/`QueryOf`/`SuccessBody` 从生成的 `paths` 推导,业务代码不写 URL 与 `fetch`
### 设计 Token 与基础组件
- `client/src/styles/tokens.css`(承接 `deliverables/design/design-tokens.css`)、`reset.css``main.css`
- `components/base/`AppIcon内联线性 SVG 图标集替代依赖、AppButton、AppCard、AppBadge。
- `components/feedback/`AppLoading、AppEmpty、AppError、AppToast、ComingSoon。
### 路由、Store 与双端布局
- `router/nav.ts`PC 8 项 / Mobile 5 项导航配置)、`router/index.ts`8 条路由 + 兜底重定向 + 文档标题)。
- `stores/app.ts`busy + toast`profile.ts`(档案加载)、`practice.ts`(会话状态)。
- `layouts/DesktopLayout.vue`220px 侧栏)、`layouts/MobileLayout.vue`(顶部栏 + 底部 TabBar
- `App.vue``<768px` 断点切换双端布局;`main.ts` 接入 Pinia + Router。
- 首页 `DashboardView.vue` 通过 `dashboardApi.overview()` 加载真实数据,覆盖加载/空/错误/成功四态;其余 7 个导航页为 ComingSoon 占位。
## 过程中发现并修复的问题
1. **后端 OpenAPI 参数污染**`routes.ts``emptyObjectSchema = { type: 'object' }` 占位导致 `@fastify/swagger` 把 "type" 关键字误当参数名,每个路由生成虚假 `type`query/path参数会污染前端生成类型。改为「无 schema 时省略 params/querystring 键」,重新导出后 31 个路径参数干净0 个虚假参数)。
2. **openapi-typescript v7 API 变化**:返回 `ts.Node[]` 而非字符串,需 `astToString` 转换。
3. **Vite 依赖优化缓存**`client/node_modules/.vite` 过期导致 dev 启动触发 bulk-delete 守卫报错,清除缓存后恢复。
4. **空响应体错误处理**:后端宕机时 Vite 代理返回 502 空 bodyopenapi-fetch 返回 `error: undefined`,导致 `unwrap` 误判成功并返回 undefined触发 `isEmpty` 读取 undefined 报错。改为在 `unwrap` 中按 `response.ok` 判定并映射 502/503/504 为「无法连接服务」。
## 命令检查
- `pnpm typecheck`通过client + server
- `pnpm build`通过client 构建前自动从 `http://localhost:3000/api/openapi.json` 执行 `api:generate`(后端运行中)。
- `pnpm --filter @gwy/client api:generate`:后端运行中生成成功;后端未启动时 exit 1 并提示「请确认后端服务已启动pnpm dev:server」。
## 浏览器检查agent-browser实际渲染 DOM 验证)
| 场景 | 结果 |
|---|---|
| 桌面 1440 视口 | 侧栏 8 项导航齐全;首页标题「数据中枢」+ 问候语profile 真实加载) |
| 首页数据 | hero 卡「学习天数 92 / 累计答题 0 / 正确率 0%」+ 5 张统计卡(题库题量 3、待复习 0、今日任务 0/0、今日学习 0、连续打卡 0均来自 `/api/dashboard/overview` |
| 移动 390 视口 | 顶部栏标题 + 底部 TabBar 5 项(首页/刷题/要闻/分析/我的) |
| SPA 导航 | 点击「刷题中心」→ 路由切到 `/practice`TabBar/标题同步变化,占位页「建设中」正常 |
| 错误态 | 停后端 → 显示「数据加载失败 / 无法连接服务,请确认后端已启动」+「重试」按钮 |
| 重试恢复 | 重启后端点击「重试」→ 数据恢复渲染 |
| 空态 | 清空题库 → 显示「还没有学习数据」空状态,恢复数据后正常 |
截图:`deliverables/checks/task-04-desktop.png``task-04-mobile.png`
## 完成标准核对
- ✅ 前端业务代码不直接写 URL 和 `fetch`(统一走 `api/index.ts` 类型化方法 + `openapi-fetch`)。
- ✅ 修改后端 Schema 能重新生成类型(`pnpm api:generate` 从运行时 API 拉取生成,需后端运行)。
- ✅ 首页路由能通过真实接口加载overview + profile 经 Vite 代理到后端)。
- ✅ 页面可在桌面和移动断点切换768px 断点,双端布局实测)。
- ✅ 统一加载、空数据、错误提示组件AppLoading/AppEmpty/AppError均实测
## 当前结论
任务 04 完成:前端 API 自动生成管线、类型化请求客户端、路由与 Pinia 基础 store、设计 Token、PC 侧栏与 Mobile TabBar、以及四态反馈组件全部落地首页已通过真实接口加载。图标采用内联线性 SVG 替代 lucide 依赖(`lucide-vue-next` 已改名弃用为 `@lucide/vue` 且安装受阻,为避免大体积依赖与不稳定安装而自建图标集)。剩余 7 个页面为占位,待任务 0511 逐个实现。
> 微调2026-08-31前端类型生成改为从后端运行时端点 `http://localhost:3000/api/openapi.json` 拉取,不再依赖本地 `server/openapi.json` 文件;`dev`/`build` 前置 `api:generate` 需后端已启动。