feat(题目): 左栏顶上标出「这节课做完几道」、题单进度或比赛倒计时,统计 / 点评 / 我的提交收进抽屉
- 上下文条(按入口三选一):课堂 = 「这节课 做完 3/5」+ 每道题号 + 上一题 / 下一题; 题单 = 题单名 + 进度 +「第 N 题」+ 上一题 / 下一题;比赛 = 比赛名 + 每道题号 +「还剩 38:12」+ 排名。 题号只放号码,状态用 ✓ / ! 字符区分,不只靠颜色;多于 5 道横向滚动,当前题保持可见。 通过之后当场 +1,不用刷新 - 左栏只剩「题目 / 结果」两个页签;统计 / 点评 / 我的提交变成页签行右侧的按钮,点开是抽屉, 只盖题面、编辑器不动,交完判出结果时自动收起。手机上收进页签行的「⋯」 - 流程图并进「题目」页签:参考流程图默认折叠;自己画到 A/S 的那张缩略显示、可放大, 一进题就能看到(原来要先切到「流程图」这门「语言」),也不再强行切页签 - 「本题已经被你解决啦」两条整宽提示改成标题下的「✓ 已解决 / ! 尝试过」标签; 时间内存限制只写「限时 1 秒」 - 入口差异收成一张表(problemPageContext):题单里不给抽屉、状态标签、相似题; 比赛里没有点评和求助。工具栏的「本题提交」挪到「我的提交」抽屉底部 - 顺手修:没有例子的题点「去自测猫」会抛错;「我的提交」没交过时一片空白 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
45505e8384
commit
116b0a2c95
16 files changed
+1057
-251
No files matched your search
@@ -2,7 +2,7 @@ import { errorCode } from "utils/api"
|
||||
import { formatISO, getTime, parseISO } from "date-fns"
|
||||
import { useUserStore } from "shared/store/user"
|
||||
import { ContestStatus, ContestType } from "utils/constants"
|
||||
import { duration } from "utils/functions"
|
||||
import { duration, secondsToDuration } from "utils/functions"
|
||||
import type { Contest, ProblemRow } from "utils/types"
|
||||
import { checkContestPassword, getContest, getContestAccess, getContestProblems } from "../api"
|
||||
|
||||
@@ -40,6 +40,14 @@ export const useContestStore = defineStore("contest", () => {
|
||||
}
|
||||
})
|
||||
|
||||
/** 进行中还剩多久,「38:12」「1:05:00」这种;不在进行中为空串。题目页上下文条用 */
|
||||
const remaining = computed(() => {
|
||||
if (contestStatus.value !== ContestStatus.underway) return ""
|
||||
const end = getTime(parseISO(contest.value!.endTime.toString()))
|
||||
const text = secondsToDuration(Math.max(0, Math.floor((end - now.value) / 1000)))
|
||||
return text.startsWith("0:") ? text.slice(2) : text
|
||||
})
|
||||
|
||||
const isContestAdmin = computed(
|
||||
() =>
|
||||
userStore.isSuperAdmin ||
|
||||
@@ -112,6 +120,7 @@ export const useContestStore = defineStore("contest", () => {
|
||||
problems,
|
||||
isPrivate,
|
||||
countdown,
|
||||
remaining,
|
||||
init,
|
||||
clear,
|
||||
checkPassword,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { defineStore } from "pinia"
|
||||
import { getClassActivity } from "oj/api"
|
||||
import type { ClassActivity, ClassActivityProblem } from "utils/types"
|
||||
|
||||
/** 同一节课里来回换题不用每次都重拉;过了这么久再进题目页就重拉一次 */
|
||||
const STALE_MS = 2 * 60 * 1000
|
||||
|
||||
/**
|
||||
* 「这节课的题」:首页「班里在做」那几道(老师在课堂看板布置的,或者从同班提交推断的)。
|
||||
*
|
||||
* 题目页有两处要用:顶部的课堂条(做完几道、每道的状态),和通过之后的「下一题」。
|
||||
* 原来「下一题」每次通过都自己拉一遍,现在两处读同一份,通过之后由判题那边
|
||||
* (SubmissionEffects)先把这道就地标成做完、再重拉一次。
|
||||
*/
|
||||
export const useLessonStore = defineStore("lesson", () => {
|
||||
const activity = ref<ClassActivity | null>(null)
|
||||
let loadedAt = 0
|
||||
let loading: Promise<void> | null = null
|
||||
|
||||
/** 没拉过或者旧了才拉;force 用在刚通过一道题之后 */
|
||||
function load(force = false) {
|
||||
if (!force && activity.value && Date.now() - loadedAt < STALE_MS) return loading
|
||||
loading = getClassActivity()
|
||||
.then((res) => {
|
||||
activity.value = res
|
||||
loadedAt = Date.now()
|
||||
})
|
||||
// 拉不到就没有课堂条,不影响做题
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
loading = null
|
||||
})
|
||||
return loading
|
||||
}
|
||||
|
||||
/** 刚通过:不等重拉,先把这道标成做完,课堂条上的「做完 N/M」马上加一 */
|
||||
function markAccepted(problemDisplayId: string) {
|
||||
const item = find(problemDisplayId)
|
||||
if (item) item.myStatus = "accepted"
|
||||
}
|
||||
|
||||
function find(problemDisplayId: string): ClassActivityProblem | undefined {
|
||||
const id = problemDisplayId.toLowerCase()
|
||||
return activity.value?.problems.find((item) => item.problemDisplayId.toLowerCase() === id)
|
||||
}
|
||||
|
||||
/** 这道题在不在这节课里;不在(学生在做别的)就没有课堂条、也没有「下一题」 */
|
||||
function includes(problemDisplayId: string) {
|
||||
return !!find(problemDisplayId)
|
||||
}
|
||||
|
||||
/** 老师布置的叫「老师布置的题」,推断出来的叫「这节课的题」 */
|
||||
const label = computed(() =>
|
||||
activity.value?.source === "teacher" ? "老师布置的题" : "这节课的题",
|
||||
)
|
||||
|
||||
return { activity, label, load, markAccepted, includes }
|
||||
})
|
||||
Reference in new issue
Block a user