update
Some checks failed
Deploy / deploy (build, debian, 22, /root/OJDeploy/data/clientnext) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822, /root/OJ/data/dist) (push) Has been cancelled

This commit is contained in:
2026-05-11 04:41:07 -06:00
parent 3503d19055
commit 73bde644f5
3 changed files with 38 additions and 19 deletions

View File

@@ -21,6 +21,14 @@ async function banUser() {
</script> </script>
<template> <template>
<n-flex> <n-flex>
<n-button
size="small"
type="info"
secondary
@click="$router.push({ name: 'ai', query: { username: props.user.username, duration: 'months:2' } })"
>
智能分析
</n-button>
<n-button <n-button
size="small" size="small"
type="error" type="error"

View File

@@ -8,7 +8,7 @@
<n-flex align="center"> <n-flex align="center">
<n-input <n-input
v-if="userStore.isSuperAdmin" v-if="userStore.isSuperAdmin"
v-model:value="aiStore.targetUsername" v-model:value="urlUsername"
placeholder="查看指定用户" placeholder="查看指定用户"
clearable clearable
style="width: 140px" style="width: 140px"
@@ -18,7 +18,7 @@
<n-select <n-select
style="width: 140px" style="width: 140px"
:options="options" :options="options"
v-model:value="aiStore.duration" v-model:value="urlDuration"
/> />
</n-flex> </n-flex>
</n-flex> </n-flex>
@@ -63,6 +63,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useBreakpoints } from "shared/composables/breakpoints" import { useBreakpoints } from "shared/composables/breakpoints"
import { formatISO, sub, type Duration } from "date-fns" import { formatISO, sub, type Duration } from "date-fns"
import { useRouteQuery } from "@vueuse/router"
import TagsRadarChart from "./components/TagsRadarChart.vue" import TagsRadarChart from "./components/TagsRadarChart.vue"
import DifficultyGradeChart from "./components/DifficultyGradeChart.vue" import DifficultyGradeChart from "./components/DifficultyGradeChart.vue"
import TimeActivityHeatmap from "./components/TimeActivityHeatmap.vue" import TimeActivityHeatmap from "./components/TimeActivityHeatmap.vue"
@@ -80,42 +81,41 @@ import { DURATION_OPTIONS } from "utils/constants"
const aiStore = useAIStore() const aiStore = useAIStore()
const userStore = useUserStore() const userStore = useUserStore()
const { isDesktop } = useBreakpoints() const { isDesktop } = useBreakpoints()
const options = [...DURATION_OPTIONS] const options = [...DURATION_OPTIONS]
const urlUsername = useRouteQuery<string>("username", "")
const urlDuration = useRouteQuery<string>("duration", "months:6")
// Initialize store synchronously from URL params before watch fires
aiStore.targetUsername = urlUsername.value
aiStore.duration = urlDuration.value
const subOptions = computed<Duration>(() => { const subOptions = computed<Duration>(() => {
let dur = options.find((it) => it.value === aiStore.duration) ?? options[0] let dur = options.find((it) => it.value === aiStore.duration) ?? options[0]
const x = dur.value!.toString().split(":") const x = dur.value!.toString().split(":")
const unit = x[0] return { [x[0]]: parseInt(x[1]) } as Duration
const n = x[1]
return { [unit]: parseInt(n) } as Duration
}) })
const start = computed(() => { const start = computed(() => formatISO(sub(new Date(), subOptions.value)))
const current = new Date() const end = computed(() => formatISO(new Date()))
return formatISO(sub(current, subOptions.value))
})
const end = computed(() => {
return formatISO(new Date())
})
function onUsernameChange() { function onUsernameChange() {
aiStore.targetUsername = urlUsername.value
aiStore.fetchHeatmapData() aiStore.fetchHeatmapData()
aiStore.fetchAnalysisData(start.value, end.value, aiStore.duration) aiStore.fetchAnalysisData(start.value, end.value, aiStore.duration)
} }
// 获取热力图数据(仅一次)
onMounted(() => { onMounted(() => {
aiStore.fetchHeatmapData() aiStore.fetchHeatmapData()
}) })
watch( watch(
() => aiStore.duration, () => urlDuration.value,
() => { (val) => {
aiStore.fetchAnalysisData(start.value, end.value, aiStore.duration) aiStore.duration = val
aiStore.fetchAnalysisData(start.value, end.value, val)
}, },
{ immediate: true }, { immediate: true },
) )

View File

@@ -4,12 +4,14 @@ import { NH2, NH3 } from "naive-ui"
import { getProfile } from "shared/api" import { getProfile } from "shared/api"
import { useBreakpoints } from "shared/composables/breakpoints" import { useBreakpoints } from "shared/composables/breakpoints"
import { durationToDays, parseTime } from "utils/functions" import { durationToDays, parseTime } from "utils/functions"
import { Profile, UserBadge as UserBadgeType } from "utils/types" import type { Profile, UserBadge as UserBadgeType } from "utils/types"
import { getMetrics, getUserBadges } from "../api" import { getMetrics, getUserBadges } from "../api"
import GroupedUserBadge from "shared/components/GroupedUserBadge.vue" import GroupedUserBadge from "shared/components/GroupedUserBadge.vue"
import { useUserStore } from "shared/store/user"
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const userStore = useUserStore()
const profile = ref<Profile | null>(null) const profile = ref<Profile | null>(null)
const problems = ref<string[]>([]) const problems = ref<string[]>([])
const firstSubmissionAt = ref("") const firstSubmissionAt = ref("")
@@ -204,6 +206,15 @@ onMounted(init)
}" }"
/> />
<h2>{{ profile.user.username }}</h2> <h2>{{ profile.user.username }}</h2>
<n-button
v-if="userStore.isSuperAdmin"
type="info"
secondary
size="small"
@click="router.push({ name: 'ai', query: { username: profile.user.username, duration: 'months:1' } })"
>
智能分析
</n-button>
<p class="desc">{{ profile.mood }}</p> <p class="desc">{{ profile.mood }}</p>
</n-flex> </n-flex>