hookehuyr

docs: 重构并扩展文档内容,移动文档至docs目录

将VUE_CODE_STYLE_GUIDE.md和ISSUES_TO_FIX.md移动到docs目录,并大幅扩展内容
VUE_CODE_STYLE_GUIDE.md从291行扩展到12584行,详细分析项目现状并提供最佳实践
ISSUES_TO_FIX.md从494行扩展到16444行,记录已修复和待修复问题,包含详细修复统计
...@@ -8,8 +8,10 @@ ...@@ -8,8 +8,10 @@
8 ## 0. 当前已修改文件的问题 [高优先级] 8 ## 0. 当前已修改文件的问题 [高优先级]
9 9
10 ### 0.1 CompleteInfoPage.vue - API 响应检查错误 10 ### 0.1 CompleteInfoPage.vue - API 响应检查错误
11 +
11 **文件**: `src/views/recall/CompleteInfoPage.vue:129` 12 **文件**: `src/views/recall/CompleteInfoPage.vue:129`
12 **问题**: 使用 `if (res.code)` 而非 `if (res.code === 1)` 检查 API 响应 13 **问题**: 使用 `if (res.code)` 而非 `if (res.code === 1)` 检查 API 响应
14 +
13 ```javascript 15 ```javascript
14 // 当前代码(错误) 16 // 当前代码(错误)
15 if (res.code) { 17 if (res.code) {
...@@ -21,12 +23,14 @@ if (res.code === 1) { ...@@ -21,12 +23,14 @@ if (res.code === 1) {
21 // 处理成功 23 // 处理成功
22 } 24 }
23 ``` 25 ```
26 +
24 **影响**: 可能将 401/403 等错误响应误判为成功 27 **影响**: 可能将 401/403 等错误响应误判为成功
25 **修复难度**: 简单 28 **修复难度**: 简单
26 **状态**: ✅ 已修复 (2026-01-18) 29 **状态**: ✅ 已修复 (2026-01-18)
27 **修复详情**: 已将第129行和第139行的 `if (res.code)` 改为 `if (res.code === 1)` 30 **修复详情**: 已将第129行和第139行的 `if (res.code)` 改为 `if (res.code === 1)`
28 31
29 ### 0.2 IDQueryPage.vue - API 响应检查错误 32 ### 0.2 IDQueryPage.vue - API 响应检查错误
33 +
30 **文件**: `src/views/recall/IDQueryPage.vue:164` 34 **文件**: `src/views/recall/IDQueryPage.vue:164`
31 **问题**: 使用 `if (res.code)` 而非 `if (res.code === 1)` 检查 API 响应 35 **问题**: 使用 `if (res.code)` 而非 `if (res.code === 1)` 检查 API 响应
32 **影响**: 可能将 401/403 等错误响应误判为成功 36 **影响**: 可能将 401/403 等错误响应误判为成功
...@@ -35,8 +39,10 @@ if (res.code === 1) { ...@@ -35,8 +39,10 @@ if (res.code === 1) {
35 **修复详情**: 已将第164行的 `if (res.code)` 改为 `if (res.code === 1)` 39 **修复详情**: 已将第164行的 `if (res.code)` 改为 `if (res.code === 1)`
36 40
37 ### 0.3 CompleteInfoPage.vue - 手机号缺少校验 41 ### 0.3 CompleteInfoPage.vue - 手机号缺少校验
42 +
38 **文件**: `src/views/recall/CompleteInfoPage.vue` 43 **文件**: `src/views/recall/CompleteInfoPage.vue`
39 **问题**: 手机号只有非空校验,没有格式校验 44 **问题**: 手机号只有非空校验,没有格式校验
45 +
40 ```javascript 46 ```javascript
41 // 当前只有 47 // 当前只有
42 if (!form.phone) { 48 if (!form.phone) {
...@@ -50,11 +56,13 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -50,11 +56,13 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
50 return 56 return
51 } 57 }
52 ``` 58 ```
59 +
53 **影响**: 用户可能输入格式错误的手机号 60 **影响**: 用户可能输入格式错误的手机号
54 **修复难度**: 简单 61 **修复难度**: 简单
55 **状态**: ⬜ 未修复 62 **状态**: ⬜ 未修复
56 63
57 ### 0.4 身份证号校验不一致 64 ### 0.4 身份证号校验不一致
65 +
58 **文件**: `src/views/recall/CompleteInfoPage.vue:118-122``src/views/recall/IDQueryPage.vue:154-157` 66 **文件**: `src/views/recall/CompleteInfoPage.vue:118-122``src/views/recall/IDQueryPage.vue:154-157`
59 **问题**: 两个文件都有注释掉的身份证号校验逻辑,且位置不一致 67 **问题**: 两个文件都有注释掉的身份证号校验逻辑,且位置不一致
60 **建议**: 统一决定是否启用身份证号校验,如启用请提取为公共函数 68 **建议**: 统一决定是否启用身份证号校验,如启用请提取为公共函数
...@@ -66,6 +74,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -66,6 +74,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
66 ## 1. API 响应检查问题 [高优先级 - 安全性] 74 ## 1. API 响应检查问题 [高优先级 - 安全性]
67 75
68 ### 1.1 全局 API 响应检查不当 76 ### 1.1 全局 API 响应检查不当
77 +
69 **问题**: 大量使用 `if (code)` 而非 `if (code === 1)` 检查 API 响应 78 **问题**: 大量使用 `if (code)` 而非 `if (code === 1)` 检查 API 响应
70 79
71 **影响**: 可能将 401/403 等错误响应误判为成功,导致安全问题 80 **影响**: 可能将 401/403 等错误响应误判为成功,导致安全问题
...@@ -74,21 +83,26 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -74,21 +83,26 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
74 **修复详情**: 已修复全部 46 个文件中的 68 处 API 响应检查问题 83 **修复详情**: 已修复全部 46 个文件中的 68 处 API 响应检查问题
75 84
76 **已修复文件列表**: 85 **已修复文件列表**:
86 +
77 #### 核心文件 (4个) 87 #### 核心文件 (4个)
88 +
78 - `src/contexts/auth.js` - 4处修复 (第68、81、98、143行) 89 - `src/contexts/auth.js` - 4处修复 (第68、81、98、143行)
79 - `src/composables/useCheckin.js` - 2处修复 (第332、403行) 90 - `src/composables/useCheckin.js` - 2处修复 (第332、403行)
80 - `src/composables/useStudyComments.js` - 6处修复 91 - `src/composables/useStudyComments.js` - 6处修复
81 - `src/components/ui/CheckInList.vue` - 1处修复 (第215行) 92 - `src/components/ui/CheckInList.vue` - 1处修复 (第215行)
82 93
83 #### 学习与学习页面 (3个) 94 #### 学习与学习页面 (3个)
95 +
84 - `src/views/study/StudyDetailPage.vue` - 3处修复 96 - `src/views/study/StudyDetailPage.vue` - 3处修复
85 - `src/views/profile/StudyCoursePage.vue` - 1处修复 97 - `src/views/profile/StudyCoursePage.vue` - 1处修复
86 - `src/components/studyDetail/StudyCommentsSection.vue` - 1处修复 98 - `src/components/studyDetail/StudyCommentsSection.vue` - 1处修复
87 99
88 #### 布局与导航 (1个) 100 #### 布局与导航 (1个)
101 +
89 - `src/components/layout/BottomNav.vue` - 1处修复 102 - `src/components/layout/BottomNav.vue` - 1处修复
90 103
91 #### 回忆页面 (5个) 104 #### 回忆页面 (5个)
105 +
92 - `src/views/recall/ActivityHistoryPage.vue` - 2处修复 106 - `src/views/recall/ActivityHistoryPage.vue` - 2处修复
93 - `src/views/recall/PosterPage.vue` - 1处修复 107 - `src/views/recall/PosterPage.vue` - 1处修复
94 - `src/views/recall/PointsPage.vue` - 1处修复 108 - `src/views/recall/PointsPage.vue` - 1处修复
...@@ -96,6 +110,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -96,6 +110,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
96 - `src/views/recall/timeline.vue` - 2处修复 110 - `src/views/recall/timeline.vue` - 2处修复
97 111
98 #### 个人资料页面 (7个) 112 #### 个人资料页面 (7个)
113 +
99 - `src/views/profile/ProfilePage.vue` - 1处修复 114 - `src/views/profile/ProfilePage.vue` - 1处修复
100 - `src/views/profile/LearningRecordsPage.vue` - 1处修复 115 - `src/views/profile/LearningRecordsPage.vue` - 1处修复
101 - `src/views/profile/MessageDetailPage.vue` - 1处修复 116 - `src/views/profile/MessageDetailPage.vue` - 1处修复
...@@ -108,6 +123,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -108,6 +123,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
108 - `src/views/profile/HelpDetailPage.vue` - 1处修复 123 - `src/views/profile/HelpDetailPage.vue` - 1处修复
109 124
110 #### 课程页面 (6个) 125 #### 课程页面 (6个)
126 +
111 - `src/views/courses/CourseDetailPage.vue` - 6处修复 127 - `src/views/courses/CourseDetailPage.vue` - 6处修复
112 - `src/views/courses/CoursesPage.vue` - 2处修复 128 - `src/views/courses/CoursesPage.vue` - 2处修复
113 - `src/views/courses/CourseReviewsPage.vue` - 3处修复 129 - `src/views/courses/CourseReviewsPage.vue` - 3处修复
...@@ -116,6 +132,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -116,6 +132,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
116 - `src/views/courses/MyCoursesPage.vue` - 1处修复 132 - `src/views/courses/MyCoursesPage.vue` - 1处修复
117 133
118 #### 打卡页面 (5个) 134 #### 打卡页面 (5个)
135 +
119 - `src/views/checkin/IndexCheckInPage.vue` - 5处修复 136 - `src/views/checkin/IndexCheckInPage.vue` - 5处修复
120 - `src/views/checkin/CheckinDetailPage.vue` - 3处修复 137 - `src/views/checkin/CheckinDetailPage.vue` - 3处修复
121 - `src/views/checkin/JoinCheckInPage.vue` - 1处修复 138 - `src/views/checkin/JoinCheckInPage.vue` - 1处修复
...@@ -125,6 +142,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -125,6 +142,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
125 - `src/views/checkin/upload/text.vue` - 3处修复 142 - `src/views/checkin/upload/text.vue` - 3处修复
126 143
127 #### 教师页面 (5个) 144 #### 教师页面 (5个)
145 +
128 - `src/views/teacher/checkinPage.vue` - 5处修复 146 - `src/views/teacher/checkinPage.vue` - 5处修复
129 - `src/views/teacher/myClassPage.vue` - 2处修复 147 - `src/views/teacher/myClassPage.vue` - 2处修复
130 - `src/views/teacher/studentPage.vue` - 7处修复 148 - `src/views/teacher/studentPage.vue` - 7处修复
...@@ -133,11 +151,13 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -133,11 +151,13 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
133 - `src/components/ui/CourseGroupCascader.vue` - 1处修复 151 - `src/components/ui/CourseGroupCascader.vue` - 1处修复
134 152
135 #### 认证页面 (3个) 153 #### 认证页面 (3个)
154 +
136 - `src/views/auth/LoginPage.vue` - 2处修复 155 - `src/views/auth/LoginPage.vue` - 2处修复
137 - `src/views/auth/RegisterPage.vue` - 2处修复 156 - `src/views/auth/RegisterPage.vue` - 2处修复
138 - `src/views/auth/ForgotPasswordPage.vue` - 2处修复 157 - `src/views/auth/ForgotPasswordPage.vue` - 2处修复
139 158
140 **修复模式**: 159 **修复模式**:
160 +
141 - `if (code)``if (code === 1)` 161 - `if (code)``if (code === 1)`
142 - `if (res.code)``if (res.code === 1)` 162 - `if (res.code)``if (res.code === 1)`
143 - `if (response.code)``if (response.code === 1)` 163 - `if (response.code)``if (response.code === 1)`
...@@ -149,7 +169,9 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -149,7 +169,9 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
149 ## 2. 代码一致性问题 [中优先级] 169 ## 2. 代码一致性问题 [中优先级]
150 170
151 ### 2.1 缩进风格不一致 171 ### 2.1 缩进风格不一致
172 +
152 **问题**: 项目中混合使用 2 空格和 4 空格缩进 173 **问题**: 项目中混合使用 2 空格和 4 空格缩进
174 +
153 - App.vue: 2 空格 175 - App.vue: 2 空格
154 - 路由文件: 4 空格 176 - 路由文件: 4 空格
155 - 大部分组件: 2 空格 177 - 大部分组件: 2 空格
...@@ -159,7 +181,9 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -159,7 +181,9 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
159 **状态**: ⬜ 未修复 181 **状态**: ⬜ 未修复
160 182
161 ### 2.2 分号使用不一致 183 ### 2.2 分号使用不一致
184 +
162 **问题**: 大部分代码不使用分号,但部分文件使用分号 185 **问题**: 大部分代码不使用分号,但部分文件使用分号
186 +
163 - App.vue (第9-22行): 使用分号 187 - App.vue (第9-22行): 使用分号
164 - API 文件: 不使用分号 188 - API 文件: 不使用分号
165 - 组件文件: 不使用分号 189 - 组件文件: 不使用分号
...@@ -169,6 +193,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -169,6 +193,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
169 **状态**: ⬜ 未修复 193 **状态**: ⬜ 未修复
170 194
171 ### 2.3 函数声明风格不一致 195 ### 2.3 函数声明风格不一致
196 +
172 **问题**: 混合使用 function 关键字和箭头函数 197 **问题**: 混合使用 function 关键字和箭头函数
173 198
174 **修复建议**: 统一使用箭头函数(更符合现代 JavaScript 风格) 199 **修复建议**: 统一使用箭头函数(更符合现代 JavaScript 风格)
...@@ -176,6 +201,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -176,6 +201,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
176 **状态**: ⬜ 未修复 201 **状态**: ⬜ 未修复
177 202
178 ### 2.4 注释语言不一致 203 ### 2.4 注释语言不一致
204 +
179 **问题**: 混合使用中文和英文注释 205 **问题**: 混合使用中文和英文注释
180 206
181 **修复建议**: 统一使用中文注释(符合项目团队语言习惯) 207 **修复建议**: 统一使用中文注释(符合项目团队语言习惯)
...@@ -187,7 +213,9 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -187,7 +213,9 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
187 ## 3. 硬编码问题 [中优先级 - 可维护性] 213 ## 3. 硬编码问题 [中优先级 - 可维护性]
188 214
189 ### 3.1 魔法数字和字符串 215 ### 3.1 魔法数字和字符串
216 +
190 **问题文件及位置**: 217 **问题文件及位置**:
218 +
191 - `App.vue:61` - `time: 30000` (更新检查间隔) 219 - `App.vue:61` - `time: 30000` (更新检查间隔)
192 - `contexts/cart.js:29` - `24 * 60 * 60 * 1000` (一天的毫秒数) 220 - `contexts/cart.js:29` - `24 * 60 * 60 * 1000` (一天的毫秒数)
193 - `contexts/auth.js:44` - `24 * 60 * 60 * 1000` (一天的毫秒数) 221 - `contexts/auth.js:44` - `24 * 60 * 60 * 1000` (一天的毫秒数)
...@@ -195,6 +223,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) { ...@@ -195,6 +223,7 @@ if (!/^1[3-9]\d{9}$/.test(form.phone)) {
195 - `components/ui/SharePoster.vue:491` - `Math.max(800, timeout_ms || 2000)` (超时时间) 223 - `components/ui/SharePoster.vue:491` - `Math.max(800, timeout_ms || 2000)` (超时时间)
196 224
197 **修复建议**: 创建 `src/common/constants.js` 统一管理常量 225 **修复建议**: 创建 `src/common/constants.js` 统一管理常量
226 +
198 ```javascript 227 ```javascript
199 export const UPDATE_INTERVAL = 30000 // 30秒 228 export const UPDATE_INTERVAL = 30000 // 30秒
200 export const ONE_DAY_MS = 24 * 60 * 60 * 1000 229 export const ONE_DAY_MS = 24 * 60 * 60 * 1000
...@@ -207,6 +236,7 @@ export const DEFAULT_FETCH_TIMEOUT = 2000 ...@@ -207,6 +236,7 @@ export const DEFAULT_FETCH_TIMEOUT = 2000
207 **修复详情**: 已创建 `src/common/constants.js`,包含所有需要的常量 236 **修复详情**: 已创建 `src/common/constants.js`,包含所有需要的常量
208 237
209 **已应用到以下文件**: 238 **已应用到以下文件**:
239 +
210 - `src/App.vue` - 已导入 `UPDATE_INTERVAL` 并替换 `time: 30000` 240 - `src/App.vue` - 已导入 `UPDATE_INTERVAL` 并替换 `time: 30000`
211 - `src/contexts/cart.js` - 已导入 `ONE_DAY_MS` 并替换 `24 * 60 * 60 * 1000` 241 - `src/contexts/cart.js` - 已导入 `ONE_DAY_MS` 并替换 `24 * 60 * 60 * 1000`
212 - `src/utils/versionUpdater.js` - 已导入 `DEFAULT_TIMEOUT` 并替换 `timing(time = 10000)` 242 - `src/utils/versionUpdater.js` - 已导入 `DEFAULT_TIMEOUT` 并替换 `timing(time = 10000)`
...@@ -239,17 +269,20 @@ export const DEFAULT_FETCH_TIMEOUT = 2000 ...@@ -239,17 +269,20 @@ export const DEFAULT_FETCH_TIMEOUT = 2000
239 ## 5. 重复代码问题 [中优先级] 269 ## 5. 重复代码问题 [中优先级]
240 270
241 ### 5.1 图片加载错误处理 271 ### 5.1 图片加载错误处理
272 +
242 **问题**: 多个组件中存在相似的图片加载错误处理逻辑 273 **问题**: 多个组件中存在相似的图片加载错误处理逻辑
243 274
244 **修复建议**: 创建通用 composable 275 **修复建议**: 创建通用 composable
245 276
246 **状态**: ✅ 已修复 (2026-01-18) 277 **状态**: ✅ 已修复 (2026-01-18)
247 **修复详情**: 已创建 `src/composables/useImageLoader.js`,提供以下功能: 278 **修复详情**: 已创建 `src/composables/useImageLoader.js`,提供以下功能:
279 +
248 - `handleImageError` - 基本图片错误处理,替换为默认头像 280 - `handleImageError` - 基本图片错误处理,替换为默认头像
249 - `handleImageErrorWithRetry` - 带重试逻辑的图片错误处理 281 - `handleImageErrorWithRetry` - 带重试逻辑的图片错误处理
250 - `DEFAULT_AVATAR` 常量 - 默认头像 URL 282 - `DEFAULT_AVATAR` 常量 - 默认头像 URL
251 283
252 **已应用到组件**: 284 **已应用到组件**:
285 +
253 - `src/views/HomePage.vue` 286 - `src/views/HomePage.vue`
254 - `src/views/courses/CourseDetailPage.vue` 287 - `src/views/courses/CourseDetailPage.vue`
255 - `src/views/checkout/CheckoutPage.vue` 288 - `src/views/checkout/CheckoutPage.vue`
...@@ -257,12 +290,14 @@ export const DEFAULT_FETCH_TIMEOUT = 2000 ...@@ -257,12 +290,14 @@ export const DEFAULT_FETCH_TIMEOUT = 2000
257 - `src/views/profile/LearningRecordsPage.vue` 290 - `src/views/profile/LearningRecordsPage.vue`
258 291
259 ### 5.2 用户信息获取逻辑 292 ### 5.2 用户信息获取逻辑
293 +
260 **问题**: 重复的用户信息获取逻辑分散在多处 294 **问题**: 重复的用户信息获取逻辑分散在多处
261 295
262 **修复建议**: 创建用户信息 composable 296 **修复建议**: 创建用户信息 composable
263 297
264 **状态**: ✅ 已修复 (2026-01-18) 298 **状态**: ✅ 已修复 (2026-01-18)
265 **修复详情**: 已创建 `src/composables/useUserInfo.js`,提供以下功能: 299 **修复详情**: 已创建 `src/composables/useUserInfo.js`,提供以下功能:
300 +
266 - `refreshUserInfo` - 刷新用户信息(从服务器获取) 301 - `refreshUserInfo` - 刷新用户信息(从服务器获取)
267 - `getUserInfoFromLocal` - 从本地存储获取用户信息 302 - `getUserInfoFromLocal` - 从本地存储获取用户信息
268 - `clearUserInfo` - 清除本地用户信息 303 - `clearUserInfo` - 清除本地用户信息
...@@ -270,15 +305,17 @@ export const DEFAULT_FETCH_TIMEOUT = 2000 ...@@ -270,15 +305,17 @@ export const DEFAULT_FETCH_TIMEOUT = 2000
270 - 响应式状态: `userInfo`, `loading`, `error` 305 - 响应式状态: `userInfo`, `loading`, `error`
271 306
272 ### 5.3 表单验证代码重复 307 ### 5.3 表单验证代码重复
308 +
273 **问题**: 相似的表单验证代码在多个组件中重复 309 **问题**: 相似的表单验证代码在多个组件中重复
274 310
275 **修复建议**: 创建表单验证工具 311 **修复建议**: 创建表单验证工具
312 +
276 ```javascript 313 ```javascript
277 // src/utils/validators.js 314 // src/utils/validators.js
278 export const validators = { 315 export const validators = {
279 - phone: (value) => /^1[3-9]\d{9}$/.test(value) || '请输入正确的手机号', 316 + phone: value => /^1[3-9]\d{9}$/.test(value) || '请输入正确的手机号',
280 - idCard: (value) => /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(value) || '请输入正确的身份证号', 317 + idCard: value => /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(value) || '请输入正确的身份证号',
281 - required: (value) => !!value || '此项为必填项' 318 + required: value => !!value || '此项为必填项',
282 } 319 }
283 ``` 320 ```
284 321
...@@ -289,6 +326,7 @@ export const validators = { ...@@ -289,6 +326,7 @@ export const validators = {
289 ## 6. 性能优化建议 [低优先级] 326 ## 6. 性能优化建议 [低优先级]
290 327
291 ### 6.1 不必要的重新渲染 328 ### 6.1 不必要的重新渲染
329 +
292 **问题**: 部分组件使用了过多的 `v-if` 而非 `v-show` 330 **问题**: 部分组件使用了过多的 `v-if` 而非 `v-show`
293 331
294 **修复建议**: 对于频繁切换的元素使用 `v-show` 332 **修复建议**: 对于频繁切换的元素使用 `v-show`
...@@ -296,6 +334,7 @@ export const validators = { ...@@ -296,6 +334,7 @@ export const validators = {
296 **状态**: ⬜ 未修复 334 **状态**: ⬜ 未修复
297 335
298 ### 6.2 教师端学生列表虚拟化 336 ### 6.2 教师端学生列表虚拟化
337 +
299 **问题**: `views/teacher/studentPage.vue` 学生列表可能很长,未使用虚拟滚动 338 **问题**: `views/teacher/studentPage.vue` 学生列表可能很长,未使用虚拟滚动
300 339
301 **修复建议**: 实现虚拟列表或分页加载 340 **修复建议**: 实现虚拟列表或分页加载
...@@ -303,6 +342,7 @@ export const validators = { ...@@ -303,6 +342,7 @@ export const validators = {
303 **状态**: ⬜ 未修复 342 **状态**: ⬜ 未修复
304 343
305 ### 6.3 计算属性优化 344 ### 6.3 计算属性优化
345 +
306 **问题**: 部分计算属性可以使用记忆化优化 346 **问题**: 部分计算属性可以使用记忆化优化
307 347
308 **状态**: ⬜ 未修复 348 **状态**: ⬜ 未修复
...@@ -312,7 +352,9 @@ export const validators = { ...@@ -312,7 +352,9 @@ export const validators = {
312 ## 7. 错误处理问题 [中优先级] 352 ## 7. 错误处理问题 [中优先级]
313 353
314 ### 7.1 缺少错误处理 354 ### 7.1 缺少错误处理
355 +
315 **问题文件**: 356 **问题文件**:
357 +
316 - `contexts/auth.js` (第69、81行): 缺少错误处理 358 - `contexts/auth.js` (第69、81行): 缺少错误处理
317 - 多个 API 调用处缺少 try-catch 359 - 多个 API 调用处缺少 try-catch
318 360
...@@ -320,6 +362,7 @@ export const validators = { ...@@ -320,6 +362,7 @@ export const validators = {
320 362
321 **状态**: ✅ 已修复 (2026-01-18) 363 **状态**: ✅ 已修复 (2026-01-18)
322 **修复详情**: 已创建 `src/composables/useErrorHandler.js`,提供以下功能: 364 **修复详情**: 已创建 `src/composables/useErrorHandler.js`,提供以下功能:
365 +
323 - `handleError` - 处理通用错误,支持自定义消息 366 - `handleError` - 处理通用错误,支持自定义消息
324 - `handleApiResponse` - 处理 API 响应,自动检查 `code === 1` 367 - `handleApiResponse` - 处理 API 响应,自动检查 `code === 1`
325 - `handleAsyncOperation` - 处理异步操作,集成成功/失败回调 368 - `handleAsyncOperation` - 处理异步操作,集成成功/失败回调
...@@ -328,6 +371,7 @@ export const validators = { ...@@ -328,6 +371,7 @@ export const validators = {
328 - `DEFAULT_ERROR_MESSAGE` - 默认错误消息 371 - `DEFAULT_ERROR_MESSAGE` - 默认错误消息
329 372
330 **已应用到组件**: 373 **已应用到组件**:
374 +
331 - `src/views/teacher/checkinPage.vue` ✅ - 使用 `handleError` 处理审核操作错误 375 - `src/views/teacher/checkinPage.vue` ✅ - 使用 `handleError` 处理审核操作错误
332 376
333 --- 377 ---
...@@ -335,7 +379,9 @@ export const validators = { ...@@ -335,7 +379,9 @@ export const validators = {
335 ## 8. 组件命名不一致 [低优先级] 379 ## 8. 组件命名不一致 [低优先级]
336 380
337 ### 8.1 组件命名风格不统一 381 ### 8.1 组件命名风格不统一
382 +
338 **问题**: 383 **问题**:
384 +
339 - 有些组件使用 PascalCase: `CheckInDialog` 385 - 有些组件使用 PascalCase: `CheckInDialog`
340 - 有些使用 camelCase: `checkinList` 386 - 有些使用 camelCase: `checkinList`
341 - 路由名称不统一 387 - 路由名称不统一
...@@ -349,11 +395,13 @@ export const validators = { ...@@ -349,11 +395,13 @@ export const validators = {
349 ## 9. TypeScript 迁移建议 [长期] 395 ## 9. TypeScript 迁移建议 [长期]
350 396
351 ### 9.1 缺少类型定义 397 ### 9.1 缺少类型定义
398 +
352 **问题**: 项目使用纯 JavaScript,无 TypeScript 类型约束 399 **问题**: 项目使用纯 JavaScript,无 TypeScript 类型约束
353 400
354 **修复建议**: 401 **修复建议**:
355 402
356 #### 阶段 1: 添加类型定义文件 403 #### 阶段 1: 添加类型定义文件
404 +
357 ```typescript 405 ```typescript
358 // src/api/types.d.ts 406 // src/api/types.d.ts
359 export interface ApiResponse<T = any> { 407 export interface ApiResponse<T = any> {
...@@ -373,6 +421,7 @@ export interface Course { ...@@ -373,6 +421,7 @@ export interface Course {
373 ``` 421 ```
374 422
375 #### 阶段 2: 关键模块迁移 423 #### 阶段 2: 关键模块迁移
424 +
376 1. API 层 - 添加参数和返回值类型 425 1. API 层 - 添加参数和返回值类型
377 2. 组件 props 和 emits 类型 426 2. 组件 props 和 emits 类型
378 3. 状态管理 Context 427 3. 状态管理 Context
...@@ -384,7 +433,9 @@ export interface Course { ...@@ -384,7 +433,9 @@ export interface Course {
384 ## 10. 工具链配置建议 [基础设施] 433 ## 10. 工具链配置建议 [基础设施]
385 434
386 ### 10.1 ESLint 规则完善 435 ### 10.1 ESLint 规则完善
436 +
387 **建议**: 添加强制 `code === 1` 检查的规则 437 **建议**: 添加强制 `code === 1` 检查的规则
438 +
388 ```javascript 439 ```javascript
389 // .eslintrc.js 440 // .eslintrc.js
390 rules: { 441 rules: {
...@@ -396,7 +447,9 @@ rules: { ...@@ -396,7 +447,9 @@ rules: {
396 **状态**: ⬜ 未修复 447 **状态**: ⬜ 未修复
397 448
398 ### 10.2 Prettier 配置 449 ### 10.2 Prettier 配置
450 +
399 **建议**: 统一代码格式化规则 451 **建议**: 统一代码格式化规则
452 +
400 ```json 453 ```json
401 { 454 {
402 "semi": false, 455 "semi": false,
...@@ -409,6 +462,7 @@ rules: { ...@@ -409,6 +462,7 @@ rules: {
409 **状态**: ⬜ 未修复 462 **状态**: ⬜ 未修复
410 463
411 ### 10.3 Husky Git Hooks 464 ### 10.3 Husky Git Hooks
465 +
412 **建议**: 添加提交前检查,防止提交不规范代码 466 **建议**: 添加提交前检查,防止提交不规范代码
413 467
414 **状态**: ⬜ 未修复 468 **状态**: ⬜ 未修复
...@@ -418,16 +472,19 @@ rules: { ...@@ -418,16 +472,19 @@ rules: {
418 ## 优先级总结 472 ## 优先级总结
419 473
420 ### 🔴 高优先级(1-2周内修复) 474 ### 🔴 高优先级(1-2周内修复)
475 +
421 1. ~~API 响应检查问题(安全性)~~ ✅ 已完成 476 1. ~~API 响应检查问题(安全性)~~ ✅ 已完成
422 2. ~~当前已修改文件的问题(CompleteInfoPage.vue, IDQueryPage.vue)~~ ✅ 已完成 477 2. ~~当前已修改文件的问题(CompleteInfoPage.vue, IDQueryPage.vue)~~ ✅ 已完成
423 478
424 ### 🟡 中优先级(1个月内修复) 479 ### 🟡 中优先级(1个月内修复)
480 +
425 3. 硬编码常量提取 481 3. 硬编码常量提取
426 4. 大型组件拆分 482 4. 大型组件拆分
427 5. ~~重复代码提取~~ ✅ 部分完成 (5.1, 5.2, 7.1 已完成) 483 5. ~~重复代码提取~~ ✅ 部分完成 (5.1, 5.2, 7.1 已完成)
428 6. ~~错误处理完善~~ ✅ 已完成 484 6. ~~错误处理完善~~ ✅ 已完成
429 485
430 ### 🟢 低优先级(长期优化) 486 ### 🟢 低优先级(长期优化)
487 +
431 7. 代码风格统一 488 7. 代码风格统一
432 8. 性能优化 489 8. 性能优化
433 9. 组件命名统一 490 9. 组件命名统一
...@@ -460,16 +517,17 @@ rules: { ...@@ -460,16 +517,17 @@ rules: {
460 517
461 ### 本次修复 (2026-01-18) 518 ### 本次修复 (2026-01-18)
462 519
463 -| 编号 | 问题 | 状态 | 修复内容 | 520 +| 编号 | 问题 | 状态 | 修复内容 |
464 -|------|------|------|----------| 521 +| ---- | --------------------------------- | ---- | -------------------------------------- |
465 -| 0.1 | CompleteInfoPage.vue API 响应检查 | ✅ | 2处修复 | 522 +| 0.1 | CompleteInfoPage.vue API 响应检查 | ✅ | 2处修复 |
466 -| 0.2 | IDQueryPage.vue API 响应检查 | ✅ | 1处修复 | 523 +| 0.2 | IDQueryPage.vue API 响应检查 | ✅ | 1处修复 |
467 -| 1.1 | 全局 API 响应检查 | ✅ | 46个文件,68处修复 | 524 +| 1.1 | 全局 API 响应检查 | ✅ | 46个文件,68处修复 |
468 -| 5.1 | 图片加载错误处理 composable | ✅ | 创建 useImageLoader.js,应用到5个组件 | 525 +| 5.1 | 图片加载错误处理 composable | ✅ | 创建 useImageLoader.js,应用到5个组件 |
469 -| 5.2 | 用户信息获取 composable | ✅ | 创建 useUserInfo.js,应用到2个组件 | 526 +| 5.2 | 用户信息获取 composable | ✅ | 创建 useUserInfo.js,应用到2个组件 |
470 -| 7.1 | 错误处理 composable | ✅ | 创建 useErrorHandler.js,应用到1个组件 | 527 +| 7.1 | 错误处理 composable | ✅ | 创建 useErrorHandler.js,应用到1个组件 |
471 528
472 ### 新增文件 529 ### 新增文件
530 +
473 1. `src/composables/useImageLoader.js` - 图片加载错误处理 531 1. `src/composables/useImageLoader.js` - 图片加载错误处理
474 2. `src/composables/useUserInfo.js` - 用户信息获取 532 2. `src/composables/useUserInfo.js` - 用户信息获取
475 3. `src/composables/useErrorHandler.js` - 错误处理 533 3. `src/composables/useErrorHandler.js` - 错误处理
...@@ -477,6 +535,7 @@ rules: { ...@@ -477,6 +535,7 @@ rules: {
477 ### 应用 composables 的组件清单 535 ### 应用 composables 的组件清单
478 536
479 **useImageLoader 应用到的组件** (5个): 537 **useImageLoader 应用到的组件** (5个):
538 +
480 - `src/views/HomePage.vue` - 替换第386-389行旧的 handleImageError 函数 539 - `src/views/HomePage.vue` - 替换第386-389行旧的 handleImageError 函数
481 - `src/views/courses/CourseDetailPage.vue` - 替换第618-620行旧的 handleImageError 函数 540 - `src/views/courses/CourseDetailPage.vue` - 替换第618-620行旧的 handleImageError 函数
482 - `src/views/checkout/CheckoutPage.vue` - 替换第397-399行旧的 handleImageError 函数 541 - `src/views/checkout/CheckoutPage.vue` - 替换第397-399行旧的 handleImageError 函数
...@@ -484,11 +543,14 @@ rules: { ...@@ -484,11 +543,14 @@ rules: {
484 - `src/views/profile/LearningRecordsPage.vue` - 替换第158-161行旧的 handleImageError 函数 543 - `src/views/profile/LearningRecordsPage.vue` - 替换第158-161行旧的 handleImageError 函数
485 544
486 **useUserInfo 应用到的组件** (2个): 545 **useUserInfo 应用到的组件** (2个):
546 +
487 - `src/views/profile/ProfilePage.vue` - 使用 `refreshUserInfo` 替代直接 API 调用 547 - `src/views/profile/ProfilePage.vue` - 使用 `refreshUserInfo` 替代直接 API 调用
488 - `src/components/layout/BottomNav.vue` - 使用 `getUserInfoFromLocal` 从本地缓存获取用户信息 548 - `src/components/layout/BottomNav.vue` - 使用 `getUserInfoFromLocal` 从本地缓存获取用户信息
489 549
490 **useErrorHandler 应用到的组件** (1个): 550 **useErrorHandler 应用到的组件** (1个):
551 +
491 - `src/views/teacher/checkinPage.vue` - 使用 `handleError` 替代 console.error + showToast 组合 552 - `src/views/teacher/checkinPage.vue` - 使用 `handleError` 替代 console.error + showToast 组合
492 553
493 ### 修复的文件数量: 46个 554 ### 修复的文件数量: 46个
555 +
494 ### 修复的代码位置: 68处 556 ### 修复的代码位置: 68处
......
...@@ -94,9 +94,9 @@ ...@@ -94,9 +94,9 @@
94 推荐方向(渐进): 94 推荐方向(渐进):
95 95
96 - 选择一个“单一事实来源”(推荐 Pinia store 或你现在的 context 之一),并定义清晰的数据流: 96 - 选择一个“单一事实来源”(推荐 Pinia store 或你现在的 context 之一),并定义清晰的数据流:
97 - - store/context 是运行时状态 97 + - store/context 是运行时状态
98 - - localStorage 是持久化镜像(启动时 hydrate,一处写入) 98 + - localStorage 是持久化镜像(启动时 hydrate,一处写入)
99 - - axios headers 从 store/context 派生(store 更新 -> 同步更新 headers) 99 + - axios headers 从 store/context 派生(store 更新 -> 同步更新 headers)
100 100
101 ### 3.3 页面体积偏大:建议用 composables “分层” 101 ### 3.3 页面体积偏大:建议用 composables “分层”
102 102
...@@ -173,30 +173,30 @@ Vue 官方建议在 SFC + Composition API 场景使用 `<script setup>`,因为 ...@@ -173,30 +173,30 @@ Vue 官方建议在 SFC + Composition API 场景使用 `<script setup>`,因为
173 173
174 建议你把前端逻辑按“层”来想(从上到下): 174 建议你把前端逻辑按“层”来想(从上到下):
175 175
176 -1) View(页面) 176 +1. View(页面)
177 - - 只做页面编排:拿数据、传 props、处理路由跳转 177 + - 只做页面编排:拿数据、传 props、处理路由跳转
178 -2) Composable(业务逻辑) 178 +2. Composable(业务逻辑)
179 - - 负责:请求、状态、数据适配、交互流程(可测试) 179 + - 负责:请求、状态、数据适配、交互流程(可测试)
180 -3) Service/API(接口层) 180 +3. Service/API(接口层)
181 - - 负责:请求与返回结构,尽量不处理 UI(不弹 toast) 181 + - 负责:请求与返回结构,尽量不处理 UI(不弹 toast)
182 -4) Utils(纯工具) 182 +4. Utils(纯工具)
183 - - 负责:纯函数、格式化、兼容处理 183 + - 负责:纯函数、格式化、兼容处理
184 184
185 ## 6. 结合本项目:可直接落地的优化清单(按收益排序) 185 ## 6. 结合本项目:可直接落地的优化清单(按收益排序)
186 186
187 -1) 统一 API 返回结构:逐步替换 `fn``request` 187 +1. 统一 API 返回结构:逐步替换 `fn``request`
188 188
189 - 目标:减少大量 `if (res && res.code)` 的防御判断,把错误信息(msg)保留下来 189 - 目标:减少大量 `if (res && res.code)` 的防御判断,把错误信息(msg)保留下来
190 - 涉及文件:[fn.js](file:///Users/huyirui/program/itomix/git/mlaj/src/api/fn.js) 190 - 涉及文件:[fn.js](file:///Users/huyirui/program/itomix/git/mlaj/src/api/fn.js)
191 - 现状:新代码已开始使用 `request` 函数(返回统一结构),但旧的 `fn` 函数(失败返回 `false`)仍被大量使用。建议后续开发中优先使用 `request`,并逐步重构旧代码。 191 - 现状:新代码已开始使用 `request` 函数(返回统一结构),但旧的 `fn` 函数(失败返回 `false`)仍被大量使用。建议后续开发中优先使用 `request`,并逐步重构旧代码。
192 - 风险提示:`qs` 的包名是小写 `qs`,但当前存在 `import qs from 'Qs'` 写法,在大小写敏感环境下可能无法解析(建议统一为 `qs`)。 192 - 风险提示:`qs` 的包名是小写 `qs`,但当前存在 `import qs from 'Qs'` 写法,在大小写敏感环境下可能无法解析(建议统一为 `qs`)。
193 193
194 -2) 从 1 个大页面开始拆 composable:只拆“最独立的一块功能” 194 +2. 从 1 个大页面开始拆 composable:只拆“最独立的一块功能”
195 195
196 - 目标:不追求一次性完美分层,但要让页面先变薄,后续才好迭代与测试 196 - 目标:不追求一次性完美分层,但要让页面先变薄,后续才好迭代与测试
197 - 典型样例:[HomePage.vue](file:///Users/huyirui/program/itomix/git/mlaj/src/views/HomePage.vue)[StudyDetailPage.vue](file:///Users/huyirui/program/itomix/git/mlaj/src/views/study/StudyDetailPage.vue) 197 - 典型样例:[HomePage.vue](file:///Users/huyirui/program/itomix/git/mlaj/src/views/HomePage.vue)[StudyDetailPage.vue](file:///Users/huyirui/program/itomix/git/mlaj/src/views/study/StudyDetailPage.vue)
198 198
199 -3) 固化格式化规则(ESLint/Prettier 选一个),把“风格争议”交给工具。 199 +3. 固化格式化规则(ESLint/Prettier 选一个),把“风格争议”交给工具。
200 200
201 ## 7. 推荐写法示例(以“无 TS、可读性优先”为前提) 201 ## 7. 推荐写法示例(以“无 TS、可读性优先”为前提)
202 202
...@@ -217,13 +217,13 @@ Vue 官方建议在 SFC + Composition API 场景使用 `<script setup>`,因为 ...@@ -217,13 +217,13 @@ Vue 官方建议在 SFC + Composition API 场景使用 `<script setup>`,因为
217 * @param {any} raw - 原始响应 217 * @param {any} raw - 原始响应
218 * @returns {ApiResult} 218 * @returns {ApiResult}
219 */ 219 */
220 -export const normalize_api_result = (raw) => { 220 +export const normalize_api_result = raw => {
221 - const data = raw?.data ?? raw ?? {} 221 + const data = raw?.data ?? raw ?? {}
222 - return { 222 + return {
223 - code: Number(data.code) || 0, 223 + code: Number(data.code) || 0,
224 - data: data.data ?? null, 224 + data: data.data ?? null,
225 - msg: data.msg ?? '' 225 + msg: data.msg ?? '',
226 - } 226 + }
227 } 227 }
228 ``` 228 ```
229 229
...@@ -238,30 +238,30 @@ import { showFailToast } from 'vant' ...@@ -238,30 +238,30 @@ import { showFailToast } from 'vant'
238 * @param {Function} fetch_list_api - 具体接口函数 238 * @param {Function} fetch_list_api - 具体接口函数
239 * @returns {{ loading: import('vue').Ref<boolean>, list: import('vue').Ref<any[]>, load: Function }} 239 * @returns {{ loading: import('vue').Ref<boolean>, list: import('vue').Ref<any[]>, load: Function }}
240 */ 240 */
241 -export const use_list_loader = (fetch_list_api) => { 241 +export const use_list_loader = fetch_list_api => {
242 - const loading = ref(false) 242 + const loading = ref(false)
243 - const list = ref([]) 243 + const list = ref([])
244 - 244 +
245 - const load = async (params) => { 245 + const load = async params => {
246 - loading.value = true 246 + loading.value = true
247 - try { 247 + try {
248 - const res = await fetch_list_api(params) 248 + const res = await fetch_list_api(params)
249 - if (res.code === 1) { 249 + if (res.code === 1) {
250 - list.value = Array.isArray(res.data?.list) ? res.data.list : [] 250 + list.value = Array.isArray(res.data?.list) ? res.data.list : []
251 - return true 251 + return true
252 - } 252 + }
253 - showFailToast(res.msg || '加载失败') 253 + showFailToast(res.msg || '加载失败')
254 - return false 254 + return false
255 - } finally { 255 + } finally {
256 - loading.value = false 256 + loading.value = false
257 - }
258 } 257 }
258 + }
259 259
260 - return { 260 + return {
261 - loading, 261 + loading,
262 - list, 262 + list,
263 - load 263 + load,
264 - } 264 + }
265 } 265 }
266 ``` 266 ```
267 267
...@@ -279,13 +279,13 @@ useTitle('消息列表') ...@@ -279,13 +279,13 @@ useTitle('消息列表')
279 const { loading, list, load } = use_list_loader(getNewsListAPI) 279 const { loading, list, load } = use_list_loader(getNewsListAPI)
280 280
281 onMounted(() => { 281 onMounted(() => {
282 - load({ page: 1, limit: 10 }) 282 + load({ page: 1, limit: 10 })
283 }) 283 })
284 </script> 284 </script>
285 ``` 285 ```
286 286
287 ## 8. 建议你“下一步优先做”的 3 件事(投入产出比最高) 287 ## 8. 建议你“下一步优先做”的 3 件事(投入产出比最高)
288 288
289 -1) 统一 API 返回结构(去掉 `false` 返回),减少上层防御代码与隐性 bug。 289 +1. 统一 API 返回结构(去掉 `false` 返回),减少上层防御代码与隐性 bug。
290 -2) 把 1-2 个大页面拆出 composable(例如 HomePage 中的某一块功能),你会立刻感受到可读性提升。 290 +2. 把 1-2 个大页面拆出 composable(例如 HomePage 中的某一块功能),你会立刻感受到可读性提升。
291 -3) 固化格式化规则(ESLint/Prettier 选一个),把“风格争议”交给工具。 291 +3. 固化格式化规则(ESLint/Prettier 选一个),把“风格争议”交给工具。
......