# 作品数据模块优化 — 设计方案

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

---

## 1. 背景与问题

超管端"作品数据"菜单目前是两层结构：第一层按活动维度展示活动列表（每行一个活动，显示作品提交数），点击"查看详情"进入第二层查看某活动的具体作品。问题模式与报名数据完全一致。

### 核心问题

| 问题 | 说明 |
|------|------|
| 第一层与全部活动列表重复 | Index.vue 是活动列表精简版，全部活动列表已有可点击的作品数列 |
| 超管写操作未隔离 | "分配评委"按钮、批量分配、分配抽屉均为机构管理端功能，超管不应有 |
| 缺少全局统计 | 没有作品总量、提交率等概览 |
| 无跨活动搜索 | 不能跨活动搜索某个作者的作品 |
| 第二层围绕"分配评委"设计 | 分配状态列、评委列、分配操作均为管理功能，超管只需查看 |
| 样式不一致 | 主色写死 `#1890ff` |

---

## 2. 现状分析

### 2.1 第一层：作品管理（works/Index.vue）

- 个人参与/团队参与 Tab 切换
- 数据来源：`contestsApi.getList()` 按活动类型过滤
- 搜索：仅活动名称
- 表格列：序号/活动名称/主办机构/报名人数/已递交÷应递交/活动时间/操作
- 操作：查看详情（跳转 WorksDetail）

### 2.2 第二层：参赛作品（works/WorksDetail.vue）

- 数据来源：`worksApi.getList({ contestId })`
- 搜索：选手名/报名账号/作品编号/分配状态/递交时间/机构
- 表格列：序号/作品编号/报名账号/选手名/递交时间/分配状态/评委/操作
- 功能：作品详情弹框（WorkDetailModal）、分配评委抽屉（批量+单个）、行勾选

---

## 3. 设计方案

### 3.1 整体思路

与报名数据优化方案一致：**超管端将两层合并为一层**，扁平展示全平台所有作品记录，通过筛选条件切换活动。机构端保持原有两层结构不变。

超管端隐藏所有写操作（分配评委、批量分配），只保留查看能力。

### 3.2 页面结构

```
┌─ 标题卡片 ──────────────────────────────────────────────┐
│  作品数据                                                │
└─────────────────────────────────────────────────────────┘

┌─ 统计卡片 ──────────────────────────────────────────────┐
│ [全部 120] [已提交 85] [评审中 30] [已评完 20]            │
└─────────────────────────────────────────────────────────┘

┌─ 筛选栏 ────────────────────────────────────────────────┐
│ 所属活动:[下拉搜索] 作者/编号:[______] 作品状态:[下拉]    │
│ 递交时间:[日期范围]                     [搜索] [重置]     │
└─────────────────────────────────────────────────────────┘

┌─ 数据表格 ──────────────────────────────────────────────┐
│ 所属活动 | 作品编号 | 提交者 | 报名账号 | 主办机构 |      │
│          | 作品状态 | 递交时间 | 操作                     │
└─────────────────────────────────────────────────────────┘
```

### 3.3 统计卡片

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

| 卡片 | 数据 | 颜色 | 说明 |
|------|------|------|------|
| 全部 | 作品总数 | 主色 | 全平台所有作品 |
| 已提交 | submitted 数量 | 蓝色 | 已提交待评审 |
| 评审中 | reviewing 数量 | 橙色 | 正在评审（已分配评委） |
| 已评完 | reviewed 数量 | 绿色 | 评审完成 |

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

### 3.4 筛选栏

| 筛选项 | 组件 | 说明 |
|--------|------|------|
| 所属活动 | Select（远程搜索） | 按活动名称搜索下拉，选择后按 contestId 过滤 |
| 作者/编号 | Input | 搜索作品编号、提交者姓名 |
| 作品状态 | Select | 已提交 / 评审中 / 已评完 |
| 递交时间 | RangePicker | 时间范围筛选 |

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

### 3.5 表格列

| 列 | 宽度 | 渲染方式 |
|----|------|----------|
| 所属活动 | 180 | 活动名称，可点击跳转活动详情 |
| 作品编号 | 130 | workNo，可点击查看作品详情弹框 |
| 提交者 | 130 | 提交者昵称；团队参与时显示队伍名 |
| 报名账号 | 120 | username |
| 主办机构 | 120 | 提交者所属租户名 |
| 作品状态 | 90 | Tag：已提交（蓝）/ 评审中（橙）/ 已评完（绿） |
| 递交时间 | 150 | YYYY-MM-DD HH:mm |
| 操作 | 80 | 查看详情 |

超管操作列**只有"查看详情"**（打开 WorkDetailModal），不提供分配评委。

### 3.6 详情交互

点击"查看详情"复用现有的 `WorkDetailModal` 组件，展示作品内容。

点击"作品编号"也打开同一个弹框（与现有行为一致）。

### 3.7 URL 参数支持

- `/super/contests/works` — 全部作品
- `/super/contests/works?contestId=3` — 筛选某活动的作品（从活动列表跳转）

---

## 4. 后端改动

### 4.1 新增 GET /api/contests/works/stats

超管调用时返回全平台作品统计：

```json
{
  "total": 120,
  "submitted": 85,
  "reviewing": 30,
  "reviewed": 20
}
```

支持可选的 `contestId` 参数。

判定逻辑：
- submitted：status = 'submitted' 且无评分记录
- reviewing：status = 'submitted' 或 'reviewing'，且有评分记录但未全部评完
- reviewed：所有分配的评委都已评分

简化方案：直接按数据库 status 字段统计（submitted / reviewing），reviewed 通过 `reviewedCount` 从活动列表接口已有的逻辑复用。

### 4.2 扩展 GET /api/contests/works

超管调用时：
- `contestId` 变为可选（不传时查全部活动的作品）
- 新增 `keyword` 参数：搜索作品编号、提交者姓名
- 新增 `status` 参数：按作品状态筛选
- 返回增加活动名称：`contest: { id, contestName }`
- 返回增加提交者租户：`registration.user.tenant.name`

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

---

## 5. 前端改动

| 文件 | 操作 | 说明 |
|------|------|------|
| `frontend/src/api/contests.ts` | 修改 | 新增 WorksStats 类型和 works.getStats 接口，扩展 works 查询参数 |
| `frontend/src/views/contests/works/Index.vue` | 修改 | 超管端改为扁平作品列表（统计卡片+筛选+全平台表格+详情弹框），机构端保持不变 |
| `backend/src/contests/works/works.service.ts` | 修改 | 新增 getStats，扩展 findAll 支持跨活动查询 |
| `backend/src/contests/works/works.controller.ts` | 修改 | 新增 stats 端点，findAll 增加参数 |

---

## 6. 实施记录

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

**后端改动（3 个文件）：**
- `backend/src/contests/works/dto/query-work.dto.ts` — 新增 keyword 参数
- `backend/src/contests/works/works.service.ts` — 新增 getStats() 方法（按状态统计）；findAll 增加 keyword 搜索（作品编号/提交者姓名/队伍名）
- `backend/src/contests/works/works.controller.ts` — 新增 GET /api/contests/works/stats 端点（stats 在 :id 之前）

**前端改动（2 个文件）：**
- `frontend/src/api/contests.ts` — 扩展 QueryWorkParams（keyword），新增 WorksStats 类型和 worksApi.getStats
- `frontend/src/views/contests/works/Index.vue` — 超管端改为扁平作品列表（统计卡片+筛选栏+全平台表格+WorkDetailModal），机构端保持原有两层结构；支持 URL 参数联动（?contestId=xx）
