# 报名数据模块优化 — 设计方案

> 所属端：超管端
> 状态：已实现（待验收）
> 创建日期：2026-03-27
> 最后更新：2026-03-27

---

## 1. 背景与问题

超管端"报名数据"菜单目前是两层结构：第一层按活动维度展示活动列表（每行一个活动，显示报名人数），点击"报名记录"进入第二层查看某活动的具体报名记录。

### 核心问题

| 问题 | 说明 |
|------|------|
| 第一层与全部活动列表重复 | Index.vue 本质是活动列表精简版，而全部活动列表已有可点击的报名人数列，两个入口功能重叠 |
| 超管写操作未隔离 | "启动报名/关闭报名"、"通过/拒绝/删除"等写操作对超管应隐藏 |
| 缺少全局统计 | 没有报名总量、各审核状态分布等概览 |
| 无跨活动搜索 | 只能看某个活动的报名记录，不能跨活动搜索（如"某个用户报了哪些活动"） |
| 样式不一致 | 主色写死 `#1890ff` |

---

## 2. 现状分析

### 2.1 第一层：报名管理（registrations/Index.vue）

- 个人参与/团队参与 Tab 切换
- 数据来源：`contestsApi.getList()` 按活动类型过滤
- 搜索：仅活动名称
- 操作：报名记录（跳转）、启动报名、关闭报名
- **本质是活动列表的子集视图，对超管来说是冗余页面**

### 2.2 第二层：报名记录（registrations/Records.vue）

- 数据来源：`registrationsApi.getList({ contestId })` 查某活动的报名记录
- 搜索：机构、姓名/队伍名、报名账号、审核状态、报名时间
- 功能：批量审核、导出、通过/拒绝/删除、详细信息 Drawer、团队成员弹窗

---

## 3. 设计方案

### 3.1 整体思路

**超管端将两层合并为一层**：一个扁平的报名记录列表，展示全平台所有活动的报名数据，通过筛选条件实现分活动查看。

理由：
1. 活动维度的概览已由"全部活动"列表承担（报名数字可点击跳转）
2. 超管需要的是跨活动的全局视角，快速找到某条报名记录
3. 从"全部活动"列表点击报名数字时，带 contestId 参数跳转过来，自动筛选该活动

机构端保持现有两层结构不变。

### 3.2 页面结构

```
┌─ 标题卡片 ──────────────────────────────────────────────┐
│  报名数据                                                │
└─────────────────────────────────────────────────────────┘

┌─ 统计卡片 ──────────────────────────────────────────────┐
│ [全部 256] [待审核 38] [已通过 200] [已拒绝 18]           │
└─────────────────────────────────────────────────────────┘

┌─ 筛选栏 ────────────────────────────────────────────────┐
│ 所属活动:[下拉搜索] 参与者:[______] 审核状态:[下拉]       │
│ 参与方式:[下拉] 报名时间:[日期范围]    [搜索] [重置]      │
└─────────────────────────────────────────────────────────┘

┌─ 数据表格 ──────────────────────────────────────────────┐
│ 所属活动 | 参与者 | 参与方式 | 报名账号 | 主办机构 |      │
│          | 审核状态 | 报名时间 | 操作                     │
└─────────────────────────────────────────────────────────┘
```

### 3.3 统计卡片

4 张卡片，可点击切换筛选：

| 卡片 | 数据 | 颜色 |
|------|------|------|
| 全部 | 报名总数 | 主色 |
| 待审核 | pending 数量 | 橙色 |
| 已通过 | passed 数量 | 绿色 |
| 已拒绝 | rejected 数量 | 红色 |

数据来源：新增 `GET /api/registrations/stats` 接口。

### 3.4 筛选栏

| 筛选项 | 组件 | 说明 |
|--------|------|------|
| 所属活动 | Select（远程搜索） | 按活动名称搜索下拉，选择后按 contestId 过滤 |
| 参与者 | Input | 搜索参与者姓名、手机号、子女姓名 |
| 审核状态 | Select | 待审核 / 已通过 / 已拒绝 |
| 参与方式 | Select | 本人参与 / 代子女报名 |
| 报名时间 | RangePicker | 时间范围筛选 |

**联动逻辑**：从"全部活动"列表点击报名数字跳转时，URL 带 `?contestId=xx`，页面初始化时自动设置"所属活动"筛选。

