CLAUDE.md
此文件为 Claude Code (claude.ai/code) 在此代码库中工作时提供指导。
目录
- 项目概述 - 技术栈与基本信息
- 快速开始 - 环境准备与新手指南
- 常用开发命令 - 开发、测试、部署命令
- 目录结构 - 项目文件组织
- 路径别名 - 导入路径快捷方式
- 核心架构模式 - API、组件、路由、样式、状态管理
- 重要注意事项 - 常见陷阱与最佳实践
- 业务系统架构 - 6大业务模块说明
- 常见问题与解决方案 - Q&A
- 功能更新记录 - 最近变更历史
- 文档索引 - 相关文档链接
项目概述
美乐爱觉 (mlaj) - 基于 Vue 3 的教育平台,深度集成微信生态,服务于教师和学生。
-
框架: Vue 3.5.13 (Composition API +
<script setup>) - 构建工具: Vite 6.2.0
-
包管理器: pnpm (项目使用
.nvm管理 Node.js 18.19.1) - UI 框架: Vant 4.9.22 (移动端优先,组件自动导入)
- 样式: TailwindCSS 3.4.1 + Less 4.2.2 (分层架构)
-
状态管理: Context API (
/src/contexts/) + localStorage - 路由: Vue Router 4.5.0,支持懒加载和路由守卫
- HTTP: Axios 1.8.4,集中式拦截器
-
视频播放: Video.js 7.21.7 (通过
@videojs-player/vue封装)
快速开始
环境准备
-
Node.js 版本: 使用
.nvm管理 Node.js 18.19.1
nvm use 18.19.1
- 安装依赖:
pnpm install
-
启动开发服务器:
bash pnpm dev
新手指南
添加新页面:
- 在
/src/views/相应模块下创建页面组件 - 在
/src/router/对应路由文件中注册路由 - 如需认证,在路由
meta中添加requiresAuth: true
调用 API:
- 在
/src/api/对应模块中定义 API 函数 - 遵循统一返回结构:
{ code: 1, data: any, msg: string } - 使用
if (res.code === 1)检查成功(而非if (res.code))
创建可复用组件:
- 放置在
/src/components/ui/(通用)或相应业务目录 - 使用
defineProps和defineEmits定义接口 - 通过 emit 事件向父组件传递操作,避免直接调用 API
提取逻辑到 Composable:
- 在
/src/composables/创建useXxx.js - 返回响应式 refs 和函数
- 内部处理副作用(API 调用、事件监听等)
常用开发命令
核心开发
pnpm dev # 启动开发服务器 (使用 Node.js 18.19.1)
pnpm build # 生产环境构建
pnpm preview # 本地预览生产构建
pnpm test # 使用 Vitest 运行测试
API 代码生成 ⭐ NEW
yarn api:generate # 从 OpenAPI 文档自动生成 API 代码
yarn api:diff # 对比 API 变更
使用流程:
- 在
docs/api-specs/模块名/接口名.md创建 OpenAPI 文档 - 运行
yarn api:generate自动生成src/api/模块名.js - 导入并使用生成的 API 函数
部署
pnpm dev_upload # 部署到开发服务器
pnpm behalo_upload # 部署到 behalo 环境
pnpm oa_upload # 部署到 OA 服务器
开发流程
- 每个部署命令都会:构建、归档(tar.gz)、通过 scp 上传、在服务器上解压、清理归档文件。
目录结构
src/
├── api/ # API 接口层 - 15个模块,按领域组织
│ ├── auth.js # 认证相关接口
│ ├── checkin.js # 打卡/作业接口
│ ├── course.js # 课程相关接口
│ ├── teacher.js # 教师端接口
│ ├── recall_users.js # 召回系统接口
│ └── ...
├── assets/ # 静态资源 (图片、CSS、模拟数据)
├── components/ # 可复用的 Vue 组件
│ ├── ui/ # 通用 UI 组件 (VideoPlayer, AudioPlayer, CheckInDialog 等)
│ ├── checkin/ # 打卡相关组件
│ ├── courses/ # 课程相关组件
│ └── studyDetail/ # 学习详情页组件
├── composables/ # 13个组合式函数,用于逻辑复用
│ ├── useAuth.js # 认证相关逻辑
│ ├── useCheckin.js # 打卡流程与提交
│ ├── useCheckinDraft.js # 打卡草稿缓存
│ ├── useStudyComments.js # 评论获取、提交、分页
│ ├── useStudyRecordTracker.js # 学习时长追踪和分析
│ └── ...
├── contexts/ # 全局状态提供者 (auth, cart)
├── common/ # 共享常量和工具
├── router/ # Vue Router 配置
│ ├── index.js # 路由实例与守卫
│ ├── routes.js # 路由定义(懒加载)
│ └── guards.js # 认证守卫辅助函数
├── utils/ # 工具函数
│ ├── axios.js # Axios 实例与拦截器 (认证头、401 处理)
│ ├── tools.js # 通用工具 (wxInfo, 文件处理等)
│ └── auth_user_info.js # 用户信息存储辅助函数
└── views/ # 页面组件,按功能组织
├── auth/ # 登录/认证页面
├── courses/ # 课程浏览、详情、购买
├── profile/ # 用户资料、设置、课程历史
├── recall/ # 提醒/回忆系统
├── study/ # 学习资料和内容
└── teacher/ # 教师端仪表板、任务、班级管理
路径别名
在 vite.config.js 和 jsconfig.json 中配置:
-
@/→src/ -
@components/→src/components/ -
@composables/→src/composables/ -
@utils/→src/utils/ -
@api/→src/api/ -
@images/→src/assets/images/ -
@css/→src/assets/css/ -
@mock/→src/assets/mock/ -
common/→src/common/ -
@root/→ 项目根目录
核心架构模式
API 接口层架构
位置: /src/api/
所有接口遵循统一的响应结构:
{
code: 1, // 1 = 成功,其他 = 失败
data: any, // 响应数据
msg: string // 消息(成功时为空,失败时为错误信息)
}
重要: 检查成功时使用 if (res.code === 1) 而不是 if (res.code),避免将 401/403 误判为成功。
认证头 通过 axios 拦截器自动添加 (/src/utils/axios.js):
- 每次请求读取
localStorage.user_info - 设置
User-Id和User-Token请求头 - 401 响应处理:仅当当前路由需要认证时才重定向到登录页 (见 guards.js 中的
checkAuth)
组件分层
视图页面 (/src/views/): 页面级组件,负责编排:
- 通过 API 获取数据
- 通过 refs/composables 管理状态
- 路由和导航
- 集成更小的组件
UI 组件 (/src/components/ui/): 可复用、无状态或自包含:
- VideoPlayer, AudioPlayer, CheckInDialog, SharePoster, SearchBar 等
- 应尽可能避免直接调用 API
- 通过 emit 事件向父组件传递操作
组合式函数 (/src/composables/): 可复用逻辑:
-
useAuth: 认证状态管理与登录流程 -
useCheckin: 打卡流程和提交(七牛上传、校验、提交) -
useCheckinDraft: 打卡草稿自动缓存(防止数据丢失) -
useStudyComments: 评论获取、提交、分页 -
useStudyRecordTracker: 学习时长追踪和分析 - 返回响应式 refs 和函数,内部处理副作用
路由与认证
⚠️ CRITICAL: 本项目使用 Hash 模式路由
在生成任何路由 URL 前,必须确认使用
/#/前缀:
- ✅ 正确:
/#/login,/#/profile,/#/courses/123- ❌ 错误:
/login,/profile,/courses/123配置来源:
src/router/index.js:15history: createWebHashHistory(import.meta.env.VITE_BASE || '/')此规则适用于:
- 代码生成:
router.push(),<router-link to="">- 测试编写:Playwright
page.goto(), Vitest 路由测试- 文档编写:所有示例 URL
路由 使用懒加载进行代码分割:
{
path: '/courses/:id',
component: () => import('@/views/courses/CourseDetailPage.vue')
}
认证守卫 (/src/router/guards.js):
-
checkAuth(route)- 返回true或重定向对象 - 不再自动触发微信认证;仅通过登录页手动触发
- 路由可标记
meta: { requireAuth: false }以公开访问
样式策略
TailwindCSS 是主要样式方案:
- 用于布局、间距、排版、颜色
- 自定义颜色定义在
tailwind.config.js中 (primary: #4caf50)
Less 用于组件特定样式:
- 组件边界内的嵌套选择器
- 使用
<style lang="less" scoped>限制作用域到组件 - 使用
:deep()修改 Vant/VideoJS 内部样式
自动导入:
- Vant 组件自动导入(无需手动导入)
- Vue 组合式函数自动导入 (ref, computed, onMounted 等)
状态管理模式
用户认证状态:
- 运行时:
contexts/auth.js→currentUserref - 持久化:
localStorage.currentUser和localStorage.user_info - Axios 请求头: 从 localStorage 派生(在拦截器中)
- 辅助函数:
getUserInfoFromStorage(),removeUserInfoFromStorage()
购物车/订单状态:
- 运行时:
contexts/cart.js - 通过 provide/inject 在页面间保持
微信集成
微信 JS SDK 初始化在 App.vue 中:
- 仅在浏览器环境中
- 通过
wxInfo()工具检测环境 -
wxInfo().isWeiXin- 在微信浏览器中运行 -
wxInfo().isPc- 在 PC 上运行
微信认证流程:
- 用户在登录页点击微信图标
- 重定向到微信 OAuth
- 回调 →
getUserIsLoginAPI()→ 写入用户信息到存储 - 重定向到原页面或首页
微信支付:
- 集成在结账流程中
- 生产环境: 需要微信浏览器环境 (
wxInfo().isWeiXin) - 免费课程: 跳过环境验证
文件预览系统
PDF 预览:
- 组件:
@sunsetglow/vue-pdf-viewer - 专用路由:
/pdfPreview用于浏览器内预览
Office 文档 (Word, Excel, PPT):
-
@vue-office/docx,@vue-office/excel,@vue-office/pptx - 弹窗预览组件,带错误处理
图片:
- 使用 Vant 的
van-image-preview
视频播放器:
- 基于
@videojs-player/vue(Video.js 7.21.7 的封装) - 自定义
VideoPlayer.vue组件添加错误处理、自动重试和播放速率控制
重要注意事项
视频播放器问题
VideoPlayer 使用 v-show vs v-if:
- Video.js 在
v-show(display: none) 下无法正常工作 - 使用
v-if确保视频元素在初始化前完全挂载 - 参考
StudyDetailPage.vue中的正确模式:设置isPlaying = true,然后setTimeout再调用play()
401 响应处理
src/utils/axios.js 中的 axios 响应拦截器:
- 仅当
checkAuth(to)返回重定向时才跳转到登录页 - 公开页面(如课程详情)即使在 401 时也保持当前页
- 页面组件应为自己的用户操作处理 401
API 响应安全性
始终检查 res.code === 1(而不仅仅是 res.code):
// 正确
const { code, data } = await getCourseDetailAPI({ i: courseId })
if (code === 1) { ... }
// 错误 - 将 401/403 当作成功
if (code) { ... }
组件自动导入
Vant 组件通过 unplugin-vue-components 自动导入:
-
不要导入
import { Button } from 'vant' - 直接使用
<van-button> - 参见
src/components.d.ts查看所有可用组件
打卡系统
核心 composable: useCheckin.js 封装打卡流程:
- 文件上传:七牛云 + Hash 秒传(避免重复上传)
- 支持类型:文本、图片、视频、音频、计数打卡
- 数据校验:文件大小、数量、类型验证
- 提交流程:新增/编辑统一处理
草稿缓存: useCheckinDraft.js 防止数据丢失:
- 自动保存:输入内容实时缓存到 localStorage
- 智能恢复:页面加载时自动填充上次未提交的内容
- 清理机制:提交成功或手动清除时移除草稿
- 作业有效性校验:作业失效时自动清理草稿
- 完整文档:docs/tasks/done/暂存用户打卡信息.md
统一组件: CheckInDialog.vue 处理所有打卡流程:
- 接收
items_today和items_historyprops - 每项包含
id,name,task_type('checkin'/'upload'),is_gray - 完成时触发
check-in-success事件
可复用列表组件: CheckInList.vue
- 用于首页和 CheckInDialog
- 对话框使用紧凑模式,页面使用正常模式
-
submit-success事件向上冒泡
环境配置
使用 .env.development, .env.production 等:
-
VITE_PORT- 开发服务器端口 -
VITE_BASE- 基础路径 -
VITE_PROXY_PREFIX- API 代理前缀 -
VITE_PROXY_TARGET- 后端 API 目标 -
VITE_OUTDIR- 构建输出目录
环境与部署:
-
开发环境:
http://oa-dev.onwall.cn/ -
生产环境:
http://oa.onwall.cn - 部署流程: 构建 → 归档(tar.gz) → SCP 上传 → 服务器解压 → 清理归档
- 代码分割: 手动配置 chunks,Office 工具和图片工具单独打包
Speckit 框架命令
位于 .cursor/commands/:
-
/speckit.specify- 从需求生成规范 -
/speckit.plan- 创建实现计划 -
/speckit.tasks- 生成任务分解 -
/speckit.analyze- 跨文件一致性分析 -
/speckit.implement- 执行实现计划 -
/speckit.checklist- 验证检查清单 -
/speckit.clarify- 需求澄清 -
/speckit.constitution- 项目原则和约束
代码风格说明 (来自 VUE_CODE_STYLE_GUIDE.md)
当前实践:
- 新组件使用
<script setup>和 Composition API - API 函数应始终返回
{ code, data, msg }- 永不返回false - 尽可能将逻辑提取到 composables (
/src/composables/useXxx.js) - 本地状态使用 refs,派生状态使用 computed
- 除非必要避免 JSX;大多数页面使用 Vue 模板
已知不一致性 (需注意):
- 文件中混合使用分号/无分号
- 混合使用 2 空格/4 空格缩进
- 混合使用 function/箭头函数
- 混合使用中文/英文注释
- API 返回值语义不一致(部分返回
false而非标准结构) - 部分页面组件过大,应考虑拆分
改进建议:
- 统一使用 ESLint + Prettier 进行代码格式化
- 制定统一的 API 返回值规范
- 大型页面组件应拆分为多个子组件
- 增加单元测试覆盖(当前测试文件较少)
特殊功能
欢迎页 (2026-01 新增)
- 首次访问检测: localStorage 标志位 + 路由守卫拦截
- 视频背景: 星空宇宙主题视频(循环播放)
- 功能入口: 课程、活动、个人中心(水平布局 + 浮动动画)
-
环境变量控制:
VITE_WELCOME_PAGE_ENABLED/VITE_WELCOME_VIDEO_URL -
调试工具:
window.resetWelcomeFlag()/window.showWelcome() - 完整文档: docs/tasks/done/26.1.28-欢迎页开发计划/
分享海报生成
- 基于 Canvas 的海报生成,带二维码 (
qrcode包) - 自动优化
cdn.ipadbiz.cnURL 的图片 - 使用
crossorigin="anonymous"处理跨域 Canvas 污染 - 组件:
SharePoster.vue(可复用)
动态 Open Graph 元标签
- 课程详情页动态添加
og:title,og:description,og:image,og:url - 页面卸载时移除标签以避免冲突
标签指示器动画
- 基于 ResizeObserver 的指示器定位
- 处理异步加载第三列而不错位
- 参考
StudyCoursePage.vue的实现
测试
- 测试运行器: Vitest
-
命令:
pnpm test - 测试文件位于
/test/(当前可能测试覆盖较少)
业务系统架构
1. 用户与认证 (User & Auth)
- 核心流程:支持手机号+验证码/密码登录,注册流程。
-
微信集成:
- 环境检测:
wxInfo().isWeiXin检测微信浏览器。 - 授权登录:基于 OAuth 2.0,通过
getAuthInfoAPI获取 OpenID/UserInfo,支持静默/显式授权。 - 分享配置:通过 JS-SDK 配置自定义分享标题、描述和图片。
- 环境检测:
- 个人中心:管理个人资料(头像/昵称/手机/密码)、消息通知、帮助中心。
-
状态管理:
contexts/auth.js维护全局currentUser,使用 localStorage 持久化,Axios 拦截器自动注入 Token。
2. 课程系统 (Courses)
- 展示与浏览:首页推荐、课程列表(热门/精选/搜索)、课程详情页(介绍/目录/评价)。
-
购买流程:
- 购物车:
contexts/cart.js支持单品/多品模式,本地持久化。 - 结算:收银台页面 (
/checkout),集成微信支付 (api/wx/pay.js)。 - 环境校验:付费课程强制在微信环境内购买。
- 购物车:
-
学习体验:
- 学习页:展示课程目录与学习进度。
- 内容预览:集成 Video.js 播放视频,PDF.js 预览文档,Office Web Viewer 预览 Word/Excel/PPT。
- 记录追踪:
useStudyRecordTracker自动追踪学习时长与进度。
3. 打卡与作业系统 (Check-in & Tasks)
-
学生端:
-
作业日历:
IndexCheckInPage展示每日打卡状态,支持按月切换。 -
统一打卡页 (
/checkin/detail): - 支持类型:文本、图片、视频、音频、计数打卡(如感恩日记)。
- 核心逻辑:
useCheckin封装上传(七牛云+Hash秒传)、校验、提交逻辑。 - 交互:支持补卡、编辑已提交内容、选择打卡对象(计数模式)。
- 互动社区:打卡动态流,支持点赞、评论、查看他人作品。
-
作业日历:
-
教师端 (
/teacher):- 作业管理:发布新作业,查看作业列表与详情(完成率/出勤率统计)。
- 班级管理:查看班级学员列表,进入学员详情页。
- 审批流程:审核学生提交的打卡内容,支持通过/驳回。
4. 活动系统 (Activities)
-
功能:活动列表展示、活动详情、在线报名表单 (
/activities/:id/signup)。 - 记录:用户可查看“我的活动”历史记录。
5. 召回系统 (Recall)
- 目标:面向老用户的营销与促活流程。
-
流程:
- 登录/身份验证(支持身份证查询历史)。
- 引导页与信息补全。
- 时光机旅程 (Timeline):展示用户在平台的历史里程碑。
- 活动历史回顾与积分汇总。
- 专属海报:生成个性化分享海报。
- 导流:引导至 AI 星球或最新课程。
6. 积分与订单
- 订单:查看订单状态(待支付/已支付/退款),支持取消订单。
- 积分:查看积分余额与变动明细,积分可用于抵扣或兑换(视具体业务规则)。
核心技术栈与实现
状态管理
-
Context API:项目主要使用 Vue 3
provide/inject模式进行全局状态管理。-
auth.js:用户认证、登录态、Token 管理。 -
cart.js:购物车逻辑、商品增删改、结算流程。
-
-
持久化:关键状态同步至
localStorage,页面刷新不丢失。
文件处理
-
七牛云上传:
- 前端计算文件 Hash (
browser-md5-file/qiniuFileHash)。 - 申请上传 Token(支持 Hash 秒传检测)。
- 直传七牛云对象存储。
- 回填业务服务器保存元数据。
- 前端计算文件 Hash (
-
文档预览:
- PDF:
@sunsetglow/vue-pdf-viewer - Office:
@vue-office/docx,@vue-office/excel,@vue-office/pptx - Video:
@videojs-player/vue(Video.js 7.x)
- PDF:
路由与权限
-
路由守卫 (
guards.js):-
checkAuth:基于白名单或meta.requiresAuth拦截未登录访问。 -
startWxAuth:按需触发微信授权流程(非自动触发,避免死循环)。
-
常见问题与解决方案
Q: 为什么 Video.js 在切换显示/隐藏时无法正常工作?
A: Video.js 在 v-show (display: none) 状态下初始化会失败。
-
解决: 使用
v-if而非v-show -
正确模式: 设置
isPlaying = true,然后setTimeout再调用play() -
参考:
StudyDetailPage.vue中的实现
Q: 为什么我的 401 响应导致页面跳转到登录页?
A: 这是预期的行为,但仅对需要认证的页面生效。
- 公开页面(如课程详情)不会跳转
- 受限页面会清理登录信息并重定向
- 如需自定义处理,在组件内监听 API 错误
Q: 如何正确检查 API 响应是否成功?
A: 始终使用 if (res.code === 1) 而非 if (res.code)。
// ✓ 正确
const { code, data } = await getCourseDetailAPI({ i: courseId })
if (code === 1) {
/* 成功 */
}
// ✗ 错误 - 会将 401/403 当作成功
if (code) {
/* ... */
}
Q: 为什么 Vant 组件导入后报错或无法使用?
A: Vant 组件已配置自动导入,无需手动导入。
// ✗ 错误
import { Button } from 'vant'
// ✓ 正确
// 直接使用 <van-button>,无需导入
Q: 如何在微信环境下正确处理支付和授权?
A: 使用 wxInfo() 工具函数检测环境:
import { wxInfo } from '@/utils/tools'
// 检测微信环境
if (wxInfo().isWeiXin) {
// 微信浏览器
} else {
// 其他浏览器
}
Q: 打卡时如何避免数据丢失?
A: 使用 useCheckinDraft 自动缓存草稿:
import { useCheckinDraft } from '@/composables/useCheckinDraft'
const { saveDraft, loadDraft, clearDraft } = useCheckinDraft()
// 输入时自动保存
onMounted(() => loadDraft())
watch(inputValue, () => saveDraft(inputValue.value))
// 提交成功后清除
onSubmitSuccess(() => clearDraft())
功能更新记录 (Recent Changes)
最新更新 (2026-01)
欢迎页功能 (2026-01-28):
- 新增首次访问欢迎页 (
/welcome):星空宇宙主题视频背景 - 实现首次访问检测:localStorage 标志位 + 路由守卫拦截
- 水平布局功能入口:课程、活动、个人中心(带浮动动画)
- 环境变量控制:
VITE_WELCOME_PAGE_ENABLED开关 - 详细文档:docs/tasks/done/26.1.28-欢迎页开发计划/
打卡系统增强 (2026-01):
- 打卡详情页重构 (
/checkin/detail):统一文本、媒体上传和计数打卡入口 - 新增草稿缓存功能 (
useCheckinDraft):防止数据丢失,自动保存和恢复 - 打卡卡片优化 (CheckinCard):长文本折叠、多附件 Tab 切换
- 草稿恢复时的作业有效性校验:作业失效时自动清理
- 优化附件预览和编辑回填逻辑
- 实现基于
useCheckin的通用提交流程
教师端完善 (2025):
- 作业管理页面 (
/teacher/tasks) - 作业主页 (
/teacher/tasks/:id):统计与日历视图 - 学员作业记录页面 (
/teacher/student-record)
召回系统 (2025):
- 身份证查询历史功能
- 时光机旅程 (Timeline) 展示用户里程碑
- 专属海报生成与分享
基础体验优化 (2025):
- 登录逻辑调整:仅在点击微信图标时触发授权
- 搜索栏优化:支持 iOS 软键盘搜索键
- 课程详情页:动态 Open Graph 标签,优化分享体验
- 401 拦截策略优化:公开页面不再跳转登录页
历史功能更新 (详细记录)
- 教师端新增作业管理页面:路径
/teacher/tasks,标题“作业管理”。- 列表展示:作业名称、开始时间、截止时间。
- 当前数据来源为Mock,后续可替换为真实接口数据。
- 教师端新增作业主页:路径
/teacher/tasks/:id,标题“作业主页”。- 头部:作业名称、介绍文案、细项信息(周期、频次、时间段、附件类型)。
- 统计:出勤率与任务完成率(参考
myClassPage.vue统计样式,数据Mock)。 - 日历:使用
van-calendar单选模式,选择日期后展示当日学生完成情况。 - 学生完成情况:参考图片2样式,勾选代表已完成,未勾选代表未完成(数据Mock)。
- 教师端新增学员作业记录页面:路径
/teacher/student-record,标题“学员作业记录”。- 在作业主页的学生列表点击卡片可跳转至该页面(当前版本为固定示例页面)。
- 列表展示:作业帖子、图片/视频/音频、点赞与点评弹窗(与
studentPage.vue的作业记录样式一致)。 - 接口参数固定:
user_id=817017,group_id=816653(后续可替换为动态参数)。
- 学习详情页标签指示条修复:
/src/views/profile/StudyCoursePage.vue- 现象:首次进入且存在“打卡互动”时,底部绿色指示条定位错误。
- 修复:新增标签容器
ref与ResizeObserver,按栏目数量对容器进行等分,指示条宽度与位移按分段和索引计算,异步加载第三个栏目时不再错位。
-
登录逻辑调整:仅在登录页微信图标点击时触发授权
- 变更文件:
/src/views/auth/LoginPage.vue、/src/router/guards.js、/src/router/index.js - 路由守卫:移除自动微信授权检查,新增
startWxAuth供手动触发。 - 登录页:微信图标绑定点击事件,非微信环境提示“请在微信内打开”。
- 使用方式:进入登录页,点击微信图标进行授权登录。
- 变更文件:
-
课程详情页动态 Open Graph 元标签
- 行为:进入课程详情页时,在
<head>中插入 4 个meta标签:og:title(课程title)、og:description(课程subtitle)、og:image(课程cover)、og:url(当前页面 URL);这些meta必须插在<title>标签的前面;离开页面时移除。 - CDN 规则:若图片域名为
cdn.ipadbiz.cn,自动追加?imageMogr2/thumbnail/200x/strip/quality/70。 - 位置:
/src/views/courses/CourseDetailPage.vue,在onMounted插入,onUnmounted清理。 - 函数:
build_og_image_url(src)、set_og_meta(payload)、remove_og_meta()。
- 行为:进入课程详情页时,在
-
课程详情页咨询弹窗(Mock)
- 入口:详情页顶部快捷操作中的“咨询”按钮。
- 展示:底部弹出层,仅底部关闭按钮;展示联系人列表,可点击名称或号码直接拨打(
tel:)。 - 联系人:支持多个联系人,展示姓名与号码;电话号码使用绿色高亮;联系人与号码字体大小一致。
- 位置:
/src/views/courses/CourseDetailPage.vue,“咨询弹窗”模板与交互逻辑(open_consult_dialog、close_consult_dialog、consult_contacts、call_phone(phone))。
-
购买流程环境校验
- 行为:仅对非免费课程在详情页点击“购买”时进行校验;生产环境下必须为微信内置浏览器(
wxInfo().isWeiXin)。 - 免费课程:跳过微信环境校验,允许直接进入结算流程。
- 非微信环境(付费课):提示“请在微信内打开进行购买”,不进入结算。
- 位置:
/src/views/courses/CourseDetailPage.vue的handlePurchase中,使用wxInfo进行环境判断。
- 行为:仅对非免费课程在详情页点击“购买”时进行校验;生产环境下必须为微信内置浏览器(
-
微信授权自动触发(微信环境内)
- 行为:在微信内置浏览器环境下点击“购买”时,若检测到未完成微信授权(
openid_has=false),系统将自动发起一次微信授权流程并中止本次购买;授权完成后再次点击可进入结算。 - 开发环境:不触发微信授权流程(保留现有调试行为)。
- 位置:
/src/views/courses/CourseDetailPage.vue的handlePurchase中,调用getAuthInfoAPI()探测并使用startWxAuth()触发授权。
- 行为:在微信内置浏览器环境下点击“购买”时,若检测到未完成微信授权(
-
401拦截策略优化(公开页面不再跳登录)
- 行为:接口返回
code=401时,不再对公开页面(如课程详情/courses/:id)进行登录重定向;仅当当前路由确实需要登录权限时才跳转至登录页。 - 原理:响应拦截器调用路由守卫
checkAuth判断当前路由是否为受限页面,受限则清理登录信息并附带redirect重定向至登录页;公开页面保持当前页,由业务自行处理401。 - 位置:
/src/utils/axios.js,在响应拦截器中按需处理重定向。
- 行为:接口返回
-
搜索栏回车搜索兼容性提升
- 行为:将输入框类型改为
search,并可选开启form @submit.prevent机制以提升 iOS 设备软键盘“搜索”键触发稳定性;同时保留@keyup.enter回车触发搜索。 - 路由行为:
- 课程页(
isCoursePage=true):回车或搜索键触发后跳转至/courses-list并携带keyword参数。 - 列表页:仅更新当前路由的查询参数
keyword。 - 位置:
/src/components/ui/SearchBar.vue,新增useFormSubmit可选参数(默认开启)。
- 行为:将输入框类型改为
-
课程列表页搜索触发修复
- 现象:进入列表页后,输入关键字并更新为
/courses-list?keyword=xxx时未触发搜索。 - 原因:父组件
handleSearch对“相同关键字”进行了拦截,导致路由参数变化不执行请求。 - 修复:移除拦截逻辑;无论关键字是否变化均触发搜索(防抖控制频率)。
- 位置:
/src/views/courses/CourseListPage.vue的handleSearch。
- 现象:进入列表页后,输入关键字并更新为
-
分享海报弹窗(通用组件)
- 入口:课程详情页底部操作栏“分享”按钮。
- 组件:
/src/components/ui/SharePoster.vue(支持复用),v-model:show控制显隐,course传入课程信息,qr_url可指定二维码内容地址(默认取当前页面 URL)。 - 布局:上部封面图;下部信息区左侧二维码,右侧课程标题、副标题、精简介绍与日期范围。
- 样式:优先使用 TailwindCSS 布局;组件内部使用 Less 做层级嵌套的样式补充。
- 图片规则:当封面图域名为
cdn.ipadbiz.cn时,自动追加?imageMogr2/thumbnail/200x/strip/quality/70压缩参数。 - 接入位置:
/src/views/courses/CourseDetailPage.vue,导入并渲染<SharePoster v-model:show="show_share_poster" :course="course" />。 - Canvas 合成:弹窗打开时使用 Canvas 直接合成海报(封面图、二维码、文案),生成
dataURL并以<img>展示,用户可直接长按图片保存到手机(无需额外按钮)。 - 依赖:
pnpm add qrcode(在 Canvas 内本地生成二维码,避免跨域图片导致画布污染)。 - 跨域:通过
crossorigin="anonymous"加载封面,并追加时间戳防缓存;若封面跨域不允许,则显示降级卡片,仍可长按截图保存。 - 文案:使用中文字体并自动换行限制行数,末行超出追加省略号。
-
打卡弹窗统一为通用组件 CheckInDialog
- 目的:统一 CourseDetailPage、StudyCoursePage、StudyDetailPage 三处页面的打卡弹窗与交互,避免重复逻辑。
- 组件:
/src/components/ui/CheckInDialog.vue,v-model:show控制显隐;支持外部传入任务列表。 - Props:
-
items_today:今日打卡任务数组(外部传入)。 -
items_history:历史打卡任务数组(外部传入)。 - 数据结构:每项需包含
id、title(name)、task_type(checkin/upload)、is_gray。 - 使用位置:
/src/views/courses/CourseDetailPage.vue/src/views/profile/StudyCoursePage.vue/src/views/study/StudyDetailPage.vue- 清理:上述页面已移除旧弹窗的冗余状态与方法(如
default_list、showTaskList、showTimeoutTaskList、selectedCheckIn等),统一由组件内部处理。
-
打卡列表组件 CheckInList(复用)
- 目的:抽取首页与弹窗内重复的“打卡类型列表 + 提交按钮”UI与交互逻辑,提升复用性与维护性。
- 位置:
/src/components/ui/CheckInList.vue,样式补充:/src/components/ui/CheckInList.less(使用 Less 层级嵌套)。 - Props:
-
items:打卡任务数组,元素包含id、name、task_type(checkin/upload)、is_gray。 -
dense:是否使用更紧凑的栅格与间距。 -
scroll:是否启用滚动容器(最大高度 13rem)。 - Emits:
-
submit-success:提交成功后触发,由父组件决定后续行为(轻提示、关闭弹窗等)。 - 行为:
- 点击置灰的
checkin项时提示“您已经完成了今天的打卡”。 - 点击
upload类型时跳转到/checkin/index?id=xxx上传页面。 - 选择
checkin类型后显示提交按钮,点击后调用接口提交;成功后抛出submit-success并重置选中项。 - 使用位置:
- 首页:
/src/views/HomePage.vue(替换原重复 UI,监听submit-success显示“打卡成功”)。 - 弹窗:
/src/components/ui/CheckInDialog.vue(以active_list作为数据源,监听submit-success转发为check-in-success并延时关闭)。
-
打卡详情页(统一提交/编辑页)CheckinDetailPage
- 路径:
/checkin/detail,页面文件:/src/views/checkin/CheckinDetailPage.vue - 目标:统一“提交作业/打卡”的入口,覆盖文本/图片/视频/音频上传,以及计数打卡;同时承接“编辑已提交动态”的入口。
- 路由参数约定(query):
- 新增提交:
task_id(大作业ID)、subtask_id(可选,默认选中小作业)、date(YYYY-MM-DD,用于补卡与月份计算)、is_patch(是否补卡:'1'/'0')、task_type(当前作业类型,常见为upload/count)。 - 编辑提交:额外携带
status=edit、post_id(打卡动态ID)、type(原file_type,实际以详情接口回填为准)。 - 初始化数据:
- 大作业详情:调用
getTaskDetailAPI({ i: task_id, month })获取作业描述、默认附件类型、完成状态等。 - 小作业列表:调用
getSubtaskListAPI({ task_id, date })获取小作业列表并生成 Picker 选项(包含note/field_list/person_type/attachment_type/is_makeup等)。 - 编辑回填:通过
useCheckin().initEditData调getUploadTaskInfoAPI({ i: post_id })回填note/file_type/files/subtask_id/gratitude_count等。 - 附件类型与上传限制:
-
normalizeAttachmentTypeConfig解析后端attachment_type配置,生成attachmentTypeOptions,并通过setMaxFileSizeMbMap写入不同类型的最大上传大小。 - 计数打卡(
task_type=count)下过滤掉text类型;非计数模式下当activeType不在选项中时自动重置为第一个可用类型。 - 提交流程(核心封装在
useCheckin): - 上传:
beforeRead/afterRead先校验数量/大小/类型,再走七牛上传(hash 去重 → token → upload → saveFile),成功回填meta_id/url/status。 - 提交:
onSubmit新增走addUploadTaskAPI,编辑走editUploadTaskInfoAPI;成功后写sessionStorage.checkin_refresh_flag/checkin_refresh_id供列表页局部刷新,并router.back()返回。 - 页面层校验:文本打卡至少 10 字;计数打卡必须选择对象且次数 > 0;非文本类型必须有文件。
- 计数打卡交互:
- 通过
reuseGratitudeFormAPI({ subtask_id })获取可选对象列表targetList与最近使用last_used_list,进入页面会自动勾选最近使用项。 - 选中对象首次会弹窗确认(标记
has_confirmed),支持新增/编辑;删除对象调用gratitudeDeleteAPI(当前页面内未引入该 API,需补齐后才可生效)。 - 预览能力:
- 上传组件点击预览时,根据扩展名识别:音频使用
AudioPlayer底部弹窗;视频使用VideoPlayer居中弹窗(封面 → 点击播放 → 关闭时重置进度);图片使用van-image-preview。
- 路径:
/src/components 目录结构
项目包含 12个主要组件目录,涵盖了从基础 UI 到业务功能的完整组件体系:
| 目录 | 组件示例 | 说明 |
|---|---|---|
ui/ |
VideoPlayer, AudioPlayer, CheckInDialog, SharePoster, SearchBar
|
通用 UI 组件库 |
checkin/ |
CheckInDialog, CheckInList, CheckInResult
|
打卡业务组件 |
media/ |
AudioPlayer, VideoPlayer, MusicPlayer
|
音视频播放组件 |
activity/ |
ActivityCard, ActivityTicket, ActivityStatusBadge
|
活动相关组件 |
common/ |
ConfirmDialog, GradientHeader, MenuItem
|
通用基础组件 |
effects/ |
FrostedGlass, LoadingSpinner
|
视觉特效组件 |
courses/ |
CourseCard, LiveStreamCard
|
课程展示组件 |
payment/ |
WechatPayment |
支付组件 |
studyDetail/ |
StudyMaterialsPopup |
学习资料弹窗 |
layout/ |
AppLayout, BottomNav
|
布局与导航 |
share/ |
SharePoster |
分享海报 |
files/ |
FilePreview |
文件预览 |
feedback/ |
FeedbackForm |
反馈表单 |
teacher/ |
教师端专用组件 | 教师业务组件 |
文档索引
📚 完整文档导航
项目文档中心: docs/README.md - 13+ 篇文档的分类索引
🏗️ 架构设计
💻 开发配置
- ESLint + Prettier - 代码规范与自动格式化配置
- Husky + lint-staged - Git Hooks 自动化检查配置
- 开发工作流 - 团队开发流程与规范
🧪 测试文档
- Playwright 指南 - E2E 测试框架完整指南
- E2E 认证指南 - 测试登录认证流程
- E2E 代理配置 - Vite 反向代理配置说明
- E2E 测试服务器 - 测试环境配置与服务器信息
🛠️ 工具指南
- Claude Skills - Claude Code 技能完全指南
📝 变更记录
- 更新日志 - 功能更新历史(按时间倒序)
📋 任务管理
📖 核心文档
- CLAUDE.md (本文档) - 项目总体架构与开发指南
- VUE_CODE_STYLE_GUIDE.md - Vue 代码风格规范与最佳实践
- ISSUES_TO_FIX.md - 已知问题与改进建议
- DOCUMENTATION_STANDARDS.md - 文档编写规范
⚙️ 配置文件
- package.json - 依赖与脚本配置
- vite.config.js - Vite 构建工具配置
- tailwind.config.js - TailwindCSS 样式系统配置
- .env - 环境变量配置