import type { Ref } from 'vue' import type { ExamBatchCreateMarkingTaskRecordRequest, ExamCreateMarkingTaskRecordRequest, ExamQuestionWithTasksResponse, ExamSetProblemRecordRequest, ExamStudentMarkingQuestionResponse } from '@/api' import type { MarkingNavigation } from '@/composables/marking/useMarkingNavigation' import { useQuery } from '@tanstack/vue-query' import { computed, inject, provide, readonly, ref, watch } from 'vue' import { examMarkingTaskApi } from '@/api' import { getMarkingContext } from '@/composables/marking/MarkingContext' import { useMarkingHistory } from '@/composables/marking/useMarkingHistory' import { useUserId } from '@/composables/useUserId' export interface MarkingSubmitData { score: number remark: any status: 'draft' | 'submitted' markingTime?: number isExcellent?: boolean isTypical?: boolean isProblem?: boolean problemType?: string problemRemark?: string } export interface UseMarkingDataOptions { taskId: Ref questionId: Ref examId?: Ref subjectId?: Ref isLandscape?: Ref taskType?: Ref markingNavigation: MarkingNavigation } function createMarkingData(options: UseMarkingDataOptions) { const { taskId, questionId, examId, subjectId, isLandscape, taskType, markingNavigation } = options // 获取阅卷上下文和历史管理 const markingContext = getMarkingContext() const markingHistory = useMarkingHistory() // 基础数据 const questionData = ref([]) const currentMarkingSubmitData = ref([]) const isLoading = ref(false) const isSubmitted = ref(false) const markingStartTime = ref(0) const mode = ref<'single' | 'multi'>('single') // 当前分数 const firstNotScoredIndex = computed(() => { return 0 }) const currentScore = computed({ get: () => { const score = currentMarkingSubmitData.value[firstNotScoredIndex.value]?.score return score !== undefined ? score : -1 }, set: (value) => { if (currentMarkingSubmitData.value[firstNotScoredIndex.value]) { currentMarkingSubmitData.value[firstNotScoredIndex.value].score = value } }, }) // 获取题目数据(使用上下文提供者) const { data: questionResponse, isLoading: isQuestionLoading, error: questionError, refetch: refetchQuestion, } = useQuery({ queryKey: ['marking-question', taskId, useUserId()], queryFn: async () => { // 如果在历史查看模式,返回历史记录 if (markingHistory.isViewingHistory.value && markingHistory.currentHistoryRecord.value) { const record = markingHistory.currentHistoryRecord.value // 将历史记录转换为题目数据格式 return { id: record.id, task_id: record.task_id, question_id: record.question_id, scan_info_id: record.scan_info_id, score: record.score, full_score: record.full_score, question_major: record.question_major, question_minor: record.question_minor, image_urls: record.image_urls, } as ExamStudentMarkingQuestionResponse } // 否则使用上下文提供者获取当前题目 return markingContext.dataProvider.getSingleQuestion(taskId.value) }, enabled: computed(() => !!taskId.value), gcTime: 0, }) // 获取题目列表数据(用于Layout显示) const { data: questionsListData, refetch: refetchQuestionsList, } = useQuery({ queryKey: ['marking-questions-list', examId, subjectId, useUserId()], queryFn: async () => examMarkingTaskApi.byQuestionList({ exam_id: examId!.value!, exam_subject_id: subjectId!.value!, }) as unknown as Promise, enabled: computed(() => !!examId?.value && !!subjectId?.value), placeholderData: [], }) // 获取平均分对比 const { data: avgScoreData, refetch: refetchAvgScore, } = useQuery({ queryKey: ['marking-avg-score', questionId, taskId, useUserId()], queryFn: async () => examMarkingTaskApi.questionTaskAverageScoreComparisonDetail( questionId.value, taskId.value, ), enabled: computed(() => !!questionId.value && !!taskId.value), }) // 处理题目数据 const processQuestionData = () => { if (!questionResponse.value) return let questions: ExamStudentMarkingQuestionResponse[] = [] // 复制数据但不添加 score 字段到原类型中 questions = [questionResponse.value] questionData.value = questions // 初始化提交数据 currentMarkingSubmitData.value = questions.map(() => ({ score: -1, remark: '', status: 'draft' as const, isExcellent: false, isTypical: false, isProblem: false, })) // 设置开始时间 if (questions.length > 0) { markingStartTime.value = Date.now() } } // 当前任务信息(用于获取总数) const currentTaskInfo = computed(() => { if (!questionId.value || !taskId.value || !questionsListData.value.length) return null // 找到当前题目 const currentQuestion = questionsListData.value.find(q => q.question_id === questionId.value) if (!currentQuestion) return null return currentQuestion.tasks?.[taskType.value] }) // 总题目数量(从任务中获取) const totalQuestions = computed(() => { return currentTaskInfo.value?.task_quantity || 0 }) // 监听数据变化 watch(() => questionResponse.value, processQuestionData, { immediate: true }) // 计算阅卷用时 const getMarkingTime = (): number => { return markingStartTime.value > 0 ? Math.floor((Date.now() - markingStartTime.value) / 1000) : 0 } // 提交单个记录 const submitSingleRecord = async (data?: MarkingSubmitData, index: number = 0) => { try { const currentData = data || currentMarkingSubmitData.value[index] const isHistoryMode = markingHistory.isViewingHistory.value // 根据模式选择数据源 const question = isHistoryMode ? markingHistory.currentHistoryRecord.value! : questionData.value[index] if (!question) throw new Error('题目数据不存在') // 构建提交数据 const submitData: ExamCreateMarkingTaskRecordRequest = { id: question.id, scan_info_id: question.scan_info_id!, question_id: questionId.value, record_id: 0, task_id: taskId.value, duration: getMarkingTime(), score: currentData.score, remark: JSON.stringify(currentData.remark), page_mode: 'single', is_excellent: currentData.isExcellent ? 1 : 0, is_model: currentData.isTypical ? 1 : 0, is_problem: currentData.isProblem ? 1 : 0, } // 如果是问题卷,不提交正常记录,只提交问题卷记录 const response = !currentData.isProblem ? await markingContext.dataProvider.submitRecords({ is_review: isHistoryMode, batch_data: [submitData], }) : null // 处理问题卷 if (currentData.isProblem) { const problemRequest: ExamSetProblemRecordRequest = { id: question.id!, task_id: taskId.value, problem_type: currentData.problemType! as any, problem_addition: currentData.problemRemark, } if (markingContext.dataProvider.createProblemRecord) { await markingContext.dataProvider.createProblemRecord(problemRequest as any) } } if (response) { if (response.success_count === 0) { const errorMessage = response.fail_data?.map(item => item.message).join('、') || '提交失败' throw new Error(errorMessage) } // 更新历史记录总数 if (!isHistoryMode && response.success_count > 0) { markingHistory.incrementHistoryCount(response.success_count) } } isSubmitted.value = true // 如果是历史模式,刷新历史记录 if (!isHistoryMode) { // 更新已阅数量 if (currentTaskInfo.value) { currentTaskInfo.value.marked_quantity = (currentTaskInfo.value.marked_quantity || 0) + 1 } // 重新获取下一题 questionData.value = [] currentMarkingSubmitData.value = [] markingStartTime.value = 0 await refetchQuestion() processQuestionData() } else { (question as any).final_score = currentData.score markingNavigation.goToNextQuestion() } return response } catch (error) { console.error('提交阅卷记录失败:', error) throw error } } // 提交记录(根据模式选择单个或批量) const submitRecord = async (data?: MarkingSubmitData, index?: number) => { return await submitSingleRecord(data, index) } // 重新加载数据 const reload = async () => { await Promise.all([ refetchQuestion(), refetchAvgScore(), refetchQuestionsList(), ]) } return { // 数据状态 questionData, questionsList: computed(() => questionsListData.value || []), // 题目列表数据 currentMarkingSubmitData, currentScore, firstNotScoredIndex, totalQuestions, // 总题目数量 currentTaskInfo, isLoading: computed(() => isQuestionLoading.value || isLoading.value), isSubmitted: readonly(isSubmitted), mode, isLandscape: isLandscape || ref(false), // 平均分数据 avgScoreData: computed(() => avgScoreData.value), // 错误状态 questionError, // 方法 submitRecord, submitSingleRecord, getMarkingTime, reload, refetchQuestion, refetchAvgScore, refetchQuestionsList, } } type MarkingData = ReturnType const markingDataKey = Symbol('markingData') export function useMarkingData() { const markingData = inject(markingDataKey) if (!markingData) { throw new Error('Marking data not found') } return markingData } export function provideMarkingData(options: UseMarkingDataOptions) { const markingData = createMarkingData(options) provide(markingDataKey, markingData) return markingData }