fix(提交列表/统计): 按方向键会顺手切掉「代码/流程图」、重判后右栏还是旧内容、没人交时统计说「全都做完了」

提交列表:
- 焦点在单选框、下拉框上时方向键不再一边切控件一边换条;跨页没回来前不再连跳一整页
- 重判、重新评分之后右栏重新拉,不再命中旧缓存;同一条新旧请求不再互相覆盖
- 从「我的提交」点顶栏「提交」、浏览器后退时,地址里没有的筛选回到默认
- 找回点用户名开个人主页;「只看他 / 只看这题」学生也能用;重判不用等代码拉下来
- 手机上老师也能重判、能按语言筛,流程图行有手机排版;学生界面带着的班级筛选显示成能点掉的标签
- mermaid 画不出来时先说中文、原始报错折起来

统计页:
- 没人交时说「这段时间没有人交」,不再说「全都做完了」;没填题号时各处统一叫「做对过题」
- 班级按 class_name 单独传;查询失败清掉旧数据并说清原因;标签跟着查出来的那一份走
- 「语法未过」的格子单独标「语」;正确率不算还在判的;截断提示说清哪些数字受影响
- 看板的请假隐藏只藏还没交过、没在举手的人,「恢复」只恢复这个班的
- 手机上筛选折行、上下排,不再挤出屏幕

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-29 18:52:58 +08:00
1 parent 26c8d65e89
commit 0bcc981e6f
15 files changed
+613 -197

No files matched your search

@@ -1,21 +1,19 @@
import { onMounted, ref } from "vue"
/**
* 统计面板的「暂时隐藏某个学生」。
* 课堂看板的「请假隐藏」:暂时把某个学生从「今天还没交过」里藏起来。
*
* 老师是把面板投在屏幕上盯着看的,未完成名单里总有那么几个是请假/转班/学号错了的,
* 老师是把看板投在屏幕上盯着看的,没交名单里总有那么几个是请假/转班/学号错了的,
* 一直挂在上面会盖住真正需要盯的人。隐藏是**带过期时间**的(两小时,够一节课),
* 不是永久删除 —— 下节课自动回来,免得有人被无声地漏掉。
*
* 存在 localStorage 而不是后端:这是「这台电脑上这位老师这节课不想看谁」,
* 换个人、换台机器都不该继承。
*
* 原来这套(loadHidden / saveHidden / hideStudent / showAll / 过期清理)在
* StatisticsPanel.vue 和 FlowchartStatisticsPanel.vue 里各写了一遍,除了存储键
* 和一个参数名逐字相同;判断「有没有被隐藏」两边还各自内联了三处。
* 原来在提交统计、流程图统计两个弹框里;统计改成回头看的页面之后(2026-09),
* 课上盯人的事归看板,这个也挪了过来。
*
* @param storageKey localStorage 的键。**两个面板各用各的** —— 提交统计里隐掉的人
* 不该连带在流程图统计里也消失,那是两件事。
* @param storageKey localStorage 的键
*/
export function useHiddenStudents(storageKey: string) {
/** 隐藏时长:两小时,一节课的量级 */
@@ -30,7 +28,7 @@ export function useHiddenStudents(storageKey: string) {
}
const hiddenStudents = ref<Record<string, number>>(load())
/** 面板上的「隐藏模式」开关:打开后每行才出现那个隐藏按钮 */
/** 「请假隐藏」开关:打开后名字旁边才出现那个 × */
const hideMode = ref(false)
function save(data: Record<string, number>) {
@@ -45,9 +43,13 @@ export function useHiddenStudents(storageKey: string) {
save(hiddenStudents.value)
}
function showAll() {
hiddenStudents.value = {}
save({})
/** 恢复这几个人。只恢复看板上这个班的,别把别的班藏着的人一起放出来 */
function unhide(usernames: string[]) {
const drop = new Set(usernames)
hiddenStudents.value = Object.fromEntries(
Object.entries(hiddenStudents.value).filter(([name]) => !drop.has(name)),
)
save(hiddenStudents.value)
}
function isHidden(username: string) {
@@ -55,11 +57,6 @@ export function useHiddenStudents(storageKey: string) {
return !!expiresAt && expiresAt > Date.now()
}
/** 给 filter 用:`list.filter(notHidden)` */
function notHidden(item: { username: string }) {
return !isHidden(item.username)
}
onMounted(() => {
// 把已经到期的清掉再落一次盘,否则这张表只增不减
const now = Date.now()
@@ -70,7 +67,7 @@ export function useHiddenStudents(storageKey: string) {
save(cleaned)
})
// 不导出 hiddenStudents 本身:两个面板要的都是「这个人该不该显示」,
// 把那张表递出去只会让判断逻辑又散回各自的组件里
return { hideMode, hideStudent, showAll, isHidden, notHidden }
// 不导出 hiddenStudents 本身:用的地方要的是「这个人该不该显示」,
// 把那张表递出去只会让判断逻辑又散回组件里
return { hideMode, hideStudent, unhide, isHidden }
}