feat(teacher): 添加学生详情页相关API和功能实现
添加学生详情、打卡统计、作业记录和点评相关API 实现学生详情页数据绑定和交互逻辑 更新学生信息展示和点评状态处理
Showing
2 changed files
with
83 additions
and
29 deletions
| 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 | }) | ... | ... |
-
Please register or login to post a comment