|
|
@@ -4,36 +4,223 @@
|
|
|
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>
|
|
|
+ </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;">评分: </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 +239,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 +298,53 @@ 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 dataForm = reactive<{
|
|
|
messages: IDialogue[],
|
|
|
dialogueId: string;
|
|
|
@@ -103,6 +355,55 @@ const dataForm = reactive<{
|
|
|
engagement: {} as engagementVO
|
|
|
})
|
|
|
|
|
|
+// 学生信息
|
|
|
+const studentInfo = reactive({
|
|
|
+ name: '',
|
|
|
+ className: ''
|
|
|
+})
|
|
|
+
|
|
|
+// 学生回答内容
|
|
|
+const studentAnswer = ref('')
|
|
|
+
|
|
|
+// 格式化消息数据用于显示
|
|
|
+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)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 获取数据
|
|
|
async function fetchData() {
|
|
|
await apis.getAIDialogues(assignmentId, studentId)
|
|
|
@@ -113,48 +414,243 @@ 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()
|
|
|
+
|
|
|
+ // 获取批改进度统计
|
|
|
+ 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',
|
|
|
+ }).then(() => {
|
|
|
+ window.location.reload();
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 返回上一页(学生版)
|
|
|
+const goBackStudent = () => {
|
|
|
+ router.push({ name: 'myHomework' }).then(() => {
|
|
|
+ window.location.reload(); // 强制刷新页面
|
|
|
});
|
|
|
- showDialog.value = false;
|
|
|
};
|
|
|
|
|
|
onMounted(() => {
|
|
|
fetchData()
|
|
|
- getBehaviorRecords()
|
|
|
+ getBehaviorRecords() // 页面加载时获取行为记录数据
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
@@ -170,3 +666,4 @@ export default {
|
|
|
name: "CorrectPage"
|
|
|
}
|
|
|
</script>
|
|
|
+
|