262 lines
9.9 KiB
Vue
262 lines
9.9 KiB
Vue
<!-- 阅卷监控组件 -->
|
||
<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>
|