Deploy / deploy (push) Successful in 1m0s
- 提交结果 / 运行例子 / 自己输入改成浅灰底的分段按钮;状态大字和测试点方块放大一档 - 哪里不对一律是黄色小节标题(例子 1 没对上、错在没给你看的测试点、第 2 行出错),说明用正文大小 - 答错的对比竖着排成灰 / 绿 / 红三格,和题面的例子一个样子;运行时错误把出错那一行放进红格 - 做对了去掉浅绿卡片,改成「接着做」一句话加写着下一题题号题名的大按钮 - 提交详情页的答错对比不变 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
138 lines
5.0 KiB
Vue
138 lines
5.0 KiB
Vue
<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'
|
||
import { stripJudgePath } from 'utils/judge'
|
||
import SectionHead from './SectionHead.vue'
|
||
|
||
/**
|
||
* 编译错误的中文说明卡片: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),
|
||
)
|
||
|
||
const rawError = computed(() => stripJudgePath(props.errInfo))
|
||
|
||
/**
|
||
* 报错只点名第一处。别的行也有中文标点的话,一句话带过(设计稿「语法没过」:
|
||
* 「第 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>
|
||
<!-- 设计稿「题目页观感」结果 X:黄色小节标题说第几行,正文大小的中文说明;那一行编辑器里已经标红了 -->
|
||
<div class="explain-section">
|
||
<template v-if="explanation">
|
||
<SectionHead icon="problem" tone="amber">{{ explanation.line ? `第 ${explanation.line} 行` : '代码的格式写错了' }}</SectionHead>
|
||
<p class="explain">{{ explanation.message }}</p>
|
||
<p v-if="otherPunctuationLines.length" class="explain other">第 {{ otherPunctuationLines.join('、') }} 行也有中文标点,编辑器里都标出来了。</p>
|
||
<div v-if="explanation.punctuation" class="fix">
|
||
<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 class="raw-collapse">
|
||
<n-collapse-item title="原始报错(英文)" name="raw">
|
||
<div class="raw">{{ rawError }}</div>
|
||
</n-collapse-item>
|
||
</n-collapse>
|
||
</template>
|
||
<!-- 翻译表没覆盖到的句式(生产数据里约 0.3%):照旧给原文 -->
|
||
<template v-else>
|
||
<SectionHead icon="problem" tone="amber">代码的格式写错了</SectionHead>
|
||
<p class="explain">请仔细检查,看看代码的格式是不是写错了!</p>
|
||
<div class="raw">{{ rawError }}</div>
|
||
</template>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.explain-section {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.explain {
|
||
margin: 0 0 10px;
|
||
font-size: 16px;
|
||
line-height: 1.75;
|
||
}
|
||
|
||
.explain.other {
|
||
font-size: 14px;
|
||
color: v-bind('theme.textColor2');
|
||
}
|
||
|
||
.fix {
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.raw {
|
||
white-space: pre;
|
||
overflow-x: auto;
|
||
line-height: 1.5;
|
||
font-family: Consolas, Monaco, monospace;
|
||
font-size: 13px;
|
||
}
|
||
</style>
|