接上 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>
83 lines
2.6 KiB
Vue
83 lines
2.6 KiB
Vue
<script setup lang="ts">
|
||
import { darkTheme, dateZhCN, zhCN } from "naive-ui"
|
||
import "normalize.css"
|
||
import "./index.css"
|
||
import { useConfigStore } from "shared/store/config"
|
||
import { useConfigUpdate } from "shared/composables/configUpdate"
|
||
import { useMaxKB } from "shared/composables/maxkb"
|
||
import { useUserStore } from "shared/store/user"
|
||
|
||
const isDark = useDark()
|
||
const configStore = useConfigStore()
|
||
const userStore = useUserStore()
|
||
|
||
// 初始化配置和实时更新
|
||
onMounted(() => {
|
||
configStore.getConfig()
|
||
userStore.getMyProfile()
|
||
})
|
||
|
||
// 配置实时推送 + MaxKB 挂件。阶段 2 时因为后端还没有 /ws/config 而暂时关掉,
|
||
// 阶段 3 迁完之后一直没接回来 —— 表现是管理员改站点配置后学生要刷新才生效、
|
||
// 知识库挂件根本不出现,且没有任何报错。两者现在都走新后端的 /ws/config。
|
||
useConfigUpdate()
|
||
useMaxKB()
|
||
|
||
// 延迟加载 highlight.js,避免阻塞首屏
|
||
const hljsInstance = ref<any>(null)
|
||
const loadHighlightJS = async () => {
|
||
if (hljsInstance.value) return hljsInstance.value
|
||
|
||
// 逐个取 .default,不要在 Promise.all 后面 map —— 那样元组会塌成
|
||
// (HLJSApi | LanguageFn)[],每个元素都变成联合类型,hljs 上就找不到方法了
|
||
const [core, c, cpp, python, java, javascript, go, sql] = await Promise.all([
|
||
import("highlight.js/lib/core"),
|
||
import("highlight.js/lib/languages/c"),
|
||
import("highlight.js/lib/languages/cpp"),
|
||
import("highlight.js/lib/languages/python"),
|
||
import("highlight.js/lib/languages/java"),
|
||
import("highlight.js/lib/languages/javascript"),
|
||
import("highlight.js/lib/languages/go"),
|
||
import("highlight.js/lib/languages/sql"),
|
||
])
|
||
const hljs = core.default
|
||
|
||
hljs.registerLanguage("c", c.default)
|
||
hljs.registerLanguage("python", python.default)
|
||
hljs.registerLanguage("cpp", cpp.default)
|
||
hljs.registerLanguage("java", java.default)
|
||
hljs.registerLanguage("javascript", javascript.default)
|
||
hljs.registerLanguage("go", go.default)
|
||
hljs.registerLanguage("sql", sql.default)
|
||
|
||
hljsInstance.value = hljs
|
||
return hljs
|
||
}
|
||
|
||
// 在空闲时预加载
|
||
onMounted(() => {
|
||
if ("requestIdleCallback" in window) {
|
||
requestIdleCallback(() => loadHighlightJS())
|
||
} else {
|
||
setTimeout(() => loadHighlightJS(), 1000)
|
||
}
|
||
})
|
||
|
||
provide("hljs", hljsInstance)
|
||
</script>
|
||
|
||
<template>
|
||
<n-config-provider
|
||
:theme="isDark ? darkTheme : null"
|
||
:locale="zhCN"
|
||
:date-locale="dateZhCN"
|
||
:hljs="hljsInstance"
|
||
>
|
||
<n-dialog-provider>
|
||
<n-message-provider>
|
||
<router-view></router-view>
|
||
</n-message-provider>
|
||
</n-dialog-provider>
|
||
</n-config-provider>
|
||
</template>
|