hookehuyr

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

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

确保在计划阶段和生成阶段都能看到此重要规则。
Showing 1 changed file with 82 additions and 3 deletions
...@@ -35,11 +35,13 @@ ...@@ -35,11 +35,13 @@
35 ### 环境准备 35 ### 环境准备
36 36
37 1. **Node.js 版本**: 使用 `.nvm` 管理 Node.js 18.19.1 37 1. **Node.js 版本**: 使用 `.nvm` 管理 Node.js 18.19.1
38 +
38 ```bash 39 ```bash
39 nvm use 18.19.1 40 nvm use 18.19.1
40 ``` 41 ```
41 42
42 2. **安装依赖**: 43 2. **安装依赖**:
44 +
43 ```bash 45 ```bash
44 pnpm install 46 pnpm install
45 ``` 47 ```
...@@ -52,21 +54,25 @@ ...@@ -52,21 +54,25 @@
52 ### 新手指南 54 ### 新手指南
53 55
54 **添加新页面**: 56 **添加新页面**:
57 +
55 1. 在 `/src/views/` 相应模块下创建页面组件 58 1. 在 `/src/views/` 相应模块下创建页面组件
56 2. 在 `/src/router/` 对应路由文件中注册路由 59 2. 在 `/src/router/` 对应路由文件中注册路由
57 3. 如需认证,在路由 `meta` 中添加 `requiresAuth: true` 60 3. 如需认证,在路由 `meta` 中添加 `requiresAuth: true`
58 61
59 **调用 API**: 62 **调用 API**:
63 +
60 1. 在 `/src/api/` 对应模块中定义 API 函数 64 1. 在 `/src/api/` 对应模块中定义 API 函数
61 2. 遵循统一返回结构:`{ code: 1, data: any, msg: string }` 65 2. 遵循统一返回结构:`{ code: 1, data: any, msg: string }`
62 3. 使用 `if (res.code === 1)` 检查成功(而非 `if (res.code)` 66 3. 使用 `if (res.code === 1)` 检查成功(而非 `if (res.code)`
63 67
64 **创建可复用组件**: 68 **创建可复用组件**:
69 +
65 1. 放置在 `/src/components/ui/`(通用)或相应业务目录 70 1. 放置在 `/src/components/ui/`(通用)或相应业务目录
66 2. 使用 `defineProps``defineEmits` 定义接口 71 2. 使用 `defineProps``defineEmits` 定义接口
67 3. 通过 emit 事件向父组件传递操作,避免直接调用 API 72 3. 通过 emit 事件向父组件传递操作,避免直接调用 API
68 73
69 **提取逻辑到 Composable**: 74 **提取逻辑到 Composable**:
75 +
70 1. 在 `/src/composables/` 创建 `useXxx.js` 76 1. 在 `/src/composables/` 创建 `useXxx.js`
71 2. 返回响应式 refs 和函数 77 2. 返回响应式 refs 和函数
72 3. 内部处理副作用(API 调用、事件监听等) 78 3. 内部处理副作用(API 调用、事件监听等)
...@@ -74,6 +80,7 @@ ...@@ -74,6 +80,7 @@
74 ## 常用开发命令 80 ## 常用开发命令
75 81
76 ### 核心开发 82 ### 核心开发
83 +
77 ```bash 84 ```bash
78 pnpm dev # 启动开发服务器 (使用 Node.js 18.19.1) 85 pnpm dev # 启动开发服务器 (使用 Node.js 18.19.1)
79 pnpm build # 生产环境构建 86 pnpm build # 生产环境构建
...@@ -82,6 +89,7 @@ pnpm test # 使用 Vitest 运行测试 ...@@ -82,6 +89,7 @@ pnpm test # 使用 Vitest 运行测试
82 ``` 89 ```
83 90
84 ### 部署 91 ### 部署
92 +
85 ```bash 93 ```bash
86 pnpm dev_upload # 部署到开发服务器 94 pnpm dev_upload # 部署到开发服务器
87 pnpm behalo_upload # 部署到 behalo 环境 95 pnpm behalo_upload # 部署到 behalo 环境
...@@ -89,6 +97,7 @@ pnpm oa_upload # 部署到 OA 服务器 ...@@ -89,6 +97,7 @@ pnpm oa_upload # 部署到 OA 服务器
89 ``` 97 ```
90 98
91 ### 开发流程 99 ### 开发流程
100 +
92 - 每个部署命令都会:构建、归档(tar.gz)、通过 scp 上传、在服务器上解压、清理归档文件。 101 - 每个部署命令都会:构建、归档(tar.gz)、通过 scp 上传、在服务器上解压、清理归档文件。
93 102
94 ## 目录结构 103 ## 目录结构
...@@ -137,6 +146,7 @@ src/ ...@@ -137,6 +146,7 @@ src/
137 ## 路径别名 146 ## 路径别名
138 147
139 在 `vite.config.js` 和 `jsconfig.json` 中配置: 148 在 `vite.config.js` 和 `jsconfig.json` 中配置:
149 +
140 - `@/` → `src/` 150 - `@/` → `src/`
141 - `@components/` → `src/components/` 151 - `@components/` → `src/components/`
142 - `@composables/` → `src/composables/` 152 - `@composables/` → `src/composables/`
...@@ -155,6 +165,7 @@ src/ ...@@ -155,6 +165,7 @@ src/
155 **位置**: `/src/api/` 165 **位置**: `/src/api/`
156 166
157 所有接口遵循统一的响应结构: 167 所有接口遵循统一的响应结构:
168 +
158 ```javascript 169 ```javascript
159 { 170 {
160 code: 1, // 1 = 成功,其他 = 失败 171 code: 1, // 1 = 成功,其他 = 失败
...@@ -166,6 +177,7 @@ src/ ...@@ -166,6 +177,7 @@ src/
166 **重要**: 检查成功时使用 `if (res.code === 1)` 而不是 `if (res.code)`,避免将 401/403 误判为成功。 177 **重要**: 检查成功时使用 `if (res.code === 1)` 而不是 `if (res.code)`,避免将 401/403 误判为成功。
167 178
168 **认证头** 通过 axios 拦截器自动添加 (`/src/utils/axios.js`): 179 **认证头** 通过 axios 拦截器自动添加 (`/src/utils/axios.js`):
180 +
169 - 每次请求读取 `localStorage.user_info` 181 - 每次请求读取 `localStorage.user_info`
170 - 设置 `User-Id` 和 `User-Token` 请求头 182 - 设置 `User-Id` 和 `User-Token` 请求头
171 - 401 响应处理:仅当当前路由需要认证时才重定向到登录页 (见 guards.js 中的 `checkAuth`) 183 - 401 响应处理:仅当当前路由需要认证时才重定向到登录页 (见 guards.js 中的 `checkAuth`)
...@@ -173,17 +185,20 @@ src/ ...@@ -173,17 +185,20 @@ src/
173 ### 组件分层 185 ### 组件分层
174 186
175 **视图页面** (`/src/views/`): 页面级组件,负责编排: 187 **视图页面** (`/src/views/`): 页面级组件,负责编排:
188 +
176 - 通过 API 获取数据 189 - 通过 API 获取数据
177 - 通过 refs/composables 管理状态 190 - 通过 refs/composables 管理状态
178 - 路由和导航 191 - 路由和导航
179 - 集成更小的组件 192 - 集成更小的组件
180 193
181 **UI 组件** (`/src/components/ui/`): 可复用、无状态或自包含: 194 **UI 组件** (`/src/components/ui/`): 可复用、无状态或自包含:
195 +
182 - VideoPlayer, AudioPlayer, CheckInDialog, SharePoster, SearchBar 等 196 - VideoPlayer, AudioPlayer, CheckInDialog, SharePoster, SearchBar 等
183 - 应尽可能避免直接调用 API 197 - 应尽可能避免直接调用 API
184 - 通过 emit 事件向父组件传递操作 198 - 通过 emit 事件向父组件传递操作
185 199
186 **组合式函数** (`/src/composables/`): 可复用逻辑: 200 **组合式函数** (`/src/composables/`): 可复用逻辑:
201 +
187 - `useAuth`: 认证状态管理与登录流程 202 - `useAuth`: 认证状态管理与登录流程
188 - `useCheckin`: 打卡流程和提交(七牛上传、校验、提交) 203 - `useCheckin`: 打卡流程和提交(七牛上传、校验、提交)
189 - `useCheckinDraft`: 打卡草稿自动缓存(防止数据丢失) 204 - `useCheckinDraft`: 打卡草稿自动缓存(防止数据丢失)
...@@ -193,7 +208,27 @@ src/ ...@@ -193,7 +208,27 @@ src/
193 208
194 ### 路由与认证 209 ### 路由与认证
195 210
211 +> **⚠️ CRITICAL: 本项目使用 Hash 模式路由**
212 +>
213 +> 在生成任何路由 URL 前,必须确认使用 `/#/` 前缀:
214 +>
215 +> - ✅ 正确:`/#/login`, `/#/profile`, `/#/courses/123`
216 +> - ❌ 错误:`/login`, `/profile`, `/courses/123`
217 +>
218 +> 配置来源:`src/router/index.js:15`
219 +>
220 +> ```javascript
221 +> history: createWebHashHistory(import.meta.env.VITE_BASE || '/')
222 +> ```
223 +>
224 +> 此规则适用于:
225 +>
226 +> - 代码生成:`router.push()`, `<router-link to="">`
227 +> - 测试编写:Playwright `page.goto()`, Vitest 路由测试
228 +> - 文档编写:所有示例 URL
229 +
196 **路由** 使用懒加载进行代码分割: 230 **路由** 使用懒加载进行代码分割:
231 +
197 ```javascript 232 ```javascript
198 { 233 {
199 path: '/courses/:id', 234 path: '/courses/:id',
...@@ -202,6 +237,7 @@ src/ ...@@ -202,6 +237,7 @@ src/
202 ``` 237 ```
203 238
204 **认证守卫** (`/src/router/guards.js`): 239 **认证守卫** (`/src/router/guards.js`):
240 +
205 - `checkAuth(route)` - 返回 `true` 或重定向对象 241 - `checkAuth(route)` - 返回 `true` 或重定向对象
206 - 不再自动触发微信认证;仅通过登录页手动触发 242 - 不再自动触发微信认证;仅通过登录页手动触发
207 - 路由可标记 `meta: { requireAuth: false }` 以公开访问 243 - 路由可标记 `meta: { requireAuth: false }` 以公开访问
...@@ -209,45 +245,53 @@ src/ ...@@ -209,45 +245,53 @@ src/
209 ### 样式策略 245 ### 样式策略
210 246
211 **TailwindCSS** 是主要样式方案: 247 **TailwindCSS** 是主要样式方案:
248 +
212 - 用于布局、间距、排版、颜色 249 - 用于布局、间距、排版、颜色
213 - 自定义颜色定义在 `tailwind.config.js` 中 (primary: #4caf50) 250 - 自定义颜色定义在 `tailwind.config.js` 中 (primary: #4caf50)
214 251
215 **Less** 用于组件特定样式: 252 **Less** 用于组件特定样式:
253 +
216 - 组件边界内的嵌套选择器 254 - 组件边界内的嵌套选择器
217 - 使用 `<style lang="less" scoped>` 限制作用域到组件 255 - 使用 `<style lang="less" scoped>` 限制作用域到组件
218 - 使用 `:deep()` 修改 Vant/VideoJS 内部样式 256 - 使用 `:deep()` 修改 Vant/VideoJS 内部样式
219 257
220 **自动导入**: 258 **自动导入**:
259 +
221 - Vant 组件自动导入(无需手动导入) 260 - Vant 组件自动导入(无需手动导入)
222 - Vue 组合式函数自动导入 (ref, computed, onMounted 等) 261 - Vue 组合式函数自动导入 (ref, computed, onMounted 等)
223 262
224 ### 状态管理模式 263 ### 状态管理模式
225 264
226 **用户认证状态**: 265 **用户认证状态**:
266 +
227 - 运行时: `contexts/auth.js` → `currentUser` ref 267 - 运行时: `contexts/auth.js` → `currentUser` ref
228 - 持久化: `localStorage.currentUser` 和 `localStorage.user_info` 268 - 持久化: `localStorage.currentUser` 和 `localStorage.user_info`
229 - Axios 请求头: 从 localStorage 派生(在拦截器中) 269 - Axios 请求头: 从 localStorage 派生(在拦截器中)
230 - 辅助函数: `getUserInfoFromStorage()`, `removeUserInfoFromStorage()` 270 - 辅助函数: `getUserInfoFromStorage()`, `removeUserInfoFromStorage()`
231 271
232 **购物车/订单状态**: 272 **购物车/订单状态**:
273 +
233 - 运行时: `contexts/cart.js` 274 - 运行时: `contexts/cart.js`
234 - 通过 provide/inject 在页面间保持 275 - 通过 provide/inject 在页面间保持
235 276
236 ### 微信集成 277 ### 微信集成
237 278
238 **微信 JS SDK** 初始化在 `App.vue` 中: 279 **微信 JS SDK** 初始化在 `App.vue` 中:
280 +
239 - 仅在浏览器环境中 281 - 仅在浏览器环境中
240 - 通过 `wxInfo()` 工具检测环境 282 - 通过 `wxInfo()` 工具检测环境
241 - `wxInfo().isWeiXin` - 在微信浏览器中运行 283 - `wxInfo().isWeiXin` - 在微信浏览器中运行
242 - `wxInfo().isPc` - 在 PC 上运行 284 - `wxInfo().isPc` - 在 PC 上运行
243 285
244 **微信认证流程**: 286 **微信认证流程**:
287 +
245 1. 用户在登录页点击微信图标 288 1. 用户在登录页点击微信图标
246 2. 重定向到微信 OAuth 289 2. 重定向到微信 OAuth
247 3. 回调 → `getUserIsLoginAPI()` → 写入用户信息到存储 290 3. 回调 → `getUserIsLoginAPI()` → 写入用户信息到存储
248 4. 重定向到原页面或首页 291 4. 重定向到原页面或首页
249 292
250 **微信支付**: 293 **微信支付**:
294 +
251 - 集成在结账流程中 295 - 集成在结账流程中
252 - 生产环境: 需要微信浏览器环境 (`wxInfo().isWeiXin`) 296 - 生产环境: 需要微信浏览器环境 (`wxInfo().isWeiXin`)
253 - 免费课程: 跳过环境验证 297 - 免费课程: 跳过环境验证
...@@ -255,17 +299,21 @@ src/ ...@@ -255,17 +299,21 @@ src/
255 ### 文件预览系统 299 ### 文件预览系统
256 300
257 **PDF 预览**: 301 **PDF 预览**:
302 +
258 - 组件: `@sunsetglow/vue-pdf-viewer` 303 - 组件: `@sunsetglow/vue-pdf-viewer`
259 - 专用路由: `/pdfPreview` 用于浏览器内预览 304 - 专用路由: `/pdfPreview` 用于浏览器内预览
260 305
261 **Office 文档** (Word, Excel, PPT): 306 **Office 文档** (Word, Excel, PPT):
307 +
262 - `@vue-office/docx`, `@vue-office/excel`, `@vue-office/pptx` 308 - `@vue-office/docx`, `@vue-office/excel`, `@vue-office/pptx`
263 - 弹窗预览组件,带错误处理 309 - 弹窗预览组件,带错误处理
264 310
265 **图片**: 311 **图片**:
312 +
266 - 使用 Vant 的 `van-image-preview` 313 - 使用 Vant 的 `van-image-preview`
267 314
268 **视频播放器**: 315 **视频播放器**:
316 +
269 - 基于 `@videojs-player/vue` (Video.js 7.21.7 的封装) 317 - 基于 `@videojs-player/vue` (Video.js 7.21.7 的封装)
270 - 自定义 `VideoPlayer.vue` 组件添加错误处理、自动重试和播放速率控制 318 - 自定义 `VideoPlayer.vue` 组件添加错误处理、自动重试和播放速率控制
271 319
...@@ -274,6 +322,7 @@ src/ ...@@ -274,6 +322,7 @@ src/
274 ### 视频播放器问题 322 ### 视频播放器问题
275 323
276 **VideoPlayer 使用 `v-show` vs `v-if`**: 324 **VideoPlayer 使用 `v-show` vs `v-if`**:
325 +
277 - Video.js 在 `v-show` (display: none) 下无法正常工作 326 - Video.js 在 `v-show` (display: none) 下无法正常工作
278 - 使用 `v-if` 确保视频元素在初始化前完全挂载 327 - 使用 `v-if` 确保视频元素在初始化前完全挂载
279 - 参考 `StudyDetailPage.vue` 中的正确模式:设置 `isPlaying = true`,然后 `setTimeout` 再调用 `play()` 328 - 参考 `StudyDetailPage.vue` 中的正确模式:设置 `isPlaying = true`,然后 `setTimeout` 再调用 `play()`
...@@ -281,6 +330,7 @@ src/ ...@@ -281,6 +330,7 @@ src/
281 ### 401 响应处理 330 ### 401 响应处理
282 331
283 `src/utils/axios.js` 中的 axios 响应拦截器: 332 `src/utils/axios.js` 中的 axios 响应拦截器:
333 +
284 - 仅当 `checkAuth(to)` 返回重定向时才跳转到登录页 334 - 仅当 `checkAuth(to)` 返回重定向时才跳转到登录页
285 - 公开页面(如课程详情)即使在 401 时也保持当前页 335 - 公开页面(如课程详情)即使在 401 时也保持当前页
286 - 页面组件应为自己的用户操作处理 401 336 - 页面组件应为自己的用户操作处理 401
...@@ -288,6 +338,7 @@ src/ ...@@ -288,6 +338,7 @@ src/
288 ### API 响应安全性 338 ### API 响应安全性
289 339
290 始终检查 `res.code === 1`(而不仅仅是 `res.code`): 340 始终检查 `res.code === 1`(而不仅仅是 `res.code`):
341 +
291 ```javascript 342 ```javascript
292 // 正确 343 // 正确
293 const { code, data } = await getCourseDetailAPI({ i: courseId }) 344 const { code, data } = await getCourseDetailAPI({ i: courseId })
...@@ -300,6 +351,7 @@ if (code) { ... } ...@@ -300,6 +351,7 @@ if (code) { ... }
300 ### 组件自动导入 351 ### 组件自动导入
301 352
302 Vant 组件通过 `unplugin-vue-components` 自动导入: 353 Vant 组件通过 `unplugin-vue-components` 自动导入:
354 +
303 - **不要导入** `import { Button } from 'vant'` 355 - **不要导入** `import { Button } from 'vant'`
304 - 直接使用 `<van-button>` 356 - 直接使用 `<van-button>`
305 - 参见 `src/components.d.ts` 查看所有可用组件 357 - 参见 `src/components.d.ts` 查看所有可用组件
...@@ -307,22 +359,26 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -307,22 +359,26 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
307 ### 打卡系统 359 ### 打卡系统
308 360
309 **核心 composable**: `useCheckin.js` 封装打卡流程: 361 **核心 composable**: `useCheckin.js` 封装打卡流程:
362 +
310 - 文件上传:七牛云 + Hash 秒传(避免重复上传) 363 - 文件上传:七牛云 + Hash 秒传(避免重复上传)
311 - 支持类型:文本、图片、视频、音频、计数打卡 364 - 支持类型:文本、图片、视频、音频、计数打卡
312 - 数据校验:文件大小、数量、类型验证 365 - 数据校验:文件大小、数量、类型验证
313 - 提交流程:新增/编辑统一处理 366 - 提交流程:新增/编辑统一处理
314 367
315 **草稿缓存**: `useCheckinDraft.js` 防止数据丢失: 368 **草稿缓存**: `useCheckinDraft.js` 防止数据丢失:
369 +
316 - 自动保存:输入内容实时缓存到 localStorage 370 - 自动保存:输入内容实时缓存到 localStorage
317 - 智能恢复:页面加载时自动填充上次未提交的内容 371 - 智能恢复:页面加载时自动填充上次未提交的内容
318 - 清理机制:提交成功或手动清除时移除草稿 372 - 清理机制:提交成功或手动清除时移除草稿
319 373
320 **统一组件**: `CheckInDialog.vue` 处理所有打卡流程: 374 **统一组件**: `CheckInDialog.vue` 处理所有打卡流程:
375 +
321 - 接收 `items_today` 和 `items_history` props 376 - 接收 `items_today` 和 `items_history` props
322 - 每项包含 `id`, `name`, `task_type` ('checkin'/'upload'), `is_gray` 377 - 每项包含 `id`, `name`, `task_type` ('checkin'/'upload'), `is_gray`
323 - 完成时触发 `check-in-success` 事件 378 - 完成时触发 `check-in-success` 事件
324 379
325 **可复用列表组件**: `CheckInList.vue` 380 **可复用列表组件**: `CheckInList.vue`
381 +
326 - 用于首页和 CheckInDialog 382 - 用于首页和 CheckInDialog
327 - 对话框使用紧凑模式,页面使用正常模式 383 - 对话框使用紧凑模式,页面使用正常模式
328 - `submit-success` 事件向上冒泡 384 - `submit-success` 事件向上冒泡
...@@ -330,6 +386,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -330,6 +386,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
330 ### 环境配置 386 ### 环境配置
331 387
332 使用 `.env.development`, `.env.production` 等: 388 使用 `.env.development`, `.env.production` 等:
389 +
333 - `VITE_PORT` - 开发服务器端口 390 - `VITE_PORT` - 开发服务器端口
334 - `VITE_BASE` - 基础路径 391 - `VITE_BASE` - 基础路径
335 - `VITE_PROXY_PREFIX` - API 代理前缀 392 - `VITE_PROXY_PREFIX` - API 代理前缀
...@@ -337,6 +394,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -337,6 +394,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
337 - `VITE_OUTDIR` - 构建输出目录 394 - `VITE_OUTDIR` - 构建输出目录
338 395
339 **环境与部署**: 396 **环境与部署**:
397 +
340 - **开发环境**: `http://oa-dev.onwall.cn/` 398 - **开发环境**: `http://oa-dev.onwall.cn/`
341 - **生产环境**: `http://oa.onwall.cn` 399 - **生产环境**: `http://oa.onwall.cn`
342 - **部署流程**: 构建 → 归档(tar.gz) → SCP 上传 → 服务器解压 → 清理归档 400 - **部署流程**: 构建 → 归档(tar.gz) → SCP 上传 → 服务器解压 → 清理归档
...@@ -345,6 +403,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -345,6 +403,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
345 ### Speckit 框架命令 403 ### Speckit 框架命令
346 404
347 位于 `.cursor/commands/`: 405 位于 `.cursor/commands/`:
406 +
348 - `/speckit.specify` - 从需求生成规范 407 - `/speckit.specify` - 从需求生成规范
349 - `/speckit.plan` - 创建实现计划 408 - `/speckit.plan` - 创建实现计划
350 - `/speckit.tasks` - 生成任务分解 409 - `/speckit.tasks` - 生成任务分解
...@@ -357,6 +416,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -357,6 +416,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
357 ### 代码风格说明 (来自 VUE_CODE_STYLE_GUIDE.md) 416 ### 代码风格说明 (来自 VUE_CODE_STYLE_GUIDE.md)
358 417
359 **当前实践**: 418 **当前实践**:
419 +
360 - 新组件使用 `<script setup>` 和 Composition API 420 - 新组件使用 `<script setup>` 和 Composition API
361 - API 函数应始终返回 `{ code, data, msg }` - 永不返回 `false` 421 - API 函数应始终返回 `{ code, data, msg }` - 永不返回 `false`
362 - 尽可能将逻辑提取到 composables (`/src/composables/useXxx.js`) 422 - 尽可能将逻辑提取到 composables (`/src/composables/useXxx.js`)
...@@ -364,6 +424,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -364,6 +424,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
364 - 除非必要避免 JSX;大多数页面使用 Vue 模板 424 - 除非必要避免 JSX;大多数页面使用 Vue 模板
365 425
366 **已知不一致性** (需注意): 426 **已知不一致性** (需注意):
427 +
367 - 文件中混合使用分号/无分号 428 - 文件中混合使用分号/无分号
368 - 混合使用 2 空格/4 空格缩进 429 - 混合使用 2 空格/4 空格缩进
369 - 混合使用 function/箭头函数 430 - 混合使用 function/箭头函数
...@@ -372,6 +433,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -372,6 +433,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
372 - 部分页面组件过大,应考虑拆分 433 - 部分页面组件过大,应考虑拆分
373 434
374 **改进建议**: 435 **改进建议**:
436 +
375 - 统一使用 ESLint + Prettier 进行代码格式化 437 - 统一使用 ESLint + Prettier 进行代码格式化
376 - 制定统一的 API 返回值规范 438 - 制定统一的 API 返回值规范
377 - 大型页面组件应拆分为多个子组件 439 - 大型页面组件应拆分为多个子组件
...@@ -380,16 +442,19 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -380,16 +442,19 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
380 ## 特殊功能 442 ## 特殊功能
381 443
382 ### 分享海报生成 444 ### 分享海报生成
445 +
383 - 基于 Canvas 的海报生成,带二维码 (`qrcode` 包) 446 - 基于 Canvas 的海报生成,带二维码 (`qrcode` 包)
384 - 自动优化 `cdn.ipadbiz.cn` URL 的图片 447 - 自动优化 `cdn.ipadbiz.cn` URL 的图片
385 - 使用 `crossorigin="anonymous"` 处理跨域 Canvas 污染 448 - 使用 `crossorigin="anonymous"` 处理跨域 Canvas 污染
386 - 组件: `SharePoster.vue` (可复用) 449 - 组件: `SharePoster.vue` (可复用)
387 450
388 ### 动态 Open Graph 元标签 451 ### 动态 Open Graph 元标签
452 +
389 - 课程详情页动态添加 `og:title`, `og:description`, `og:image`, `og:url` 453 - 课程详情页动态添加 `og:title`, `og:description`, `og:image`, `og:url`
390 - 页面卸载时移除标签以避免冲突 454 - 页面卸载时移除标签以避免冲突
391 455
392 ### 标签指示器动画 456 ### 标签指示器动画
457 +
393 - 基于 ResizeObserver 的指示器定位 458 - 基于 ResizeObserver 的指示器定位
394 - 处理异步加载第三列而不错位 459 - 处理异步加载第三列而不错位
395 - 参考 `StudyCoursePage.vue` 的实现 460 - 参考 `StudyCoursePage.vue` 的实现
...@@ -491,6 +556,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -491,6 +556,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
491 ### Q: 为什么 Video.js 在切换显示/隐藏时无法正常工作? 556 ### Q: 为什么 Video.js 在切换显示/隐藏时无法正常工作?
492 557
493 **A**: Video.js 在 `v-show` (display: none) 状态下初始化会失败。 558 **A**: Video.js 在 `v-show` (display: none) 状态下初始化会失败。
559 +
494 - **解决**: 使用 `v-if` 而非 `v-show` 560 - **解决**: 使用 `v-if` 而非 `v-show`
495 - **正确模式**: 设置 `isPlaying = true`,然后 `setTimeout` 再调用 `play()` 561 - **正确模式**: 设置 `isPlaying = true`,然后 `setTimeout` 再调用 `play()`
496 - **参考**: `StudyDetailPage.vue` 中的实现 562 - **参考**: `StudyDetailPage.vue` 中的实现
...@@ -498,6 +564,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -498,6 +564,7 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
498 ### Q: 为什么我的 401 响应导致页面跳转到登录页? 564 ### Q: 为什么我的 401 响应导致页面跳转到登录页?
499 565
500 **A**: 这是预期的行为,但仅对需要认证的页面生效。 566 **A**: 这是预期的行为,但仅对需要认证的页面生效。
567 +
501 - 公开页面(如课程详情)不会跳转 568 - 公开页面(如课程详情)不会跳转
502 - 受限页面会清理登录信息并重定向 569 - 受限页面会清理登录信息并重定向
503 - 如需自定义处理,在组件内监听 API 错误 570 - 如需自定义处理,在组件内监听 API 错误
...@@ -505,18 +572,24 @@ Vant 组件通过 `unplugin-vue-components` 自动导入: ...@@ -505,18 +572,24 @@ Vant 组件通过 `unplugin-vue-components` 自动导入:
505 ### Q: 如何正确检查 API 响应是否成功? 572 ### Q: 如何正确检查 API 响应是否成功?
506 573
507 **A**: 始终使用 `if (res.code === 1)` 而非 `if (res.code)`。 574 **A**: 始终使用 `if (res.code === 1)` 而非 `if (res.code)`。
575 +
508 ```javascript 576 ```javascript
509 // ✓ 正确 577 // ✓ 正确
510 const { code, data } = await getCourseDetailAPI({ i: courseId }) 578 const { code, data } = await getCourseDetailAPI({ i: courseId })
511 -if (code === 1) { /* 成功 */ } 579 +if (code === 1) {
580 + /* 成功 */
581 +}
512 582
513 // ✗ 错误 - 会将 401/403 当作成功 583 // ✗ 错误 - 会将 401/403 当作成功
514 -if (code) { /* ... */ } 584 +if (code) {
585 + /* ... */
586 +}
515 ``` 587 ```
516 588
517 ### Q: 为什么 Vant 组件导入后报错或无法使用? 589 ### Q: 为什么 Vant 组件导入后报错或无法使用?
518 590
519 **A**: Vant 组件已配置自动导入,无需手动导入。 591 **A**: Vant 组件已配置自动导入,无需手动导入。
592 +
520 ```javascript 593 ```javascript
521 // ✗ 错误 594 // ✗ 错误
522 import { Button } from 'vant' 595 import { Button } from 'vant'
...@@ -528,6 +601,7 @@ import { Button } from 'vant' ...@@ -528,6 +601,7 @@ import { Button } from 'vant'
528 ### Q: 如何在微信环境下正确处理支付和授权? 601 ### Q: 如何在微信环境下正确处理支付和授权?
529 602
530 **A**: 使用 `wxInfo()` 工具函数检测环境: 603 **A**: 使用 `wxInfo()` 工具函数检测环境:
604 +
531 ```javascript 605 ```javascript
532 import { wxInfo } from '@/utils/tools' 606 import { wxInfo } from '@/utils/tools'
533 607
...@@ -542,6 +616,7 @@ if (wxInfo().isWeiXin) { ...@@ -542,6 +616,7 @@ if (wxInfo().isWeiXin) {
542 ### Q: 打卡时如何避免数据丢失? 616 ### Q: 打卡时如何避免数据丢失?
543 617
544 **A**: 使用 `useCheckinDraft` 自动缓存草稿: 618 **A**: 使用 `useCheckinDraft` 自动缓存草稿:
619 +
545 ```javascript 620 ```javascript
546 import { useCheckinDraft } from '@/composables/useCheckinDraft' 621 import { useCheckinDraft } from '@/composables/useCheckinDraft'
547 622
...@@ -560,22 +635,26 @@ onSubmitSuccess(() => clearDraft()) ...@@ -560,22 +635,26 @@ onSubmitSuccess(() => clearDraft())
560 ### 最近重要更新 635 ### 最近重要更新
561 636
562 **打卡系统增强** (2025): 637 **打卡系统增强** (2025):
638 +
563 - 打卡详情页重构 (`/checkin/detail`):统一文本、媒体上传和计数打卡入口 639 - 打卡详情页重构 (`/checkin/detail`):统一文本、媒体上传和计数打卡入口
564 - 新增草稿缓存功能 (`useCheckinDraft`):防止数据丢失,自动保存和恢复 640 - 新增草稿缓存功能 (`useCheckinDraft`):防止数据丢失,自动保存和恢复
565 - 优化附件预览和编辑回填逻辑 641 - 优化附件预览和编辑回填逻辑
566 - 实现基于 `useCheckin` 的通用提交流程 642 - 实现基于 `useCheckin` 的通用提交流程
567 643
568 **教师端完善** (2025): 644 **教师端完善** (2025):
645 +
569 - 作业管理页面 (`/teacher/tasks`) 646 - 作业管理页面 (`/teacher/tasks`)
570 - 作业主页 (`/teacher/tasks/:id`):统计与日历视图 647 - 作业主页 (`/teacher/tasks/:id`):统计与日历视图
571 - 学员作业记录页面 (`/teacher/student-record`) 648 - 学员作业记录页面 (`/teacher/student-record`)
572 649
573 **召回系统** (2025): 650 **召回系统** (2025):
651 +
574 - 身份证查询历史功能 652 - 身份证查询历史功能
575 - 时光机旅程 (Timeline) 展示用户里程碑 653 - 时光机旅程 (Timeline) 展示用户里程碑
576 - 专属海报生成与分享 654 - 专属海报生成与分享
577 655
578 **基础体验优化** (2025): 656 **基础体验优化** (2025):
657 +
579 - 登录逻辑调整:仅在点击微信图标时触发授权 658 - 登录逻辑调整:仅在点击微信图标时触发授权
580 - 搜索栏优化:支持 iOS 软键盘搜索键 659 - 搜索栏优化:支持 iOS 软键盘搜索键
581 - 课程详情页:动态 Open Graph 标签,优化分享体验 660 - 课程详情页:动态 Open Graph 标签,优化分享体验
...@@ -713,7 +792,7 @@ onSubmitSuccess(() => clearDraft()) ...@@ -713,7 +792,7 @@ onSubmitSuccess(() => clearDraft())
713 项目包含 **12个主要组件目录**,涵盖了从基础 UI 到业务功能的完整组件体系: 792 项目包含 **12个主要组件目录**,涵盖了从基础 UI 到业务功能的完整组件体系:
714 793
715 | 目录 | 组件示例 | 说明 | 794 | 目录 | 组件示例 | 说明 |
716 -|------|---------|------| 795 +| -------------- | ------------------------------------------------------------------------- | -------------- |
717 | `ui/` | `VideoPlayer`, `AudioPlayer`, `CheckInDialog`, `SharePoster`, `SearchBar` | 通用 UI 组件库 | 796 | `ui/` | `VideoPlayer`, `AudioPlayer`, `CheckInDialog`, `SharePoster`, `SearchBar` | 通用 UI 组件库 |
718 | `checkin/` | `CheckInDialog`, `CheckInList`, `CheckInResult` | 打卡业务组件 | 797 | `checkin/` | `CheckInDialog`, `CheckInList`, `CheckInResult` | 打卡业务组件 |
719 | `media/` | `AudioPlayer`, `VideoPlayer`, `MusicPlayer` | 音视频播放组件 | 798 | `media/` | `AudioPlayer`, `VideoPlayer`, `MusicPlayer` | 音视频播放组件 |
......