hookehuyr

feat(teacher): 添加学生详情页相关API和功能实现

添加学生详情、打卡统计、作业记录和点评相关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-26 10:35:45 4 + * @LastEditTime: 2025-06-26 16:15:15
5 * @FilePath: /mlaj/src/api/teacher.js 5 * @FilePath: /mlaj/src/api/teacher.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
...@@ -12,6 +12,12 @@ const Api = { ...@@ -12,6 +12,12 @@ const Api = {
12 TEACHER_FIND_SETTINGS: '/srv/?a=task&t=teacher_find_settings', 12 TEACHER_FIND_SETTINGS: '/srv/?a=task&t=teacher_find_settings',
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',
16 + STUDENT_CHECKIN_LIST: '/srv/?a=checkin&t=student_checkin_list',
17 + STUDENT_UPLOAD_LIST: '/srv/?a=checkin&t=student_upload_list',
18 + STUDENT_CHECKIN_FEEDBACK_LIST: '/srv/?a=checkin_feedback&t=list',
19 + ADD_CHECKIN_FEEDBACK: '/srv/?a=checkin_feedback&t=add',
20 + DEL_CHECKIN_FEEDBACK: '/srv/?a=checkin_feedback&t=del',
15 } 21 }
16 22
17 /** 23 /**
...@@ -55,3 +61,56 @@ export const setTeacherTaskAPI = (params) => fn(fetch.post(Api.TEACHER_ADD_TASK, ...@@ -55,3 +61,56 @@ export const setTeacherTaskAPI = (params) => fn(fetch.post(Api.TEACHER_ADD_TASK,
55 * @returns {Object} data { count, user_list[{id, name, avatar, mobile, class_list[{id, class_name}], last_checkin_time, last_checkin_time_desc}] } 61 * @returns {Object} data { count, user_list[{id, name, avatar, mobile, class_list[{id, class_name}], last_checkin_time, last_checkin_time_desc}] }
56 */ 62 */
57 export const getStudentListAPI = (params) => fn(fetch.get(Api.STUDENT_LIST, params)) 63 export const getStudentListAPI = (params) => fn(fetch.get(Api.STUDENT_LIST, params))
64 +
65 +/**
66 + * 获取学员详情
67 + * @param {*} i 学员ID
68 + * @returns {Object} data { id, name, avatar, mobile, grade_list[{id, grade_name, class_list[{id, class_name}]}], lesson_list[{id, title}] }
69 + */
70 +export const getStudentDetailAPI = (params) => fn(fetch.get(Api.STUDENT_DETAIL, params))
71 +
72 +/**
73 + * 获取学员打卡统计
74 + * @param {*} user_id 学员ID
75 + * @param {*} group_id 课程ID
76 + * @param {*} limit 条数
77 + * @param {*} page 页码
78 + * @returns {Object} data { date, time, serial_number, status }
79 + */
80 +export const getStudentCheckinListAPI = (params) => fn(fetch.get(Api.STUDENT_CHECKIN_LIST, params))
81 +
82 +/**
83 + * 获取学员作业记录
84 + * @param {*} user_id 学员ID
85 + * @param {*} group_id 课程ID
86 + * @param {*} limit 条数
87 + * @param {*} page 页码
88 + * @returns {Object} data
89 + */
90 +export const getStudentUploadListAPI = (params) => fn(fetch.get(Api.STUDENT_UPLOAD_LIST, params))
91 +
92 +/**
93 + * 获取老师点评列表
94 + * @param {*} user_id 学员ID
95 + * @param {*} group_id 课程ID
96 + * @param {*} limit 条数
97 + * @param {*} page 页码
98 + * @returns {Object} data
99 + */
100 +export const getCheckinFeedbackListAPI = (params) => fn(fetch.get(Api.STUDENT_CHECKIN_FEEDBACK_LIST, params))
101 +
102 +/**
103 + * 老师点评
104 + * @param {*} checkin_id 打卡ID
105 + * @param {*} note 点评内容
106 + * @param {*} rate 点评分数
107 + * @returns {Object} data { id }
108 + */
109 +export const addCheckinFeedbackAPI = (params) => fn(fetch.post(Api.ADD_CHECKIN_FEEDBACK, params))
110 +
111 +/**
112 + * 老师删除点评
113 + * @param {*} id 点评ID
114 + * @returns {Object} data { id }
115 + */
116 +export const delCheckinFeedbackAPI = (params) => fn(fetch.post(Api.DEL_CHECKIN_FEEDBACK, 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-26 11:46:50 5 + * @LastEditTime: 2025-06-26 16:36:01
6 * @FilePath: /mlaj/src/views/teacher/studentPage.vue 6 * @FilePath: /mlaj/src/views/teacher/studentPage.vue
7 * @Description: 学生详情页面 7 * @Description: 学生详情页面
8 --> 8 -->
...@@ -17,19 +17,19 @@ ...@@ -17,19 +17,19 @@
17 <div class="flex-1"> 17 <div class="flex-1">
18 <div class="flex items-center mb-2"> 18 <div class="flex items-center mb-2">
19 <h2 class="text-xl font-bold text-gray-800 mr-2">{{ studentInfo.name }}</h2> 19 <h2 class="text-xl font-bold text-gray-800 mr-2">{{ studentInfo.name }}</h2>
20 - <font-awesome-icon v-if="studentInfo.gender === 'male'" icon="venus" color="#3b82f6" class="mr-2" style="font-size: 0.85rem;" /> 20 + <!-- <font-awesome-icon v-if="studentInfo.gender === 'male'" icon="venus" color="#3b82f6" class="mr-2" style="font-size: 0.85rem;" />
21 - <font-awesome-icon v-else icon="mars" color="#ec4899" class="mr-2" style="font-size: 0.85rem;" /> 21 + <font-awesome-icon v-else icon="mars" color="#ec4899" class="mr-2" style="font-size: 0.85rem;" /> -->
22 </div> 22 </div>
23 <div class="flex items-center mb-2"> 23 <div class="flex items-center mb-2">
24 <van-icon name="chat-o" size="16" color="#10b981" class="mr-1" /> 24 <van-icon name="chat-o" size="16" color="#10b981" class="mr-1" />
25 - <span class="text-sm text-gray-600 mr-4">{{ studentInfo.className }}</span> 25 + <span class="text-sm text-gray-600 mr-4" v-for="(item, index) in studentInfo.class_list" :key="index">{{ item.class_name }}</span>
26 <van-icon name="phone-o" size="16" color="#10b981" class="mr-1" /> 26 <van-icon name="phone-o" size="16" color="#10b981" class="mr-1" />
27 - <span class="text-sm text-gray-600">{{ formatPhone(studentInfo.phone) }}</span> 27 + <span class="text-sm text-gray-600">{{ formatPhone(studentInfo.mobile) }}</span>
28 </div> 28 </div>
29 <!-- 标签 --> 29 <!-- 标签 -->
30 <div class="flex flex-wrap gap-2"> 30 <div class="flex flex-wrap gap-2">
31 - <van-tag v-for="tag in studentInfo.tags" :key="tag" type="success" size="large" plain> 31 + <van-tag v-for="grade in studentInfo.grade_list" :key="grade.id" type="success" size="large" plain>
32 - {{ tag }} 32 + {{ grade.grade_name }}
33 </van-tag> 33 </van-tag>
34 </div> 34 </div>
35 </div> 35 </div>
...@@ -43,12 +43,12 @@ ...@@ -43,12 +43,12 @@
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 v-for="course in studentInfo.courses" :key="course" 46 + <van-tag v-for="course in studentInfo.lesson_list" :key="course.id"
47 :type="selectedCourses.includes(course) ? 'primary' : 'default'" 47 :type="selectedCourses.includes(course) ? 'primary' : 'default'"
48 :color="selectedCourses.includes(course) ? '#10b981' : '#f0f0f0'" 48 :color="selectedCourses.includes(course) ? '#10b981' : '#f0f0f0'"
49 :text-color="selectedCourses.includes(course) ? '#ffffff' : '#666666'" size="large" 49 :text-color="selectedCourses.includes(course) ? '#ffffff' : '#666666'" size="large"
50 @click="toggleCourseSelection(course)" class="cursor-pointer transition-all duration-200 hover:opacity-80"> 50 @click="toggleCourseSelection(course)" class="cursor-pointer transition-all duration-200 hover:opacity-80">
51 - {{ course }} 51 + {{ course.title }}
52 </van-tag> 52 </van-tag>
53 </div> 53 </div>
54 </div> 54 </div>
...@@ -256,13 +256,13 @@ ...@@ -256,13 +256,13 @@
256 <div class="flex items-center cursor-pointer" @click="openCommentPopup(post)"> 256 <div class="flex items-center cursor-pointer" @click="openCommentPopup(post)">
257 <van-icon 257 <van-icon
258 name="comment-o" 258 name="comment-o"
259 - :color="post.is_commented ? '#10b981' : '#999'" 259 + :color="post.is_feedback ? '#10b981' : '#999'"
260 size="19" 260 size="19"
261 class="mr-1" 261 class="mr-1"
262 style="margin-top: 0.2rem;" 262 style="margin-top: 0.2rem;"
263 /> 263 />
264 - <span class="text-sm" :class="post.is_commented ? 'text-green-600' : 'text-gray-500'"> 264 + <span class="text-sm" :class="post.is_feedback ? 'text-green-600' : 'text-gray-500'">
265 - {{ post.is_commented ? '已点评' : '点评' }} 265 + {{ post.is_feedback ? '已点评' : '待点评' }}
266 </span> 266 </span>
267 </div> 267 </div>
268 </div> 268 </div>
...@@ -369,6 +369,7 @@ import { useTitle } from '@vueuse/core'; ...@@ -369,6 +369,7 @@ import { useTitle } from '@vueuse/core';
369 import dayjs from 'dayjs'; 369 import dayjs from 'dayjs';
370 370
371 import { getCheckinTeacherListAPI, delUploadTaskInfoAPI, likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI } from "@/api/checkin"; 371 import { getCheckinTeacherListAPI, delUploadTaskInfoAPI, likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI } from "@/api/checkin";
372 +import { getStudentDetailAPI, getStudentCheckinListAPI, getStudentUploadListAPI, getCheckinFeedbackListAPI, addCheckinFeedbackAPI, delCheckinFeedbackAPI } from "@/api/teacher";
372 373
373 374
374 const router = useRouter() 375 const router = useRouter()
...@@ -380,19 +381,10 @@ const themeVars = reactive({ ...@@ -380,19 +381,10 @@ const themeVars = reactive({
380 }) 381 })
381 382
382 // 学生信息 383 // 学生信息
383 -const studentInfo = ref({ 384 +const studentInfo = ref({})
384 - id: 1,
385 - name: '李华',
386 - gender: 'female',
387 - className: '高一(3)班',
388 - phone: '13812345678',
389 - avatar: 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg',
390 - tags: ['亲子学堂2025级'],
391 - courses: ['讲师训2025', '亲子学堂课程']
392 -})
393 385
394 // 选中的课程列表(默认选中第一个课程) 386 // 选中的课程列表(默认选中第一个课程)
395 -const selectedCourses = ref([studentInfo.value.courses[0] || '']) 387 +const selectedCourses = ref([])
396 388
397 // 统计数据 389 // 统计数据
398 const studentStats = ref({ 390 const studentStats = ref({
...@@ -469,7 +461,7 @@ const submitComment = async () => { ...@@ -469,7 +461,7 @@ const submitComment = async () => {
469 461
470 // 更新本地数据 462 // 更新本地数据
471 if (currentCommentPost.value) { 463 if (currentCommentPost.value) {
472 - currentCommentPost.value.is_commented = true 464 + currentCommentPost.value.is_feedback = true
473 currentCommentPost.value.comment = { 465 currentCommentPost.value.comment = {
474 rating: commentForm.value.rating, 466 rating: commentForm.value.rating,
475 content: commentForm.value.content, 467 content: commentForm.value.content,
...@@ -705,9 +697,12 @@ onMounted(async () => { ...@@ -705,9 +697,12 @@ onMounted(async () => {
705 * 加载学生数据 697 * 加载学生数据
706 * @param {string} studentId - 学生ID 698 * @param {string} studentId - 学生ID
707 */ 699 */
708 -const loadStudentData = (studentId) => { 700 +const loadStudentData = async (studentId) => {
709 - // 这里可以调用API获取实际数据 701 + const { code, data } = await getStudentDetailAPI({ i: studentId })
710 - console.log('加载学生数据:', studentId) 702 + if (code) {
703 + studentInfo.value = data;
704 + selectedCourses.value = [studentInfo.value.lesson_list[0] || '']
705 + }
711 } 706 }
712 707
713 // 处理标签页切换 708 // 处理标签页切换
...@@ -985,7 +980,7 @@ const formatData = (data) => { ...@@ -985,7 +980,7 @@ const formatData = (data) => {
985 is_liked: item.is_like, 980 is_liked: item.is_like,
986 is_my: item.is_my, 981 is_my: item.is_my,
987 file_type: item.file_type, 982 file_type: item.file_type,
988 - is_commented: item.is_commented || false, // 是否已点评 983 + is_feedback: item.is_feedback || false, // 是否已点评
989 comment: item.comment || null, // 点评内容 984 comment: item.comment || null, // 点评内容
990 } 985 }
991 }) 986 })
......