xlx_teacher_app/src/components/class-analysis/AverageScoreChart.vue

260 lines
5.7 KiB
Vue

<script lang="ts" setup>
import type * as API from '@/service/types'
import * as echarts from 'echarts'
import { computed, onMounted, ref, watch } from 'vue'
import { teacherAnalysisTrendUsingPost } from '@/service/laoshichengjifenxi'
import { useHomeStore } from '@/store/home'
// 类型别名
type TrendInfo = API.TrendInfo
const props = defineProps<{
selectedSubjectId: number
compareClassId: number | null
}>()
const emit = defineEmits<{
openCompareClassDialog: []
}>()
// 使用store
const homeStore = useHomeStore()
// 数据
const classTrendData = ref<TrendInfo[]>([])
const compareTrendData = ref<TrendInfo[]>([])
// 加载状态
const loading = ref(false)
// 图表引用
const chartRef = ref<HTMLElement>()
let chartInstance: echarts.ECharts | null = null
// 对比班级名称
const compareClassName = computed(() => {
if (!props.compareClassId) {
return '选择对比班级'
}
const cls = homeStore.classOptions.find(item => item.value === props.compareClassId)
return cls?.label || '选择对比班级'
})
// 获取走势数据
async function fetchTrendData() {
if (!homeStore.selectedClassId || !homeStore.selectedGradeKey) {
return
}
try {
loading.value = true
// 获取本班级走势数据
const classResponse = await teacherAnalysisTrendUsingPost({
body: {
class_key: homeStore.selectedClassId,
grade_key: homeStore.selectedGradeKey,
subject_id: props.selectedSubjectId || undefined,
top_n: 50,
},
})
if (classResponse) {
classTrendData.value = classResponse.trend_list || []
}
// 如果选择了对比班级,获取对比班级的走势数据
if (props.compareClassId) {
const compareResponse = await teacherAnalysisTrendUsingPost({
body: {
class_key: props.compareClassId,
grade_key: homeStore.selectedGradeKey,
subject_id: props.selectedSubjectId || undefined,
top_n: 50,
},
})
if (compareResponse) {
compareTrendData.value = compareResponse.trend_list || []
}
}
else {
compareTrendData.value = []
}
updateChart()
}
catch (error) {
console.error('获取走势数据失败:', error)
uni.showToast({
title: '获取数据失败',
icon: 'none',
})
}
finally {
loading.value = false
}
}
// 初始化图表
function initChart() {
if (!chartRef.value)
return
chartInstance = echarts.init(chartRef.value)
updateChart()
}
// 更新图表
function updateChart() {
if (!chartInstance)
return
const option = getAverageScoreOption()
chartInstance.setOption(option, true)
}
// 班级平均分图表配置
function getAverageScoreOption(): echarts.EChartsOption {
// 获取考试名称作为横轴
const categories = classTrendData.value.map(item => item.exam_name || '')
// 本班级平均分
const classScores = classTrendData.value.map(item => item.class_avg_score || 0)
// 年级平均分
const gradeScores = classTrendData.value.map(item => item.grade_avg_score || 0)
// 对比班级平均分(如果选择了对比班级)
const compareScores = compareTrendData.value.map(item => item.class_avg_score || 0)
const series: any[] = [
{
name: '本班级',
type: 'line',
data: classScores,
itemStyle: {
color: '#3b82f6',
},
},
{
name: '年级平均',
type: 'line',
data: gradeScores,
itemStyle: {
color: '#f59e0b',
},
},
]
const legendData = ['本班级', '年级平均']
// 如果有对比班级数据,添加到图表中
if (props.compareClassId && compareTrendData.value.length > 0) {
series.push({
name: '对比班级',
type: 'line',
data: compareScores,
itemStyle: {
color: '#10b981',
},
})
legendData.push('对比班级')
}
return {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
},
},
legend: {
data: legendData,
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'category',
data: categories,
},
yAxis: {
type: 'value',
name: '分数',
},
series,
}
}
// 打开对比班级选择
function openCompareClassDialog() {
emit('openCompareClassDialog')
}
// 监听props变化
watch([() => props.selectedSubjectId, () => props.compareClassId], () => {
if (homeStore.selectedClassId && homeStore.selectedGradeKey) {
fetchTrendData()
}
}, { immediate: false })
// 组件初始化
onMounted(() => {
// 初始化图表
setTimeout(() => {
initChart()
}, 100)
// 获取初始数据
if (homeStore.selectedClassId && homeStore.selectedGradeKey) {
fetchTrendData()
}
})
// 暴露方法给父组件
defineExpose({
fetchTrendData,
})
</script>
<template>
<view class="rounded-xl bg-white p-4 shadow-sm">
<view class="mb-4 flex items-center justify-between">
<text class="text-lg text-slate-800 font-semibold">均分对比</text>
<wd-button size="small" @click="openCompareClassDialog">
{{ compareClassName }}
</wd-button>
</view>
<!-- 图表容器 -->
<view
ref="chartRef"
class="w-full"
/>
<!-- 暂无数据提示 -->
<view v-if="classTrendData.length === 0 && !loading" class="py-8 text-center">
<text class="text-sm text-gray-500">暂无数据</text>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="h-80 flex items-center justify-center">
<wd-loading size="24" />
</view>
</view>
</template>
<style lang="scss" scoped>
// 确保图表容器有正确的尺寸
[ref='chartRef'] {
width: 100%;
height: 200px;
}
</style>