Files
reader-star/apps/admin/src/views/rank/rank-list/index.vue
rjl f1dacf7c66 feat(admin): 新增题库、排行榜、实时结果和模板管理功能
- 添加题库管理页面及相关组件
- 实现排行榜管理功能,包括列表和详情页
- 新增实时结果展示页面
- 添加模板制作和管理功能
- 完善路由配置和国际化支持
- 新增阿里云OSS文件上传服务
- 添加多种工具函数和类型定义
- 优化表格和表单组件
- 调整布局和样式细节
2026-01-22 17:55:45 +08:00

185 lines
5.5 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="tsx">
import { NButton, NCard, NTag } from 'naive-ui'
import { reactive } from 'vue'
import { useRouter } from 'vue-router'
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table'
import { fetchGetUserList } from '@/service/api'
import { useAppStore } from '@/store/modules/app'
import RankSearch from './modules/rank-search.vue'
const appStore = useAppStore()
const router = useRouter()
const searchParams: Api.SystemManage.UserSearchParams = reactive({
current: 1,
size: 10,
status: null,
userName: null,
userGender: null,
nickName: null,
userPhone: null,
userEmail: null,
})
const rankTitle = '排行榜管理列表'
const statusMap: Record<string, string> = {
1: '已发布',
2: '待审核',
}
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination } = useNaivePaginatedTable({
api: () => fetchGetUserList(searchParams),
transform: (response) => {
const transformed = defaultTransform(response)
// Mocking data for Rank Management based on User API response
const mockCompetitions = ['阅读之星-辞海遨游环节 (2026)', '古诗词大会年度精英巅峰赛', '趣味百科常识挑战周']
const mockDates = ['2026.01.12', '2026.01.15', '2026.01.18']
const mockScales = ['40 支队伍', '24 支队伍', '60 支队伍']
const mockScores = ['145', '180', '120']
const mockTimes = ['2026-01-22 16:30:12', '2026-01-22 12:00:00', '2026-01-21 18:22:45']
transformed.data.forEach((item, index) => {
const mockIndex = index % 3
item.userName = mockCompetitions[mockIndex] // Competition Name
item.userEmail = mockDates[mockIndex] // Activity Date (using userEmail field)
item.userPhone = mockScales[mockIndex] // Team Scale (using userPhone field)
item.nickName = mockScores[mockIndex] // Max Score (using nickName field)
// item.createTime is typically available, we'll simulate update time
item.createTime = mockTimes[mockIndex]
// Status: 1 (Published), 2 (Pending)
item.status = (index % 2 === 0) ? '2' : '1'
})
return transformed
},
onPaginationParamsChange: (params) => {
searchParams.current = params.page
searchParams.size = params.pageSize
},
columns: () => [
{
type: 'selection',
align: 'center',
width: 48,
},
{
key: 'userName',
title: '比赛活动名称',
align: 'left',
minWidth: 200,
},
{
key: 'userEmail',
title: '活动日期',
align: 'center',
minWidth: 120,
},
{
key: 'userPhone',
title: '队伍规模',
align: 'center',
minWidth: 100,
},
{
key: 'nickName',
title: '最高积分',
align: 'center',
minWidth: 100,
render: row => (
<span class="text-primary font-bold">
{row.nickName}
{' '}
Pts
</span>
),
},
{
key: 'createTime',
title: '最后更新时间',
align: 'center',
minWidth: 160,
},
{
key: 'status',
title: '发布状态',
align: 'center',
width: 100,
render: (row) => {
if (row.status === null)
return null
const label = statusMap[row.status] || '未知'
// 1: Published (Success/Green), 2: Pending (Warning/Orange)
return <NTag type={row.status === '1' ? 'success' : 'warning'}>{label}</NTag>
},
},
{
key: 'operate',
title: '操作',
align: 'center',
width: 120,
render: row => (
<div class="flex-center">
<NButton type="primary" text onClick={() => edit(row.id)}>
进入排行详情
</NButton>
</div>
),
},
],
})
const {
drawerVisible,
operateType,
editingData,
handleEdit,
checkedRowKeys,
onBatchDeleted,
} = useTableOperate(data, 'id', getData)
function edit(id: number) {
router.push({ name: 'rank_rank-detail', query: { id } })
}
function handleBatchPublish() {
window.$message?.success('批量发布成功')
}
</script>
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<RankSearch v-model:model="searchParams" @search="getDataByPage" />
<NCard :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header>
<div class="flex items-center justify-between">
<div>
<div class="text-16px font-bold">
{{ rankTitle }}
</div>
<div class="mt-4px text-12px text-gray-500">
您可以对所有比赛场次的最终积分进行核对修正并正式发布结果
</div>
</div>
<NButton type="primary" ghost class="ml-auto" @click="handleBatchPublish">
<template #icon>
<icon-ic-round-upload class="text-icon" />
</template>
批量发布至终端
</NButton>
</div>
</template>
<!-- <template #header-extra>
<TableHeaderOperation v-model:columns="columnChecks" :disabled-delete="checkedRowKeys.length === 0"
:loading="loading" @add="handleAdd" @delete="handleBatchDelete" @refresh="getData" />
</template> -->
<NDataTable
v-model:checked-row-keys="checkedRowKeys" :columns="columns" :data="data" size="small" striped
:flex-height="!appStore.isMobile" :scroll-x="962" :loading="loading" remote :row-key="row => row.id"
:pagination="mobilePagination" class="sm:h-full"
/>
</NCard>
</div>
</template>
<style scoped></style>