From 6331391792468678343fb8af62b6f6f7e6a56ea3 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Thu, 23 Apr 2026 01:52:20 -0600 Subject: [PATCH] feat: add interactive MCQ and code-sort exercise widgets to tutorial lessons Co-Authored-By: Claude Sonnet 4.6 --- src/admin/api.ts | 30 +++ .../tutorial/components/ExerciseManager.vue | 226 ++++++++++++++++++ src/admin/tutorial/detail.vue | 5 + src/oj/api.ts | 6 + src/oj/learn/components/ExerciseMcq.vue | 78 ++++++ src/oj/learn/components/ExerciseSort.vue | 121 ++++++++++ src/oj/learn/components/ExerciseWidget.vue | 13 + src/oj/learn/composables/useExerciseParse.ts | 31 +++ src/oj/learn/index.vue | 93 ++++--- src/utils/types.ts | 18 ++ 10 files changed, 568 insertions(+), 53 deletions(-) create mode 100644 src/admin/tutorial/components/ExerciseManager.vue create mode 100644 src/oj/learn/components/ExerciseMcq.vue create mode 100644 src/oj/learn/components/ExerciseSort.vue create mode 100644 src/oj/learn/components/ExerciseWidget.vue create mode 100644 src/oj/learn/composables/useExerciseParse.ts diff --git a/src/admin/api.ts b/src/admin/api.ts index d537bd1..2e68b2c 100644 --- a/src/admin/api.ts +++ b/src/admin/api.ts @@ -6,6 +6,7 @@ import { BlankContest, BlankProblem, Contest, + Exercise, Server, TestcaseUploadedReturns, Tutorial, @@ -261,6 +262,35 @@ export function setTutorialVisibility(id: number, is_public: boolean) { return http.put("admin/tutorial/visibility", { id, is_public }) } +export async function getAdminExercises(tutorialId: number) { + const res = await http.get("admin/exercise", { params: { tutorial_id: tutorialId } }) + return res.data as Exercise[] +} + +export async function createExercise(data: { + tutorial_id: number + type: "mcq" | "sort" + data: object + order: number +}) { + const res = await http.post("admin/exercise", data) + return res.data as Exercise +} + +export async function updateExercise(data: { + id: number + type: "mcq" | "sort" + data: object + order: number +}) { + const res = await http.put("admin/exercise", data) + return res.data as Exercise +} + +export function deleteExercise(id: number) { + return http.delete("admin/exercise", { params: { id } }) +} + // 将竞赛题目转为公开题目 export function makeProblemPublic(id: number, display_id: string) { return http.post("admin/contest_problem/make_public", { diff --git a/src/admin/tutorial/components/ExerciseManager.vue b/src/admin/tutorial/components/ExerciseManager.vue new file mode 100644 index 0000000..7527547 --- /dev/null +++ b/src/admin/tutorial/components/ExerciseManager.vue @@ -0,0 +1,226 @@ + + + diff --git a/src/admin/tutorial/detail.vue b/src/admin/tutorial/detail.vue index 7898c21..3941a8d 100644 --- a/src/admin/tutorial/detail.vue +++ b/src/admin/tutorial/detail.vue @@ -3,6 +3,7 @@ import CodeEditor from "shared/components/CodeEditor.vue" import MarkdownEditor from "shared/components/MarkdownEditor.vue" import { Tutorial } from "utils/types" import { createTutorial, getTutorial, updateTutorial } from "../api" +import ExerciseManager from "./components/ExerciseManager.vue" interface Props { tutorialID?: string @@ -112,6 +113,10 @@ onMounted(init) height="400px" /> + + + +