feat(统计面板): 展开一个学生后按题目分组,每道题画一条「状态轨迹」
Some checks failed
Deploy / deploy (push) Has been cancelled

老师展开一个学生,原来看到的是一排 12 位十六进制的提交编号按钮。编号本身没有信息量,
而一节课里学生常在好几道题之间来回跳,那一排看不出他到底卡在哪一道。

现在一道题一行:题号、标题、交了几次、过没过,后面跟一排按时间**从早到晚**的小方块,
颜色就是判题状态(绿=通过 红=答错 黄=编译失败/超时 灰=判题中)。
「八绿一红一绿」和「五黄到底」一眼分得开。

- **排序按老师的用法来**:没过的排前面,其中交得越多越靠前 —— 卡得最久的那道顶到眼前;
  已通过的沉底,它们只是「做完了」。
- 鼠标悬停出「状态 · 时间 · 提交号」,点击照旧打开提交详情。
- 「语法未过」(ast_check_failed) 算做出来了,和表格上「已解决」那一列口径一致。
- 方块用内联样式而不是 class:它们是 h() 出来、挂在 NDataTable 展开槽里渲染的,
  <style scoped> 能不能盖到并不确定。色值沿用 Naive 的语义色,和 ExerciseMatch.vue 一致。

为此 GET /submissions/statistics/items 多带三个字段(problem / problemTitle /
createTime)—— 原来只有 id 和 result,分组和悬停都无从谈起。innerJoin problem 不会漏行:
submission.problem_id 是 NOT NULL 且外键 NO ACTION,题目删不掉。

## 验证

起全栈在浏览器里真点过(提交列表 → 数据统计 → 提交记录 → 展开 student):

- 五道题各成一行,顺序是 1005(5次未过) → 1006(4次) → 1020(1次) → 1018(1次) →
  1004(10次已通过),符合「没过的在前、交得多的在前、已过的沉底」;
- 现造一条「先错后对」:轨迹读出来是 绿绿绿绿绿绿绿绿红绿,新交的 WA→AC 落在末尾,
  确认是从早到晚而不是倒序;
- 悬停取到「编译失败 · 09-02 22:03:14 · 9f02da4c5f01」。

tsc、check:routes、vue-tsc、vite build、单二进制编译均通过。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012j1vgeDqay8wKCh8dPgPcH
This commit is contained in:
2026-09-10 20:09:09 -06:00
parent f581029eb2
commit 8eae4bea7b
3 changed files with 145 additions and 20 deletions

View File

@@ -273,7 +273,22 @@ export const submissionStatisticsUserSchema = z.object({
* 免得老师以为这人就交了这么多。
*/
export const submissionStatisticsItemsSchema = z.object({
items: z.array(z.object({ id: z.string(), result: judgeStatusSchema })),
/**
* 展开某个学生时列出他这段时间的提交。**带上题目**:一节课里学生往往在好几道题
* 之间来回跳,一串只有编号的按钮看不出他卡在哪一道 —— 前端按题目分组展示。
*
* 字段名沿用 submissionListItemSchema 的口径:`problem` 是展示用题号problem._id
* `problemTitle` 是标题。
*/
items: z.array(
z.object({
id: z.string(),
result: judgeStatusSchema,
createTime: z.string(),
problem: z.string(),
problemTitle: z.string(),
}),
),
truncated: z.boolean(),
})