diff --git a/apps/web/src/admin/ai/list.vue b/apps/web/src/admin/ai/list.vue index e6d62b4..c46b858 100644 --- a/apps/web/src/admin/ai/list.vue +++ b/apps/web/src/admin/ai/list.vue @@ -21,7 +21,13 @@ const theme = useThemeVars() const reports = ref([]) const total = ref(0) -const query = reactive({ limit: 10, page: 1, username: "" }) +// 前台「智能分析」看某个学生时的「AI 报告」按钮带着 ?username= 过来,直接筛到他 +const route = useRoute() +const query = reactive({ + limit: 10, + page: 1, + username: typeof route.query.username === "string" ? route.query.username : "", +}) const selectedId = ref(null) const detail = ref(null) diff --git a/apps/web/src/oj/achievement/index.vue b/apps/web/src/oj/achievement/index.vue index e482705..25a2369 100644 --- a/apps/web/src/oj/achievement/index.vue +++ b/apps/web/src/oj/achievement/index.vue @@ -4,6 +4,7 @@ import { getAchievements } from "oj/achievement/api" import { getUserBadges } from "oj/api" import { useBreakpoints } from "shared/composables/breakpoints" import UserName from "shared/components/UserName.vue" +import AdminLink from "shared/components/AdminLink.vue" import { useUserStore } from "shared/store/user" import { RARITY_COLOR, RARITY_LABEL } from "utils/constants" import type { AchievementList, AchievementRarity, UserBadge } from "utils/types" @@ -83,6 +84,9 @@ watch(name, load) 看我的 › + @@ -168,6 +172,16 @@ h2 { font-variant-numeric: tabular-nums; } +/* 两个都在时「看我的」占着 auto,按钮跟在它后面 */ +.admin-link { + margin-left: auto; + align-self: center; +} + +.mine + .admin-link { + margin-left: 0; +} + .mine { margin-left: auto; font-size: var(--oj-fs-sec); diff --git a/apps/web/src/oj/ai/analysis.vue b/apps/web/src/oj/ai/analysis.vue index a8e366b..4589337 100644 --- a/apps/web/src/oj/ai/analysis.vue +++ b/apps/web/src/oj/ai/analysis.vue @@ -8,6 +8,7 @@ import { import { useRouteQuery } from "@vueuse/router" import { useThemeVars } from "naive-ui" import { useBreakpoints } from "shared/composables/breakpoints" +import AdminLink from "shared/components/AdminLink.vue" import { useUserStore } from "shared/store/user" import UserName from "shared/components/UserName.vue" import { classLabel } from "oj/submission/utils" @@ -137,6 +138,13 @@ function goStudent(username: string | null | undefined) { > 看的提交 › + + AI 报告 + diff --git a/apps/web/src/oj/announcement/list.vue b/apps/web/src/oj/announcement/list.vue index a9571af..75c2ea4 100644 --- a/apps/web/src/oj/announcement/list.vue +++ b/apps/web/src/oj/announcement/list.vue @@ -1,8 +1,10 @@ + + diff --git a/apps/web/src/shared/store/user.ts b/apps/web/src/shared/store/user.ts index f99d6d9..8b34b1b 100644 --- a/apps/web/src/shared/store/user.ts +++ b/apps/web/src/shared/store/user.ts @@ -49,6 +49,22 @@ export const useUserStore = defineStore("user", () => { const canToggleDemoMode = computed(() => realIsSuperAdmin.value) + /** + * 前台「编辑题目」入口给谁看,对齐后端 admin/problem.ts 的 canEdit: + * 公开题看题目的创建者(或题目权限是「全部」),比赛题看比赛的创建者。 + * 题目页拿不到比赛的创建者,比赛题就按题目的创建者估 —— 比赛题基本是出题老师 + * 自己建或克隆的;估错了后台会拦,只是多一个点进去 404 的入口。 + */ + function canEditProblem(problem: { contestId: number | null; createdBy: { id: number } }) { + if (isSuperAdmin.value) return true + const mine = problem.createdBy.id === user.value?.id + if (problem.contestId !== null) return isTeacherOrAbove.value && mine + return ( + hasProblemPermission.value && + (mine || user.value?.problemPermission === PROBLEM_PERMISSION.ALL) + ) + } + function toggleDemoMode() { demoSwitch.value = !demoSwitch.value storage.set(STORAGE_KEY.DEMO_MODE, demoSwitch.value) @@ -124,6 +140,7 @@ export const useUserStore = defineStore("user", () => { hasProblemPermission, demoMode, canToggleDemoMode, + canEditProblem, toggleDemoMode, isAuthed, showSubmissions,