fix(课堂任务): 后台改一节不再把它排到同一分钟布置的另一节前面;学生管理员算进统计的课

- 后台「改」时刻只填到分,原来一保存就把布置时刻截成整分:先画流程图、紧接着再开一节写代码,
  只改了做什么,「今天最后一节」就换回了画流程图那节。没改时刻就留着原来的
- 数据统计「一行一节课」只数 Regular User,学生管理员的提交不算,和「做完 / 全班」的花名册对不上
- 学生「老师改了这节课」:画流程图改成写 C、先画再写改成写代码,原来写「语言 改成 C语言」,
  看不出不用画了;现在写「做法 改成 写代码 · C语言」
- 学生课堂标题按每道题合起来的要求写:先画流程图、再用这几道题写代码时,不再只写「用 C语言做」
- 先画再写的题,切到别的题再回来不再被拽回画布(原来只记最后一道题)
- 课堂任务顶上写代码的课也写「写代码」;讲错误的弹层开着时不再因为 15 秒刷新换了个人

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-11-03 09:26:39 +08:00
1 parent b09d479893
commit c3f1413545
7 files changed
+67 -25

No files matched your search

+2 -1
View File
@@ -7,6 +7,7 @@ import { getClassActivity } from 'shared/api'
import SubmissionResultTag from 'shared/components/SubmissionResultTag.vue'
import { pickHeadline, type Headline } from 'oj/user/knowledge'
import { useUserStore } from 'shared/store/user'
import { activityMode } from 'shared/store/lesson'
import { ContestStatus, CONTEST_STATUS, JUDGE_STATUS, LANGUAGE_SHOW_VALUE } from 'utils/constants'
import { duration, parseTime, zonedParts, zonedYear } from 'utils/functions'
import type { AnnouncementListItem, ClassActivity, ClassActivityProblem, Contest, LastVisit, SubmissionListItem, WeeklyRank } from 'utils/types'
@@ -191,7 +192,7 @@ const classActivityTitle = computed(() => {
// 老师在课堂任务布置的,说成「老师布置的」,比「班里在做」更让人知道该做这几道
if (classActivity.value?.source === 'teacher') {
const language = classActivity.value.language
const mode = classActivity.value.mode
const mode = activityMode(classActivity.value)
if (mode === 'flowchart') return '这节课 · 画流程图'
if (mode === 'both') return `这节课 · 先画流程图,再${language ? `用${LANGUAGE_SHOW_VALUE[language]}` : ''}写代码`
return `这节课${language ? ` · 用${LANGUAGE_SHOW_VALUE[language]}做` : ''}`
@@ -182,16 +182,25 @@ watch(
* 没有画这一步的课,画着流程图时不动他
*/
const lessonStore = useLessonStore()
let lessonLanguageFor: string | null = null
/**
* 换过的(题 + 这一节 + 该做的那一步)都记下:换到别的题再回来,同一步不再换第二次。
* 编辑器在换题时不重新挂载(detail.vue 只重新 init),只记最后一个的话一回来又被拽回去
*/
const lessonSwitched = new Set<string>()
let lessonViewing: string | null = null
watch(
() => [problem.value?._id, lessonStore.activity] as const,
([id, activity]) => {
if (!id || !activity) return
const key = [id, activity.day, activity.mode, activity.language].join('|')
if (lessonLanguageFor === key) return
lessonLanguageFor = key
if (ctx.value.entry !== 'problem' || teacherCollab.value) return
const lesson = [id, activity.day, activity.mode, activity.language].join('|')
const item = lessonStore.find(id)
const key = [lesson, item?.draw && item.draw !== 'accepted' ? 'draw' : 'code'].join('|')
// 停在这道题上、只是课堂状态刷新了(比如刚画对图):不跟着换,也不记,下回打开这道题再换
const stay = lessonViewing === lesson
lessonViewing = lesson
if (lessonSwitched.has(key) || stay) return
lessonSwitched.add(key)
if (ctx.value.entry !== 'problem' || teacherCollab.value) return
if (!item) return
if (item.draw && item.draw !== 'accepted') {
if (problemStore.canDraw && codeStore.code.language !== 'Flowchart') problemStore.switchLanguage('Flowchart')
+12 -6
View File
@@ -408,10 +408,14 @@ const explainShow = ref(false)
const explainIndex = ref(0)
const explainProblemIndex = ref(-1)
const explainProblem = computed(() => board.value?.problems[explainProblemIndex.value] ?? null)
/** 弹层顶上那排人:这道题错着的人,顺序和错法堆一样 */
const explainPeople = computed<ExplainPerson[]>(() => {
if (explainProblemIndex.value < 0) return []
const people = wrongOf(explainProblemIndex.value).filter((person) => person.explain)
/**
* 弹层顶上那排人:这道题错着的人,顺序和错法堆一样。打开那一刻定下来,开着时不跟 15 秒的刷新变:
* 弹层记的是第几个人,名单一变(有人改对了、图评完了回到名单里)就成了讲着讲着换了个人
*/
const explainPeople = ref<ExplainPerson[]>([])
function explainPeopleOf(problemIndex: number): ExplainPerson[] {
if (problemIndex < 0) return []
const people = wrongOf(problemIndex).filter((person) => person.explain)
const order = new Map<string, number>()
for (const person of people) {
order.set(person.kind.key, (order.get(person.kind.key) ?? 0) + 1)
@@ -423,10 +427,11 @@ const explainPeople = computed<ExplainPerson[]>(() => {
? { userId: person.student.userId, username: person.student.username, flowchartId: person.cell.flowchart!.latest!.id, label: person.kind.label }
: { userId: person.student.userId, username: person.student.username, submissionId: person.cell.latest!.submissionId, label: person.kind.label },
)
})
}
function openExplain(problemIndex: number, userId: number) {
explainProblemIndex.value = problemIndex
explainPeople.value = explainPeopleOf(problemIndex)
explainIndex.value = Math.max(
0,
explainPeople.value.findIndex((person) => person.userId === userId),
@@ -532,7 +537,8 @@ function lastText(student: TaskBoardStudent) {
/>
<div v-else-if="board?.problems.length && !needLesson" class="lesson">
<span class="lesson-label">{{ board.source === 'teacher' ? '这节课' : '今天在做' }}</span>
<span v-if="board.mode && board.mode !== 'code'" class="chip lang">{{ LESSON_MODE_LABEL[board.mode] }}</span>
<!-- 写代码也写出来:只写「Python」的话,看不出这节是写代码还是先画再写 -->
<span v-if="board.mode" class="chip lang">{{ LESSON_MODE_LABEL[board.mode] }}</span>
<span v-if="board.language" class="chip lang">{{ LANGUAGE_SHOW_VALUE[board.language] }}</span>
<span v-for="problem in board.problems" :key="problem.problemId" class="chip">
{{ problem.problemDisplayId }}