# 赛事详情页面生成规范

## 概述

本规范用于快速生成赛事管理系统中的详情页面，这类页面具有统一的结构：
- 顶部导航栏（返回按钮 + 标题 + 操作按钮）
- 搜索表单
- 数据表格

## 页面结构

```
┌─────────────────────────────────────────────────┐
│  [← 返回]  页面标题                    [操作按钮] │
├─────────────────────────────────────────────────┤
│  搜索表单: [搜索条件...] [搜索] [重置]           │
├─────────────────────────────────────────────────┤
│  数据表格                                        │
│  ┌───┬──────┬──────┬──────┬──────┐              │
│  │序号│ 列1  │ 列2  │ 列3  │ 操作 │              │
│  ├───┼──────┼──────┼──────┼──────┤              │
│  │ 1 │ ...  │ ...  │ ...  │ ... │              │
│  └───┴──────┴──────┴──────┴──────┘              │
└─────────────────────────────────────────────────┘
```

## 快速生成指令格式

向 Claude 提供以下格式的指令即可快速生成详情页面：

```
请生成一个详情页面：

页面名称：xxx
文件路径：xxx
数据来源：xxx API

标题：字段名（如 contestName）
操作按钮：按钮名称 -> 处理函数

搜索条件：
- 字段1（类型）
- 字段2（类型）

表格列：
- 列名1（数据字段）
- 列名2（数据字段）
- ...

排序：字段名 + 排序方式（升序/降序）
```

## 配置参数说明

### 1. 基础配置

| 参数 | 说明 | 示例 |
|------|------|------|
| 页面名称 | 显示在标题旁 | 赛果发布详情 |
| 文件路径 | Vue 文件位置 | views/contests/results/Detail.vue |
| 数据来源 | API 模块 | resultsApi / worksApi |
| 标题字段 | 动态标题来源 | record.contestName |
| 返回路径 | 返回按钮跳转 | 上一页 / 指定路径 |

### 2. 操作按钮

| 属性 | 说明 |
|------|------|
| 名称 | 按钮显示文本 |
| 类型 | primary / default / danger |
| 处理函数 | 点击触发的方法 |
| 确认弹窗 | 是否需要二次确认 |

### 3. 搜索条件类型

| 类型 | 说明 | 示例 |
|------|------|------|
| input | 输入框 | 作品编号、账号 |
| select | 下拉选择 | 状态筛选 |
| date | 日期选择 | 提交日期 |
| dateRange | 日期范围 | 时间区间 |

### 4. 表格列渲染类型

| 类型 | 说明 | 示例 |
|------|------|------|
| index | 序号 | 自动计算 |
| text | 纯文本 | 直接显示字段值 |
| nested | 嵌套字段 | record.user?.nickname |
| array | 数组拼接 | teachers.map(t => t.name).join('、') |
| score | 分数格式 | 保留2位小数 |
| tag | 标签样式 | 状态标签 |
| org | 机构信息 | 学校 + 年级 + 班级 |

### 5. 排序配置

| 属性 | 说明 |
|------|------|
| 字段 | 排序依据的字段名 |
| 方式 | asc（升序）/ desc（降序）|

## 示例

### 赛果发布详情页

```
请生成一个详情页面：

页面名称：赛果发布详情
文件路径：views/contests/results/Detail.vue
数据来源：resultsApi.getResults

标题：contestName（赛事名称）
操作按钮：发布赛果 -> handlePublish（需确认）

搜索条件：
- workNo 作品编号（input）
- accountNo 报名账号（input）

表格列：
- 序号（index）
- 作品编号（workNo）
- 评委评分（finalScore，分数格式）
- 姓名（registration.user.nickname）
- 账号（registration.user.username）
- 机构信息（registration.user，机构格式）
- 指导老师（registration.teachers，数组拼接）

排序：finalScore 降序
```

### 报名记录详情页

