- 方块格多一列「配套题」:他打开过的那些课挂的题,做对几道 / 一共几道;格子提示里也带上 - 右栏分「练一练 / 配套题」两个页签:每道题读过这课的人里几人做对,点开看谁没交、谁交了没对, 一键跳到这个人这道题的提交 - 学生明细每课多一段配套题:做对没有、交了几次、最后哪天交的 - 做对不论早晚,读教程之前在课上做对的也算;口径同学生端课文里那个勾(代码 AC 或流程图 A / S) - 1366 宽下 C 语言 20 课也不用横着滑了:「最后学习」只留日期,几列收窄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7839a65f59
commit
1ebb23cdac
5 files changed
+474
-33
No files matched your search
@@ -0,0 +1,113 @@
|
||||
<script lang="ts">
|
||||
import type { LearnBoardStudent } from "utils/types"
|
||||
|
||||
/** 读过这课的一个人,和他在这道配套题上的情况(一次没交是 null) */
|
||||
export interface ProblemReader {
|
||||
userId: number
|
||||
username: string
|
||||
state: LearnBoardStudent["problems"][number] | null
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NText } from "naive-ui"
|
||||
import { parseTime } from "utils/functions"
|
||||
import UserName from "shared/components/UserName.vue"
|
||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||
|
||||
/**
|
||||
* 一道配套题,读过这课的人各自做没做对。数据就是方块格那份,不另外请求。
|
||||
* 交过没对的排最前(老师点进来是找卡住的人),其次一次没交的,做对的垫底。
|
||||
*/
|
||||
const props = defineProps<{ displayId: string; people: ProblemReader[] }>()
|
||||
const router = useRouter()
|
||||
const { isMobile } = useBreakpoints()
|
||||
|
||||
function rank(row: ProblemReader) {
|
||||
if (!row.state) return 1
|
||||
return row.state.solved ? 2 : 0
|
||||
}
|
||||
|
||||
const rows = computed(() => [...props.people].sort((a, b) => rank(a) - rank(b)))
|
||||
|
||||
function renderResult(row: ProblemReader) {
|
||||
if (!row.state) return h(NText, { depth: 3 }, () => "没交")
|
||||
return row.state.solved
|
||||
? h(NText, { type: "success" }, () => "做对了")
|
||||
: h(NText, { type: "error" }, () => `交了 ${row.state!.submissions} 次还没对`)
|
||||
}
|
||||
|
||||
/** 提交列表按这个人、这道题筛好,新标签页打开,后台这页不动 */
|
||||
function renderLink(row: ProblemReader) {
|
||||
if (!row.state) return null
|
||||
const { href } = router.resolve({
|
||||
path: "/submission",
|
||||
query: { problem: props.displayId, username: row.username, exactUsername: "1" },
|
||||
})
|
||||
return h("a", { href, target: "_blank", class: "link" }, "看提交")
|
||||
}
|
||||
|
||||
const desktopColumns: DataTableColumn<ProblemReader>[] = [
|
||||
{
|
||||
title: "学生",
|
||||
key: "username",
|
||||
width: 150,
|
||||
render: (row) => h(UserName, { username: row.username }),
|
||||
},
|
||||
{ title: "结果", key: "result", width: 150, render: renderResult },
|
||||
{
|
||||
title: "最后提交",
|
||||
key: "lastSubmitAt",
|
||||
width: 150,
|
||||
render: (row) =>
|
||||
row.state ? parseTime(row.state.lastSubmitAt, "YYYY年M月D日") : h(NText, { depth: 3 }, "-"),
|
||||
},
|
||||
{ title: "", key: "link", render: renderLink },
|
||||
]
|
||||
|
||||
const mobileColumns: DataTableColumn<ProblemReader>[] = [
|
||||
{
|
||||
title: "学生",
|
||||
key: "username",
|
||||
render: (row) =>
|
||||
h("div", { class: "m-line" }, [
|
||||
h(UserName, { username: row.username }),
|
||||
h("span", { class: "m-right" }, [renderResult(row), renderLink(row)]),
|
||||
]),
|
||||
},
|
||||
]
|
||||
|
||||
const columns = computed(() => (isMobile.value ? mobileColumns : desktopColumns))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-data-table
|
||||
striped
|
||||
:columns="columns"
|
||||
:data="rows"
|
||||
:row-key="(row: ProblemReader) => row.userId"
|
||||
size="small"
|
||||
:pagination="rows.length > 10 ? { pageSize: 10 } : false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.link) {
|
||||
color: #18a058;
|
||||
text-decoration: none;
|
||||
}
|
||||
:deep(.link:hover) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
:deep(.m-line) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
:deep(.m-right) {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user