信封是 Django 时代的形状:拦截器手工造一个**恒为 null** 的 error 字段,再把
真正的载荷塞进 data。后端 http.ts 的 success 其实只返回 { data },那个 error
从头到尾没人用 —— 全站成功路径读 res.error 的只有 admin/api.ts 的
resetPassword 一处,而它自己就是个把信封拆开再重新包一遍的 shim。
代价是每个调用点都要 .data 一次:47 个组件、3 个 api 层文件、200 多处。
现在拦截器直接返回 response.data.data,ApiResponse<T> 退化成 T,文件末尾那句
`as unknown as Api2Client` 的类型谎言也少了一层。失败路径不动,仍然 reject
`{ error: 错误码, data: 文案 }` —— 和成功路径不对称是故意的,成功没有错误码
可言,接口注释里写清楚了。
顺带把 api2 改回 api:utils/ 下早就没有 api.ts 了,"2" 是迁移期用来和旧
client 区分的,现在只剩下让人多想一秒的作用。
## 怎么改的
**没有全局 sed。** 先把客户端的返回类型从 Promise<ApiResponse<T>> 改成
Promise<T>,让 vue-tsc 把每一处报出来(210 条),再按它给的 file:line:col
精确删 `.data`(192 处),剩下的手工处理:
- 6 处 `const { data } = await ...` 解构 → `const data = await ...`
- 3 个 api 层函数(getProfile / getProblem / getSubmission)自己手工造信封,
改成直接返回值;getProfile 的返回类型跟着从 ApiResponse<Profile|null>
变成 Profile|null
**类型检查抓不到的,人工把剩下的每一处 `.data` 过了一遍** —— 载荷本身带
data 字段、或者载荷是索引签名时,`res.data` 照样过类型。这一遍捞出三条真 bug:
- `getTutorialList` 的载荷是 `{ [key: string]: TutorialListItem[] }`(按
python / c 分组)。索引签名让 `res.data` 编译通过、运行时是 undefined ——
改完信封之后教程列表会**两个 tab 全空且不报错**。实跑确认过修好了。
- `createExercise` / `updateExercise` 返回 `res.data`,而 Exercise 自己有
data 字段(练习内容)。两个调用方都不看返回值,所以类型和运行时都不响。
- `getSimilarProblems` 的 `.then(r => ({ ...r, data: r.data.map(...) }))`
删掉 .data 之后变成往对象里摊一个数组,能跑但形状是错的。
另外两处是**对的**,加了注释免得下次被"顺手清理"掉:
StatisticsPanel 的 `res.data` 是契约 submissionStatisticsSchema 自己的 data
字段(每个学生一行);download.ts 是独立 axios 实例,`res.data` 是 axios 的
响应体(zip 二进制,不走信封)。
## 验证
tsc(apps/api) 0 error、check:routes 168 条无遮蔽、vue-tsc 0 error、vite build
通过。**因为这改动碰的是每一个请求,静态检查不够,起了全套服务用浏览器实跑:**
- oj 侧 12 个页面 + 后台 13 个页面逐个打开,断言没有重定向、console 无报错。
- 关键页面进一步断言渲染出了真数据(后台用户列表 3 行、题目列表 10 行、
站点配置表单三个输入框有值、教程列表分组正确)。
- 三条写路径实打:重置密码(库里 student123 → 531554,表格当场刷新)、
公告可见性开关(走 getAnnouncement + editAnnouncement,就是手改解构那处,
库里 visible t → f)、提交代码(POST → 判题机真跑出 -2 → 提交列表和详情页
都正确渲染状态、语言、代码)。
- /rank 有一条 `{error: "class-missing"}` 的未捕获 reject,stash 掉本次改动
复现同样报错,**是既有问题**,不在本次范围内。
本地 dev 库为了打通后台测试改了三处,都只影响本机:devadmin 补了 email 和
user_profile 行(原来缺这两样,getProfile 报 profile-not-found,AUTHED 存不
进去,所有 /admin 路由被守卫弹回首页)、密码重置成 devpass123。冒烟用的教程/
公告/提交三条测试数据已删干净,题目和用户的提交计数也回滚了。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -56,7 +56,7 @@ watch(
|
||||
if (!show) return
|
||||
if (!metrics.value.length) {
|
||||
const res = await getMetricOptions()
|
||||
metrics.value = res.data
|
||||
metrics.value = res
|
||||
}
|
||||
if (props.editing) {
|
||||
form.value = { ...emptyForm(), ...props.editing }
|
||||
|
||||
@@ -30,7 +30,7 @@ async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getAdminAchievements()
|
||||
list.value = res.data
|
||||
list.value = res
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ const columns: DataTableColumn<ReportItem>[] = [
|
||||
|
||||
async function loadPinnedReports() {
|
||||
const res = await getPinnedAIReports()
|
||||
pinnedReports.value = res.data.results
|
||||
pinnedReports.value = res.results
|
||||
}
|
||||
|
||||
async function togglePin(row: ReportItem) {
|
||||
@@ -155,8 +155,8 @@ async function togglePin(row: ReportItem) {
|
||||
async function listReports() {
|
||||
const offset = (query.page - 1) * query.limit
|
||||
const res = await getAIReportList(offset, query.limit, query.username)
|
||||
reports.value = res.data.results
|
||||
total.value = res.data.total
|
||||
reports.value = res.results
|
||||
total.value = res.total
|
||||
}
|
||||
|
||||
async function openDetail(id: number) {
|
||||
@@ -165,7 +165,7 @@ async function openDetail(id: number) {
|
||||
detail.value = null
|
||||
try {
|
||||
const res = await getAIReportDetail(id)
|
||||
detail.value = res.data
|
||||
detail.value = res
|
||||
} finally {
|
||||
loadingDetail.value = false
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@ async function init() {
|
||||
const res = await getAnnouncement(id)
|
||||
toggleReady(true)
|
||||
announcement.id = id
|
||||
announcement.title = res.data.title
|
||||
announcement.content = res.data.content
|
||||
announcement.visible = res.data.visible
|
||||
announcement.tag = res.data.tag
|
||||
announcement.top = res.data.top
|
||||
announcement.title = res.title
|
||||
announcement.content = res.content
|
||||
announcement.visible = res.visible
|
||||
announcement.tag = res.tag
|
||||
announcement.top = res.top
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
|
||||
@@ -67,7 +67,7 @@ const columns: DataTableColumn<AnnouncementListItem>[] = [
|
||||
async function toggleVisible(announcement: AnnouncementListItem) {
|
||||
const next = !announcement.visible
|
||||
try {
|
||||
const { data: full } = await getAnnouncement(announcement.id)
|
||||
const full = await getAnnouncement(announcement.id)
|
||||
await editAnnouncement({
|
||||
id: full.id,
|
||||
title: full.title,
|
||||
@@ -85,8 +85,8 @@ async function toggleVisible(announcement: AnnouncementListItem) {
|
||||
async function listAnnouncements() {
|
||||
const offset = (query.page - 1) * query.limit
|
||||
const res = await getAnnouncementList(offset, query.limit)
|
||||
announcements.value = res.data.results
|
||||
total.value = res.data.total
|
||||
announcements.value = res.results
|
||||
total.value = res.total
|
||||
}
|
||||
|
||||
onMounted(listAnnouncements)
|
||||
|
||||
+97
-109
@@ -1,4 +1,4 @@
|
||||
import api2 from "utils/api2"
|
||||
import api from "utils/api"
|
||||
import { toProblemListItem } from "admin/transforms"
|
||||
import type {
|
||||
AcTrend,
|
||||
@@ -42,11 +42,11 @@ import type {
|
||||
} from "utils/types"
|
||||
|
||||
export function getBaseInfo() {
|
||||
return api2.get<DashboardInfo>("admin/dashboard")
|
||||
return api.get<DashboardInfo>("admin/dashboard")
|
||||
}
|
||||
|
||||
export function randomUser10(classroom: string) {
|
||||
return api2.get<string[]>("admin/random-usernames", {
|
||||
return api.get<string[]>("admin/random-usernames", {
|
||||
params: { classroom },
|
||||
})
|
||||
}
|
||||
@@ -62,69 +62,69 @@ export async function getProblemList(
|
||||
const endpoint = contestID
|
||||
? `admin/contests/${contestID}/problems`
|
||||
: "admin/problems"
|
||||
const res = await api2.get<AdminProblemList>(endpoint, {
|
||||
const res = await api.get<AdminProblemList>(endpoint, {
|
||||
params: { offset, limit, keyword, author, tagId },
|
||||
})
|
||||
return {
|
||||
results: res.data.results.map(toProblemListItem),
|
||||
total: res.data.total,
|
||||
results: res.results.map(toProblemListItem),
|
||||
total: res.total,
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteProblem(id: number) {
|
||||
return api2.delete(`admin/problems/${id}`)
|
||||
return api.delete(`admin/problems/${id}`)
|
||||
}
|
||||
|
||||
// 比赛题与公开题共用一条删除路由,比赛由后端从题目推导
|
||||
export function deleteContestProblem(id: number) {
|
||||
return api2.delete(`admin/problems/${id}`)
|
||||
return api.delete(`admin/problems/${id}`)
|
||||
}
|
||||
|
||||
export function editProblem(problem: AdminProblem | BlankProblem) {
|
||||
return api2.put<AdminProblem>(
|
||||
return api.put<AdminProblem>(
|
||||
`admin/problems/${(problem as AdminProblem).id}`,
|
||||
toProblemBody(problem),
|
||||
)
|
||||
}
|
||||
|
||||
export function toggleProblemVisible(problemID: number) {
|
||||
return api2.put<{ visible: boolean }>(
|
||||
return api.put<{ visible: boolean }>(
|
||||
`admin/problems/${problemID}/visibility`,
|
||||
)
|
||||
}
|
||||
|
||||
export function generateFlowchartFromPythonCode(python: string) {
|
||||
return api2.post<{ flowchart: string }>("admin/problems/flowchart", {
|
||||
return api.post<{ flowchart: string }>("admin/problems/flowchart", {
|
||||
python,
|
||||
})
|
||||
}
|
||||
|
||||
export function editContestProblem(problem: AdminProblem | BlankProblem) {
|
||||
return api2.put<AdminProblem>(
|
||||
return api.put<AdminProblem>(
|
||||
`admin/problems/${(problem as AdminProblem).id}`,
|
||||
toProblemBody(problem),
|
||||
)
|
||||
}
|
||||
|
||||
export function getProblem(id: string | number) {
|
||||
return api2.get<AdminProblem>(`admin/problems/${id}`)
|
||||
return api.get<AdminProblem>(`admin/problems/${id}`)
|
||||
}
|
||||
|
||||
export function getContestProblem(id: number) {
|
||||
return api2.get<AdminProblem>(`admin/problems/${id}`)
|
||||
return api.get<AdminProblem>(`admin/problems/${id}`)
|
||||
}
|
||||
|
||||
// 标签管理
|
||||
export function getTagAdminList(keyword = "") {
|
||||
return api2.get<AdminTag[]>("admin/problem-tags", { params: { keyword } })
|
||||
return api.get<AdminTag[]>("admin/problem-tags", { params: { keyword } })
|
||||
}
|
||||
|
||||
export function renameTag(id: number, name: string) {
|
||||
return api2.put<RenameTagResponse>(`admin/problem-tags/${id}`, { name })
|
||||
return api.put<RenameTagResponse>(`admin/problem-tags/${id}`, { name })
|
||||
}
|
||||
|
||||
export function deleteTag(id: number) {
|
||||
return api2.delete(`admin/problem-tags/${id}`)
|
||||
return api.delete(`admin/problem-tags/${id}`)
|
||||
}
|
||||
|
||||
export function batchTagProblems(
|
||||
@@ -132,7 +132,7 @@ export function batchTagProblems(
|
||||
tagNames: string[],
|
||||
action: "add" | "remove",
|
||||
) {
|
||||
return api2.post<BatchProblemTagResponse>("admin/problems/batch-tag", {
|
||||
return api.post<BatchProblemTagResponse>("admin/problems/batch-tag", {
|
||||
problemIds,
|
||||
tagNames,
|
||||
action,
|
||||
@@ -141,7 +141,7 @@ export function batchTagProblems(
|
||||
|
||||
// 用户排名(后台版,无 100 名上限;公开榜单是 oj/api.ts 的 getRank)
|
||||
export function getAdminUserRank(offset: number, limit: number, keyword: string) {
|
||||
return api2.get<AdminUserRank>("admin/rankings/users", {
|
||||
return api.get<AdminUserRank>("admin/rankings/users", {
|
||||
params: { offset, limit, keyword },
|
||||
})
|
||||
}
|
||||
@@ -154,7 +154,7 @@ export function getUserList(
|
||||
keyword: string,
|
||||
orderBy = "",
|
||||
) {
|
||||
return api2.get<AdminUserList>("admin/users", {
|
||||
return api.get<AdminUserList>("admin/users", {
|
||||
// 旧接口的 order_by 只有 "-last_login" 一个取值
|
||||
params: {
|
||||
offset,
|
||||
@@ -168,7 +168,7 @@ export function getUserList(
|
||||
|
||||
// 编辑用户
|
||||
export function editUser(user: User) {
|
||||
return api2.put<AdminUser>(`admin/users/${user.id}`, {
|
||||
return api.put<AdminUser>(`admin/users/${user.id}`, {
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
adminType: user.adminType,
|
||||
@@ -180,27 +180,26 @@ export function editUser(user: User) {
|
||||
})
|
||||
}
|
||||
|
||||
// 重置用户密码。调用方直接用 res.data 当密码字符串(旧后端返回的就是裸字符串),
|
||||
// 新后端返回 { password },在这里解包,组件不动
|
||||
// 重置用户密码,返回新密码
|
||||
export async function resetPassword(userID: number) {
|
||||
const res = await api2.post<{ password: string }>(
|
||||
const res = await api.post<{ password: string }>(
|
||||
`admin/users/${userID}/reset-password`,
|
||||
)
|
||||
return { error: res.error, data: res.data.password }
|
||||
return res.password
|
||||
}
|
||||
|
||||
// 导入用户
|
||||
export function importUsers(users: string[][]) {
|
||||
return api2.post("admin/users", { users })
|
||||
return api.post("admin/users", { users })
|
||||
}
|
||||
|
||||
// 批量删除用户
|
||||
export function deleteUsers(userIDs: number[]) {
|
||||
return api2.delete("admin/users", { data: { ids: userIDs } })
|
||||
return api.delete("admin/users", { data: { ids: userIDs } })
|
||||
}
|
||||
|
||||
export function getContestList(offset = 0, limit = 10, keyword: string) {
|
||||
return api2.get<AdminContestList>("admin/contests", {
|
||||
return api.get<AdminContestList>("admin/contests", {
|
||||
params: { offset, limit, keyword },
|
||||
})
|
||||
}
|
||||
@@ -209,14 +208,14 @@ export function getContestList(offset = 0, limit = 10, keyword: string) {
|
||||
export async function uploadImage(file: File): Promise<string> {
|
||||
const form = new window.FormData()
|
||||
form.append("image", file)
|
||||
const res = await api2.post<{
|
||||
const res = await api.post<{
|
||||
success: boolean
|
||||
filePath: string
|
||||
msg: string
|
||||
}>("admin/upload-image", form, {
|
||||
headers: { "content-type": "multipart/form-data" },
|
||||
})
|
||||
return res.data.success ? res.data.filePath : ""
|
||||
return res.success ? res.filePath : ""
|
||||
}
|
||||
|
||||
// 上传测试用例;SQL 题的压缩包是 1.sql..N.sql(每个文件一个测试点的建表+数据脚本)
|
||||
@@ -226,7 +225,7 @@ export function uploadTestcases(file: File, options: { sql?: boolean } = {}) {
|
||||
if (options.sql) {
|
||||
form.append("sql", "1")
|
||||
}
|
||||
return api2.post<TestcaseUploadedReturns>("admin/test-cases", form, {
|
||||
return api.post<TestcaseUploadedReturns>("admin/test-cases", form, {
|
||||
headers: { "content-type": "multipart/form-data" },
|
||||
})
|
||||
}
|
||||
@@ -237,12 +236,12 @@ export function previewSQLTestcase(data: {
|
||||
refSql: string
|
||||
mode: "query" | "modify"
|
||||
}) {
|
||||
return api2.post<SqlDisplay>("admin/sql-test-cases/preview", data)
|
||||
return api.post<SqlDisplay>("admin/sql-test-cases/preview", data)
|
||||
}
|
||||
|
||||
// 回显已上传的 SQL 测试点脚本内容(按 1.sql, 2.sql... 排序)
|
||||
export function getSQLTestcaseScripts(problemId: number) {
|
||||
return api2.get<SqlTestCaseScript[]>(
|
||||
return api.get<SqlTestCaseScript[]>(
|
||||
`admin/problems/${problemId}/sql-scripts`,
|
||||
)
|
||||
}
|
||||
@@ -252,7 +251,7 @@ export function generateSQLTestcase(data: {
|
||||
refSql: string
|
||||
mode: "query" | "modify"
|
||||
}) {
|
||||
return api2.post<GenerateSqlTestCaseResponse>(
|
||||
return api.post<GenerateSqlTestCaseResponse>(
|
||||
"admin/sql-test-cases/generate",
|
||||
data,
|
||||
)
|
||||
@@ -292,12 +291,12 @@ function toProblemBody(problem: AdminProblem | BlankProblem) {
|
||||
}
|
||||
|
||||
export function createProblem(problem: BlankProblem) {
|
||||
return api2.post<AdminProblem>("admin/problems", toProblemBody(problem))
|
||||
return api.post<AdminProblem>("admin/problems", toProblemBody(problem))
|
||||
}
|
||||
|
||||
export function createContestProblem(problem: BlankProblem) {
|
||||
// contestId 由 detail.vue 在提交前写进 problem 对象
|
||||
return api2.post<AdminProblem>(
|
||||
return api.post<AdminProblem>(
|
||||
`admin/contests/${problem.contestId}/problems`,
|
||||
toProblemBody(problem),
|
||||
)
|
||||
@@ -318,22 +317,22 @@ function toContestBody(contest: Contest | BlankContest) {
|
||||
}
|
||||
|
||||
export function createContest(contest: BlankContest) {
|
||||
return api2.post<Contest>("admin/contests", toContestBody(contest))
|
||||
return api.post<Contest>("admin/contests", toContestBody(contest))
|
||||
}
|
||||
|
||||
export function editContest(contest: Contest | BlankContest) {
|
||||
return api2.put<Contest>(
|
||||
return api.put<Contest>(
|
||||
`admin/contests/${(contest as Contest).id}`,
|
||||
toContestBody(contest),
|
||||
)
|
||||
}
|
||||
|
||||
export function cloneContest(contestId: number) {
|
||||
return api2.post<Contest>(`admin/contests/${contestId}/clone`)
|
||||
return api.post<Contest>(`admin/contests/${contestId}/clone`)
|
||||
}
|
||||
|
||||
export function getContest(id: string) {
|
||||
return api2.get<Contest>(`admin/contests/${id}`)
|
||||
return api.get<Contest>(`admin/contests/${id}`)
|
||||
}
|
||||
|
||||
export function addProblemForContest(
|
||||
@@ -341,71 +340,67 @@ export function addProblemForContest(
|
||||
problemID: number,
|
||||
displayID: string,
|
||||
) {
|
||||
return api2.post<AdminProblem>(
|
||||
return api.post<AdminProblem>(
|
||||
`admin/contests/${contestID}/problems/from-public`,
|
||||
{ problemId: problemID, displayId: displayID },
|
||||
)
|
||||
}
|
||||
|
||||
export function getWebsite() {
|
||||
return api2.get<WebsiteConfig>("admin/website")
|
||||
return api.get<WebsiteConfig>("admin/website")
|
||||
}
|
||||
|
||||
export function editWebsite(data: WebsiteConfig) {
|
||||
return api2.post<WebsiteConfig>("admin/website", data)
|
||||
return api.post<WebsiteConfig>("admin/website", data)
|
||||
}
|
||||
|
||||
export function listInvalidTestcases() {
|
||||
return api2.get<OrphanTestCase[]>("admin/orphan-test-cases")
|
||||
return api.get<OrphanTestCase[]>("admin/orphan-test-cases")
|
||||
}
|
||||
|
||||
export function pruneInvalidTestcases(id?: string) {
|
||||
return api2.delete("admin/orphan-test-cases", { params: { id } })
|
||||
return api.delete("admin/orphan-test-cases", { params: { id } })
|
||||
}
|
||||
|
||||
export function getJudgeServer() {
|
||||
return api2.get<JudgeServerList>("admin/judge-servers")
|
||||
return api.get<JudgeServerList>("admin/judge-servers")
|
||||
}
|
||||
|
||||
export function deleteJudgeServer(hostname: string) {
|
||||
return api2.delete(`admin/judge-servers/${encodeURIComponent(hostname)}`)
|
||||
return api.delete(`admin/judge-servers/${encodeURIComponent(hostname)}`)
|
||||
}
|
||||
|
||||
export function getAnnouncementList(offset = 0, limit = 10) {
|
||||
return api2.get<{ results: AnnouncementListItem[]; total: number }>(
|
||||
return api.get<{ results: AnnouncementListItem[]; total: number }>(
|
||||
"admin/announcements",
|
||||
{ params: { offset, limit } },
|
||||
)
|
||||
}
|
||||
|
||||
export function getAnnouncement(id: number) {
|
||||
return api2.get<Announcement>(`admin/announcements/${id}`)
|
||||
return api.get<Announcement>(`admin/announcements/${id}`)
|
||||
}
|
||||
|
||||
export function deleteAnnouncement(id: number) {
|
||||
return api2.delete(`admin/announcements/${id}`)
|
||||
return api.delete(`admin/announcements/${id}`)
|
||||
}
|
||||
|
||||
export function editAnnouncement(announcement: AnnouncementEdit) {
|
||||
const { id, ...body } = announcement
|
||||
return api2.put<Announcement>(`admin/announcements/${id}`, body)
|
||||
return api.put<Announcement>(`admin/announcements/${id}`, body)
|
||||
}
|
||||
|
||||
export function createAnnouncement(announcement: AnnouncementEdit) {
|
||||
const { id: _id, ...body } = announcement
|
||||
return api2.post<Announcement>("admin/announcements", body)
|
||||
return api.post<Announcement>("admin/announcements", body)
|
||||
}
|
||||
|
||||
export async function getTutorialList() {
|
||||
const res = await api2.get<{ [key: string]: TutorialListItem[] }>(
|
||||
"admin/tutorials",
|
||||
)
|
||||
return res.data
|
||||
export function getTutorialList() {
|
||||
return api.get<{ [key: string]: TutorialListItem[] }>("admin/tutorials")
|
||||
}
|
||||
|
||||
export async function getTutorial(id: number) {
|
||||
const res = await api2.get<Tutorial>(`admin/tutorials/${id}`)
|
||||
return res.data
|
||||
export function getTutorial(id: number) {
|
||||
return api.get<Tutorial>(`admin/tutorials/${id}`)
|
||||
}
|
||||
|
||||
function toTutorialBody(data: Partial<Tutorial>) {
|
||||
@@ -419,32 +414,27 @@ function toTutorialBody(data: Partial<Tutorial>) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function createTutorial(data: Partial<Tutorial>) {
|
||||
const res = await api2.post<Tutorial>("admin/tutorials", toTutorialBody(data))
|
||||
return res.data
|
||||
export function createTutorial(data: Partial<Tutorial>) {
|
||||
return api.post<Tutorial>("admin/tutorials", toTutorialBody(data))
|
||||
}
|
||||
|
||||
export async function updateTutorial(data: Partial<Tutorial>) {
|
||||
const res = await api2.put<Tutorial>(
|
||||
export function updateTutorial(data: Partial<Tutorial>) {
|
||||
return api.put<Tutorial>(
|
||||
`admin/tutorials/${data.id}`,
|
||||
toTutorialBody(data),
|
||||
)
|
||||
return res.data
|
||||
}
|
||||
|
||||
export function deleteTutorial(id: number) {
|
||||
return api2.delete(`admin/tutorials/${id}`)
|
||||
return api.delete(`admin/tutorials/${id}`)
|
||||
}
|
||||
|
||||
export function setTutorialVisibility(id: number, isPublic: boolean) {
|
||||
return api2.put<Tutorial>(`admin/tutorials/${id}/visibility`, { isPublic })
|
||||
return api.put<Tutorial>(`admin/tutorials/${id}/visibility`, { isPublic })
|
||||
}
|
||||
|
||||
export async function getAdminExercises(tutorialId: number) {
|
||||
const res = await api2.get<Exercise[]>(
|
||||
`admin/tutorials/${tutorialId}/exercises`,
|
||||
)
|
||||
return res.data
|
||||
export function getAdminExercises(tutorialId: number) {
|
||||
return api.get<Exercise[]>(`admin/tutorials/${tutorialId}/exercises`)
|
||||
}
|
||||
|
||||
export async function createExercise(data: {
|
||||
@@ -453,8 +443,7 @@ export async function createExercise(data: {
|
||||
data: object
|
||||
order: number
|
||||
}) {
|
||||
const res = await api2.post<Exercise>("admin/exercises", data)
|
||||
return res.data
|
||||
return api.post<Exercise>("admin/exercises", data)
|
||||
}
|
||||
|
||||
export async function updateExercise(data: {
|
||||
@@ -463,21 +452,20 @@ export async function updateExercise(data: {
|
||||
data: object
|
||||
order: number
|
||||
}) {
|
||||
const res = await api2.put<Exercise>(`admin/exercises/${data.id}`, {
|
||||
return api.put<Exercise>(`admin/exercises/${data.id}`, {
|
||||
type: data.type,
|
||||
data: data.data,
|
||||
order: data.order,
|
||||
})
|
||||
return res.data
|
||||
}
|
||||
|
||||
export function deleteExercise(id: number) {
|
||||
return api2.delete(`admin/exercises/${id}`)
|
||||
return api.delete(`admin/exercises/${id}`)
|
||||
}
|
||||
|
||||
// 将竞赛题目转为公开题目
|
||||
export function makeProblemPublic(id: number, displayId: string) {
|
||||
return api2.post<AdminProblem>(`admin/problems/${id}/make-public`, {
|
||||
return api.post<AdminProblem>(`admin/problems/${id}/make-public`, {
|
||||
displayId,
|
||||
})
|
||||
}
|
||||
@@ -486,7 +474,7 @@ export function makeProblemPublic(id: number, displayId: string) {
|
||||
export function getACMHelperList(contestId: number) {
|
||||
// acInfo 在契约里是 Record<string, unknown>(acm_contest_rank 的 JSONB 原文),
|
||||
// 组件侧按 SubmissionInfo 读,收窄放在这里
|
||||
return api2.get<
|
||||
return api.get<
|
||||
Array<Omit<AcmHelperItem, "acInfo"> & { acInfo: SubmissionInfo }>
|
||||
>(`admin/contests/${contestId}/acm-helper`)
|
||||
}
|
||||
@@ -497,7 +485,7 @@ export function updateACMHelperChecked(
|
||||
problem_id: string,
|
||||
checked: boolean,
|
||||
) {
|
||||
return api2.put(`admin/contests/${contest_id}/acm-helper`, {
|
||||
return api.put(`admin/contests/${contest_id}/acm-helper`, {
|
||||
rankId: rank_id,
|
||||
problemId: problem_id,
|
||||
checked,
|
||||
@@ -512,13 +500,13 @@ export function getProblemSetList(
|
||||
difficulty = "",
|
||||
status = "",
|
||||
) {
|
||||
return api2.get<ProblemSetList>("admin/problem-sets", {
|
||||
return api.get<ProblemSetList>("admin/problem-sets", {
|
||||
params: { offset, limit, keyword, difficulty, status },
|
||||
})
|
||||
}
|
||||
|
||||
export function getProblemSetDetail(id: number) {
|
||||
return api2.get<ProblemSet>(`admin/problem-sets/${id}`)
|
||||
return api.get<ProblemSet>(`admin/problem-sets/${id}`)
|
||||
}
|
||||
|
||||
interface ProblemSetBody {
|
||||
@@ -543,31 +531,31 @@ function toProblemSetBody(data: ProblemSetBody) {
|
||||
}
|
||||
|
||||
export function createProblemSet(data: ProblemSetBody) {
|
||||
return api2.post<ProblemSet>("admin/problem-sets", toProblemSetBody(data))
|
||||
return api.post<ProblemSet>("admin/problem-sets", toProblemSetBody(data))
|
||||
}
|
||||
|
||||
export function editProblemSet(data: ProblemSetBody & { id: number }) {
|
||||
return api2.put<ProblemSet>(
|
||||
return api.put<ProblemSet>(
|
||||
`admin/problem-sets/${data.id}`,
|
||||
toProblemSetBody(data),
|
||||
)
|
||||
}
|
||||
|
||||
export function deleteProblemSet(id: number) {
|
||||
return api2.delete(`admin/problem-sets/${id}`)
|
||||
return api.delete(`admin/problem-sets/${id}`)
|
||||
}
|
||||
|
||||
export function toggleProblemSetVisible(id: number) {
|
||||
return api2.put<ProblemSet>(`admin/problem-sets/${id}/visibility`)
|
||||
return api.put<ProblemSet>(`admin/problem-sets/${id}/visibility`)
|
||||
}
|
||||
|
||||
export function updateProblemSetStatus(id: number, status: string) {
|
||||
return api2.put<ProblemSet>(`admin/problem-sets/${id}/status`, { status })
|
||||
return api.put<ProblemSet>(`admin/problem-sets/${id}/status`, { status })
|
||||
}
|
||||
|
||||
// 题单题目管理 API
|
||||
export function getProblemSetProblems(problemSetId: number) {
|
||||
return api2.get<ProblemSetProblem[]>(
|
||||
return api.get<ProblemSetProblem[]>(
|
||||
`admin/problem-sets/${problemSetId}/problems`,
|
||||
)
|
||||
}
|
||||
@@ -582,7 +570,7 @@ export function addProblemToSet(
|
||||
hint?: string
|
||||
},
|
||||
) {
|
||||
return api2.post(`admin/problem-sets/${problemSetId}/problems`, {
|
||||
return api.post(`admin/problem-sets/${problemSetId}/problems`, {
|
||||
problemId: data.problemId,
|
||||
order: data.order ?? 0,
|
||||
isRequired: data.isRequired ?? true,
|
||||
@@ -601,7 +589,7 @@ export function editProblemInSet(
|
||||
hint?: string
|
||||
},
|
||||
) {
|
||||
return api2.put(
|
||||
return api.put(
|
||||
`admin/problem-sets/${problemSetId}/problems/${problemSetProblemId}`,
|
||||
data,
|
||||
)
|
||||
@@ -611,14 +599,14 @@ export function removeProblemFromSet(
|
||||
problemSetId: number,
|
||||
problemSetProblemId: number,
|
||||
) {
|
||||
return api2.delete(
|
||||
return api.delete(
|
||||
`admin/problem-sets/${problemSetId}/problems/${problemSetProblemId}`,
|
||||
)
|
||||
}
|
||||
|
||||
// 题单奖章管理 API
|
||||
export function getProblemSetBadges(problemSetId: number) {
|
||||
return api2.get<ProblemSetBadge[]>(
|
||||
return api.get<ProblemSetBadge[]>(
|
||||
`admin/problem-sets/${problemSetId}/badges`,
|
||||
)
|
||||
}
|
||||
@@ -642,7 +630,7 @@ function toBadgeBody(data: BadgeBody) {
|
||||
}
|
||||
|
||||
export function createProblemSetBadge(problemSetId: number, data: BadgeBody) {
|
||||
return api2.post<ProblemSetBadge>(
|
||||
return api.post<ProblemSetBadge>(
|
||||
`admin/problem-sets/${problemSetId}/badges`,
|
||||
toBadgeBody(data),
|
||||
)
|
||||
@@ -653,31 +641,31 @@ export function editProblemSetBadge(
|
||||
badgeId: number,
|
||||
data: BadgeBody,
|
||||
) {
|
||||
return api2.put<ProblemSetBadge>(
|
||||
return api.put<ProblemSetBadge>(
|
||||
`admin/problem-sets/${problemSetId}/badges/${badgeId}`,
|
||||
toBadgeBody(data),
|
||||
)
|
||||
}
|
||||
|
||||
export function deleteProblemSetBadge(problemSetId: number, badgeId: number) {
|
||||
return api2.delete(`admin/problem-sets/${problemSetId}/badges/${badgeId}`)
|
||||
return api.delete(`admin/problem-sets/${problemSetId}/badges/${badgeId}`)
|
||||
}
|
||||
|
||||
// 题单进度管理 API
|
||||
// 注意:返回的是裸数组,不是分页信封 —— 和 oj 侧的 /user-progress 不同
|
||||
export function getProblemSetProgress(problemSetId: number) {
|
||||
return api2.get<AdminProblemSetProgress[]>(
|
||||
return api.get<AdminProblemSetProgress[]>(
|
||||
`admin/problem-sets/${problemSetId}/progress`,
|
||||
)
|
||||
}
|
||||
|
||||
export function removeUserFromProblemSet(problemSetId: number, userId: number) {
|
||||
return api2.delete(`admin/problem-sets/${problemSetId}/progress/${userId}`)
|
||||
return api.delete(`admin/problem-sets/${problemSetId}/progress/${userId}`)
|
||||
}
|
||||
|
||||
// 学生卡点分析
|
||||
export function getStuckProblems() {
|
||||
return api2.get<StuckProblem[]>("admin/problem-analytics/stuck")
|
||||
return api.get<StuckProblem[]>("admin/problem-analytics/stuck")
|
||||
}
|
||||
|
||||
export function getTopACTrend(params: {
|
||||
@@ -685,26 +673,26 @@ export function getTopACTrend(params: {
|
||||
untilYear: number
|
||||
minPerYear: number
|
||||
}) {
|
||||
return api2.get<AcTrend[]>("admin/problem-analytics/ac-trend", { params })
|
||||
return api.get<AcTrend[]>("admin/problem-analytics/ac-trend", { params })
|
||||
}
|
||||
|
||||
// AI 学习分析报告
|
||||
export function getAIReportList(offset = 0, limit = 10, username = "") {
|
||||
return api2.get<AdminAiReportList>("admin/ai/reports", {
|
||||
return api.get<AdminAiReportList>("admin/ai/reports", {
|
||||
params: { offset, limit, username: username || undefined },
|
||||
})
|
||||
}
|
||||
|
||||
export function getAIReportDetail(id: number) {
|
||||
return api2.get<AdminAiReport>(`admin/ai/reports/${id}`)
|
||||
return api.get<AdminAiReport>(`admin/ai/reports/${id}`)
|
||||
}
|
||||
|
||||
export function pinAIReport(id: number) {
|
||||
return api2.post<{ isPinned: boolean }>(`admin/ai/reports/${id}/pin`)
|
||||
return api.post<{ isPinned: boolean }>(`admin/ai/reports/${id}/pin`)
|
||||
}
|
||||
|
||||
export function getPinnedAIReports() {
|
||||
return api2.get<AdminAiReportList>("admin/ai/reports", {
|
||||
return api.get<AdminAiReportList>("admin/ai/reports", {
|
||||
params: { pinnedOnly: "true" },
|
||||
})
|
||||
}
|
||||
@@ -734,27 +722,27 @@ function toAchievementBody(data: Partial<AdminAchievement>) {
|
||||
}
|
||||
|
||||
export function getAdminAchievements() {
|
||||
return api2.get<AdminAchievement[]>("admin/achievements")
|
||||
return api.get<AdminAchievement[]>("admin/achievements")
|
||||
}
|
||||
|
||||
export function getMetricOptions() {
|
||||
return api2.get<MetricOption[]>("admin/achievement-metrics")
|
||||
return api.get<MetricOption[]>("admin/achievement-metrics")
|
||||
}
|
||||
|
||||
export function createAchievement(data: Partial<AdminAchievement>) {
|
||||
return api2.post<AdminAchievement>(
|
||||
return api.post<AdminAchievement>(
|
||||
"admin/achievements",
|
||||
toAchievementBody(data),
|
||||
)
|
||||
}
|
||||
|
||||
export function updateAchievement(data: Partial<AdminAchievement>) {
|
||||
return api2.put<AdminAchievement>(
|
||||
return api.put<AdminAchievement>(
|
||||
`admin/achievements/${data.id}`,
|
||||
toAchievementBody(data),
|
||||
)
|
||||
}
|
||||
|
||||
export function deleteAchievement(id: number) {
|
||||
return api2.delete(`admin/achievements/${id}`)
|
||||
return api.delete(`admin/achievements/${id}`)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ async function clone() {
|
||||
message.success("复制成功")
|
||||
router.push({
|
||||
name: "admin contest edit",
|
||||
params: { contestID: res.data.id },
|
||||
params: { contestID: res.id },
|
||||
})
|
||||
} catch {
|
||||
message.error("复制失败")
|
||||
|
||||
@@ -69,7 +69,7 @@ async function getContestDetail() {
|
||||
toggleReady(true)
|
||||
return
|
||||
}
|
||||
const { data } = await getContest(props.contestID)
|
||||
const data = await getContest(props.contestID)
|
||||
toggleReady(true)
|
||||
contest.id = data.id
|
||||
contest.title = data.title
|
||||
|
||||
@@ -142,18 +142,18 @@ async function viewSubmission(item: HelperItem) {
|
||||
limit: 1,
|
||||
})
|
||||
|
||||
if (res.data.results.length === 0) {
|
||||
if (res.results.length === 0) {
|
||||
message.warning("未找到该用户的 AC 提交")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取提交详情
|
||||
const submissionListItem = res.data.results[0]
|
||||
const submissionListItem = res.results[0]
|
||||
const detailRes = await getSubmission(submissionListItem.id)
|
||||
|
||||
// 手动添加 contest 字段(ACM模式下后端不返回此字段)
|
||||
currentSubmission.value = {
|
||||
...detailRes.data,
|
||||
...detailRes,
|
||||
contest: Number(props.contestID),
|
||||
problem_display_id: item.problemDisplayId,
|
||||
}
|
||||
@@ -169,10 +169,10 @@ async function loadData() {
|
||||
try {
|
||||
// 先获取比赛信息,获取开始时间
|
||||
const contestRes = await getContest(props.contestID)
|
||||
contestStartTime.value = new Date(contestRes.data.startTime)
|
||||
contestStartTime.value = new Date(contestRes.startTime)
|
||||
|
||||
// 再获取 AC 提交列表
|
||||
const { data } = await getACMHelperList(Number(props.contestID))
|
||||
const data = await getACMHelperList(Number(props.contestID))
|
||||
submissions.value = data
|
||||
} catch (err: any) {
|
||||
message.error(err.data || "加载失败")
|
||||
|
||||
@@ -87,8 +87,8 @@ const columns: DataTableColumn<Contest>[] = [
|
||||
async function listContests() {
|
||||
const offset = (query.page - 1) * query.limit
|
||||
const res = await getContestList(offset, query.limit, query.keyword)
|
||||
contests.value = res.data.results
|
||||
total.value = res.data.total
|
||||
contests.value = res.results
|
||||
total.value = res.total
|
||||
}
|
||||
onMounted(listContests)
|
||||
watch(() => [query.page, query.limit], listContests)
|
||||
|
||||
@@ -29,7 +29,7 @@ const columns: DataTableColumn<StuckProblem>[] = [
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await getStuckProblems()
|
||||
data.value = res.data
|
||||
data.value = res
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ async function fetchData() {
|
||||
untilYear: untilYear.value,
|
||||
minPerYear: minPerYear.value,
|
||||
})
|
||||
data.value = res.data
|
||||
data.value = res
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ function toggleTag(name: string) {
|
||||
|
||||
async function listTags() {
|
||||
const res = await getTagAdminList()
|
||||
tags.value = res.data
|
||||
tags.value = res
|
||||
}
|
||||
|
||||
function close() {
|
||||
@@ -60,7 +60,7 @@ async function submit() {
|
||||
)
|
||||
const verb = props.action === "add" ? "添加" : "移除"
|
||||
message.success(
|
||||
`已为 ${res.data.problemCount} 道题${verb} ${res.data.tagCount} 个标签`,
|
||||
`已为 ${res.problemCount} 道题${verb} ${res.tagCount} 个标签`,
|
||||
)
|
||||
close()
|
||||
emit("done")
|
||||
|
||||
@@ -78,8 +78,8 @@ onMounted(async () => {
|
||||
if (!props.problemId) return
|
||||
try {
|
||||
const res = await getSQLTestcaseScripts(props.problemId)
|
||||
if (res.data.length) {
|
||||
scripts.value = res.data.map((f) => ({ ...blankEntry(), sql: f.content }))
|
||||
if (res.length) {
|
||||
scripts.value = res.map((f) => ({ ...blankEntry(), sql: f.content }))
|
||||
}
|
||||
} catch (err: any) {
|
||||
// 新题、以及旧格式(非 SQL)测试点,后端回 404/409,保持空白就是对的,不该报错。
|
||||
@@ -128,7 +128,7 @@ async function generate() {
|
||||
refSql: refSQL.value,
|
||||
mode: props.mode,
|
||||
})
|
||||
s.sql = res.data.sql
|
||||
s.sql = res.sql
|
||||
} catch (err) {
|
||||
const data = (err as { data?: unknown })?.data
|
||||
message.error(typeof data === "string" ? data : "AI 生成失败")
|
||||
@@ -158,7 +158,7 @@ async function preview() {
|
||||
refSql: refSQL.value,
|
||||
mode: props.mode,
|
||||
})
|
||||
s.display = res.data
|
||||
s.display = res
|
||||
} catch (err) {
|
||||
const data = (err as { data?: unknown })?.data
|
||||
s.error = typeof data === "string" ? data : "预览失败"
|
||||
@@ -182,7 +182,7 @@ async function upload() {
|
||||
|
||||
const res = await uploadTestcases(file, { sql: true })
|
||||
// score 不在上传响应里,是这里按测试点数量平分补上的(余数给最后一个)
|
||||
const entries = res.data.info
|
||||
const entries = res.info
|
||||
const baseScore = Math.floor(100 / entries.length)
|
||||
const remainder = 100 - baseScore * entries.length
|
||||
const testcases: Testcase[] = entries.map((entry, i) => ({
|
||||
@@ -192,7 +192,7 @@ async function upload() {
|
||||
),
|
||||
}))
|
||||
|
||||
emit("uploaded", res.data.id, testcases)
|
||||
emit("uploaded", res.id, testcases)
|
||||
message.success("上传成功")
|
||||
} catch {
|
||||
message.error("上传失败")
|
||||
|
||||
@@ -170,7 +170,7 @@ async function upload() {
|
||||
|
||||
const res = await uploadTestcases(file)
|
||||
// score 不在上传响应里,是这里按测试点数量平分补上的(余数给最后一个)
|
||||
const entries = res.data.info
|
||||
const entries = res.info
|
||||
const baseScore = Math.floor(100 / entries.length)
|
||||
const remainder = 100 - baseScore * entries.length
|
||||
const testcases: Testcase[] = entries.map((entry, i) => ({
|
||||
@@ -180,7 +180,7 @@ async function upload() {
|
||||
),
|
||||
}))
|
||||
|
||||
emit("uploaded", res.data.id, testcases)
|
||||
emit("uploaded", res.id, testcases)
|
||||
message.success("上传成功")
|
||||
} catch {
|
||||
message.error("上传失败")
|
||||
|
||||
@@ -208,7 +208,7 @@ async function getProblemDetail() {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const { data } = await getProblem(props.problemID)
|
||||
const data = await getProblem(props.problemID)
|
||||
problem.value.id = data.id
|
||||
problem.value._id = data._id
|
||||
problem.value.title = data.title
|
||||
@@ -269,7 +269,7 @@ async function getProblemDetail() {
|
||||
|
||||
async function getTagList() {
|
||||
const res = await getProblemTagList()
|
||||
tagList.value = res.data
|
||||
tagList.value = res
|
||||
tagListLoaded.value = true
|
||||
syncTagInputsFromProblemTags()
|
||||
}
|
||||
@@ -295,13 +295,13 @@ async function handleUploadTestcases({ file }: UploadCustomRequestOptions) {
|
||||
return
|
||||
}
|
||||
// score 不在上传响应里,前端按测试点数量平分补上
|
||||
const entries = res.data.info
|
||||
const entries = res.info
|
||||
const testcases: Testcase[] = entries.map((entry) => ({
|
||||
...entry,
|
||||
score: (100 / entries.length).toFixed(0),
|
||||
}))
|
||||
problem.value.testCaseScore = testcases
|
||||
problem.value.testCaseId = res.data.id
|
||||
problem.value.testCaseId = res.id
|
||||
} catch (err) {
|
||||
message.error("上传测试用例失败")
|
||||
}
|
||||
@@ -495,7 +495,7 @@ async function generateMermaid() {
|
||||
)
|
||||
isAIGenerating.value = false
|
||||
message.warning("如果渲染不成功,请复制到外部 AI 网站检查语法")
|
||||
problem.value.mermaidCode = res.data.flowchart
|
||||
problem.value.mermaidCode = res.flowchart
|
||||
}
|
||||
|
||||
const showGeneratorModal = ref(false)
|
||||
|
||||
@@ -97,7 +97,7 @@ const columns: DataTableColumn<AdminTag>[] = [
|
||||
|
||||
async function listTags() {
|
||||
const res = await getTagAdminList(keyword.value)
|
||||
tags.value = res.data
|
||||
tags.value = res
|
||||
}
|
||||
|
||||
function startEdit(tag: AdminTag) {
|
||||
@@ -121,9 +121,9 @@ async function saveTag(tag: AdminTag) {
|
||||
return
|
||||
}
|
||||
const res = await renameTag(tag.id, name)
|
||||
if (res.data.merged) {
|
||||
if (res.merged) {
|
||||
message.success(
|
||||
`已合并到「${res.data.name}」,影响 ${res.data.affectedCount} 道题`,
|
||||
`已合并到「${res.name}」,影响 ${res.affectedCount} 道题`,
|
||||
)
|
||||
} else {
|
||||
message.success("已重命名")
|
||||
|
||||
@@ -52,7 +52,7 @@ const editingBadge = ref<ProblemSetBadge | null>(null)
|
||||
async function loadProblemSetDetail() {
|
||||
try {
|
||||
const res = await getProblemSetDetail(problemSetId.value)
|
||||
problemSet.value = res.data
|
||||
problemSet.value = res
|
||||
} catch (err: any) {
|
||||
message.error("加载题单详情失败:" + (err.data || "未知错误"))
|
||||
}
|
||||
@@ -61,7 +61,7 @@ async function loadProblemSetDetail() {
|
||||
async function loadProblems() {
|
||||
try {
|
||||
const res = await getProblemSetProblems(problemSetId.value)
|
||||
problems.value = res.data
|
||||
problems.value = res
|
||||
} catch (err: any) {
|
||||
message.error("加载题目列表失败:" + (err.data || "未知错误"))
|
||||
}
|
||||
@@ -70,7 +70,7 @@ async function loadProblems() {
|
||||
async function loadBadges() {
|
||||
try {
|
||||
const res = await getProblemSetBadges(problemSetId.value)
|
||||
badges.value = res.data
|
||||
badges.value = res
|
||||
} catch (err: any) {
|
||||
message.error("加载奖章列表失败:" + (err.data || "未知错误"))
|
||||
}
|
||||
@@ -79,7 +79,7 @@ async function loadBadges() {
|
||||
async function loadProgress() {
|
||||
try {
|
||||
const res = await getProblemSetProgress(problemSetId.value)
|
||||
progress.value = res.data
|
||||
progress.value = res
|
||||
} catch (err: any) {
|
||||
message.error("加载进度列表失败:" + (err.data || "未知错误"))
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ async function loadProblemSetDetail() {
|
||||
|
||||
try {
|
||||
const res = await getProblemSetDetail(problemSetId.value)
|
||||
const data = res.data
|
||||
const data = res
|
||||
formData.value = {
|
||||
id: data.id,
|
||||
title: data.title,
|
||||
|
||||
@@ -123,8 +123,8 @@ async function listProblemSets() {
|
||||
query.difficulty,
|
||||
query.status,
|
||||
)
|
||||
total.value = res.data.total
|
||||
problemSets.value = res.data.results
|
||||
total.value = res.total
|
||||
problemSets.value = res.results
|
||||
}
|
||||
|
||||
async function toggleVisible(problemSetId: number) {
|
||||
|
||||
@@ -129,14 +129,14 @@ const websiteConfig = reactive<WebsiteConfig>({
|
||||
|
||||
async function getWebsiteConfig() {
|
||||
const res = await getWebsite()
|
||||
websiteConfig.websiteBaseUrl = res.data.websiteBaseUrl
|
||||
websiteConfig.websiteName = res.data.websiteName
|
||||
websiteConfig.websiteNameShortcut = res.data.websiteNameShortcut
|
||||
websiteConfig.websiteFooter = res.data.websiteFooter
|
||||
websiteConfig.allowRegister = res.data.allowRegister
|
||||
websiteConfig.submissionListShowAll = res.data.submissionListShowAll
|
||||
websiteConfig.classList = res.data.classList
|
||||
websiteConfig.enableMaxkb = res.data.enableMaxkb
|
||||
websiteConfig.websiteBaseUrl = res.websiteBaseUrl
|
||||
websiteConfig.websiteName = res.websiteName
|
||||
websiteConfig.websiteNameShortcut = res.websiteNameShortcut
|
||||
websiteConfig.websiteFooter = res.websiteFooter
|
||||
websiteConfig.allowRegister = res.allowRegister
|
||||
websiteConfig.submissionListShowAll = res.submissionListShowAll
|
||||
websiteConfig.classList = res.classList
|
||||
websiteConfig.enableMaxkb = res.enableMaxkb
|
||||
}
|
||||
|
||||
async function saveWebsiteConfig() {
|
||||
@@ -172,13 +172,13 @@ async function deleteTestcase(id?: string) {
|
||||
|
||||
async function getTestcases() {
|
||||
const res = await listInvalidTestcases()
|
||||
testcases.value = res.data
|
||||
testcases.value = res
|
||||
}
|
||||
|
||||
async function getJudgeServerData() {
|
||||
const res = await getJudgeServer()
|
||||
token.value = res.data.token
|
||||
servers.value = res.data.servers
|
||||
token.value = res.token
|
||||
servers.value = res.servers
|
||||
}
|
||||
|
||||
async function delJudgeServer(hostname: string) {
|
||||
|
||||
@@ -64,16 +64,16 @@ const columns: DataTableColumn<Rank>[] = [
|
||||
|
||||
onMounted(async () => {
|
||||
const res = await getBaseInfo()
|
||||
userCount.value = res.data.userCount
|
||||
submissionCount.value = res.data.todaySubmissionCount
|
||||
contestCount.value = res.data.recentContestCount
|
||||
userCount.value = res.userCount
|
||||
submissionCount.value = res.todaySubmissionCount
|
||||
contestCount.value = res.recentContestCount
|
||||
})
|
||||
|
||||
async function listRanks() {
|
||||
const offset = (query.page - 1) * query.limit
|
||||
const res = await getAdminUserRank(offset, query.limit, query.classroom)
|
||||
data.value = res.data.results
|
||||
total.value = res.data.total
|
||||
data.value = res.results
|
||||
total.value = res.total
|
||||
}
|
||||
|
||||
function stopRolling() {
|
||||
@@ -108,7 +108,7 @@ function startRolling(finalName: string) {
|
||||
|
||||
async function getRandom() {
|
||||
const res = await randomUser10(query.classroom)
|
||||
const names = (res.data as string[]).map(
|
||||
const names = (res as string[]).map(
|
||||
(name) => name.split(query.classroom)[1],
|
||||
)
|
||||
rollingNames.value = names
|
||||
|
||||
@@ -129,8 +129,8 @@ async function listUsers() {
|
||||
query.keyword,
|
||||
query.orderBy,
|
||||
)
|
||||
total.value = res.data.total
|
||||
users.value = res.data.results
|
||||
total.value = res.total
|
||||
users.value = res.results
|
||||
}
|
||||
|
||||
function chooseUsers(rowKeys: DataTableRowKey[]) {
|
||||
@@ -144,10 +144,10 @@ async function onDeleteUsers(userIDs: DataTableRowKey[] | Ref<number[]>) {
|
||||
|
||||
async function onResetPassword(user: User) {
|
||||
const res = await resetPassword(user.id)
|
||||
message.success(`【${user.username}】的密码已重置成【${res.data}】`)
|
||||
message.success(`【${user.username}】的密码已重置成【${res}】`)
|
||||
users.value = users.value.map((it) => {
|
||||
if (it.id === user.id && user.adminType === USER_TYPE.REGULAR_USER) {
|
||||
it.rawPassword = res.data
|
||||
it.rawPassword = res
|
||||
}
|
||||
return it
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user