2025-10-05 20:10:51 +08:00
|
|
|
<script setup lang="ts">
|
2025-10-10 00:12:59 +08:00
|
|
|
import { useQuery } from '@tanstack/vue-query'
|
2025-10-05 20:10:51 +08:00
|
|
|
import { computed, onMounted, ref } from 'vue'
|
2025-10-10 00:12:59 +08:00
|
|
|
import AdvantageAnalysisChart from '@/components/student/AdvantageAnalysisChart.vue'
|
|
|
|
|
import AverageComparisonChart from '@/components/student/AverageComparisonChart.vue'
|
|
|
|
|
import ScoreDistributionChart from '@/components/student/ScoreDistributionChart.vue'
|
|
|
|
|
import ScoreTrendChart from '@/components/student/ScoreTrendChart.vue'
|
|
|
|
|
import { teacherAnalysisPersonalReportUsingPost } from '@/service/laoshichengjifenxi'
|
2025-10-05 20:10:51 +08:00
|
|
|
import { useHomeStore } from '@/store/home'
|
|
|
|
|
|
|
|
|
|
const homeStore = useHomeStore()
|
|
|
|
|
|
2025-10-10 00:12:59 +08:00
|
|
|
const studentNumber = ref('')
|
|
|
|
|
const studentName = ref('')
|
2025-10-05 20:10:51 +08:00
|
|
|
const selectedSubjectId = ref(0)
|
|
|
|
|
|
2025-11-19 14:18:14 +08:00
|
|
|
const { data: reportData, isLoading } = useQuery({
|
2025-10-10 00:12:59 +08:00
|
|
|
queryKey: computed(() => [
|
|
|
|
|
'personal-report',
|
2025-11-20 23:24:05 +08:00
|
|
|
homeStore.selectedClassKey,
|
2025-10-10 00:12:59 +08:00
|
|
|
homeStore.selectedGradeKey,
|
|
|
|
|
homeStore.selectedExamId,
|
|
|
|
|
studentNumber.value,
|
|
|
|
|
selectedSubjectId.value,
|
|
|
|
|
]),
|
|
|
|
|
queryFn: async () => {
|
|
|
|
|
const response = await teacherAnalysisPersonalReportUsingPost({
|
|
|
|
|
body: {
|
2025-11-20 23:24:05 +08:00
|
|
|
class_key: homeStore.selectedClassKey,
|
2025-10-10 00:12:59 +08:00
|
|
|
grade_key: homeStore.selectedGradeKey,
|
|
|
|
|
exam_id: homeStore.selectedExamId,
|
|
|
|
|
student_number: studentNumber.value,
|
|
|
|
|
subject_id: selectedSubjectId.value || 0,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
return response
|
|
|
|
|
},
|
|
|
|
|
enabled: computed(() =>
|
2025-11-20 23:24:05 +08:00
|
|
|
!!homeStore.selectedClassKey
|
2025-10-10 00:12:59 +08:00
|
|
|
&& !!homeStore.selectedGradeKey
|
|
|
|
|
&& !!homeStore.selectedExamId
|
|
|
|
|
&& !!studentNumber.value,
|
|
|
|
|
),
|
|
|
|
|
staleTime: 30000,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const examSummary = computed(() => reportData.value?.exam_summary)
|
|
|
|
|
|
|
|
|
|
const studentInfo = computed(() => {
|
|
|
|
|
const summary = examSummary.value
|
|
|
|
|
if (!summary)
|
|
|
|
|
return null
|
|
|
|
|
return {
|
|
|
|
|
totalScore: summary.all_score || 0,
|
|
|
|
|
classRank: summary.class_rank || 0,
|
|
|
|
|
classTotal: summary.class_member_count || 0,
|
|
|
|
|
gradeRank: summary.grade_rank || 0,
|
|
|
|
|
gradeTotal: summary.grade_member_count || 0,
|
|
|
|
|
gradeAverage: summary.grade_average_score || 0,
|
|
|
|
|
classAverage: summary.class_average_score || 0,
|
|
|
|
|
fullScore: summary.full_score || 0,
|
|
|
|
|
}
|
2025-10-05 20:10:51 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const subjectTabs = computed(() => {
|
2025-11-19 14:18:14 +08:00
|
|
|
const tabs = [{ name: 'all', title: '全科', subjectId: 0 }]
|
2025-10-05 20:10:51 +08:00
|
|
|
homeStore.subjectOptions.forEach((subject) => {
|
|
|
|
|
tabs.push({
|
2025-10-10 00:12:59 +08:00
|
|
|
name: `subject-${subject.subjectId}`,
|
2025-10-05 20:10:51 +08:00
|
|
|
title: subject.label,
|
2025-10-10 00:12:59 +08:00
|
|
|
subjectId: subject.subjectId,
|
2025-10-05 20:10:51 +08:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
return tabs
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const activeTab = computed({
|
|
|
|
|
get: () => selectedSubjectId.value === 0 ? 'all' : `subject-${selectedSubjectId.value}`,
|
|
|
|
|
set: (value) => {
|
2025-11-19 14:18:14 +08:00
|
|
|
selectedSubjectId.value = value === 'all' ? 0 : Number.parseInt(value.replace('subject-', ''))
|
2025-10-05 20:10:51 +08:00
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function goBack() {
|
|
|
|
|
uni.navigateBack()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleTabClick({ name }: { name: string }) {
|
|
|
|
|
activeTab.value = name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
const pages = getCurrentPages()
|
|
|
|
|
const currentPage = pages[pages.length - 1]
|
|
|
|
|
const options = (currentPage as any).options || {}
|
2025-10-10 00:12:59 +08:00
|
|
|
studentNumber.value = options.student_number || ''
|
|
|
|
|
studentName.value = options.student_name || ''
|
|
|
|
|
selectedSubjectId.value = options.subject_id || 0
|
2025-10-05 20:10:51 +08:00
|
|
|
|
|
|
|
|
if (homeStore.examOptions.length === 0) {
|
|
|
|
|
await homeStore.fetchOptions()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
2025-11-19 14:18:14 +08:00
|
|
|
<view class="min-h-screen bg-gray-100 pb-8">
|
|
|
|
|
<wd-navbar placeholder fixed custom-class="bg-white">
|
2025-10-05 20:10:51 +08:00
|
|
|
<template #left>
|
2025-11-19 14:18:14 +08:00
|
|
|
<view class="h-8 w-8 flex items-center justify-center" @click="goBack">
|
|
|
|
|
<wd-icon name="arrow-left" size="20px" color="#333" />
|
2025-10-05 20:10:51 +08:00
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
<template #title>
|
2025-11-19 14:18:14 +08:00
|
|
|
<text class="text-base text-gray-800 font-bold">{{ studentName }} 成绩报告</text>
|
2025-10-05 20:10:51 +08:00
|
|
|
</template>
|
|
|
|
|
</wd-navbar>
|
|
|
|
|
|
2025-10-12 22:16:49 +08:00
|
|
|
<wd-tabs
|
|
|
|
|
v-model="activeTab"
|
|
|
|
|
sticky
|
2025-11-19 14:18:14 +08:00
|
|
|
line-width="20"
|
|
|
|
|
line-height="3"
|
|
|
|
|
color="#2563EB"
|
|
|
|
|
inactive-color="#64748B"
|
|
|
|
|
custom-class="bg-white shadow-sm mb-3"
|
2025-10-12 22:16:49 +08:00
|
|
|
@click="handleTabClick"
|
|
|
|
|
>
|
|
|
|
|
<wd-tab
|
|
|
|
|
v-for="tab in subjectTabs"
|
|
|
|
|
:key="tab.name"
|
|
|
|
|
:name="tab.name"
|
|
|
|
|
:title="tab.title"
|
|
|
|
|
/>
|
|
|
|
|
</wd-tabs>
|
|
|
|
|
|
2025-11-19 14:18:14 +08:00
|
|
|
<view v-if="isLoading" class="h-64 flex items-center justify-center">
|
|
|
|
|
<wd-loading size="32" color="#2563EB" />
|
2025-10-10 00:12:59 +08:00
|
|
|
</view>
|
2025-10-05 20:10:51 +08:00
|
|
|
|
2025-11-19 14:18:14 +08:00
|
|
|
<view v-else-if="studentInfo" class="px-3">
|
|
|
|
|
<view class="relative mb-3 overflow-hidden rounded-xl bg-blue-600 shadow-lg">
|
|
|
|
|
<view class="absolute right-0 top-0 h-32 w-32 translate-x-8 rounded-full bg-white opacity-10 -translate-y-8" />
|
|
|
|
|
<view class="absolute bottom-0 left-0 h-24 w-24 translate-y-8 rounded-full bg-white opacity-10 -translate-x-8" />
|
2025-10-05 20:10:51 +08:00
|
|
|
|
2025-11-19 14:18:14 +08:00
|
|
|
<view class="relative z-10 p-5">
|
|
|
|
|
<view class="flex items-start justify-between">
|
|
|
|
|
<view>
|
|
|
|
|
<text class="mb-1 block text-sm text-blue-100">学生姓名</text>
|
|
|
|
|
<text class="text-xl text-white font-bold">{{ studentName }}</text>
|
2025-10-05 20:10:51 +08:00
|
|
|
</view>
|
2025-11-19 14:18:14 +08:00
|
|
|
<view class="text-right">
|
|
|
|
|
<text class="mb-1 block text-sm text-blue-100">卷面总分</text>
|
|
|
|
|
<view class="flex items-baseline justify-end">
|
|
|
|
|
<text class="mr-1 text-3xl text-white font-bold">{{ studentInfo.totalScore }}</text>
|
|
|
|
|
<text class="text-sm text-blue-200">/ {{ studentInfo.fullScore }}</text>
|
2025-10-05 20:10:51 +08:00
|
|
|
</view>
|
|
|
|
|
</view>
|
2025-11-19 14:18:14 +08:00
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
2025-10-05 20:10:51 +08:00
|
|
|
|
2025-11-19 14:18:14 +08:00
|
|
|
<view class="grid grid-cols-2 mb-4 gap-3">
|
|
|
|
|
<view class="flex flex-col justify-center rounded-xl bg-white p-4 shadow-sm">
|
|
|
|
|
<view class="mb-1 flex items-baseline">
|
|
|
|
|
<text class="mr-1 text-xl text-gray-800 font-bold">{{ studentInfo.classRank }}</text>
|
|
|
|
|
<text class="text-xs text-gray-400">/ {{ studentInfo.classTotal }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<text class="text-xs text-gray-500">班级排名</text>
|
|
|
|
|
</view>
|
2025-10-05 20:10:51 +08:00
|
|
|
|
2025-11-19 14:18:14 +08:00
|
|
|
<view class="flex flex-col justify-center rounded-xl bg-white p-4 shadow-sm">
|
|
|
|
|
<view class="mb-1 flex items-baseline">
|
|
|
|
|
<text class="mr-1 text-xl text-gray-800 font-bold">{{ studentInfo.gradeRank }}</text>
|
|
|
|
|
<text class="text-xs text-gray-400">/ {{ studentInfo.gradeTotal }}</text>
|
2025-10-05 20:10:51 +08:00
|
|
|
</view>
|
2025-11-19 14:18:14 +08:00
|
|
|
<text class="text-xs text-gray-500">年级排名</text>
|
2025-10-05 20:10:51 +08:00
|
|
|
</view>
|
2025-10-10 00:12:59 +08:00
|
|
|
|
2025-11-19 14:18:14 +08:00
|
|
|
<view class="flex flex-col justify-center rounded-xl bg-white p-4 shadow-sm">
|
|
|
|
|
<text class="mb-1 text-xl text-gray-800 font-bold">{{ studentInfo.classAverage.toFixed(1) }}</text>
|
|
|
|
|
<text class="text-xs text-gray-500">班级平均分</text>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="flex flex-col justify-center rounded-xl bg-white p-4 shadow-sm">
|
|
|
|
|
<text class="mb-1 text-xl text-gray-800 font-bold">{{ studentInfo.gradeAverage.toFixed(1) }}</text>
|
|
|
|
|
<text class="text-xs text-gray-500">年级平均分</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="space-y-3">
|
|
|
|
|
<view class="rounded-xl bg-white p-3 shadow-sm">
|
|
|
|
|
<view class="mb-2 border-b border-gray-100 pb-2">
|
|
|
|
|
<text class="border-l-4 border-blue-500 pl-2 text-sm text-gray-700 font-bold">成绩分布</text>
|
|
|
|
|
</view>
|
|
|
|
|
<ScoreDistributionChart :data="reportData?.learning_situation_analysis" />
|
|
|
|
|
</view>
|
2025-10-10 00:12:59 +08:00
|
|
|
|
|
|
|
|
<template v-if="selectedSubjectId === 0">
|
2025-11-19 14:18:14 +08:00
|
|
|
<view class="rounded-xl bg-white p-3 shadow-sm">
|
|
|
|
|
<view class="mb-2 border-b border-gray-100 pb-2">
|
|
|
|
|
<text class="border-l-4 border-blue-500 pl-2 text-sm text-gray-700 font-bold">优劣势分析</text>
|
|
|
|
|
</view>
|
|
|
|
|
<AdvantageAnalysisChart :data="reportData?.advantage_analysis" />
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="rounded-xl bg-white p-3 shadow-sm">
|
|
|
|
|
<view class="mb-2 border-b border-gray-100 pb-2">
|
|
|
|
|
<text class="border-l-4 border-blue-500 pl-2 text-sm text-gray-700 font-bold">均分对比</text>
|
|
|
|
|
</view>
|
|
|
|
|
<AverageComparisonChart
|
|
|
|
|
:data="reportData?.average_comparison_analysis"
|
|
|
|
|
:exam-summary="reportData?.exam_summary"
|
|
|
|
|
/>
|
|
|
|
|
</view>
|
2025-10-10 00:12:59 +08:00
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template v-else>
|
2025-11-19 14:18:14 +08:00
|
|
|
<view class="rounded-xl bg-white p-3 shadow-sm">
|
|
|
|
|
<view class="mb-2 border-b border-gray-100 pb-2">
|
|
|
|
|
<text class="border-l-4 border-blue-500 pl-2 text-sm text-gray-700 font-bold">历史趋势</text>
|
|
|
|
|
</view>
|
|
|
|
|
<ScoreTrendChart :data="reportData?.score_trend" />
|
|
|
|
|
</view>
|
2025-10-10 00:12:59 +08:00
|
|
|
</template>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
2025-11-19 14:18:14 +08:00
|
|
|
<view v-else class="mt-20 flex flex-col items-center justify-center">
|
|
|
|
|
<wd-img
|
|
|
|
|
width="160"
|
|
|
|
|
height="160"
|
|
|
|
|
src="https://img.yzcdn.cn/vant/empty-image-default.png"
|
|
|
|
|
/>
|
|
|
|
|
<text class="mt-4 text-sm text-gray-400">暂无数据</text>
|
2025-10-05 20:10:51 +08:00
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<route lang="jsonc">
|
|
|
|
|
{
|
|
|
|
|
"style": {
|
|
|
|
|
"navigationStyle": "custom",
|
|
|
|
|
"navigationBarTitleText": "学生详情"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</route>
|