chore(前端): 删掉三个从 ojnext 搬来就没人用的文件

阶段 1 是「原样搬入、不改业务代码」,所以 ojnext 里已经死掉的东西也一起
搬了过来。这三个文件全仓零引用:

- utils/permissions.ts(usePermissions / checkRoutePermission)
- oj/ai/components/StreakStats.vue
- shared/components/IconButton.vue

确认方式:在 apps/web 全部 .ts / .vue / .json / .html / .js 里搜这五个名字
(排除文件自身)零命中;两个组件也不在自动生成的 components.d.ts 里,
permissions.ts 的两个导出同样不在 auto-imports.d.ts 里 —— 也就是说
它们连自动导入的注册都没拿到,不存在「模板里隐式用到」的可能。

permissions.ts 在 ojnext 里也是死的,不是搬运过程产生的。

vue-tsc 0 error,vite build 通过。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 17:37:58 -06:00
parent 25c6e99c54
commit a03e22e60f
3 changed files with 0 additions and 303 deletions

View File

@@ -1,166 +0,0 @@
<template>
<n-card title="连续做题统计" size="small">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">激励持续学习</n-text>
</template>
<n-spin :show="aiStore.loading.heatmap" :delay="50">
<n-grid :cols="2" :x-gap="12" :y-gap="12">
<n-gi>
<n-statistic label="当前连续" :value="currentStreak">
<template #suffix>
<span style="font-size: 14px"></span>
<span
v-if="currentStreak > 0"
style="font-size: 20px; margin-left: 4px"
>
🔥
</span>
</template>
</n-statistic>
</n-gi>
<n-gi>
<n-statistic label="最长连续" :value="maxStreak">
<template #suffix>
<span style="font-size: 14px"></span>
<span
v-if="maxStreak >= 7"
style="font-size: 20px; margin-left: 4px"
>
</span>
</template>
</n-statistic>
</n-gi>
<n-gi>
<n-statistic label="本周做题" :value="weekCount">
<template #suffix>
<span style="font-size: 14px"></span>
</template>
</n-statistic>
</n-gi>
<n-gi>
<n-statistic label="本月做题" :value="monthCount">
<template #suffix>
<span style="font-size: 14px"></span>
</template>
</n-statistic>
</n-gi>
</n-grid>
<n-divider style="margin: 12px 0" />
<n-flex vertical size="small">
<n-text depth="2" style="font-size: 12px">
<span v-if="currentStreak === 0"> 开始做题建立学习连续记录 </span>
<span v-else-if="currentStreak < 3"> 继续保持争取连续3天 </span>
<span v-else-if="currentStreak < 7">
很棒继续保持一周连续记录
</span>
<span v-else-if="currentStreak < 30">
太棒了坚持满30天将获得持之以恒成就
</span>
<span v-else>
🎉 恭喜你你已经连续学习 {{ currentStreak }} 真的非常厉害
</span>
</n-text>
</n-flex>
</n-spin>
</n-card>
</template>
<script setup lang="ts">
import { useAIStore } from "oj/store/ai"
const aiStore = useAIStore()
// 计算连续天数
const streakData = computed(() => {
const heatmap = aiStore.heatmapData
if (!heatmap || heatmap.length === 0) {
return {
currentStreak: 0,
maxStreak: 0,
weekCount: 0,
monthCount: 0,
}
}
// 按时间戳排序
const sortedData = [...heatmap].sort((a, b) => a.timestamp - b.timestamp)
let currentStreak = 0
let maxStreak = 0
let tempStreak = 0
let lastDate: Date | null = null
const now = new Date()
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
const monthAgo = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)
let weekCount = 0
let monthCount = 0
// 遍历数据计算连续天数
for (const item of sortedData) {
if (item.value > 0) {
const currentDate = new Date(item.timestamp)
// 统计本周和本月
if (currentDate >= weekAgo) {
weekCount++
}
if (currentDate >= monthAgo) {
monthCount++
}
if (lastDate === null) {
tempStreak = 1
} else {
const dayDiff = Math.floor(
(currentDate.getTime() - lastDate.getTime()) / (24 * 60 * 60 * 1000),
)
if (dayDiff === 1) {
tempStreak++
} else {
maxStreak = Math.max(maxStreak, tempStreak)
tempStreak = 1
}
}
lastDate = currentDate
}
}
maxStreak = Math.max(maxStreak, tempStreak)
// 计算当前连续天数(必须包含今天或昨天)
if (lastDate) {
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
const lastDateOnly = new Date(
lastDate.getFullYear(),
lastDate.getMonth(),
lastDate.getDate(),
)
if (
lastDateOnly.getTime() === today.getTime() ||
lastDateOnly.getTime() === yesterday.getTime()
) {
currentStreak = tempStreak
} else {
currentStreak = 0
}
}
return {
currentStreak,
maxStreak,
weekCount,
monthCount,
}
})
const currentStreak = computed(() => streakData.value.currentStreak)
const maxStreak = computed(() => streakData.value.maxStreak)
const weekCount = computed(() => streakData.value.weekCount)
const monthCount = computed(() => streakData.value.monthCount)
</script>

