feat(学生页面): 添加作业点评功能
实现作业点评弹窗,包含评分和点评内容输入 支持已有点评的编辑和展示 添加点评状态标识和样式优化
Showing
1 changed file
with
180 additions
and
19 deletions
| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | * @Author: hookehuyr hookehuyr@gmail.com | 2 | * @Author: hookehuyr hookehuyr@gmail.com |
| 3 | * @Date: 2025-06-19 17:12:19 | 3 | * @Date: 2025-06-19 17:12:19 |
| 4 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 4 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 5 | - * @LastEditTime: 2025-06-20 00:11:37 | 5 | + * @LastEditTime: 2025-06-20 09:55:57 |
| 6 | * @FilePath: /mlaj/src/views/teacher/studentPage.vue | 6 | * @FilePath: /mlaj/src/views/teacher/studentPage.vue |
| 7 | * @Description: 学生详情页面 | 7 | * @Description: 学生详情页面 |
| 8 | --> | 8 | --> |
| ... | @@ -43,16 +43,11 @@ | ... | @@ -43,16 +43,11 @@ |
| 43 | <span class="text-sm font-medium text-gray-700">所学课程</span> | 43 | <span class="text-sm font-medium text-gray-700">所学课程</span> |
| 44 | </div> | 44 | </div> |
| 45 | <div class="flex flex-wrap gap-2"> | 45 | <div class="flex flex-wrap gap-2"> |
| 46 | - <van-tag | 46 | + <van-tag v-for="course in studentInfo.courses" :key="course" |
| 47 | - v-for="course in studentInfo.courses" | ||
| 48 | - :key="course" | ||
| 49 | :type="selectedCourses.includes(course) ? 'primary' : 'default'" | 47 | :type="selectedCourses.includes(course) ? 'primary' : 'default'" |
| 50 | :color="selectedCourses.includes(course) ? '#10b981' : '#f0f0f0'" | 48 | :color="selectedCourses.includes(course) ? '#10b981' : '#f0f0f0'" |
| 51 | - :text-color="selectedCourses.includes(course) ? '#ffffff' : '#666666'" | 49 | + :text-color="selectedCourses.includes(course) ? '#ffffff' : '#666666'" size="large" |
| 52 | - size="large" | 50 | + @click="toggleCourseSelection(course)" class="cursor-pointer transition-all duration-200 hover:opacity-80"> |
| 53 | - @click="toggleCourseSelection(course)" | ||
| 54 | - class="cursor-pointer transition-all duration-200 hover:opacity-80" | ||
| 55 | - > | ||
| 56 | {{ course }} | 51 | {{ course }} |
| 57 | </van-tag> | 52 | </van-tag> |
| 58 | </div> | 53 | </div> |
| ... | @@ -106,15 +101,15 @@ | ... | @@ -106,15 +101,15 @@ |
| 106 | </div> | 101 | </div> |
| 107 | 102 | ||
| 108 | <!-- 使用van-sticky包裹van-tabs实现粘性布局 --> | 103 | <!-- 使用van-sticky包裹van-tabs实现粘性布局 --> |
| 109 | - <div class="bg-white" style="margin: 1rem;"> | 104 | + <div class="bg-white" style="margin: 1rem;"> |
| 110 | - <van-sticky :offset-top="0"> | 105 | + <van-sticky :offset-top="0"> |
| 111 | <van-tabs v-model:active="activeTab" color="#10b981" animated swipeable @change="handleTabChange"> | 106 | <van-tabs v-model:active="activeTab" color="#10b981" animated swipeable @change="handleTabChange"> |
| 112 | <van-tab title="作业记录" name="homework"></van-tab> | 107 | <van-tab title="作业记录" name="homework"></van-tab> |
| 113 | <van-tab title="班主任点评" name="evaluation"></van-tab> | 108 | <van-tab title="班主任点评" name="evaluation"></van-tab> |
| 114 | <van-tab title="打卡统计" name="statistics"></van-tab> | 109 | <van-tab title="打卡统计" name="statistics"></van-tab> |
| 115 | </van-tabs> | 110 | </van-tabs> |
| 116 | - </van-sticky> | 111 | + </van-sticky> |
| 117 | - </div> | 112 | + </div> |
| 118 | 113 | ||
| 119 | <!-- 记录列表 --> | 114 | <!-- 记录列表 --> |
| 120 | <van-list v-show="activeTab === 'statistics'" v-model:loading="recordLoading" :finished="recordFinished" | 115 | <van-list v-show="activeTab === 'statistics'" v-model:loading="recordLoading" :finished="recordFinished" |
| ... | @@ -134,7 +129,7 @@ | ... | @@ -134,7 +129,7 @@ |
| 134 | <span class="text-sm text-gray-600">{{ record.time }}</span> | 129 | <span class="text-sm text-gray-600">{{ record.time }}</span> |
| 135 | </div> | 130 | </div> |
| 136 | </div> | 131 | </div> |
| 137 | - | 132 | + |
| 138 | <!-- 右侧:状态 --> | 133 | <!-- 右侧:状态 --> |
| 139 | <div class="flex items-center"> | 134 | <div class="flex items-center"> |
| 140 | <span v-if="record.status === '正常'" class="text-green-600 text-sm mr-2">{{ record.status }}</span> | 135 | <span v-if="record.status === '正常'" class="text-green-600 text-sm mr-2">{{ record.status }}</span> |
| ... | @@ -159,7 +154,7 @@ | ... | @@ -159,7 +154,7 @@ |
| 159 | <van-icon name="calendar-o" size="16" color="#10b981" class="mr-2" /> | 154 | <van-icon name="calendar-o" size="16" color="#10b981" class="mr-2" /> |
| 160 | <span class="text-sm text-gray-600">{{ evaluation.date }}</span> | 155 | <span class="text-sm text-gray-600">{{ evaluation.date }}</span> |
| 161 | </div> | 156 | </div> |
| 162 | - | 157 | + |
| 163 | <!-- 第二行:点评内容 + 图标 --> | 158 | <!-- 第二行:点评内容 + 图标 --> |
| 164 | <div class="flex items-start mb-3"> | 159 | <div class="flex items-start mb-3"> |
| 165 | <van-icon name="chat-o" size="16" color="#3b82f6" class="mr-2 mt-0.5" /> | 160 | <van-icon name="chat-o" size="16" color="#3b82f6" class="mr-2 mt-0.5" /> |
| ... | @@ -167,7 +162,7 @@ | ... | @@ -167,7 +162,7 @@ |
| 167 | <p class="text-gray-800 text-sm leading-relaxed">{{ evaluation.content }}</p> | 162 | <p class="text-gray-800 text-sm leading-relaxed">{{ evaluation.content }}</p> |
| 168 | </div> | 163 | </div> |
| 169 | </div> | 164 | </div> |
| 170 | - | 165 | + |
| 171 | <!-- 第三行:点评分数 + rate评分组件 --> | 166 | <!-- 第三行:点评分数 + rate评分组件 --> |
| 172 | <div class="flex items-center"> | 167 | <div class="flex items-center"> |
| 173 | <van-icon name="star-o" size="16" color="#f59e0b" class="mr-2" /> | 168 | <van-icon name="star-o" size="16" color="#f59e0b" class="mr-2" /> |
| ... | @@ -242,9 +237,25 @@ | ... | @@ -242,9 +237,25 @@ |
| 242 | }" @play="(player) => handleAudioPlay(player, post)" /> | 237 | }" @play="(player) => handleAudioPlay(player, post)" /> |
| 243 | </div> | 238 | </div> |
| 244 | </div> | 239 | </div> |
| 245 | - <div class="post-footer"> | 240 | + <div class="post-footer flex items-center justify-between"> |
| 246 | - <van-icon @click="handLike(post)" name="good-job" class="like-icon" :color="post.is_liked ? 'red' : ''" /> | 241 | + <!-- 左侧:点赞 --> |
| 247 | - <span class="like-count">{{ post.likes }}</span> | 242 | + <div class="flex items-center"> |
| 243 | + <van-icon @click="handLike(post)" name="good-job" class="like-icon" :color="post.is_liked ? 'red' : ''" /> | ||
| 244 | + <span class="like-count ml-1">{{ post.likes }}</span> | ||
| 245 | + </div> | ||
| 246 | + | ||
| 247 | + <!-- 右侧:点评 --> | ||
| 248 | + <div class="flex items-center cursor-pointer" @click="openCommentPopup(post)"> | ||
| 249 | + <van-icon | ||
| 250 | + name="comment-o" | ||
| 251 | + :color="post.is_commented ? '#10b981' : '#999'" | ||
| 252 | + size="18" | ||
| 253 | + class="mr-1" | ||
| 254 | + /> | ||
| 255 | + <span class="text-sm" :class="post.is_commented ? 'text-green-600' : 'text-gray-500'"> | ||
| 256 | + {{ post.is_commented ? '已点评' : '点评' }} | ||
| 257 | + </span> | ||
| 258 | + </div> | ||
| 248 | </div> | 259 | </div> |
| 249 | </div> | 260 | </div> |
| 250 | </van-list> | 261 | </van-list> |
| ... | @@ -269,6 +280,55 @@ | ... | @@ -269,6 +280,55 @@ |
| 269 | </div> | 280 | </div> |
| 270 | </div> | 281 | </div> |
| 271 | </van-popup> | 282 | </van-popup> |
| 283 | + | ||
| 284 | + <!-- 点评弹窗 --> | ||
| 285 | + <van-popup v-model:show="showCommentPopup" position="bottom" round class="comment-popup"> | ||
| 286 | + <div class="p-6 w-100"> | ||
| 287 | + <div class="text-center text-lg font-bold mb-4">作业点评</div> | ||
| 288 | + | ||
| 289 | + <!-- 评分 --> | ||
| 290 | + <div class="mb-4"> | ||
| 291 | + <div class="text-sm text-gray-600 mb-2">评分</div> | ||
| 292 | + <van-rate v-model="commentForm.rating" :size="24" color="#ffd21e" void-color="#eee" /> | ||
| 293 | + </div> | ||
| 294 | + | ||
| 295 | + <!-- 点评内容 --> | ||
| 296 | + <div class="mb-6"> | ||
| 297 | + <div class="text-sm text-gray-600 mb-2">点评内容</div> | ||
| 298 | + <van-field | ||
| 299 | + v-model="commentForm.content" | ||
| 300 | + type="textarea" | ||
| 301 | + placeholder="请输入点评内容..." | ||
| 302 | + rows="4" | ||
| 303 | + maxlength="200" | ||
| 304 | + show-word-limit | ||
| 305 | + :border="false" | ||
| 306 | + class="bg-gray-50 rounded-lg" | ||
| 307 | + /> | ||
| 308 | + </div> | ||
| 309 | + | ||
| 310 | + <!-- 操作按钮 --> | ||
| 311 | + <div class="flex gap-3"> | ||
| 312 | + <van-button | ||
| 313 | + block | ||
| 314 | + type="default" | ||
| 315 | + @click="closeCommentPopup" | ||
| 316 | + class="flex-1" | ||
| 317 | + > | ||
| 318 | + 取消 | ||
| 319 | + </van-button> | ||
| 320 | + <van-button | ||
| 321 | + block | ||
| 322 | + type="primary" | ||
| 323 | + @click="submitComment" | ||
| 324 | + class="flex-1" | ||
| 325 | + :disabled="!commentForm.content.trim()" | ||
| 326 | + > | ||
| 327 | + 提交 | ||
| 328 | + </van-button> | ||
| 329 | + </div> | ||
| 330 | + </div> | ||
| 331 | + </van-popup> | ||
| 272 | </div> | 332 | </div> |
| 273 | </van-config-provider> | 333 | </van-config-provider> |
| 274 | </template> | 334 | </template> |
| ... | @@ -322,6 +382,83 @@ const activeTab = ref('homework') | ... | @@ -322,6 +382,83 @@ const activeTab = ref('homework') |
| 322 | const statusFilter = ref('按状态') | 382 | const statusFilter = ref('按状态') |
| 323 | const showStatusPopup = ref(false) | 383 | const showStatusPopup = ref(false) |
| 324 | 384 | ||
| 385 | +// 点评相关 | ||
| 386 | +const showCommentPopup = ref(false) | ||
| 387 | +const currentCommentPost = ref(null) | ||
| 388 | +const commentForm = ref({ | ||
| 389 | + rating: 0, | ||
| 390 | + content: '' | ||
| 391 | +}) | ||
| 392 | + | ||
| 393 | +/** | ||
| 394 | + * 打开点评弹窗 | ||
| 395 | + * @param {Object} post - 作业帖子对象 | ||
| 396 | + */ | ||
| 397 | +const openCommentPopup = (post) => { | ||
| 398 | + currentCommentPost.value = post | ||
| 399 | + // 如果已有点评,填充表单 | ||
| 400 | + if (post.comment) { | ||
| 401 | + commentForm.value.rating = post.comment.rating || 0 | ||
| 402 | + commentForm.value.content = post.comment.content || '' | ||
| 403 | + } else { | ||
| 404 | + // 重置表单 | ||
| 405 | + commentForm.value.rating = 0 | ||
| 406 | + commentForm.value.content = '' | ||
| 407 | + } | ||
| 408 | + showCommentPopup.value = true | ||
| 409 | +} | ||
| 410 | + | ||
| 411 | +/** | ||
| 412 | + * 关闭点评弹窗 | ||
| 413 | + */ | ||
| 414 | +const closeCommentPopup = () => { | ||
| 415 | + showCommentPopup.value = false | ||
| 416 | + currentCommentPost.value = null | ||
| 417 | + commentForm.value.rating = 0 | ||
| 418 | + commentForm.value.content = '' | ||
| 419 | +} | ||
| 420 | + | ||
| 421 | +/** | ||
| 422 | + * 提交点评 | ||
| 423 | + */ | ||
| 424 | +const submitComment = async () => { | ||
| 425 | + if (!commentForm.value.content.trim()) { | ||
| 426 | + showFailToast('请输入点评内容') | ||
| 427 | + return | ||
| 428 | + } | ||
| 429 | + | ||
| 430 | + if (commentForm.value.rating === 0) { | ||
| 431 | + showFailToast('请选择评分') | ||
| 432 | + return | ||
| 433 | + } | ||
| 434 | + | ||
| 435 | + try { | ||
| 436 | + showLoadingToast('提交中...') | ||
| 437 | + | ||
| 438 | + // 这里应该调用API提交点评 | ||
| 439 | + // await submitCommentAPI(currentCommentPost.value.id, commentForm.value) | ||
| 440 | + | ||
| 441 | + // 模拟API调用 | ||
| 442 | + await new Promise(resolve => setTimeout(resolve, 1000)) | ||
| 443 | + | ||
| 444 | + // 更新本地数据 | ||
| 445 | + if (currentCommentPost.value) { | ||
| 446 | + currentCommentPost.value.is_commented = true | ||
| 447 | + currentCommentPost.value.comment = { | ||
| 448 | + rating: commentForm.value.rating, | ||
| 449 | + content: commentForm.value.content, | ||
| 450 | + created_at: new Date().toISOString() | ||
| 451 | + } | ||
| 452 | + } | ||
| 453 | + | ||
| 454 | + showSuccessToast('点评提交成功') | ||
| 455 | + closeCommentPopup() | ||
| 456 | + } catch (error) { | ||
| 457 | + console.error('提交点评失败:', error) | ||
| 458 | + showFailToast('提交失败,请重试') | ||
| 459 | + } | ||
| 460 | +} | ||
| 461 | + | ||
| 325 | /** | 462 | /** |
| 326 | * 切换课程选中状态(单选模式) | 463 | * 切换课程选中状态(单选模式) |
| 327 | * @param {string} course - 课程名称 | 464 | * @param {string} course - 课程名称 |
| ... | @@ -786,6 +923,8 @@ const formatData = (data) => { | ... | @@ -786,6 +923,8 @@ const formatData = (data) => { |
| 786 | is_liked: item.is_like, | 923 | is_liked: item.is_like, |
| 787 | is_my: item.is_my, | 924 | is_my: item.is_my, |
| 788 | file_type: item.file_type, | 925 | file_type: item.file_type, |
| 926 | + is_commented: item.is_commented || false, // 是否已点评 | ||
| 927 | + comment: item.comment || null, // 点评内容 | ||
| 789 | } | 928 | } |
| 790 | }) | 929 | }) |
| 791 | return formattedData; | 930 | return formattedData; |
| ... | @@ -887,4 +1026,26 @@ const formatData = (data) => { | ... | @@ -887,4 +1026,26 @@ const formatData = (data) => { |
| 887 | } | 1026 | } |
| 888 | } | 1027 | } |
| 889 | } | 1028 | } |
| 1029 | + | ||
| 1030 | +/* 点评弹窗样式 */ | ||
| 1031 | +.comment-popup { | ||
| 1032 | + .van-popup { | ||
| 1033 | + max-width: 90vw; | ||
| 1034 | + } | ||
| 1035 | + | ||
| 1036 | + .van-rate { | ||
| 1037 | + display: flex; | ||
| 1038 | + justify-content: center; | ||
| 1039 | + } | ||
| 1040 | + | ||
| 1041 | + .van-field { | ||
| 1042 | + padding: 12px; | ||
| 1043 | + border-radius: 8px; | ||
| 1044 | + } | ||
| 1045 | + | ||
| 1046 | + .van-button { | ||
| 1047 | + height: 44px; | ||
| 1048 | + border-radius: 8px; | ||
| 1049 | + } | ||
| 1050 | +} | ||
| 890 | </style> | 1051 | </style> | ... | ... |
-
Please register or login to post a comment