feat(admin): 新增用户排行榜功能并优化活动流程

- 新增用户排行榜页面和待发布页面,支持查看和发布排行榜
- 新增排行榜相关API接口,包括获取活动排行列表、修改队伍分数、发布排名和获取用户排名列表
- 新增RankPublishStatus枚举,用于管理排行榜发布状态
- 在活动卡片中新增结束活动按钮,点击后调用结束活动接口并生成排行榜数据
- 优化用户活动首页,只显示已发布和处理中的活动
- 调整游戏页面定时刷新间隔从2秒改为3秒,优化性能
- 修复排行榜详情页分数输入最大值限制问题
- 清理未使用的代码文件,移除results-detail-copy相关组件和路由
- 更新路由配置和类型定义,支持新的排行榜页面
This commit is contained in:
2026-03-09 08:37:32 +08:00
parent e8cd6732d7
commit 8469e16638
28 changed files with 627 additions and 502 deletions

View File

@ -3,6 +3,7 @@ import { NButton, NGi, NGrid, NSpace } from 'naive-ui'
import { computed, onMounted, ref } from 'vue'
import { useRouterPush } from '@/hooks/common/router'
import { fetchCopyActivity, fetchDeleteActivity, fetchGetActivityList } from '@/service/api/competition'
import { endActivity } from '@/service/api/result'
import { useAppStore } from '@/store/modules/app'
import CompetitionAddModal from '../competition-add/index.vue'
@ -94,6 +95,39 @@ function toDetail(item: Api.Competition.CompetitionItem) {
routerPushByKey('competition_competition-detail', { query: { Id: item.Id.toString() } })
}
async function handleCloseActivity(item: Api.Competition.CompetitionItem) {
const d = window.$dialog?.warning({
title: '确认结束',
content: `确定结束活动:${item.Name}吗?结束活动后将生成排行榜数据。`,
positiveText: '确认结束',
negativeText: '取消',
onPositiveClick: async () => {
if (d) {
d.loading = true
}
try {
const { error } = await endActivity(item.Id)
if (error) {
window.$message?.error(error.message || '结束活动失败')
return false
}
window.$message?.success(`已结束活动:${item.Name}`)
fetchActivityList()
return true
}
catch (e: any) {
window.$message?.error(e.message || '操作失败')
return false
}
finally {
if (d) {
d.loading = false
}
}
},
})
}
onMounted(() => fetchActivityList())
</script>
@ -122,7 +156,7 @@ onMounted(() => fetchActivityList())
<!-- 响应式布局手机1列平板2列中屏3列大屏4列 -->
<NGrid :x-gap="gap" :y-gap="gap" responsive="screen" item-responsive>
<NGi v-for="item in competitionListRef" :key="item.Id" span="24 s:12 m:8 l:6" @click="toDetail(item)">
<CompetitionCard :item="item" @copy="handleCopy" @delete="handleDelete" />
<CompetitionCard :item="item" @copy="handleCopy" @delete="handleDelete" @close="handleCloseActivity" />
</NGi>
</NGrid>

View File

@ -10,6 +10,7 @@ const props = defineProps<Props>()
const emit = defineEmits<{
(e: 'copy', item: Api.Competition.CompetitionItem): void
(e: 'delete', item: Api.Competition.CompetitionItem): void
(e: 'close', item: Api.Competition.CompetitionItem): void
}>()
const { options: publishStatusOptions } = useDict('publish_status')
@ -83,6 +84,20 @@ const decorationBgClass = computed(() => {
<div
class="absolute right-4 top-4 z-20 flex gap-2 opacity-0 transition-opacity duration-300 group-hover:opacity-100"
>
<NTooltip v-if="[PublishStatus.Published, PublishStatus.Processing].includes(item.PublishStatus)">
<template #trigger>
<NButton
circle secondary size="small" type="warning"
class="shadow-sm backdrop-blur-sm !bg-white/80 hover:!bg-orange-50" @click.stop="emit('close', item)"
>
<template #icon>
<icon-ic:round-stop-circle class="text-icon" />
</template>
</NButton>
</template>
结束活动
</NTooltip>
<NTooltip>
<template #trigger>
<NButton