原来要先打开开关、名字变成带 × 的标签再点 ×,× 像删除;标完人从页面上消失, 恢复只能全部恢复。现在: - 班级下拉旁「请假」按钮,标了人之后按钮直接写「请假 2 人:陈言、黄依露 · 修改」 - 弹框只列今天还没交过的人(请假的人一定在里面,刚上课时就是全班),勾上点确定 - 请假的人在表格里变灰排到最后,不算进进度和「还没交过」 - 交了题或举手就算来了,自动取消标记 - 有效期从两小时改成今天(请假一般是一整天),仍然只记在这台电脑上 顺带修掉:被标过的学生后来交了题,会从表格里整个消失、也不算进进度。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
af2ca31de5
commit
1ba8928d41
4 files changed
+331
-198
No files matched your search
@@ -1,73 +0,0 @@
|
||||
import { onMounted, ref } from "vue"
|
||||
|
||||
/**
|
||||
* 课堂看板的「请假隐藏」:暂时把某个学生从「今天还没交过」里藏起来。
|
||||
*
|
||||
* 老师是把看板投在屏幕上盯着看的,没交名单里总有那么几个是请假/转班/学号错了的,
|
||||
* 一直挂在上面会盖住真正需要盯的人。隐藏是**带过期时间**的(两小时,够一节课),
|
||||
* 不是永久删除 —— 下节课自动回来,免得有人被无声地漏掉。
|
||||
*
|
||||
* 存在 localStorage 而不是后端:这是「这台电脑上这位老师这节课不想看谁」,
|
||||
* 换个人、换台机器都不该继承。
|
||||
*
|
||||
* 原来在提交统计、流程图统计两个弹框里;统计改成回头看的页面之后(2026-09),
|
||||
* 课上盯人的事归看板,这个也挪了过来。
|
||||
*
|
||||
* @param storageKey localStorage 的键
|
||||
*/
|
||||
export function useHiddenStudents(storageKey: string) {
|
||||
/** 隐藏时长:两小时,一节课的量级 */
|
||||
const HIDE_DURATION = 2 * 60 * 60 * 1000
|
||||
|
||||
function load(): Record<string, number> {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(storageKey) ?? "{}")
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const hiddenStudents = ref<Record<string, number>>(load())
|
||||
/** 「请假隐藏」开关:打开后名字旁边才出现那个 × */
|
||||
const hideMode = ref(false)
|
||||
|
||||
function save(data: Record<string, number>) {
|
||||
localStorage.setItem(storageKey, JSON.stringify(data))
|
||||
}
|
||||
|
||||
function hideStudent(username: string) {
|
||||
hiddenStudents.value = {
|
||||
...hiddenStudents.value,
|
||||
[username]: Date.now() + HIDE_DURATION,
|
||||
}
|
||||
save(hiddenStudents.value)
|
||||
}
|
||||
|
||||
/** 恢复这几个人。只恢复看板上这个班的,别把别的班藏着的人一起放出来 */
|
||||
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) {
|
||||
const expiresAt = hiddenStudents.value[username]
|
||||
return !!expiresAt && expiresAt > Date.now()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 把已经到期的清掉再落一次盘,否则这张表只增不减
|
||||
const now = Date.now()
|
||||
const cleaned = Object.fromEntries(
|
||||
Object.entries(hiddenStudents.value).filter(([, expiresAt]) => expiresAt > now),
|
||||
)
|
||||
hiddenStudents.value = cleaned
|
||||
save(cleaned)
|
||||
})
|
||||
|
||||
// 不导出 hiddenStudents 本身:用的地方要的是「这个人该不该显示」,
|
||||
// 把那张表递出去只会让判断逻辑又散回组件里
|
||||
return { hideMode, hideStudent, unhide, isHidden }
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import { onMounted, ref } from "vue"
|
||||
import { parseTime } from "utils/functions"
|
||||
|
||||
/**
|
||||
* 课堂看板的「请假」:老师手动标记今天不在的学生,看板的人数、进度、「还没交过」都不算他们。
|
||||
*
|
||||
* **今天有效**(按东八区的日子),第二天自动清掉 —— 请假一般是请一整天,原来按两小时算,
|
||||
* 连上两节课的第二节中途人又冒出来,得重标一遍。
|
||||
*
|
||||
* 存在 localStorage 而不是后端:这是「这台电脑上这位老师今天不想算谁」,
|
||||
* 换个人、换台机器都不该继承(2026-10 用户定的,不上服务器)。
|
||||
*
|
||||
* 原来叫「请假隐藏」(开关 → 名字变成带 × 的标签 → 点 ×),用户说不会用,2026-10 改成
|
||||
* 看板上一个「请假」按钮勾名单。旧键 `oj_hidden_students` 存的是两小时的过期时刻,
|
||||
* 口径不同,不迁移,直接删掉。
|
||||
*
|
||||
* @param storageKey localStorage 的键
|
||||
*/
|
||||
export function useLeaveStudents(storageKey: string) {
|
||||
/** 用户名 → 标记的那一天(YYYY-MM-DD,东八区) */
|
||||
type Marks = Record<string, string>
|
||||
|
||||
function today() {
|
||||
return parseTime(new Date(), "YYYY-MM-DD")
|
||||
}
|
||||
|
||||
function load(): Marks {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(storageKey) ?? "{}")
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
function save(data: Marks) {
|
||||
try {
|
||||
localStorage.setItem(storageKey, JSON.stringify(data))
|
||||
} catch {
|
||||
// 存不下就只在这次打开里有效,不影响看板
|
||||
}
|
||||
}
|
||||
|
||||
const marks = ref<Marks>(load())
|
||||
|
||||
function onLeave(username: string) {
|
||||
return marks.value[username] === today()
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 `usernames` 这批人的标记换成 `selected`:在里面但没选的取消,选了的标上。
|
||||
* 只动这一批(看板上这个班),别的班标着的人不受影响
|
||||
*/
|
||||
function setLeave(usernames: string[], selected: string[]) {
|
||||
const scope = new Set(usernames)
|
||||
const day = today()
|
||||
const next = Object.fromEntries(
|
||||
Object.entries(marks.value).filter(([name]) => !scope.has(name)),
|
||||
)
|
||||
for (const name of selected) next[name] = day
|
||||
marks.value = next
|
||||
save(next)
|
||||
}
|
||||
|
||||
function cancelLeave(usernames: string[]) {
|
||||
setLeave(usernames, [])
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
try {
|
||||
localStorage.removeItem("oj_hidden_students")
|
||||
} catch {
|
||||
// 读写不了 localStorage 的环境,没什么可清的
|
||||
}
|
||||
// 把不是今天的清掉再落一次盘,否则这张表只增不减
|
||||
const day = today()
|
||||
const cleaned = Object.fromEntries(
|
||||
Object.entries(marks.value).filter(([, marked]) => marked === day),
|
||||
)
|
||||
marks.value = cleaned
|
||||
save(cleaned)
|
||||
})
|
||||
|
||||
return { onLeave, setLeave, cancelLeave }
|
||||
}
|
||||
Reference in new issue
Block a user