|
|
@@ -22,14 +22,18 @@
|
|
|
|
|
|
<!-- 菜单列表 -->
|
|
|
<div class="menu-section">
|
|
|
- <div class="menu-item" v-for="course in courseList" :key="course.id">
|
|
|
- <div class="course-header" @click="toggleCourse(course.id)">
|
|
|
+ <!-- 加载状态 -->
|
|
|
+ <div v-if="loading" class="loading-container">
|
|
|
+ <div class="loading-text">加载中...</div>
|
|
|
+ </div>
|
|
|
+ <div class="menu-item" v-for="course in courseList" :key="course.courseId">
|
|
|
+ <div class="course-header" @click="toggleCourse(course.courseId)">
|
|
|
<el-icon class="expand-icon" :class="{ 'expanded': course.expanded }">
|
|
|
<ArrowDown v-if="course.expanded" />
|
|
|
<ArrowRight v-else />
|
|
|
</el-icon>
|
|
|
<el-icon class="course-icon"><Document /></el-icon>
|
|
|
- <span class="course-name">{{ course.name }}</span>
|
|
|
+ <span class="course-name">{{ course.courseName }}</span>
|
|
|
</div>
|
|
|
|
|
|
<!-- 作业子菜单 -->
|
|
|
@@ -37,14 +41,14 @@
|
|
|
<div
|
|
|
class="assignment-item"
|
|
|
v-for="assignment in course.assignments"
|
|
|
- :key="assignment.id"
|
|
|
+ :key="assignment.assignmentId"
|
|
|
:class="{ 'active': assignment.active }"
|
|
|
- @click="selectAssignment(assignment.id)"
|
|
|
+ @click="selectAssignment(assignment.assignmentId)"
|
|
|
>
|
|
|
<el-icon class="assignment-icon" :class="{ 'active-icon': assignment.active }">
|
|
|
<Document />
|
|
|
</el-icon>
|
|
|
- <span class="assignment-name">{{ assignment.name }}</span>
|
|
|
+ <span class="assignment-name">{{ assignment.assignmentName }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -76,7 +80,7 @@
|
|
|
</div>
|
|
|
<div class="stats-content">
|
|
|
<div class="stats-label">作业总人数</div>
|
|
|
- <div class="stats-value">32</div>
|
|
|
+ <div class="stats-value">{{ statsData.totalStudents }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -86,7 +90,7 @@
|
|
|
</div>
|
|
|
<div class="stats-content">
|
|
|
<div class="stats-label">已批改人数</div>
|
|
|
- <div class="stats-value">10</div>
|
|
|
+ <div class="stats-value">{{ statsData.correctedCount }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -96,7 +100,7 @@
|
|
|
</div>
|
|
|
<div class="stats-content">
|
|
|
<div class="stats-label">已提交未批改人数</div>
|
|
|
- <div class="stats-value">12</div>
|
|
|
+ <div class="stats-value">{{ statsData.submittedNotCorrectedCount }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -106,7 +110,7 @@
|
|
|
</div>
|
|
|
<div class="stats-content">
|
|
|
<div class="stats-label">未提交人数</div>
|
|
|
- <div class="stats-value">10</div>
|
|
|
+ <div class="stats-value">{{ statsData.notSubmittedCount }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -114,7 +118,7 @@
|
|
|
<div class="assignment-body">
|
|
|
<!-- 课程作业标题 -->
|
|
|
<div class="assignment-title-section">
|
|
|
- <h3 class="assignment-full-title">{{ selectedAssignment.courseName }}:{{ selectedAssignment.name }}</h3>
|
|
|
+ <h3 class="assignment-full-title">{{ selectedAssignment.courseName }}:{{ selectedAssignment.assignmentName }}</h3>
|
|
|
</div>
|
|
|
|
|
|
<!-- 搜索筛选栏 -->
|
|
|
@@ -135,13 +139,14 @@
|
|
|
<el-option label="已提交未批改" value="已提交未批改"></el-option>
|
|
|
<el-option label="已批改" value="已批改"></el-option>
|
|
|
</el-select>
|
|
|
- <el-select v-model="classFilter" placeholder="班级" class="filter-select">
|
|
|
+ <el-select v-model="classFilter" placeholder="班级" class="filter-select" @change="handleClassFilterChange">
|
|
|
<el-option label="全部" value=""></el-option>
|
|
|
- <el-option label="软工一班" value="软工一班"></el-option>
|
|
|
- <el-option label="软工二班" value="软工二班"></el-option>
|
|
|
- <el-option label="计科一班" value="计科一班"></el-option>
|
|
|
- <el-option label="计科二班" value="计科二班"></el-option>
|
|
|
- <el-option label="信息与计算科学(强基计划)" value="信息与计算科学(强基计划)"></el-option>
|
|
|
+ <el-option
|
|
|
+ v-for="classItem in classList"
|
|
|
+ :key="classItem.classId"
|
|
|
+ :label="classItem.className"
|
|
|
+ :value="classItem.classId.toString()"
|
|
|
+ ></el-option>
|
|
|
</el-select>
|
|
|
<el-button type="primary" class="search-button">搜索</el-button>
|
|
|
</div>
|
|
|
@@ -153,12 +158,16 @@
|
|
|
<el-table-column prop="status" label="状态" width="290" align="left">
|
|
|
<template #default="scope">
|
|
|
<span :class="getStatusClass(scope.row.status)" class="status-badge">
|
|
|
- {{ scope.row.status }}
|
|
|
+ {{ statusMap[scope.row.status] || scope.row.status }}
|
|
|
</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="name" label="学生姓名" width="290"></el-table-column>
|
|
|
- <el-table-column prop="className" label="所属班级" width="290"></el-table-column>
|
|
|
+ <el-table-column prop="stuName" label="学生姓名" width="290"></el-table-column>
|
|
|
+ <el-table-column label="班级" width="290">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ getStudentClassName(scope.row) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column prop="score" label="得分" width="290">
|
|
|
<template #header>
|
|
|
<div class="score-header" @click="toggleSort">
|
|
|
@@ -196,6 +205,7 @@
|
|
|
:page-size="pageSize"
|
|
|
layout="total, prev, pager, next"
|
|
|
:total="total"
|
|
|
+ :page-count="totalPages"
|
|
|
@current-change="handleCurrentChange"
|
|
|
/>
|
|
|
</div>
|
|
|
@@ -261,73 +271,145 @@
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
import useApis from "@/apis";
|
|
|
- import {useStore} from "@/store";
|
|
|
- import {inject, ref, reactive, computed} from "vue";
|
|
|
- import {Refresh, Search, Document, ArrowDown, ArrowRight, User, CircleCheck, WarningFilled, View, Sort, ZoomIn} from "@element-plus/icons-vue";
|
|
|
+ import {ref, reactive, computed, onMounted} from "vue";
|
|
|
+ import {useRouter} from "vue-router";
|
|
|
+ import {Refresh, Search, Document, ArrowDown, ArrowRight, User, CircleCheck, WarningFilled, Sort, ZoomIn} from "@element-plus/icons-vue";
|
|
|
+ import type {ClassDTO, StudentAssignmentStatusDTO} from "@/types/dto";
|
|
|
+ import type {CourseVO, IAssignment} from "@/types/vo";
|
|
|
import "./CoursePage.scss"
|
|
|
-
|
|
|
- const apis = useApis()
|
|
|
+ import {useStore} from "@/store";
|
|
|
const store = useStore()
|
|
|
- const doRefresh:Function = inject("reload")
|
|
|
+ const router = useRouter()
|
|
|
|
|
|
- let courseName = ref('')
|
|
|
+ const apis = useApis()
|
|
|
const selectedAssignment = ref(null)
|
|
|
|
|
|
- // 假数据 - 课程和作业列表
|
|
|
- const courseList = reactive([
|
|
|
- {
|
|
|
- id: 1,
|
|
|
- name: '课程1',
|
|
|
- expanded: true,
|
|
|
- assignments: [
|
|
|
- { id: 1, name: '作业1', active: false },
|
|
|
- { id: 2, name: '作业2', active: false },
|
|
|
- { id: 3, name: '作业3', active: false }
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- id: 2,
|
|
|
- name: '课程2',
|
|
|
- expanded: false,
|
|
|
- assignments: [
|
|
|
- { id: 4, name: '作业1', active: false },
|
|
|
- { id: 5, name: '作业2', active: false }
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- id: 3,
|
|
|
- name: '课程3',
|
|
|
- expanded: false,
|
|
|
- assignments: [
|
|
|
- { id: 6, name: '作业1', active: false },
|
|
|
- { id: 7, name: '作业2', active: false },
|
|
|
- { id: 8, name: '作业3', active: false }
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- id: 4,
|
|
|
- name: '课程4',
|
|
|
- expanded: false,
|
|
|
- assignments: [
|
|
|
- { id: 9, name: '作业1', active: false }
|
|
|
- ]
|
|
|
+ // 定义课程和作业的数据结构
|
|
|
+ interface CourseWithAssignments {
|
|
|
+ courseId: number;
|
|
|
+ courseName: string;
|
|
|
+ expanded: boolean;
|
|
|
+ assignments: AssignmentWithStatus[];
|
|
|
+ }
|
|
|
+
|
|
|
+ interface AssignmentWithStatus {
|
|
|
+ assignmentId: number;
|
|
|
+ assignmentName: string;
|
|
|
+ active: boolean;
|
|
|
+ courseId: number;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 真实数据 - 课程和作业列表
|
|
|
+ const courseList = reactive<CourseWithAssignments[]>([])
|
|
|
+
|
|
|
+ // 原始课程数据(从API获取)
|
|
|
+ const rawCourses = reactive<CourseVO[]>([])
|
|
|
+
|
|
|
+ // 原始作业数据(从API获取)
|
|
|
+ const rawAssignments = reactive<IAssignment[]>([])
|
|
|
+
|
|
|
+ // 加载状态
|
|
|
+ const loading = ref(false)
|
|
|
+
|
|
|
+ // 加载所有课程
|
|
|
+ const loadCourses = async () => {
|
|
|
+ try {
|
|
|
+ loading.value = true
|
|
|
+ // 使用正确的CourseQuery来获取所有课程
|
|
|
+ const courseQuery = {
|
|
|
+ courseId: null,
|
|
|
+ teacherId: store.user.id,
|
|
|
+ teacherPid: Number(store.user.pid),
|
|
|
+ studentId: null,
|
|
|
+ courseName: null,
|
|
|
+ semester: null,
|
|
|
+ }
|
|
|
+ const response = await apis.getAllCourse(courseQuery, 1, 1000)
|
|
|
+
|
|
|
+ if (response.data.code === 200) {
|
|
|
+ // 根据其他代码,数据可能在response.data.data.records中
|
|
|
+ const courseData = response.data.data.records || response.data.data.content || response.data.data
|
|
|
+ console.log('加载到的课程数据 1:', courseData)
|
|
|
+ rawCourses.splice(0, rawCourses.length, ...courseData)
|
|
|
+ await loadAllAssignments()
|
|
|
+ buildCourseTree()
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载课程失败 :', error)
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
}
|
|
|
- ])
|
|
|
+ }
|
|
|
|
|
|
- const handleSearch = () => {
|
|
|
- // TODO: 实现搜索功能
|
|
|
+ // 加载所有作业
|
|
|
+ const loadAllAssignments = async () => {
|
|
|
+ try {
|
|
|
+ // 使用正确的AssignmentQuery来获取所有作业
|
|
|
+ const assignmentQuery = {
|
|
|
+ assignmentId: null,
|
|
|
+ teacherId: store.user.id,
|
|
|
+ courseId: null,
|
|
|
+ assignmentName: null,
|
|
|
+ status: null,
|
|
|
+ type: null,
|
|
|
+ }
|
|
|
+ const response = await apis.getAssignmentByQuery(assignmentQuery, 1, 1000)
|
|
|
+
|
|
|
+ if (response.data.code === 200) {
|
|
|
+ // 数据可能在不同的路径中
|
|
|
+ const assignmentData = response.data.data.records || response.data.data.content || response.data.data
|
|
|
+ console.log('加载到的作业数据:', assignmentData)
|
|
|
+ rawAssignments.splice(0, rawAssignments.length, ...assignmentData)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载作业失败:', error)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ // 构建课程树结构
|
|
|
+ const buildCourseTree = () => {
|
|
|
+ const courseMap = new Map<number, CourseWithAssignments>()
|
|
|
+
|
|
|
+ console.log('构建课程树 - 课程数量:', rawCourses.length, '作业数量:', rawAssignments.length)
|
|
|
+
|
|
|
+ // 初始化课程
|
|
|
+ rawCourses.forEach(course => {
|
|
|
+ courseMap.set(course.courseId, {
|
|
|
+ courseId: course.courseId,
|
|
|
+ courseName: course.courseName,
|
|
|
+ expanded: false,
|
|
|
+ assignments: []
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ // 添加作业到对应课程
|
|
|
+ rawAssignments.forEach(assignment => {
|
|
|
+ const course = courseMap.get(assignment.courseId)
|
|
|
+ if (course) {
|
|
|
+ course.assignments.push({
|
|
|
+ assignmentId: assignment.assignmentId,
|
|
|
+ assignmentName: assignment.assignmentName,
|
|
|
+ active: false,
|
|
|
+ courseId: assignment.courseId
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 更新courseList
|
|
|
+ courseList.splice(0, courseList.length, ...Array.from(courseMap.values()))
|
|
|
+ console.log('构建完成的课程树:', courseList.length, '个课程')
|
|
|
+ }
|
|
|
+
|
|
|
// 切换课程展开/收起
|
|
|
const toggleCourse = (courseId: number) => {
|
|
|
- const course = courseList.find(c => c.id === courseId)
|
|
|
+ const course = courseList.find(c => c.courseId === courseId)
|
|
|
if (course) {
|
|
|
course.expanded = !course.expanded
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 选择作业
|
|
|
- const selectAssignment = (assignmentId: number) => {
|
|
|
+ const selectAssignment = async (assignmentId: number) => {
|
|
|
// 清除所有作业的active状态
|
|
|
courseList.forEach(course => {
|
|
|
course.assignments.forEach(assignment => {
|
|
|
@@ -337,15 +419,22 @@
|
|
|
|
|
|
// 设置选中的作业为active
|
|
|
courseList.forEach(course => {
|
|
|
- const assignment = course.assignments.find(a => a.id === assignmentId)
|
|
|
+ const assignment = course.assignments.find(a => a.assignmentId === assignmentId)
|
|
|
if (assignment) {
|
|
|
assignment.active = true
|
|
|
// 设置选中的作业信息
|
|
|
selectedAssignment.value = {
|
|
|
- id: assignment.id,
|
|
|
- name: assignment.name,
|
|
|
- courseName: course.name
|
|
|
+ assignmentId: assignment.assignmentId,
|
|
|
+ assignmentName: assignment.assignmentName,
|
|
|
+ courseName: course.courseName,
|
|
|
+ courseId: course.courseId
|
|
|
}
|
|
|
+
|
|
|
+ // 加载相关数据
|
|
|
+ onAssignmentSelected({
|
|
|
+ assignmentId: assignment.assignmentId,
|
|
|
+ courseId: course.courseId
|
|
|
+ })
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
@@ -361,25 +450,44 @@
|
|
|
const statusFilter = ref('')
|
|
|
const classFilter = ref('')
|
|
|
|
|
|
+ // 监听班级筛选变化
|
|
|
+ const handleClassFilterChange = async () => {
|
|
|
+ if (selectedAssignment.value && classFilter.value) {
|
|
|
+ // 重新加载选中班级的学生数据
|
|
|
+ studentList.splice(0, studentList.length)
|
|
|
+ await loadStudentAssignmentStatus(classFilter.value, selectedAssignment.value.assignmentId.toString())
|
|
|
+ } else if (selectedAssignment.value && !classFilter.value) {
|
|
|
+ // 如果选择了"全部",重新加载所有班级的数据
|
|
|
+ await loadAllStudentAssignmentStatus(selectedAssignment.value.assignmentId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 排序相关逻辑
|
|
|
const sortOrder = ref(0) // 0: 原始顺序, 1: 高到低, 2: 低到高
|
|
|
|
|
|
- // 学生数据模拟
|
|
|
- const studentList = reactive([
|
|
|
- { id: 1, name: 'student0', className: '软工一班', status: '未提交', score: null },
|
|
|
- { id: 2, name: 'student1', className: '软工二班', status: '已提交未批改', score: null },
|
|
|
- { id: 3, name: 'student2', className: '计科一班', status: '已批改', score: 58 },
|
|
|
- { id: 4, name: 'student3', className: '计科二班', status: '未提交', score: null },
|
|
|
- { id: 5, name: 'student4', className: '信息与计算科学(强基计划)', status: '已提交未批改', score: null },
|
|
|
- { id: 6, name: 'student5', className: '软工一班', status: '已批改', score: 77 },
|
|
|
- { id: 7, name: 'student6', className: '软工二班', status: '已批改', score: 72 },
|
|
|
- { id: 8, name: 'student7', className: '计科一班', status: '已批改', score: 89 },
|
|
|
- { id: 9, name: 'student8', className: '计科二班', status: '已批改', score: 93 },
|
|
|
- { id: 10, name: 'student9', className: '信息与计算科学(强基计划)', status: '已批改', score: 100 },
|
|
|
- { id: 11, name: 'student10', className: '信息与计算科学(强基计划)', status: '未提交', score: null },
|
|
|
- { id: 12, name: 'student11', className: '信息与计算科学(强基计划)', status: '未提交', score: null },
|
|
|
- { id: 13, name: 'student12', className: '信息与计算科学(强基计划)', status: '已提交未批改', score: null }
|
|
|
- ])
|
|
|
+ // 学生数据
|
|
|
+ const studentList = reactive<StudentAssignmentStatusDTO[]>([])
|
|
|
+
|
|
|
+ // 班级列表数据
|
|
|
+ const classList = reactive<ClassDTO[]>([])
|
|
|
+
|
|
|
+ // 统计卡片数据
|
|
|
+ const statsData = reactive({
|
|
|
+ totalStudents: 0,
|
|
|
+ correctedCount: 0,
|
|
|
+ submittedNotCorrectedCount: 0,
|
|
|
+ notSubmittedCount: 0
|
|
|
+ })
|
|
|
+
|
|
|
+ // 学生班级映射数据(用于存储学生ID到班级名称的映射)
|
|
|
+ const studentClassMap = reactive(new Map<number, string>())
|
|
|
+
|
|
|
+ // 状态映射
|
|
|
+ const statusMap = {
|
|
|
+ 'NOT_SUBMITTED': '未提交',
|
|
|
+ 'SUBMITTED': '已提交未批改',
|
|
|
+ 'CORRECTED': '已批改'
|
|
|
+ }
|
|
|
|
|
|
// 当前页码
|
|
|
const currentPage = ref(1)
|
|
|
@@ -393,16 +501,18 @@
|
|
|
// 筛选逻辑
|
|
|
if (filterQuery.value) {
|
|
|
filtered = filtered.filter(student =>
|
|
|
- student.name.toLowerCase().includes(filterQuery.value.toLowerCase())
|
|
|
+ student.stuName.toLowerCase().includes(filterQuery.value.toLowerCase())
|
|
|
)
|
|
|
}
|
|
|
|
|
|
if (statusFilter.value) {
|
|
|
- filtered = filtered.filter(student => student.status === statusFilter.value)
|
|
|
+ filtered = filtered.filter(student => statusMap[student.status] === statusFilter.value)
|
|
|
}
|
|
|
|
|
|
if (classFilter.value) {
|
|
|
- filtered = filtered.filter(student => student.className === classFilter.value)
|
|
|
+ // 根据选中的班级ID重新加载数据
|
|
|
+ // 这里可以实现按班级筛选的逻辑
|
|
|
+ // 暂时保持原有的筛选逻辑
|
|
|
}
|
|
|
|
|
|
// 排序逻辑
|
|
|
@@ -422,9 +532,6 @@
|
|
|
})
|
|
|
}
|
|
|
// sortOrder.value === 0 时保持原始顺序
|
|
|
-
|
|
|
- // 计算总页数
|
|
|
- totalPages.value = Math.ceil(filtered.length / pageSize)
|
|
|
|
|
|
// 分页逻辑
|
|
|
const startIndex = (currentPage.value - 1) * pageSize
|
|
|
@@ -432,22 +539,46 @@
|
|
|
return filtered.slice(startIndex, endIndex)
|
|
|
})
|
|
|
|
|
|
- // 总页数
|
|
|
- const totalPages = ref(1)
|
|
|
+ // 单独的计算属性来获取总页数
|
|
|
+ const totalPages = computed(() => {
|
|
|
+ let filtered = [...studentList]
|
|
|
+
|
|
|
+ if (filterQuery.value) {
|
|
|
+ filtered = filtered.filter(student =>
|
|
|
+ student.stuName.toLowerCase().includes(filterQuery.value.toLowerCase())
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ if (statusFilter.value) {
|
|
|
+ filtered = filtered.filter(student => statusMap[student.status] === statusFilter.value)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (classFilter.value) {
|
|
|
+ // 根据选中的班级ID重新加载数据
|
|
|
+ // 这里可以实现按班级筛选的逻辑
|
|
|
+ // 暂时保持原有的筛选逻辑
|
|
|
+ }
|
|
|
+
|
|
|
+ return Math.ceil(filtered.length / pageSize)
|
|
|
+ })
|
|
|
+
|
|
|
+
|
|
|
|
|
|
// 总条目数
|
|
|
const total = computed(() => {
|
|
|
let filtered = [...studentList]
|
|
|
if (filterQuery.value) {
|
|
|
filtered = filtered.filter(student =>
|
|
|
- student.name.toLowerCase().includes(filterQuery.value.toLowerCase())
|
|
|
+ student.stuName.toLowerCase().includes(filterQuery.value.toLowerCase())
|
|
|
)
|
|
|
}
|
|
|
if (statusFilter.value) {
|
|
|
- filtered = filtered.filter(student => student.status === statusFilter.value)
|
|
|
+ filtered = filtered.filter(student => statusMap[student.status] === statusFilter.value)
|
|
|
}
|
|
|
if (classFilter.value) {
|
|
|
- filtered = filtered.filter(student => student.className === classFilter.value)
|
|
|
+ // 根据选中的班级ID重新加载数据
|
|
|
+ // 这里可以实现按班级筛选的逻辑
|
|
|
+ // 暂时保持原有的筛选逻辑
|
|
|
}
|
|
|
return filtered.length
|
|
|
})
|
|
|
@@ -459,6 +590,11 @@
|
|
|
classFilter.value = ''
|
|
|
sortOrder.value = 0
|
|
|
currentPage.value = 1 // 重置页码
|
|
|
+
|
|
|
+ // 重新加载所有数据并更新统计
|
|
|
+ if (selectedAssignment.value) {
|
|
|
+ loadAllStudentAssignmentStatus(selectedAssignment.value.assignmentId)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 处理页码改变
|
|
|
@@ -467,10 +603,10 @@
|
|
|
}
|
|
|
|
|
|
// 获取用于排序的分数值
|
|
|
- const getScoreForSort = (student) => {
|
|
|
- if (student.status === '已批改' && student.score !== null) {
|
|
|
+ const getScoreForSort = (student: StudentAssignmentStatusDTO) => {
|
|
|
+ if (student.status === 'CORRECTED' && student.score !== null) {
|
|
|
return student.score
|
|
|
- } else if (student.status === '已提交未批改') {
|
|
|
+ } else if (student.status === 'SUBMITTED') {
|
|
|
return -1 // 未批改排在未提交前面
|
|
|
} else {
|
|
|
return -2 // 未提交排在最后
|
|
|
@@ -492,30 +628,126 @@
|
|
|
}
|
|
|
|
|
|
// 获取状态样式类
|
|
|
- const getStatusClass = (status) => {
|
|
|
- return `status-${status.replace(/[^a-zA-Z0-9]/g, '')}`
|
|
|
+ const getStatusClass = (status: string) => {
|
|
|
+ const mappedStatus = statusMap[status] || status
|
|
|
+ return `status-${mappedStatus.replace(/[^a-zA-Z0-9]/g, '')}`
|
|
|
}
|
|
|
|
|
|
// 获取分数样式类
|
|
|
- const getScoreClass = (row) => {
|
|
|
- if (row.status === '未提交') return 'score-not-submitted'
|
|
|
- if (row.status === '已提交未批改') return 'score-not-graded'
|
|
|
- if (row.score !== null) {
|
|
|
+ const getScoreClass = (row: StudentAssignmentStatusDTO) => {
|
|
|
+ if (row.status === 'NOT_SUBMITTED') return 'score-not-submitted'
|
|
|
+ if (row.status === 'SUBMITTED') return 'score-not-graded'
|
|
|
+ if (row.status === 'CORRECTED' && row.score !== null) {
|
|
|
return row.score < 60 ? 'score-low' : 'score-high'
|
|
|
}
|
|
|
return ''
|
|
|
}
|
|
|
|
|
|
// 获取分数显示内容
|
|
|
- const getScoreDisplay = (row) => {
|
|
|
- if (row.status === '未提交') return '未提交'
|
|
|
- if (row.status === '已提交未批改') return '未批改'
|
|
|
+ const getScoreDisplay = (row: StudentAssignmentStatusDTO) => {
|
|
|
+ if (row.status === 'NOT_SUBMITTED') return '未提交'
|
|
|
+ if (row.status === 'SUBMITTED') return '未批改'
|
|
|
return row.score !== null ? row.score : '未批改'
|
|
|
}
|
|
|
|
|
|
// 处理批改操作
|
|
|
- const handleCorrect = (row) => {
|
|
|
+ const handleCorrect = (row: StudentAssignmentStatusDTO) => {
|
|
|
console.log('批改学生作业:', row)
|
|
|
+
|
|
|
+ // 跳转到批改页面,路径为 home/assignment/:assignmentId/correct,并传递studentId参数
|
|
|
+ if (selectedAssignment.value) {
|
|
|
+ router.push({
|
|
|
+ path: `/home/assignment/${selectedAssignment.value.assignmentId}/correct`,
|
|
|
+ query: {
|
|
|
+ studentId: row.studentId.toString()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 加载班级列表
|
|
|
+ const loadClassList = async (courseId: number) => {
|
|
|
+ try {
|
|
|
+ const response = await apis.getClassesByCourse(courseId)
|
|
|
+ if (response.data.code === 200) {
|
|
|
+ classList.splice(0, classList.length, ...response.data.data)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载班级列表失败:', error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 加载学生作业状态
|
|
|
+ const loadStudentAssignmentStatus = async (classId: string, assignmentId: string) => {
|
|
|
+ try {
|
|
|
+ const response = await apis.getAssignmentStatusByClass(classId, assignmentId)
|
|
|
+ if (response.data.code === 200) {
|
|
|
+ // 获取班级名称
|
|
|
+ const classItem = classList.find(c => c.classId.toString() === classId)
|
|
|
+ const className = classItem ? classItem.className : '未知班级'
|
|
|
+
|
|
|
+ // 为每个学生建立班级映射关系
|
|
|
+ response.data.data.forEach((student: StudentAssignmentStatusDTO) => {
|
|
|
+ studentClassMap.set(student.studentId, className)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 追加数据而不是替换
|
|
|
+ studentList.push(...response.data.data)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载学生作业状态失败:', error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 当选择作业时加载相关数据
|
|
|
+ const onAssignmentSelected = async (assignment: any) => {
|
|
|
+ if (assignment && assignment.courseId) {
|
|
|
+ // 首先加载班级列表
|
|
|
+ await loadClassList(assignment.courseId)
|
|
|
+
|
|
|
+ // 加载所有班级的学生作业状态
|
|
|
+ if (classList.length > 0) {
|
|
|
+ await loadAllStudentAssignmentStatus(assignment.assignmentId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 加载所有班级的学生作业状态
|
|
|
+ const loadAllStudentAssignmentStatus = async (assignmentId: number) => {
|
|
|
+ try {
|
|
|
+ // 清空现有学生数据
|
|
|
+ studentList.splice(0, studentList.length)
|
|
|
+
|
|
|
+ // 遍历所有班级,加载每个班级的学生数据
|
|
|
+ for (const classItem of classList) {
|
|
|
+ await loadStudentAssignmentStatus(classItem.classId.toString(), assignmentId.toString())
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新统计数据
|
|
|
+ updateStatsData()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载所有学生作业状态失败:', error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取学生班级名称
|
|
|
+ const getStudentClassName = (student: StudentAssignmentStatusDTO) => {
|
|
|
+ // 由于StudentAssignmentStatusDTO中没有班级信息,我们需要通过其他方式获取
|
|
|
+ // 这里暂时返回一个默认值,实际项目中可能需要通过API获取学生班级信息
|
|
|
+ return studentClassMap.get(student.studentId) || '未知班级'
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新统计数据
|
|
|
+ const updateStatsData = () => {
|
|
|
+ const total = studentList.length
|
|
|
+ const corrected = studentList.filter(student => student.status === 'CORRECTED').length
|
|
|
+ const submittedNotCorrected = studentList.filter(student => student.status === 'SUBMITTED').length
|
|
|
+ const notSubmitted = studentList.filter(student => student.status === 'NOT_SUBMITTED').length
|
|
|
+
|
|
|
+ statsData.totalStudents = total
|
|
|
+ statsData.correctedCount = corrected
|
|
|
+ statsData.submittedNotCorrectedCount = submittedNotCorrected
|
|
|
+ statsData.notSubmittedCount = notSubmitted
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -548,21 +780,21 @@
|
|
|
// 搜索课程和作业
|
|
|
courseList.forEach(course => {
|
|
|
// 搜索课程
|
|
|
- if (course.name.toLowerCase().includes(query)) {
|
|
|
+ if (course.courseName.toLowerCase().includes(query)) {
|
|
|
results.push({
|
|
|
- id: course.id,
|
|
|
- name: course.name,
|
|
|
+ id: course.courseId,
|
|
|
+ name: course.courseName,
|
|
|
type: 'course'
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// 搜索作业
|
|
|
course.assignments.forEach(assignment => {
|
|
|
- if (assignment.name.toLowerCase().includes(query)) {
|
|
|
+ if (assignment.assignmentName.toLowerCase().includes(query)) {
|
|
|
results.push({
|
|
|
- id: assignment.id,
|
|
|
- name: assignment.name,
|
|
|
- courseName: course.name,
|
|
|
+ id: assignment.assignmentId,
|
|
|
+ name: assignment.assignmentName,
|
|
|
+ courseName: course.courseName,
|
|
|
type: 'assignment'
|
|
|
})
|
|
|
}
|
|
|
@@ -575,14 +807,14 @@
|
|
|
const handleSearchResultClick = (result) => {
|
|
|
if (result.type === 'course') {
|
|
|
// 展开对应的课程
|
|
|
- const course = courseList.find(c => c.id === result.id)
|
|
|
+ const course = courseList.find(c => c.courseId === result.id)
|
|
|
if (course) {
|
|
|
course.expanded = true
|
|
|
}
|
|
|
} else {
|
|
|
// 选中对应的作业并展开对应课程
|
|
|
courseList.forEach(course => {
|
|
|
- const assignment = course.assignments.find(a => a.id === result.id)
|
|
|
+ const assignment = course.assignments.find(a => a.assignmentId === result.id)
|
|
|
if (assignment) {
|
|
|
course.expanded = true
|
|
|
}
|
|
|
@@ -594,6 +826,11 @@
|
|
|
searchQuery.value = ''
|
|
|
searchResults.value = []
|
|
|
}
|
|
|
+
|
|
|
+ // 组件挂载时加载数据
|
|
|
+ onMounted(() => {
|
|
|
+ loadCourses()
|
|
|
+ })
|
|
|
</script>
|
|
|
|
|
|
<script lang="ts">
|
|
|
@@ -610,10 +847,8 @@ export default {
|
|
|
}
|
|
|
|
|
|
.left-sidebar {
|
|
|
- min-width: 320px;
|
|
|
- max-width: 320px;
|
|
|
- width: 320px;
|
|
|
- background-color: white;
|
|
|
+ width: 280px;
|
|
|
+ background-color: #F0F2F5;
|
|
|
border-right: 1px solid #e8e8e8;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
@@ -781,6 +1016,20 @@ export default {
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
+/* 加载状态样式 */
|
|
|
+.loading-container {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.loading-text {
|
|
|
+ color: #606266;
|
|
|
+ font-size: 14px;
|
|
|
+ font-family: "Noto Sans SC", sans-serif;
|
|
|
+}
|
|
|
+
|
|
|
.right-content {
|
|
|
flex: 1;
|
|
|
background-color: white;
|