update
Some checks failed
Deploy / deploy (build, debian, 22, /root/OJDeploy/data/clientnext) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822, /root/OJ/data/dist) (push) Has been cancelled

This commit is contained in:
2026-05-19 07:44:26 -06:00
parent ffa55cb92d
commit 5f92aeaea4

View File

@@ -52,13 +52,13 @@ interface ClassComparison {
iqr: number iqr: number
std_dev: number std_dev: number
top_10_avg: number top_10_avg: number
middle_80_avg: number
bottom_10_avg: number bottom_10_avg: number
top_25_avg: number
bottom_25_avg: number
excellent_rate: number excellent_rate: number
pass_rate: number pass_rate: number
active_rate: number active_rate: number
ac_rate: number ac_rate: number
composite_score: number
recent_total_ac?: number recent_total_ac?: number
recent_avg_ac?: number recent_avg_ac?: number
recent_median_ac?: number recent_median_ac?: number
@@ -170,6 +170,24 @@ function getClassColor(index: number) {
return colors[index % colors.length] return colors[index % colors.length]
} }
// 综合分对比图
const compositeScoreChartData = computed(() => {
if (comparisons.value.length === 0) return null
const labels = comparisons.value.map((c) => c.class_name)
const datasets = [
{
label: "综合分",
data: comparisons.value.map((c) => c.composite_score),
backgroundColor: comparisons.value.map((_, i) => getClassColor(i).bg),
borderColor: comparisons.value.map((_, i) => getClassColor(i).border),
borderWidth: 2,
},
]
return { labels, datasets }
})
// 总AC数对比图 - 每个班级用不同颜色 // 总AC数对比图 - 每个班级用不同颜色
const totalAcChartData = computed(() => { const totalAcChartData = computed(() => {
if (comparisons.value.length === 0) return null if (comparisons.value.length === 0) return null
@@ -278,14 +296,14 @@ const activeRateChartData = computed(() => {
return { labels, datasets } return { labels, datasets }
}) })
// 前10平均对比图 // 前10%平均对比图
const top10AvgChartData = computed(() => { const top10AvgChartData = computed(() => {
if (comparisons.value.length === 0) return null if (comparisons.value.length === 0) return null
const labels = comparisons.value.map((c) => c.class_name) const labels = comparisons.value.map((c) => c.class_name)
const datasets = [ const datasets = [
{ {
label: "前10平均", label: "前10%平均",
data: comparisons.value.map((c) => c.top_10_avg), data: comparisons.value.map((c) => c.top_10_avg),
backgroundColor: comparisons.value.map((_, i) => getClassColor(i).bg), backgroundColor: comparisons.value.map((_, i) => getClassColor(i).bg),
borderColor: comparisons.value.map((_, i) => getClassColor(i).border), borderColor: comparisons.value.map((_, i) => getClassColor(i).border),
@@ -296,14 +314,14 @@ const top10AvgChartData = computed(() => {
return { labels, datasets } return { labels, datasets }
}) })
// 后10平均对比图 // 后10%平均对比图
const bottom10AvgChartData = computed(() => { const bottom10AvgChartData = computed(() => {
if (comparisons.value.length === 0) return null if (comparisons.value.length === 0) return null
const labels = comparisons.value.map((c) => c.class_name) const labels = comparisons.value.map((c) => c.class_name)
const datasets = [ const datasets = [
{ {
label: "后10平均", label: "后10%平均",
data: comparisons.value.map((c) => c.bottom_10_avg), data: comparisons.value.map((c) => c.bottom_10_avg),
backgroundColor: comparisons.value.map((_, i) => getClassColor(i).bg), backgroundColor: comparisons.value.map((_, i) => getClassColor(i).bg),
borderColor: comparisons.value.map((_, i) => getClassColor(i).border), borderColor: comparisons.value.map((_, i) => getClassColor(i).border),
@@ -314,33 +332,15 @@ const bottom10AvgChartData = computed(() => {
return { labels, datasets } return { labels, datasets }
}) })
// 前25%平均对比图 // 中间80%均值对比图
const top25AvgChartData = computed(() => { const middle80AvgChartData = computed(() => {
if (comparisons.value.length === 0) return null if (comparisons.value.length === 0) return null
const labels = comparisons.value.map((c) => c.class_name) const labels = comparisons.value.map((c) => c.class_name)
const datasets = [ const datasets = [
{ {
label: "前25%平均", label: "中间80%均值",
data: comparisons.value.map((c) => c.top_25_avg), data: comparisons.value.map((c) => c.middle_80_avg),
backgroundColor: comparisons.value.map((_, i) => getClassColor(i).bg),
borderColor: comparisons.value.map((_, i) => getClassColor(i).border),
borderWidth: 2,
},
]
return { labels, datasets }
})
// 后25%平均对比图
const bottom25AvgChartData = computed(() => {
if (comparisons.value.length === 0) return null
const labels = comparisons.value.map((c) => c.class_name)
const datasets = [
{
label: "后25%平均",
data: comparisons.value.map((c) => c.bottom_25_avg),
backgroundColor: comparisons.value.map((_, i) => getClassColor(i).bg), backgroundColor: comparisons.value.map((_, i) => getClassColor(i).bg),
borderColor: comparisons.value.map((_, i) => getClassColor(i).border), borderColor: comparisons.value.map((_, i) => getClassColor(i).border),
borderWidth: 2, borderWidth: 2,
@@ -474,6 +474,17 @@ const chartOptions = {
}, },
} }
const compositeScoreChartOptions = {
...chartOptions,
scales: {
...chartOptions.scales,
y: {
...chartOptions.scales.y,
max: 100,
},
},
}
const radarChartOptions = { const radarChartOptions = {
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
@@ -575,6 +586,9 @@ const radarChartOptions = {
<template #header-extra> <template #header-extra>
<n-tag :type="getRankColor(index).type" size="large"> <n-tag :type="getRankColor(index).type" size="large">
#{{ getRankColor(index).text }} #{{ getRankColor(index).text }}
<span style="margin-left: 6px; font-size: 12px; opacity: 0.85">
{{ classData.composite_score }}
</span>
</n-tag> </n-tag>
</template> </template>
@@ -676,26 +690,21 @@ const radarChartOptions = {
</n-descriptions-item> </n-descriptions-item>
<!-- 分层统计 --> <!-- 分层统计 -->
<n-descriptions-item label="前10名平均"> <n-descriptions-item label="前10%均值">
<span style="color: #cf1322; font-weight: 600">{{ <span style="color: #cf1322; font-weight: 600">{{
classData.top_10_avg.toFixed(2) classData.top_10_avg.toFixed(2)
}}</span> }}</span>
</n-descriptions-item> </n-descriptions-item>
<n-descriptions-item label="后10名平均"> <n-descriptions-item label="中间80%均值">
<span style="color: #389e0d; font-weight: 600">{{
classData.middle_80_avg.toFixed(2)
}}</span>
</n-descriptions-item>
<n-descriptions-item label="后10%均值">
<span style="color: #096dd9; font-weight: 500">{{ <span style="color: #096dd9; font-weight: 500">{{
classData.bottom_10_avg.toFixed(2) classData.bottom_10_avg.toFixed(2)
}}</span> }}</span>
</n-descriptions-item> </n-descriptions-item>
<n-descriptions-item label="前25%平均">
<span style="color: #f5222d; font-weight: 600">{{
classData.top_25_avg.toFixed(2)
}}</span>
</n-descriptions-item>
<n-descriptions-item label="后25%平均">
<span style="color: #531dab; font-weight: 500">{{
classData.bottom_25_avg.toFixed(2)
}}</span>
</n-descriptions-item>
<!-- 人数 --> <!-- 人数 -->
<n-descriptions-item label="人数"> <n-descriptions-item label="人数">
@@ -793,6 +802,17 @@ const radarChartOptions = {
<!-- 可视化图表 - 专注于对比 --> <!-- 可视化图表 - 专注于对比 -->
<template v-if="comparisons.length > 0"> <template v-if="comparisons.length > 0">
<!-- 综合分对比 - 一眼看出胜负 -->
<n-card title="综合分对比满分100" style="margin-top: 20px">
<div style="height: 300px">
<Bar
v-if="compositeScoreChartData"
:data="compositeScoreChartData"
:options="compositeScoreChartOptions"
/>
</div>
</n-card>
<!-- AC核心指标对比 - 三个独立图表并排显示 --> <!-- AC核心指标对比 - 三个独立图表并排显示 -->
<n-card title="AC核心指标对比" style="margin-top: 20px"> <n-card title="AC核心指标对比" style="margin-top: 20px">
<n-grid :cols="3" :x-gap="16" :y-gap="16"> <n-grid :cols="3" :x-gap="16" :y-gap="16">
@@ -859,9 +879,9 @@ const radarChartOptions = {
</n-grid> </n-grid>
</n-card> </n-card>
<!-- 分层统计对比 - 个独立图表并排显示 --> <!-- 分层统计对比 - 个独立图表并排显示 -->
<n-card title="分层统计对比" style="margin-top: 20px"> <n-card title="分层统计对比" style="margin-top: 20px">
<n-grid :cols="2" :x-gap="16" :y-gap="16"> <n-grid :cols="3" :x-gap="16" :y-gap="16">
<n-gi> <n-gi>
<div style="height: 300px"> <div style="height: 300px">
<Bar <Bar
@@ -871,6 +891,15 @@ const radarChartOptions = {
/> />
</div> </div>
</n-gi> </n-gi>
<n-gi>
<div style="height: 300px">
<Bar
v-if="middle80AvgChartData"
:data="middle80AvgChartData"
:options="chartOptions"
/>
</div>
</n-gi>
<n-gi> <n-gi>
<div style="height: 300px"> <div style="height: 300px">
<Bar <Bar
@@ -880,24 +909,6 @@ const radarChartOptions = {
/> />
</div> </div>
</n-gi> </n-gi>
<n-gi>
<div style="height: 300px">
<Bar
v-if="top25AvgChartData"
:data="top25AvgChartData"
:options="chartOptions"
/>
</div>
</n-gi>
<n-gi>
<div style="height: 300px">
<Bar
v-if="bottom25AvgChartData"
:data="bottom25AvgChartData"
:options="chartOptions"
/>
</div>
</n-gi>
</n-grid> </n-grid>
</n-card> </n-card>
@@ -928,6 +939,17 @@ const radarChartOptions = {
render: (_, index) => getRankColor(index).text, render: (_, index) => getRankColor(index).text,
width: 80, width: 80,
}, },
{
title: '综合分',
key: 'composite_score',
width: 90,
render: (row) =>
h(
'span',
{ style: { color: '#722ed1', fontWeight: '700', fontSize: '15px' } },
row.composite_score.toFixed(1),
),
},
{ {
title: '班级', title: '班级',
key: 'class_name', key: 'class_name',
@@ -980,7 +1002,7 @@ const radarChartOptions = {
), ),
}, },
{ {
title: '前10名平均', title: '前10%均值',
key: 'top_10_avg', key: 'top_10_avg',
width: 100, width: 100,
render: (row) => render: (row) =>
@@ -991,7 +1013,18 @@ const radarChartOptions = {
), ),
}, },
{ {
title: '后10名平均', title: '中间80%均值',
key: 'middle_80_avg',
width: 110,
render: (row) =>
h(
'span',
{ style: { color: '#389e0d', fontWeight: '600' } },
row.middle_80_avg.toFixed(2),
),
},
{
title: '后10%均值',
key: 'bottom_10_avg', key: 'bottom_10_avg',
width: 100, width: 100,
render: (row) => render: (row) =>