xlx_teacher_app/src/components/marking/composables/useMarkingData.ts

294 lines
7.7 KiB
TypeScript
Raw Normal View History

2025-08-16 16:42:40 +08:00
import type { Ref } from 'vue'
import type {
CreateMarkingTaskRecordRequest,
StudentMarkingQuestionResponse,
} from '@/service/types'
import { useQuery } from '@tanstack/vue-query'
import { computed, inject, provide, readonly, ref, watch } from 'vue'
import {
markingTasksQuestionQuestionIdTaskTaskIdAverageScoreComparisonUsingGet,
markingTasksTaskIdHistoryUsingGet,
} from '@/service/yuejuanrenwu'
import {
markingTaskRecordsBatchUsingPost,
markingTaskRecordsUsingPost,
} from '@/service/yuejuanrenwujilu'
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<number>
questionId: Ref<number>
isLandscape?: Ref<boolean>
}
function createMarkingData(options: UseMarkingDataOptions) {
const { taskId, questionId, isLandscape } = options
// 基础数据
const questionData = ref<StudentMarkingQuestionResponse[]>([])
const currentMarkingSubmitData = ref<MarkingSubmitData[]>([])
const isLoading = ref(false)
const isSubmitted = ref(false)
const markingStartTime = ref<number>(0)
const mode = ref<'single' | 'multi'>('single')
// 当前分数
const firstNotScoredIndex = computed(() => {
return 0
})
const currentScore = computed({
get: () => {
return currentMarkingSubmitData.value[firstNotScoredIndex.value]?.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: computed(() => ['marking-question', taskId.value]),
queryFn: async () => {
return {
tpl_question_id: 0,
full_score: 12,
question_type: 'single',
question_major: '一',
question_minor: '1',
image_urls: [
'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png',
'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png',
],
}
// return await markingTasksTaskIdQuestionUsingGet({
// params: { task_id: taskId.value },
// })
},
enabled: computed(() => !!taskId.value),
})
// 获取历史记录
const {
data: historyData,
isLoading: isHistoryLoading,
refetch: refetchHistory,
} = useQuery({
queryKey: computed(() => ['marking-history', taskId.value]),
queryFn: () => markingTasksTaskIdHistoryUsingGet({
params: { task_id: taskId.value },
}),
enabled: computed(() => !!taskId.value),
})
// 获取平均分对比
const {
data: avgScoreData,
refetch: refetchAvgScore,
} = useQuery({
queryKey: computed(() => ['marking-avg-score', questionId.value, taskId.value]),
queryFn: () => markingTasksQuestionQuestionIdTaskTaskIdAverageScoreComparisonUsingGet({
params: {
question_id: questionId.value,
task_id: taskId.value,
},
}),
enabled: computed(() => !!questionId.value && !!taskId.value),
})
// 处理题目数据
const processQuestionData = () => {
if (!questionResponse.value)
return
let questions: StudentMarkingQuestionResponse[] = []
questions = [questionResponse.value]
questionData.value = questions
// 初始化提交数据
currentMarkingSubmitData.value = questions.map(() => ({
score: -1,
remark: '',
status: 'draft' as const,
}))
// 设置开始时间
if (questions.length > 0) {
markingStartTime.value = Date.now()
}
}
// 监听数据变化
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 question = questionData.value[index]
if (!question)
throw new Error('题目数据不存在')
const submitData: CreateMarkingTaskRecordRequest = {
scan_info_id: question.scan_info_id!,
question_id: questionId.value,
task_id: taskId.value,
duration: getMarkingTime(),
score: data.score,
remark: JSON.stringify(data.remark),
page_mode: 'single',
is_excellent: data.isExcellent ? 1 : 0,
is_model: data.isTypical ? 1 : 0,
is_problem: data.isProblem ? 1 : 0,
}
const response = await markingTaskRecordsUsingPost({
body: submitData,
})
if (response) {
isSubmitted.value = true
// 重新获取下一题
await refetchQuestion()
processQuestionData()
return response
}
}
catch (error) {
console.error('提交阅卷记录失败:', error)
throw error
}
}
// 批量提交记录
const submitBatchRecords = async () => {
try {
const submitDataList: CreateMarkingTaskRecordRequest[] = currentMarkingSubmitData.value.map(
(item, index) => {
const question = questionData.value[index]
return {
scan_info_id: question.scan_info_id!,
question_id: questionId.value,
task_id: taskId.value,
duration: getMarkingTime(),
score: item.score,
remark: JSON.stringify(item.remark),
page_mode: 'single',
is_excellent: item.isExcellent ? 1 : 0,
is_model: item.isTypical ? 1 : 0,
is_problem: item.isProblem ? 1 : 0,
}
},
)
const response = await markingTaskRecordsBatchUsingPost({
body: {
batch_data: submitDataList,
},
})
if (response) {
isSubmitted.value = true
// 重新获取下一批题目
await refetchQuestion()
processQuestionData()
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(),
refetchHistory(),
refetchAvgScore(),
])
}
return {
// 数据状态
questionData,
currentMarkingSubmitData,
currentScore,
firstNotScoredIndex,
isLoading: computed(() => isQuestionLoading.value || isLoading.value),
isSubmitted: readonly(isSubmitted),
mode,
isLandscape: isLandscape || ref(false),
// 历史数据
historyData: computed(() => historyData.value),
isHistoryLoading,
// 平均分数据
avgScoreData: computed(() => avgScoreData.value),
// 错误状态
questionError,
// 方法
submitRecord,
submitSingleRecord,
submitBatchRecords,
getMarkingTime,
reload,
refetchQuestion,
refetchHistory,
refetchAvgScore,
}
}
type MarkingData = ReturnType<typeof createMarkingData>
const markingDataKey = Symbol('markingData')
export function useMarkingData() {
const markingData = inject<MarkingData>(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
}