xlx_teacher_app/src/components/marking/MarkingMonitor.vue

262 lines
9.9 KiB
Vue
Raw Normal View History

2025-08-16 21:21:34 +08:00
<!-- 阅卷监控组件 -->
<script lang="ts" setup>
import type { MarkingProgressResponse } from '@/service/types'
import { computed, onMounted, ref } from 'vue'
import { markingProgressUsingGet } from '@/service/yuejuanjindu'
defineOptions({
name: 'MarkingMonitor',
})
const props = defineProps<{
subjectId: number
}>()
// API数据
const markingData = ref<MarkingProgressResponse>()
const loading = ref(false)
const error = ref<string | null>(null)
// 计算总进度数据
const totalProgress = computed(() => {
const progress = markingData.value?.total_progress
if (!progress)
return { percentage: 0, completed: 0, total: 0 }
const total = (progress.marked_quantity || 0) + (progress.unmerged_quantity || 0)
const completed = progress.marked_quantity || 0
const percentage = total > 0 ? Math.round((completed / total) * 100) : 0
return { percentage, completed, total }
})
// 计算单评进度数据
const singleReviewProgress = computed(() => {
const progress = markingData.value?.single_progress
if (!progress)
return { percentage: 0, completed: 0, total: 0 }
const total = (progress.marked_quantity || 0) + (progress.unmerged_quantity || 0)
const completed = progress.marked_quantity || 0
const percentage = total > 0 ? Math.round((completed / total) * 100) : 0
return { percentage, completed, total }
})
// 计算终评进度数据
const finalReviewProgress = computed(() => {
const progress = markingData.value?.final_progress
if (!progress)
return { percentage: 0, completed: 0, total: 0 }
const total = (progress.marked_quantity || 0) + (progress.unmerged_quantity || 0)
const completed = progress.marked_quantity || 0
const percentage = total > 0 ? Math.round((completed / total) * 100) : 0
return { percentage, completed, total }
})
// 获取阅卷进度数据
async function fetchMarkingProgress() {
try {
loading.value = true
error.value = null
markingData.value = await markingProgressUsingGet({
params: {
exam_subject_id: props.subjectId,
},
})
}
catch (err) {
console.error('获取阅卷进度失败:', err)
error.value = '获取阅卷进度失败,请重试'
}
finally {
loading.value = false
}
}
// 计算剩余数量
function getRemaining(completed: number, total: number) {
return total - completed
}
// 跳转到阅卷进度页面
function navigateToProgress(questionId: number) {
uni.navigateTo({
url: `/pages/marking/marking-progress?examSubjectId=${props.subjectId}&questionId=${questionId}`,
})
}
// 跳转到阅卷质量页面
function navigateToQuality(questionId: number) {
uni.navigateTo({
url: `/pages/marking/marking-quality?examSubjectId=${props.subjectId}&questionId=${questionId}`,
})
}
// 组件挂载时获取数据
onMounted(() => {
fetchMarkingProgress()
})
</script>
<template>
<view class="p-2">
<!-- 三个进度环形图 -->
<view class="mx-2 mb-4 border border-slate-100 rounded-2xl bg-white p-6 shadow-md">
<view class="grid grid-cols-3 gap-4">
<!-- 总进度 -->
<view class="flex flex-col items-center">
<view class="relative mb-3 h-20 w-20">
<!-- 背景圆环 -->
<view class="absolute inset-0 border-4 border-gray-200 rounded-full" />
<!-- 进度圆环 -->
<view
class="absolute inset-0 transform border-4 border-blue-500 rounded-full -rotate-90"
:style="{
background: `conic-gradient(#3B82F6 ${totalProgress.percentage * 3.6}deg, transparent ${totalProgress.percentage * 3.6}deg)`,
borderRadius: '50%',
mask: 'radial-gradient(circle at center, transparent 32px, black 32px)',
WebkitMask: 'radial-gradient(circle at center, transparent 32px, black 32px)',
}"
/>
<!-- 百分比文字 -->
<view class="absolute inset-0 flex items-center justify-center">
<text class="text-lg font-bold">{{ totalProgress.percentage }}%</text>
</view>
</view>
<text class="mb-2 text-sm font-medium">总进度</text>
<text class="text-xs text-gray-600">已合分{{ totalProgress.completed }}</text>
<text class="text-xs text-gray-600">未合分{{ getRemaining(totalProgress.completed, totalProgress.total) }}</text>
</view>
<!-- 单评进度 -->
<view class="flex flex-col items-center">
<view class="relative mb-3 h-20 w-20">
<!-- 背景圆环 -->
<view class="absolute inset-0 border-4 border-gray-200 rounded-full" />
<!-- 进度圆环 -->
<view
class="absolute inset-0 transform border-4 border-green-500 rounded-full -rotate-90"
:style="{
background: `conic-gradient(#10B981 ${singleReviewProgress.percentage * 3.6}deg, transparent ${singleReviewProgress.percentage * 3.6}deg)`,
borderRadius: '50%',
mask: 'radial-gradient(circle at center, transparent 32px, black 32px)',
WebkitMask: 'radial-gradient(circle at center, transparent 32px, black 32px)',
}"
/>
<!-- 百分比文字 -->
<view class="absolute inset-0 flex items-center justify-center">
<text class="text-lg font-bold">{{ singleReviewProgress.percentage }}%</text>
</view>
</view>
<text class="mb-2 text-sm font-medium">单评进度</text>
<text class="text-xs text-gray-600">已阅卷{{ singleReviewProgress.completed }}</text>
<text class="text-xs text-gray-600">未合分{{ getRemaining(singleReviewProgress.completed, singleReviewProgress.total) }}</text>
</view>
<!-- 终评进度 -->
<view class="flex flex-col items-center">
<view class="relative mb-3 h-20 w-20">
<!-- 背景圆环 -->
<view class="absolute inset-0 border-4 border-gray-200 rounded-full" />
<!-- 进度圆环 -->
<view
class="absolute inset-0 transform border-4 border-orange-500 rounded-full -rotate-90"
:style="{
background: `conic-gradient(#F59E0B ${finalReviewProgress.percentage * 3.6}deg, transparent ${finalReviewProgress.percentage * 3.6}deg)`,
borderRadius: '50%',
mask: 'radial-gradient(circle at center, transparent 32px, black 32px)',
WebkitMask: 'radial-gradient(circle at center, transparent 32px, black 32px)',
}"
/>
<!-- 百分比文字 -->
<view class="absolute inset-0 flex items-center justify-center">
<text class="text-lg font-bold">{{ finalReviewProgress.percentage }}%</text>
</view>
</view>
<text class="mb-2 text-sm font-medium">终评进度</text>
<text class="text-xs text-gray-600">已阅卷{{ finalReviewProgress.completed }}</text>
<text class="text-xs text-gray-600">未合分{{ getRemaining(finalReviewProgress.completed, finalReviewProgress.total) }}</text>
</view>
</view>
</view>
<!-- 主观题列表 -->
<view class="mx-2">
<text class="mb-2 block text-lg font-semibold">主观题</text>
<!-- 加载状态 -->
<view v-if="loading" class="flex items-center justify-center py-8">
<text class="text-gray-500">加载中...</text>
</view>
<!-- 错误状态 -->
<view v-else-if="error" class="flex flex-col items-center justify-center py-8">
<text class="mb-2 text-red-500">{{ error }}</text>
<view
class="rounded-md bg-blue-100 px-4 py-2"
@click="fetchMarkingProgress"
>
<text class="text-blue-600">重新加载</text>
</view>
</view>
<!-- 主观题列表 -->
<view
v-for="question in markingData.questions"
v-else
:key="question.question_id"
class="mb-2 border border-slate-100 rounded-2xl bg-white p-4 shadow-md"
>
<!-- 题目信息 -->
<view class="mb-3 flex items-center justify-between">
<text class="text-base font-medium">{{ question.question_major }}.{{ question.question_minor }}</text>
<text class="text-sm text-gray-600">总分{{ question.full_score }} 平均分{{ question.average_score }}</text>
</view>
<!-- 进度条 -->
<view class="mb-3">
<view class="mb-2 flex items-center justify-between">
<text class="text-sm text-gray-600">阅卷进度</text>
<view class="h-4 w-[70%] rounded-full bg-gray-200">
<view
class="h-4 rounded-full bg-blue-500 transition-all duration-300"
:style="{ width: `${question.progress_percent}%` }"
/>
</view>
<!-- <text class="text-sm font-medium">{{ question.progress_percent }}%</text> -->
<text class="text-sm font-medium">{{ question.task_marked_num }}/{{ question.task_total_num }}</text>
</view>
<!-- <view class="mt-1 flex justify-center">
<text class="text-xs text-gray-500">{{ question.task_marked_num }}/{{ question.task_total_num }}</text>
</view> -->
</view>
<!-- 操作按钮 -->
<view class="flex items-center justify-end space-x-3">
<view
class="rounded-md bg-orange-100 px-3 py-1"
@click="navigateToProgress(question.question_id)"
>
<text class="flex items-center text-sm text-orange-600">
阅卷进度
</text>
</view>
<view
class="rounded-md bg-blue-100 px-3 py-1"
@click="navigateToQuality(question.question_id)"
>
<text class="flex items-center text-sm text-blue-600">
阅卷质量
</text>
</view>
</view>
</view>
</view>
</view>
</template>