CLAUDE.md 35.9 KB

CLAUDE.md

此文件为 Claude Code (claude.ai/code) 在此代码库中工作时提供指导。

目录

项目概述

美乐爱觉 (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 封装)

快速开始

环境准备

  1. Node.js 版本: 使用 .nvm 管理 Node.js 18.19.1
   nvm use 18.19.1
  1. 安装依赖:
   pnpm install
  1. 启动开发服务器: bash pnpm dev

新手指南

添加新页面:

  1. /src/views/ 相应模块下创建页面组件
  2. /src/router/ 对应路由文件中注册路由
  3. 如需认证,在路由 meta 中添加 requiresAuth: true

调用 API:

  1. /src/api/ 对应模块中定义 API 函数
  2. 遵循统一返回结构:{ code: 1, data: any, msg: string }
  3. 使用 if (res.code === 1) 检查成功(而非 if (res.code)

创建可复用组件:

  1. 放置在 /src/components/ui/(通用)或相应业务目录
  2. 使用 definePropsdefineEmits 定义接口
  3. 通过 emit 事件向父组件传递操作,避免直接调用 API

提取逻辑到 Composable:

  1. /src/composables/ 创建 useXxx.js
  2. 返回响应式 refs 和函数
  3. 内部处理副作用(API 调用、事件监听等)

常用开发命令

核心开发

pnpm dev          # 启动开发服务器 (使用 Node.js 18.19.1)
pnpm build        # 生产环境构建
pnpm preview      # 本地预览生产构建
pnpm test         # 使用 Vitest 运行测试

部署

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.jsjsconfig.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-IdUser-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:15

history: 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.jscurrentUser ref
  • 持久化: localStorage.currentUserlocalStorage.user_info
  • Axios 请求头: 从 localStorage 派生(在拦截器中)
  • 辅助函数: getUserInfoFromStorage(), removeUserInfoFromStorage()

购物车/订单状态:

  • 运行时: contexts/cart.js
  • 通过 provide/inject 在页面间保持

微信集成

微信 JS SDK 初始化在 App.vue 中:

  • 仅在浏览器环境中
  • 通过 wxInfo() 工具检测环境
  • wxInfo().isWeiXin - 在微信浏览器中运行
  • wxInfo().isPc - 在 PC 上运行

微信认证流程:

  1. 用户在登录页点击微信图标
  2. 重定向到微信 OAuth
  3. 回调 → getUserIsLoginAPI() → 写入用户信息到存储
  4. 重定向到原页面或首页

微信支付:

  • 集成在结账流程中
  • 生产环境: 需要微信浏览器环境 (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
  • 智能恢复:页面加载时自动填充上次未提交的内容
  • 清理机制:提交成功或手动清除时移除草稿

统一组件: CheckInDialog.vue 处理所有打卡流程:

  • 接收 items_todayitems_history props
  • 每项包含 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 返回值规范
  • 大型页面组件应拆分为多个子组件
  • 增加单元测试覆盖(当前测试文件较少)

特殊功能

分享海报生成

  • 基于 Canvas 的海报生成,带二维码 (qrcode 包)
  • 自动优化 cdn.ipadbiz.cn URL 的图片
  • 使用 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)

  • 目标:面向老用户的营销与促活流程。
  • 流程
    1. 登录/身份验证(支持身份证查询历史)。
    2. 引导页与信息补全。
    3. 时光机旅程 (Timeline):展示用户在平台的历史里程碑。
    4. 活动历史回顾与积分汇总。
    5. 专属海报:生成个性化分享海报。
    6. 导流:引导至 AI 星球或最新课程。

6. 积分与订单

  • 订单:查看订单状态(待支付/已支付/退款),支持取消订单。
  • 积分:查看积分余额与变动明细,积分可用于抵扣或兑换(视具体业务规则)。

核心技术栈与实现

状态管理

  • Context API:项目主要使用 Vue 3 provide/inject 模式进行全局状态管理。
    • auth.js:用户认证、登录态、Token 管理。
    • cart.js:购物车逻辑、商品增删改、结算流程。
  • 持久化:关键状态同步至 localStorage,页面刷新不丢失。

文件处理

  • 七牛云上传
    • 前端计算文件 Hash (browser-md5-file / qiniuFileHash)。
    • 申请上传 Token(支持 Hash 秒传检测)。
    • 直传七牛云对象存储。
    • 回填业务服务器保存元数据。
  • 文档预览
    • PDF: @sunsetglow/vue-pdf-viewer
    • Office: @vue-office/docx, @vue-office/excel, @vue-office/pptx
    • Video: @videojs-player/vue (Video.js 7.x)

路由与权限

  • 路由守卫 (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)

最近重要更新

打卡系统增强 (2025):

  • 打卡详情页重构 (/checkin/detail):统一文本、媒体上传和计数打卡入口
  • 新增草稿缓存功能 (useCheckinDraft):防止数据丢失,自动保存和恢复
  • 优化附件预览和编辑回填逻辑
  • 实现基于 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=817017group_id=816653(后续可替换为动态参数)。
  • 学习详情页标签指示条修复:/src/views/profile/StudyCoursePage.vue
    • 现象:首次进入且存在“打卡互动”时,底部绿色指示条定位错误。
    • 修复:新增标签容器 refResizeObserver,按栏目数量对容器进行等分,指示条宽度与位移按分段和索引计算,异步加载第三个栏目时不再错位。
  • 登录逻辑调整:仅在登录页微信图标点击时触发授权

    • 变更文件:/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_dialogclose_consult_dialogconsult_contactscall_phone(phone))。
  • 购买流程环境校验

    • 行为:仅对非免费课程在详情页点击“购买”时进行校验;生产环境下必须为微信内置浏览器(wxInfo().isWeiXin)。
    • 免费课程:跳过微信环境校验,允许直接进入结算流程。
    • 非微信环境(付费课):提示“请在微信内打开进行购买”,不进入结算。
    • 位置:/src/views/courses/CourseDetailPage.vuehandlePurchase 中,使用 wxInfo 进行环境判断。
  • 微信授权自动触发(微信环境内)

    • 行为:在微信内置浏览器环境下点击“购买”时,若检测到未完成微信授权(openid_has=false),系统将自动发起一次微信授权流程并中止本次购买;授权完成后再次点击可进入结算。
    • 开发环境:不触发微信授权流程(保留现有调试行为)。
    • 位置:/src/views/courses/CourseDetailPage.vuehandlePurchase 中,调用 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.vuehandleSearch
  • 分享海报弹窗(通用组件)

    • 入口:课程详情页底部操作栏“分享”按钮。
    • 组件:/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.vuev-model:show 控制显隐;支持外部传入任务列表。
    • Props:
    • items_today:今日打卡任务数组(外部传入)。
    • items_history:历史打卡任务数组(外部传入)。
    • 数据结构:每项需包含 idtitle(name)task_typecheckin/upload)、is_gray
    • 使用位置:
    • /src/views/courses/CourseDetailPage.vue
    • /src/views/profile/StudyCoursePage.vue
    • /src/views/study/StudyDetailPage.vue
    • 清理:上述页面已移除旧弹窗的冗余状态与方法(如 default_listshowTaskListshowTimeoutTaskListselectedCheckIn 等),统一由组件内部处理。
  • 打卡列表组件 CheckInList(复用)

    • 目的:抽取首页与弹窗内重复的“打卡类型列表 + 提交按钮”UI与交互逻辑,提升复用性与维护性。
    • 位置:/src/components/ui/CheckInList.vue,样式补充:/src/components/ui/CheckInList.less(使用 Less 层级嵌套)。
    • Props:
    • items:打卡任务数组,元素包含 idnametask_typecheckin/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=editpost_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().initEditDatagetUploadTaskInfoAPI({ 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/ 教师端专用组件 教师业务组件

文档索引

项目包含以下文档,帮助理解不同方面:

  • CLAUDE.md (本文档) - 项目总体架构与开发指南
  • VUE_CODE_STYLE_GUIDE.md - Vue 代码风格规范与最佳实践
  • CHANGELOG.md - 变更日志记录
  • package.json - 依赖与脚本配置
  • vite.config.js - 构建工具配置
  • tailwind.config.js - 样式系统配置