feat: 批改页加“课堂总结”入口
- 作品墙顶部“开始讲评”旁边加“课堂总结”,用批改页当前的班级和任务打开 - 在批改页里打开时不显示“去批改”;点高分作品直接在批改页里打开, 那个学生不在当前班就先切过去(地址栏跟着切,关掉作品回到那个班) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
54003fd404
commit
915b48ebc0
2 files changed
+52
-8
No files matched your search
@@ -134,7 +134,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</n-spin>
|
</n-spin>
|
||||||
|
|
||||||
<template #footer>
|
<template v-if="!inReview" #footer>
|
||||||
<n-flex justify="end">
|
<n-flex justify="end">
|
||||||
<n-button type="primary" icon-placement="right" @click="openReview">
|
<n-button type="primary" icon-placement="right" @click="openReview">
|
||||||
去批改{{ isAll ? "" : " " + classname + " 班" }}
|
去批改{{ isAll ? "" : " " + classname + " 班" }}
|
||||||
@@ -153,8 +153,19 @@ import { Submission } from "../../api"
|
|||||||
import type { TaskStatsOut, TopScoredItem, UserTag } from "../../utils/type"
|
import type { TaskStatsOut, TopScoredItem, UserTag } from "../../utils/type"
|
||||||
|
|
||||||
// 老师下课前看的这道题的总结:默认看批改页正在看的班
|
// 老师下课前看的这道题的总结:默认看批改页正在看的班
|
||||||
const props = defineProps<{ taskId: number; show: boolean; title?: string }>()
|
const props = defineProps<{
|
||||||
defineEmits<{ (e: "update:show", v: boolean): void }>()
|
taskId: number
|
||||||
|
show: boolean
|
||||||
|
title?: string
|
||||||
|
// 在批改页里打开:不显示“去批改”,点高分作品直接在批改页里打开
|
||||||
|
inReview?: boolean
|
||||||
|
// 批改页正在看的班级(“__all__” 是全部班级);不传就用记住的
|
||||||
|
initialClass?: string | null
|
||||||
|
}>()
|
||||||
|
const emit = defineEmits<{
|
||||||
|
"update:show": [v: boolean]
|
||||||
|
open: [submissionId: string, classname: string]
|
||||||
|
}>()
|
||||||
|
|
||||||
// 和批改页共用“当前班级”
|
// 和批改页共用“当前班级”
|
||||||
const ALL = "__all__"
|
const ALL = "__all__"
|
||||||
@@ -237,11 +248,11 @@ watch(
|
|||||||
() => props.show,
|
() => props.show,
|
||||||
async (open) => {
|
async (open) => {
|
||||||
if (!open || !props.taskId) return
|
if (!open || !props.taskId) return
|
||||||
|
let saved: string | null = props.initialClass ?? null
|
||||||
try {
|
try {
|
||||||
classname.value = localStorage.getItem(CLASS_KEY) || ALL
|
saved ||= localStorage.getItem(CLASS_KEY)
|
||||||
} catch {
|
} catch {}
|
||||||
classname.value = ALL
|
classname.value = saved || ALL
|
||||||
}
|
|
||||||
await load()
|
await load()
|
||||||
// 记住的班级已经没有学生了
|
// 记住的班级已经没有学生了
|
||||||
if (!isAll.value && !stats.value?.classes.includes(classname.value)) {
|
if (!isAll.value && !stats.value?.classes.includes(classname.value)) {
|
||||||
@@ -257,6 +268,10 @@ function openInNewTab(query: Record<string, string>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openSubmission(item: TopScoredItem) {
|
function openSubmission(item: TopScoredItem) {
|
||||||
|
if (props.inReview) {
|
||||||
|
emit("open", item.submission_id, item.classname)
|
||||||
|
return
|
||||||
|
}
|
||||||
openInNewTab({
|
openInNewTab({
|
||||||
class: isAll.value ? item.classname : classname.value,
|
class: isAll.value ? item.classname : classname.value,
|
||||||
task: String(props.taskId),
|
task: String(props.taskId),
|
||||||
|
|||||||
@@ -48,6 +48,10 @@
|
|||||||
>
|
>
|
||||||
开始讲评({{ presentList.length }})
|
开始讲评({{ presentList.length }})
|
||||||
</n-button>
|
</n-button>
|
||||||
|
<n-button size="small" @click="showStats = true">
|
||||||
|
<template #icon><Icon icon="lucide:bar-chart-2" /></template>
|
||||||
|
课堂总结
|
||||||
|
</n-button>
|
||||||
<n-tooltip>
|
<n-tooltip>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-button size="small" quaternary @click="loadWall">
|
<n-button size="small" quaternary @click="loadWall">
|
||||||
@@ -119,6 +123,16 @@
|
|||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<TaskStatsModal
|
||||||
|
v-if="task"
|
||||||
|
v-model:show="showStats"
|
||||||
|
:task-id="task.id"
|
||||||
|
:title="`${task.display}. ${task.title}`"
|
||||||
|
:initial-class="classname"
|
||||||
|
in-review
|
||||||
|
@open="openFromStats"
|
||||||
|
/>
|
||||||
|
|
||||||
<div v-if="presenting" class="present-mode">
|
<div v-if="presenting" class="present-mode">
|
||||||
<div class="present-head">
|
<div class="present-head">
|
||||||
<b>{{ presentList[presentIndex]?.username }}</b>
|
<b>{{ presentList[presentIndex]?.username }}</b>
|
||||||
@@ -134,11 +148,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue"
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from "vue"
|
||||||
import { useRoute, useRouter } from "vue-router"
|
import { useRoute, useRouter } from "vue-router"
|
||||||
import { useStorage } from "@vueuse/core"
|
import { useStorage } from "@vueuse/core"
|
||||||
import { Icon } from "../icons"
|
import { Icon } from "../icons"
|
||||||
import ReviewPanel from "../components/review/ReviewPanel.vue"
|
import ReviewPanel from "../components/review/ReviewPanel.vue"
|
||||||
|
import TaskStatsModal from "../components/task/TaskStatsModal.vue"
|
||||||
import { Review } from "../api"
|
import { Review } from "../api"
|
||||||
import { buildPreviewDocument, PREVIEW_SANDBOX } from "../utils/previewDocument"
|
import { buildPreviewDocument, PREVIEW_SANDBOX } from "../utils/previewDocument"
|
||||||
import type { FlagType, ReviewOptions, ReviewProgress, ReviewStudent } from "../utils/type"
|
import type { FlagType, ReviewOptions, ReviewProgress, ReviewStudent } from "../utils/type"
|
||||||
@@ -170,6 +185,20 @@ function openDetail(id: string) {
|
|||||||
router.push({ query: { ...route.query, sub: id } })
|
router.push({ query: { ...route.query, sub: id } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 课堂总结里点了高分作品:在这里打开;那个学生不在当前班就先切过去
|
||||||
|
const showStats = ref(false)
|
||||||
|
async function openFromStats(id: string, cls: string) {
|
||||||
|
showStats.value = false
|
||||||
|
if (classname.value !== ALL && classname.value !== cls) {
|
||||||
|
classname.value = cls
|
||||||
|
// 先让地址栏变成新班级(切班的 watch 也会这么改),关掉作品时“后退”才回到这个班
|
||||||
|
await nextTick()
|
||||||
|
await router.replace({ query: { class: cls, task: taskId.value ?? undefined } })
|
||||||
|
}
|
||||||
|
pushedDetail = true
|
||||||
|
router.push({ query: { class: classname.value ?? undefined, task: taskId.value ?? undefined, sub: id } })
|
||||||
|
}
|
||||||
|
|
||||||
function closeDetail() {
|
function closeDetail() {
|
||||||
if (pushedDetail) {
|
if (pushedDetail) {
|
||||||
pushedDetail = false
|
pushedDetail = false
|
||||||
|
|||||||
Reference in new issue
Block a user