From a9278589c92885735e19d19011b20e0a3737f4f4 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Tue, 4 Aug 2026 06:59:27 -0600 Subject: [PATCH] =?UTF-8?q?feat(achievement):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=90=8E=E5=8F=B0=E6=88=90=E5=B0=B1=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 指标下拉框由后端 METRIC_REGISTRY 动态提供,选中后显示该指标的说明。 列表显示已解锁人数——阈值配错时学生永远拿不到也永远不会来问, 这个计数器是唯一的仪表盘。 API 追加到 src/admin/api.ts(仓库的 admin 接口集中在这一个文件里), 路由 meta 用 requiresSuperAdmin 以匹配后端的 super_admin_required。 Co-Authored-By: Claude Opus 5 --- .../components/AchievementModal.vue | 160 ++++++++++++++++++ src/admin/achievement/list.vue | 135 +++++++++++++++ src/admin/api.ts | 45 +++++ src/routes.ts | 8 + src/shared/layout/admin.vue | 9 + 5 files changed, 357 insertions(+) create mode 100644 src/admin/achievement/components/AchievementModal.vue create mode 100644 src/admin/achievement/list.vue diff --git a/src/admin/achievement/components/AchievementModal.vue b/src/admin/achievement/components/AchievementModal.vue new file mode 100644 index 0000000..bab15b1 --- /dev/null +++ b/src/admin/achievement/components/AchievementModal.vue @@ -0,0 +1,160 @@ + + + diff --git a/src/admin/achievement/list.vue b/src/admin/achievement/list.vue new file mode 100644 index 0000000..6c1f2f7 --- /dev/null +++ b/src/admin/achievement/list.vue @@ -0,0 +1,135 @@ + + + diff --git a/src/admin/api.ts b/src/admin/api.ts index 2d8f7c8..fe34e5b 100644 --- a/src/admin/api.ts +++ b/src/admin/api.ts @@ -531,3 +531,48 @@ export function pinAIReport(id: number) { export function getPinnedAIReports() { return http.get("admin/ai/reports", { params: { pinned_only: "true" } }) } + +// ==================== 成就 ==================== + +export interface AdminAchievement { + id: number + name: string + description: string + icon: string + rarity: string + hidden: boolean + metric: string + metric_name: string + operator: "gte" | "lte" + threshold: number + visible: boolean + unlock_count: number + order: number + create_time: string +} + +export interface MetricOption { + key: string + name: string + help_text: string +} + +export function getAdminAchievements() { + return http.get("admin/achievement") +} + +export function getMetricOptions() { + return http.get("admin/achievement/metrics") +} + +export function createAchievement(data: Partial) { + return http.post("admin/achievement", data) +} + +export function updateAchievement(data: Partial) { + return http.put("admin/achievement", data) +} + +export function deleteAchievement(id: number) { + return http.delete("admin/achievement", { params: { id } }) +} diff --git a/src/routes.ts b/src/routes.ts index 067f28e..8f8f383 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -301,6 +301,14 @@ export const admins: RouteRecordRaw = { component: () => import("admin/problemset/list.vue"), meta: { requiresTeacherAdmin: true }, }, + { + path: "achievement/list", + name: "admin achievement list", + // 后端 admin 接口是 super_admin_required,这里必须跟着用 + // requiresSuperAdmin,否则教师能进页面但每个请求都 403 + component: () => import("admin/achievement/list.vue"), + meta: { requiresSuperAdmin: true }, + }, { path: "problemset/create", name: "admin problemset create", diff --git a/src/shared/layout/admin.vue b/src/shared/layout/admin.vue index 1923e46..bf9d9e0 100644 --- a/src/shared/layout/admin.vue +++ b/src/shared/layout/admin.vue @@ -141,6 +141,15 @@ const options = computed(() => { ), key: "admin tutorial list", }, + { + label: () => + h( + RouterLink, + { to: "/admin/achievement/list" }, + { default: () => "成就" }, + ), + key: "admin achievement list", + }, { label: () => h(