hookehuyr

docs(CLAUDE): 添加 Hash 路由模式重要警告

在项目文档中突出显示路由配置规则:
- 所有 URL 必须使用 /#/ 前缀
- 适用于代码生成、测试编写、文档编写
- 标记为 CRITICAL 级别规则

确保在计划阶段和生成阶段都能看到此重要规则。
Showing 1 changed file with 97 additions and 18 deletions
......@@ -35,11 +35,13 @@
### 环境准备
1. **Node.js 版本**: 使用 `.nvm` 管理 Node.js 18.19.1
```bash
nvm use 18.19.1
```
2. **安装依赖**:
```bash
pnpm install
```
......@@ -52,21 +54,25 @@
### 新手指南
**添加新页面**:
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. 使用 `defineProps``defineEmits` 定义接口
3. 通过 emit 事件向父组件传递操作,避免直接调用 API
**提取逻辑到 Composable**:
1. 在 `/src/composables/` 创建 `useXxx.js`
2. 返回响应式 refs 和函数
3. 内部处理副作用(API 调用、事件监听等)
......@@ -74,6 +80,7 @@
## 常用开发命令
### 核心开发
```bash
pnpm dev # 启动开发服务器 (使用 Node.js 18.19.1)
pnpm build # 生产环境构建
......@@ -82,6 +89,7 @@ pnpm test # 使用 Vitest 运行测试
```
### 部署
```bash
pnpm dev_upload # 部署到开发服务器
pnpm behalo_upload # 部署到 behalo 环境
......@@ -89,6 +97,7 @@ pnpm oa_upload # 部署到 OA 服务器
```
### 开发流程
- 每个部署命令都会:构建、归档(tar.gz)、通过 scp 上传、在服务器上解压、清理归档文件。
## 目录结构
......@@ -137,6 +146,7 @@ src/
## 路径别名
在 `vite.config.js` 和 `jsconfig.json` 中配置:
- `@/` → `src/`
- `@components/` → `src/components/`
- `@composables/` → `src/composables/`
......@@ -155,6 +165,7 @@ src/
**位置**: `/src/api/`
所有接口遵循统一的响应结构:
```javascript
{
code: 1, // 1 = 成功,其他 = 失败
......@@ -166,6 +177,7 @@ src/
**重要**: 检查成功时使用 `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`)
......@@ -173,17 +185,20 @@ src/
### 组件分层
**视图页面** (`/src/views/`): 页面级组件,负责编排:
- 通过 API 获取数据
- 通过 refs/composables 管理状态
- 路由和导航
- 集成更小的组件
**UI 组件** (`/src/components/ui/`): 可复用、无状态或自包含:
- VideoPlayer, AudioPlayer, CheckInDialog, SharePoster, SearchBar 等
- 应尽可能避免直接调用 API
- 通过 emit 事件向父组件传递操作
**组合式函数** (`/src/composables/`): 可复用逻辑:
- `useAuth`: 认证状态管理与登录流程
- `useCheckin`: 打卡流程和提交(七牛上传、校验、提交)
- `useCheckinDraft`: 打卡草稿自动缓存(防止数据丢失)
......@@ -193,7 +208,27 @@ src/
### 路由与认证
> **⚠️ CRITICAL: 本项目使用 Hash 模式路由**
>
> 在生成任何路由 URL 前,必须确认使用 `/#/` 前缀:
>
> - ✅ 正确:`/#/login`, `/#/profile`, `/#/courses/123`
> - ❌ 错误:`/login`, `/profile`, `/courses/123`
>
> 配置来源:`src/router/index.js:15`
>
> ```javascript
> history: createWebHashHistory(import.meta.env.VITE_BASE || '/')
> ```
>
> 此规则适用于:
>
> - 代码生成:`router.push()`, `<router-link to="">`
> - 测试编写:Playwright `page.goto()`, Vitest 路由测试
> - 文档编写:所有示例 URL
**路由** 使用懒加载进行代码分割:
```javascript
{
path: '/courses/:id',
......@@ -202,6 +237,7 @@ src/
```
**认证守卫** (`/src/router/guards.js`):
- `checkAuth(route)` - 返回 `true` 或重定向对象
- 不再自动触发微信认证;仅通过登录页手动触发
- 路由可标记 `meta: { requireAuth: false }` 以公开访问
......@@ -209,45 +245,53 @@ src/
### 样式策略
**TailwindCSS** 是主要样式方案:
- 用于布局、间距、排版、颜色
- 自定义颜色定义在 `tailwind.config.js` 中 (primary: #4caf50)
**Less** 用于组件特定样式:
- 组件边界内的嵌套选择器
- 使用 `<style lang="less" scoped>` 限制作用域到组件
- 使用 `:deep()` 修改 Vant/VideoJS 内部样式
**自动导入**:
- Vant 组件自动导入(无需手动导入)
- Vue 组合式函数自动导入 (ref, computed, onMounted 等)
### 状态管理模式
**用户认证状态**:
- 运行时: `contexts/auth.js` → `currentUser` ref
- 持久化: `localStorage.currentUser` 和 `localStorage.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`)
- 免费课程: 跳过环境验证
......@@ -255,17 +299,21 @@ src/
### 文件预览系统
**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` 组件添加错误处理、自动重试和播放速率控制
......@@ -274,6 +322,7 @@ src/
### 视频播放器问题
**VideoPlayer 使用 `v-show` vs `v-if`**:
- Video.js 在 `v-show` (display: none) 下无法正常工作
- 使用 `v-if` 确保视频元素在初始化前完全挂载
- 参考 `StudyDetailPage.vue` 中的正确模式:设置 `isPlaying = true`,然后 `setTimeout` 再调用 `play()`
......@@ -281,6 +330,7 @@ src/
### 401 响应处理
`src/utils/axios.js` 中的 axios 响应拦截器:
- 仅当 `checkAuth(to)` 返回重定向时才跳转到登录页
- 公开页面(如课程详情)即使在 401 时也保持当前页
- 页面组件应为自己的用户操作处理 401
......@@ -288,6 +338,7 @@ src/
### API 响应安全性
始终检查 `res.code === 1`(而不仅仅是 `res.code`):
```javascript
// 正确
const { code, data } = await getCourseDetailAPI({ i: courseId })
......@@ -300,6 +351,7 @@ if (code) { ... }
### 组件自动导入
Vant 组件通过 `unplugin-vue-components` 自动导入:
- **不要导入** `import { Button } from 'vant'`
- 直接使用 `<van-button>`
- 参见 `src/components.d.ts` 查看所有可用组件
......@@ -307,22 +359,26 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
### 打卡系统
**核心 composable**: `useCheckin.js` 封装打卡流程:
- 文件上传:七牛云 + Hash 秒传(避免重复上传)
- 支持类型:文本、图片、视频、音频、计数打卡
- 数据校验:文件大小、数量、类型验证
- 提交流程:新增/编辑统一处理
**草稿缓存**: `useCheckinDraft.js` 防止数据丢失:
- 自动保存:输入内容实时缓存到 localStorage
- 智能恢复:页面加载时自动填充上次未提交的内容
- 清理机制:提交成功或手动清除时移除草稿
**统一组件**: `CheckInDialog.vue` 处理所有打卡流程:
- 接收 `items_today` 和 `items_history` props
- 每项包含 `id`, `name`, `task_type` ('checkin'/'upload'), `is_gray`
- 完成时触发 `check-in-success` 事件
**可复用列表组件**: `CheckInList.vue`
- 用于首页和 CheckInDialog
- 对话框使用紧凑模式,页面使用正常模式
- `submit-success` 事件向上冒泡
......@@ -330,6 +386,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
### 环境配置
使用 `.env.development`, `.env.production` 等:
- `VITE_PORT` - 开发服务器端口
- `VITE_BASE` - 基础路径
- `VITE_PROXY_PREFIX` - API 代理前缀
......@@ -337,6 +394,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
- `VITE_OUTDIR` - 构建输出目录
**环境与部署**:
- **开发环境**: `http://oa-dev.onwall.cn/`
- **生产环境**: `http://oa.onwall.cn`
- **部署流程**: 构建 → 归档(tar.gz) → SCP 上传 → 服务器解压 → 清理归档
......@@ -345,6 +403,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
### Speckit 框架命令
位于 `.cursor/commands/`:
- `/speckit.specify` - 从需求生成规范
- `/speckit.plan` - 创建实现计划
- `/speckit.tasks` - 生成任务分解
......@@ -357,6 +416,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
### 代码风格说明 (来自 VUE_CODE_STYLE_GUIDE.md)
**当前实践**:
- 新组件使用 `<script setup>` 和 Composition API
- API 函数应始终返回 `{ code, data, msg }` - 永不返回 `false`
- 尽可能将逻辑提取到 composables (`/src/composables/useXxx.js`)
......@@ -364,6 +424,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
- 除非必要避免 JSX;大多数页面使用 Vue 模板
**已知不一致性** (需注意):
- 文件中混合使用分号/无分号
- 混合使用 2 空格/4 空格缩进
- 混合使用 function/箭头函数
......@@ -372,6 +433,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
- 部分页面组件过大,应考虑拆分
**改进建议**:
- 统一使用 ESLint + Prettier 进行代码格式化
- 制定统一的 API 返回值规范
- 大型页面组件应拆分为多个子组件
......@@ -380,16 +442,19 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
## 特殊功能
### 分享海报生成
- 基于 Canvas 的海报生成,带二维码 (`qrcode` 包)
- 自动优化 `cdn.ipadbiz.cn` URL 的图片
- 使用 `crossorigin="anonymous"` 处理跨域 Canvas 污染
- 组件: `SharePoster.vue` (可复用)
### 动态 Open Graph 元标签
- 课程详情页动态添加 `og:title`, `og:description`, `og:image`, `og:url`
- 页面卸载时移除标签以避免冲突
### 标签指示器动画
- 基于 ResizeObserver 的指示器定位
- 处理异步加载第三列而不错位
- 参考 `StudyCoursePage.vue` 的实现
......@@ -491,6 +556,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
### Q: 为什么 Video.js 在切换显示/隐藏时无法正常工作?
**A**: Video.js 在 `v-show` (display: none) 状态下初始化会失败。
- **解决**: 使用 `v-if` 而非 `v-show`
- **正确模式**: 设置 `isPlaying = true`,然后 `setTimeout` 再调用 `play()`
- **参考**: `StudyDetailPage.vue` 中的实现
......@@ -498,6 +564,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
### Q: 为什么我的 401 响应导致页面跳转到登录页?
**A**: 这是预期的行为,但仅对需要认证的页面生效。
- 公开页面(如课程详情)不会跳转
- 受限页面会清理登录信息并重定向
- 如需自定义处理,在组件内监听 API 错误
......@@ -505,18 +572,24 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
### Q: 如何正确检查 API 响应是否成功?
**A**: 始终使用 `if (res.code === 1)` 而非 `if (res.code)`。
```javascript
// ✓ 正确
const { code, data } = await getCourseDetailAPI({ i: courseId })
if (code === 1) { /* 成功 */ }
if (code === 1) {
/* 成功 */
}
// ✗ 错误 - 会将 401/403 当作成功
if (code) { /* ... */ }
if (code) {
/* ... */
}
```
### Q: 为什么 Vant 组件导入后报错或无法使用?
**A**: Vant 组件已配置自动导入,无需手动导入。
```javascript
// ✗ 错误
import { Button } from 'vant'
......@@ -528,6 +601,7 @@ import { Button } from 'vant'
### Q: 如何在微信环境下正确处理支付和授权?
**A**: 使用 `wxInfo()` 工具函数检测环境:
```javascript
import { wxInfo } from '@/utils/tools'
......@@ -542,6 +616,7 @@ if (wxInfo().isWeiXin) {
### Q: 打卡时如何避免数据丢失?
**A**: 使用 `useCheckinDraft` 自动缓存草稿:
```javascript
import { useCheckinDraft } from '@/composables/useCheckinDraft'
......@@ -560,22 +635,26 @@ onSubmitSuccess(() => clearDraft())
### 最近重要更新
**打卡系统增强** (2025):
- 打卡详情页重构 (`/checkin/detail`):统一文本、媒体上传和计数打卡入口
- 新增草稿缓存功能 (`useCheckinDraft`):防止数据丢失,自动保存和恢复
- 优化附件预览和编辑回填逻辑
- 实现基于 `useCheckin` 的通用提交流程
**教师端完善** (2025):
- 作业管理页面 (`/teacher/tasks`)
- 作业主页 (`/teacher/tasks/:id`):统计与日历视图
- 学员作业记录页面 (`/teacher/student-record`)
**召回系统** (2025):
- 身份证查询历史功能
- 时光机旅程 (Timeline) 展示用户里程碑
- 专属海报生成与分享
**基础体验优化** (2025):
- 登录逻辑调整:仅在点击微信图标时触发授权
- 搜索栏优化:支持 iOS 软键盘搜索键
- 课程详情页:动态 Open Graph 标签,优化分享体验
......@@ -712,22 +791,22 @@ onSubmitSuccess(() => clearDraft())
项目包含 **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/` | 教师端专用组件 | 教师业务组件 |
| 目录 | 组件示例 | 说明 |
| -------------- | ------------------------------------------------------------------------- | -------------- |
| `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/` | 教师端专用组件 | 教师业务组件 |
---
......