This commit is contained in:
2025-10-07 17:03:59 +08:00
parent 6f345611eb
commit 437da9d588
9 changed files with 859 additions and 864 deletions

View File

@@ -142,4 +142,3 @@ const options = {
},
}
</script>

View File

@@ -1,9 +1,7 @@
<template>
<n-card :title="title" size="small">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">
全面评估学习情况
</n-text>
<n-text depth="3" style="font-size: 12px"> 全面评估学习情况 </n-text>
</template>
<div class="chart">
<Chart type="bar" :data="data" :options="options" />
@@ -173,11 +171,20 @@ const options = computed<ChartOptions<"bar" | "line">>(() => {
return `${dsLabel}: ${ctx.formattedValue}`
},
footer: (items: TooltipItem<"bar">[]) => {
const barItems = items.filter(item => (item.dataset as any).yAxisID === "y")
const barItems = items.filter(
(item) => (item.dataset as any).yAxisID === "y",
)
if (barItems.length >= 2) {
const problemCount = barItems.find(item => item.dataset.label === "完成题目数")?.parsed.y || 0
const submissionCount = barItems.find(item => item.dataset.label === "总提交次数")?.parsed.y || 0
const efficiency = submissionCount > 0 ? ((problemCount / submissionCount) * 100).toFixed(1) : "0"
const problemCount =
barItems.find((item) => item.dataset.label === "完成题目数")
?.parsed.y || 0
const submissionCount =
barItems.find((item) => item.dataset.label === "总提交次数")
?.parsed.y || 0
const efficiency =
submissionCount > 0
? ((problemCount / submissionCount) * 100).toFixed(1)
: "0"
return `AC率: ${efficiency}%`
}
return ""

View File

@@ -1,9 +1,7 @@
<template>
<n-card :title="title" size="small" v-if="show">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">
反映刷题质量提升
</n-text>
<n-text depth="3" style="font-size: 12px">反映刷题质量提升</n-text>
</template>
<div class="chart">
<Chart type="line" :data="data" :options="options" />
@@ -69,7 +67,8 @@ const efficiencyData = computed(() => {
const efficiency = problemCount > 0 ? submissionCount / problemCount : 0
// 计算一次AC率百分比
const onePassRate = problemCount > 0 ? (problemCount / submissionCount) * 100 : 0
const onePassRate =
problemCount > 0 ? (problemCount / submissionCount) * 100 : 0
return {
label: [
@@ -233,4 +232,3 @@ const options = computed<ChartOptions<"line">>(() => {
width: 100%;
}
</style>

View File

@@ -1,9 +1,7 @@
<template>
<n-card title="过去一年的提交热力图" size="small">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">
激励持续学习
</n-text>
<n-text depth="3" style="font-size: 12px">激励持续学习</n-text>
</template>
<n-spin :show="aiStore.loading.heatmap">
<div class="heatmap-container" ref="containerRef">

View File

@@ -1,9 +1,7 @@
<template>
<n-card :title="title" size="small" v-if="show">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">
追踪学习成长轨迹
</n-text>
<n-text depth="3" style="font-size: 12px">追踪学习成长轨迹</n-text>
</template>
<div class="chart">
<Chart type="line" :data="data" :options="options" />
@@ -87,7 +85,8 @@ const progressData = computed(() => {
totalProblems += problemCount
// 计算累计平均等级
const avgGradeValue = totalProblems > 0 ? totalWeightedGrade / totalProblems : 0
const avgGradeValue =
totalProblems > 0 ? totalWeightedGrade / totalProblems : 0
return {
label: [

View File

@@ -1,9 +1,7 @@
<template>
<n-card title="解题排名分布" size="small" v-if="show">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">
了解解题速度和竞争力
</n-text>
<n-text depth="3" style="font-size: 12px">了解解题速度和竞争力</n-text>
</template>
<div style="height: 300px">
<Pie :data="data" :options="options" />
@@ -13,13 +11,7 @@
<script setup lang="ts">
import { Pie } from "vue-chartjs"
import {
Chart as ChartJS,
ArcElement,
Title,
Tooltip,
Legend,
} from "chart.js"
import { Chart as ChartJS, ArcElement, Title, Tooltip, Legend } from "chart.js"
import { useAIStore } from "oj/store/ai"
ChartJS.register(ArcElement, Title, Tooltip, Legend)
@@ -111,8 +103,12 @@ const options = {
callbacks: {
label: (context: any) => {
const count = context.parsed
const total = rankDistribution.value.reduce((sum, r) => sum + r.count, 0)
const percentage = total > 0 ? ((count / total) * 100).toFixed(1) : "0.0"
const total = rankDistribution.value.reduce(
(sum, r) => sum + r.count,
0,
)
const percentage =
total > 0 ? ((count / total) * 100).toFixed(1) : "0.0"
const label = context.label || ""
return `${label}: ${count} 道题 (${percentage}%)`
},
@@ -122,7 +118,10 @@ const options = {
if (problems.length > 0 && problems.length <= 5) {
return problems
} else if (problems.length > 5) {
return [...problems.slice(0, 3), `... 还有 ${problems.length - 3} 道题`]
return [
...problems.slice(0, 3),
`... 还有 ${problems.length - 3} 道题`,
]
}
return ""
},
@@ -131,4 +130,3 @@ const options = {
},
}
</script>

View File

@@ -1,9 +1,7 @@
<template>
<n-card title="连续做题统计" size="small">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">
激励持续学习
</n-text>
<n-text depth="3" style="font-size: 12px">激励持续学习</n-text>
</template>
<n-spin :show="aiStore.loading.heatmap">
<n-grid :cols="2" :x-gap="12" :y-gap="12">
@@ -11,7 +9,10 @@
<n-statistic label="当前连续" :value="currentStreak">
<template #suffix>
<span style="font-size: 14px"></span>
<span v-if="currentStreak > 0" style="font-size: 20px; margin-left: 4px">
<span
v-if="currentStreak > 0"
style="font-size: 20px; margin-left: 4px"
>
🔥
</span>
</template>
@@ -21,7 +22,10 @@
<n-statistic label="最长连续" :value="maxStreak">
<template #suffix>
<span style="font-size: 14px"></span>
<span v-if="maxStreak >= 7" style="font-size: 20px; margin-left: 4px">
<span
v-if="maxStreak >= 7"
style="font-size: 20px; margin-left: 4px"
>
</span>
</template>
@@ -45,12 +49,8 @@
<n-divider style="margin: 12px 0" />
<n-flex vertical size="small">
<n-text depth="2" style="font-size: 12px">
<span v-if="currentStreak === 0">
开始做题建立学习连续记录
</span>
<span v-else-if="currentStreak < 3">
继续保持争取连续3天
</span>
<span v-if="currentStreak === 0"> 开始做题建立学习连续记录 </span>
<span v-else-if="currentStreak < 3"> 继续保持争取连续3天 </span>
<span v-else-if="currentStreak < 7">
很棒继续保持一周连续记录
</span>
@@ -175,4 +175,3 @@ const maxStreak = computed(() => streakData.value.maxStreak)
const weekCount = computed(() => streakData.value.weekCount)
const monthCount = computed(() => streakData.value.monthCount)
</script>

View File

@@ -1,9 +1,7 @@
<template>
<n-card :title="title" size="small" v-if="show">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">
可视化知识点覆盖面
</n-text>
<n-text depth="3" style="font-size: 12px">可视化知识点覆盖面</n-text>
</template>
<div class="chart">
<Radar :data="data" :options="options" />

View File

@@ -1,9 +1,7 @@
<template>
<n-card title="时间活跃度分析" size="small" v-if="show">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">
发现最佳学习时段
</n-text>
<n-text depth="3" style="font-size: 12px">发现最佳学习时段</n-text>
</template>
<div style="height: 300px">
<Bar :data="data" :options="options" />
@@ -75,7 +73,9 @@ const data = computed(() => {
const datasets = TIME_PERIODS.map((period, periodIndex) => {
return {
label: period.label,
data: WEEKDAYS.map((_, weekday) => activityMatrix.value[weekday][periodIndex]),
data: WEEKDAYS.map(
(_, weekday) => activityMatrix.value[weekday][periodIndex],
),
backgroundColor: getTimePeriodColor(periodIndex),
borderColor: getTimePeriodColor(periodIndex),
borderWidth: 1,
@@ -150,4 +150,3 @@ const options = {
},
}
</script>