kindergarten_java/docs/前端项目规范.md
2026-03-12 13:41:32 +08:00

13 KiB
Raw Permalink Blame History

AI 开发规范media-science-frontend

本文档基于当前仓库的真实配置与现有代码风格整理,目标是让 AI/新同学在不“自作主张改风格/改架构”的前提下,稳定产出可合并的代码。

技术栈与关键约束

  • 框架Vue 3 + TypeScript + Vitetype: module
  • 路由unplugin-vue-router 文件路由(vue-router/autoHash 模式(createWebHashHistory()
  • 状态Piniasetup store 风格)
  • 图表图表echarts
  • UIAnt Design Vue
  • 样式UnoCSSvirtual:uno.css允许在模板里大量使用原子类复杂样式例如动画渐变等使用class声明其余使用原子样式
  • 国际化@intlify/unplugin-vue-i18nsrc/locales/*.json
  • 格式化门禁Husky + lint-staged + Prettier提交前会对 *.{vue,ts,tsx,js,jsx,md,css,less,json} 自动 prettier --write
  • TypeScript 严格性strict: true,并开启 noUnusedLocals/noUnusedParameters
  • 自动导入unplugin-auto-importvuepinia@vueuse/core、router auto imports因此代码里可能未显式 import ref/computed/watch/...

项目架构(目录分层)

src/ 为根:

  • 入口src/main.ts(创建 app、挂载 router/pinia/Antd、全局指令
  • 根组件src/App.vue(全局初始化逻辑、主题/locale、监听生命周期等
  • 页面src/pages/**(文件路由:一个目录通常对应一个页面模块;页面内可用 definePage({ alias: [...] })
  • 通用组件src/components/**(跨页面复用的 UI/布局组件,如 Layout.vue
  • 状态管理src/store/**Pinia stores
  • 接口层src/api/**
    • src/api/generated/**Orval 自动生成(接口类型与路径的唯一真源,禁止手改)
    • src/api/client.ts:项目侧统一入口/别名层(导出客户端实例与类型工具)
    • src/api/*.ts:业务适配层(可选:解包 Result、分页扁平化、兼容旧页面结构等
  • 工具src/utils/**(如 useRouteUtil.tsuseLocalStorage.tsuseWebSocket.ts 等)
  • 类型声明src/**/*.d.tssrc/global.d.tssrc/vite-env.d.ts

仓库根目录速览(常用)

.
├─ src/                  # 应用源码
├─ public/               # 静态资源(按 Vite 约定)
├─ vite/                 # Vite 插件/构建辅助(如 LocaleType
├─ vite.config.ts        # 构建与插件配置(含 proxy、自动路由、自动导入等
├─ uno.config.ts         # UnoCSS 主题/shortcuts
├─ tsconfig*.json        # TS 严格配置strict + noUnused*
├─ .prettierrc           # 代码格式标准(提交前会强制执行)
├─ .husky/pre-commit     # 提交钩子lint-staged
├─ .env*                 # 环境变量(通过 import.meta.env 读取)
└─ AI_DEV_GUIDE.md       # 本规范

编码风格(必须遵守)

代码格式Prettier 为准)

来自 .prettierrc

  • 单引号'...'
  • 缩进2 空格,禁用 tab
  • 行宽100

任何“手动对齐/自定义格式化习惯”都会被提交前的 Prettier 重写AI 不要和格式化对抗。

Vue SFC 约定

  • 优先使用<script lang="ts" setup>(当前仓库主流写法)
  • 模板类名:允许 UnoCSS 原子类;尽量复用 uno.config.ts 里的 shortcutsflex-center
  • 样式块
    • 页面级样式:优先 scoped
    • 全局样式:放在明确的全局入口(项目当前也存在在 App.vue 内写全局样式的情况;新增时优先放到统一样式文件,除非确实需要跟随根组件)

TypeScript/类型策略

  • 避免any(除非是三方库/遗留代码无法避免,且要把 any 限制在最小范围)
  • 参数与返回值对外导出的工具函数、store action、请求函数调用处尽量补齐类型
  • 未使用变量/参数:因为启用了 noUnusedLocals/noUnusedParameters,新增代码必须确保无未使用项

