236 lines
5.6 KiB
Vue
236 lines
5.6 KiB
Vue
|
|
<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>
|