```
请生成一个详情页面：

页面名称：报名记录
文件路径：views/contests/registrations/Records.vue
数据来源：registrationsApi.getList

标题：contestName（赛事名称）
操作按钮：无

搜索条件：
- accountNo 报名账号（input）
- registrationState 审核状态（select）

表格列：
- 序号（index）
- 报名账号（accountNo）
- 姓名（user.nickname）
- 机构信息（user，机构格式）
- 指导老师（teachers，数组拼接）
- 审核状态（registrationState，标签）
- 报名时间（registrationTime，日期）
- 操作（查看详情、审核）

排序：registrationTime 降序
```

## 页面模板代码

```vue
<template>
  <div class="detail-page">
    <!-- 顶部导航 -->
    <div class="page-header">
      <div class="header-left">
        <a-button type="text" @click="handleBack">
          <template #icon><ArrowLeftOutlined /></template>
          返回
        </a-button>
        <span class="page-title">{{ pageTitle }}</span>
      </div>
      <div class="header-right">
        <a-button type="primary" @click="handleAction">
          {{ actionButtonText }}
        </a-button>
      </div>
    </div>

    <!-- 搜索表单 -->
    <a-form
      :model="searchParams"
      layout="inline"
      class="search-form"
      @finish="handleSearch"
    >
      <!-- 根据配置生成搜索项 -->
      <a-form-item>
        <a-button type="primary" html-type="submit">
          <template #icon><SearchOutlined /></template>
          搜索
        </a-button>
        <a-button style="margin-left: 8px" @click="handleReset">
          <template #icon><ReloadOutlined /></template>
          重置
        </a-button>
      </a-form-item>
    </a-form>

    <!-- 数据表格 -->
    <a-table
      :columns="columns"
      :data-source="dataSource"
      :loading="loading"
      :pagination="pagination"
      row-key="id"
      @change="handleTableChange"
    >
      <template #bodyCell="{ column, record, index }">
        <!-- 根据配置生成列渲染 -->
      </template>
    </a-table>
  </div>
</template>
```

## 特殊字段渲染规范

### 机构信息（org 类型）
```vue
<template v-else-if="column.key === 'org'">
  <div>
    <div>{{ record.user?.tenant?.name || "-" }}</div>
    <div v-if="record.user?.student?.class" class="org-detail">
      {{ record.user.student.class.grade?.name }}
      {{ record.user.student.class.name }}
    </div>
  </div>
</template>
```

### 指导老师（数组拼接）
```vue
<template v-else-if="column.key === 'teachers'">
  {{ formatTeachers(record.teachers || record.registration?.teachers) }}
</template>

// 格式化函数
const formatTeachers = (teachers: any[]) => {
  if (!teachers || teachers.length === 0) return "-"
  return teachers.map(t => t.user?.nickname || t.user?.username).join("、")
}
```

### 分数格式
```vue
<template v-else-if="column.key === 'score'">
  <span v-if="record.finalScore !== null" class="score">
    {{ Number(record.finalScore).toFixed(2) }}
  </span>
  <span v-else>-</span>
</template>
```

## 样式规范

```css
.detail-page {
  padding: 0;
}

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px;
  background: #fff;
  border-radius: 8px;
  margin-bottom: 16px;
}

.header-left {
  display: flex;
  align-items: center;
  gap: 8px;
}

.page-title {
  font-size: 16px;
  font-weight: 500;
}

.search-form {
  margin-bottom: 16px;
  padding: 16px;
  background: #fff;
  border-radius: 8px;
}

.org-detail {
  font-size: 12px;
  color: #666;
  margin-top: 2px;
}

.score {
  font-weight: bold;
  color: #52c41a;
}
```

## 注意事项

1. **API 数据**：确保后端返回了所需的关联数据（如 user、teachers、tenant、student.class.grade）
2. **排序字段**：需要后端支持对应字段的排序
3. **权限控制**：操作按钮需要配置权限检查
4. **空值处理**：所有字段都需要处理空值情况，显示 "-"
5. **嵌套数据**：使用可选链操作符 `?.` 避免空指针错误
