## utils/functions.ts 砍掉一半(622 → 291 行) - trickOrTreat():317 行、八种页面恶搞效果(中文乱码、页面翻转、去掉鼠标……), **全仓零调用**,占这个共享工具文件的一半; - 文件末尾注释掉的 getChromeVersion / isLowVersion / protocol 六行。 ## 其余没有调用方的导出 - services/achievement-metrics.ts 的 RARITIES / OPERATORS —— 和契约的 achievementRaritySchema / achievementOperatorSchema 取值逐字相同,是没人用的第二份; - collab/state.ts 的 hasRequest;以及 resetCollabState,它的注释写着「仅供进程退出或 测试用」,而本仓库不写测试(项目约定),进程退出那条路径也没调过; - contract/language.ts 的 JUDGE_LANGUAGES,注释说「前端用它排语言 tab」——前端并没有, 它排 tab 用的是 constants.ts 里以 ProblemLanguage 为键的 SOURCES / LANGUAGE_SHOW_VALUE (那组映射的完整性 tsc 已经在管); - web 的 useSimplePagination(usePagination 的一层空壳包装)和 updateProblemSetProgress。 扫描口径:三个包全部 .ts / .vue 里逐个导出符号数出现次数,只在定义处出现的算无引用 (.vue 模板里的引用也计入)。剩下 56 个仍无引用的全部是契约里 `z.infer` 派生的一行 类型(46 个请求类型 + 10 个领域类型)—— 它们是在用的 schema 的类型另一半,成体系的 1:1 镜像,删一部分只会让那个文件变得随意,所以不动。 ## 留给你定的一件事 `updateProblemSetProgress` 删掉之后,后端 `PUT /problem-set-progress`(routes/problemset.ts:249) 就没有任何调用方了。题单进度实际是判完之后由 services/problemset.ts 的 recordSolvedProblem 在服务端记账的(schema.ts 里那条注释写明了),这个端点是一条客户端自报进度的平行路径。 没顺手删:删端点是行为变化,而且不能排除有脚本在打它。 tsc、vue-tsc、vite build、单二进制编译、check:routes、check:ast 均通过。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012j1vgeDqay8wKCh8dPgPcH
This commit is contained in:
@@ -69,9 +69,6 @@ export function removeRequest(studentId: number) {
|
|||||||
return requests.delete(studentId)
|
return requests.delete(studentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasRequest(studentId: number) {
|
|
||||||
return requests.has(studentId)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 按发起时间正序。老师端按等待时长排序展示,不强制先来先到 */
|
/** 按发起时间正序。老师端按等待时长排序展示,不强制先来先到 */
|
||||||
export function listRequests() {
|
export function listRequests() {
|
||||||
@@ -136,9 +133,3 @@ export function roomOf(ws: CollabSocket) {
|
|||||||
return ownerId === undefined ? undefined : rooms.get(ownerId)
|
return ownerId === undefined ? undefined : rooms.get(ownerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 仅供进程退出或测试用,正常路径不该调 */
|
|
||||||
export function resetCollabState() {
|
|
||||||
requests.clear()
|
|
||||||
teachers.clear()
|
|
||||||
rooms.clear()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -44,6 +44,3 @@ export function metricName(key: string) {
|
|||||||
return BY_KEY.get(key)?.name ?? key
|
return BY_KEY.get(key)?.name ?? key
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 稀有度四档。乱填的值会让成就汇总接口的分档统计对不上:野值算进总数却不出现在任何一档 */
|
|
||||||
export const RARITIES = ["bronze", "silver", "gold", "platinum"] as const
|
|
||||||
export const OPERATORS = ["gte", "lte"] as const
|
|
||||||
|
|||||||
@@ -459,17 +459,6 @@ export function joinProblemSet(problemSetId: number) {
|
|||||||
return api.post("problem-set-progress", { problemSetId })
|
return api.post("problem-set-progress", { problemSetId })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateProblemSetProgress(
|
|
||||||
problemSetId: number,
|
|
||||||
problemId: number,
|
|
||||||
submissionId: string,
|
|
||||||
) {
|
|
||||||
return api.put("problem-set-progress", {
|
|
||||||
problemSetId,
|
|
||||||
problemId,
|
|
||||||
submissionId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUserBadges(username?: string) {
|
export function getUserBadges(username?: string) {
|
||||||
return api.get<UserBadge[]>(
|
return api.get<UserBadge[]>(
|
||||||
|
|||||||
@@ -139,16 +139,3 @@ export function usePagination<T extends Record<string, any>>(
|
|||||||
syncFromRoute,
|
syncFromRoute,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 简化版本的分页 composable,只处理基本的分页逻辑
|
|
||||||
* 每次调用创建新的分页状态实例
|
|
||||||
* @param defaultLimit 默认每页条数
|
|
||||||
* @param defaultPage 默认页码
|
|
||||||
*/
|
|
||||||
export function useSimplePagination(defaultLimit = 10, defaultPage = 1) {
|
|
||||||
return usePagination(
|
|
||||||
{},
|
|
||||||
{ defaultLimit, defaultPage, resetPageOnChange: false },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -289,334 +289,3 @@ export function getRandomId() {
|
|||||||
const nanoid = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz")
|
const nanoid = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz")
|
||||||
return nanoid()
|
return nanoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 恶搞效果函数 - 随机触发不同的页面恶搞效果
|
|
||||||
*/
|
|
||||||
export function trickOrTreat() {
|
|
||||||
const effects = [
|
|
||||||
// 效果1: 中文乱码
|
|
||||||
() => {
|
|
||||||
document.body.innerHTML = document.body.innerHTML.replace(
|
|
||||||
/[\u4e00-\u9fa5]/g,
|
|
||||||
function (c) {
|
|
||||||
return String.fromCharCode(c.charCodeAt(0) ^ 0xa5) // 将中文字符转为乱码
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
// 效果2: 页面一直缩放
|
|
||||||
() => {
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-scale-style"
|
|
||||||
style.textContent = `
|
|
||||||
body {
|
|
||||||
animation: trickScale 0.5s ease-in-out infinite alternate;
|
|
||||||
}
|
|
||||||
@keyframes trickScale {
|
|
||||||
from { transform: scale(0.8); }
|
|
||||||
to { transform: scale(1.2); }
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
},
|
|
||||||
// 效果3: 页面左右颠倒
|
|
||||||
() => {
|
|
||||||
document.body.style.transform = "scaleX(-1)"
|
|
||||||
},
|
|
||||||
// 效果4: 去掉鼠标
|
|
||||||
() => {
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-cursor-none-style"
|
|
||||||
style.textContent = `
|
|
||||||
* {
|
|
||||||
cursor: none !important;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
},
|
|
||||||
// 效果5: 页面一直旋转
|
|
||||||
() => {
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-rotate-style"
|
|
||||||
style.textContent = `
|
|
||||||
body {
|
|
||||||
animation: trickRotate 2s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes trickRotate {
|
|
||||||
from { transform: rotate(0deg); }
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
},
|
|
||||||
// 效果6: 页面上下颠倒
|
|
||||||
() => {
|
|
||||||
document.body.style.transform = "scaleY(-1)"
|
|
||||||
},
|
|
||||||
// 效果7: 页面颜色反转(反色)
|
|
||||||
() => {
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-invert-style"
|
|
||||||
style.textContent = `
|
|
||||||
body {
|
|
||||||
filter: invert(1) !important;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
},
|
|
||||||
// 效果8: 页面抖动/震动
|
|
||||||
() => {
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-shake-style"
|
|
||||||
style.textContent = `
|
|
||||||
body {
|
|
||||||
animation: trickShake 0.1s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
@keyframes trickShake {
|
|
||||||
0%, 100% { transform: translate(0, 0); }
|
|
||||||
25% { transform: translate(-5px, -5px); }
|
|
||||||
50% { transform: translate(5px, 5px); }
|
|
||||||
75% { transform: translate(-5px, 5px); }
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
},
|
|
||||||
// 效果9: 页面模糊
|
|
||||||
() => {
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-blur-style"
|
|
||||||
style.textContent = `
|
|
||||||
body {
|
|
||||||
filter: blur(5px) !important;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
},
|
|
||||||
// 效果10: 点击哪里,哪里的DOM就飞掉
|
|
||||||
() => {
|
|
||||||
// 添加CSS动画样式
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-flyaway-style"
|
|
||||||
style.textContent = `
|
|
||||||
.trick-flyaway {
|
|
||||||
animation: trickFlyAway 0.5s ease-out forwards !important;
|
|
||||||
pointer-events: none !important;
|
|
||||||
}
|
|
||||||
@keyframes trickFlyAway {
|
|
||||||
0% {
|
|
||||||
transform: translate(0, 0) rotate(0deg);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translate(var(--fly-x, 500px), var(--fly-y, -500px)) rotate(720deg);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
|
|
||||||
// 添加全局点击事件监听器
|
|
||||||
const clickHandler = (e: MouseEvent) => {
|
|
||||||
const target = e.target as HTMLElement
|
|
||||||
// 跳过body和html元素,以及已经飞走的元素
|
|
||||||
if (
|
|
||||||
target === document.body ||
|
|
||||||
target === document.documentElement ||
|
|
||||||
target.classList.contains("trick-flyaway")
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算飞出方向(随机方向)
|
|
||||||
const angle = Math.random() * Math.PI * 2
|
|
||||||
const distance = 1000 + Math.random() * 500
|
|
||||||
const flyX = Math.cos(angle) * distance
|
|
||||||
const flyY = Math.sin(angle) * distance
|
|
||||||
|
|
||||||
// 设置CSS变量
|
|
||||||
target.style.setProperty("--fly-x", `${flyX}px`)
|
|
||||||
target.style.setProperty("--fly-y", `${flyY}px`)
|
|
||||||
|
|
||||||
// 添加飞走动画类
|
|
||||||
target.classList.add("trick-flyaway")
|
|
||||||
|
|
||||||
// 动画结束后移除元素或隐藏
|
|
||||||
setTimeout(() => {
|
|
||||||
target.style.display = "none"
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("click", clickHandler, true)
|
|
||||||
},
|
|
||||||
// 效果11: 页面元素随机位置
|
|
||||||
() => {
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-random-position-style"
|
|
||||||
style.textContent = `
|
|
||||||
* {
|
|
||||||
position: relative !important;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
|
|
||||||
// 随机移动所有元素
|
|
||||||
const allElements = document.querySelectorAll("*")
|
|
||||||
allElements.forEach((el) => {
|
|
||||||
if (el === document.body || el === document.documentElement) return
|
|
||||||
const element = el as HTMLElement
|
|
||||||
const randomX = (Math.random() - 0.5) * 200
|
|
||||||
const randomY = (Math.random() - 0.5) * 200
|
|
||||||
element.style.transform = `translate(${randomX}px, ${randomY}px)`
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 效果12: 页面变成3D倾斜效果
|
|
||||||
() => {
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-3d-tilt-style"
|
|
||||||
style.textContent = `
|
|
||||||
body {
|
|
||||||
perspective: 1000px;
|
|
||||||
transform-style: preserve-3d;
|
|
||||||
animation: trick3DTilt 3s ease-in-out infinite alternate;
|
|
||||||
}
|
|
||||||
@keyframes trick3DTilt {
|
|
||||||
0% {
|
|
||||||
transform: perspective(1000px) rotateX(0deg) rotateY(0deg);
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
transform: perspective(1000px) rotateX(15deg) rotateY(-15deg);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: perspective(1000px) rotateX(-15deg) rotateY(15deg);
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
transform: perspective(1000px) rotateX(15deg) rotateY(15deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: perspective(1000px) rotateX(-15deg) rotateY(-15deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
},
|
|
||||||
// 效果13: 页面所有链接失效
|
|
||||||
() => {
|
|
||||||
const clickHandler = (e: MouseEvent) => {
|
|
||||||
const target = e.target as HTMLElement
|
|
||||||
// 检查是否是链接或按钮
|
|
||||||
if (
|
|
||||||
target.tagName === "A" ||
|
|
||||||
target.closest("a") ||
|
|
||||||
(target.tagName === "BUTTON" && !target.closest("n-button"))
|
|
||||||
) {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
e.stopImmediatePropagation()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.addEventListener("click", clickHandler, true)
|
|
||||||
},
|
|
||||||
// 效果14: 页面变成波浪效果
|
|
||||||
() => {
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-wave-style"
|
|
||||||
style.textContent = `
|
|
||||||
body {
|
|
||||||
animation: trickWave 2s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
@keyframes trickWave {
|
|
||||||
0%, 100% {
|
|
||||||
transform: translateY(0) scaleY(1);
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
transform: translateY(-10px) scaleY(1.02);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: translateY(0) scaleY(1);
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
transform: translateY(10px) scaleY(0.98);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
},
|
|
||||||
// 效果15: 页面变成故障效果(Glitch)
|
|
||||||
() => {
|
|
||||||
const style = document.createElement("style")
|
|
||||||
style.id = "trick-glitch-style"
|
|
||||||
style.textContent = `
|
|
||||||
body {
|
|
||||||
animation: trickGlitch 0.3s infinite;
|
|
||||||
}
|
|
||||||
@keyframes trickGlitch {
|
|
||||||
0% {
|
|
||||||
transform: translate(0);
|
|
||||||
filter: hue-rotate(0deg);
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
transform: translate(-2px, 2px);
|
|
||||||
filter: hue-rotate(90deg);
|
|
||||||
}
|
|
||||||
40% {
|
|
||||||
transform: translate(-2px, -2px);
|
|
||||||
filter: hue-rotate(180deg);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translate(2px, 2px);
|
|
||||||
filter: hue-rotate(270deg);
|
|
||||||
}
|
|
||||||
80% {
|
|
||||||
transform: translate(2px, -2px);
|
|
||||||
filter: hue-rotate(360deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translate(0);
|
|
||||||
filter: hue-rotate(0deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
body::before {
|
|
||||||
content: '';
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background:
|
|
||||||
repeating-linear-gradient(
|
|
||||||
0deg,
|
|
||||||
transparent,
|
|
||||||
transparent 2px,
|
|
||||||
rgba(255, 0, 0, 0.03) 2px,
|
|
||||||
rgba(255, 0, 0, 0.03) 4px
|
|
||||||
),
|
|
||||||
repeating-linear-gradient(
|
|
||||||
90deg,
|
|
||||||
transparent,
|
|
||||||
transparent 2px,
|
|
||||||
rgba(0, 255, 255, 0.03) 2px,
|
|
||||||
rgba(0, 255, 255, 0.03) 4px
|
|
||||||
);
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 9999;
|
|
||||||
mix-blend-mode: difference;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
document.head.appendChild(style)
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
// 随机选择一种效果
|
|
||||||
const randomEffect = effects[Math.floor(Math.random() * effects.length)]
|
|
||||||
randomEffect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// function getChromeVersion() {
|
|
||||||
// var raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)
|
|
||||||
// return raw ? parseInt(raw[2], 10) : 0
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export const isLowVersion = getChromeVersion() < 80
|
|
||||||
|
|
||||||
// export const protocol = isLowVersion ? "http" : "https"
|
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ export const problemLanguageSchema = z.enum([
|
|||||||
"Flowchart",
|
"Flowchart",
|
||||||
])
|
])
|
||||||
|
|
||||||
/** 沙箱语言组成的数组(顺序即提权顺序,前端用它排语言 tab) */
|
|
||||||
export const JUDGE_LANGUAGES = judgeLanguageSchema.options
|
|
||||||
|
|
||||||
export type JudgeLanguage = z.infer<typeof judgeLanguageSchema>
|
export type JudgeLanguage = z.infer<typeof judgeLanguageSchema>
|
||||||
export type ProblemLanguage = z.infer<typeof problemLanguageSchema>
|
export type ProblemLanguage = z.infer<typeof problemLanguageSchema>
|
||||||
|
|||||||
Reference in New Issue
Block a user