|
|
@@ -1,33 +1,28 @@
|
|
|
<template>
|
|
|
<view class="container">
|
|
|
<NavBar ref="navBarRef" title="教师签到记录" :showBack="true" />
|
|
|
- <view class="content-wrapper" :style="{ paddingTop: navHeight + 'px' }">
|
|
|
+ <view class="content-wrapper" style=" paddingTop: 140px">
|
|
|
<view class="header">
|
|
|
- <text class="header-title">教师签到记录</text>
|
|
|
+ <text class="header-title">所有课程与班级</text>
|
|
|
</view>
|
|
|
- <view class="teacher-view">
|
|
|
- <text v-if="loading" class="loading-text">正在加载记录...</text>
|
|
|
- <view v-else-if="Object.keys(groupedRecords).length > 0">
|
|
|
- <view v-for="(course, courseId) in groupedRecords" :key="courseId" class="course-group">
|
|
|
- <text class="course-name">{{ course.courseName }}</text>
|
|
|
- <view v-for="(cls, classId) in course.classes" :key="classId" class="class-group">
|
|
|
- <text class="class-name">{{ cls.className }}</text>
|
|
|
- <view class="task-list">
|
|
|
- <view v-for="task in cls.tasks" :key="task.id" class="task-item">
|
|
|
- <text class="task-status" :class="task.status">{{ formatStatus(task.status) }}</text>
|
|
|
- <text class="task-info">签到码: {{ task.signCode }}</text>
|
|
|
- <text class="task-info">签到方式: {{ task.signType }}</text>
|
|
|
- <text class="task-info">总人数: {{ task.totalNumber }}</text>
|
|
|
- <text class="task-info">已签到: {{ task.signedNumber }}</text>
|
|
|
- <text class="task-info">发起时间: {{ formatTime(task.createTime) }}</text>
|
|
|
- <text class="task-info">结束时间: {{ formatTime(task.endTime) }}</text>
|
|
|
- </view>
|
|
|
+ <view class="record-view">
|
|
|
+ <text v-if="loading" class="loading-text">正在加载课程...</text>
|
|
|
+ <view v-else-if="coursesWithClasses.length > 0" class="course-list">
|
|
|
+ <view v-for="course in coursesWithClasses" :key="course.courseId" class="course-item">
|
|
|
+ <view class="course-header" @click="toggleExpand(course.courseId)">
|
|
|
+ <text class="course-name">{{ course.courseName }}</text>
|
|
|
+ <text class="arrow" :class="{ 'expanded': course.expanded }">></text>
|
|
|
+ </view>
|
|
|
+ <view v-if="course.expanded" class="class-list-container">
|
|
|
+ <view v-for="cls in course.classes" :key="cls.classId" class="class-item" @click="navigateToClassDetails(cls.classId)">
|
|
|
+ <text class="class-name">{{ cls.className }}</text>
|
|
|
+ <text class="arrow">></text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view v-else class="empty-text">
|
|
|
- <text>暂无签到记录</text>
|
|
|
+ <text>暂无课程</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -37,7 +32,7 @@
|
|
|
<script setup lang="ts">
|
|
|
import { ref, computed, onMounted, nextTick, watch } from 'vue'
|
|
|
import { useUserStore } from '@/store/user'
|
|
|
-import { getAllTeacherCheckinTasksApi } from '@/api/checkin'
|
|
|
+import { getAllTeacherCoursesApi, getClassesByCourseIdApi } from '@/api/checkin'
|
|
|
import NavBar from '../components/NavBar/NavBar.vue'
|
|
|
|
|
|
const navHeight = ref(88)
|
|
|
@@ -45,61 +40,59 @@ const navBarRef = ref(null)
|
|
|
|
|
|
const userStore = useUserStore()
|
|
|
const loading = ref(false)
|
|
|
-const groupedRecords = ref({})
|
|
|
+const coursesWithClasses = ref([])
|
|
|
+const expandedCourseId = ref(null)
|
|
|
|
|
|
onMounted(async () => {
|
|
|
await nextTick()
|
|
|
navHeight.value = navBarRef.value?.totalNavHeight.value || 88
|
|
|
watch(() => navBarRef.value?.totalNavHeight.value, (val) => { if (val) navHeight.value = val }, { immediate: true })
|
|
|
|
|
|
- if (userStore.isLoggedIn && userStore.userInfo.role === 2) {
|
|
|
+ if (userStore.isLoggedIn && userStore.userInfo.role === "TEACHER") {
|
|
|
loading.value = true
|
|
|
- await fetchTeacherRecords()
|
|
|
+ await fetchTeacherCourses()
|
|
|
loading.value = false
|
|
|
}
|
|
|
})
|
|
|
|
|
|
-const fetchTeacherRecords = async () => {
|
|
|
+const fetchTeacherCourses = async () => {
|
|
|
const teacherId = userStore.userInfo.pid
|
|
|
if (!teacherId) return
|
|
|
|
|
|
try {
|
|
|
- const allTasks = await getAllTeacherCheckinTasksApi(teacherId)
|
|
|
- const grouped = {}
|
|
|
- allTasks.forEach(task => {
|
|
|
- const courseId = task.courseId
|
|
|
- const classId = task.classId
|
|
|
- const courseName = '课程名 ' + task.courseId
|
|
|
- const className = '班级名 ' + task.classId
|
|
|
-
|
|
|
- if (!grouped[courseId]) {
|
|
|
- grouped[courseId] = { courseName, classes: {} }
|
|
|
- }
|
|
|
- if (!grouped[courseId].classes[classId]) {
|
|
|
- grouped[courseId].classes[classId] = { className, tasks: [] }
|
|
|
- }
|
|
|
- grouped[courseId].classes[classId].tasks.push(task)
|
|
|
- })
|
|
|
- groupedRecords.value = grouped
|
|
|
+ const courses = await getAllTeacherCoursesApi(teacherId)
|
|
|
+ coursesWithClasses.value = courses.map(course => ({
|
|
|
+ ...course,
|
|
|
+ classes: [], // 预留一个空数组来存放班级
|
|
|
+ expanded: false // 控制收起展开状态
|
|
|
+ }))
|
|
|
} catch (error) {
|
|
|
- console.error('获取教师签到记录失败:', error)
|
|
|
- uni.showToast({ title: '获取记录失败', icon: 'none' })
|
|
|
+ console.error('获取教师课程失败:', error)
|
|
|
+ uni.showToast({ title: '获取课程失败', icon: 'none' })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const formatStatus = (status) => {
|
|
|
- const statusMap = {
|
|
|
- NOT_START: '未开始',
|
|
|
- PROCESSING: '进行中',
|
|
|
- FINISHED: '已结束',
|
|
|
- CANCELLED: '已取消'
|
|
|
+const toggleExpand = async (courseId) => {
|
|
|
+ const course = coursesWithClasses.value.find(c => c.courseId === courseId)
|
|
|
+ if (course) {
|
|
|
+ course.expanded = !course.expanded
|
|
|
+ if (course.expanded && course.classes.length === 0) {
|
|
|
+ // 如果是展开状态,且班级列表为空,则发起请求获取
|
|
|
+ try {
|
|
|
+ const classes = await getClassesByCourseIdApi(courseId)
|
|
|
+ course.classes = classes
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取班级列表失败:', error)
|
|
|
+ uni.showToast({ title: '获取班级失败', icon: 'none' })
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- return statusMap[status] || '未知'
|
|
|
}
|
|
|
-const formatTime = (time) => {
|
|
|
- if (!time) return '不限'
|
|
|
- const date = new Date(time)
|
|
|
- return date.toLocaleString()
|
|
|
+
|
|
|
+const navigateToClassDetails = (classId) => {
|
|
|
+ uni.navigateTo({
|
|
|
+ url: `/pages/record/teacher-class-details?classId=${classId}`
|
|
|
+ })
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
@@ -132,68 +125,69 @@ const formatTime = (time) => {
|
|
|
color: #999;
|
|
|
}
|
|
|
|
|
|
-.course-group, .class-group {
|
|
|
- margin-bottom: 40rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.course-name {
|
|
|
- font-size: 38rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #2E5F8A;
|
|
|
- margin-bottom: 20rpx;
|
|
|
- display: block;
|
|
|
- border-left: 8rpx solid #7DB4D6;
|
|
|
- padding-left: 15rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.class-name {
|
|
|
- font-size: 34rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #333;
|
|
|
- margin: 20rpx 0;
|
|
|
- display: block;
|
|
|
+.record-view {
|
|
|
+ padding-bottom: 50rpx;
|
|
|
}
|
|
|
|
|
|
-.task-list {
|
|
|
+.course-list {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 20rpx;
|
|
|
}
|
|
|
|
|
|
-.task-item {
|
|
|
+.course-item {
|
|
|
background-color: #ffffff;
|
|
|
border-radius: 16rpx;
|
|
|
- padding: 30rpx;
|
|
|
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
|
- display: grid;
|
|
|
- grid-template-columns: 1fr 1fr;
|
|
|
- grid-gap: 10rpx 20rpx;
|
|
|
- position: relative;
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
|
|
|
-.task-status {
|
|
|
- position: absolute;
|
|
|
- top: 20rpx;
|
|
|
- right: 20rpx;
|
|
|
- font-size: 28rpx;
|
|
|
+.course-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 30rpx;
|
|
|
+ border-bottom: 1rpx solid #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.course-name {
|
|
|
+ font-size: 38rpx;
|
|
|
font-weight: bold;
|
|
|
+ color: #2E5F8A;
|
|
|
}
|
|
|
|
|
|
-.task-status.PROCESSING {
|
|
|
- color: #5CB85C;
|
|
|
+.class-list-container {
|
|
|
+ padding: 20rpx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10rpx;
|
|
|
}
|
|
|
-.task-status.FINISHED {
|
|
|
- color: #e54d42;
|
|
|
+
|
|
|
+.class-item {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 20rpx;
|
|
|
+ border-bottom: 1rpx solid #f0f0f0;
|
|
|
}
|
|
|
-.task-status.NOT_START {
|
|
|
- color: #7DB4D6;
|
|
|
+
|
|
|
+.class-item:last-child {
|
|
|
+ border-bottom: none;
|
|
|
}
|
|
|
-.task-status.CANCELLED {
|
|
|
- color: #999;
|
|
|
+
|
|
|
+.class-name {
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #333;
|
|
|
}
|
|
|
|
|
|
-.task-info {
|
|
|
+.arrow {
|
|
|
font-size: 28rpx;
|
|
|
- color: #666;
|
|
|
+ color: #999;
|
|
|
+ transform: rotate(90deg);
|
|
|
+ transition: transform 0.2s ease-in-out;
|
|
|
+}
|
|
|
+
|
|
|
+.arrow.expanded {
|
|
|
+ transform: rotate(270deg);
|
|
|
}
|
|
|
</style>
|