12 KiB
12 KiB
LPT 前端应用(lpt-fe)
Vue 3 + TypeScript + Vite 单页应用。
Git 提交规范
- 提交信息必须简短且使用中文,不要使用英文长句。
- 格式:
类型: 简述,例如feat: 新增学习会话页面、fix: 修复路由守卫401跳转、docs: 补充组件说明。
技术栈
- 框架:Vue 3 + TypeScript + Vite 5
- UI 库:Element Plus 2.8
- HTTP:Axios(
withCredentials: true,自动携带 Cookiesatoken) - 思维导图:mind-elixir 5.13
- Markdown:marked 18.0
- 测试:Vitest + Playwright
项目结构
lpt-fe/src/
├── api/ → API 接口层
├── components/ → 页面组件
├── components/composables/ → 组合式函数
├── router/ → 路由配置
├── utils/ → 工具函数
├── assets/ → 全局样式与资源
└── __tests__/ → 单元测试
核心组件:Login.vue、Welcome.vue、Study.vue、StartTask.vue、Review.vue、ReviewDetail.vue、ReviewRecall.vue、TaskForm.vue、MindMapViewer.vue、MarkdownRenderer.vue。
路由表
| 路径 | 组件 | 说明 |
|---|---|---|
/login |
Login.vue | 登录页(无需认证) |
/welcome |
Welcome.vue | 首页 |
/study |
Study.vue | 学习任务列表 |
/start-task/:taskNum |
StartTask.vue | 学习会话 |
/add-task |
TaskForm.vue | 创建任务 |
/update-task/:taskId |
TaskForm.vue | 更新任务 |
/review |
Review.vue | 复习总览 |
/review/detail/:type/:id |
ReviewDetail.vue | 复习详情 |
/review/recall/:taskNum |
ReviewRecall.vue | 回忆复习 |
认证与请求
- Axios
withCredentials: true,Cookiesatoken自动携带。 - 401 处理:HTTP 层和业务层双重检测,清理
localStorage.isLoggedIn后跳转/login。 validateResponse:code !== 200时 reject。- Vite 代理:
/api→http://localhost:5157,自动重写去掉/api前缀。
Markdown 学习材料
- 编辑:textarea 输入,支持
[文字](url)和裸 URL。 - 预览:
renderMarkdown(raw)同步函数,使用__LINK_N__占位符防止嵌套 HTML。 - 保存:
convertMaterialUrls()异步拉取标题,替换裸 URL 为[标题](url)。 getUrlTitle(url):调用/utils/fetch-title,带内存缓存和请求去重。
应用场景
- TaskForm 编辑页使用
el-dialog弹窗管理应用场景。 - Study 详情页使用
getUrlTitle展示应用场景链接标题。
样式与交互约定
- 链接使用绿色系
var(--green-600),虚线下划线,hover 变实线。 - 区块分隔使用
.detail-block:border-top+padding-top,首个除外。 - 小字提示使用 12px
var(--text-secondary)。 Study.vue任务清单每页 20 条。StartTask.vue编辑任务时先 GET 详情,合并后再 PUT,避免覆盖其他字段。MindMapViewer.vue支持只读、编辑、selectable、colorByCompare 模式;selectable 模式下点击节点 emitnode-select。- ReviewRecall 中
standardExpanded展开后节点可点击,用于选择复习起点。
前端代码标准
交互防抖与加载原则
- 点击后需要跳转到其他页面时,应立刻执行跳转,不要在跳转前等待接口返回;目标页面在数据未加载完成时必须展示页面级 loading。
- 点击后不跳转页面时,触发接口的按钮必须自身展示 loading 或在请求期间禁用,防止网络慢时被多次触发。
- 登录、创建/更新、结束会话等必须确认后端成功的操作属于例外:按钮先进入 loading,成功后再跳转。
- 同一异步动作执行期间必须通过 loading 或 disabled 阻止重复触发,不能只依赖路由跳转后的页面卸载。
按钮规范
- 主操作 / 卡片 CTA:
size="large"(实测 40px 高、15px 字),如 Study 开始任务、Welcome 快速入口。 - 区块工具栏 / 页内操作:默认尺寸(32px 高、14px 字),如 Review 刷新、ReviewRecall 面板操作。
- 表格/列表行内操作:
size="small"(28px 高、13px 字;不得再调小)。 - 弹窗 footer:默认尺寸;确认按钮
type="success",取消按钮默认;危险操作统一type="danger"。 - 正向/保存语义统一
success,次级编辑语义可用primary,不要在同一功能上混用。 - 窄屏(
max-width: 768px)操作区按钮纵向全宽,全局规则位于src/assets/main.css的.action-row / .edit-actions / .fragment-edit-actions / .task-actions。
颜色与主题
- 禁止在组件内写颜色字面量:新增颜色先加到
src/assets/base.css的:root,组件只用var(--x)。语义色已提供--accent-warning / --accent-warning-soft / --accent-warning-strong / --accent-success-soft / --accent-success-softer / --accent-success-strong / --accent-success-text。 - Element Plus 主题色在
src/assets/main.css的:root覆盖(--el-color-success/--el-color-primary指向--green-700),全站按钮/标签/开关自动生效,不要在组件里逐个覆盖 EP 组件色。 src/main.ts中自定义样式必须写在import 'element-plus/dist/index.css'之后,否则主题覆盖会被 EP 覆盖。- 实底主色统一用满足 WCAG AA(白字对比度 ≥4.5:1)的
--green-700;--green-600仅用于文字/链接。 - 颜色字面量不得出现在 JS/模板表达式里(如
:color="'#xxx'"引 CSS 变量会失效),需要动态色时保留字面量或改用 CSS 类。
卡片列表交互
- 卡片列表的卡片整体可点击进入详情:外层加
role="link" tabindex="0"与aria-label,并绑定@click及@keydown.enter/@keydown.space.prevent。 - 卡片内的附加按钮必须
@click.stop,避免被卡片点击吞掉(如 Review 的「回忆复习」)。 - 卡片操作使用右对齐操作簇(
justify-content: flex-end),禁止用space-between把两个按钮拉到卡片两端。 - 加载失败必须区分于空数据:保留一个
loadFailed状态,失败时给出可读提示 + 重试按钮,重试前先重置该状态;不要只靠finally关 loading 导致空白页。
移动端适配
- 断点约定:按钮/弹窗使用 768px,卡片与汇总布局使用 900px。
- 弹窗:窄屏宽度
calc(100% - 24px),body 允许纵向滚动,footer 按钮等宽;全局规则位于main.css的@media (max-width: 768px),新增弹窗不需要再逐页适配。 - 多按钮弹窗(如 Welcome 回忆卡片)在窄屏纵向堆叠。
- 列表页优先使用卡片列表而非
el-table,桌面与移动端视觉统一,参考Review.vue的.task-list。 - 文案不缩写:使用完整字段名,如
学习报告数量、学习残片数量,不使用报告数、残片数。
代码整洁
- Markdown 渲染统一使用
src/utils/markdown.ts的renderMarkdown,禁止在组件内复制实现。 - 应用场景状态常量统一使用
src/utils/taskApplication.ts的applicationStatusOptions。 - 用户可见错误提示必须口语化、可理解,不要直接展示接口原始 message、JSON、参数名或“服务器返回”等技术信息;技术细节留在日志。
- 不保留无消费者代码:未使用的 import、prop、事件、API 封装、CSS class、组件文件与依赖应及时删除。
- 组件公开 props/events 只在有实际消费者时保留,例如 MindMapViewer 通过
toOutline()导出编辑内容,不依赖无人监听的 change 事件。 - Vite 模板遗留文件(示例组件、icon、logo)不进入业务代码。
请求层与类型规范
- 所有接口封装必须放在
src/api/,组件内禁止直接拼 URL 调用request(历史遗留的裸调用在改动到时迁移)。 - request 层统一返回
Promise<ApiResponse<T>>:新增接口必须显式标注泛型T,组件消费res.data时应有明确类型,禁止把后端字段拼错暴露到运行期。 - 默认超时 30 秒;AI 聚合类接口(残片生成、思维导图生成/对比、报告草稿、结束会话)在 api 层显式覆写
{ timeout: 300_000 },不要调大全局默认值。 - 组件解构响应时使用
res?.data判空兜底;ApiResponse从@/utils/request导入复用。
组合式函数复用
- “已等待 N 秒”类秒表计时统一使用
useElapsedSeconds,禁止组件内手写setInterval秒数递增。 - 页面新增功能域时先抽 composable(参考
useSessionHistory/useSummaryReport/useSessionExpectation),不要继续膨胀 StartTask 等大页面组件。 - 通用解析器、格式化工具放
src/utils/,不埋在组件内(如大纲文本转树应放 utils)。
测试规范(分层策略)
测试按「金字塔」分层组织,新增功能优先补中间层:
| 层 | 位置 | 职责 | 风格 |
|---|---|---|---|
| 逻辑单测 | src/__tests__/{api,composables,utils,router}/ |
纯函数、composable、request 封装 | 直接调用,快、准 |
| 交互式集成测试 | src/__tests__/integration/ |
页面关键用户路径 | 真实挂载 + 模拟点击 + DOM 断言 |
| E2E | e2e/ |
真实浏览器回归 | Playwright + page.route 拦后端 |
交互式集成测试要求
- 必须真实挂载组件(不
shallow),通过trigger('click')、setValue()、find('…')模拟并断言用户可见行为;禁止wrapper.vm.xxx()直调内部方法作为主要测试手段(历史遗留的 vm 直调在改动到时迁移)。 - 页面关键路径必须有集成测试:登录、学习会话暂停/继续、任务创建/更新、回忆对比等新增关键流程同步补
integration/*.spec.ts。 - 只断言应用自身行为(API 调用参数、提示、路由、状态文案),不要重复验证 Element Plus 内部行为;jsdom 下 EP 的 callback 式表单校验不可靠(空表单也可能判有效),「校验拦截」类断言由 e2e 在真实浏览器覆盖。
- 已知兼容问题:
el-tag在 jsdom + VTU 全量挂载时 vnode mounted 钩子崩溃(EP 2.8),集成测试统一stubs: { ElTag: true };依赖 mind-elixir 的MindMapViewer用可编程 stub(提供toOutline)。 - E2E 定位优先使用角色与可访问名称(
getByRole('button', { name })、getByPlaceholder),仅断言 EP 内部 UI(校验错误、消息弹层)时才用类选择器;改文案不应导致大面积碎测。
逻辑单测要求
- 单元测试必须直接测试真实代码:直接调用 composable、挂载真实组件、使用真实路由;禁止把组件或 composable 的逻辑复制到测试里再自测。
- 测试中可以 mock 外部依赖(API、Element Plus 服务、Audio 等),但被测逻辑本身必须来自生产模块。
script setup内部状态需要测试访问时,通过defineExpose暴露,而不是在测试里重写一份相同逻辑。
覆盖率
npm run test:coverage输出 v8 覆盖率报告(coverage/),阈值配置在vite.config.ts,作为棘轮只升不降:任何低于阈值的改动不允许提交。- 提升覆盖率优先补集成测试与零覆盖模块(当前缺口:Review、ReviewDetail、fetchTitle、markdown 渲染分支),不为凑数写快照式断言。
提交前检查
- 运行
npx vue-tsc --noEmit、npm run lint(0 error 才可提交;no-explicit-any允许存量警告,新代码避免 any)、npm run test、npm run build。 - 提交信息按 Git 提交规范使用中文短句;样式类改动用
style:,清理类用refactor:/chore:。
开发配置
- 开发端口:5158
- 代理:
/api→http://localhost:5157 - 环境变量:
.env.development/.env.production/.env.uat
启动命令
npm install
npm run dev
npm run build
npm run test
npm run test:e2e
npx vue-tsc --noEmit
npm run lint
npm run test:coverage
编译检查也可以使用:
npx vite build
npx vue-tsc --noEmit
Docker
- 多阶段构建:
node:20-alpine编译 →nginx:alpine运行。 - 暴露端口:80。
- 构建参数:
BUILD_MODE,默认 production。
关联项目
| 项目 | 路径 | 端口 | 说明 |
|---|---|---|---|
| lpt-be | ../lpt-be/ |
5157 | Spring Boot 后端,前端通过 /api 代理调用 |
| lpt-ai | ../lpt-ai/ |
5199 | AI 服务,前端不直接调用 |
调用关系
浏览器 → lpt-fe (5158) ──/api──→ lpt-be (5157) ──HTTP──→ lpt-ai (5199)
│
└── MySQL (8109)