路由规范(文件路由 + definePage

  • 页面文件位置:放在 src/pages/... 下,按业务模块分目录
  • 页面元信息:在页面组件的 setup 中使用:
import { definePage } from 'vue-router/auto';

definePage({
  alias: ['/xxx', '/yyy'],
});
  • 路由工具:推荐使用 src/utils/useRouteUtil.ts 获取 route/router/params<T>()

状态管理规范Pinia setup store

  • 位置src/store/*.ts
  • 风格defineStore('name', () => { ... }),对外返回 ref/computed
  • 与请求层鉴权同步:涉及登录态/鉴权时,确保“状态层的 token”与“API 客户端实际使用的鉴权头/拦截器/存储”一致,避免出现“界面认为已登录但请求未携带 token”或相反的情况

API 开发规范Orval 生成代码)

本规范以 src/api/generated/接口类型与路径的唯一真源,通过 Orval 从后端 OpenAPI 自动生成 TypeScript 类型 + 客户端方法。

1. 目录与职责边界

  • src/api/generated/Orval 自动生成目录,禁止手改
    • api.tsgetReadingPlatformAPI() 工厂函数,返回包含全部接口方法的对象。
    • model/OpenAPI 生成的 DTO/VO/Result/PageResult/Params 类型。
  • src/api/client.ts:项目侧的“统一入口/别名层”,导出 readingApi(完整客户端实例)以及常用的类型工具(解包、分页别名等)。
  • src/api/*.ts:业务侧“适配层”(可选),用于:
    • 兼容既有页面期望的“扁平结构/字段名/返回形态”
    • 补齐 OpenAPI 暂未覆盖的历史接口(短期过渡)
    • 汇聚跨接口的业务逻辑(例如组合请求、额外校验)

2. 基本原则(必须遵守)

  • 生成代码只读:不得在 src/api/generated/** 内做任何手工修改(包括修复类型、改路径、加字段)。
  • 以生成类型为准:参数/返回类型优先使用 src/api/generated/model 导出的类型,避免手写 interface 漂移。
  • 对外只暴露稳定的业务接口:页面/组件尽量通过 src/api/*.ts(适配层)或 src/api/client.ts(直接调用)访问,避免散落调用方式导致难以迁移。

3. 推荐调用方式

3.1 直接使用 Orval 客户端

  • 统一从 src/api/client.ts 引入:
    • readingApi: getReadingPlatformAPI() 的实例
    • ApiResultOf / UnwrapResult / PageDataOf 等类型工具

示例(以 Result<T> 为包裹结构):

import { readingApi } from '@/api/client';
import type { ResultTenant } from '@/api/generated/model';

async function loadTenant(id: number) {
  const res = (await readingApi.getTenant(id)) as ResultTenant;
  return res.data; // T可能为 undefined取决于后端返回与类型定义
}

3.2 使用“适配层”稳定返回结构

当页面已经依赖历史返回结构(例如直接要 items/total/page/pageSize),在 src/api/*.ts 内做一次性适配,页面只消费适配后的结构。

分页适配建议统一输出:

  • items: T[]
  • total: number
  • page: number
  • pageSize: number

4. Result / PageResult 约定与解包

后端统一响应通常为:

  • 普通接口Result<T>,字段一般为 code/message/data
  • 分页接口Result<PageResult<T>>,字段一般为 items/total/page/pageSize

在生成代码中常见类型形态:

  • ResultXXX(如 ResultTenantResultUserInfoResponse
  • ResultPageResultXXX(如 ResultPageResultTenant
  • PageResultXXX(如 PageResultTenant

建议做法:

  • 组件/页面层尽量不要直接处理 ResultXXX,而是由适配层解包并做兜底(空数组、默认分页参数等)。
  • 严禁在页面散落 as any;确需兼容时,集中在 src/api/*.ts 适配层进行,并在适配层内把“最终对页面返回的类型”定义清楚。

5. 命名与重复接口(getXxx/getXxx1/getXxx2

由于不同角色端点teacher/school/parent/admin可能存在同名资源Orval 在生成时会用 1/2/3 后缀消歧,例如:

  • getTaskteacher vs getTask1school vs getTask2parent

规范建议:

  • 业务层不要直接暴露带数字后缀的方法名
  • src/api/*.ts 中封装为语义化名称,例如:
    • teacherGetTask / schoolGetTask / parentGetTask
    • 或按模块拆分到 src/api/teacher/task.ts 等(如后续重构允许)

6. 何时需要更新生成代码

当后端 Controller 或 DTO/VO 发生变更:

  1. 后端更新 OpenAPIKnife4j/SpringDoc
  2. 前端更新规范并重新生成(项目已有脚本):
npm run api:update
  1. 提交生成物(通常包含 api-spec.*src/api/generated/**

注意:如果某接口在后端已存在但 OpenAPI 未导出(例如缺少注解/返回类型不规范),应优先修后端文档,而不是在前端“硬编码路径”长期绕过。

7. 禁止事项(高频踩坑)

  • 禁止:手改 src/api/generated/**(下次生成会被覆盖,且会引入不可追踪差异)。
  • 禁止:页面里手写 axios 调用去访问 /api/v1/...(除非 OpenAPI 暂缺且已在适配层集中兜底)。
  • 禁止:在业务代码中扩散 any 来“快速通过类型检查”。

8. 迁移策略(从旧 http 到 Orval

若已有模块使用 src/api/index.tshttp.get/post/...

  • 短期:保留旧实现,但新增/变更接口优先走 readingApi
  • 中期:逐模块把旧 http 调用替换为 readingApi,并在适配层维持页面不改
  • 长期:页面全面只依赖适配层/生成客户端,减少重复封装

本地存储规范

  • Key 统一:只在 src/utils/useLocalStorage.tsLocalStorageKey 中声明与复用
  • 登出清理:使用 outLoginClearStorage()(如需扩展清理范围,优先扩展 LocalStorageKey

组件与文件命名

  • Vue 组件
    • 通用组件:src/components/PascalCase.vue(现有仓库既有 PascalCase 也有小写文件名;新增时优先 PascalCase避免混乱扩大
    • 页面组件:src/pages/<module>/index.vue(仓库已有此习惯)
    • 页面子组件:src/pages/<module>/components/*.vue
  • TS 工具src/utils/camelCase.tsuseXxx.tshook/组合式函数)
  • Storesrc/store/useXxx.tssrc/store/xxx.ts(与现有文件保持一致,避免引入第三种命名体系)

变更边界AI 必须遵守)

  • 不做无关重构:只改与需求相关的文件/代码;不要“顺便”换写法、换目录结构、统一命名
  • 不引入新依赖:除非需求明确且必要;新增依赖要同步更新 package.json 并说明原因
  • 不改公共行为:如 request、token 同步、路由生成规则、构建配置(除非需求明确)

环境变量与配置(不要绕开)

  • 读取方式:统一用 src/utils/env.tsgetAppEnvConfig(),不要在业务代码里到处直接读 import.meta.env.xxx
  • 常用字段
    • VITE_PREFIX_API:请求前缀(当前 request 使用的是 (${VITE_PREFIX_API}${url})
    • VITE_BASE_API:存在但当前请求实现未拼接(历史上有注释;如需启用必须全局评估影响)
  • 注意vite.config.ts 已配置 proxy/scienceApi/localhostApi);开发环境应优先通过前缀 + proxy 工作,而不是写死域名

提交与协作

  • 提交前:确保 pnpm dev 能启动、关键页面可访问(至少覆盖改动路径)
  • 格式化:提交时会自动跑 Prettier如果出现大量无关格式 diff说明你改动触发了更大范围格式化需收敛修改范围
  • 变更说明PR/提交信息优先解释“为什么”而不是“改了什么”

AI 变更自检清单(提交前必过)

  • 范围:是否只改了需求相关文件?是否避免“顺手重构/改命名/大面积格式化”?
  • 类型tsconfig 已启用 strictnoUnused*,新增代码是否无未使用变量/参数?
  • 路由:新增页面是否放在 src/pages/ 并使用 definePage(如需 alias
  • 请求:是否遵守 Orval 规范(生成代码只读、调用集中在 src/api/client.ts/适配层,避免页面散落 axios/fetch
  • 鉴权:涉及登录态/鉴权时,是否确保 store 中的 token 与 API 客户端实际使用的鉴权头/拦截器/存储一致?
  • 存储localStorage key 是否只使用 LocalStorageKey
  • 格式:是否符合 .prettierrc单引号、2 空格、行宽 100并接受提交前会被 Prettier 重写?

常见开发模板(给 AI 直接套用)

新增页面(文件路由)

  1. 新建 src/pages/<biz>/index.vue
  2. script setup 中写 definePage({ alias: [...] })
  3. 使用 RouterView/组件拼装页面

新增接口并调用

  1. 后端更新 OpenAPI确保该接口可被导出
  2. 前端执行 npm run api:update 重新生成(产物在 src/api/generated/**
  3. src/api/client.tssrc/api/*.ts(适配层)封装稳定接口
  4. 页面/组件只从 src/api/client.ts 或适配层引入调用,避免散落直连请求