题目页重构之后按「功能对照 / 状态 / 结果页 / 布局与后端」四路审查了一遍,查出的问题一起修: 提交与换题 - 提交、画流程图途中换题: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>
107 lines
3.6 KiB
Vue
107 lines
3.6 KiB
Vue
<script setup lang="ts">
|
|
import { Icon } from "@iconify/vue"
|
|
import { useThemeVars } from "naive-ui"
|
|
import { storeToRefs } from "pinia"
|
|
import { useSubmissionStore } from "oj/store/submission"
|
|
import { useCodeStore } from "oj/store/code"
|
|
import { isFlowchartPass, useFlowchartStore } from "oj/store/flowchart"
|
|
import { SubmissionStatus } from "utils/constants"
|
|
|
|
/**
|
|
* 「结果」页签的标题:带上这一次的状态。用图标而不是一个彩色小圆点 ——
|
|
* 机房投影、色弱的学生只靠颜色分不出对错。
|
|
*
|
|
* 新结果出来、学生还没点开看过:「结果」两个字跟着状态上色、加粗。看过一次就算已读,
|
|
* 图标留着、不再强调(设计文档 5.3)。流程图评完时学生在看题目不会被强制切过去,
|
|
* 靠的就是这一下提醒
|
|
*/
|
|
const props = defineProps<{ active: boolean }>()
|
|
|
|
const theme = useThemeVars()
|
|
const { submission, syntaxErrorInfo, judging, pending, submitting } =
|
|
storeToRefs(useSubmissionStore())
|
|
|
|
const codeStore = useCodeStore()
|
|
const flowchart = storeToRefs(useFlowchartStore())
|
|
|
|
/** 画流程图时:评中转圈,A/S 打勾,B/C 是「还没过关」的 !,评失败是 ✕ */
|
|
const flowchartStatus = computed(() => {
|
|
const phase = flowchart.phase.value
|
|
if (phase === "evaluating")
|
|
return { icon: "ph:circle-notch-bold", color: theme.value.infoColor, spin: true }
|
|
if (phase === "failed") return { icon: "ph:x-bold", color: theme.value.errorColor }
|
|
if (phase !== "done") return null
|
|
return isFlowchartPass(flowchart.latestRating.value.grade)
|
|
? { icon: "ph:check-bold", color: theme.value.successColor }
|
|
: { icon: "ph:warning-bold", color: theme.value.warningColor }
|
|
})
|
|
|
|
const status = computed(() => {
|
|
if (codeStore.code.language === "Flowchart") return flowchartStatus.value
|
|
if (syntaxErrorInfo.value) return { icon: "ph:warning-bold", color: theme.value.warningColor }
|
|
if (!submission.value) return null
|
|
if (judging.value || pending.value || submitting.value)
|
|
return { icon: "ph:circle-notch-bold", color: theme.value.infoColor, spin: true }
|
|
const result = submission.value.result
|
|
if (result === SubmissionStatus.accepted)
|
|
return { icon: "ph:check-bold", color: theme.value.successColor }
|
|
if (result === SubmissionStatus.ast_check_failed)
|
|
return { icon: "ph:warning-bold", color: theme.value.warningColor }
|
|
return { icon: "ph:x-bold", color: theme.value.errorColor }
|
|
})
|
|
|
|
/**
|
|
* 这一次结果的身份:哪条提交判成了什么、第几次流程图评分、哪一次语法没过。
|
|
* 换了就是新结果,要重新提醒
|
|
*/
|
|
const resultKey = computed(() => {
|
|
if (!status.value) return ""
|
|
if (codeStore.code.language === "Flowchart")
|
|
return `flowchart:${flowchart.evaluatedSeq.value}:${flowchart.phase.value}`
|
|
if (syntaxErrorInfo.value) return `syntax:${syntaxErrorInfo.value}`
|
|
return `submission:${submission.value?.id}:${submission.value?.result}`
|
|
})
|
|
const readKey = ref("")
|
|
watchEffect(() => {
|
|
if (props.active) readKey.value = resultKey.value
|
|
})
|
|
const unread = computed(
|
|
() => !!status.value && !status.value.spin && !props.active && resultKey.value !== readKey.value,
|
|
)
|
|
</script>
|
|
|
|
<template>
|
|
<span class="label" :class="{ unread }" :style="unread ? { color: status!.color } : undefined">
|
|
结果
|
|
<Icon
|
|
v-if="status"
|
|
:icon="status.icon"
|
|
:color="status.color"
|
|
:class="{ spin: status.spin }"
|
|
width="14"
|
|
/>
|
|
</span>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.label {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.unread {
|
|
font-weight: 700;
|
|
}
|
|
|
|
.spin {
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
</style>
|