hookehuyr

feat(teacher): 添加教师打卡列表页面功能

- 新增教师年级、班级、课程列表API接口
- 实现教师打卡动态列表功能,支持年级、班级、课程筛选
- 优化教师页面菜单显示逻辑,根据用户角色显示不同菜单项
- 移除点评相关代码,改用审核状态判断
1 /* 1 /*
2 * @Date: 2025-06-06 09:26:16 2 * @Date: 2025-06-06 09:26:16
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-06-06 18:12:47 4 + * @LastEditTime: 2025-06-25 11:26:10
5 * @FilePath: /mlaj/src/api/checkin.js 5 * @FilePath: /mlaj/src/api/checkin.js
6 * @Description: 签到模块相关接口 6 * @Description: 签到模块相关接口
7 */ 7 */
...@@ -18,6 +18,7 @@ const Api = { ...@@ -18,6 +18,7 @@ const Api = {
18 TASK_UPLOAD_DEL: '/srv/?a=checkin&t=upload_del', 18 TASK_UPLOAD_DEL: '/srv/?a=checkin&t=upload_del',
19 TASK_UPLOAD_LIKE: '/srv/?a=checkin&t=like', 19 TASK_UPLOAD_LIKE: '/srv/?a=checkin&t=like',
20 TASK_UPLOAD_DISLIKE: '/srv/?a=checkin&t=dislike', 20 TASK_UPLOAD_DISLIKE: '/srv/?a=checkin&t=dislike',
21 + CHECKIN_TEACHER_LIST: '/srv/?a=checkin&t=teacher_list',
21 } 22 }
22 23
23 /** 24 /**
...@@ -102,3 +103,17 @@ export const likeUploadTaskInfoAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_ ...@@ -102,3 +103,17 @@ export const likeUploadTaskInfoAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_
102 * @returns 103 * @returns
103 */ 104 */
104 export const dislikeUploadTaskInfoAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_DISLIKE, params)) 105 export const dislikeUploadTaskInfoAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_DISLIKE, params))
106 +
107 +/**
108 + * @description: 老师查看打卡动态列表
109 + * @param grade_id 年级ID
110 + * @param class_id 班级ID
111 + * @param group_id 课程ID
112 + * @param date 日期
113 + * @param keyword 搜索
114 + * @param order_by_time asc=正序,desc=倒序。默认为倒序
115 + * @param limit
116 + * @param offset
117 + * @returns
118 + */
119 +export const getCheckinTeacherListAPI = (params) => fn(fetch.get(Api.CHECKIN_TEACHER_LIST, params))
......
1 +/*
2 + * @Date: 2025-06-23 11:46:21
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-06-25 10:51:28
5 + * @FilePath: /mlaj/src/api/teacher.js
6 + * @Description: 文件描述
7 + */
8 +import { fn, fetch } from './fn'
9 +
10 +const Api = {
11 + TEACHER_GRADE_CLASS_LIST: '/srv/?a=user&t=teacher_grade_class_group_list',
12 +}
13 +
14 +/**
15 + * 获取老师的年级、班级、课程列表信息
16 + * @param {*} grade_id 年级ID 用来缩小班级、课程的筛选范围
17 + * @param {*} class_id 班级ID 用来缩小课程的筛选范围
18 + * @returns {Array} data { grade_list [{id, grade_name}], class_list [{id, class_name}], group_list [{id, title}] }
19 + */
20 +export const getTeacherGradeClassListAPI = (params) => fn(fetch.get(Api.TEACHER_GRADE_CLASS_LIST, params))
......
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
88 /> 88 />
89 </FrostedGlass> 89 </FrostedGlass>
90 90
91 - <FrostedGlass class="rounded-xl overflow-hidden mb-5"> 91 + <FrostedGlass v-if="isTeacher" class="rounded-xl overflow-hidden mb-5">
92 <MenuItem 92 <MenuItem
93 v-for="item in menuItems4" 93 v-for="item in menuItems4"
94 :key="item.path" 94 :key="item.path"
...@@ -146,12 +146,14 @@ useTitle($route.meta.title); ...@@ -146,12 +146,14 @@ useTitle($route.meta.title);
146 146
147 const profile = ref({}); 147 const profile = ref({});
148 const checkIns = ref({}); 148 const checkIns = ref({});
149 +const isTeacher = ref(false);
149 150
150 onMounted(async () => { 151 onMounted(async () => {
151 const { code, data } = await getUserInfoAPI(); 152 const { code, data } = await getUserInfoAPI();
152 if (code) { 153 if (code) {
153 profile.value = data.user; 154 profile.value = data.user;
154 checkIns.value = data.checkin; 155 checkIns.value = data.checkin;
156 + isTeacher.value = data.is_teacher;
155 // 处理消息中心的未读消息数量 157 // 处理消息中心的未读消息数量
156 menuItems2.value.forEach(item => { 158 menuItems2.value.forEach(item => {
157 if (item.path === '/profile/messages') { 159 if (item.path === '/profile/messages') {
...@@ -193,9 +195,11 @@ const handleMenuClick = (path) => { ...@@ -193,9 +195,11 @@ const handleMenuClick = (path) => {
193 // window.location.href = 'https://community.mlaj.com'; 195 // window.location.href = 'https://community.mlaj.com';
194 showToast('功能暂未开放') 196 showToast('功能暂未开放')
195 } else if(path === '/profile/activities') { 197 } else if(path === '/profile/activities') {
196 - showToast('功能暂未开放') 198 + // showToast('功能暂未开放')
199 + window.location.href = 'https://wxm-dev.behalo.cc/pages/activity/activity?token=&user_id=';
197 } else if(path === '/profile/userinfo') { 200 } else if(path === '/profile/userinfo') {
198 - showToast('功能暂未开放') 201 + // showToast('功能暂未开放')
202 + window.location.href = 'https://wxm-dev.behalo.cc/pages/student/student?token=&user_id=';
199 } else { 203 } else {
200 router.push(path); 204 router.push(path);
201 } 205 }
......
1 <!-- 1 <!--
2 * @Date: 2025-05-29 15:34:17 2 * @Date: 2025-05-29 15:34:17
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-06-24 17:11:59 4 + * @LastEditTime: 2025-06-25 11:26:57
5 * @FilePath: /mlaj/src/views/teacher/checkinPage.vue 5 * @FilePath: /mlaj/src/views/teacher/checkinPage.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
24 <div style="padding: 0 1rem; margin-top: 1rem;"> 24 <div style="padding: 0 1rem; margin-top: 1rem;">
25 <van-row gutter="15"> 25 <van-row gutter="15">
26 <van-col span="12"> 26 <van-col span="12">
27 - <van-button type="primary" block icon="photo" @click="handleAdd('checkin')">安排打卡</van-button> 27 + <van-button type="primary" block icon="edit" @click="handleAdd('checkin')">安排打卡</van-button>
28 </van-col> 28 </van-col>
29 <van-col span="12"> 29 <van-col span="12">
30 <van-button type="primary" block icon="video" @click="handleAdd('homework')">设置作业</van-button> 30 <van-button type="primary" block icon="video" @click="handleAdd('homework')">设置作业</van-button>
...@@ -177,51 +177,6 @@ ...@@ -177,51 +177,6 @@
177 <div style="height: 5rem;"></div> 177 <div style="height: 5rem;"></div>
178 </van-config-provider> 178 </van-config-provider>
179 179
180 - <!-- 点评详情弹窗 -->
181 - <van-popup v-model:show="showCommentPopup" position="bottom" round class="comment-popup">
182 - <div class="p-6 w-100">
183 - <div class="text-center text-lg font-bold mb-4">点评详情</div>
184 -
185 - <!-- 显示评分 -->
186 - <div class="mb-4">
187 - <div class="text-sm text-gray-600 mb-2">评分</div>
188 - <div class="flex justify-center">
189 - <van-rate :model-value="currentCommentPost?.comment?.rating || 0" :size="24" color="#ffd21e" void-color="#eee" readonly />
190 - <span class="ml-2 text-sm text-gray-500">({{ currentCommentPost?.comment?.rating || 0 }}/5)</span>
191 - </div>
192 - </div>
193 -
194 - <!-- 显示点评内容 -->
195 - <div class="mb-6">
196 - <div class="text-sm text-gray-600 mb-2">点评内容</div>
197 - <div class="bg-gray-50 rounded-lg p-3 min-h-[100px]">
198 - <p class="text-gray-800 text-sm leading-relaxed">
199 - {{ currentCommentPost?.comment?.content || '暂无点评内容' }}
200 - </p>
201 - </div>
202 - </div>
203 -
204 - <!-- 点评时间 -->
205 - <div class="mb-6" v-if="currentCommentPost?.comment?.created_at">
206 - <div class="text-sm text-gray-600 mb-2">点评时间</div>
207 - <div class="text-sm text-gray-500">
208 - {{ formatCommentTime(currentCommentPost.comment.created_at) }}
209 - </div>
210 - </div>
211 -
212 - <!-- 关闭按钮 -->
213 - <div class="flex justify-center">
214 - <van-button
215 - type="primary"
216 - @click="closeCommentPopup"
217 - class="w-32"
218 - >
219 - 关闭
220 - </van-button>
221 - </div>
222 - </div>
223 - </van-popup>
224 -
225 <van-dialog v-model:show="dialog_show" title="标题" show-cancel-button></van-dialog> 180 <van-dialog v-model:show="dialog_show" title="标题" show-cancel-button></van-dialog>
226 </AppLayout> 181 </AppLayout>
227 </template> 182 </template>
...@@ -237,42 +192,29 @@ import AudioPlayer from "@/components/ui/AudioPlayer.vue"; ...@@ -237,42 +192,29 @@ import AudioPlayer from "@/components/ui/AudioPlayer.vue";
237 import { useTitle } from '@vueuse/core'; 192 import { useTitle } from '@vueuse/core';
238 import dayjs from 'dayjs'; 193 import dayjs from 'dayjs';
239 194
240 -import { getTaskDetailAPI, getUploadTaskListAPI, delUploadTaskInfoAPI, likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI } from "@/api/checkin"; 195 +import { getTaskDetailAPI, getCheckinTeacherListAPI, delUploadTaskInfoAPI, likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI } from "@/api/checkin";
196 +import { getTeacherGradeClassListAPI } from "@/api/teacher";
241 197
242 const route = useRoute() 198 const route = useRoute()
243 const router = useRouter() 199 const router = useRouter()
244 useTitle(route.meta.title); 200 useTitle(route.meta.title);
245 201
246 -// 点评相关
247 -const showCommentPopup = ref(false)
248 -const currentCommentPost = ref(null)
249 -
250 // 审核相关 202 // 审核相关
251 const showAuditDialog = ref(false) 203 const showAuditDialog = ref(false)
252 const currentAuditPost = ref(null) 204 const currentAuditPost = ref(null)
253 205
254 -const selectGradeValue = ref(0); 206 +const selectGradeValue = ref(null);
255 -const selectClassValue = ref('a'); 207 +const selectClassValue = ref(null);
256 -const selectCourseValue = ref('v'); 208 +const selectCourseValue = ref(null);
257 -const gradeOption = [ 209 +const gradeOption = ref([]);
258 - { text: '全部年级', value: 0 }, 210 +const classOption = ref([]);
259 - { text: '一年级', value: 1 }, 211 +const courseOption = ref([]);
260 - { text: '二年级', value: 2 }, 212 +
261 -]; 213 +const handleGradeChange = async (val) => {
262 -const classOption = [
263 - { text: '全部班级', value: 'a' },
264 - { text: '一班级', value: 'b' },
265 - { text: '二班级', value: 'c' },
266 -];
267 -const courseOption = [
268 - { text: '全部课程', value: 'v' },
269 - { text: '一课程', value: 'b' },
270 - { text: '二课程', value: 'c' },
271 -];
272 -
273 -const handleGradeChange = (val) => {
274 console.log('val', val); 214 console.log('val', val);
275 selectGradeValue.value = val; 215 selectGradeValue.value = val;
216 + // 根据年级ID 更新过滤列表
217 + getFilterList(val);
276 // 重置分页参数 218 // 重置分页参数
277 page.value = 0 219 page.value = 0
278 checkinDataList.value = [] 220 checkinDataList.value = []
...@@ -284,6 +226,8 @@ const handleGradeChange = (val) => { ...@@ -284,6 +226,8 @@ const handleGradeChange = (val) => {
284 const handleClassChange = (val) => { 226 const handleClassChange = (val) => {
285 console.log('val', val); 227 console.log('val', val);
286 selectClassValue.value = val; 228 selectClassValue.value = val;
229 + // 根据年级ID和班级ID 更新过滤列表
230 + getFilterList(selectGradeValue.value, val);
287 // 重置分页参数 231 // 重置分页参数
288 page.value = 0 232 page.value = 0
289 checkinDataList.value = [] 233 checkinDataList.value = []
...@@ -640,23 +584,6 @@ const handLike = async (post) => { ...@@ -640,23 +584,6 @@ const handLike = async (post) => {
640 } 584 }
641 585
642 /** 586 /**
643 - * 打开点评详情弹窗
644 - * @param {Object} post - 作业帖子对象
645 - */
646 -const openCommentPopup = (post) => {
647 - currentCommentPost.value = post
648 - showCommentPopup.value = true
649 -}
650 -
651 -/**
652 - * 关闭点评详情弹窗
653 - */
654 -const closeCommentPopup = () => {
655 - showCommentPopup.value = false
656 - currentCommentPost.value = null
657 -}
658 -
659 -/**
660 * 打开审核确认弹框 587 * 打开审核确认弹框
661 * @param {Object} post - 帖子对象 588 * @param {Object} post - 帖子对象
662 */ 589 */
...@@ -819,12 +746,11 @@ const onLoad = async (date) => { ...@@ -819,12 +746,11 @@ const onLoad = async (date) => {
819 const nextPage = page.value; 746 const nextPage = page.value;
820 const current_date = date || route.query.date || dayjs().format('YYYY-MM-DD'); 747 const current_date = date || route.query.date || dayjs().format('YYYY-MM-DD');
821 // 748 //
822 - const res = await getUploadTaskListAPI({ 749 + const res = await getCheckinTeacherListAPI({
823 limit: limit.value, 750 limit: limit.value,
824 page: nextPage, 751 page: nextPage,
825 - task_id: route.query.id,
826 date: current_date, 752 date: current_date,
827 - grade: selectGradeValue.value, 753 + grade_id: selectGradeValue.value,
828 class_id: selectClassValue.value, 754 class_id: selectClassValue.value,
829 course_id: selectCourseValue.value, 755 course_id: selectCourseValue.value,
830 }); 756 });
...@@ -837,6 +763,47 @@ const onLoad = async (date) => { ...@@ -837,6 +763,47 @@ const onLoad = async (date) => {
837 loading.value = false; 763 loading.value = false;
838 }; 764 };
839 765
766 +const gradeList = ref([]);
767 +const classList = ref([]);
768 +const courseList = ref([]);
769 +
770 +const getFilterList = async (grade_id=null, class_id=null) => {
771 + const { code, data } = await getTeacherGradeClassListAPI({ grade_id, class_id });
772 + if (code) {
773 + // 处理数据
774 + gradeOption.value = data.grade_list?.map(item => {
775 + return {
776 + text: item.grade_name,
777 + value: item.id,
778 + }
779 + });
780 + gradeOption.value.unshift({
781 + text: '全部年级',
782 + value: null,
783 + });
784 + classOption.value = data.class_list?.map(item => {
785 + return {
786 + text: item.class_name,
787 + value: item.id,
788 + }
789 + });
790 + classOption.value.unshift({
791 + text: '全部班级',
792 + value: null,
793 + });
794 + courseOption.value = data.group_list?.map(item => {
795 + return {
796 + text: item.title,
797 + value: item.id,
798 + }
799 + });
800 + courseOption.value.unshift({
801 + text: '全部课程',
802 + value: null,
803 + });
804 + }
805 +}
806 +
840 onMounted(async () => { 807 onMounted(async () => {
841 const current_date = route.query.date; 808 const current_date = route.query.date;
842 if (current_date) { 809 if (current_date) {
...@@ -847,6 +814,8 @@ onMounted(async () => { ...@@ -847,6 +814,8 @@ onMounted(async () => {
847 getTaskDetail(dayjs().format('YYYY-MM')); 814 getTaskDetail(dayjs().format('YYYY-MM'));
848 onLoad(dayjs().format('YYYY-MM-DD')); 815 onLoad(dayjs().format('YYYY-MM-DD'));
849 } 816 }
817 + // 获取老师的年级、班级、课程列表信息
818 + getFilterList();
850 }) 819 })
851 820
852 const formatData = (data) => { 821 const formatData = (data) => {
...@@ -895,12 +864,7 @@ const formatData = (data) => { ...@@ -895,12 +864,7 @@ const formatData = (data) => {
895 is_liked: item.is_like, 864 is_liked: item.is_like,
896 is_my: item.is_my, 865 is_my: item.is_my,
897 file_type: item.file_type, 866 file_type: item.file_type,
898 - is_audit: item.is_audit || Math.random() > 0.5, // 随机设置是否已点评,用于测试 867 + is_audit: item.status === 5 ? true : false, // 5=审批通过,7=审批不通过
899 - comment: item.comment || (Math.random() > 0.5 ? {
900 - rating: Math.floor(Math.random() * 5) + 1,
901 - content: ['作业完成得很好,继续保持!', '需要更加仔细一些,注意细节。', '表现优秀,值得表扬!', '还有进步空间,加油!'][Math.floor(Math.random() * 4)],
902 - created_at: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000).toISOString()
903 - } : null)
904 } 868 }
905 }) 869 })
906 return formattedData; 870 return formattedData;
...@@ -1035,26 +999,4 @@ const handleAdd = (type) => { ...@@ -1035,26 +999,4 @@ const handleAdd = (type) => {
1035 } 999 }
1036 } 1000 }
1037 } 1001 }
1038 -
1039 -// 点评弹窗样式
1040 -.comment-popup {
1041 - .van-popup {
1042 - max-width: 500px;
1043 - margin: 0 auto;
1044 - }
1045 -
1046 - .van-rate {
1047 - justify-content: center;
1048 - }
1049 -
1050 - .van-field {
1051 - padding: 12px;
1052 - border-radius: 8px;
1053 - }
1054 -
1055 - .van-button {
1056 - height: 44px;
1057 - border-radius: 8px;
1058 - }
1059 -}
1060 </style> 1002 </style>
......