feat(题目): 答错时说清楚「输出了什么、应该是什么」,交对后给相似题,零碎几处对齐设计
Deploy / deploy (push) Canceled after 0s

- 答案错误里「格式和内容都不一样」那一类(样例上错的 44%):原来只说「对照一下,找找
  从哪里开始不一样」,现在是「你的程序输出了「E」,正确的是「D」。拿例子 2 的输入「60」,
  一行一行走一遍你的程序,看它走进了哪个分支」;多行的说第几行开始不一样
- 不在这节课里的题交对之后,结果页签里给「再练几道相似的」(原来只在题面最底下)
- 提交前自动排版改了代码的,结果上面提一句「编辑器里的代码也跟着变了,按 Ctrl+Z 能撤回」
- 交对之后「提交」降成描边按钮,「下一题」是唯一的主按钮
- 「重置代码」先确认(原来点一下就把代码和本地草稿一起清掉了)
- AI 提示的档位写「第 1 档 · 反问」,不写「L0 反问」
- 成就弹窗从右下角挪到右上角,别和结果、点评弹窗叠在一起

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-28 18:39:05 +08:00
1 parent 9110c678fc
commit 972dac3776
10 files changed
+206 -93

No files matched your search

@@ -0,0 +1,97 @@
<script lang="ts">
import type { ProblemRow } from "utils/types"
/**
* 同一道题两处(题面末尾、结果页签)都挂着时只拉一次。放在模块级:写在 setup 里就成了
* 每个实例各一份
*/
const cache = new Map<string, Promise<ProblemRow[]>>()
</script>
<script setup lang="ts">
import { storeToRefs } from "pinia"
import { getSimilarProblems } from "oj/api"
import { useProblemStore } from "oj/store/problem"
/**
* 相似题推荐。题面末尾(做对了、或者错了 3 次以上)和结果页签(不在课堂里的题做对之后)
* 两处用:原来只在题面最底下,学生交完不会再滚回去看。什么时候出现由调用方的 v-if 决定。
*/
const props = defineProps<{ title: string }>()
const { problem } = storeToRefs(useProblemStore())
const list = ref<ProblemRow[]>([])
watch(
() => problem.value?._id,
async (id) => {
list.value = []
if (!id) return
if (!cache.has(id))
cache.set(
id,
getSimilarProblems(id).catch(() => []),
)
const rows = await cache.get(id)!
if (problem.value?._id === id) list.value = rows
},
{ immediate: true },
)
// getSimilarProblems 已经过 toProblemRow,难度是中文,不是 Low/Mid/High
function difficultyType(difficulty: string) {
if (difficulty === "简单") return "success"
if (difficulty === "困难") return "error"
return "warning"
}
</script>
<template>
<section v-if="list.length" class="similar">
<h3 class="title">{{ props.title }}</h3>
<n-list bordered>
<n-list-item v-for="sp in list" :key="sp._id">
<n-flex align="center" justify="space-between" :wrap="false">
<router-link :to="{ name: 'problem', params: { problemID: sp._id } }" class="link">
<n-tag size="small" :bordered="false">{{ sp._id }}</n-tag>
<span class="name">{{ sp.title }}</span>
</router-link>
<n-tag v-if="sp.difficulty" size="small" :type="difficultyType(sp.difficulty)">
{{ sp.difficulty }}
</n-tag>
</n-flex>
</n-list-item>
</n-list>
</section>
</template>
<style scoped>
.similar {
margin-top: 20px;
}
.title {
font-size: 16px;
font-weight: 600;
margin: 0 0 8px;
}
.link {
display: inline-flex;
align-items: center;
gap: 8px;
min-width: 0;
color: inherit;
text-decoration: none;
}
.link:hover .name {
text-decoration: underline;
}
.name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>