Files
OJ2/apps/web/src/oj/problem/components/CompileErrorExplain.vue
T
xuyueandClaude Opus 5.5 e8a20d12f9 refactor(前端): shared / utils 不再 import oj 和 admin 下的任何东西
依赖方向原来是反的:顶栏、登录框、举手列表这些 shared 组件从 oj/ 里拿东西。
- 跨端的接口挪进 shared/api.ts(按 apps/web/CLAUDE.md 的分法):学生端题目列表、
  出题人列表、这节课的题、待弹的成就
- 这节课的 store → shared/store/lesson.ts;头像 RankAvatar → shared/components
- 纯函数挪进 utils/:举手列表的错法归类 errorKind、它依赖的答案错误分类 wrongAnswer
  和 Python 报错翻译 pythonError、题目列表行的 toProblemRow
- 只有后台在用的出题人下拉 AuthorSelect 挪回 admin/components

纯搬家。首页、题目列表、题目页、排名、课堂看板、后台题目列表打开无报错,
顶栏题号框敲 1001 回车直达题目页。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:11:21 +08:00

153 lines
5.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { useThemeVars } from "naive-ui"
import { useCodeStore } from "oj/store/code"
import { findChinesePunctuation } from "oj/problem/utils/chinesePunctuation"
import {
applyFixes,
clearErrorMark,
currentCode,
showErrorMark,
showPunctuationMarks,
} from "oj/problem/utils/errorMark"
import { explainCCompileError } from "oj/problem/utils/cError"
import { explainPythonCompileError } from "utils/pythonError"
/**
* 编译错误的中文说明卡片:Python 的语法错误走 pythonError.ts,C / C++ 的 gcc 报错走
* cError.ts,两张表给出同一种形状。Python 有两处来源:提交前的语法检查(SubmitCode,
* 报错来自服务端的 CPython)和判题回来的编译错误(来自判题机),同一个大版本的
* CPython,报错原文一样。
*
* 挂上就在编辑器里标出位置,卸掉就清掉。
*/
const props = withDefaults(
defineProps<{
/** 编译器的报错原文(判题机的 `err_info`,或 `/code/format` 的 syntax-error) */
errInfo: string
language?: string
}>(),
{ language: "Python" },
)
const codeStore = useCodeStore()
const theme = useThemeVars()
const explanation = computed(() =>
props.language === "Python"
? explainPythonCompileError(props.errInfo, codeStore.code.value)
: explainCCompileError(props.errInfo, codeStore.code.value, props.language),
)
/** 判题机的临时目录名(`/judger/run/<32 位随机串>/`)对学生没有意义,只剩文件名 */
const rawError = computed(() => props.errInfo.replace(/\/judger\/run\/[^/"]+\//g, ""))
/**
* 报错只点名第一处。别的行也有中文标点的话,一句话带过(设计稿「语法没过」:
* 「第 4 行也有一个中文冒号,编辑器里都标出来了」)。原来这里还摆一段出错那行的代码摘录,
* 编辑器里已经标着红,设计稿里去掉了
*/
const otherPunctuationLines = computed(() => {
const ex = explanation.value
if (!ex?.punctuation) return []
const code = codeStore.code.value
const lines = new Set<number>()
for (const fix of findChinesePunctuation(code, props.language)) {
lines.add(code.slice(0, fix.from).split("\n").length)
}
lines.delete(ex.line ?? -1)
return [...lines].sort((a, b) => a - b)
})
// 数的是编辑器里**现在**的代码:学生可能已经自己改掉几处了
const punctuationFixCount = computed(() =>
explanation.value?.punctuation
? findChinesePunctuation(codeStore.code.value, props.language).length
: 0,
)
const fixedCount = ref<number | null>(null)
function fixPunctuation() {
const code = currentCode()
if (code === null) return
const fixes = findChinesePunctuation(code, props.language)
if (applyFixes(fixes)) fixedCount.value = fixes.length
}
watch(
explanation,
(ex) => {
fixedCount.value = null
if (ex?.line) showErrorMark(ex.line, ex.sourceLine, ex.caret)
else clearErrorMark()
// 中文标点一处不漏地标出来,和按钮上的「N 处」对得上
const code = ex?.punctuation ? currentCode() : null
if (code !== null) showPunctuationMarks(findChinesePunctuation(code, props.language))
},
{ immediate: true },
)
onUnmounted(clearErrorMark)
</script>
<template>
<div class="explain-card">
<template v-if="explanation">
<p class="explain">
<b v-if="explanation.line">第 {{ explanation.line }} 行:</b>{{ explanation.message }}
</p>
<p v-if="otherPunctuationLines.length" class="explain other">
第 {{ otherPunctuationLines.join("、") }} 行也有中文标点,编辑器里都标出来了。
</p>
<div v-if="explanation.punctuation">
<n-button v-if="punctuationFixCount" type="primary" @click="fixPunctuation">
把中文标点都换成英文({{ punctuationFixCount }} 处)
</n-button>
<n-text v-else-if="fixedCount" type="success">
换好了 {{ fixedCount }} 处,再提交一次试试。
</n-text>
</div>
<n-collapse>
<n-collapse-item title="原始报错(英文)" name="raw">
<div class="raw">{{ rawError }}</div>
</n-collapse-item>
</n-collapse>
</template>
<!-- 翻译表没覆盖到的句式(生产数据里约 0.3%):照旧给原文 -->
<template v-else>
<p class="explain">请仔细检查,看看代码的格式是不是写错了!</p>
<div class="raw">{{ rawError }}</div>
</template>
</div>
</template>
<style scoped>
/* 和答错的说明同一种浅灰卡片(设计稿「语法没过」) */
.explain-card {
padding: 12px 14px;
border-radius: 6px;
background-color: rgba(128, 128, 128, 0.06);
border: 1px solid v-bind("theme.dividerColor");
display: flex;
flex-direction: column;
gap: 10px;
}
.explain {
margin: 0;
line-height: 1.7;
}
.explain.other {
font-size: 14px;
color: v-bind("theme.textColor2");
}
.raw {
white-space: pre;
overflow-x: auto;
line-height: 1.5;
font-family: Consolas, Monaco, monospace;
font-size: 13px;
}
</style>