130 lines
2.8 KiB
Vue
130 lines
2.8 KiB
Vue
<script lang="ts" setup>
|
||
import type * as API from '@/service/types'
|
||
import UniEcharts from 'uni-echarts'
|
||
import { computed } from 'vue'
|
||
|
||
const props = defineProps<{
|
||
data: API.StudentScoreStat[]
|
||
loading?: boolean
|
||
}>()
|
||
|
||
const emit = defineEmits<{
|
||
viewStudents: [score: number]
|
||
}>()
|
||
|
||
// 图表配置(横向柱状图)
|
||
const chartOption = computed(() => {
|
||
if (!props.data || props.data.length === 0) {
|
||
return {}
|
||
}
|
||
|
||
// 按分数排序
|
||
const sortedData = [...props.data].sort((a, b) => (a.score || 0) - (b.score || 0))
|
||
|
||
const categories = sortedData.map(item => `${item.score || 0}分`)
|
||
const counts = sortedData.map(item => item.student_count || 0)
|
||
|
||
return {
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
confine: true,
|
||
axisPointer: {
|
||
type: 'shadow',
|
||
},
|
||
formatter: (params: any) => {
|
||
if (!Array.isArray(params))
|
||
return ''
|
||
const param = params[0]
|
||
// 使用 \n 换行而不是 <br/>
|
||
return `${param.axisValue}\n${param.marker} 人数: ${param.value}人`
|
||
},
|
||
},
|
||
grid: {
|
||
left: 50,
|
||
right: 30,
|
||
bottom: 30,
|
||
top: 20,
|
||
containLabel: false,
|
||
},
|
||
// 横向柱状图:xAxis 为数值轴,yAxis 为类目轴
|
||
xAxis: {
|
||
type: 'value',
|
||
name: '人数',
|
||
nameTextStyle: {
|
||
fontSize: 11,
|
||
},
|
||
axisLabel: {
|
||
fontSize: 10,
|
||
},
|
||
splitLine: {
|
||
lineStyle: {
|
||
type: 'dashed',
|
||
color: '#eeeeee',
|
||
},
|
||
},
|
||
},
|
||
yAxis: {
|
||
type: 'category',
|
||
data: categories,
|
||
axisLabel: {
|
||
fontSize: 10,
|
||
},
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: '#cccccc',
|
||
},
|
||
},
|
||
// 反转 Y 轴,让分数从上到下递增
|
||
inverse: true,
|
||
},
|
||
series: [
|
||
{
|
||
name: '人数',
|
||
type: 'bar',
|
||
data: counts,
|
||
itemStyle: {
|
||
color: '#3b82f6',
|
||
},
|
||
barMaxWidth: 30,
|
||
label: {
|
||
show: true,
|
||
position: 'right',
|
||
fontSize: 10,
|
||
formatter: '{c}人',
|
||
},
|
||
},
|
||
],
|
||
}
|
||
})
|
||
|
||
const showChart = computed(() => {
|
||
return !props.loading && props.data && props.data.length > 0
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<view class="score-chart">
|
||
<!-- 图表 -->
|
||
<view v-if="showChart" class="chart-container">
|
||
<uni-echarts :option="chartOption" custom-class="chart" />
|
||
</view>
|
||
|
||
<!-- 加载状态 -->
|
||
<view v-if="loading" class="h-60 flex items-center justify-center">
|
||
<wd-loading size="24" />
|
||
</view>
|
||
|
||
<!-- 暂无数据 -->
|
||
<view v-if="!loading && (!data || data.length === 0)" class="py-8 text-center">
|
||
<text class="text-sm text-gray-500">暂无数据</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.chart {
|
||
width: 100%;
|
||
height: 250px;
|
||
}
|
||
</style>
|