feat: 批改页加“课堂总结”入口

- 作品墙顶部“开始讲评”旁边加“课堂总结”,用批改页当前的班级和任务打开
- 在批改页里打开时不显示“去批改”;点高分作品直接在批改页里打开,
  那个学生不在当前班就先切过去(地址栏跟着切,关掉作品回到那个班)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-27 15:06:53 +08:00
1 parent 54003fd404
commit 915b48ebc0
2 files changed
+52 -8

No files matched your search

+22 -7
View File
@@ -134,7 +134,7 @@
</div>
</n-spin>
<template #footer>
<template v-if="!inReview" #footer>
<n-flex justify="end">
<n-button type="primary" icon-placement="right" @click="openReview">
去批改{{ isAll ? "" : " " + classname + " 班" }}
@@ -153,8 +153,19 @@ import { Submission } from "../../api"
import type { TaskStatsOut, TopScoredItem, UserTag } from "../../utils/type"
// 老师下课前看的这道题的总结:默认看批改页正在看的班
const props = defineProps<{ taskId: number; show: boolean; title?: string }>()
defineEmits<{ (e: "update:show", v: boolean): void }>()
const props = defineProps<{
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__"
@@ -237,11 +248,11 @@ watch(
() => props.show,
async (open) => {
if (!open || !props.taskId) return
let saved: string | null = props.initialClass ?? null
try {
classname.value = localStorage.getItem(CLASS_KEY) || ALL
} catch {
classname.value = ALL
}
saved ||= localStorage.getItem(CLASS_KEY)
} catch {}
classname.value = saved || ALL
await load()
// 记住的班级已经没有学生了
if (!isAll.value && !stats.value?.classes.includes(classname.value)) {
@@ -257,6 +268,10 @@ function openInNewTab(query: Record<string, string>) {
}
function openSubmission(item: TopScoredItem) {
if (props.inReview) {
emit("open", item.submission_id, item.classname)
return
}
openInNewTab({
class: isAll.value ? item.classname : classname.value,
task: String(props.taskId),
+30 -1
View File
@@ -48,6 +48,10 @@
>
开始讲评({{ presentList.length }})
</n-button>
<n-button size="small" @click="showStats = true">
<template #icon><Icon icon="lucide:bar-chart-2" /></template>
课堂总结
</n-button>
<n-tooltip>
<template #trigger>
<n-button size="small" quaternary @click="loadWall">
@@ -119,6 +123,16 @@
</aside>
</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 class="present-head">
<b>{{ presentList[presentIndex]?.username }}</b>
@@ -134,11 +148,12 @@
</div>
</template>
<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 { useStorage } from "@vueuse/core"
import { Icon } from "../icons"
import ReviewPanel from "../components/review/ReviewPanel.vue"
import TaskStatsModal from "../components/task/TaskStatsModal.vue"
import { Review } from "../api"
import { buildPreviewDocument, PREVIEW_SANDBOX } from "../utils/previewDocument"
import type { FlagType, ReviewOptions, ReviewProgress, ReviewStudent } from "../utils/type"
@@ -170,6 +185,20 @@ function openDetail(id: string) {
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() {
if (pushedDetail) {
pushedDetail = false