View File

@@ -1,29 +0,0 @@
<template>
<n-tooltip>
<template #trigger>
<n-button round :type="type ?? 'default'" @click="$emit('click')">
<template #icon>
<Icon :icon="icon" />
</template>
</n-button>
</template>
{{ tip }}
</n-tooltip>
</template>
<script lang="ts" setup>
import { Icon } from "@iconify/vue"
defineProps<{
tip: string
icon: string
type?:
| "default"
| "tertiary"
| "primary"
| "info"
| "success"
| "warning"
| "error"
}>()
defineEmits(["click"])
</script>

View File

@@ -1,108 +0,0 @@
import { useUserStore } from "shared/store/user"
export function usePermissions() {
const userStore = useUserStore()
return {
isAuthenticated: computed(() => userStore.isAuthed),
isAdminRole: computed(() => userStore.isAdminRole),
isTeacherOrAbove: computed(() => userStore.isTeacherOrAbove),
isSuperAdmin: computed(() => userStore.isSuperAdmin),
hasProblemPermission: computed(() => userStore.hasProblemPermission),
canManageUsers: computed(() => userStore.isSuperAdmin),
canManageAnnouncements: computed(() => userStore.isSuperAdmin),
canManageTutorials: computed(() => userStore.isSuperAdmin),
canManageSystemConfig: computed(() => userStore.isSuperAdmin),
canSendMessages: computed(() => userStore.isSuperAdmin),
canManageProblems: computed(() => userStore.hasProblemPermission),
canManageContests: computed(() => userStore.isTeacherOrAbove),
canManageProblemsets: computed(() => userStore.isTeacherOrAbove),
canViewClassroomData: computed(() => userStore.isTeacherOrAbove),
canManageAllProblems: computed(
() =>
userStore.user?.problemPermission === "All" || userStore.isSuperAdmin,
),
canManageOwnProblems: computed(
() =>
userStore.user?.problemPermission === "Own" && !userStore.isSuperAdmin,
),
getUserPermissionLevel: computed(() => {
if (userStore.isSuperAdmin) return "超级管理员"
if (userStore.isTeacherAdmin) return "教师管理员"
if (userStore.isStudentAdmin) return "学生管理员"
return "普通用户"
}),
getProblemPermissionLevel: computed(() => {
if (!userStore.user) return "无权限"
switch (userStore.user.problemPermission) {
case "All":
return "管理所有题目"
case "Own":
return "管理自己的题目"
case "None":
return "无题目权限"
default:
return "无权限"
}
}),
}
}
export function checkRoutePermission(routeName: string): boolean {
const userStore = useUserStore()
const superAdminRoutes = [
"admin home",
"admin config",
"admin user list",
"admin user generate",
"admin announcement list",
"admin announcement create",
"admin announcement edit",
"admin message list",
"admin tutorial list",
"admin tutorial create",
"admin tutorial edit",
]
const teacherAdminRoutes = [
"admin contest list",
"admin contest create",
"admin contest edit",
"admin contest problem list",
"admin contest problem create",
"admin contest problem edit",
"admin contest helper",
"admin problemset list",
"admin problemset create",
"admin problemset edit",
"admin problemset detail",
"admin stuck problems",
"admin top ac trend",
]
const problemPermissionRoutes = [
"admin problem list",
"admin problem create",
"admin problem edit",
]
if (superAdminRoutes.includes(routeName)) {
return userStore.isSuperAdmin
}
if (teacherAdminRoutes.includes(routeName)) {
return userStore.isTeacherOrAbove
}
if (problemPermissionRoutes.includes(routeName)) {
return userStore.hasProblemPermission
}
return true
}