5.9 KiB
5.9 KiB
任务 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= clientapi: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 占位。
过程中发现并修复的问题
- 后端 OpenAPI 参数污染:
routes.ts用emptyObjectSchema = { type: 'object' }占位导致@fastify/swagger把 "type" 关键字误当参数名,每个路由生成虚假type(query/path)参数,会污染前端生成类型。改为「无 schema 时省略 params/querystring 键」,重新导出后 31 个路径参数干净(0 个虚假参数)。 - openapi-typescript v7 API 变化:返回
ts.Node[]而非字符串,需astToString转换。 - Vite 依赖优化缓存:
client/node_modules/.vite过期导致 dev 启动触发 bulk-delete 守卫报错,清除缓存后恢复。 - 空响应体错误处理:后端宕机时 Vite 代理返回 502 空 body,openapi-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 个页面为占位,待任务 05–11 逐个实现。
微调(2026-08-31):前端类型生成改为从后端运行时端点
http://localhost:3000/api/openapi.json拉取,不再依赖本地server/openapi.json文件;dev/build前置api:generate需后端已启动。