Files
ojnext/src/oj/problemset/components/UserProgressView.vue
yuetsh 1cb96a1073
Some checks failed
Deploy / deploy (push) Has been cancelled
add flowchart dead data info
2025-12-13 17:48:31 +08:00

209 lines
5.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { h, computed, ref, onMounted, watch } from "vue"
import { watchDebounced } from "@vueuse/core"
import { parseTime } from "utils/functions"
import { ProblemSetProgress } from "utils/types"
import { getProblemSetUserProgress } from "../../api"
import { NTag } from "naive-ui"
import { usePagination } from "shared/composables/pagination"
import Pagination from "shared/components/Pagination.vue"
const route = useRoute()
const problemSetId = computed(() => Number(route.params.problemSetId))
const progress = ref<ProblemSetProgress[]>([])
const loading = ref(false)
const total = ref(0)
const statistics = ref<{
total: number
completed: number
avg_progress: number
} | null>(null)
const classFilter = ref<string>("")
// 使用分页 composable
const { query } = usePagination({}, { defaultLimit: 10 })
// 加载用户进度数据
async function loadUserProgress() {
loading.value = true
const offset = (query.page - 1) * query.limit
const params: { limit?: number; offset?: number; class_name?: string } = {
limit: query.limit,
offset,
}
if (classFilter.value.trim()) {
params.class_name = classFilter.value.trim()
}
const res = await getProblemSetUserProgress(problemSetId.value, params)
progress.value = res.data.results
total.value = res.data.total
// 使用后端返回的统计数据(基于所有数据)
if (res.data.statistics) {
statistics.value = res.data.statistics
}
loading.value = false
}
// 监听分页参数变化
watch([() => query.page, () => query.limit], loadUserProgress)
// 监听班级过滤变化(防抖)
watchDebounced(
classFilter,
() => {
query.page = 1 // 重置到第一页
loadUserProgress()
},
{ debounce: 500 },
)
// 使用后端返回的统计数据
const stats = computed(() => {
if (statistics.value) {
return {
total: statistics.value.total,
completed: statistics.value.completed,
avgProgress: Math.round(statistics.value.avg_progress),
}
}
// 如果后端还没有返回统计数据,使用默认值
return {
total: total.value,
completed: 0,
avgProgress: 0,
}
})
onMounted(loadUserProgress)
// 定义表格列
const progressColumns = [
{
title: "排名",
key: "rank",
width: 80,
render: (row: ProblemSetProgress, index: number) => {
// 计算全局排名:当前页偏移 + 当前行索引 + 1
const globalRank = (query.page - 1) * query.limit + index + 1
return globalRank
},
},
{
title: "用户",
key: "user.username",
width: 120,
render: (row: ProblemSetProgress) => row.user.username,
},
{
title: "加入时间",
key: "join_time",
width: 180,
render: (row: ProblemSetProgress) =>
parseTime(row.join_time, "YYYY-MM-DD HH:mm:ss"),
},
{
title: "已完成数量",
key: "completed_problems_count",
width: 100,
},
{
title: "已完成题目",
key: "completed_problems",
width: 300,
render: (row: ProblemSetProgress) => {
return h("div", { style: "max-height: 120px; overflow-y: auto" }, [
h(
"div",
{ style: "display: flex; flex-wrap: wrap; gap: 4px" },
row.completed_problems.map((problem: any) =>
h(
NTag,
{
type: "success",
size: "small",
style: "margin: 2px",
},
`${problem._id}: ${problem.title}`,
),
),
),
])
},
},
{
title: "进度",
key: "progress_percentage",
width: 120,
render: (row: ProblemSetProgress) => {
return `${row.progress_percentage.toFixed(0)}%`
},
},
{
title: "状态",
key: "is_completed",
width: 100,
render: (row: ProblemSetProgress) => {
if (row.is_completed) {
return h(NTag, { type: "success" }, "已完成")
} else {
return h(NTag, { type: "warning" }, "进行中")
}
},
},
]
</script>
<template>
<div>
<!-- 过滤条件 -->
<n-space style="margin-bottom: 16px" align="center">
<n-text>班级筛选</n-text>
<n-input
v-model:value="classFilter"
placeholder="输入班级名称"
style="width: 200px"
clearable
/>
</n-space>
<!-- 统计信息卡片 -->
<n-grid :cols="3" :x-gap="16" style="margin-bottom: 16px">
<n-grid-item>
<n-card size="small">
<n-statistic label="总参与人数" :value="stats.total" />
</n-card>
</n-grid-item>
<n-grid-item>
<n-card size="small">
<n-statistic label="已完成人数" :value="stats.completed" />
</n-card>
</n-grid-item>
<n-grid-item>
<n-card size="small">
<n-statistic
label="平均进度"
:value="stats.avgProgress.toFixed(0) + '%'"
/>
</n-card>
</n-grid-item>
</n-grid>
<n-data-table
:loading="loading"
:columns="progressColumns"
:data="progress"
:pagination="false"
:bordered="false"
:single-line="false"
/>
<Pagination
:total="total"
:limit="query.limit"
:page="query.page"
@update:limit="(limit: number) => (query.limit = limit)"
@update:page="(page: number) => (query.page = page)"
/>
</div>
</template>