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

5.9 KiB
Raw Permalink Blame History

任务 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,并接入 devbuild 前置(npm run api:generate && viteOpenAPI 无法获取或生成失败时命令直接失败。
  • package.jsonpnpm api:generate = client api:generate(需后端服务运行中)。
  • client/src/api/generated/schema.d.ts:由 openapi-typescript 生成,已在 .gitignore 中忽略、禁止手工修改。

请求客户端与领域 API

  • client/src/api/client.tsopenapi-fetchcreateClient<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.cssmain.css
  • components/base/AppIcon内联线性 SVG 图标集替代依赖、AppButton、AppCard、AppBadge。
  • components/feedback/AppLoading、AppEmpty、AppError、AppToast、ComingSoon。

路由、Store 与双端布局

  • router/nav.tsPC 8 项 / Mobile 5 项导航配置)、router/index.ts8 条路由 + 兜底重定向 + 文档标题)。
  • stores/app.tsbusy + toastprofile.ts(档案加载)、practice.ts(会话状态)。
  • layouts/DesktopLayout.vue220px 侧栏)、layouts/MobileLayout.vue(顶部栏 + 底部 TabBar
  • App.vue<768px 断点切换双端布局;main.ts 接入 Pinia + Router。
  • 首页 DashboardView.vue 通过 dashboardApi.overview() 加载真实数据,覆盖加载/空/错误/成功四态;其余 7 个导航页为 ComingSoon 占位。

过程中发现并修复的问题

  1. 后端 OpenAPI 参数污染routes.tsemptyObjectSchema = { type: 'object' } 占位导致 @fastify/swagger 把 "type" 关键字误当参数名,每个路由生成虚假 typequery/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 导航 点击「刷题中心」→ 路由切到 /practiceTabBar/标题同步变化,占位页「建设中」正常
错误态 停后端 → 显示「数据加载失败 / 无法连接服务,请确认后端已启动」+「重试」按钮
重试恢复 重启后端点击「重试」→ 数据恢复渲染
空态 清空题库 → 显示「还没有学习数据」空状态,恢复数据后正常

截图:deliverables/checks/task-04-desktop.pngtask-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 需后端已启动。