提交列表: - 焦点在单选框、下拉框上时方向键不再一边切控件一边换条;跨页没回来前不再连跳一整页 - 重判、重新评分之后右栏重新拉,不再命中旧缓存;同一条新旧请求不再互相覆盖 - 从「我的提交」点顶栏「提交」、浏览器后退时,地址里没有的筛选回到默认 - 找回点用户名开个人主页;「只看他 / 只看这题」学生也能用;重判不用等代码拉下来 - 手机上老师也能重判、能按语言筛,流程图行有手机排版;学生界面带着的班级筛选显示成能点掉的标签 - mermaid 画不出来时先说中文、原始报错折起来 统计页: - 没人交时说「这段时间没有人交」,不再说「全都做完了」;没填题号时各处统一叫「做对过题」 - 班级按 class_name 单独传;查询失败清掉旧数据并说清原因;标签跟着查出来的那一份走 - 「语法未过」的格子单独标「语」;正确率不算还在判的;截断提示说清哪些数字受影响 - 看板的请假隐藏只藏还没交过、没在举手的人,「恢复」只恢复这个班的 - 手机上筛选折行、上下排,不再挤出屏幕 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
3.6 KiB
Vue
125 lines
3.6 KiB
Vue
<script setup lang="ts">
|
||
import { getSubmission } from "oj/api"
|
||
import type { JudgeCaseResult } from "@oj2/contract"
|
||
import { JUDGE_STATUS, LANGUAGE_FORMAT_VALUE, LANGUAGE_SHOW_VALUE } from "utils/constants"
|
||
import {
|
||
parseTime,
|
||
submissionCaseResults,
|
||
submissionMemoryFormat,
|
||
submissionResultTitle,
|
||
submissionTimeFormat,
|
||
} from "utils/functions"
|
||
import type { Submission } from "utils/types"
|
||
import SubmissionResultTag from "shared/components/SubmissionResultTag.vue"
|
||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||
import { useCopySubmission } from "./composables/copySubmission"
|
||
|
||
const props = defineProps<{
|
||
submissionID: string
|
||
submission?: Submission
|
||
hideList?: boolean
|
||
}>()
|
||
|
||
// 在弹框中使用时,父组件监听此事件关闭弹框,否则弹框会挡住已更新的编辑器
|
||
const emit = defineEmits<{ copied: [] }>()
|
||
|
||
const { isMobile, isDesktop } = useBreakpoints()
|
||
|
||
const submission = ref<Submission>()
|
||
const loading = ref(false)
|
||
|
||
/**
|
||
* 测试点明细。`info` 在契约里是「完整形状或空对象」的联合(非管理员拿到的是空对象),
|
||
* `data` 本身也可能为 null —— 两种情况都由这个访问器归成空数组,模板里不再直接取。
|
||
*/
|
||
const caseResults = computed(() => submissionCaseResults(submission.value?.info))
|
||
|
||
async function init() {
|
||
submission.value = props.submission
|
||
if (submission.value) return
|
||
loading.value = true
|
||
const res = await getSubmission(props.submissionID)
|
||
submission.value = res
|
||
loading.value = false
|
||
}
|
||
|
||
const columns: DataTableColumn<JudgeCaseResult>[] = [
|
||
{ title: "测试用例", key: "test_case" },
|
||
{
|
||
title: "测试状态",
|
||
key: "result",
|
||
render: (row) => h(SubmissionResultTag, { result: row.result }),
|
||
},
|
||
{
|
||
title: "占用内存",
|
||
key: "memory",
|
||
render: (row) => submissionMemoryFormat(row.memory),
|
||
},
|
||
{
|
||
title: "执行耗时",
|
||
key: "real_time",
|
||
render: (row) => submissionTimeFormat(row.real_time),
|
||
},
|
||
]
|
||
|
||
const { copyToCat: catCopy, copyToProblem: problemCopy } = useCopySubmission()
|
||
|
||
function copyToCat() {
|
||
catCopy(submission.value!)
|
||
}
|
||
|
||
function copyToProblem() {
|
||
problemCopy(submission.value!)
|
||
emit("copied")
|
||
}
|
||
|
||
onMounted(init)
|
||
</script>
|
||
|
||
<template>
|
||
<n-flex vertical v-if="submission" :size="24">
|
||
<n-flex :vertical="isMobile" justify="space-between">
|
||
<n-alert
|
||
style="flex: 1"
|
||
:type="JUDGE_STATUS[submission.result]['type']"
|
||
:title="submissionResultTitle(submission)"
|
||
>
|
||
<n-flex>
|
||
<span>提交时间:{{ parseTime(submission.createTime) }}</span>
|
||
<span>编程语言:{{ LANGUAGE_SHOW_VALUE[submission.language] }}</span>
|
||
<span>用户:{{ submission.username }}</span>
|
||
</n-flex>
|
||
</n-alert>
|
||
<n-flex :vertical="isDesktop" justify="center">
|
||
<n-button v-if="submission.language !== 'SQL'" secondary @click="copyToCat">
|
||
复制到自测猫
|
||
</n-button>
|
||
<n-button secondary @click="copyToProblem">复制回到题目</n-button>
|
||
</n-flex>
|
||
</n-flex>
|
||
<n-card embedded>
|
||
<n-code
|
||
class="code"
|
||
:language="LANGUAGE_FORMAT_VALUE[submission.language]"
|
||
:code="submission.code"
|
||
show-line-numbers
|
||
/>
|
||
</n-card>
|
||
<n-data-table v-if="!hideList && caseResults.length" :columns="columns" :data="caseResults" />
|
||
</n-flex>
|
||
<n-spin v-else :show="loading" class="loading-container"> </n-spin>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.code {
|
||
font-size: 20px;
|
||
overflow: auto;
|
||
}
|
||
.loading-container {
|
||
min-height: 200px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
</style>
|