hookehuyr

feat(teacher): 添加学员学习数据统计功能

添加新的API接口和学生详情页面的统计数据显示功能
移除测试成绩显示并调整出勤率和作业完成率的布局
1 /* 1 /*
2 * @Date: 2025-06-23 11:46:21 2 * @Date: 2025-06-23 11:46:21
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-06-27 09:55:12 4 + * @LastEditTime: 2025-06-27 14:04:05
5 * @FilePath: /mlaj/src/api/teacher.js 5 * @FilePath: /mlaj/src/api/teacher.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
...@@ -13,6 +13,7 @@ const Api = { ...@@ -13,6 +13,7 @@ const Api = {
13 TEACHER_ADD_TASK: '/srv/?a=task&t=teacher_add', 13 TEACHER_ADD_TASK: '/srv/?a=task&t=teacher_add',
14 STUDENT_LIST: '/srv/?a=user&t=student_list', 14 STUDENT_LIST: '/srv/?a=user&t=student_list',
15 STUDENT_DETAIL: '/srv/?a=user&t=student_detail', 15 STUDENT_DETAIL: '/srv/?a=user&t=student_detail',
16 + STUDENT_STAT: '/srv/?a=user&t=student_stat',
16 STUDENT_CHECKIN_LIST: '/srv/?a=checkin&t=student_checkin_list', 17 STUDENT_CHECKIN_LIST: '/srv/?a=checkin&t=student_checkin_list',
17 STUDENT_UPLOAD_LIST: '/srv/?a=checkin&t=student_upload_list', 18 STUDENT_UPLOAD_LIST: '/srv/?a=checkin&t=student_upload_list',
18 STUDENT_CHECKIN_FEEDBACK_LIST: '/srv/?a=feedback&t=list', 19 STUDENT_CHECKIN_FEEDBACK_LIST: '/srv/?a=feedback&t=list',
...@@ -114,3 +115,11 @@ export const addCheckinFeedbackAPI = (params) => fn(fetch.post(Api.ADD_CHECKIN_F ...@@ -114,3 +115,11 @@ export const addCheckinFeedbackAPI = (params) => fn(fetch.post(Api.ADD_CHECKIN_F
114 * @returns {Object} data { id } 115 * @returns {Object} data { id }
115 */ 116 */
116 export const delCheckinFeedbackAPI = (params) => fn(fetch.post(Api.DEL_CHECKIN_FEEDBACK, params)) 117 export const delCheckinFeedbackAPI = (params) => fn(fetch.post(Api.DEL_CHECKIN_FEEDBACK, params))
118 +
119 +/**
120 + * 学员学习数据
121 + * @param {*} i 学员ID
122 + * @param {*} group_id 课程ID
123 + * @returns {Object} data { need_checkin_count, real_checkin_count, need_upload_count, real_upload_count }
124 + */
125 +export const getStudentStatAPI = (params) => fn(fetch.post(Api.STUDENT_STAT, params))
......
...@@ -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-27 10:49:01 5 + * @LastEditTime: 2025-06-27 14:39:13
6 * @FilePath: /mlaj/src/views/teacher/studentPage.vue 6 * @FilePath: /mlaj/src/views/teacher/studentPage.vue
7 * @Description: 学生详情页面 7 * @Description: 学生详情页面
8 --> 8 -->
...@@ -57,35 +57,35 @@ ...@@ -57,35 +57,35 @@
57 <div class="mt-2"> 57 <div class="mt-2">
58 <van-row> 58 <van-row>
59 <!-- 出勤率 --> 59 <!-- 出勤率 -->
60 - <van-col span="8"> 60 + <van-col span="12">
61 <div class="bg-white p-4 text-center"> 61 <div class="bg-white p-4 text-center">
62 <div class="relative w-16 h-16 mx-auto mb-2"> 62 <div class="relative w-16 h-16 mx-auto mb-2">
63 - <van-circle v-model:current-rate="studentStats.attendanceRate" :rate="studentStats.attendanceRate" 63 + <van-circle v-model:current-rate="checkinCount" :rate="checkinCount"
64 - :speed="100" :text="studentStats.attendanceRate + '%'" stroke-width="70" color="#10b981" size="64" /> 64 + :text="`${checkinCountText}`" stroke-width="70" color="#10b981" size="64" />
65 </div> 65 </div>
66 <div class="text-sm text-gray-600">出勤率</div> 66 <div class="text-sm text-gray-600">出勤率</div>
67 </div> 67 </div>
68 </van-col> 68 </van-col>
69 <!-- 作业完成率 --> 69 <!-- 作业完成率 -->
70 - <van-col span="8"> 70 + <van-col span="12">
71 <div class="bg-white p-4 text-center"> 71 <div class="bg-white p-4 text-center">
72 <div class="relative w-16 h-16 mx-auto mb-2"> 72 <div class="relative w-16 h-16 mx-auto mb-2">
73 - <van-circle v-model:current-rate="studentStats.homeworkRate" :rate="studentStats.homeworkRate" 73 + <van-circle v-model:current-rate="uploadCount" :rate="uploadCount"
74 - :speed="100" :text="studentStats.homeworkRate + '%'" stroke-width="70" color="#3b82f6" size="64" /> 74 + :text="`${uploadCountText}`" stroke-width="70" color="#3b82f6" size="64" />
75 </div> 75 </div>
76 <div class="text-sm text-gray-600">作业完成率</div> 76 <div class="text-sm text-gray-600">作业完成率</div>
77 </div> 77 </div>
78 </van-col> 78 </van-col>
79 <!-- 测验成绩 --> 79 <!-- 测验成绩 -->
80 - <van-col span="8"> 80 + <!-- <van-col span="8">
81 <div class="bg-white p-4 text-center"> 81 <div class="bg-white p-4 text-center">
82 <div class="relative w-16 h-16 mx-auto mb-2"> 82 <div class="relative w-16 h-16 mx-auto mb-2">
83 <van-circle v-model:current-rate="studentStats.testScore" :rate="studentStats.testScore" :speed="100" 83 <van-circle v-model:current-rate="studentStats.testScore" :rate="studentStats.testScore" :speed="100"
84 - :text="studentStats.testScore + '%'" stroke-width="70" color="#f59e0b" size="64" /> 84 + :text="`${studentStats.testScore}%`" stroke-width="70" color="#f59e0b" size="64" />
85 </div> 85 </div>
86 <div class="text-sm text-gray-600">测验成绩</div> 86 <div class="text-sm text-gray-600">测验成绩</div>
87 </div> 87 </div>
88 - </van-col> 88 + </van-col> -->
89 </van-row> 89 </van-row>
90 </div> 90 </div>
91 91
...@@ -381,7 +381,7 @@ import { useTitle } from '@vueuse/core'; ...@@ -381,7 +381,7 @@ import { useTitle } from '@vueuse/core';
381 import dayjs from 'dayjs'; 381 import dayjs from 'dayjs';
382 382
383 import { getCheckinTeacherListAPI, delUploadTaskInfoAPI, likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI } from "@/api/checkin"; 383 import { getCheckinTeacherListAPI, delUploadTaskInfoAPI, likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI } from "@/api/checkin";
384 -import { getStudentDetailAPI, getStudentCheckinListAPI, getStudentUploadListAPI, getCheckinFeedbackListAPI, addCheckinFeedbackAPI, delCheckinFeedbackAPI } from "@/api/teacher"; 384 +import { getStudentDetailAPI, getStudentCheckinListAPI, getStudentUploadListAPI, getCheckinFeedbackListAPI, addCheckinFeedbackAPI, delCheckinFeedbackAPI, getStudentStatAPI } from "@/api/teacher";
385 385
386 386
387 const router = useRouter() 387 const router = useRouter()
...@@ -398,13 +398,6 @@ const studentInfo = ref({}) ...@@ -398,13 +398,6 @@ const studentInfo = ref({})
398 // 选中的课程列表(默认选中第一个课程) 398 // 选中的课程列表(默认选中第一个课程)
399 const selectedCourses = ref([]) 399 const selectedCourses = ref([])
400 400
401 -// 统计数据
402 -const studentStats = ref({
403 - attendanceRate: 92,
404 - homeworkRate: 88,
405 - testScore: 85
406 -})
407 -
408 // 当前选中的标签页 401 // 当前选中的标签页
409 const activeTab = ref('homework') 402 const activeTab = ref('homework')
410 403
...@@ -517,6 +510,8 @@ const toggleCourseSelection = (course) => { ...@@ -517,6 +510,8 @@ const toggleCourseSelection = (course) => {
517 resetAndReload() 510 resetAndReload()
518 resetAndReloadRecords() 511 resetAndReloadRecords()
519 resetAndReloadEvaluations() 512 resetAndReloadEvaluations()
513 + // 重新获取统计数据以匹配当前选中的课程
514 + getStatList()
520 } 515 }
521 516
522 // 状态选项 517 // 状态选项
...@@ -678,15 +673,18 @@ onMounted(async () => { ...@@ -678,15 +673,18 @@ onMounted(async () => {
678 673
679 // 这里可以根据studentId调用API获取学生详细信息 674 // 这里可以根据studentId调用API获取学生详细信息
680 await loadStudentData(studentId) 675 await loadStudentData(studentId)
676 + // 加载统计数据
677 + await getStatList()
681 678
682 // 加载作业记录 679 // 加载作业记录
683 - onLoad() 680 + await onLoad()
684 681
685 // 加载签到记录 682 // 加载签到记录
686 - onRecordLoad() 683 + await onRecordLoad()
687 684
688 // 加载班主任点评 685 // 加载班主任点评
689 - onEvaluationLoad() 686 + await onEvaluationLoad()
687 +
690 }) 688 })
691 689
692 /** 690 /**
...@@ -1026,6 +1024,28 @@ const resetAndReloadEvaluations = () => { ...@@ -1026,6 +1024,28 @@ const resetAndReloadEvaluations = () => {
1026 evaluationLoading.value = true; 1024 evaluationLoading.value = true;
1027 onEvaluationLoad(); 1025 onEvaluationLoad();
1028 } 1026 }
1027 +
1028 +// 统计数据
1029 +const checkinCount = ref(0);
1030 +const checkinCountText = computed(() => checkinCount.value.toFixed(1) + '%');
1031 +const uploadCount = ref(0);
1032 +const uploadCountText = computed(() => uploadCount.value.toFixed(1) + '%');
1033 +
1034 +const getStatList = async () => {
1035 + try {
1036 + const { code, data } = await getStudentStatAPI({
1037 + i: route.params.id,
1038 + group_id: selectedCourses.value.length ? selectedCourses.value[0]['id'] : '',
1039 + })
1040 +
1041 + if (code) {
1042 + checkinCount.value = data.real_checkin_count/data.need_checkin_count * 100;
1043 + uploadCount.value = data.real_upload_count/data.need_upload_count * 100;
1044 + }
1045 + } catch (error) {
1046 + console.error('获取统计数据失败:', error);
1047 + }
1048 +}
1029 </script> 1049 </script>
1030 1050
1031 <style lang="less"> 1051 <style lang="less">
......