ソースを参照

Merge branch 'feature/new-teacher' of FanYanPeng/EAIFrontend into host-nju

LiuLuLin 1 年間 前
コミット
2803af4614

+ 17 - 1
src/apis/assignment.ts

@@ -33,6 +33,15 @@ const assignmentApis = {
             }
         })
     },
+    // 新增:获取学生在特定课程下的作业状态
+    getStudentAssignmentsByCourse(studentId: string, courseId: string) {
+        return axiosInstance.get("/api/course/student/assignments", {
+            params: {
+                studentId,
+                courseId
+            }
+        })
+    },
     updateAssignment(assignmentId:number, assignmentDTO:AssignmentDTO){
         return axiosInstance.post(`${ASSIGNMENT_PREFIX}/update`, assignmentDTO, {
             params: {
@@ -47,7 +56,14 @@ const assignmentApis = {
                 assignmentId
             }
         })
-
+    },
+    // 新增:查询某班级某作业的情况
+    getAssignmentStatusByClass: (classId: string, assignmentId: string) => {
+        return axiosInstance.get(`${ASSIGNMENT_PREFIX}/class/${classId}/status`, {
+            params: {
+                assignmentId
+            }
+        })
     }
 
 }

+ 8 - 0
src/apis/course.ts

@@ -82,6 +82,14 @@ const courseApis = {
             }
         })
     },
+    // 新增:查询某课程的所有班级
+    getClassesByCourse: (courseId: number) => {
+        return axiosInstance.get(`/api/class/select`, {
+            params: {
+                courseId
+            }
+        })
+    },
 }
 
 export default courseApis

+ 3 - 2
src/apis/engagement.ts

@@ -27,11 +27,12 @@ const engagementApis = {
         })
     },
     // 提交作业
