Files
ojnext/src/oj/ai/components/DifficultyGradeChart.vue
2025-10-07 03:05:08 +08:00

146 lines
3.2 KiB
Vue

<template>
<n-card title="难度掌握情况" size="small" v-if="show">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">
了解不同难度题目的完成等级分布
</n-text>
</template>
<div style="height: 300px">
<Bar :data="data" :options="options" />
</div>
</n-card>
</template>
<script setup lang="ts">
import { Bar } from "vue-chartjs"
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from "chart.js"
import { useAIStore } from "oj/store/ai"
import type { Grade } from "utils/types"
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend)
const aiStore = useAIStore()
// 难度和等级的顺序(后端返回的是中文)
const difficultyOrder = ["简单", "中等", "困难"]
const gradeOrder: Grade[] = ["S", "A", "B", "C"]
// 统计每个难度-等级组合的题目数量
const matrix = computed(() => {
const result: { [difficulty: string]: { [grade: string]: number } } = {}
// 初始化矩阵
difficultyOrder.forEach((diff) => {
result[diff] = {}
gradeOrder.forEach((grade) => {
result[diff][grade] = 0
})
})
// 统计数据
aiStore.detailsData.solved.forEach((item) => {
const diff = item.difficulty
const grade = item.grade
if (diff && grade && result[diff]) {
result[diff][grade]++
}
})
return result
})
const show = computed(() => {
return aiStore.detailsData.solved.length > 0
})
// 为每个等级准备数据集
const data = computed(() => {
// 为每个等级生成一个 dataset
const datasets = gradeOrder.map((grade) => {
return {
label: `等级 ${grade}`,
data: difficultyOrder.map((diff) => matrix.value[diff][grade]),
backgroundColor: getGradeColor(grade),
borderColor: getGradeColor(grade),
borderWidth: 1,
}
})
return {
labels: difficultyOrder,
datasets,
}
})
// 根据等级返回对应的颜色
function getGradeColor(grade: Grade): string {
const colors: { [key in Grade]: string } = {
S: "#FF6384",
A: "#FFCE56",
B: "#36A2EB",
C: "#95F204",
}
return colors[grade]
}
const options = {
responsive: true,
maintainAspectRatio: false,
interaction: {
intersect: false,
mode: "index" as const,
},
scales: {
x: {
stacked: true,
grid: {
display: false,
},
},
y: {
stacked: true,
ticks: {
stepSize: 1,
},
title: {
display: true,
text: "题目数量",
},
},
},
plugins: {
legend: {
display: true,
position: "bottom" as const,
labels: {
boxWidth: 12,
padding: 8,
font: {
size: 11,
},
},
},
title: {
display: false,
},
tooltip: {
callbacks: {
footer: (items: any[]) => {
const total = items.reduce((sum, item) => sum + item.parsed.y, 0)
return `该难度总计: ${total}`
},
},
},
},
}
</script>