|
@@ -4,91 +4,208 @@
|
|
|
Created Date: 2024-7-3
|
|
Created Date: 2024-7-3
|
|
|
-->
|
|
-->
|
|
|
<template>
|
|
<template>
|
|
|
- <el-card style="margin: 10px auto 0;height: 100%; width: 80%" shadow="never">
|
|
|
|
|
- <el-button :icon="Refresh" circle @click="doRefresh"></el-button>
|
|
|
|
|
- <el-select
|
|
|
|
|
- v-model="state"
|
|
|
|
|
- placeholder="状态"
|
|
|
|
|
- size="default"
|
|
|
|
|
- style="width: 150px; margin-left: 10px;"
|
|
|
|
|
- @change="handleSearch"
|
|
|
|
|
- >
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="item in stateOptions"
|
|
|
|
|
- :key="item.value"
|
|
|
|
|
- :label="item.label"
|
|
|
|
|
- :value="item.value"
|
|
|
|
|
- />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <el-input v-model="assignmentName" placeholder="请输入作业名称" clearable style="width: 180px;"/>
|
|
|
|
|
- <el-button @click="handleSearch" color="#597D3B">
|
|
|
|
|
- <el-icon style="margin-right: 3px" ><Search/></el-icon>
|
|
|
|
|
- 查询
|
|
|
|
|
- </el-button>
|
|
|
|
|
-
|
|
|
|
|
- <el-table :data="pageInfo.tableData" style="width: 100%" height="100%" show-overflow-tooltip>
|
|
|
|
|
- <template #empty>
|
|
|
|
|
- 暂无作业
|
|
|
|
|
- </template>
|
|
|
|
|
- <el-table-column type="index"/>
|
|
|
|
|
- <el-table-column prop="assignmentName" label="课程名称" width="400"/>
|
|
|
|
|
- <el-table-column prop="type" label="类型" width="100"/>
|
|
|
|
|
-<!-- <el-table-column prop="description" label="描述" width="400"/>-->
|
|
|
|
|
- <el-table-column prop="endTime" label="截止时间" sortable/>
|
|
|
|
|
- <el-table-column prop="status" label="状态" sortable>
|
|
|
|
|
- <template #default="{row}">
|
|
|
|
|
- <el-tag v-if="row.status === AssignmentStatus.NOT_STARTED" type="info">未发布</el-tag>
|
|
|
|
|
- <el-tag v-else-if="row.status === AssignmentStatus.PROCEEDING" type="success">进行中</el-tag>
|
|
|
|
|
- <el-tag v-else-if="row.status === AssignmentStatus.FINISHED" type="danger">已截止</el-tag>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="操作" width="100">
|
|
|
|
|
- <template #default="scope">
|
|
|
|
|
- <el-button @click="handleShowDetail(scope.row.assignmentId)" style="margin-right: 0; margin-left: 0;padding-left: 10px; padding-right: 10px" color="#597D3B">
|
|
|
|
|
- <el-icon style="margin-right: 3px"><ZoomIn/></el-icon>
|
|
|
|
|
- 详情
|
|
|
|
|
|
|
+ <div class="homework-page">
|
|
|
|
|
+ <!-- 左侧课程菜单栏 -->
|
|
|
|
|
+ <div class="left-sidebar">
|
|
|
|
|
+ <div class="course-list">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="course in courseList"
|
|
|
|
|
+ :key="course.courseId"
|
|
|
|
|
+ class="course-item"
|
|
|
|
|
+ :class="{ 'active': selectedCourseId === course.courseId }"
|
|
|
|
|
+ @click="selectCourse(course.courseId)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="course-name">{{ course.courseName }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 右侧内容区域 -->
|
|
|
|
|
+ <div class="right-content">
|
|
|
|
|
+ <!-- 空白状态 -->
|
|
|
|
|
+ <div v-if="!selectedCourseId" class="empty-state">
|
|
|
|
|
+ <div class="empty-content">
|
|
|
|
|
+ <div class="empty-text">请选择一门课程查看作业</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 作业内容区域 -->
|
|
|
|
|
+ <div v-else class="assignment-content">
|
|
|
|
|
+ <!-- 作业列表标题 -->
|
|
|
|
|
+ <h2 class="assignment-title">作业列表</h2>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 搜索框 -->
|
|
|
|
|
+ <div class="search-section">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="searchQuery"
|
|
|
|
|
+ placeholder="Please enter"
|
|
|
|
|
+ class="search-input"
|
|
|
|
|
+ @input="handleSearch"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #suffix>
|
|
|
|
|
+ <el-icon class="search-icon"><Search /></el-icon>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-input>
|
|
|
|
|
+ <el-button class="reset-btn" @click="resetSearch">Reset</el-button>
|
|
|
|
|
+ <el-button class="query-btn" @click="handleSearch">Query</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 筛选栏 -->
|
|
|
|
|
+ <div class="filter-section">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-for="status in statusFilters"
|
|
|
|
|
+ :key="status.value"
|
|
|
|
|
+ :class="['filter-btn', { 'active': selectedStatus === status.value }]"
|
|
|
|
|
+ @click="selectStatus(status.value)"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ status.label }}
|
|
|
</el-button>
|
|
</el-button>
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="查看" width="150">
|
|
|
|
|
- <template #default="scope">
|
|
|
|
|
- <el-button @click="viewCorrection(scope.row.assignmentId,store.user.id,scope.row.type, scope.row.assignmentName)" style="margin-right: 0; margin-left: 0;padding-left: 10px; padding-right: 10px" color="#597D3B">
|
|
|
|
|
- <el-icon style="margin-right: 3px"><ZoomIn/></el-icon>
|
|
|
|
|
- 批改结果
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 第二个筛选栏 -->
|
|
|
|
|
+ <div class="filter-section">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-for="status in secondStatusFilters"
|
|
|
|
|
+ :key="status.value"
|
|
|
|
|
+ :class="['filter-btn', { 'active': selectedSecondStatus === status.value }]"
|
|
|
|
|
+ @click="selectSecondStatus(status.value)"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ status.label }}
|
|
|
</el-button>
|
|
</el-button>
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- </el-table>
|
|
|
|
|
-
|
|
|
|
|
- <el-pagination
|
|
|
|
|
- small
|
|
|
|
|
- style="font-size: 16px;margin-top: 10px;margin-left: 450px"
|
|
|
|
|
- @current-change="handleCurrentChange"
|
|
|
|
|
- @size-change="handleSizeChange"
|
|
|
|
|
- :page-sizes="[5, 10, 15]"
|
|
|
|
|
- :current-page="pageInfo.pages"
|
|
|
|
|
- :page-size="pageInfo.size"
|
|
|
|
|
- layout="total, sizes, prev, pager, next, jumper"
|
|
|
|
|
- :total="pageInfo.total">
|
|
|
|
|
- </el-pagination>
|
|
|
|
|
- </el-card>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 作业卡片列表 -->
|
|
|
|
|
+ <div class="assignment-cards">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="assignment in filteredAssignments"
|
|
|
|
|
+ :key="assignment.assignmentId"
|
|
|
|
|
+ class="assignment-card"
|
|
|
|
|
+ @click="handleShowDetail(assignment.assignmentId)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <!-- 左侧状态装饰条和圆点 -->
|
|
|
|
|
+ <div class="card-accent">
|
|
|
|
|
+ <div class="accent-line" :class="getStatusClass(assignment)"></div>
|
|
|
|
|
+ <div class="accent-dot" :class="getStatusClass(assignment)"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 卡片内容 -->
|
|
|
|
|
+ <div class="card-content">
|
|
|
|
|
+ <!-- 左侧信息 -->
|
|
|
|
|
+ <div class="left-info">
|
|
|
|
|
+ <div class="assignment-name">{{ assignment.assignmentName }}</div>
|
|
|
|
|
+ <div class="assignment-type">{{ assignment.type }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 中间状态信息 -->
|
|
|
|
|
+ <div class="middle-info">
|
|
|
|
|
+ <div class="status-badge" :class="getSecondStatusClass(assignment)">
|
|
|
|
|
+ <el-icon class="status-icon" v-if="getSecondStatusText(assignment) === '已批改'"><Check /></el-icon>
|
|
|
|
|
+ <el-icon class="status-icon" v-else-if="getSecondStatusText(assignment) === '(补交)已批改'"><Check /></el-icon>
|
|
|
|
|
+ <el-icon class="status-icon" v-else-if="getSecondStatusText(assignment) === '已提交'"><Calendar /></el-icon>
|
|
|
|
|
+ <el-icon class="status-icon" v-else-if="getSecondStatusText(assignment) === '(补交)未批改'"><Calendar /></el-icon>
|
|
|
|
|
+ <el-icon class="status-icon" v-else><Close /></el-icon>
|
|
|
|
|
+ <span>{{ getSecondStatusText(assignment) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 右侧时间信息 -->
|
|
|
|
|
+ <div class="right-info">
|
|
|
|
|
+ <div class="time-row">
|
|
|
|
|
+ <span class="time-label">开始时间:</span>
|
|
|
|
|
+ <span class="time-value">{{ assignment.startTime || '未设置' }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="time-row">
|
|
|
|
|
+ <span class="time-label">截止时间:</span>
|
|
|
|
|
+ <span class="time-value">{{ assignment.endTime }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 操作按钮 -->
|
|
|
|
|
+ <div class="card-actions" @click.stop>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ class="detail-btn"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="handleShowDetail(assignment.assignmentId)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 详情
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ class="result-btn"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ :disabled="getSecondStatusText(assignment) !== '已批改' && getSecondStatusText(assignment) !== '(补交)已批改'"
|
|
|
|
|
+ @click="viewCorrection(assignment.assignmentId, store.user.id, assignment.type, assignment.assignmentName)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 批改结果
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
<script lang="ts" setup>
|
|
|
- import {Refresh, ZoomIn, Search} from "@element-plus/icons-vue";
|
|
|
|
|
- import {inject, onMounted, reactive, ref} from "vue";
|
|
|
|
|
|
|
+ import {Search} from "@element-plus/icons-vue";
|
|
|
|
|
+ import {Calendar,Check,Close} from "@element-plus/icons-vue";
|
|
|
|
|
+ import {onMounted, reactive, ref, computed, watch} from "vue";
|
|
|
import useApis from "@/apis";
|
|
import useApis from "@/apis";
|
|
|
import {useStore} from "@/store";
|
|
import {useStore} from "@/store";
|
|
|
- import type {CourseVO, IAssignment} from "@/types/vo";
|
|
|
|
|
|
|
+ import type {CourseVO, IAssignment, StudentCourseAssignmentsResponse, StudentAssignmentStatus} from "@/types/vo";
|
|
|
import router from "@/router";
|
|
import router from "@/router";
|
|
|
- import {AssignmentStatus} from "@/types/enums";
|
|
|
|
|
|
|
+ import {useRoute} from "vue-router";
|
|
|
|
|
+
|
|
|
import {useFormatDate} from "@/hooks/useFormatDate";
|
|
import {useFormatDate} from "@/hooks/useFormatDate";
|
|
|
|
|
|
|
|
const apis = useApis()
|
|
const apis = useApis()
|
|
|
const store = useStore()
|
|
const store = useStore()
|
|
|
- const doRefresh:Function = inject("reload")
|
|
|
|
|
const {formatDate} = useFormatDate()
|
|
const {formatDate} = useFormatDate()
|
|
|
const role:'teachers' | 'student' = store.user.role === 'STUDENT' ? 'student':'teachers';
|
|
const role:'teachers' | 'student' = store.user.role === 'STUDENT' ? 'student':'teachers';
|
|
|
|
|
+
|
|
|
|
|
+ // 获取路由信息
|
|
|
|
|
+ const route = useRoute()
|
|
|
|
|
+
|
|
|
|
|
+ // 课程列表
|
|
|
|
|
+ const courseList = ref<CourseVO[]>([])
|
|
|
|
|
+
|
|
|
|
|
+ // 选中的课程ID
|
|
|
|
|
+ const selectedCourseId = ref<number | null>(null)
|
|
|
|
|
+
|
|
|
|
|
+ // 搜索查询
|
|
|
|
|
+ const searchQuery = ref('')
|
|
|
|
|
+
|
|
|
|
|
+ // 选中的状态
|
|
|
|
|
+ const selectedStatus = ref('全部')
|
|
|
|
|
+
|
|
|
|
|
+ // 状态筛选选项
|
|
|
|
|
+ const statusFilters = [
|
|
|
|
|
+ { label: '全部', value: '全部' },
|
|
|
|
|
+ { label: '未发布', value: '未发布' },
|
|
|
|
|
+ { label: '进行中', value: '进行中' },
|
|
|
|
|
+ { label: '已截止', value: '已截止' }
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ // 选中的第二个状态
|
|
|
|
|
+ const selectedSecondStatus = ref('全部')
|
|
|
|
|
+
|
|
|
|
|
+ // 第二个状态筛选选项
|
|
|
|
|
+ const secondStatusFilters = [
|
|
|
|
|
+ { label: '全部', value: '全部' },
|
|
|
|
|
+ { label: '已批改', value: '已批改' },
|
|
|
|
|
+ { label: '已提交', value: '已提交' },
|
|
|
|
|
+ { label: '未完成', value: '未完成' }
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ // 当前课程的作业列表
|
|
|
|
|
+ const currentCourseAssignments = ref<IAssignment[]>([])
|
|
|
|
|
+
|
|
|
|
|
+ // 所有作业数据
|
|
|
|
|
+ const allAssignments = ref<IAssignment[]>([])
|
|
|
|
|
+
|
|
|
|
|
+ // 学生作业状态数据
|
|
|
|
|
+ const studentAssignmentStatus = ref<Map<number, StudentAssignmentStatus[]>>(new Map())
|
|
|
|
|
|
|
|
// 获取的所有作业
|
|
// 获取的所有作业
|
|
|
let homeworkList = reactive<{
|
|
let homeworkList = reactive<{
|
|
@@ -119,7 +236,7 @@
|
|
|
// 查看教师批改结果 跳转至批改页面
|
|
// 查看教师批改结果 跳转至批改页面
|
|
|
const viewCorrection = (assignmentId:number,studentId:number, type:string, assignmentName:string) => {
|
|
const viewCorrection = (assignmentId:number,studentId:number, type:string, assignmentName:string) => {
|
|
|
if(type === '写作' || type ==='writing'){
|
|
if(type === '写作' || type ==='writing'){
|
|
|
- router.push(`/home/assignment/${assignmentId}/correct?studentId=${studentId}`)
|
|
|
|
|
|
|
+ router.push(`/home/assignment/${assignmentId}/correct?studentId=${studentId}`).then(() => {window.location.reload()})
|
|
|
} else if(type === 'AI口语' || type === 'speaking'){
|
|
} else if(type === 'AI口语' || type === 'speaking'){
|
|
|
router.push({
|
|
router.push({
|
|
|
path: `/home/assignment/${assignmentId}/airecord`,
|
|
path: `/home/assignment/${assignmentId}/airecord`,
|
|
@@ -127,33 +244,18 @@
|
|
|
userId: studentId,
|
|
userId: studentId,
|
|
|
assignmentId: assignmentId,
|
|
assignmentId: assignmentId,
|
|
|
assignmentName: assignmentName
|
|
assignmentName: assignmentName
|
|
|
- }});
|
|
|
|
|
|
|
+ }}).then(() => {window.location.reload()});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleShowDetail = (assignmentId:number) => {
|
|
const handleShowDetail = (assignmentId:number) => {
|
|
|
- router.push(`/home/homeworkDetail/${assignmentId}`)
|
|
|
|
|
|
|
+ router.push(`/home/homeworkDetail/${assignmentId}`).then(() => {window.location.reload()})
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const handleCurrentChange = (pageNum: number) => {
|
|
|
|
|
- Object.assign(pageInfo, {
|
|
|
|
|
- tableData: getSlice(pageNum, pageInfo.size),
|
|
|
|
|
- size: pageInfo.size,
|
|
|
|
|
- pages: pageNum,
|
|
|
|
|
- total: processedHomeworkList.tableData.length,
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- const handleSizeChange = (pageSize: number) => {
|
|
|
|
|
- Object.assign(pageInfo, {
|
|
|
|
|
- tableData: getSlice(1, pageSize),
|
|
|
|
|
- size: pageSize,
|
|
|
|
|
- pages: 1,
|
|
|
|
|
- total: processedHomeworkList.tableData.length,
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
|
const fetchData = () => {
|
|
const fetchData = () => {
|
|
@@ -164,9 +266,10 @@
|
|
|
let total = res.data.data.total;
|
|
let total = res.data.data.total;
|
|
|
apis.getCourseByStudentId(1, total)
|
|
apis.getCourseByStudentId(1, total)
|
|
|
.then((res: any) => {
|
|
.then((res: any) => {
|
|
|
- let data:CourseVO[] = res.data.data.records
|
|
|
|
|
- for(let i = 0; i < data.length; i++){
|
|
|
|
|
- fetchHomeworkListByCourseId(data[i].courseId)
|
|
|
|
|
|
|
+ courseList.value = res.data.data.records
|
|
|
|
|
+ // 为每个课程获取作业数据
|
|
|
|
|
+ for(let i = 0; i < courseList.value.length; i++){
|
|
|
|
|
+ fetchHomeworkListByCourseId(courseList.value[i].courseId)
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
@@ -177,26 +280,269 @@
|
|
|
let total = res.data.data.total;
|
|
let total = res.data.data.total;
|
|
|
apis.getCourseByTeacherId(1, total)
|
|
apis.getCourseByTeacherId(1, total)
|
|
|
.then((res: any) => {
|
|
.then((res: any) => {
|
|
|
- let data:CourseVO[] = res.data.data.records
|
|
|
|
|
- for(let i = 0; i < data.length; i++){
|
|
|
|
|
- fetchHomeworkListByCourseId(data[i].courseId)
|
|
|
|
|
|
|
+ courseList.value = res.data.data.records
|
|
|
|
|
+ // 为每个课程获取作业数据
|
|
|
|
|
+ for(let i = 0; i < courseList.value.length; i++){
|
|
|
|
|
+ fetchHomeworkListByCourseId(courseList.value[i].courseId)
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 选择课程
|
|
|
|
|
+ const selectCourse = (courseId: number) => {
|
|
|
|
|
+ console.log('selectCourse 被调用,课程ID:', courseId)
|
|
|
|
|
+ selectedCourseId.value = courseId
|
|
|
|
|
+ // 筛选当前课程的作业
|
|
|
|
|
+ currentCourseAssignments.value = allAssignments.value.filter(
|
|
|
|
|
+ assignment => assignment.courseId === courseId
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ console.log('当前role:', role)
|
|
|
|
|
+ console.log('studentAssignmentStatus.value.has(courseId):', studentAssignmentStatus.value.has(courseId))
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是学生角色且还没有该课程的作业状态数据,主动获取
|
|
|
|
|
+ if (role === 'student' && !studentAssignmentStatus.value.has(courseId)) {
|
|
|
|
|
+ console.log('没有找到课程状态数据,主动获取...')
|
|
|
|
|
+ fetchStudentAssignmentStatus(courseId)
|
|
|
|
|
+ } else if (role === 'student') {
|
|
|
|
|
+ console.log('已存在课程状态数据:', studentAssignmentStatus.value.get(courseId))
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log('选中课程后,当前课程作业数量:', currentCourseAssignments.value.length)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理路由参数 - 从其他页面返回时自动选中课程
|
|
|
|
|
+ const handleRouteParams = () => {
|
|
|
|
|
+ const { courseId, fromAssignment } = route.query
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 如果有课程ID参数且是从作业页面返回的
|
|
|
|
|
+ if (courseId && fromAssignment === 'true') {
|
|
|
|
|
+ const targetCourseId = Number(courseId)
|
|
|
|
|
+
|
|
|
|
|
+ // 检查目标课程是否存在
|
|
|
|
|
+ const targetCourse = courseList.value.find(course => course.courseId === targetCourseId)
|
|
|
|
|
+ if (targetCourse) {
|
|
|
|
|
+ selectCourse(targetCourseId)
|
|
|
|
|
+
|
|
|
|
|
+ // 清除路由参数,避免重复处理
|
|
|
|
|
+ router.replace({
|
|
|
|
|
+ name: 'myHomework',
|
|
|
|
|
+ query: {}
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.log('未找到目标课程')
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 重置搜索
|
|
|
|
|
+ const resetSearch = () => {
|
|
|
|
|
+ searchQuery.value = ''
|
|
|
|
|
+ selectedStatus.value = '全部'
|
|
|
|
|
+ selectedSecondStatus.value = '全部'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 选择状态
|
|
|
|
|
+ const selectStatus = (status: string) => {
|
|
|
|
|
+ selectedStatus.value = status
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 选择第二个状态
|
|
|
|
|
+ const selectSecondStatus = (status: string) => {
|
|
|
|
|
+ selectedSecondStatus.value = status
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 筛选后的作业列表
|
|
|
|
|
+ const filteredAssignments = computed(() => {
|
|
|
|
|
+ let filtered = [...currentCourseAssignments.value]
|
|
|
|
|
+
|
|
|
|
|
+ // 按搜索关键词筛选
|
|
|
|
|
+ if (searchQuery.value) {
|
|
|
|
|
+ filtered = filtered.filter(assignment =>
|
|
|
|
|
+ assignment.assignmentName.toLowerCase().includes(searchQuery.value.toLowerCase())
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 按状态筛选
|
|
|
|
|
+ if (selectedStatus.value !== '全部') {
|
|
|
|
|
+ filtered = filtered.filter(assignment => getActualStatus(assignment) === selectedStatus.value)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 按第二个状态筛选
|
|
|
|
|
+ if (selectedSecondStatus.value !== '全部') {
|
|
|
|
|
+ filtered = filtered.filter(assignment => getSecondStatusText(assignment) === selectedSecondStatus.value)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return filtered
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 获取作业的实际状态(基于开始时间和截止时间)
|
|
|
|
|
+ const getActualStatus = (assignment: IAssignment) => {
|
|
|
|
|
+ const now = new Date()
|
|
|
|
|
+
|
|
|
|
|
+ // 确保能正确解析结束日期,支持多种格式
|
|
|
|
|
+ let endTime: Date
|
|
|
|
|
+
|
|
|
|
|
+ // 如果endTime是字符串,尝试解析
|
|
|
|
|
+ if (typeof assignment.endTime === 'string') {
|
|
|
|
|
+ // 处理 "YYYY-MM-DD HH:mm:ss" 格式
|
|
|
|
|
+ endTime = new Date(assignment.endTime.replace(' ', 'T'))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ endTime = new Date(assignment.endTime)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查结束日期是否有效
|
|
|
|
|
+ if (isNaN(endTime.getTime())) {
|
|
|
|
|
+ console.warn('Invalid end date format:', assignment.endTime)
|
|
|
|
|
+ return '进行中' // 默认返回进行中
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 解析开始时间(如果存在)
|
|
|
|
|
+ let startTime: Date | null = null
|
|
|
|
|
+ if (assignment.startTime) {
|
|
|
|
|
+ if (typeof assignment.startTime === 'string') {
|
|
|
|
|
+ startTime = new Date(assignment.startTime.replace(' ', 'T'))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ startTime = new Date(assignment.startTime)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查开始日期是否有效
|
|
|
|
|
+ if (isNaN(startTime.getTime())) {
|
|
|
|
|
+ console.warn('Invalid start date format:', assignment.startTime)
|
|
|
|
|
+ startTime = null
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 判断状态优先级:未发布 > 已截止 > 进行中
|
|
|
|
|
+ if (startTime && now < startTime) {
|
|
|
|
|
+ return '未发布'
|
|
|
|
|
+ } else if (now > endTime) {
|
|
|
|
|
+ return '已截止'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return '进行中'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取状态样式类
|
|
|
|
|
+ const getStatusClass = (assignment: IAssignment) => {
|
|
|
|
|
+ const actualStatus = getActualStatus(assignment)
|
|
|
|
|
+ switch(actualStatus) {
|
|
|
|
|
+ case '未发布':
|
|
|
|
|
+ return 'status-unpublished'
|
|
|
|
|
+ case '进行中':
|
|
|
|
|
+ return 'status-proceeding'
|
|
|
|
|
+ case '已截止':
|
|
|
|
|
+ return 'status-finished'
|
|
|
|
|
+ default:
|
|
|
|
|
+ return 'status-unknown'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 获取第二个状态的文本
|
|
|
|
|
+ const getSecondStatusText = (assignment: IAssignment) => {
|
|
|
|
|
+ if (role === 'student') {
|
|
|
|
|
+ // 从真实API数据中获取状态
|
|
|
|
|
+ const courseStatus = studentAssignmentStatus.value.get(assignment.courseId)
|
|
|
|
|
+ console.log(2)
|
|
|
|
|
+ if (courseStatus) {
|
|
|
|
|
+ console.log(1)
|
|
|
|
|
+ const assignmentStatus = courseStatus.find(status => status.assignmentId === assignment.assignmentId)
|
|
|
|
|
+ if (assignmentStatus) {
|
|
|
|
|
+ switch (assignmentStatus.status) {
|
|
|
|
|
+ case 'CORRECTED':
|
|
|
|
|
+ return '已批改'
|
|
|
|
|
+ case 'SUBMITTED':
|
|
|
|
|
+ return '已提交'
|
|
|
|
|
+ case 'NOT_SUBMITTED':
|
|
|
|
|
+ return '未完成'
|
|
|
|
|
+ case 'LATE_SUBMITTED':
|
|
|
|
|
+ return '(补交)未批改'
|
|
|
|
|
+ case 'LATE_SUBMITTED_CORRECTED':
|
|
|
|
|
+ return '(补交)已批改'
|
|
|
|
|
+ default:
|
|
|
|
|
+ return '未完成'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return '未完成'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取第二个状态的样式类
|
|
|
|
|
+ const getSecondStatusClass = (assignment: IAssignment) => {
|
|
|
|
|
+ const statusText = getSecondStatusText(assignment)
|
|
|
|
|
+ switch(statusText) {
|
|
|
|
|
+ case '已批改':
|
|
|
|
|
+ case '已提交':
|
|
|
|
|
+ return 'status-submitted'
|
|
|
|
|
+ case '未完成':
|
|
|
|
|
+ return 'status-incomplete'
|
|
|
|
|
+ case '(补交)已批改':
|
|
|
|
|
+ case '(补交)未批改':
|
|
|
|
|
+ return 'status-late-submitted'
|
|
|
|
|
+ default:
|
|
|
|
|
+ return 'status-incomplete'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取学生作业状态数据
|
|
|
|
|
+ const fetchStudentAssignmentStatus = (courseId: number) => {
|
|
|
|
|
+ if (role === 'student') {
|
|
|
|
|
+ console.log('开始获取学生作业状态数据,课程ID:', courseId)
|
|
|
|
|
+ apis.getStudentAssignmentsByCourse(store.user.id.toString(), courseId.toString())
|
|
|
|
|
+ .then((res: any) => {
|
|
|
|
|
+ console.log('API响应:', res)
|
|
|
|
|
+ if (res.data.code === 200) {
|
|
|
|
|
+ const responseData: StudentCourseAssignmentsResponse = res.data.data
|
|
|
|
|
+ console.log('获取到学生作业状态数据:', responseData)
|
|
|
|
|
+ console.log('存储到Map中,courseId:', courseId, 'assignments:', responseData.assignments)
|
|
|
|
|
+ studentAssignmentStatus.value.set(courseId, responseData.assignments)
|
|
|
|
|
+ console.log('存储后的Map:', studentAssignmentStatus.value)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.log('API返回错误代码:', res.data.code, res.data.msg)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch((error: any) => {
|
|
|
|
|
+ console.error('获取学生作业状态数据失败:', error)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
const fetchHomeworkListByCourseId = (courseId:number) => {
|
|
const fetchHomeworkListByCourseId = (courseId:number) => {
|
|
|
|
|
+ // console.log('开始获取课程', courseId, '的作业数据')
|
|
|
apis.getAssignmentByCourseId(courseId)
|
|
apis.getAssignmentByCourseId(courseId)
|
|
|
.then((res: any) => {
|
|
.then((res: any) => {
|
|
|
let total = res.data.data.total
|
|
let total = res.data.data.total
|
|
|
|
|
+ // console.log('课程', courseId, '总作业数:', total)
|
|
|
apis.getAssignmentByCourseId(courseId, 1, total)
|
|
apis.getAssignmentByCourseId(courseId, 1, total)
|
|
|
.then((res: any) => {
|
|
.then((res: any) => {
|
|
|
let data = res.data.data.records
|
|
let data = res.data.data.records
|
|
|
- for(let i = 0; i < data.length; i++) {
|
|
|
|
|
- data[i].endTime = formatDate(data[i].endTime)
|
|
|
|
|
- homeworkList.tableData.push(data[i])
|
|
|
|
|
|
|
+ // console.log('课程', courseId, '获取到', data.length, '个作业')
|
|
|
|
|
+ for(let i = 0; i < data.length; i++) {
|
|
|
|
|
+ data[i].endTime = formatDate(data[i].endTime)
|
|
|
|
|
+ data[i].startTime = data[i].startTime ? formatDate(data[i].startTime) : null
|
|
|
|
|
+ data[i].courseId = courseId // 添加课程ID
|
|
|
|
|
+ homeworkList.tableData.push(data[i])
|
|
|
|
|
+ allAssignments.value.push(data[i]) // 添加到所有作业数组
|
|
|
|
|
+ // console.log('添加作业:', data[i].assignmentName, 'courseId:', data[i].courseId)
|
|
|
|
|
+ }
|
|
|
|
|
+ // console.log('课程', courseId, '作业加载完成,当前allAssignments总数:', allAssignments.value.length)
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是学生角色,获取作业状态数据
|
|
|
|
|
+ if (role === 'student') {
|
|
|
|
|
+ fetchStudentAssignmentStatus(courseId)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果有选中的课程且正好是这个课程ID,立即重新筛选
|
|
|
|
|
+ if (selectedCourseId.value === courseId) {
|
|
|
|
|
+ // console.log('检测到当前选中课程', courseId, '的作业已加载,重新筛选')
|
|
|
|
|
+ selectCourse(courseId)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
processedHomeworkList.tableData = homeworkList.tableData
|
|
processedHomeworkList.tableData = homeworkList.tableData
|
|
|
Object.assign(pageInfo, {
|
|
Object.assign(pageInfo, {
|
|
|
tableData: getSlice(pageInfo.pages, pageInfo.size),
|
|
tableData: getSlice(pageInfo.pages, pageInfo.size),
|
|
@@ -208,21 +554,9 @@
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 旧的handleSearch方法保留用于兼容性
|
|
|
const handleSearch = () => {
|
|
const handleSearch = () => {
|
|
|
- processedHomeworkList.tableData = []
|
|
|
|
|
- for(let i = 0; i < homeworkList.tableData.length; i++){
|
|
|
|
|
- let assignmentItem = homeworkList.tableData[i]
|
|
|
|
|
- let input = true;
|
|
|
|
|
- if(state.value != null && state.value != assignmentItem.status) input = false;
|
|
|
|
|
- if(assignmentName.value != '' && !assignmentItem.assignmentName.includes(assignmentName.value)) input = false;
|
|
|
|
|
- if(input) processedHomeworkList.tableData.push(assignmentItem);
|
|
|
|
|
- Object.assign(pageInfo, {
|
|
|
|
|
- tableData: getSlice(pageInfo.pages, pageInfo.size),
|
|
|
|
|
- size: pageInfo.size,
|
|
|
|
|
- pages: pageInfo.pages,
|
|
|
|
|
- total: processedHomeworkList.tableData.length,
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 新版本中不需要实现,因为使用了响应式筛选
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//用于分页展示,截取数据
|
|
//用于分页展示,截取数据
|
|
@@ -230,27 +564,46 @@
|
|
|
return processedHomeworkList.tableData.slice((page-1)*size, page*size)
|
|
return processedHomeworkList.tableData.slice((page-1)*size, page*size)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 监听课程列表变化,数据加载完成后处理路由参数
|
|
|
|
|
+ watch(courseList, (newCourseList) => {
|
|
|
|
|
+ // console.log('courseList 变化,课程数量:', newCourseList.length)
|
|
|
|
|
+ if (newCourseList.length > 0) {
|
|
|
|
|
+ handleRouteParams()
|
|
|
|
|
+ }
|
|
|
|
|
+ }, { immediate: false })
|
|
|
|
|
+
|
|
|
|
|
+ // 监听作业数据变化,确保作业数据也加载完成
|
|
|
|
|
+ watch(allAssignments, (newAssignments) => {
|
|
|
|
|
+ // console.log('allAssignments 变化,作业数量:', newAssignments.length)
|
|
|
|
|
+
|
|
|
|
|
+ // 如果有选中的课程且作业数据更新了,重新筛选作业
|
|
|
|
|
+ if (selectedCourseId.value && newAssignments.length > 0) {
|
|
|
|
|
+ // console.log('重新筛选选中课程的作业,课程ID:', selectedCourseId.value)
|
|
|
|
|
+ selectCourse(selectedCourseId.value)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果作业数据加载完成,再次尝试处理路由参数
|
|
|
|
|
+ if (newAssignments.length > 0) {
|
|
|
|
|
+ handleRouteParams()
|
|
|
|
|
+ }
|
|
|
|
|
+ }, { immediate: false })
|
|
|
|
|
+
|
|
|
|
|
+ // 监听学生作业状态数据变化
|
|
|
|
|
+ watch(studentAssignmentStatus, (newStatus) => {
|
|
|
|
|
+ console.log('studentAssignmentStatus 变化,课程数量:', newStatus.size)
|
|
|
|
|
+ // 当状态数据更新时,重新筛选当前选中课程的作业
|
|
|
|
|
+ if (selectedCourseId.value) {
|
|
|
|
|
+ currentCourseAssignments.value = allAssignments.value.filter(
|
|
|
|
|
+ assignment => assignment.courseId === selectedCourseId.value
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }, { immediate: false, deep: true })
|
|
|
|
|
+
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
fetchData()
|
|
fetchData()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- let state = ref(null)
|
|
|
|
|
- const stateOptions = [
|
|
|
|
|
- {
|
|
|
|
|
- value: AssignmentStatus.NOT_STARTED,
|
|
|
|
|
- label: "未发布"
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- value: AssignmentStatus.PROCEEDING,
|
|
|
|
|
- label: "进行中"
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- value: AssignmentStatus.FINISHED,
|
|
|
|
|
- label: "已截止"
|
|
|
|
|
- },
|
|
|
|
|
- ]
|
|
|
|
|
|
|
|
|
|
- let assignmentName = ref('')
|
|
|
|
|
|
|
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
@@ -259,3 +612,383 @@ export default {
|
|
|
name: "HomeworkPage"
|
|
name: "HomeworkPage"
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.homework-page {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ height: 95.5vh;
|
|
|
|
|
+ font-family: "Noto Sans SC", sans-serif;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 左侧菜单栏 */
|
|
|
|
|
+.left-sidebar {
|
|
|
|
|
+ width: 250px;
|
|
|
|
|
+ min-width: 250px;
|
|
|
|
|
+ max-width: 250px;
|
|
|
|
|
+ background: linear-gradient(180deg, #89CDF1 0%, #1890FF 100%);
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.course-list {
|
|
|
|
|
+ padding: 20px 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.course-item {
|
|
|
|
|
+ padding: 15px 25px;
|
|
|
|
|
+ color: white;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+ border-left: 4px solid transparent;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.course-item:hover {
|
|
|
|
|
+ background-color: rgba(255, 255, 255, 0.1);
|
|
|
|
|
+ border-left-color: white;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.course-item.active {
|
|
|
|
|
+ background-color: rgba(255, 255, 255, 0.2);
|
|
|
|
|
+ border-left-color: white;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.course-name {
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 右侧内容区域 */
|
|
|
|
|
+.right-content {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ background-color: white;
|
|
|
|
|
+ padding: 30px;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 空白状态 */
|
|
|
|
|
+.empty-state {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-content {
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-text {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 作业内容区域 */
|
|
|
|
|
+.assignment-content {
|
|
|
|
|
+ max-width: 1200px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.assignment-title {
|
|
|
|
|
+ font-size: 24px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #2c3e50;
|
|
|
|
|
+ margin-bottom: 0px;
|
|
|
|
|
+ margin-top: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 搜索区域 */
|
|
|
|
|
+.search-section {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 15px;
|
|
|
|
|
+ margin-bottom: 0px;
|
|
|
|
|
+ padding: 20px 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.search-input {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ max-width: 400px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.reset-btn, .query-btn {
|
|
|
|
|
+ padding: 8px 20px;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.reset-btn {
|
|
|
|
|
+ background-color: #f5f5f5;
|
|
|
|
|
+ border-color: #d9d9d9;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.query-btn {
|
|
|
|
|
+ background-color: #1890ff;
|
|
|
|
|
+ border-color: #1890ff;
|
|
|
|
|
+ color: white;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 筛选区域 */
|
|
|
|
|
+.filter-section {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-btn {
|
|
|
|
|
+ padding: 8px 16px;
|
|
|
|
|
+ border: 1px solid #d9d9d9;
|
|
|
|
|
+ background-color: white;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-btn:hover {
|
|
|
|
|
+ border-color: #1890ff;
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-btn.active {
|
|
|
|
|
+ background-color: #1890ff;
|
|
|
|
|
+ border-color: #1890ff;
|
|
|
|
|
+ color: white;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 作业卡片区域 */
|
|
|
|
|
+.assignment-cards {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.assignment-card {
|
|
|
|
|
+ background: white;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
|
|
+ min-height: 80px;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.assignment-card:hover {
|
|
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
|
|
|
|
+ transform: translateY(-1px);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 左侧状态装饰条和圆点 */
|
|
|
|
|
+.card-accent {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ width: 8px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.accent-line {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.accent-line.status-proceeding {
|
|
|
|
|
+ background-color: #52c41a; /* 绿色 - 进行中 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.accent-line.status-unpublished {
|
|
|
|
|
+ background-color: #faad14; /* 黄色 - 未发布 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.accent-line.status-finished {
|
|
|
|
|
+ background-color: #ff4d4f; /* 红色 - 已截止 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.accent-line.status-unknown {
|
|
|
|
|
+ background-color: #d9d9d9; /* 灰色 - 未知状态 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.accent-dot {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 20px;
|
|
|
|
|
+ top: 20px;
|
|
|
|
|
+ width: 14px;
|
|
|
|
|
+ height: 14px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.accent-dot.status-proceeding {
|
|
|
|
|
+ background-color: #52c41a; /* 绿色 - 进行中 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.accent-dot.status-unpublished {
|
|
|
|
|
+ background-color: #faad14; /* 黄色 - 未发布 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.accent-dot.status-finished {
|
|
|
|
|
+ background-color: #ff4d4f; /* 红色 - 已截止 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.accent-dot.status-unknown {
|
|
|
|
|
+ background-color: #d9d9d9; /* 灰色 - 未知状态 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 卡片内容 */
|
|
|
|
|
+.card-content {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 16px 20px 16px 30px;
|
|
|
|
|
+ gap: 20px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 左侧信息 */
|
|
|
|
|
+.left-info {
|
|
|
|
|
+ margin-left: 15px;
|
|
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
+ min-width: 200px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.assignment-name {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #2c3e50;
|
|
|
|
|
+ margin: 0 0 6px 0;
|
|
|
|
|
+ line-height: 1.3;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ max-width: 180px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.assignment-type {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ width: 50px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ background-color: #e6f7ff;
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+ font-weight: 400;
|
|
|
|
|
+ border-radius: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 中间状态信息 */
|
|
|
|
|
+.middle-info {
|
|
|
|
|
+ margin-left: 20px;
|
|
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ min-width: 120px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.status-badge {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+ padding: 6px 12px;
|
|
|
|
|
+ border-radius: 16px;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.status-badge.status-submitted {
|
|
|
|
|
+ background-color: #e6f7ff;
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.status-badge.status-late-submitted {
|
|
|
|
|
+ background-color: #fff2e8;
|
|
|
|
|
+ color: #fa8c16;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.status-badge.status-incomplete {
|
|
|
|
|
+ background-color: #fff2e8;
|
|
|
|
|
+ color: #ff4d4f;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.status-icon {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 右侧时间信息 */
|
|
|
|
|
+.right-info {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 4px;
|
|
|
|
|
+ align-items: flex-end;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.time-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.time-label {
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.time-value {
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 操作按钮 */
|
|
|
|
|
+.card-actions {
|
|
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ margin-left: auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.detail-btn {
|
|
|
|
|
+ background-color: #5397F8;
|
|
|
|
|
+ opacity: 1;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ color: #ffffff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.result-btn {
|
|
|
|
|
+ background-color: #1890ff;
|
|
|
|
|
+ border-color: #1890ff;
|
|
|
|
|
+ color: white;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.detail-btn:hover {
|
|
|
|
|
+ background-color: #5397F8;
|
|
|
|
|
+ opacity: 0.8;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.result-btn:hover {
|
|
|
|
|
+ background-color: #40a9ff;
|
|
|
|
|
+ border-color: #40a9ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 禁用状态的批改结果按钮样式 */
|
|
|
|
|
+.result-btn:disabled {
|
|
|
|
|
+ background-color: #f5f5f5;
|
|
|
|
|
+ border-color: #d9d9d9;
|
|
|
|
|
+ color: #bfbfbf;
|
|
|
|
|
+ opacity: 1;
|
|
|
|
|
+ cursor: not-allowed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.result-btn:disabled:hover {
|
|
|
|
|
+ background-color: #f5f5f5;
|
|
|
|
|
+ border-color: #d9d9d9;
|
|
|
|
|
+ color: #bfbfbf;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|