260 lines
5.7 KiB
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>
|