@@ -361,6 +361,9 @@ export function getProblemSetBadges(problemSetId: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取题单用户进度列表
|
// 获取题单用户进度列表
|
||||||
export function getProblemSetUserProgress(problemSetId: number) {
|
export function getProblemSetUserProgress(
|
||||||
return http.get(`problemset/${problemSetId}/users_progress`)
|
problemSetId: number,
|
||||||
|
params?: { limit?: number; offset?: number },
|
||||||
|
) {
|
||||||
|
return http.get(`problemset/${problemSetId}/users_progress`, { params })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,49 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { h, computed, ref, onMounted } from "vue"
|
import { h, computed, ref, onMounted, watch } from "vue"
|
||||||
import { Icon } from "@iconify/vue"
|
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import { ProblemSetProgress } from "utils/types"
|
import { ProblemSetProgress } from "utils/types"
|
||||||
import { getProblemSetUserProgress } from "../../api"
|
import { getProblemSetUserProgress } from "../../api"
|
||||||
import { NP, NProgress, NTag, useMessage } from "naive-ui"
|
import { NTag } from "naive-ui"
|
||||||
|
import { usePagination } from "shared/composables/pagination"
|
||||||
const message = useMessage()
|
import Pagination from "shared/components/Pagination.vue"
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const problemSetId = computed(() => Number(route.params.problemSetId))
|
const problemSetId = computed(() => Number(route.params.problemSetId))
|
||||||
const progress = ref<ProblemSetProgress[]>([])
|
const progress = ref<ProblemSetProgress[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const total = ref(0)
|
||||||
|
|
||||||
|
// 使用分页 composable
|
||||||
|
const { query } = usePagination({}, { defaultLimit: 10 })
|
||||||
|
|
||||||
// 加载用户进度数据
|
// 加载用户进度数据
|
||||||
async function loadUserProgress() {
|
async function loadUserProgress() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
const offset = (query.page - 1) * query.limit
|
||||||
const res = await getProblemSetUserProgress(problemSetId.value)
|
const res = await getProblemSetUserProgress(problemSetId.value, {
|
||||||
progress.value = res.data
|
limit: query.limit,
|
||||||
} catch (err: any) {
|
offset,
|
||||||
message.error("加载用户进度失败:" + (err.data || "未知错误"))
|
})
|
||||||
} finally {
|
|
||||||
loading.value = false
|
progress.value = res.data.results
|
||||||
}
|
total.value = res.data.total
|
||||||
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 监听分页参数变化
|
||||||
|
watch([() => query.page, () => query.limit], loadUserProgress)
|
||||||
|
|
||||||
// 计算统计数据
|
// 计算统计数据
|
||||||
const stats = computed(() => {
|
const stats = computed(() => {
|
||||||
const total = progress.value.length
|
|
||||||
const completed = progress.value.filter((p) => p.is_completed).length
|
const completed = progress.value.filter((p) => p.is_completed).length
|
||||||
const avgProgress =
|
const avgProgress =
|
||||||
total > 0
|
progress.value.length > 0
|
||||||
? progress.value.reduce((sum, p) => sum + p.progress_percentage, 0) /
|
? progress.value.reduce((sum, p) => sum + p.progress_percentage, 0) /
|
||||||
total
|
progress.value.length
|
||||||
: 0
|
: 0
|
||||||
|
|
||||||
return {
|
return {
|
||||||
total,
|
total: total.value,
|
||||||
completed,
|
completed,
|
||||||
avgProgress: Math.round(avgProgress),
|
avgProgress: Math.round(avgProgress),
|
||||||
}
|
}
|
||||||
@@ -51,7 +57,11 @@ const progressColumns = [
|
|||||||
title: "排名",
|
title: "排名",
|
||||||
key: "rank",
|
key: "rank",
|
||||||
width: 80,
|
width: 80,
|
||||||
render: (row: ProblemSetProgress, index: number) => index + 1,
|
render: (row: ProblemSetProgress, index: number) => {
|
||||||
|
// 计算全局排名:当前页偏移 + 当前行索引 + 1
|
||||||
|
const globalRank = (query.page - 1) * query.limit + index + 1
|
||||||
|
return globalRank
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "用户",
|
title: "用户",
|
||||||
@@ -116,16 +126,10 @@ const progressColumns = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<n-flex justify="space-between" align="center" style="margin-bottom: 16px">
|
|
||||||
<n-h3 style="margin: 0">用户进度</n-h3>
|
|
||||||
<n-text depth="3">共 {{ stats.total }} 人参与</n-text>
|
|
||||||
</n-flex>
|
|
||||||
|
|
||||||
<!-- 统计信息卡片 -->
|
<!-- 统计信息卡片 -->
|
||||||
<n-grid :cols="3" :x-gap="16" style="margin-bottom: 16px">
|
<n-grid :cols="3" :x-gap="16" style="margin-bottom: 16px">
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
@@ -140,18 +144,29 @@ const progressColumns = [
|
|||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
<n-card size="small">
|
<n-card size="small">
|
||||||
<n-statistic label="平均进度" :value="stats.avgProgress.toFixed(0) + '%'" />
|
<n-statistic
|
||||||
|
label="平均进度"
|
||||||
|
:value="stats.avgProgress.toFixed(0) + '%'"
|
||||||
|
/>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
</n-grid>
|
</n-grid>
|
||||||
|
|
||||||
<n-data-table
|
<n-data-table
|
||||||
|
:loading="loading"
|
||||||
:columns="progressColumns"
|
:columns="progressColumns"
|
||||||
:data="progress"
|
:data="progress"
|
||||||
:loading="loading"
|
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
:single-line="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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user