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>
This commit is contained in:
1 parent
d4aadc7722
commit
e8a20d12f9
38 files changed
+78
-78
No files matched your search
+1
-1
@@ -9,7 +9,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getAuthors } from "oj/api"
|
||||
import { getAuthors } from "shared/api"
|
||||
|
||||
interface Props {
|
||||
all?: boolean
|
||||
@@ -9,7 +9,7 @@ import { batchSetProblemVisible, getProblemList, toggleProblemVisible } from "..
|
||||
import Actions from "./components/Actions.vue"
|
||||
import AddFromLibraryModal from "./components/AddFromLibraryModal.vue"
|
||||
import { useRouteQuery } from "@vueuse/router"
|
||||
import AuthorSelect from "shared/components/AuthorSelect.vue"
|
||||
import AuthorSelect from "admin/components/AuthorSelect.vue"
|
||||
import type { DataTableRowKey } from "naive-ui"
|
||||
import BatchTagModal from "./components/BatchTagModal.vue"
|
||||
import CreateProblemMenu from "./components/CreateProblemMenu.vue"
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
import type { MarkAchievementsRead } from "@oj2/contract"
|
||||
import api from "utils/api"
|
||||
import type { AchievementList, PendingAchievement } from "utils/types"
|
||||
import type { AchievementList } from "utils/types"
|
||||
|
||||
export function getAchievements(name?: string) {
|
||||
return api.get<AchievementList>("achievements", {
|
||||
params: name ? { username: name } : {},
|
||||
})
|
||||
}
|
||||
|
||||
export function getPendingAchievements() {
|
||||
return api.get<PendingAchievement[]>("achievements/pending")
|
||||
}
|
||||
|
||||
export function markAchievementsRead(ids: number[]) {
|
||||
return api.post("achievements/pending/read", { ids } satisfies MarkAchievementsRead)
|
||||
}
|
||||
+2
-29
@@ -23,7 +23,6 @@ import {
|
||||
type ProfileOverview,
|
||||
type TutorialSummary,
|
||||
type Tutorial,
|
||||
type ClassActivity,
|
||||
type ClassToday,
|
||||
type LastVisit,
|
||||
type ClassBoard,
|
||||
@@ -40,10 +39,8 @@ import {
|
||||
type WeeklyRank,
|
||||
type ProblemRank,
|
||||
type CreateSubmissionResponse,
|
||||
type ProblemAuthor,
|
||||
type ProblemListItem,
|
||||
type ProblemStats,
|
||||
type ProblemList,
|
||||
type ProblemProgress,
|
||||
type CreateFlowchartResponse,
|
||||
type FlowchartScores,
|
||||
@@ -64,7 +61,7 @@ import {
|
||||
} from "@oj2/contract"
|
||||
import api from "utils/api"
|
||||
import { contract } from "utils/contract"
|
||||
import { toProblemRow } from "oj/transforms"
|
||||
import { toProblemRow } from "utils/transforms"
|
||||
import type {
|
||||
Announcement,
|
||||
AnnouncementListItem,
|
||||
@@ -82,26 +79,12 @@ import type {
|
||||
|
||||
/**
|
||||
* 题目详情 / 提交详情的 schema 按需加载,和请求并行发出。静态 import 会把 zod
|
||||
* 运行时带进首页 —— 题目列表的 getProblemList 也在这个文件里。
|
||||
* 运行时带进首页 —— 首页、题目列表都 import 这个文件。
|
||||
* 闸门本身仍是同步的(等 schema 到了再返回),submissionDetailSchema 有 `.catch()`,
|
||||
* 放行原文和解析结果不等价,所以不能像 /me 那样改成 contractDeferred。
|
||||
*/
|
||||
const loadDetailSchemas = () => import("./detailSchemas")
|
||||
|
||||
export async function getProblemList(
|
||||
offset = 0,
|
||||
limit = 10,
|
||||
searchParams: Record<string, unknown> = {},
|
||||
) {
|
||||
const res = await api.get<ProblemList>("problems", {
|
||||
params: { paging: true, offset, limit, ...searchParams },
|
||||
})
|
||||
return {
|
||||
results: res.results.map(toProblemRow),
|
||||
total: res.total,
|
||||
}
|
||||
}
|
||||
|
||||
/** 题目列表顶上那行和左栏的进度;没登录是 null */
|
||||
export function getProblemProgress() {
|
||||
return api.get<ProblemProgress | null>("problems/progress")
|
||||
@@ -115,12 +98,6 @@ export function getRandomProblem(params: { tag: string; type: string; language:
|
||||
return api.get<string>("problems/random", { params })
|
||||
}
|
||||
|
||||
export function getAuthors(all = false) {
|
||||
return api.get<ProblemAuthor[]>("problem-authors", {
|
||||
params: { all: all ? "1" : "0" },
|
||||
})
|
||||
}
|
||||
|
||||
export async function getProblem(problemID: string, contestID: string) {
|
||||
const endpoint = contestID
|
||||
? `contests/${encodeURIComponent(contestID)}/problems/${encodeURIComponent(problemID)}`
|
||||
@@ -316,10 +293,6 @@ export function getKnowledgeMap() {
|
||||
return api.get<KnowledgeMap>("me/knowledge")
|
||||
}
|
||||
|
||||
export function getClassActivity() {
|
||||
return api.get<ClassActivity>("me/class-activity")
|
||||
}
|
||||
|
||||
/** 老师首页「今天的课」:今天在上课的每个班 */
|
||||
export function getClassToday() {
|
||||
return api.get<ClassToday>("classroom/today")
|
||||
|
||||
@@ -15,7 +15,7 @@ import { LANGUAGE_SHOW_VALUE } from "utils/constants"
|
||||
import { parseTime } from "utils/functions"
|
||||
import type { ClassBoard, ClassBoardCell, ClassBoardProblem, ClassBoardStudent } from "utils/types"
|
||||
import ExplainModal, { type ExplainPerson } from "./components/ExplainModal.vue"
|
||||
import { errorKind, type ErrorKind } from "./errorKind"
|
||||
import { errorKind, type ErrorKind } from "utils/errorKind"
|
||||
|
||||
/**
|
||||
* 课堂看板(老师,设计稿「课堂看板重设计」A+B):这个班、今天、这节课的几道题 × 全班学生。
|
||||
|
||||
@@ -3,7 +3,6 @@ import { continueStep, doneCount, lessonPath, splitTitle, type TutorialType } fr
|
||||
import { Icon } from "@iconify/vue"
|
||||
import {
|
||||
getAnnouncementList,
|
||||
getClassActivity,
|
||||
getKnowledgeMap,
|
||||
getContestList,
|
||||
getLastVisit,
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
getTutorials,
|
||||
getWeeklyRank,
|
||||
} from "oj/api"
|
||||
import { getClassActivity } from "shared/api"
|
||||
import SubmissionResultTag from "shared/components/SubmissionResultTag.vue"
|
||||
import { pickHeadline, type Headline } from "oj/user/knowledge"
|
||||
import { useUserStore } from "shared/store/user"
|
||||
|
||||
@@ -5,7 +5,7 @@ import { errorMessage } from "utils/api"
|
||||
import { SubmissionStatus } from "utils/constants"
|
||||
import { trialRun } from "utils/judge"
|
||||
import { explainCCompileError } from "oj/problem/utils/cError"
|
||||
import { explainPythonCompileError } from "oj/problem/utils/pythonError"
|
||||
import { explainPythonCompileError } from "utils/pythonError"
|
||||
import { explainRuntimeError } from "oj/problem/utils/runtimeError"
|
||||
import { useAuthModalStore } from "shared/store/authModal"
|
||||
import { useUserStore } from "shared/store/user"
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
showPunctuationMarks,
|
||||
} from "oj/problem/utils/errorMark"
|
||||
import { explainCCompileError } from "oj/problem/utils/cError"
|
||||
import { explainPythonCompileError } from "oj/problem/utils/pythonError"
|
||||
import { explainPythonCompileError } from "utils/pythonError"
|
||||
|
||||
/**
|
||||
* 编译错误的中文说明卡片:Python 的语法错误走 pythonError.ts,C / C++ 的 gcc 报错走
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { RouteLocationRaw } from "vue-router"
|
||||
import { getProblemSetDetail, getProblemSetProblems, joinProblemSet } from "oj/api"
|
||||
import { useContestStore } from "oj/store/contest"
|
||||
import { isFlowchartPass, useFlowchartStore } from "oj/store/flowchart"
|
||||
import { useLessonStore } from "oj/store/lesson"
|
||||
import { useLessonStore } from "shared/store/lesson"
|
||||
import { useProblemStore } from "oj/store/problem"
|
||||
import { useUserStore } from "shared/store/user"
|
||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useThemeVars } from "naive-ui"
|
||||
import { useLessonStore } from "oj/store/lesson"
|
||||
import { useLessonStore } from "shared/store/lesson"
|
||||
|
||||
/**
|
||||
* 通过之后告诉学生「这节课的下一题是哪道」。
|
||||
|
||||
@@ -18,7 +18,7 @@ import { useCollabHere, useTeacherCollab } from "../composables/teacherCollab"
|
||||
import { useProblemPageContext } from "../composables/problemPageContext"
|
||||
import CollabBar from "./CollabBar.vue"
|
||||
import { useFlowchartStore } from "oj/store/flowchart"
|
||||
import { useLessonStore } from "oj/store/lesson"
|
||||
import { useLessonStore } from "shared/store/lesson"
|
||||
|
||||
const FlowchartEditor = defineAsyncComponent(
|
||||
() => import("shared/components/FlowchartEditor/index.vue"),
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getReaction } from "oj/api"
|
||||
import { useProblemStore } from "oj/store/problem"
|
||||
import { useSubmissionStore } from "oj/store/submission"
|
||||
import { useFireworks } from "oj/problem/composables/useFireworks"
|
||||
import { useLessonStore } from "oj/store/lesson"
|
||||
import { useLessonStore } from "shared/store/lesson"
|
||||
import { isFlowchartPass, useFlowchartStore } from "oj/store/flowchart"
|
||||
import { SubmissionStatus } from "utils/constants"
|
||||
import { useProblemPageContext } from "../composables/problemPageContext"
|
||||
|
||||
@@ -21,7 +21,7 @@ import WrongAnswerExplain from "./WrongAnswerExplain.vue"
|
||||
import LessonNext from "./LessonNext.vue"
|
||||
import ResultHeader from "./ResultHeader.vue"
|
||||
import SimilarProblems from "./SimilarProblems.vue"
|
||||
import { useLessonStore } from "oj/store/lesson"
|
||||
import { useLessonStore } from "shared/store/lesson"
|
||||
import { useProblemPageContext } from "../composables/problemPageContext"
|
||||
import Markdown from "shared/components/Markdown.vue"
|
||||
|
||||
|
||||
@@ -5,12 +5,7 @@ import { useThemeVars } from "naive-ui"
|
||||
import { useProblemStore } from "oj/store/problem"
|
||||
import { markdownToText } from "oj/problem/utils/plainText"
|
||||
import { JUDGE_STATUS, SubmissionStatus } from "utils/constants"
|
||||
import {
|
||||
classifyDiff,
|
||||
explainDiff,
|
||||
firstDifferentLine,
|
||||
type SampleCheck,
|
||||
} from "oj/problem/utils/wrongAnswer"
|
||||
import { classifyDiff, explainDiff, firstDifferentLine, type SampleCheck } from "utils/wrongAnswer"
|
||||
|
||||
/**
|
||||
* 答案错误时的样例对比(数据见后端 judge/run.ts 的 checkSamples)。
|
||||
|
||||
@@ -3,7 +3,8 @@ import { Icon } from "@iconify/vue"
|
||||
import type { ProblemProgress, ProblemTypeFilter, Tag } from "@oj2/contract"
|
||||
import { useRouteQuery } from "@vueuse/router"
|
||||
import { useThemeVars } from "naive-ui"
|
||||
import { getAuthors, getProblemList, getProblemProgress, getRandomProblem } from "oj/api"
|
||||
import { getProblemProgress, getRandomProblem } from "oj/api"
|
||||
import { getAuthors, getProblemList } from "shared/api"
|
||||
import { useCodeStore } from "oj/store/code"
|
||||
import { useTone } from "shared/composables/tone"
|
||||
import { STORAGE_KEY } from "utils/constants"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { findChinesePunctuation } from "./chinesePunctuation"
|
||||
import { PUNCTUATION_NAMES, type PythonErrorExplanation } from "./pythonError"
|
||||
import { PUNCTUATION_NAMES, type PythonErrorExplanation } from "utils/pythonError"
|
||||
|
||||
/**
|
||||
* 把 C / C++ 的编译错误(gcc 的原文)翻成中文,和 pythonError.ts 同一个思路:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PUNCTUATION_MAP } from "./pythonError"
|
||||
import { PUNCTUATION_MAP } from "utils/pythonError"
|
||||
|
||||
export interface PunctuationFix {
|
||||
from: number
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { RankRow, WeeklyChampion } from "@oj2/contract"
|
||||
import { useThemeVars } from "naive-ui"
|
||||
import UserName from "shared/components/UserName.vue"
|
||||
import { parseTime } from "utils/functions"
|
||||
import RankAvatar from "./RankAvatar.vue"
|
||||
import RankAvatar from "shared/components/RankAvatar.vue"
|
||||
|
||||
/** 本班最近几周的每周冠军,底下再挂这张榜上「进步最大」的那个人 */
|
||||
defineProps<{
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useBackClose } from "shared/composables/backClose"
|
||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||
import { parseTime } from "utils/functions"
|
||||
import { useRankPalette } from "../palette"
|
||||
import RankAvatar from "./RankAvatar.vue"
|
||||
import RankAvatar from "shared/components/RankAvatar.vue"
|
||||
|
||||
// Markdown 连着整套代码高亮(两百多 KB),这里要等点开 / AI 写完才用得上,别让它拖慢页面
|
||||
const Markdown = defineAsyncComponent(() => import("shared/components/Markdown.vue"))
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useThemeVars } from "naive-ui"
|
||||
import UserName from "shared/components/UserName.vue"
|
||||
import { gapText, toPass } from "../utils"
|
||||
import MoodPopover from "./MoodPopover.vue"
|
||||
import RankAvatar from "./RankAvatar.vue"
|
||||
import RankAvatar from "shared/components/RankAvatar.vue"
|
||||
|
||||
export interface MiniRank {
|
||||
label: string
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useThemeVars } from "naive-ui"
|
||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||
import UserName from "shared/components/UserName.vue"
|
||||
import { useRankPalette } from "../palette"
|
||||
import RankAvatar from "./RankAvatar.vue"
|
||||
import RankAvatar from "shared/components/RankAvatar.vue"
|
||||
|
||||
/**
|
||||
* 领奖台:前三名站在台阶上,第一名戴皇冠。赛道从第 4 名接着排,前三不再出现第二遍。
|
||||
|
||||
@@ -5,7 +5,7 @@ import UserName from "shared/components/UserName.vue"
|
||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||
import { useRankPalette } from "../palette"
|
||||
import MoodPopover from "./MoodPopover.vue"
|
||||
import RankAvatar from "./RankAvatar.vue"
|
||||
import RankAvatar from "shared/components/RankAvatar.vue"
|
||||
|
||||
/**
|
||||
* 赛道:每人一根条,条越长做对越多,一样长的先做到的排前面(设计稿 G1 / G2)。
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { RankRow } from "@oj2/contract"
|
||||
import { useThemeVars } from "naive-ui"
|
||||
import UserName from "shared/components/UserName.vue"
|
||||
import { useRankPalette } from "../palette"
|
||||
import RankAvatar from "./RankAvatar.vue"
|
||||
import RankAvatar from "shared/components/RankAvatar.vue"
|
||||
|
||||
/** 这周本班前 5 名(做对过新题的),每周一清零 */
|
||||
const props = defineProps<{ rows: RankRow[]; meId?: number; loaded: boolean }>()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { explainCCompileError } from "oj/problem/utils/cError"
|
||||
import { explainPythonCompileError } from "oj/problem/utils/pythonError"
|
||||
import { explainPythonCompileError } from "utils/pythonError"
|
||||
import { explainRuntimeError } from "oj/problem/utils/runtimeError"
|
||||
import { SubmissionStatus } from "utils/constants"
|
||||
import type { Submission } from "utils/types"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { Icon } from "@iconify/vue"
|
||||
import { useThemeVars } from "naive-ui"
|
||||
import { resetAvatar, uploadAvatar } from "oj/api"
|
||||
import RankAvatar from "oj/rank/components/RankAvatar.vue"
|
||||
import RankAvatar from "shared/components/RankAvatar.vue"
|
||||
import { useUserStore } from "shared/store/user"
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@ import { MOOD_MAX, STUDENT_ROLES, type ProfileOverview } from "@oj2/contract"
|
||||
import { Icon } from "@iconify/vue"
|
||||
import { useThemeVars } from "naive-ui"
|
||||
import { clearMood, setRankHidden, updateProfile } from "oj/api"
|
||||
import RankAvatar from "oj/rank/components/RankAvatar.vue"
|
||||
import RankAvatar from "shared/components/RankAvatar.vue"
|
||||
import { classLabel } from "utils/classes"
|
||||
import UserName from "shared/components/UserName.vue"
|
||||
import { useUserStore } from "shared/store/user"
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import type { LoginRequest, RegisterRequest } from "@oj2/contract"
|
||||
import type { Quote, WebsiteConfig } from "@oj2/contract"
|
||||
import type {
|
||||
ClassActivity,
|
||||
MarkAchievementsRead,
|
||||
ProblemAuthor,
|
||||
ProblemList,
|
||||
Quote,
|
||||
WebsiteConfig,
|
||||
} from "@oj2/contract"
|
||||
import api from "utils/api"
|
||||
import { contractDeferred } from "utils/contract"
|
||||
import type { Profile, Tag } from "utils/types"
|
||||
import { toProblemRow } from "utils/transforms"
|
||||
import type { PendingAchievement, Profile, Tag } from "utils/types"
|
||||
|
||||
export function login(data: { username: string; password: string }) {
|
||||
return api.post("auth/login", data satisfies LoginRequest)
|
||||
@@ -51,3 +59,35 @@ export function getClassUsernames(classroom: string) {
|
||||
export function getWebsiteConfig() {
|
||||
return api.get<WebsiteConfig>("site")
|
||||
}
|
||||
|
||||
export async function getProblemList(
|
||||
offset = 0,
|
||||
limit = 10,
|
||||
searchParams: Record<string, unknown> = {},
|
||||
) {
|
||||
const res = await api.get<ProblemList>("problems", {
|
||||
params: { paging: true, offset, limit, ...searchParams },
|
||||
})
|
||||
return {
|
||||
results: res.results.map(toProblemRow),
|
||||
total: res.total,
|
||||
}
|
||||
}
|
||||
|
||||
export function getAuthors(all = false) {
|
||||
return api.get<ProblemAuthor[]>("problem-authors", {
|
||||
params: { all: all ? "1" : "0" },
|
||||
})
|
||||
}
|
||||
|
||||
export function getClassActivity() {
|
||||
return api.get<ClassActivity>("me/class-activity")
|
||||
}
|
||||
|
||||
export function getPendingAchievements() {
|
||||
return api.get<PendingAchievement[]>("achievements/pending")
|
||||
}
|
||||
|
||||
export function markAchievementsRead(ids: number[]) {
|
||||
return api.post("achievements/pending/read", { ids } satisfies MarkAchievementsRead)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue"
|
||||
import { errorKind } from "oj/classroom/errorKind"
|
||||
import { errorKind } from "utils/errorKind"
|
||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||
import { useTeacherHelp } from "shared/composables/teacherHelp"
|
||||
import { useCollabStore } from "shared/store/collab"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue"
|
||||
import { useThemeVars } from "naive-ui"
|
||||
import RankAvatar from "oj/rank/components/RankAvatar.vue"
|
||||
import RankAvatar from "shared/components/RankAvatar.vue"
|
||||
import { classLabel, groupClassOptions } from "utils/classes"
|
||||
import { errorCode } from "utils/api"
|
||||
import { STORAGE_KEY } from "utils/constants"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue"
|
||||
import { useThemeVars } from "naive-ui"
|
||||
import { useLessonStore } from "oj/store/lesson"
|
||||
import { useLessonStore } from "shared/store/lesson"
|
||||
import { useProblemJump } from "shared/composables/problemJump"
|
||||
import { useUserStore } from "shared/store/user"
|
||||
import type { ClassActivityProblem } from "utils/types"
|
||||
|
||||
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
import { getProblemList } from "oj/api"
|
||||
import { getProblemList } from "shared/api"
|
||||
|
||||
/**
|
||||
* 按题号直达。课上老师报一个题号、全班去找,是非比赛提交最主要的来路,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPendingAchievements, markAchievementsRead } from "oj/achievement/api"
|
||||
import { getPendingAchievements, markAchievementsRead } from "shared/api"
|
||||
import type { QueuedAchievement } from "utils/types"
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from "pinia"
|
||||
import { getClassActivity } from "oj/api"
|
||||
import { getClassActivity } from "shared/api"
|
||||
import { LANGUAGE_SHOW_VALUE } from "utils/constants"
|
||||
import type { ClassActivity, ClassActivityProblem } from "utils/types"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { classifyDiff, type DiffKind } from "oj/problem/utils/wrongAnswer"
|
||||
import { classifyDiff, type DiffKind } from "utils/wrongAnswer"
|
||||
import { JUDGE_STATUS, SubmissionStatus } from "utils/constants"
|
||||
import type { ClassBoardCell } from "utils/types"
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,5 +1,5 @@
|
||||
import type { StatisticInfo } from "@oj2/contract"
|
||||
import { PUNCTUATION_MAP, PUNCTUATION_NAMES } from "./pythonError"
|
||||
import { PUNCTUATION_MAP, PUNCTUATION_NAMES } from "utils/pythonError"
|
||||
|
||||
/**
|
||||
* 答案错误时,比较学生在**公开样例**上的输出和正确输出,归一类、给一句中文提示。
|
||||
Reference in new issue
Block a user