fix(题目): 修掉题目页重构审查查出的问题——提交途中换题会把结果算到新题上、运行例子把对的菱形判错、点评完「下一题」不跳等
题目页重构之后按「功能对照 / 状态 / 结果页 / 布局与后端」四路审查了一遍,查出的问题一起修: 提交与换题 - 提交、画流程图途中换题:A 题格式化好的代码会盖掉 B 的草稿,A 判对了把 B 标成已解决、 弹 B 的点评、题单里跳回题单。现在每个请求回来先核对题目,轮询和推送回来的也核对提交编号 - 画着流程图换到不能画的题,语言退到题目的第一门、还被记成偏好;现在回到学生习惯的那门 - 强制点评评完点「继续 / 下一题」,学生之前在看「题目」页签的话会停在原题 - 离开题目页之后判题、流程图的 WebSocket 一直连着,现在闲 15 分钟断开 运行例子 - 输出开头的空格被削掉,打印菱形这类题对的代码也显示「没对上」 - C 的编译警告拼进了「你的输出」(后台生成测试点也会写进 .out) - Python 的语法错误(中文标点)被说成「下标有没有越界、除数是不是 0」 - 比赛里仍然把空格画成「·」、把不同的地方标红 入口与协作 - 比赛里不给「课堂统计」、不绑协作:房间和统计都按题号找,比赛题号和公开题撞号 - 「看他交过什么」按整名匹配,不再混进学号相近的同学 - 协作中点上下文条、相似题、下一题开新标签,协作不断 - 题单入口给管理员的「⋯」里留「本题提交」 结果与题面 - 「去自己输入拿 51 这些数试试」取到了题面 HTML 里的颜色值;「题目原话」露出 " - 流程图 A 级的学生标题下还写着「还没交过」 - 「我的提交」上方补回「答案错误 × 3」这样的计数 - 流程图历次评分能点开看那一版的图;读分数失败不再说「还没交过流程图」;手机上能看流程图点评 - 「结果」页签有新结果没看时加粗上色,看过就恢复 - 空画布拖进第一个节点画布会跳一下;画布底下空着一条 - 比赛没开始写成「已结束」、倒计时超过一个月算错;分隔条拖到头工具栏被挤坏;题单里的题目页有备案页脚 - 流程图历次分数只读需要的几列(一个学生一道题 23 次提交:96 kB → 2 kB) - 比赛密码、比赛没开始等几条学生看得到的英文报错换成中文 设计文档照实际做法更新了第 5.5、5.6、6、8、9、10、11 节。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ee66cef367
commit
e7b95d211d
34 files changed
+579
-108
No files matched your search
@@ -4,6 +4,7 @@ import { storeToRefs } from "pinia"
|
||||
import { useCodeStore } from "oj/store/code"
|
||||
import { isFlowchartPass, useFlowchartStore } from "oj/store/flowchart"
|
||||
import { useProblemStore } from "oj/store/problem"
|
||||
import { useUserStore } from "shared/store/user"
|
||||
import { sortFlowchartCriteria } from "utils/constants"
|
||||
import { decompressFromBase64, parseTime } from "utils/functions"
|
||||
|
||||
@@ -12,13 +13,29 @@ import { decompressFromBase64, parseTime } from "utils/functions"
|
||||
*
|
||||
* 原来评分详情是提交按钮旁一个 1000px 的弹框:盖住画布,学生没法一边看改进建议一边改图;
|
||||
* 翻历次评分要一页一页点分页器。现在摆在左栏,画布整列都在右边(设计文档第 6 节)。
|
||||
*
|
||||
* viewOnly:手机上只能看、不能画(设计文档 5.6),不给「照着它写代码」「载回画布」。
|
||||
*/
|
||||
const { viewOnly = false } = defineProps<{ viewOnly?: boolean }>()
|
||||
|
||||
const MyFlowchart = defineAsyncComponent(() => import("./MyFlowchart.vue"))
|
||||
|
||||
const flowchartStore = useFlowchartStore()
|
||||
const { phase, scores, hiddenCount, selectedId, details, detailLoading, editor } =
|
||||
const { phase, scores, hiddenCount, selectedId, details, detailLoading, scoresFailed, editor } =
|
||||
storeToRefs(flowchartStore)
|
||||
const problemStore = useProblemStore()
|
||||
const codeStore = useCodeStore()
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 历次分数原来只由题面(ProblemContent)去读。手机上题面是另一个页签、不切过去就不挂载,
|
||||
// 直接打开 ?tab=result 的话「流程图点评」一条都没有。这里自己也读一次(同一道题只读一次)
|
||||
watch(
|
||||
() => [problemStore.problem?.id, userStore.isAuthed] as const,
|
||||
([id, authed]) => {
|
||||
if (id && authed) flowchartStore.ensureLoaded()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
const message = useMessage()
|
||||
const theme = useThemeVars()
|
||||
// 暗色主题的成功色是浅薄荷绿,上面的白字看不清
|
||||
@@ -33,6 +50,23 @@ const current = computed(() => {
|
||||
const detail = computed(() => (current.value ? details.value[current.value.id] : undefined))
|
||||
const viewingLatest = computed(() => selectedId.value === null)
|
||||
|
||||
// ==================== 这一版的图 ====================
|
||||
/**
|
||||
* 点历史分数条上的哪一次,就摆哪一次交上去的图(只读的 Mermaid 渲染,不动画布)。
|
||||
* 原来的评分弹框左边就是这张图;挪进结果页签时丢了,学生翻到「第 2 次 C 42」
|
||||
* 只看得到评语,想不起来那一版画成什么样。图从评语同一个详情接口来,随评语一起现拉。
|
||||
*/
|
||||
const versionIndex = computed(() =>
|
||||
current.value ? scores.value.findIndex((row) => row.id === current.value!.id) : -1,
|
||||
)
|
||||
const versionTitle = computed(() => {
|
||||
if (!current.value) return ""
|
||||
return `第 ${versionIndex.value + 1} 次画的图 · ${parseTime(current.value.createTime, "MM-DD HH:mm")}`
|
||||
})
|
||||
/** 收起来之后翻别的次也保持收着:左栏在机房屏上只有六百来像素高 */
|
||||
const versionOpen = ref(true)
|
||||
const versionZoom = ref(false)
|
||||
|
||||
// 评完的那次、点到的那次,评语都要现拉(历次分数里只有分数)
|
||||
watch(
|
||||
() => current.value?.id,
|
||||
@@ -162,10 +196,18 @@ function goCode() {
|
||||
<template>
|
||||
<!-- 设计稿「画流程图:画布占满右栏,AI 点评在左边」「流程图评分中」 -->
|
||||
<div class="flowchart-result">
|
||||
<div v-if="scoresFailed && !scores.length && phase !== 'evaluating'" class="failed load-failed">
|
||||
<span>以前交过的流程图评分没读出来,可能是网络断了一下</span>
|
||||
<n-button size="small" secondary @click="flowchartStore.ensureLoaded()">再读一次</n-button>
|
||||
</div>
|
||||
<n-empty
|
||||
v-if="!scores.length && phase !== 'evaluating'"
|
||||
v-else-if="!scores.length && phase !== 'evaluating'"
|
||||
class="empty"
|
||||
description="还没交过流程图。画好之后点「交给 AI 点评」,评分和建议会出现在这里"
|
||||
:description="
|
||||
viewOnly
|
||||
? '还没交过流程图。流程图要在电脑上画(手机上拖不动),交给 AI 点评之后,评分和建议在这里也看得到'
|
||||
: '还没交过流程图。画好之后点「交给 AI 点评」,评分和建议会出现在这里'
|
||||
"
|
||||
/>
|
||||
|
||||
<!-- 历次分数连成一条:一眼看出是不是越改越好;正在评的那次是虚线框 -->
|
||||
@@ -229,6 +271,35 @@ function goCode() {
|
||||
</n-flex>
|
||||
|
||||
<template v-if="detail">
|
||||
<div v-if="detail.mermaidCode" class="version">
|
||||
<div class="version-head">
|
||||
<span class="version-title">{{ versionTitle }}</span>
|
||||
<n-button text type="primary" size="small" @click="versionZoom = true">
|
||||
放大看
|
||||
</n-button>
|
||||
<n-button text size="small" @click="versionOpen = !versionOpen">
|
||||
{{ versionOpen ? "收起" : "展开" }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div
|
||||
v-if="versionOpen"
|
||||
class="version-chart"
|
||||
role="button"
|
||||
:aria-label="`放大看${versionTitle}`"
|
||||
@click="versionZoom = true"
|
||||
>
|
||||
<MyFlowchart compact :code="detail.mermaidCode" />
|
||||
</div>
|
||||
<n-modal
|
||||
v-model:show="versionZoom"
|
||||
preset="card"
|
||||
:title="versionTitle"
|
||||
:style="{ maxWidth: '900px' }"
|
||||
>
|
||||
<MyFlowchart :code="detail.mermaidCode" />
|
||||
</n-modal>
|
||||
</div>
|
||||
|
||||
<div v-if="criteria.length" class="criteria">
|
||||
<div v-for="[name, item] in criteria" :key="name" class="criterion">
|
||||
<div class="criterion-head">
|
||||
@@ -254,7 +325,7 @@ function goCode() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<div v-if="!viewOnly" class="actions">
|
||||
<n-button
|
||||
v-if="isFlowchartPass(current.grade) && canCode"
|
||||
type="primary"
|
||||
@@ -369,6 +440,41 @@ function goCode() {
|
||||
border: 1px solid rgba(240, 160, 32, 0.35);
|
||||
}
|
||||
|
||||
.load-failed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 12px;
|
||||
}
|
||||
|
||||
/* 这一版的图:和题面里「你画的流程图」同一个样子,点一下放大 */
|
||||
.version {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.version-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.version-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.version-chart {
|
||||
cursor: zoom-in;
|
||||
border-radius: 6px;
|
||||
background-color: rgba(128, 128, 128, 0.06);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.version-chart :deep(.flowchart-container.compact) {
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.score-line {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
||||
Reference in new issue
Block a user