看板格子的次数原来是代码和流程图加在一起的,链接却总是去代码那边;名字链接看的「今天交过」 还把比赛提交算了进去。现在格子分开给代码 / 流程图次数、学生分开给今天交过代码 / 画过流程图, 只画了流程图的跳到提交列表的「流程图」那边。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
52bf2d6111
commit
923516ad36
3 files changed
+50
-8
No files matched your search
@@ -560,6 +560,10 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => {
|
|||||||
.select({
|
.select({
|
||||||
userId: schema.submission.userId,
|
userId: schema.submission.userId,
|
||||||
lastAt: sql<string>`max(${schema.submission.createTime})`,
|
lastAt: sql<string>`max(${schema.submission.createTime})`,
|
||||||
|
// 非比赛的最后一次,给「点名字跳提交列表」判断用(列表只列非比赛的)
|
||||||
|
lastPublicAt: sql<
|
||||||
|
string | null
|
||||||
|
>`max(${schema.submission.createTime}) filter (where ${isNull(schema.submission.contestId)})`,
|
||||||
})
|
})
|
||||||
.from(schema.submission)
|
.from(schema.submission)
|
||||||
.where(
|
.where(
|
||||||
@@ -625,6 +629,10 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => {
|
|||||||
const cellByKey = new Map(cells.map((row) => [`${row.userId}:${row.problemId}`, row]))
|
const cellByKey = new Map(cells.map((row) => [`${row.userId}:${row.problemId}`, row]))
|
||||||
const drawnByKey = new Map(drawn.map((row) => [`${row.userId}:${row.problemId}`, row]))
|
const drawnByKey = new Map(drawn.map((row) => [`${row.userId}:${row.problemId}`, row]))
|
||||||
const lastByUser = new Map(lastSubmits.map((row) => [row.userId, row.lastAt]))
|
const lastByUser = new Map(lastSubmits.map((row) => [row.userId, row.lastAt]))
|
||||||
|
const codeTodayUsers = new Set(
|
||||||
|
lastSubmits.filter((row) => row.lastPublicAt).map((row) => row.userId),
|
||||||
|
)
|
||||||
|
const drawnTodayUsers = new Set(lastDrawn.map((row) => row.userId))
|
||||||
for (const row of lastDrawn) {
|
for (const row of lastDrawn) {
|
||||||
const known = lastByUser.get(row.userId)
|
const known = lastByUser.get(row.userId)
|
||||||
if (!known || Date.parse(row.lastAt) > Date.parse(known)) lastByUser.set(row.userId, row.lastAt)
|
if (!known || Date.parse(row.lastAt) > Date.parse(known)) lastByUser.set(row.userId, row.lastAt)
|
||||||
@@ -656,10 +664,14 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => {
|
|||||||
return {
|
return {
|
||||||
status: !cell && !flow ? "none" : acceptedAt ? "accepted" : "tried",
|
status: !cell && !flow ? "none" : acceptedAt ? "accepted" : "tried",
|
||||||
attempts: (cell?.attempts ?? 0) + (flow?.attempts ?? 0),
|
attempts: (cell?.attempts ?? 0) + (flow?.attempts ?? 0),
|
||||||
|
codeAttempts: cell?.attempts ?? 0,
|
||||||
|
flowchartAttempts: flow?.attempts ?? 0,
|
||||||
acceptedAt,
|
acceptedAt,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
lastSubmitAt: lastByUser.get(student.userId) ?? null,
|
lastSubmitAt: lastByUser.get(student.userId) ?? null,
|
||||||
|
codeToday: codeTodayUsers.has(student.userId),
|
||||||
|
drawnToday: drawnTodayUsers.has(student.userId),
|
||||||
recentAttended: attendedByUser.get(student.userId) ?? 0,
|
recentAttended: attendedByUser.get(student.userId) ?? 0,
|
||||||
}) satisfies ClassBoardStudent,
|
}) satisfies ClassBoardStudent,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -224,16 +224,24 @@ const GROUP_LABEL: Record<
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 点格子 / 名字到提交列表看他今天交的代码。新标签打开:看板多半正投在屏幕上,别把它顶掉。
|
* 点格子 / 名字到提交列表看他今天交的。新标签打开:看板多半正投在屏幕上,别把它顶掉。
|
||||||
* 用户名按整名匹配(学号互相包含是常态,ks24a1 会混进 ks24a10)
|
* 用户名按整名匹配(学号互相包含是常态,ks24a1 会混进 ks24a10)。
|
||||||
|
*
|
||||||
|
* 今天只画了流程图、没写代码的,跳到列表的「流程图」那边 —— 不然这节课整班在画流程图时,
|
||||||
|
* 点进去是一片「没有符合条件的提交」
|
||||||
*/
|
*/
|
||||||
function submissionsHref(student: ClassBoardStudent, problemDisplayId?: string) {
|
function submissionsHref(
|
||||||
|
student: ClassBoardStudent,
|
||||||
|
problemDisplayId: string | undefined,
|
||||||
|
flowchart: boolean,
|
||||||
|
) {
|
||||||
return router.resolve({
|
return router.resolve({
|
||||||
name: "submissions",
|
name: "submissions",
|
||||||
query: {
|
query: {
|
||||||
username: student.username,
|
username: student.username,
|
||||||
exactUsername: "1",
|
exactUsername: "1",
|
||||||
today: "1",
|
today: "1",
|
||||||
|
...(flowchart ? { language: "Flowchart" } : {}),
|
||||||
...(problemDisplayId ? { problem: problemDisplayId } : {}),
|
...(problemDisplayId ? { problem: problemDisplayId } : {}),
|
||||||
},
|
},
|
||||||
}).href
|
}).href
|
||||||
@@ -401,9 +409,9 @@ function submissionsHref(student: ClassBoardStudent, problemDisplayId?: string)
|
|||||||
<tr v-for="student in students" :key="student.userId">
|
<tr v-for="student in students" :key="student.userId">
|
||||||
<td class="name">
|
<td class="name">
|
||||||
<a
|
<a
|
||||||
v-if="student.lastSubmitAt"
|
v-if="student.codeToday || student.drawnToday"
|
||||||
class="cell-link"
|
class="cell-link"
|
||||||
:href="submissionsHref(student)"
|
:href="submissionsHref(student, undefined, !student.codeToday)"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
:title="`看 ${nameOf(student)} 今天交的全部`"
|
:title="`看 ${nameOf(student)} 今天交的全部`"
|
||||||
>
|
>
|
||||||
@@ -432,9 +440,19 @@ function submissionsHref(student: ClassBoardStudent, problemDisplayId?: string)
|
|||||||
<a
|
<a
|
||||||
v-if="cell.attempts"
|
v-if="cell.attempts"
|
||||||
class="cell-link"
|
class="cell-link"
|
||||||
:href="submissionsHref(student, board.problems[i]!.problemDisplayId)"
|
:href="
|
||||||
|
submissionsHref(
|
||||||
|
student,
|
||||||
|
board.problems[i]!.problemDisplayId,
|
||||||
|
!cell.codeAttempts,
|
||||||
|
)
|
||||||
|
"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
:title="`看 ${nameOf(student)} 今天在这道题上交的 ${cell.attempts} 次`"
|
:title="
|
||||||
|
cell.codeAttempts
|
||||||
|
? `看 ${nameOf(student)} 今天在这道题上交的 ${cell.codeAttempts} 次代码`
|
||||||
|
: `看 ${nameOf(student)} 今天在这道题上画的 ${cell.flowchartAttempts} 张流程图`
|
||||||
|
"
|
||||||
>
|
>
|
||||||
{{ cellText(cell) }}
|
{{ cellText(cell) }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -101,8 +101,14 @@ export const classBoardProblemSchema = z.object({
|
|||||||
export const classBoardCellSchema = z.object({
|
export const classBoardCellSchema = z.object({
|
||||||
/** accepted 看的是题库里(不含比赛)有没有通过过,不限今天 —— 以前做过的也算做完 */
|
/** accepted 看的是题库里(不含比赛)有没有通过过,不限今天 —— 以前做过的也算做完 */
|
||||||
status: z.enum(["accepted", "tried", "none"]),
|
status: z.enum(["accepted", "tried", "none"]),
|
||||||
/** 今天在这道题上交了几次 */
|
/** 今天在这道题上交了几次(代码 + 流程图) */
|
||||||
attempts: z.number().int(),
|
attempts: z.number().int(),
|
||||||
|
/**
|
||||||
|
* 上面那个数拆开:代码几次、流程图几次。点格子要知道该跳到提交列表的哪一边 ——
|
||||||
|
* 这节课整班在画流程图时,跳到代码那边是一片空
|
||||||
|
*/
|
||||||
|
codeAttempts: z.number().int(),
|
||||||
|
flowchartAttempts: z.number().int(),
|
||||||
/** 第一次通过的时刻;今天之前通过的,前端显示成「之前」 */
|
/** 第一次通过的时刻;今天之前通过的,前端显示成「之前」 */
|
||||||
acceptedAt: z.string().nullable(),
|
acceptedAt: z.string().nullable(),
|
||||||
})
|
})
|
||||||
@@ -115,6 +121,12 @@ export const classBoardStudentSchema = z.object({
|
|||||||
cells: z.array(classBoardCellSchema),
|
cells: z.array(classBoardCellSchema),
|
||||||
/** 今天最后一次提交(任何题),没交过为 null */
|
/** 今天最后一次提交(任何题),没交过为 null */
|
||||||
lastSubmitAt: z.string().nullable(),
|
lastSubmitAt: z.string().nullable(),
|
||||||
|
/**
|
||||||
|
* 今天交过**非比赛**的代码 / 画过流程图没有。点名字跳提交列表用:lastSubmitAt 把比赛提交
|
||||||
|
* 也算进去了,而提交列表只列非比赛的,只看它会跳到一个空列表
|
||||||
|
*/
|
||||||
|
codeToday: z.boolean(),
|
||||||
|
drawnToday: z.boolean(),
|
||||||
/** 最近几节课(不含今天,个数见 ClassBoard.recentLessons)里交过题的有几节 */
|
/** 最近几节课(不含今天,个数见 ClassBoard.recentLessons)里交过题的有几节 */
|
||||||
recentAttended: z.number().int(),
|
recentAttended: z.number().int(),
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in new issue
Block a user