Compare commits

..

3 Commits

Author SHA1 Message Date
8a6858d6de feat: show yearly AC rate chart on problem info page
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
2026-05-11 00:22:47 -06:00
ead9af2217 feat: add ProblemYearlyChart component 2026-05-11 00:21:51 -06:00
fa8afa84e5 feat: add getProblemYearlyAC API function 2026-05-11 00:20:51 -06:00
3 changed files with 111 additions and 1 deletions

View File

@@ -313,6 +313,19 @@ export function getSimilarProblems(problemId: string) {
return http.get("problem/similar", { params: { problem_id: problemId } })
}
export interface YearlyACData {
year: number
total: number
accepted: number
ac_rate: number
}
export function getProblemYearlyAC(problemId: string) {
return http.get<YearlyACData[]>("problem/yearly_ac", {
params: { problem_id: problemId },
})
}
// ==================== 流程图相关API ====================
export function submitFlowchart(data: {

View File

@@ -14,6 +14,8 @@ import {
Colors,
} from "chart.js"
import { getProblemBeatRate } from "oj/api"
import { getProblemYearlyAC, type YearlyACData } from "oj/api"
import ProblemYearlyChart from "./ProblemYearlyChart.vue"
import { useBreakpoints } from "shared/composables/breakpoints"
// 仅注册饼图所需的 Chart.js 组件
@@ -25,6 +27,7 @@ const { problem } = storeToRefs(problemStore)
const { isDesktop } = useBreakpoints()
const beatRate = ref("0")
const yearlyACData = ref<YearlyACData[]>([])
const data = computed(() => {
const status = problem.value!.statistic_info
@@ -90,7 +93,15 @@ async function getBeatRate() {
beatRate.value = res.data
}
onMounted(getBeatRate)
async function getYearlyAC() {
const res = await getProblemYearlyAC(problem.value!._id)
yearlyACData.value = res.data
}
onMounted(() => {
getBeatRate()
getYearlyAC()
})
</script>
<template>
@@ -142,6 +153,7 @@ onMounted(getBeatRate)
<div class="pie" v-if="problem && problem.submission_number > 0">
<Pie :data="data" :options="options" />
</div>
<ProblemYearlyChart :data="yearlyACData" />
</template>
<style scoped>
.cards {

View File

@@ -0,0 +1,85 @@
<template>
<div class="yearly-chart" v-if="props.data.length > 1">
<Line :data="chartData" :options="chartOptions" />
</div>
</template>
<script setup lang="ts">
import { Line } from "vue-chartjs"
import {
Chart as ChartJS,
CategoryScale,
Filler,
LinearScale,
LineElement,
PointElement,
Title,
Tooltip,
} from "chart.js"
import type { YearlyACData } from "oj/api"
ChartJS.register(
CategoryScale,
Filler,
LinearScale,
LineElement,
PointElement,
Title,
Tooltip,
)
const props = defineProps<{ data: YearlyACData[] }>()
const chartData = computed(() => ({
labels: props.data.map((d) => String(d.year)),
datasets: [
{
label: "AC 率",
data: props.data.map((d) => d.ac_rate),
fill: true,
tension: 0.3,
backgroundColor: "rgba(99, 179, 237, 0.2)",
borderColor: "rgba(99, 179, 237, 1)",
pointBackgroundColor: "rgba(99, 179, 237, 1)",
},
],
}))
const chartOptions = computed(() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: "历年 AC 率",
font: { size: 20 },
},
tooltip: {
callbacks: {
label: (context: any) => {
const d = props.data[context.dataIndex]
return [`AC 率: ${d.ac_rate}%`, `通过: ${d.accepted} / ${d.total}`]
},
},
},
},
scales: {
y: {
min: 0,
max: 100,
ticks: {
callback: (value: any) => `${value}%`,
},
},
},
}))
</script>
<style scoped>
.yearly-chart {
width: 100%;
max-width: 500px;
height: 250px;
margin: 24px auto;
}
</style>