-    engagementSubmit: (studentId: number, assignmentId: number) => {
+    engagementSubmit: (studentId: number, assignmentId: number,supplementarySubmission:boolean) => {
         return axiosInstance.put(ENGAGE_PREFIX+"/submit", {}, {
             params: {
                 studentId,
-                assignmentId
+                assignmentId,
+                supplementarySubmission
             }
         })
     },

+ 22 - 3
src/layout/Header/Header.vue

@@ -53,7 +53,7 @@
 <script setup lang="ts">
 import {User} from "@element-plus/icons-vue";
 import router from "../../router/index.js";
-import { onMounted, ref } from "vue";
+import { onMounted, ref, watch } from "vue";
 import './index.scss';
 import { useStore } from "@/store";
 import emitter from "@/utils/emitter";
@@ -93,20 +93,39 @@ const handleCommand = (command: string) => {
 const route = useRoute();
 let activePage = ref('');
 
-onMounted(() => {
+// 更新活跃页面状态的函数
+const updateActivePage = () => {
   let path = route.path.split('/')[1];
   if (path == "profile") {
     activePage.value = null;
+    return;
   }
 
   if (route.path.split('/')[1] == "home") {
     path = route.path.split('/')[2].charAt(0).toUpperCase() + route.path.split('/')[2].slice(1);
-    if (path === 'Assignment') {
+    if (path === 'Assignment' || path === 'MyHomework') {
       activePage.value = 'MyWork';
+    } else if (path === 'MyCorrect') {
+      // 处理 myCorrect 路径,需要检查第三级路径
+      const thirdPath = route.path.split('/')[3];
+      if (thirdPath === 'course' || thirdPath === 'assignment') {
+        activePage.value = 'MyWork';
+      } else {
+        activePage.value = path;
+      }
     } else {
       activePage.value = path;
     }
   }
+};
+
+// 监听路由变化
+watch(() => route.path, () => {
+  updateActivePage();
+}, { immediate: true });
+
+onMounted(() => {
+  updateActivePage();
 });
 </script>
 

+ 5 - 3
src/layout/Header/index.scss

@@ -34,17 +34,19 @@
       vertical-align: middle;
       color: #000000;
       height: 40px;
+      font-size: 18px;
+      margin-right: 20px;
 
       &:hover {
         background-color: #fff;
         color: #000000;
-        border-bottom: 2px solid #597d3b;
+        border-bottom: 2px solid #4A90C4;
       }
 
       &.is-active {
         background-color: #fff;
-        color: #597d3b !important;
-        border-bottom: 2px solid #597d3b;
+        color: #4A90C4 !important;
+        border-bottom: 2px solid #4A90C4;
       }
     }
   }

+ 1 - 1
src/router/index.ts

@@ -227,7 +227,7 @@ const router = createRouter({
  */
   router.beforeEach(async (to) => {
     const store = useStore();
-    const publicPaths = [ '/register', '/passwordChange', '/admin/login'];
+    const publicPaths = [ '/register', '/passwordChange', '/admin/login','];
     // 1. 检查URL中的token
     const urlToken = new URLSearchParams(window.location.search).get('token');
     if (urlToken) {

+ 19 - 0
src/types/dto.ts

@@ -85,3 +85,22 @@ export interface PasswordChangeDTO {
     officialEmail: string; 
     verifyCode: string; 
 }
+
+// 新增:班级信息接口
+export interface ClassDTO {
+    classId: number;
+    className: string;
+    courseId: number;
+    stuNumber: number;
+    classTime: string;
+    teacherId: number;
+}
+
+// 新增:学生作业状态接口
+export interface StudentAssignmentStatusDTO {
+    studentId: number;
+    officialNumber: string;
+    stuName: string;
+    status: string; // "CORRECTED" | "NOT_SUBMITTED" | "SUBMITTED"
+    score: number;
+}

+ 17 - 1
src/types/vo.ts

@@ -139,7 +139,23 @@ export interface CourseDetailVO {
     createTime: string;
     description: string;
 }
-//班级模块
+
+// 新增:学生作业状态接口
+export interface StudentAssignmentStatus {
+    assignmentId: number;
+    assignmentName: string;
+    status: 'CORRECTED' | 'SUBMITTED' | 'NOT_SUBMITTED'|'LATE_SUBMITTED_CORRECTED'|'LATE_SUBMITTED';
+    score: number;
+}
+
+// 新增:学生课程作业响应接口
+export interface StudentCourseAssignmentsResponse {
+    studentId: number;
+    officialNumber: string;
+    stuName: string;
+    assignments: StudentAssignmentStatus[];
+}
+
 export interface ClassVO {
     classId: number;
     className: string;

+ 68 - 15
src/views/AiSpeakingPage/SpeakingRecord.vue

@@ -2,7 +2,7 @@
   <div class="container">
     <div class="header-container">
       <!-- 返回按钮 -->
-      <el-button style="color:#597D3B" class="back-button" type="text" @click="goBack">
+      <el-button style="color:#4A90C4" class="back-button" type="text" @click="goBack">
         <el-icon><Back /></el-icon>
       </el-button>
       <!-- 标题 -->
@@ -255,7 +255,7 @@
             <div class="button-container">
               <div class="left-buttons">
                 <el-button 
-                  style="background-color:#597D3B;color:white;width:200px" 
+                  style="background-color:#4A90C4;color:white;width:200px" 
                   @click="submitComment" 
                   class="submit-button"
                   :loading="submitting"
@@ -264,7 +264,7 @@
                 </el-button>
                 
                 <el-button
-                  style="background-color:#597D3B;color:white;width:200px;margin-top:50px"
+                  style="background-color:#4A90C4;color:white;width:200px;margin-top:50px"
                   @click="handleNext"
                   :disabled="isAllCorrected"
                 >
@@ -272,14 +272,14 @@
                 </el-button>
                 <!-- 新增:评语模板 -->
                 <el-button
-                  style="background-color:#597D3B;color:white;width:200px;margin-top:50px;"
+                  style="background-color:#4A90C4;color:white;width:200px;margin-top:50px;"
                   @click="showTemplateDialog"
                 >
                   评语模板
                 </el-button>
 
                 <el-button
-                  style="background-color:#597D3B;color:white;width:200px;margin-top:50px;"
+                  style="background-color:#4A90C4;color:white;width:200px;margin-top:50px;"
                   @click="goBack"
                 >
                   返回
@@ -341,12 +341,18 @@ import engagementApis from "@/apis/engagement";
 import mediaApis from "@/apis/media";
 import * as echarts from "echarts";
 import { ElEmpty, ElMessage } from "element-plus";
-import { Back, Loading } from "@element-plus/icons-vue"; // 引入 Element UI
+import { Back } from "@element-plus/icons-vue"; // 引入 Element UI
 import { useRouter } from "vue-router";
 import {useStore} from "@/store";
+import useApis from "@/apis";
 
 const router = useRouter();
 const store = useStore();
+const apis = useApis();
+
+// 添加courseId ref
+const courseId = ref(0);
+
 const props = defineProps({
   userId: {
     type: Number,
@@ -403,11 +409,11 @@ const correctionStats = ref({
   correctNumber: 0
 });
 
-// 计算批改进度百分比
-const progressPercentage = computed(() => {
-  if (correctionStats.value.engageNumber === 0) return 0;
-  return Math.round((correctionStats.value.correctNumber / correctionStats.value.engageNumber) * 100);
-});
+// 计算批改进度百分比(暂时不使用,保留以防将来需要)
+// const progressPercentage = computed(() => {
+//   if (correctionStats.value.engageNumber === 0) return 0;
+//   return Math.round((correctionStats.value.correctNumber / correctionStats.value.engageNumber) * 100);
+// });
 
 // 计算未批改数量
 const uncorrectedNumber = computed(() => {
@@ -431,6 +437,30 @@ const fetchCorrectionStats = async () => {
   }
 };
 
+// 获取作业详情以获取courseId
+const fetchAssignmentDetails = async () => {
+  try {
+    const response = await apis.getAssignmentById(props.assignmentId);
+    if (response.data.code === 200) {
+      const assignmentData = response.data.data;
+      courseId.value = assignmentData.courseId || 0;
+      console.log('获取到AI口语作业courseId:', courseId.value);
+    }
+  } catch (error) {
+    console.error('获取AI口语作业详情失败:', error);
+    // 如果普通作业API失败,尝试AI口语作业API
+    try {
+      const aiResponse = await apis.getAiSpeakingAssignmentById(props.assignmentId);
+      if (aiResponse.data.code === 200 && aiResponse.data.data.courseId) {
+        courseId.value = aiResponse.data.data.courseId;
+        console.log('从AI口语API获取到courseId:', courseId.value);
+      }
+    } catch (aiError) {
+      console.error('获取AI口语作业详情也失败:', aiError);
+    }
+  }
+};
+
 // 获取下一个学生作业ID
 const getNextStudentAssignment = async () => {
   try {
@@ -937,10 +967,30 @@ onBeforeUnmount(() => {
 });
 
 const goBack = () => {
-  router.push({
-    name: 'myCorrectWithAssignmentId',
-    params: { assignmentId: props.assignmentId }
-  });
+  // 根据用户角色决定返回页面
+  if (ROLE === 'STUDENT') {
+    // 学生返回 myHomework 页面,并传递courseId
+    if (!courseId.value) {
+      ElMessage.warning('获取课程信息失败,将返回作业列表');
+      router.push({ name: 'myHomework' });
+      return;
+    }
+    
+    console.log('AI口语作业返回,courseId:', courseId.value);
+    router.push({ 
+      name: 'myHomework',
+      query: { 
+        courseId: courseId.value.toString(),
+        fromAssignment: 'true' // 标记是从作业页面返回
+      }
+    });
+  } else {
+    // 老师保持原有逻辑,返回批改页面
+    router.push({
+      name: 'myCorrectWithAssignmentId',
+      params: { assignmentId: props.assignmentId }
+    });
+  }
 };
 
 const ROLE = store.user.role;
@@ -987,6 +1037,9 @@ watch(templateDialogVisible, (val) => {
 // 在数据加载完成后渲染图表
 onMounted(async () => {
   try {
+    // 先获取作业详情以获取courseId
+    await fetchAssignmentDetails();
+    
     const response = await doubaoApis.getRecord(
       props.userId,
       props.assignmentId

+ 598 - 53
src/views/CorrectPage/CorrectPage.vue

@@ -4,36 +4,229 @@
   Created Date: 2024-7-24
 -->
 <template>
+  <div class="correct-page">
+    <!-- 最左侧AI对话区域 -->
+    <div class="ai-chat-panel">
+      <div class="chat-header">
+        <h3 class="chat-title">学生与AI对话记录</h3>
+      </div>
+      
+      <div class="chat-messages">
+        <div 
+          v-for="(message, index) in formattedMessages" 
+          :key="index"
+          class="message-group"
+        >
+          <!-- 用户消息 -->
+          <div v-if="message.role === 'user'" class="user-message">
+            <div class="message-info">
+              <div class="user-name">Student</div>
+              <div class="message-bubble user-bubble">
+              {{ message.content }}
+              </div>
+            </div>
+            <div class="message-avatar user-avatar">
+              <span>S</span>
+            </div>
+          </div>
+          
+          <!-- AI消息 -->
+          <div v-if="message.role === 'assistant'" class="ai-message">
+            <div class="message-avatar ai-avatar">
+              <span>C</span>
+            </div>
+            <div class="message-info">
+              <div class="ai-name">ChatGLM4</div>
+              <div class="message-bubble ai-bubble">
+                {{ message.content }}
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+       <div class="chat-footer"></div>
+    </div>
+  
+    <!-- 右侧内容区域 -->
+    <div class="right-content">
+      <!-- 作业描述区域 -->
+      <div class="content-panel">
+        <!-- 上半部分:作业描述 -->
+        <div class="assignment-section">
+          <div class="section-header">
+            <h2>英语写作练习</h2>
+          </div>
+          <div class="assignment-content">
+            <StudentWritingRequirements />
+          </div>
+        </div>
+      </div>
 
-  <div class="my-correct">
-    <!-- 写作题目 及 作文内容 -->
-
-    <div class="my-correct-header">
-      <el-card>
-        <StudentWritingRequirements />
-      </el-card>
-      <div style="height: 10px"></div>
-      <el-card>
-        <StudentEssay :studentId="studentId" :assignmentId="assignmentId" />
-      </el-card>
+      <!-- 学生信息和回答区域 -->
+      <div class="student-info-section">
+        <div class="student-header">
+          <span class="student-label">学生姓名:</span>
+          <span class="student-name">{{ studentInfo.name }}</span>
+          <span class="class-label">所属班级:</span>
+          <span class="class-name">{{ studentInfo.className || '信息与计算科学(强基计划)' }}</span>
+          <span 
+            v-if="!isStudent && isLateSubmission" 
+            class="late-submission-badge"
+          >
+            补交
+          </span>
+        </div>
+        <div class="student-answer-content">
+          {{ studentAnswer }}
+        </div>
+      </div>
     </div>
 
-    <div class="right-side">
-      <!-- update: 仅保留AI历史对话 -->
-      <div class="my-correct-record">
-        <h2>学生与AI对话历史记录</h2>
-        <AIChatter :dialogueId="dataForm.dialogueId" :messages="dataForm.messages" :is-teacher="true" />
-        <el-button @click="showDialog = true">查看行为记录</el-button>
+    <!-- 教师功能区域 -->
+    <div v-if="!isStudent" class="teacher-function-area">
+      <!-- 查看行为记录按钮 -->
+      <el-button 
+        type="primary" 
+        class="behavior-record-btn"
+        @click="handleBehaviorRecord"
+      >
+        查看行为记录
+      </el-button>
+      
+      <!-- 评分区域 -->
+      <div class="rating-section">
+        <div class="rating-component">
+          <div class="rating-input-wrapper">
+            <span style="font-size: 22px; font-weight: 500; margin-left: -20px;">评分:&nbsp;&nbsp;</span>
+            <div class="score-counter">
+              <button 
+                class="score-btn decrease-btn" 
+                @click="decreaseScore"
+                :disabled="correctionData.score <= 1"
+              >
+                −
+              </button>
+              <input 
+                type="number" 
+                class="score-input" 
+                :min="1" 
+                :max="100" 
+                placeholder="暂无分数" 
+                title="请输入1~100间的数字"
+                v-model="correctionData.score" 
+                @input="validateScore" 
+              />
+              <button 
+                class="score-btn increase-btn" 
+                @click="increaseScore"
+                :disabled="correctionData.score >= 100"
+              >
+                +
+              </button>
+            </div>
+          </div>
+          <div v-if="scoreError" class="error-message">{{ scoreError }}</div>
+        </div>
+      </div>
+      
+      <!-- 评语区域 -->
+      <div class="remark-section">
+        <div class="textarea-container">
+          <textarea 
+            v-model="correctionData.remark" 
+            placeholder="请输入评语" 
+            class="remark-textarea"
+          ></textarea>
+        </div>
+        
+        <!-- 提交批改和评语模板按钮 -->
+        
       </div>
 
-      <!-- 教师评分、修改意见及评价 -->
-      <div class="my-correct-remark">
-        <Remark :studentId="studentId" :assignmentId="assignmentId" />
+      <div class="button-section">
+        <!-- 第一行按钮 -->
+        <div class="button-row">
+          <el-button 
+            class="action-btn submit-btn"
+            @click="handleCorrect"
+            :loading="submitting"
+          >
+            提交评语
+          </el-button>
+          <el-button 
+            class="action-btn next-btn"
+            @click="handleNext"
+            :disabled="isAllCorrected"
+          >
+            下一个
+          </el-button>
+        </div>
+        
+        <!-- 第二行按钮 -->
+        <div class="button-row">
+          <el-button 
+            class="action-btn template-btn"
+            @click="showTemplateDialog"
+          >
+            评语模板
+          </el-button>
+          <el-button 
+            class="action-btn back-btn"
+            @click="goBack"
+          >
+            返回
+          </el-button>
+        </div>
+        
+        <!-- 进度条 -->
+        <div class="progress-section">
+          <el-progress 
+            :percentage="progressPercentage" 
+            :stroke-width="20"
+            color="#7FD67F"
+            class="progress-bar"
+          />
+        </div>
+        
+        <!-- 进度文字 -->
+        <div class="progress-text">
+          已批改/未批改:{{ correctionStats.correctNumber }}/{{ uncorrectedNumber }}
+        </div>
       </div>
+      
+      <!-- 预留区域:后续可在此添加更多教师功能 -->
     </div>
-  </div>
-
-  <el-dialog v-model="showDialog" title="文件信息">
+    
+    <!-- 学生功能区域 -->
+    <div v-if="isStudent" class="student-function-area">
+      <!-- 老师批改分数展示 -->
+      <div class="student-score-display">
+        <div class="score-label">评分:</div>
+        <div class="score-container">
+          <div class="score-value">{{ correctionData.score || '暂无评分' }}</div>
+        </div>
+      </div>
+      
+      <!-- 老师评语展示 -->
+      <div class="student-remark-display">
+        <div class="remark-content">
+          {{ correctionData.remark || '老师暂未给出评语' }}
+        </div>
+      </div>
+      
+      <!-- 学生返回按钮区域 -->
+      <div class="student-button-area">
+        <el-button 
+          class="student-back-btn"
+          @click="goBackStudent"
+        >
+          返回
+        </el-button>
+      </div>
+    </div>
+    
+    <!-- 行为记录对话框 -->
+        <el-dialog v-model="showDialog" title="文件信息">
 
       <el-table :data="fileList" stripe>
         <el-table-column prop="id" label="ID"></el-table-column>
@@ -52,31 +245,53 @@
         <el-table-column prop="studentId" label="学生 ID"></el-table-column>
       </el-table>
 
-    <template #footer>
+      <template #footer>
       <el-button @click="showDialog = false">取消</el-button>
       <el-button type="primary" @click="downloadAllFiles">导出</el-button>
-    </template>
-  </el-dialog>
-
+      </template>
+      </el-dialog>
+      
+    <!-- 评语模板对话框 -->
+    <el-dialog
+      v-model="templateDialogVisible"
+      title="评语模板"
+      width="50%"
+      :close-on-click-modal="false"
+      class="template-dialog"
+    >
+      <div class="template-list">
+        <div 
+          v-for="(template, index) in remarkTemplates" 
+          :key="index"
+          class="template-item"
+          @click="selectTemplate(template)"
+        >
+          <div class="template-content">
+            {{ index + 1 }}. {{ template }}
+          </div>
+        </div>
+      </div>
+    </el-dialog>
+  </div>
 </template>
 
 <script setup lang="ts">
-import AIChatter from "@/components/AIChatter/AIChatter.vue";
-import Dictionaries from "@/views/Home/component/Dictionaries/index.vue";
+
 import useApis from "@/apis";
 import { useStore } from "@/store";
 import router from "@/router"
-import { onMounted, reactive, ref, watchEffect } from "vue";
+import { onMounted, reactive, ref, computed } from "vue";
 import type { engagementVO } from "@/types/vo";
 import type { IDialogue } from "@/types/dialogue.ts";
-import Edit from "@/views/Home/component/Edit/index.vue";
-import Slider from '@/views/Home/component/Silder/index.vue'
+import { ElMessage } from 'element-plus';
+
 import "./index.scss"
 import { useRoute } from 'vue-router'
-import StudentEssay from "../Home/component/StudentEssay/StudentEssay.vue";
-import StudentWritingRequirements from "../Home/component/StudentEssay/StudentWritingRequirements.vue";
-import Remark from "../Home/component/Remark/Remark.vue";
-import dayjs from 'dayjs';
+
+// 导入所需组件
+import StudentWritingRequirements from "@/views/Home/component/StudentEssay/StudentWritingRequirements.vue";
+
+import * as dayjs from 'dayjs';
 const formatDateTime = (isoString: string): string => {
   return dayjs(isoString).format('YYYY-MM-DD HH:mm:ss');
 };
@@ -89,10 +304,59 @@ const studentId = Number(route.query.studentId)
 const apis = useApis()
 const store = useStore()
 
+// 弹窗相关变量
 const fileList = reactive([]);
-
 const showDialog = ref(false);
 
+// 评分和评语相关变量
+const correctionData = reactive({
+  score: 0,
+  remark: ''
+});
+const scoreError = ref('');
+const submitting = ref(false);
+
+// 评语模板相关变量
+const templateDialogVisible = ref(false);
+const remarkTemplates = [
+  '希望广大青年坚定理想信念,厚植家国情怀,练就过硬本领,发扬奋斗精神,到祖国和人民最需要的地方发光发热,为中国式现代化建设贡献青春力量',
+  '文章结构清晰,论点明确,语言表达流畅,体现了较好的写作水平。',
+  '内容充实,观点鲜明,但在语法和拼写方面还需要进一步完善。',
+  '思路清晰,表达准确,展现了良好的语言运用能力。',
+  '文章内容丰富,但需要注意段落间的逻辑连接和过渡。'
+];
+
+// 批改进度统计
+const correctionStats = reactive({
+  engageNumber: 0,
+  correctNumber: 0
+});
+
+// 计算批改进度百分比
+const progressPercentage = computed(() => {
+  if (correctionStats.engageNumber === 0) return 0;
+  return Math.round((correctionStats.correctNumber / correctionStats.engageNumber) * 100);
+});
+
+// 计算未批改数量
+const uncorrectedNumber = computed(() => {
+  return correctionStats.engageNumber - correctionStats.correctNumber;
+});
+
+// 判断是否全部批改完成
+const isAllCorrected = computed(() => {
+  return uncorrectedNumber.value === 0;
+});
+
+// 判断当前用户是否为学生
+const isStudent = computed(() => store.user.role === 'STUDENT');
+
+// 判断是否为补交状态
+const isLateSubmission = computed(() => {
+  const status = dataForm.engagement?.status;
+  return status === 'LATE_SUBMITTED' || status === 'LATE_SUBMITTED_CORRECTED';
+});
+
 const dataForm = reactive<{
   messages: IDialogue[],
   dialogueId: string;
@@ -103,6 +367,71 @@ const dataForm = reactive<{
   engagement: {} as engagementVO
 })
 
+// 学生信息
+const studentInfo = reactive({
+  name: '',
+  className: ''
+})
+
+// 学生回答内容
+const studentAnswer = ref('')
+
+// 课程ID(用于返回时保持菜单选中状态)
+const courseId = ref(0)
+
+// 格式化消息数据用于显示
+const formattedMessages = ref<IDialogue[]>([])
+
+// 更新格式化消息
+const updateFormattedMessages = () => {
+  if (!dataForm.messages || dataForm.messages.length === 0) {
+    formattedMessages.value = []
+    return
+  }
+  formattedMessages.value = [...dataForm.messages]
+}
+
+// 获取学生信息
+async function fetchStudentInfo() {
+  try {
+    const response = await apis.findUserById(studentId)
+    if (response.data.code === 200) {
+      const userData = response.data.data.records[0]
+      studentInfo.name = userData.name
+      studentInfo.className = userData.className || ''
+    }
+  } catch (error) {
+    console.error('获取学生信息失败:', error)
+  }
+}
+
+// 获取评分和评语数据
+async function fetchCorrectionData() {
+  try {
+    const response = await apis.getEngagement(studentId, assignmentId)
+    if (response.data.code === 200) {
+      const engagementData = response.data.data
+      correctionData.score = engagementData.score || 0
+      correctionData.remark = engagementData.remark || ''
+    }
+  } catch (error) {
+    console.error('获取评分数据失败:', error)
+  }
+}
+
+// 获取作业详情以获取课程ID
+async function fetchAssignmentDetails() {
+  try {
+    const response = await apis.getAssignmentById(assignmentId)
+    if (response.data.code === 200) {
+      const assignmentData = response.data.data
+      courseId.value = assignmentData.courseId || 0
+    }
+  } catch (error) {
+    console.error('获取作业详情失败:', error)
+  }
+}
+
 // 获取数据
 async function fetchData() {
   await apis.getAIDialogues(assignmentId, studentId)
@@ -113,48 +442,263 @@ async function fetchData() {
         messages: data?.messages.content,
         engagement: dataForm.engagement
       })
+      updateFormattedMessages()
     })
 
   await apis.getEngagement(studentId, assignmentId)
     .then((_res: any) => {
+      const engagementData = _res.data.data
       Object.assign(dataForm, {
         dialogueId: dataForm.dialogueId,
         messages: dataForm.messages,
-        engagement: _res.data.data
+        engagement: engagementData
       })
+      // 获取学生回答内容
+      if (engagementData.textContent) {
+        studentAnswer.value = engagementData.textContent
+      }
     }).catch((_err: any) => {
       console.log(_err)
       // router.push("/login");
       router.go(-1);
     })
+  
+  // 获取学生信息
+  await fetchStudentInfo()
+  
+  // 获取现有的评分和评语
+  await fetchCorrectionData()
+  
+  // 获取作业详情(获取课程ID)
+  await fetchAssignmentDetails()
+  
+  // 获取批改进度统计
+  await fetchCorrectionStats()
+}
+
+// 弹窗相关函数
+const handleBehaviorRecord = () => {
+  showDialog.value = true
+  // 如果数据为空,重新获取
+  if (fileList.length === 0) {
+    getBehaviorRecords()
+  }
 }
 
+// 获取行为记录数据
 const getBehaviorRecords = () => {
   apis.selectBehaviorRecord(studentId, assignmentId)
-      .then(res => {
-        Object.assign(fileList, res.data.data)
-        // fileList.push(res.data.data)
-        // fileList.value = res.data.data
-      })
-      .catch(err => {
-        console.log(err)
-      })
+    .then(res => {
+      console.log('API返回的原始数据:', res.data)
+      const records = res.data.data || []
+      console.log('解析后的记录数组:', records)
+      console.log('记录数量:', records.length)
+      
+      // 清空并重新填充数据(确保响应式更新)
+      fileList.length = 0
+      if (Array.isArray(records)) {
+        records.forEach(record => {
+          fileList.push(record)
+        })
+      } else {
+        console.warn('返回的数据不是数组:', records)
+      }
+      
+      console.log('fileList 最终内容:', fileList)
+      console.log('fileList 长度:', fileList.length)
+    })
+    .catch(err => {
+      console.error('获取行为记录失败:', err)
+      ElMessage.error('获取行为记录失败')
+      fileList.length = 0
+    })
 }
 
+
+
+// 下载单个文件
+const downloadSingleFile = (fileUrl: string) => {
+  if (!fileUrl) {
+    ElMessage.warning('文件链接不存在')
+    return
+  }
+  const link = document.createElement('a')
+  link.href = fileUrl
+  link.download = ''
+  link.target = '_blank'
+  document.body.appendChild(link)
+  link.click()
+  document.body.removeChild(link)
+}
+
+// 批量下载所有文件(参考old文件的实现)
 const downloadAllFiles = () => {
+  if (fileList.length === 0) {
+    ElMessage.warning('暂无文件可下载')
+    return
+  }
+  
+  let downloadCount = 0
   fileList.forEach((item) => {
-    const link = document.createElement('a');
-    link.href = item.recordFileUrl;
-    link.download = '';
-    link.click();
-    link.remove();
+    if (item.recordFileUrl) {
+      downloadSingleFile(item.recordFileUrl)
+      downloadCount++
+    }
+  })
+  
+  if (downloadCount > 0) {
+    ElMessage.success(`开始下载 ${downloadCount} 个文件`)
+  } else {
+    ElMessage.warning('没有可下载的文件链接')
+  }
+  
+  showDialog.value = false
+}
+
+// 验证评分
+const validateScore = () => {
+  const score = Number(correctionData.score)
+  if (isNaN(score) || score < 1 || score > 100) {
+    scoreError.value = '评分需要在1-100之间'
+  } else {
+    scoreError.value = ''
+  }
+}
+
+// 增加分数
+const increaseScore = () => {
+  if (correctionData.score < 100) {
+    correctionData.score++
+    validateScore()
+  }
+}
+
+// 减少分数
+const decreaseScore = () => {
+  if (correctionData.score > 1) {
+    correctionData.score--
+    validateScore()
+  }
+}
+
+// 提交批改
+const handleCorrect = async () => {
+  if (!correctionData.score) {
+    ElMessage.error("请先评分")
+    return
+  }
+  if (!correctionData.remark) {
+    ElMessage.error("请先输入评语")
+    return
+  }
+  
+  submitting.value = true
+  try {
+    const response = await apis.engagementCorrect(studentId, assignmentId, correctionData)
+    if (response.data.code === 200) {
+      ElMessage.success("批改成功")
+      await fetchCorrectionStats() // 刷新进度统计
+    } else {
+      ElMessage.error("批改异常: " + response.data.msg)
+    }
+  } catch (error) {
+    console.error('批改失败:', error)
+    ElMessage.error("批改失败,请重试")
+  } finally {
+    submitting.value = false
+  }
+}
+
+// 显示评语模板对话框
+const showTemplateDialog = () => {
+  templateDialogVisible.value = true
+}
+
+// 选择评语模板
+const selectTemplate = (template: string) => {
+  correctionData.remark = template
+  templateDialogVisible.value = false
+}
+
+// 获取批改进度统计
+const fetchCorrectionStats = async () => {
+  try {
+    const response = await apis.getAssignmentCorrectionStats(assignmentId);
+    if (response.data.code === 200) {
+      Object.assign(correctionStats, response.data.data);
+    }
+  } catch (error) {
+    console.error('获取批改进度失败:', error);
+  }
+}
+
+// 获取下一个学生作业ID
+const getNextStudentAssignment = async () => {
+  try {
+    const response = await apis.getNextStudentAssignment(assignmentId, studentId);
+    if (response.data.code === 200) {
+      return response.data.data;
+    }
+  } catch (error) {
+    console.error('获取下一个学生失败:', error);
+  }
+  return null;
+}
+
+// 处理下一个学生
+const handleNext = async () => {
+  const nextStudentId = await getNextStudentAssignment();
+  if (nextStudentId) {
+    router.push({
+      name: 'correct',
+      params: { assignmentId: assignmentId },
+      query: { studentId: nextStudentId }
+    }).then(() => {
+      // 强制刷新页面以重新加载数据
+      window.location.reload();
+    });
+    ElMessage.success("已自动跳转到下一个学生");
+  } else {
+    ElMessage.info("没有下一个学生");
+  }
+}
+
+// 返回上一页(教师版)
+const goBack = () => {
+  router.push({
+    name: 'myCorrectWithCourse',
+    query: {
+      courseId: courseId.value.toString(),
+      assignmentId: assignmentId.toString(),
+      fromCorrect: 'true'
+    }
+  }).then(() => {
+    window.location.reload();
+  });
+}
+
+// 返回上一页(学生版)
+const goBackStudent = () => {
+  // 如果没有获取到课程ID,则正常返回
+  if (!courseId.value) {
+    ElMessage.warning('获取课程信息失败,将返回作业列表');
+    router.push({ name: 'myHomework' });
+    return;
+  }
+  
+  // 返回时传递课程ID,让myHomework页面保持对应课程的选中状态
+  router.push({ 
+    name: 'myHomework',
+    query: { 
+      courseId: courseId.value.toString(),
+      fromAssignment: 'true' // 标记是从作业页面返回
+    }
   });
-  showDialog.value = false;
 };
 
 onMounted(() => {
   fetchData()
-  getBehaviorRecords()
+  getBehaviorRecords() // 页面加载时获取行为记录数据
 })
 
 </script>
@@ -170,3 +714,4 @@ export default {
   name: "CorrectPage"
 }
 </script>
+

ファイルの差分が大きいため隠しています
+ 875 - 137
src/views/CorrectPage/CoursePage.vue


ファイルの差分が大きいため隠しています
+ 996 - 1
src/views/CorrectPage/index.scss


+ 23 - 3
src/views/EditPage/EditPage.vue

@@ -8,7 +8,7 @@
   <div class="my-work" >
 
     <div class="my-work-slider">
-      <el-button class="back" color="#597D3B" @click="handleBack">&lt;返回</el-button>
+      <el-button class="back" color="#4A90C4" style="color: #FFFFFF;" @click="handleBack">返回</el-button>
       <AIChatter :dialogueId="dataForm.dialogueId" :messages="dataForm.messages" :is-teacher="false" />
       <Dictionaries />
     </div>
@@ -25,16 +25,17 @@
   import useApis from "@/apis";
   import {useStore} from "@/store";
   import router from "@/router"
-  import {onMounted, onUnmounted, reactive, ref, watchEffect} from "vue";
+  import {onMounted, onUnmounted, reactive} from "vue";
   import type {engagementVO} from "@/types/vo";
   import type {IDialogue} from "@/types/dialogue.ts";
   import Edit from "@/views/Home/component/Edit/index.vue";
   import Slider from '@/views/Home/component/Silder/index.vue'
   import "./index.scss"
   import {useRoute} from 'vue-router'
-  import {ElMessage, ElMessageBox} from "element-plus";
+  import {ElMessageBox} from "element-plus";
   import IndexedDB from "@/utils/indexedDBUtil";
   import {isModified, registerAllEventListeners, removeAllEventListeners} from "@/views/EditPage/userWritingRecord";
+  import emitter from "@/utils/emitter";
 
   // 获得路由中的assignmentId
   const route = useRoute()
@@ -104,6 +105,23 @@
         })
   }
 
+  // 保存AI对话内容
+  const saveAIDialogue = async () => {
+    if (dataForm.dialogueId && dataForm.messages.length > 0) {
+      try {
+        await apis.requestAI(dataForm.dialogueId, 'ChatGLM4', dataForm.messages)
+        console.log('AI对话内容已保存')
+      } catch (error) {
+        console.error('保存AI对话失败:', error)
+      }
+    }
+  }
+
+  // 监听提交前的保存对话事件
+  emitter.on('save-ai-dialogue-before-submit', async () => {
+    await saveAIDialogue()
+  })
+
   onMounted( () => {
     fetchData()
     // indexedDB.initDB("eventId")
@@ -115,6 +133,8 @@
     // indexedDB.closeDB()
     // indexedDB.deleteDBAll()
     removeAllEventListeners();
+    // 清理事件监听器
+    emitter.off('save-ai-dialogue-before-submit');
   })
 
 </script>

+ 8 - 13
src/views/Home/component/Edit/index.vue

@@ -11,8 +11,8 @@
         <QuillEditor/>
       </div>
       <div class="button-right">
-        <el-button type="primary" @click="handleSubmit" :disabled="useBtn" color="#597D3B">提交</el-button>
-        <el-button type="primary" @click="handleStage" :disabled="useBtn" color="#597D3B">暂存</el-button>
+        <el-button type="primary" @click="handleSubmit"  color="#4A90C4" style="color: #FFFFFF; width: 70px;">提交</el-button>
+        <el-button type="primary" @click="handleStage"  color="#4A90C4" style="color: #FFFFFF; width: 70px; margin-left: 15px;">暂存</el-button>
       </div>
     </div>
   </div>
@@ -91,17 +91,18 @@
     }
     const endTime = dayjs(data.assignment.endTime); // 假设截止时间在 assignment 的 endTime 属性中
     const currentTime = dayjs();
+    let supplementarySubmission = false
     if (currentTime.isAfter(endTime)) {
-      ElMessage.error('作业已截止,无法提交!');
-      return;
+      supplementarySubmission = true
     }
-    apis.engagementSubmit(store.user.id, props.engagement.assignmentId, )
+    apis.engagementSubmit(store.user.id, props.engagement.assignmentId, supplementarySubmission)
     .then((_res: any) => {
       success(_res.data.data);
-      router.push("/home/myHomework")
+      router.push("/home/myHomework").then(() => {
+        window.location.reload()
+      })
     })
     .catch((error: any) => {
-      console.error('提交失败', error);
       console.error('提交失败', error);
       error('提交失败');
     });
@@ -109,12 +110,6 @@
 
   // 暂存操作
   const handleStage = async () => {
-    const endTime = dayjs(data.assignment.endTime); // 假设截止时间在 assignment 的 endTime 属性中
-    const currentTime = dayjs();
-    if (currentTime.isAfter(endTime)) {
-      ElMessage.error('作业已截止,无法暂存!');
-      return;
-    }
     try {
       const _res = await apis.engagementStage(store.user.id, props.engagement.assignmentId, JSON.stringify(getQuillContent), getText.value, getHTML.value);
       emitter.emit('click-stage');

+ 13 - 4
src/views/Home/component/Remark/Remark.vue

@@ -260,10 +260,19 @@ export default defineComponent({
     };
 
     const goBack = () => {
-      router.push({
-        name: 'myCorrectWithAssignmentId',
-        params: { assignmentId: props.assignmentId }
-      });
+      // 根据用户角色决定返回页面
+      if (isStudent.value) {
+        // 学生返回 myHomework 页面
+        router.push({
+          name: 'myHomework'
+        });
+      } else {
+        // 老师保持原有逻辑,返回批改页面
+        router.push({
+          name: 'myCorrectWithAssignmentId',
+          params: { assignmentId: props.assignmentId }
+        });
+      }
     };
     //新增:验证评分是否在1-100之间
     const validateScore = () => {

+ 2 - 1
src/views/Home/component/Silder/index.scss

@@ -48,7 +48,8 @@
     gap: 10px;
 
     .slider-tag-item {
-      background-color: #add94f;
+      background-color: #4A90C4;
+      color: #FFFFFF;
       padding: 8px 0;
       display: flex;
       justify-content: center;

+ 112 - 11
src/views/HomeworkPage/component/HomeworkDetail.vue

@@ -16,6 +16,10 @@
           <span style="font-weight: 600">发布老师: </span>
           {{data.teacherName}}
         </div>
+        <div class="item">
+          <span style="font-weight: 600">开始时间: </span>
+          {{new Date(Date.parse(data.startTime)).toLocaleString()}}
+        </div>
         <div class="item">
           <span style="font-weight: 600">截止日期: </span>
           {{new Date(Date.parse(data.endTime)).toLocaleString()}}
@@ -23,8 +27,14 @@
         <div class="item">
           <el-link type="primary" @click="downloadFile(data.descriptionFile, `${data.assignmentName}-描述文件`)">相关文件下载</el-link>
           <div class="button-container">
-            <el-button style="margin-left: 550px" color="#597D3B" @click="handleBack">返回</el-button>
-            <el-button color="#597D3B" @click="handleStart()" :disabled="new Date() > new Date(data.endTime)">
+            <el-button style="margin-left: 550px; color: #FFFFFF;" color="#7EB5E1" @click="handleBack">返回</el-button>
+            <el-button 
+              style="color: #FFFFFF;" 
+              color="#7ADBB0" 
+              @click="handleStart()" 
+              :disabled="isAssignmentDisabled"
+              :title="getButtonTooltip"
+            >
               开始{{ data.type }}
             </el-button>
           </div>
@@ -54,8 +64,8 @@
 <script lang="ts" setup>
 import useApis from "@/apis";
 import {useRoute} from "vue-router";
-import {onMounted, reactive} from "vue";
-import type {IAssignment} from "@/types/vo";
+import {onMounted, reactive, computed, ref} from "vue";
+import type {IAssignment, engagementVO} from "@/types/vo";
 import './homeworkDetail.scss'
 import {useStore} from "@/store";
 import router from "@/router";
@@ -82,13 +92,70 @@ let data = reactive<IAssignment>({
   type:""
 })
 
+// 学生作业参与状态
+const engagementStatus = ref<engagementVO | null>(null)
+
+// 计算属性:判断作业是否应该禁用
+const isAssignmentDisabled = computed(() => {
+  const now = new Date()
+  const startTime = new Date(data.startTime)
+  const endTime = new Date(data.endTime)
+  
+  // 如果当前时间小于开始时间,则禁用
+  if (now < startTime) {
+    return true
+  }
+  
+  // 如果当前时间大于结束时间
+  if (now > endTime) {
+    // 超过截止日期时,只有未完成状态的学生可以点击
+    if (engagementStatus.value) {
+      return engagementStatus.value.status !== 'NOT_SUBMITTED'
+    }
+    // 如果没有参与状态信息,允许点击(首次参与)
+    return false
+  }
+  
+  // 在作业时间范围内,不禁用
+  return false
+})
+
+// 计算属性:按钮提示文本
+const getButtonTooltip = computed(() => {
+  const now = new Date()
+  const startTime = new Date(data.startTime)
+  const endTime = new Date(data.endTime)
+  
+  if (now < startTime) {
+    return `作业尚未开始,开始时间:${startTime.toLocaleString()}`
+  } else if (now > endTime) {
+    if (engagementStatus.value) {
+      const status = engagementStatus.value.status
+      if (status === 'NOT_SUBMITTED') {
+        return '作业已截止,但您可以继续完成作业'
+      } else if (status === 'SUBMITTED') {
+        return '作业已截止且已提交,无法再次编辑'
+      } else if (status === 'CORRECTED') {
+        return '作业已截止且已批改,无法再次编辑'
+      }
+    }
+    return `作业已截止,截止时间:${endTime.toLocaleString()}`
+  } else {
+    return '点击开始作业'
+  }
+})
+
 function handleStart(){
   getEngagement()
   console.log(data.type)
   if(data.type == 'writing' || data.type == '写作'){
-    router.push(`/home/assignment/${assignmentId}/edit`)
+    router.push(`/home/assignment/${assignmentId}/edit`).then(() => {
+      window.location.reload()
+    })
   } else if(data.type == 'speaking' || data.type == 'AI口语'){
-    router.push(`/home/assignment/${assignmentId}/aispeaking`)
+    router.push(`/home/assignment/${assignmentId}/aispeaking`).then(() => {
+      window.location.reload()
+    })
   }
 }
 
@@ -101,6 +168,21 @@ function downloadFile(url:string, fileName:string) {
 }
 
 
+// 获取学生作业参与状态(仅查询,不自动加入)
+async function fetchEngagementStatus() {
+  try {
+    const response = await apis.getEngagement(store.user.id, assignmentId)
+    if (response.data.code === 200 && response.data.data) {
+      engagementStatus.value = response.data.data
+    } else {
+      engagementStatus.value = null
+    }
+  } catch (error) {
+    console.log('获取作业参与状态失败:', error)
+    engagementStatus.value = null
+  }
+}
+
 async function getEngagement(){
   apis.getEngagement(store.user.id, assignmentId)
       .then((_res:any) => {
@@ -112,6 +194,8 @@ async function getEngagement(){
                   message: '参加作业成功',
                   type: 'success',
                 })
+                // 加入作业后重新获取状态
+                fetchEngagementStatus()
               })
               .catch((_err:any) => {
                 console.log("参加做业的错误",_err)
@@ -131,12 +215,19 @@ const fetchData = () => {
 }
 
 const handleBack = () => {
-   // 跳转到目标页面
-   router.push(`/home/courseDetail/${data.courseId}`)
+   // 跳转到我的作业页面,携带课程ID参数以便自动选中对应课程
+   router.push({
+     path: '/home/myHomework',
+     query: {
+       courseId: data.courseId,
+       fromAssignment: 'true'
+     }
+   }).then(() => {window.location.reload()})
 }
 
 onMounted(() => {
   fetchData()
+  fetchEngagementStatus()
 })
 </script>
 
@@ -149,11 +240,21 @@ export default {
 <style scoped>
   .button-container {
     display: flex;
-
   }
+  
   .el-button {
-  margin-right:0px;
-}
+    margin-right: 0px;
+  }
+  
+  /* 禁用状态的开始按钮样式 */
+  .el-button:disabled {
+    opacity: 0.6;
+    cursor: not-allowed;
+  }
+  
+  .el-button:disabled:hover {
+    opacity: 0.6;
+  }
 </style>
 
 

+ 862 - 129
src/views/HomeworkPage/index.vue

@@ -4,91 +4,208 @@
   Created Date: 2024-7-3
 -->
 <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>
-        </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>
-        </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>
 
 <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 {useStore} from "@/store";
-  import type {CourseVO, IAssignment} from "@/types/vo";
+  import type {CourseVO, IAssignment, StudentCourseAssignmentsResponse, StudentAssignmentStatus} from "@/types/vo";
   import router from "@/router";
-  import {AssignmentStatus} from "@/types/enums";
+  import {useRoute} from "vue-router";
+
   import {useFormatDate} from "@/hooks/useFormatDate";
 
   const apis = useApis()
   const store = useStore()
-  const doRefresh:Function = inject("reload")
   const {formatDate} = useFormatDate()
   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<{
@@ -119,7 +236,7 @@
   //  查看教师批改结果 跳转至批改页面
   const viewCorrection = (assignmentId:number,studentId:number, type:string, assignmentName:string) => {
     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'){
       router.push({
       path: `/home/assignment/${assignmentId}/airecord`,
@@ -127,33 +244,18 @@
         userId: studentId,
         assignmentId: assignmentId,
         assignmentName: assignmentName
-      }});
+      }}).then(() => {window.location.reload()});
     }
    
 
   }
 
   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 = () => {
@@ -164,9 +266,10 @@
             let total = res.data.data.total;
             apis.getCourseByStudentId(1, total)
                 .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;
             apis.getCourseByTeacherId(1, total)
                 .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) => {
+    // console.log('开始获取课程', courseId, '的作业数据')
     apis.getAssignmentByCourseId(courseId)
         .then((res: any) => {
           let total = res.data.data.total
+          // console.log('课程', courseId, '总作业数:', total)
           apis.getAssignmentByCourseId(courseId, 1, total)
               .then((res: any) => {
                 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
                 Object.assign(pageInfo, {
                   tableData: getSlice(pageInfo.pages, pageInfo.size),
@@ -208,21 +554,9 @@
         })
   }
 
+  // 旧的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)
   }
 
+  // 监听课程列表变化,数据加载完成后处理路由参数
+  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(() => {
     fetchData()
   })
 
-  let state = ref(null)
-  const stateOptions = [
-    {
-      value: AssignmentStatus.NOT_STARTED,
-      label: "未发布"
-    },
-    {
-      value: AssignmentStatus.PROCEEDING,
-      label: "进行中"
-    },
-    {
-      value: AssignmentStatus.FINISHED,
-      label: "已截止"
-    },
-  ]
 
-  let assignmentName = ref('')
 
 </script>
 
@@ -259,3 +612,383 @@ export default {
   name: "HomeworkPage"
 }
 </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>

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません