refactor(前端): vue-tsc 清零,type-check 可以当 CI 门禁了
接上 vue-tsc 时有 143 条既有错误,上一批迁移带走 89 条,这里把剩下的清完。
大部分是噪音(未使用的回调参数、死变量、少数 unknown),但里面躺着两个真问题:
- 个人主页查不存在的用户会抛:getProfile 返回 null 时后面照样取
.acmProblemsStatus,靠 `!` 压着。加了早返回。
- getProblemSetProgress 我上一批标错了类型:后台这个接口返回的是裸数组,
不是分页信封(和 oj 侧的 /user-progress 不一样)。已改正并加注释区分。
其余处理:
- 未使用的回调参数按 TS 约定加 `_` 前缀(v-for 的项、供子类重写的空钩子、
路由守卫的 from);确认无用的局部变量直接删(clickX/clickY 算了点击位置
但飞出方向用的是随机角度,hasToday 下面已经用 lastDateOnly 判过,
prefix 算了周/月但标签里没用上)。
- App.vue 的 highlight.js 注册:Promise.all([...]).then(m => m.map(x => x.default))
会把元组塌成 (HLJSApi | LanguageFn)[],hljs 上就找不到 registerLanguage。
改成逐个取 .default。
- 给一批 api 补上契约类型(getMetrics / getHitokoto / getTutorials /
formatCode / getProblemBeatRate / getClassUsernames 等),契约相应补了
8 个 z.infer 导出。
- ContestRank.submissionInfo 和 AcmHelperItem.acInfo 在 api 边界窄化成
SubmissionInfo —— 契约里是 Record<string, unknown>(JSONB 原文)。
- FlowchartEditor 的 TS2589:vue-flow 的 Node 嵌套太深,ref<Node[]>([]) 的
UnwrapRef 推导撞上实例化层级上限,改成 ref([]) as Ref<Node[]>。
- api2.ts 的响应拦截器**故意**不返回 AxiosResponse(要把 { data } 信封剥掉),
类型上确实说不通,没硬掰,写注释说明为什么用断言。
- 题目列表的「随机」按钮在模板里一直是注释状态,对应的 getRandom 和
getRandomProblemID 一并删除(后端 /problems/random 保留不动)。
验证:vue-tsc 0 条,apps/api tsc、check:routes、web build 全通过;
修过 key 的列都打接口确认过字段真实存在。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,11 +2,13 @@ import api2 from "utils/api2"
|
||||
import { toProblemListItem } from "admin/transforms"
|
||||
import type {
|
||||
AcTrend,
|
||||
AdminProblemSetProgress,
|
||||
BatchProblemTagResponse,
|
||||
GenerateSqlTestCaseResponse,
|
||||
RenameTagResponse,
|
||||
SqlTestCaseScript,
|
||||
AcmHelperItem,
|
||||
SubmissionInfo,
|
||||
AdminAiReport,
|
||||
AdminAiReportList,
|
||||
StuckProblem,
|
||||
@@ -36,7 +38,6 @@ import type {
|
||||
ProblemSetBadge,
|
||||
ProblemSetList,
|
||||
ProblemSetProblem,
|
||||
ProblemSetProgressList,
|
||||
TutorialListItem,
|
||||
} from "utils/types"
|
||||
|
||||
@@ -476,7 +477,11 @@ export function makeProblemPublic(id: number, displayId: string) {
|
||||
|
||||
// 比赛辅助检查
|
||||
export function getACMHelperList(contestId: number) {
|
||||
return api2.get<AcmHelperItem[]>(`admin/contests/${contestId}/acm-helper`)
|
||||
// acInfo 在契约里是 Record<string, unknown>(acm_contest_rank 的 JSONB 原文),
|
||||
// 组件侧按 SubmissionInfo 读,收窄放在这里
|
||||
return api2.get<
|
||||
Array<Omit<AcmHelperItem, "acInfo"> & { acInfo: SubmissionInfo }>
|
||||
>(`admin/contests/${contestId}/acm-helper`)
|
||||
}
|
||||
|
||||
export function updateACMHelperChecked(
|
||||
@@ -652,8 +657,9 @@ export function deleteProblemSetBadge(problemSetId: number, badgeId: number) {
|
||||
}
|
||||
|
||||
// 题单进度管理 API
|
||||
// 注意:返回的是裸数组,不是分页信封 —— 和 oj 侧的 /user-progress 不同
|
||||
export function getProblemSetProgress(problemSetId: number) {
|
||||
return api2.get<ProblemSetProgressList>(
|
||||
return api2.get<AdminProblemSetProgress[]>(
|
||||
`admin/problem-sets/${problemSetId}/progress`,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
ProblemSet,
|
||||
ProblemSetProblem,
|
||||
ProblemSetBadge,
|
||||
ProblemSetProgress,
|
||||
AdminProblemSetProgress,
|
||||
} from "utils/types"
|
||||
import {
|
||||
getProblemSetDetail,
|
||||
@@ -37,7 +37,7 @@ const problemSetId = computed(() => Number(route.params.problemSetId))
|
||||
const problemSet = ref<ProblemSet | null>(null)
|
||||
const problems = ref<ProblemSetProblem[]>([])
|
||||
const badges = ref<ProblemSetBadge[]>([])
|
||||
const progress = ref<ProblemSetProgress[]>([])
|
||||
const progress = ref<AdminProblemSetProgress[]>([])
|
||||
|
||||
// 模态框状态
|
||||
const showAddProblemModal = ref(false)
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
CLASS_NAME_RE,
|
||||
} from "utils/constants"
|
||||
import { parseTime } from "utils/functions"
|
||||
import type { Server } from "utils/types"
|
||||
import type { OrphanTestCase, Server, WebsiteConfig } from "utils/types"
|
||||
import { useConfigStore } from "shared/store/config"
|
||||
import { useConfigWebSocket } from "shared/composables/websocket"
|
||||
import {
|
||||
@@ -98,8 +98,8 @@ const serverColumns: DataTableColumn<Server>[] = [
|
||||
width: 100,
|
||||
},
|
||||
{ title: "IP", key: "ip", width: 140 },
|
||||
{ title: "判题机版本", key: "judger_version", width: 100 },
|
||||
{ title: "服务器 URL", key: "service_url", width: 200 },
|
||||
{ title: "判题机版本", key: "judgerVersion", width: 100 },
|
||||
{ title: "服务器 URL", key: "serviceUrl", width: 200 },
|
||||
{
|
||||
title: "上一次心跳",
|
||||
key: "last_heartbeat",
|
||||
@@ -114,14 +114,14 @@ const serverColumns: DataTableColumn<Server>[] = [
|
||||
},
|
||||
]
|
||||
|
||||
const testcases = ref<Testcase[]>([])
|
||||
const testcases = ref<OrphanTestCase[]>([])
|
||||
const token = ref("")
|
||||
const servers = ref<Server[]>([])
|
||||
const abnormalServers = computed(() =>
|
||||
servers.value.filter((item) => item.status === "abnormal"),
|
||||
)
|
||||
|
||||
const websiteConfig = reactive({
|
||||
const websiteConfig = reactive<WebsiteConfig>({
|
||||
websiteBaseUrl: import.meta.env.PUBLIC_OJ_URL,
|
||||
websiteName: "判题狗",
|
||||
websiteNameShortcut: "判题狗",
|
||||
|
||||
@@ -427,7 +427,7 @@ function typeTagType(type: ExerciseType) {
|
||||
<n-form-item label="选项(勾选所有正确答案)">
|
||||
<n-space vertical style="width: 100%">
|
||||
<n-flex
|
||||
v-for="(opt, i) in mcqOptions"
|
||||
v-for="(_opt, i) in mcqOptions"
|
||||
:key="i"
|
||||
align="center"
|
||||
:size="8"
|
||||
|
||||
@@ -10,7 +10,6 @@ interface Props {
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
const props = defineProps<Props>()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user