hookehuyr

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

- 新增教师年级、班级、课程列表API接口
- 实现教师打卡动态列表功能,支持年级、班级、课程筛选
- 优化教师页面菜单显示逻辑,根据用户角色显示不同菜单项
- 移除点评相关代码,改用审核状态判断
/*
* @Date: 2025-06-06 09:26:16
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-06-06 18:12:47
* @LastEditTime: 2025-06-25 11:26:10
* @FilePath: /mlaj/src/api/checkin.js
* @Description: 签到模块相关接口
*/
......@@ -18,6 +18,7 @@ const Api = {
TASK_UPLOAD_DEL: '/srv/?a=checkin&t=upload_del',
TASK_UPLOAD_LIKE: '/srv/?a=checkin&t=like',
TASK_UPLOAD_DISLIKE: '/srv/?a=checkin&t=dislike',
CHECKIN_TEACHER_LIST: '/srv/?a=checkin&t=teacher_list',
}
/**
......@@ -102,3 +103,17 @@ export const likeUploadTaskInfoAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_
* @returns
*/
export const dislikeUploadTaskInfoAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_DISLIKE, params))
/**
* @description: 老师查看打卡动态列表
* @param grade_id 年级ID
* @param class_id 班级ID
* @param group_id 课程ID
* @param date 日期
* @param keyword 搜索
* @param order_by_time asc=正序,desc=倒序。默认为倒序
* @param limit
* @param offset
* @returns
*/
export const getCheckinTeacherListAPI = (params) => fn(fetch.get(Api.CHECKIN_TEACHER_LIST, params))
......
/*
* @Date: 2025-06-23 11:46:21
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-06-25 10:51:28
* @FilePath: /mlaj/src/api/teacher.js
* @Description: 文件描述
*/
import { fn, fetch } from './fn'
const Api = {
TEACHER_GRADE_CLASS_LIST: '/srv/?a=user&t=teacher_grade_class_group_list',
}
/**
* 获取老师的年级、班级、课程列表信息
* @param {*} grade_id 年级ID 用来缩小班级、课程的筛选范围
* @param {*} class_id 班级ID 用来缩小课程的筛选范围
* @returns {Array} data { grade_list [{id, grade_name}], class_list [{id, class_name}], group_list [{id, title}] }
*/
export const getTeacherGradeClassListAPI = (params) => fn(fetch.get(Api.TEACHER_GRADE_CLASS_LIST, params))
......
......@@ -88,7 +88,7 @@
/>
</FrostedGlass>
<FrostedGlass class="rounded-xl overflow-hidden mb-5">
<FrostedGlass v-if="isTeacher" class="rounded-xl overflow-hidden mb-5">
<MenuItem
v-for="item in menuItems4"
:key="item.path"
......@@ -146,12 +146,14 @@ useTitle($route.meta.title);
const profile = ref({});
const checkIns = ref({});
const isTeacher = ref(false);
onMounted(async () => {
const { code, data } = await getUserInfoAPI();
if (code) {
profile.value = data.user;
checkIns.value = data.checkin;
isTeacher.value = data.is_teacher;
// 处理消息中心的未读消息数量
menuItems2.value.forEach(item => {
if (item.path === '/profile/messages') {
......@@ -193,9 +195,11 @@ const handleMenuClick = (path) => {
// window.location.href = 'https://community.mlaj.com';
showToast('功能暂未开放')
} else if(path === '/profile/activities') {
showToast('功能暂未开放')
// showToast('功能暂未开放')
window.location.href = 'https://wxm-dev.behalo.cc/pages/activity/activity?token=&user_id=';
} else if(path === '/profile/userinfo') {
showToast('功能暂未开放')
// showToast('功能暂未开放')
window.location.href = 'https://wxm-dev.behalo.cc/pages/student/student?token=&user_id=';
} else {
router.push(path);
}
......
<!--
* @Date: 2025-05-29 15:34:17
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-06-24 17:11:59
* @LastEditTime: 2025-06-25 11:26:57
* @FilePath: /mlaj/src/views/teacher/checkinPage.vue
* @Description: 文件描述
-->
......@@ -24,7 +24,7 @@
<div style="padding: 0 1rem; margin-top: 1rem;">
<van-row gutter="15">
<van-col span="12">
<van-button type="primary" block icon="photo" @click="handleAdd('checkin')">安排打卡</van-button>
<van-button type="primary" block icon="edit" @click="handleAdd('checkin')">安排打卡</van-button>
</van-col>
<van-col span="12">
<van-button type="primary" block icon="video" @click="handleAdd('homework')">设置作业</van-button>
......@@ -177,51 +177,6 @@
<div style="height: 5rem;"></div>
</van-config-provider>
<!-- 点评详情弹窗 -->
<van-popup v-model:show="showCommentPopup" position="bottom" round class="comment-popup">
<div class="p-6 w-100">
<div class="text-center text-lg font-bold mb-4">点评详情</div>
<!-- 显示评分 -->
<div class="mb-4">
<div class="text-sm text-gray-600 mb-2">评分</div>
<div class="flex justify-center">
<van-rate :model-value="currentCommentPost?.comment?.rating || 0" :size="24" color="#ffd21e" void-color="#eee" readonly />
<span class="ml-2 text-sm text-gray-500">({{ currentCommentPost?.comment?.rating || 0 }}/5)</span>
</div>
</div>
<!-- 显示点评内容 -->
<div class="mb-6">
<div class="text-sm text-gray-600 mb-2">点评内容</div>
<div class="bg-gray-50 rounded-lg p-3 min-h-[100px]">
<p class="text-gray-800 text-sm leading-relaxed">
{{ currentCommentPost?.comment?.content || '暂无点评内容' }}
</p>
</div>
</div>
<!-- 点评时间 -->
<div class="mb-6" v-if="currentCommentPost?.comment?.created_at">
<div class="text-sm text-gray-600 mb-2">点评时间</div>
<div class="text-sm text-gray-500">
{{ formatCommentTime(currentCommentPost.comment.created_at) }}
</div>
</div>
<!-- 关闭按钮 -->
<div class="flex justify-center">
<van-button
type="primary"
@click="closeCommentPopup"
class="w-32"
>
关闭
</van-button>
</div>
</div>
</van-popup>
<van-dialog v-model:show="dialog_show" title="标题" show-cancel-button></van-dialog>
</AppLayout>
</template>
......@@ -237,42 +192,29 @@ import AudioPlayer from "@/components/ui/AudioPlayer.vue";
import { useTitle } from '@vueuse/core';
import dayjs from 'dayjs';
import { getTaskDetailAPI, getUploadTaskListAPI, delUploadTaskInfoAPI, likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI } from "@/api/checkin";
import { getTaskDetailAPI, getCheckinTeacherListAPI, delUploadTaskInfoAPI, likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI } from "@/api/checkin";
import { getTeacherGradeClassListAPI } from "@/api/teacher";
const route = useRoute()
const router = useRouter()
useTitle(route.meta.title);
// 点评相关
const showCommentPopup = ref(false)
const currentCommentPost = ref(null)
// 审核相关
const showAuditDialog = ref(false)
const currentAuditPost = ref(null)
const selectGradeValue = ref(0);
const selectClassValue = ref('a');
const selectCourseValue = ref('v');
const gradeOption = [
{ text: '全部年级', value: 0 },
{ text: '一年级', value: 1 },
{ text: '二年级', value: 2 },
];
const classOption = [
{ text: '全部班级', value: 'a' },
{ text: '一班级', value: 'b' },
{ text: '二班级', value: 'c' },
];
const courseOption = [
{ text: '全部课程', value: 'v' },
{ text: '一课程', value: 'b' },
{ text: '二课程', value: 'c' },
];
const handleGradeChange = (val) => {
const selectGradeValue = ref(null);
const selectClassValue = ref(null);
const selectCourseValue = ref(null);
const gradeOption = ref([]);
const classOption = ref([]);
const courseOption = ref([]);
const handleGradeChange = async (val) => {
console.log('val', val);
selectGradeValue.value = val;
// 根据年级ID 更新过滤列表
getFilterList(val);
// 重置分页参数
page.value = 0
checkinDataList.value = []
......@@ -284,6 +226,8 @@ const handleGradeChange = (val) => {
const handleClassChange = (val) => {
console.log('val', val);
selectClassValue.value = val;
// 根据年级ID和班级ID 更新过滤列表
getFilterList(selectGradeValue.value, val);
// 重置分页参数
page.value = 0
checkinDataList.value = []
......@@ -640,23 +584,6 @@ const handLike = async (post) => {
}
/**
* 打开点评详情弹窗
* @param {Object} post - 作业帖子对象
*/
const openCommentPopup = (post) => {
currentCommentPost.value = post
showCommentPopup.value = true
}
/**
* 关闭点评详情弹窗
*/
const closeCommentPopup = () => {
showCommentPopup.value = false
currentCommentPost.value = null
}
/**
* 打开审核确认弹框
* @param {Object} post - 帖子对象
*/
......@@ -819,12 +746,11 @@ const onLoad = async (date) => {
const nextPage = page.value;
const current_date = date || route.query.date || dayjs().format('YYYY-MM-DD');
//
const res = await getUploadTaskListAPI({
const res = await getCheckinTeacherListAPI({
limit: limit.value,
page: nextPage,
task_id: route.query.id,
date: current_date,
grade: selectGradeValue.value,
grade_id: selectGradeValue.value,
class_id: selectClassValue.value,
course_id: selectCourseValue.value,
});
......@@ -837,6 +763,47 @@ const onLoad = async (date) => {
loading.value = false;
};
const gradeList = ref([]);
const classList = ref([]);
const courseList = ref([]);
const getFilterList = async (grade_id=null, class_id=null) => {
const { code, data } = await getTeacherGradeClassListAPI({ grade_id, class_id });
if (code) {
// 处理数据
gradeOption.value = data.grade_list?.map(item => {
return {
text: item.grade_name,
value: item.id,
}
});
gradeOption.value.unshift({
text: '全部年级',
value: null,
});
classOption.value = data.class_list?.map(item => {
return {
text: item.class_name,
value: item.id,
}
});
classOption.value.unshift({
text: '全部班级',
value: null,
});
courseOption.value = data.group_list?.map(item => {
return {
text: item.title,
value: item.id,
}
});
courseOption.value.unshift({
text: '全部课程',
value: null,
});
}
}
onMounted(async () => {
const current_date = route.query.date;
if (current_date) {
......@@ -847,6 +814,8 @@ onMounted(async () => {
getTaskDetail(dayjs().format('YYYY-MM'));
onLoad(dayjs().format('YYYY-MM-DD'));
}
// 获取老师的年级、班级、课程列表信息
getFilterList();
})
const formatData = (data) => {
......@@ -895,12 +864,7 @@ const formatData = (data) => {
is_liked: item.is_like,
is_my: item.is_my,
file_type: item.file_type,
is_audit: item.is_audit || Math.random() > 0.5, // 随机设置是否已点评,用于测试
comment: item.comment || (Math.random() > 0.5 ? {
rating: Math.floor(Math.random() * 5) + 1,
content: ['作业完成得很好,继续保持!', '需要更加仔细一些,注意细节。', '表现优秀,值得表扬!', '还有进步空间,加油!'][Math.floor(Math.random() * 4)],
created_at: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000).toISOString()
} : null)
is_audit: item.status === 5 ? true : false, // 5=审批通过,7=审批不通过
}
})
return formattedData;
......@@ -1035,26 +999,4 @@ const handleAdd = (type) => {
}
}
}
// 点评弹窗样式
.comment-popup {
.van-popup {
max-width: 500px;
margin: 0 auto;
}
.van-rate {
justify-content: center;
}
.van-field {
padding: 12px;
border-radius: 8px;
}
.van-button {
height: 44px;
border-radius: 8px;
}
}
</style>
......