diff --git a/src/api/admin/dashboard.ts b/src/api/admin/dashboard.ts new file mode 100644 index 0000000..ca51ee3 --- /dev/null +++ b/src/api/admin/dashboard.ts @@ -0,0 +1,8 @@ +import { adminHttp } from './http' +import type { AdminDashboardData } from './types' + +export async function getAdminDashboard(competitionId: number): Promise { + const { data } = await adminHttp.get(`/competitions/${competitionId}/dashboard`) + if (!data || typeof data !== 'object') throw new Error('首页统计响应无效') + return data as AdminDashboardData +} diff --git a/src/api/admin/types.ts b/src/api/admin/types.ts index 0dee885..8789a2a 100644 --- a/src/api/admin/types.ts +++ b/src/api/admin/types.ts @@ -162,6 +162,71 @@ export interface AdminApplicationDetail extends AdminApplicationRow { review_scores: AdminApplicationReviewScoreRow[] } +export interface AdminDashboardCompetition { + id: number + slug: string + name: string + status: string + published: boolean + signup_open_at: string | null + signup_close_at: string | null + form_schema_id: number | null + review_form_schema_id: number | null + pledge_content_html: string | null +} + +export interface AdminDashboardSummary { + applications_total: number + applications_draft: number + applications_submitted: number + applications_reviewed: number + applications_pending_review: number + review_scores_total: number + reviewers_total: number + reviewers_active: number + reviewer_scopes_total: number + tracks_total: number + tracks_enabled: number +} + +export interface AdminDashboardSetup { + published: boolean + signup_form_schema: boolean + tracks: boolean + pledge: boolean + review_form_schema: boolean + reviewer_scopes: boolean +} + +export interface AdminDashboardTrackStat { + track_code: string + track_title: string + is_enabled: boolean + applications_count: number + submitted_count: number + reviewer_scope_count: number +} + +export interface AdminDashboardRecentApplication { + id: number + status: string + project_name: string + player_name: string + track_code: string + track_title: string + submitted_at: string | null + updated_at: string | null + submitted_review_count: number +} + +export interface AdminDashboardData { + competition: AdminDashboardCompetition + summary: AdminDashboardSummary + setup: AdminDashboardSetup + track_stats: AdminDashboardTrackStat[] + recent_applications: AdminDashboardRecentApplication[] +} + export type SignupChannelStatus = 'enabled' | 'disabled' export type SignupChannelCallbackType = 'none' | 'web' | 'mini_program' export type SignupChannelMiniProgramMethod = 'navigateTo' | 'redirectTo' | 'reLaunch' | 'switchTab' diff --git a/src/router/admin/adminComponentMap.ts b/src/router/admin/adminComponentMap.ts index 6ca3a24..200e4f6 100644 --- a/src/router/admin/adminComponentMap.ts +++ b/src/router/admin/adminComponentMap.ts @@ -2,6 +2,7 @@ * 路由 name → 异步组件。服务端仅下发 name/path/title,实际组件映射在此维护。 */ export const ADMIN_COMPONENT_LOADERS: Record Promise> = { + 'admin-dashboard': () => import('../../views/admin/AdminDashboardView.vue'), 'admin-competitions-list': () => import('../../views/admin/competition/CompetitionListView.vue'), 'admin-competition-form': () => import('../../views/admin/competition/CompetitionFormView.vue'), 'admin-competition-workspace': () => import('../../views/admin/competition/CompetitionFormView.vue'), diff --git a/src/router/admin/registerDynamicRoutes.ts b/src/router/admin/registerDynamicRoutes.ts index 4521dd2..5aecf87 100644 --- a/src/router/admin/registerDynamicRoutes.ts +++ b/src/router/admin/registerDynamicRoutes.ts @@ -84,7 +84,7 @@ export async function registerAdminDynamicRoutes(router: Router) { router.addRoute('AdminLayout', { path: '', name: 'admin-home-redirect', - redirect: { name: 'admin-competitions-list' }, + redirect: { name: 'admin-dashboard' }, }) remember('admin-home-redirect') diff --git a/src/router/index.ts b/src/router/index.ts index 86ebe09..fbd858f 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -153,7 +153,7 @@ router.beforeEach(async (to) => { if (!adminDynamicRoutesRegistered()) { await registerAdminDynamicRoutes(router) } - return { name: 'admin-competitions-list', replace: true } + return { name: 'admin-dashboard', replace: true } } return true } diff --git a/src/views/admin/AdminDashboardView.vue b/src/views/admin/AdminDashboardView.vue new file mode 100644 index 0000000..486ee1c --- /dev/null +++ b/src/views/admin/AdminDashboardView.vue @@ -0,0 +1,631 @@ + + + + +