xlx_teacher_app/src/components/student/AverageComparisonChart.vue

236 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import type * as API from '@/service/types'
import UniEcharts from 'uni-echarts'
import { computed, ref } from 'vue'
const props = defineProps<{
data: API.AverageComparisonAnalysis | undefined
examSummary: API.ExamSummary | undefined
}>()
// 对比模式班级平均分、年级平均分、年级前N名
type ComparisonMode = 'class' | 'grade' | 'top_n'
const comparisonMode = ref<ComparisonMode>('class')
// 年级前N名配置
const topN = ref(50)
const showTopNDialog = ref(false)
const tempTopN = ref(topN.value)
// 图表标题
const chartTitle = computed(() => {
if (comparisonMode.value === 'class') {
return '班级平均分对比'
}
else if (comparisonMode.value === 'grade') {
return '年级平均分对比'
}
else {
return `年级前${topN.value}名对比`
}
})
// 图表配置
const chartOption = computed(() => {
if (!props.data?.subjects || props.data.subjects.length === 0) {
return {}
}
const categories = props.data.subjects.map(item => item.subject_name || '')
const myScores = props.data.subjects.map(item => item.my_score || 0)
const avgScores = props.data.subjects.map(item => item.average_score || 0)
let comparisonLabel = ''
if (comparisonMode.value === 'class') {
comparisonLabel = '班级平均分'
}
else if (comparisonMode.value === 'grade') {
comparisonLabel = '年级平均分'
}
else {
comparisonLabel = `年级前${topN.value}名平均分`
}
return {
tooltip: {
trigger: 'axis',
confine: true,
axisPointer: {
type: 'shadow',
},
formatter: (params: any) => {
if (!Array.isArray(params))
return ''
let result = `${params[0].axisValue}`
params.forEach((param: any) => {
result += `\n${param.marker} ${param.seriesName}: ${param.value.toFixed(2)}`
})
return result
},
},
legend: {
data: ['我的成绩', comparisonLabel],
top: 5,
textStyle: {
fontSize: 12,
},
},
grid: {
left: 45,
right: 15,
bottom: 40,
top: 40,
containLabel: false,
},
xAxis: {
type: 'category',
data: categories,
axisLabel: {
rotate: 30,
interval: 0,
fontSize: 10,
},
axisLine: {
lineStyle: {
color: '#cccccc',
},
},
},
yAxis: {
type: 'value',
name: '分数',
nameTextStyle: {
fontSize: 11,
},
axisLabel: {
fontSize: 10,
},
splitLine: {
lineStyle: {
type: 'dashed',
color: '#eeeeee',
},
},
},
series: [
{
name: '我的成绩',
type: 'bar',
data: myScores,
itemStyle: {
color: '#10b981',
},
barMaxWidth: 35,
},
{
name: comparisonLabel,
type: 'bar',
data: avgScores,
itemStyle: {
color: '#94a3b8',
},
barMaxWidth: 35,
},
],
}
})
const showChart = computed(() => {
return props.data?.subjects && props.data.subjects.length > 0
})
// 切换对比模式
function setComparisonMode(mode: ComparisonMode) {
if (mode === 'top_n') {
showTopNDialog.value = true
}
else {
comparisonMode.value = mode
}
}
// 确认设置前N名
function confirmTopN() {
topN.value = tempTopN.value
comparisonMode.value = 'top_n'
showTopNDialog.value = false
}
// 取消设置
function cancelTopN() {
showTopNDialog.value = false
tempTopN.value = topN.value
}
</script>
<template>
<view class="rounded-xl bg-white p-4 shadow-sm">
<!-- 标题 -->
<view class="mb-4">
<text class="text-lg text-slate-800 font-semibold">{{ chartTitle }}</text>
</view>
<!-- 模式切换按钮 -->
<view class="mb-4 flex justify-center gap-2">
<wd-button
size="small"
:type="comparisonMode === 'class' ? 'primary' : 'default'"
@click="setComparisonMode('class')"
>
班级平均分
</wd-button>
<wd-button
size="small"
:type="comparisonMode === 'grade' ? 'primary' : 'default'"
@click="setComparisonMode('grade')"
>
年级平均分
</wd-button>
<wd-button
size="small"
:type="comparisonMode === 'top_n' ? 'primary' : 'default'"
@click="setComparisonMode('top_n')"
>
年级前{{ topN }}
</wd-button>
</view>
<!-- 图表 -->
<view v-if="showChart" class="chart-container">
<uni-echarts :option="chartOption" custom-class="chart" />
</view>
<!-- 暂无数据 -->
<view v-else class="h-60 flex items-center justify-center rounded-lg bg-gray-50">
<text class="text-sm text-gray-400">暂无数据</text>
</view>
<!-- 设置年级前N名弹窗 -->
<wd-popup v-model="showTopNDialog" position="center" :close-on-click-modal="false">
<view class="w-70 rounded-2 bg-white p-6">
<view class="mb-4 text-center">
<text class="text-base text-slate-800 font-semibold">设置年级前N名</text>
</view>
<view class="mb-6 flex justify-center p-4">
<wd-input-number v-model="tempTopN" :min="1" :max="200" :step="1" />
</view>
<view class="flex gap-3">
<wd-button block type="default" @click="cancelTopN">
取消
</wd-button>
<wd-button block type="primary" @click="confirmTopN">
确定
</wd-button>
</view>
</view>
</wd-popup>
</view>
</template>
<style scoped>
.chart {
width: 100%;
height: 280px;
}
</style>