### 3.5 表格列

| 列 | 宽度 | 渲染方式 |
|----|------|----------|
| 所属活动 | 180 | 活动名称，可点击跳转活动详情 |
| 参与者 | 140 | 昵称；代子女报名时附带子女姓名 |
| 参与方式 | 100 | Tag：本人参与（蓝）/ 代子女报名（绿） |
| 报名账号 | 120 | username |
| 主办机构 | 120 | 活动创建者所属租户名 |
| 审核状态 | 90 | Tag：待审核（橙）/ 已通过（绿）/ 已拒绝（红） |
| 报名时间 | 150 | YYYY-MM-DD HH:mm |
| 操作 | 80 | 查看详情 |

超管操作列**只有"查看详情"**，不提供通过/拒绝/删除。

### 3.6 详情 Drawer

点击"查看详情"打开右侧 Drawer：

```
基本信息（Descriptions）
├── 所属活动 / 主办机构
├── 参与者昵称 / 报名账号 / 手机号
├── 参与方式 / 子女姓名（如有）
├── 审核状态 / 审核理由（如有）
└── 报名时间

子女信息（参与方式=代子女报名时显示）
├── 姓名 / 性别 / 年龄 / 年级
├── 城市 / 学校

团队信息（团队参与时显示）
├── 队伍名称 / 队长 / 成员数
└── 成员列表表格
```

### 3.7 URL 参数支持

- `/super/contests/registrations` — 全部报名记录
- `/super/contests/registrations?contestId=3` — 筛选某个活动的报名记录（从活动列表跳转）
- `/super/contests/registrations?status=pending` — 筛选待审核记录

页面 `onMounted` 时读取 URL query 参数，自动设置对应筛选条件。

---

## 4. 后端改动

### 4.1 新增 GET /api/registrations/stats

超管调用时返回全平台报名统计：

```json
{
  "total": 256,
  "pending": 38,
  "passed": 200,
  "rejected": 18
}
```

支持可选的 `contestId` 参数，传入时只统计该活动。

### 4.2 扩展 GET /api/registrations（现有接口）

超管调用时：
- `contestId` 变为可选参数（当前是必填），不传时查全部活动的报名记录
- 新增 `participantType` 参数：`self` / `child`
- 新增 `keyword` 参数：搜索参与者姓名、手机号、子女姓名
- 返回数据增加活动信息：`contest: { id, contestName, creatorTenant: { name } }`
- 返回数据增加子女信息：`child: { name, gender, birthday, grade, city, schoolName }`

普通租户调用时保持现有逻辑不变。

---

## 5. 前端改动

| 文件 | 操作 | 说明 |
|------|------|------|
| `frontend/src/api/contests.ts` | 修改 | 扩展 QueryRegistrationParams，新增 registrations stats 接口 |
| `frontend/src/views/contests/registrations/Index.vue` | 修改 | 超管端改为扁平报名记录列表（统计卡片+筛选+表格+Drawer），机构端保持不变 |
| `backend/src/contests/registrations/registrations.service.ts` | 修改 | 扩展 findAll 支持跨活动查询，新增 getStats |
| `backend/src/contests/registrations/registrations.controller.ts` | 修改 | 新增 stats 端点，findAll 增加参数 |

---

## 6. 实施记录

### 2026-03-27 — 首次实现

**后端改动（3 个文件）：**
- `backend/src/contests/registrations/dto/query-registration.dto.ts` — 新增 participantType、keyword 参数
- `backend/src/contests/registrations/registrations.service.ts` — 新增 getStats() 方法；findAll 增加 participantType 和 keyword（搜索昵称/手机/子女名）过滤
- `backend/src/contests/registrations/registrations.controller.ts` — 新增 GET /api/contests/registrations/stats 端点

**前端改动（2 个文件）：**
- `frontend/src/api/contests.ts` — 扩展 QueryRegistrationParams（participantType/keyword），新增 RegistrationStats 类型和 getStats 接口
- `frontend/src/views/contests/registrations/Index.vue` — 完全重写：超管端为扁平报名记录列表（统计卡片+筛选栏+全平台表格+详情Drawer），机构端保持原有两层结构；支持 URL 参数联动（?contestId=xx&status=pending）
