refactor(课堂任务): 「课堂看板」改名「课堂任务」,地址换成 /task;班级 PK 换成 /pk

- 界面上的「课堂看板」「课堂作业」统一叫「课堂任务」,顶栏、老师首页、后台菜单一起改
- 前端 oj/classroom → oj/task、oj/class → oj/pk,两个名字不再只差几个字母却毫不相干;旧地址不留跳转
- 后端 routes/classroom.ts 拆开:课堂任务的接口进 routes/task.ts(/task/board|lesson|today),
  首页的「上次来」「班里在做」进 routes/home.ts;共用的取题口径挪进 services/lesson.ts
- 契约 classroom.ts 同样拆成 task.ts / home.ts,ClassBoard* 改叫 TaskBoard*

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-11-02 22:53:07 +08:00
1 parent 46d3fe2dbe
commit 29c31640b5
82 files changed
+798 -766

No files matched your search

+63
View File
@@ -0,0 +1,63 @@
<script setup lang="ts">
import { useThemeVars } from 'naive-ui'
/**
* 全班分布:横轴做对几道(0–30,再多的堆在最右一档),一根柱一档,虚线是中间那位同学。
* 人少的档(不到 3 个)淡一点,一眼看到大部分人停在哪。
*/
const props = withDefaults(defineProps<{ values: number[]; median: number; color: string; height?: number }>(), { height: 44 })
const theme = useThemeVars()
const CAP = 30
const WIDTH = 560
const bars = computed(() => {
const counts = new Map<number, number>()
for (const value of props.values) {
const key = Math.min(value, CAP)
counts.set(key, (counts.get(key) ?? 0) + 1)
}
const tallest = Math.max(16, ...counts.values())
const width = WIDTH / (CAP + 1)
return {
width,
list: [...counts].map(([value, n]) => {
const h = Math.max(2, (props.height * n) / tallest)
return { value, n, x: value * width + 0.5, y: props.height - h, h }
}),
median: (Math.min(props.median, CAP) + 0.5) * width,
}
})
</script>
<template>
<svg class="hist" :viewBox="`0 0 ${WIDTH} ${height + 12}`" role="img" aria-label="全班做对几道的分布">
<line :x1="0" :x2="WIDTH" :y1="height + 0.5" :y2="height + 0.5" :stroke="theme.dividerColor" />
<rect
v-for="bar in bars.list"
:key="bar.value"
:x="bar.x"
:y="bar.y"
:width="bars.width - 1"
:height="bar.h"
rx="1"
:fill="color"
:opacity="bar.n >= 3 ? 0.9 : 0.55"
>
<title>做对 {{ bar.value }}{{ bar.value === CAP ? ' 道以上' : ' 道' }}:{{ bar.n }} 人</title>
</rect>
<line :x1="bars.median" :x2="bars.median" y1="0" :y2="height" :stroke="theme.textColor2" stroke-dasharray="2 2" />
<text x="0" :y="height + 11" font-size="9" :fill="theme.textColor3">0</text>
<text :x="15 * bars.width" :y="height + 11" font-size="9" :fill="theme.textColor3" text-anchor="middle">15</text>
<text :x="WIDTH" :y="height + 11" font-size="9" :fill="theme.textColor3" text-anchor="end">30+</text>
</svg>
</template>
<style scoped>
.hist {
display: block;
width: 100%;
height: auto;
}
</style>