diff --git a/src/admin/api.ts b/src/admin/api.ts index 7e60162..bbe720b 100644 --- a/src/admin/api.ts +++ b/src/admin/api.ts @@ -294,7 +294,7 @@ export function getProblemSetList( difficulty = "", status = "", ) { - return http.get("admin/problemset/", { + return http.get("admin/problemset", { params: { paging: true, offset, @@ -307,7 +307,7 @@ export function getProblemSetList( } export function getProblemSetDetail(id: number) { - return http.get(`admin/problemset/${id}/`) + return http.get(`admin/problemset/${id}`) } export function createProblemSet(data: { @@ -316,7 +316,7 @@ export function createProblemSet(data: { difficulty: string status: string }) { - return http.post("admin/problemset/", data) + return http.post("admin/problemset", data) } export function editProblemSet(data: { @@ -327,85 +327,107 @@ export function editProblemSet(data: { status?: string visible?: boolean }) { - return http.put("admin/problemset/", data) + return http.put("admin/problemset", data) } export function deleteProblemSet(id: number) { - return http.delete("admin/problemset/", { params: { id } }) + return http.delete("admin/problemset", { params: { id } }) } export function toggleProblemSetVisible(id: number) { - return http.put("admin/problemset/visible/", { id }) + return http.put("admin/problemset/visible", { id }) } export function updateProblemSetStatus(id: number, status: string) { - return http.put("admin/problemset/status/", { id, status }) + return http.put("admin/problemset/status", { id, status }) } // 题单题目管理 API export function getProblemSetProblems(problemSetId: number) { - return http.get(`admin/problemset/${problemSetId}/problems/`) + return http.get(`admin/problemset/${problemSetId}/problems`) } -export function addProblemToSet(problemSetId: number, data: { - problem_id: string - order?: number - is_required?: boolean - score?: number - hint?: string -}) { - return http.post(`admin/problemset/${problemSetId}/problems/`, data) +export function addProblemToSet( + problemSetId: number, + data: { + problem_id: string + order?: number + is_required?: boolean + score?: number + hint?: string + }, +) { + return http.post(`admin/problemset/${problemSetId}/problems`, data) } -export function editProblemInSet(problemSetId: number, problemSetProblemId: number, data: { - order?: number - is_required?: boolean - score?: number - hint?: string -}) { - return http.put(`admin/problemset/${problemSetId}/problems/${problemSetProblemId}/`, data) +export function editProblemInSet( + problemSetId: number, + problemSetProblemId: number, + data: { + order?: number + is_required?: boolean + score?: number + hint?: string + }, +) { + return http.put( + `admin/problemset/${problemSetId}/problems/${problemSetProblemId}`, + data, + ) } -export function removeProblemFromSet(problemSetId: number, problemSetProblemId: number) { - return http.delete(`admin/problemset/${problemSetId}/problems/${problemSetProblemId}/`) +export function removeProblemFromSet( + problemSetId: number, + problemSetProblemId: number, +) { + return http.delete( + `admin/problemset/${problemSetId}/problems/${problemSetProblemId}`, + ) } // 题单奖章管理 API export function getProblemSetBadges(problemSetId: number) { - return http.get(`admin/problemset/${problemSetId}/badges/`) + return http.get(`admin/problemset/${problemSetId}/badges`) } -export function createProblemSetBadge(problemSetId: number, data: { - name: string - description: string - icon: string - condition_type: string - condition_value: number - level?: number -}) { - return http.post(`admin/problemset/${problemSetId}/badges/`, data) +export function createProblemSetBadge( + problemSetId: number, + data: { + name: string + description: string + icon: string + condition_type: string + condition_value: number + level?: number + }, +) { + return http.post(`admin/problemset/${problemSetId}/badges`, data) } -export function editProblemSetBadge(problemSetId: number, badgeId: number, data: { - name?: string - description?: string - icon?: string - condition_type?: string - condition_value?: number - level?: number -}) { - return http.put(`admin/problemset/${problemSetId}/badges/${badgeId}/`, data) +export function editProblemSetBadge( + problemSetId: number, + badgeId: number, + data: { + name?: string + description?: string + icon?: string + condition_type?: string + condition_value?: number + level?: number + }, +) { + return http.put(`admin/problemset/${problemSetId}/badges/${badgeId}`, data) } export function deleteProblemSetBadge(problemSetId: number, badgeId: number) { - return http.delete(`admin/problemset/${problemSetId}/badges/${badgeId}/`) + return http.delete(`admin/problemset/${problemSetId}/badges/${badgeId}`) } // 题单进度管理 API export function getProblemSetProgress(problemSetId: number) { - return http.get(`admin/problemset/${problemSetId}/progress/`) + return http.get(`admin/problemset/${problemSetId}/progress`) } export function removeUserFromProblemSet(problemSetId: number, userId: number) { - return http.delete(`admin/problemset/${problemSetId}/progress/${userId}/`) + return http.delete(`admin/problemset/${problemSetId}/progress/${userId}`) } diff --git a/src/oj/ai/components/DurationChart.vue b/src/oj/ai/components/DurationChart.vue index ddc5aff..d436531 100644 --- a/src/oj/ai/components/DurationChart.vue +++ b/src/oj/ai/components/DurationChart.vue @@ -162,7 +162,7 @@ const options = computed>(() => { }, tooltip: { callbacks: { - label: (ctx: TooltipItem<"bar">) => { + label: (ctx: TooltipItem<"bar" | "line">) => { const dsLabel = ctx.dataset.label || "" if ((ctx.dataset as any).yAxisID === "y1") { const idx = Number(ctx.parsed.y) @@ -170,7 +170,7 @@ const options = computed>(() => { } return `${dsLabel}: ${ctx.formattedValue}` }, - footer: (items: TooltipItem<"bar">[]) => { + footer: (items: TooltipItem<"bar" | "line">[]) => { const barItems = items.filter( (item) => (item.dataset as any).yAxisID === "y", ) diff --git a/src/oj/ai/components/EfficiencyChart.vue b/src/oj/ai/components/EfficiencyChart.vue index 217b808..0af3529 100644 --- a/src/oj/ai/components/EfficiencyChart.vue +++ b/src/oj/ai/components/EfficiencyChart.vue @@ -125,12 +125,12 @@ const data = computed>(() => { }) // 图表配置 -const options = computed>(() => { +const options = computed(() => { return { responsive: true, maintainAspectRatio: false, interaction: { - mode: "index", + mode: "index" as const, intersect: false, }, scales: { @@ -142,8 +142,8 @@ const options = computed>(() => { }, }, y: { - type: "linear", - position: "left", + type: "linear" as const, + position: "left" as const, title: { display: true, text: "平均提交次数(次/题)", @@ -159,8 +159,8 @@ const options = computed>(() => { }, }, y1: { - type: "linear", - position: "right", + type: "linear" as const, + position: "right" as const, min: 0, max: 100, title: { diff --git a/src/oj/api.ts b/src/oj/api.ts index aa284bb..7477b35 100644 --- a/src/oj/api.ts +++ b/src/oj/api.ts @@ -303,3 +303,71 @@ export function getCurrentProblemFlowchartSubmission(problemId: number) { params: { problem_id: problemId }, }) } + +// ==================== 题单相关API ==================== + +export function getProblemSetList( + offset = 0, + limit = 10, + keyword = "", + difficulty = "", + status = "", +) { + return http.get("problemset", { + params: { + offset, + limit, + keyword, + difficulty, + status, + }, + }) +} + +export function getProblemSetDetail(id: number) { + return http.get(`problemset/${id}`) +} + +export function getProblemSetProblems(problemSetId: number) { + return http.get(`problemset/${problemSetId}/problems`) +} + +export function joinProblemSet(problemSetId: number) { + return http.post("problemset/progress", { + problemset_id: problemSetId, + }) +} + +export function getProblemSetSubmissions( + problemSetId: number, + params: { + problem_id?: string + result?: string + language?: string + offset?: number + limit?: number + } = {} +) { + return http.get(`problemset/${problemSetId}/submissions`, { + params, + }) +} + +export function getProblemSetStatistics(problemSetId: number) { + return http.get(`problemset/${problemSetId}/statistics`) +} + +export function updateProblemSetProgress( + problemSetId: number, + data: { + problem_id: number + status: string + score?: number + submit_time?: string + } +) { + return http.put("problemset/progress", { + problemset_id: problemSetId, + ...data, + }) +} diff --git a/src/oj/problem/components/SubmitCode.vue b/src/oj/problem/components/SubmitCode.vue index 11faa6e..0b3a1c5 100644 --- a/src/oj/problem/components/SubmitCode.vue +++ b/src/oj/problem/components/SubmitCode.vue @@ -1,7 +1,7 @@ + + + + diff --git a/src/oj/problemset/components/ProblemSetSubmissions.vue b/src/oj/problemset/components/ProblemSetSubmissions.vue new file mode 100644 index 0000000..5a9ad8e --- /dev/null +++ b/src/oj/problemset/components/ProblemSetSubmissions.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/src/oj/problemset/detail.vue b/src/oj/problemset/detail.vue new file mode 100644 index 0000000..b62bba3 --- /dev/null +++ b/src/oj/problemset/detail.vue @@ -0,0 +1,274 @@ + + + + + diff --git a/src/oj/problemset/list.vue b/src/oj/problemset/list.vue index e69de29..5eefdde 100644 --- a/src/oj/problemset/list.vue +++ b/src/oj/problemset/list.vue @@ -0,0 +1,245 @@ + + + + + diff --git a/src/routes.ts b/src/routes.ts index 8918954..458ee3e 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -100,6 +100,24 @@ export const ojs: RouteRecordRaw = { path: "flowchart", component: () => import("oj/flowchart/index.vue"), }, + { + path: "problemset", + component: () => import("oj/problemset/list.vue"), + name: "problemsets", + }, + { + path: "problemset/:problemSetId", + component: () => import("oj/problemset/detail.vue"), + props: true, + name: "problemset", + }, + { + path: "problemset/:problemSetId/problem/:problemID", + component: () => import("oj/problem/detail.vue"), + props: true, + name: "problemset problem", + meta: { requiresAuth: true }, + }, ], } diff --git a/src/shared/components/Header.vue b/src/shared/components/Header.vue index 4d4c020..5bc207e 100644 --- a/src/shared/components/Header.vue +++ b/src/shared/components/Header.vue @@ -94,6 +94,11 @@ const menus = computed(() => [ key: "problem", icon: renderIcon("streamline-emojis:blossom"), }, + { + label: () => h(RouterLink, { to: "/problemset" }, { default: () => "题单" }), + key: "problemset", + icon: renderIcon("streamline-emojis:green-book"), + }, { label: () => h(RouterLink, { to: "/submission" }, { default: () => "提交" }), diff --git a/src/utils/types.ts b/src/utils/types.ts index c065904..aa40de4 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -194,6 +194,13 @@ export interface ProblemSet { visible: boolean problems_count: number completed_count: number + user_progress: { + is_joined: boolean + progress_percentage: number + completed_count: number + total_count: number + is_completed: boolean + } } export interface ProblemSetList { @@ -213,6 +220,7 @@ export interface ProblemSetList { total_count: number is_completed: boolean } + badges: ProblemSetBadge[] } export interface ProblemSetProblem { @@ -594,3 +602,42 @@ export interface DetailsData { } export type Grade = "S" | "A" | "B" | "C" + +// 题单提交记录相关类型 +export interface ProblemSetSubmission { + id: number + problem: number + problem_id: number + problem_title: string + submission: string + result: number + result_text: string + score: number + language: string + code_length: number + execution_time: number + memory_usage: number + submit_time: string +} + +export interface ProblemSetStatistics { + total_submissions: number + accepted_submissions: number + acceptance_rate: number + problem_stats: { + [problemId: string]: { + problem_title: string + total_submissions: number + accepted_submissions: number + is_completed: boolean + } + } + language_stats: { [language: string]: number } + result_stats: { [result: number]: number } + progress: { + completed_problems_count: number + total_problems_count: number + progress_percentage: number + total_score: number + } +}