feat(统计): 数据统计从弹框改成独立页面,左边题目、右边学生,一道题看全班每人的每一次

- 课堂看板管这节课,统计管回头看:时间段默认「两节课」,还有这节课、今天、7 天、30 天、这学期、全部、自己选日子;不再自动刷新
- 选一道题看每人在这道题上的方块串,鼠标停上去看那次的代码;选「全部」每人一行、展开按题看
- 没选班级(从题目页进来)先按班级汇总;流程图统计有等级分布、四项雷达、各项平均分、词云和每人最好的一次
- 提交列表、题目页的「数据统计」改成新标签打开这个页面
- 「请假隐藏」挪到课堂看板:点名字旁的 × 藏两个小时,做完人数的分母一起减掉

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-29 18:28:27 +08:00
1 parent f09da88ff7
commit ed55ee5af6
16 files changed
+2256 -76

No files matched your search

@@ -0,0 +1,77 @@
<script setup lang="ts">
import { ArcElement, Chart as ChartJS, Tooltip } from "chart.js"
import { Doughnut } from "vue-chartjs"
import { useChartTheme } from "shared/composables/chartTheme"
ChartJS.register(ArcElement, Tooltip)
/**
* 数字行右边的小圆环(提交正确率、班级完成度)。中间写百分比,
* 鼠标停上去看两段各是多少 —— 这是 chart.js 现成的提示。
*/
const props = defineProps<{
value: number
total: number
color: string
/** 两段的名字,提示里用:["正确", "没对"] */
labels: [string, string]
size?: number
}>()
const { chartKey, gridColor } = useChartTheme()
const pct = computed(() => (props.total > 0 ? Math.round((props.value / props.total) * 100) : 0))
const data = computed(() => ({
labels: props.labels,
datasets: [
{
data: [props.value, Math.max(0, props.total - props.value)],
backgroundColor: [props.color, gridColor.value],
borderWidth: 0,
},
],
}))
const options = {
responsive: true,
maintainAspectRatio: false,
cutout: "72%",
animation: false as const,
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: (context: { label: string; parsed: number }) =>
`${context.label}:${context.parsed}`,
},
},
},
}
</script>
<template>
<span class="ring" :style="{ width: `${size ?? 48}px`, height: `${size ?? 48}px` }">
<Doughnut :key="chartKey" :data="data" :options="options" />
<span class="center">{{ total > 0 ? `${pct}%` : "—" }}</span>
</span>
</template>
<style scoped>
.ring {
position: relative;
display: inline-block;
flex: none;
}
.center {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 700;
pointer-events: none;
}
</style>