256 lines
8.0 KiB
Vue
256 lines
8.0 KiB
Vue
<script setup lang="ts">
|
||
import { useQuery } from '@tanstack/vue-query'
|
||
import { computed, onMounted, ref } from 'vue'
|
||
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'
|
||
import { useHomeStore } from '@/store/home'
|
||
|
||
const homeStore = useHomeStore()
|
||
|
||
// 获取页面参数
|
||
const studentNumber = ref('')
|
||
const studentName = ref('')
|
||
|
||
// 选中的科目ID,0表示全科
|
||
const selectedSubjectId = ref(0)
|
||
|
||
// 使用 TanStack Query 获取个人报告数据
|
||
const {
|
||
data: reportData,
|
||
isLoading,
|
||
} = useQuery({
|
||
queryKey: computed(() => [
|
||
'personal-report',
|
||
homeStore.selectedClassId,
|
||
homeStore.selectedGradeKey,
|
||
homeStore.selectedExamId,
|
||
studentNumber.value,
|
||
selectedSubjectId.value,
|
||
]),
|
||
queryFn: async () => {
|
||
const response = await teacherAnalysisPersonalReportUsingPost({
|
||
body: {
|
||
class_key: homeStore.selectedClassId,
|
||
grade_key: homeStore.selectedGradeKey,
|
||
exam_id: homeStore.selectedExamId,
|
||
student_number: studentNumber.value,
|
||
subject_id: selectedSubjectId.value || 0,
|
||
},
|
||
})
|
||
return response
|
||
},
|
||
enabled: computed(() =>
|
||
!!homeStore.selectedClassId
|
||
&& !!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,
|
||
}
|
||
})
|
||
|
||
// 科目选项(包含全科)
|
||
const subjectTabs = computed(() => {
|
||
const tabs = [
|
||
{ name: 'all', title: '全科', subjectId: 0 },
|
||
]
|
||
|
||
homeStore.subjectOptions.forEach((subject) => {
|
||
tabs.push({
|
||
name: `subject-${subject.subjectId}`,
|
||
title: subject.label,
|
||
subjectId: subject.subjectId,
|
||
})
|
||
})
|
||
|
||
return tabs
|
||
})
|
||
|
||
// 当前选中的科目标签
|
||
const activeTab = computed({
|
||
get: () => selectedSubjectId.value === 0 ? 'all' : `subject-${selectedSubjectId.value}`,
|
||
set: (value) => {
|
||
if (value === 'all') {
|
||
selectedSubjectId.value = 0
|
||
}
|
||
else {
|
||
const subjectId = Number.parseInt(value.replace('subject-', ''))
|
||
selectedSubjectId.value = subjectId
|
||
}
|
||
},
|
||
})
|
||
|
||
// 返回上一页
|
||
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 || {}
|
||
studentNumber.value = options.student_number || ''
|
||
studentName.value = options.student_name || ''
|
||
selectedSubjectId.value = options.subject_id || 0
|
||
|
||
// 如果store中没有数据,先获取选项数据
|
||
if (homeStore.examOptions.length === 0) {
|
||
await homeStore.fetchOptions()
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<view class="min-h-screen from-blue-50 to-gray-50 bg-gradient-to-b">
|
||
<!-- 导航栏 -->
|
||
<wd-navbar placeholder fixed>
|
||
<template #left>
|
||
<view class="flex items-center" @click="goBack">
|
||
<wd-icon name="arrow-left" size="20px" />
|
||
</view>
|
||
</template>
|
||
<template #title>
|
||
<text class="text-lg font-semibold">{{ studentName }}成绩报告</text>
|
||
</template>
|
||
</wd-navbar>
|
||
|
||
<!-- 科目选择标签栏 -->
|
||
<wd-tabs
|
||
v-model="activeTab"
|
||
sticky
|
||
class="bg-white shadow-sm"
|
||
@click="handleTabClick"
|
||
>
|
||
<wd-tab
|
||
v-for="tab in subjectTabs"
|
||
:key="tab.name"
|
||
:name="tab.name"
|
||
:title="tab.title"
|
||
/>
|
||
</wd-tabs>
|
||
|
||
<!-- 加载状态 -->
|
||
<view v-if="isLoading" class="h-screen flex items-center justify-center">
|
||
<wd-loading size="32" />
|
||
</view>
|
||
|
||
<!-- 内容区域 -->
|
||
<view v-else-if="studentInfo" class="pb-4">
|
||
<!-- 主要内容区域 -->
|
||
<view class="p-4 space-y-4">
|
||
<!-- 个人成绩卡片 -->
|
||
<view class="relative rounded-2xl from-blue-500 to-blue-600 bg-gradient-to-br p-5 shadow-lg">
|
||
<view class="absolute left-0 top-4 rounded-r-full bg-white/40 px-4 backdrop-blur">
|
||
<text class="text-lg text-white">{{ studentName }}</text>
|
||
</view>
|
||
<!-- 总分展示 -->
|
||
<view class="mb-4 flex items-center justify-end gap-4">
|
||
<view class="mt-1 flex items-baseline gap-1">
|
||
<text class="text-4xl text-white font-bold">{{ studentInfo.totalScore }}</text>
|
||
<text class="text-lg text-blue-100">/{{ studentInfo.fullScore }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 成绩统计网格 -->
|
||
<view class="grid grid-cols-2 gap-3">
|
||
<!-- 班级排名 -->
|
||
<view class="flex flex-col rounded-xl bg-white/90 p-3 backdrop-blur">
|
||
<view class="mb-1 flex items-baseline gap-1">
|
||
<text class="text-2xl text-slate-800 font-bold">{{ studentInfo.classRank }}</text>
|
||
<text class="text-xs text-slate-500">/{{ studentInfo.classTotal }}</text>
|
||
</view>
|
||
<text class="text-sm text-slate-600">班级排名</text>
|
||
</view>
|
||
|
||
<!-- 年级排名 -->
|
||
<view class="flex flex-col rounded-xl bg-white/90 p-3 backdrop-blur">
|
||
<view class="mb-1 flex items-baseline gap-1">
|
||
<text class="text-2xl text-slate-800 font-bold">{{ studentInfo.gradeRank }}</text>
|
||
<text class="text-xs text-slate-500">/{{ studentInfo.gradeTotal }}</text>
|
||
</view>
|
||
<text class="text-sm text-slate-600">年级排名</text>
|
||
</view>
|
||
|
||
<!-- 班级平均分 -->
|
||
<view class="flex flex-col rounded-xl bg-white/90 p-3 backdrop-blur">
|
||
<text class="text-2xl text-slate-800 font-bold">{{ studentInfo.classAverage.toFixed(1) }}</text>
|
||
<text class="text-sm text-slate-600">班级平均分</text>
|
||
</view>
|
||
|
||
<!-- 年级平均分 -->
|
||
<view class="flex flex-col rounded-xl bg-white/90 p-3 backdrop-blur">
|
||
<text class="text-2xl text-slate-800 font-bold">{{ studentInfo.gradeAverage.toFixed(1) }}</text>
|
||
<text class="text-sm text-slate-600">年级平均分</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 成绩分布图 -->
|
||
<ScoreDistributionChart :data="reportData?.learning_situation_analysis" />
|
||
|
||
<!-- 全科专属内容 -->
|
||
<template v-if="selectedSubjectId === 0">
|
||
<!-- 优劣势分析 -->
|
||
<AdvantageAnalysisChart :data="reportData?.advantage_analysis" />
|
||
|
||
<!-- 均分对比 -->
|
||
<AverageComparisonChart
|
||
:data="reportData?.average_comparison_analysis"
|
||
:exam-summary="reportData?.exam_summary"
|
||
/>
|
||
</template>
|
||
|
||
<!-- 单科专属内容 -->
|
||
<template v-else>
|
||
<!-- 成绩趋势 -->
|
||
<ScoreTrendChart :data="reportData?.score_trend" />
|
||
</template>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 暂无数据 -->
|
||
<view v-else class="h-screen flex flex-col items-center justify-center">
|
||
<text class="text-gray-400">暂无数据</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<route lang="jsonc">
|
||
{
|
||
"style": {
|
||
"navigationStyle": "custom",
|
||
"navigationBarTitleText": "学生详情"
|
||
}
|
||
}
|
||
</route>
|