hookehuyr

feat(学生页面): 添加作业点评功能

实现作业点评弹窗,包含评分和点评内容输入
支持已有点评的编辑和展示
添加点评状态标识和样式优化
...@@ -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>
......