# 上传数据页面 UI 设计文档

## 1. 页面概述

### 1.1 功能描述

上传数据页面是纪检初核系统中项目管理模块的核心页面，支持在一个项目中上传多个主体/账户数据进行汇总/独立分析。提供流水导入、征信导入、员工家庭关系导入、名单库选择等功能。

### 1.2 页面路径

- 菜单位置：项目管理 > 项目详情 > 上传数据
- 路由路径：`/project/:id/upload-data`

### 1.3 页面状态

- 项目状态：已完成
- 最后更新时间：2024-01-20 15:30

---

## 2. 页面布局

### 2.1 整体结构

```
┌─────────────────────────────────────────────────────────────┐
│  面包屑导航：项目管理 / 项目详情 / 上传数据                    │
├─────────────────────────────────────────────────────────────┤
│  页面标题区                                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ 上传数据                                               │  │
│  │ 项目状态：已完成  最后更新：2024-01-20 15:30          │  │
│  │ 支持在一个项目中上传多个主体/账户数据，进行汇总/独立分析  │  │
│  └───────────────────────────────────────────────────────┘  │
├─────────────────────────────────────────────────────────────┤
│  主要内容区（网格布局）                                        │
│  ┌─────────────────┐  ┌─────────────────┐                  │
│  │  流水导入        │  │  已上传流水查询   │                  │
│  │  [上传组件]      │  │  [上传组件]      │                  │
│  └─────────────────┘  └─────────────────┘                  │
│  ┌─────────────────┐  ┌─────────────────┐                  │
│  │  征信导入        │  │ 员工家庭关系导入  │                  │
│  │  [上传组件]      │  │  [上传组件]      │                  │
│  └─────────────────┘  └─────────────────┘                  │
│  ┌─────────────────────────────────────────────┐           │
│  │  名单库选择                                  │           │
│  │  ☑ 高风险人员名单(68人)  ☑ 历史可疑人员名单  │           │
│  │  ☑ 监管关注名单(32人)                        │           │
│  └─────────────────────────────────────────────┘           │
├─────────────────────────────────────────────────────────────┤
│  数据质量检查区                                               │
│  ┌────────────────────────────────────────────────────────┐ │
│  │ 数据完整性        格式一致性        余额连续性           │ │
│  │ 98.5%             95.2%             92.8%               │ │
│  │ 检查结果：                                   [查看详情]  │ │
│  │ • 发现 23 条数据格式不一致                              │ │
│  │ • 发现 5 条余额连续性异常                               │ │
│  │ • 发现 12 条缺失关键字段                                │ │
│  └────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│  操作按钮区                                                   │
│  [拉取本行信息]                    [生成报告]                │
└─────────────────────────────────────────────────────────────┘
```

### 2.2 响应式布局

- 桌面端（≥1200px）：4列网格布局
- 平板端（768px-1199px）：2列网格布局
- 移动端（<768px）：单列布局

---

## 3. 组件设计

### 3.1 FileUploadCard 上传卡片组件

**Props:**

```typescript
interface FileUploadCardProps {
  title: string;           // 卡片标题
  description: string;     // 描述文字
  acceptTypes: string[];   // 接受的文件类型，如 ['xlsx', 'xls', 'pdf']
  maxSize?: number;        // 最大文件大小（MB），默认 10
  multiple?: boolean;      // 是否支持多文件上传
  uploadUrl: string;       // 上传接口地址
  onUploadSuccess?: (files: UploadedFile[]) => void;
  onUploadError?: (error: Error) => void;
  showFileList?: boolean;  // 是否显示已上传文件列表
}
```

**UI 结构：**

```vue
<template>
  <el-card class="upload-card">
    <template #header>
      <div class="card-header">
        <h3>{{ title }}</h3>
        <el-tooltip :content="description" placement="top">
          <i class="el-icon-info"></i>
        </el-tooltip>
      </div>
    </template>

    <el-upload
      class="upload-area"
      :action="uploadUrl"
      :accept="acceptTypes.join(',')"
      :multiple="multiple"
      :limit="10"
      :file-list="fileList"
      :on-success="handleSuccess"
      :on-error="handleError"
      :before-upload="beforeUpload"
      drag
    >
      <div class="upload-content">
        <i class="el-icon-upload"></i>
        <p>拖拽文件到此处或点击上传</p>
        <p class="upload-tip">支持格式: {{ acceptTypes.join(', ') }}</p>
      </div>
    </el-upload>

    <div v-if="showFileList && uploadedFiles.length" class="file-list">
      <h4>已上传文件</h4>
      <el-table :data="uploadedFiles" size="small">
        <el-table-column prop="fileName" label="文件名" />
        <el-table-column prop="fileSize" label="大小" width="100" />
        <el-table-column prop="uploadTime" label="上传时间" width="160" />
        <el-table-column prop="status" label="状态" width="80">
          <template #default="{ row }">
            <el-tag :type="row.status === 'success' ? 'success' : 'danger'">
              {{ row.status === 'success' ? '成功' : '失败' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="100" fixed="right">
          <template #default="{ row }">
            <el-button type="text" size="small" @click="handleDelete(row)">
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </el-card>
</template>
```

### 3.2 CheckboxGroupSelector 名单库选择组件

**Props:**

```typescript
interface CheckboxGroupSelectorProps {
  options: NameListOption[];
  modelValue: string[];
  onChange: (value: string[]) => void;
}

interface NameListOption {
  label: string;      // 显示文本
  value: string;      // 选中值
  count: number;      // 人数统计
  disabled?: boolean;
}
```

**UI 结构：**

```vue
<template>
  <el-card class="name-list-selector">
    <template #header>
      <h3>名单库选择</h3>
    </template>
    <p class="selector-description">选择中介库管理内的名单</p>
    <el-checkbox-group v-model="selectedLists" @change="handleChange">
      <el-checkbox
        v-for="option in options"
        :key="option.value"
        :label="option.value"
        :disabled="option.disabled"
      >
        {{ option.label }}({{ option.count }}人)
      </el-checkbox>
    </el-checkbox-group>
  </el-card>
</template>
```

### 3.3 DataQualityPanel 数据质量检查面板

**Props:**

```typescript
interface DataQualityPanelProps {
  metrics: QualityMetric[];
  issues: QualityIssue[];
  onCheckQuality?: () => void;
  onViewDetails?: (issue: QualityIssue) => void;
}

interface QualityMetric {
  name: string;      // 指标名称
  value: number;     // 百分比值
  status: 'good' | 'warning' | 'error';
}

interface QualityIssue {
  type: string;      // 问题类型
  count: number;     // 数量
  description: string;
  details?: any[];
}
```

**UI 结构：**

```vue
<template>
  <el-card class="quality-panel">
    <template #header>
      <div class="panel-header">
        <h3>数据质量检查</h3>
        <el-button type="primary" size="small" @click="handleCheck">
          重新检查
        </el-button>
      </div>
    </template>

    <!-- 质量指标 -->
    <div class="metrics-container">
      <div
        v-for="metric in metrics"
        :key="metric.name"
        class="metric-item"
        :class="`metric-${metric.status}`"
      >
        <el-progress
          type="circle"
          :percentage="metric.value"
          :status="metric.status"
        />
        <span class="metric-name">{{ metric.name }}</span>
      </div>
    </div>

    <!-- 问题列表 -->
    <div class="issues-section">
      <h4>检查结果</h4>
      <el-alert
        v-for="(issue, index) in issues"
        :key="index"
        :type="getIssueType(issue)"
        :closable="false"
        class="issue-item"
      >
        <template #title>
          发现 <strong>{{ issue.count }}</strong> {{ issue.description }}
        </template>
      </el-alert>
      <el-button type="text" @click="handleViewDetails">查看详情 →</el-button>
    </div>
  </el-card>
</template>
```

---

## 4. 交互说明

### 4.1 文件上传流程

1. **拖拽上传**
    - 用户拖拽文件到上传区域
    - 显示上传进度条
    - 上传成功后显示成功提示
    - 自动添加到已上传文件列表

2. **点击上传**
    - 点击上传区域触发文件选择对话框
    - 选择文件后开始上传
    - 显示上传进度

3. **文件验证**
    - 文件格式验证：只接受指定格式
    - 文件大小验证：超过限制显示错误提示
    - 重复文件验证：同名文件提示是否覆盖

4. **上传状态**
    - 上传中：显示进度条
    - 上传成功：绿色勾选标记
    - 上传失败：红色错误标记，显示错误信息

### 4.2 名单库选择

1. 默认选中全部名单库
2. 点击复选框切换选中状态
3. 实时更新选中人数统计
4. 取消选中时显示确认提示

### 4.3 数据质量检查

1. **自动触发**
    - 文件上传完成后自动触发
    - 显示检查进度

2. **手动触发**
    - 点击"重新检查"按钮
    - 覆盖之前的检查结果

3. **结果展示**
    - 三个核心指标以环形进度图展示
    - 颜色指示：绿色(≥95%)、黄色(85-94%)、红色(<85%)
    - 问题列表按严重程度排序

### 4.4 按钮操作

1. **拉取本行信息**
    - 点击后显示加载状态
    - 从本行系统拉取相关数据
    - 完成后显示成功提示并刷新页面

2. **生成报告**
    - 验证必须上传至少一个文件
    - 显示报告生成进度
    - 生成成功后跳转到报告页面

---

## 5. 数据结构

### 5.1 后端接口

#### 5.1.1 获取项目上传数据状态

```typescript
GET /api/project/{projectId}/upload-status

Response:
{
  "code": 200,
  "data": {
    "projectStatus": "已完成",
    "lastUpdateTime": "2024-01-20 15:30:00",
    "uploadedFiles": {
      "transactionFiles": [],      // 流水文件列表
      "inquiryFiles": [],          // 征信文件列表
      "familyRelationFiles": []    // 家庭关系文件列表
    },
    "selectedNameLists": [],       // 已选名单库
    "qualityMetrics": {            // 质量指标
      "completeness": 98.5,
      "consistency": 95.2,
      "continuity": 92.8
    },
    "qualityIssues": []            // 质量问题列表
  }
}
```

#### 5.1.2 上传文件接口

```typescript
POST /api/project/{projectId}/upload
Content-Type: multipart/form-data

Body:
{
  "fileType": "transaction" | "inquiry" | "family_relation",
  "files": File[]
}

Response:
{
  "code": 200,
  "data": {
    "successCount": 2,
    "failedCount": 0,
    "uploadedFiles": [
      {
        "fileId": "123456",
        "fileName": "流水数据.xlsx",
        "fileSize": 2048576,
        "uploadTime": "2024-01-20 15:30:00",
        "status": "success"
      }
    ]
  }
}
```

#### 5.1.3 删除文件接口

```typescript
DELETE /api/project/{projectId}/file/{fileId}

Response:
{
  "code": 200,
  "msg": "删除成功"
}
```

#### 5.1.4 获取名单库列表

```typescript
GET /api/name-list/options

Response:
{
  "code": 200,
  "data": [
    {
      "value": "high_risk",
      "label": "高风险人员名单",
      "count": 68
    },
    {
      "value": "history_suspicious",
      "label": "历史可疑人员名单",
      "count": 45
    },
    {
      "value": "regulatory_focus",
      "label": "监管关注名单",
      "count": 32
    }
  ]
}
```

#### 5.1.5 更新名单库选择

```typescript
PUT /api/project/{projectId}/name-lists

Body:
{
  "selectedLists": ["high_risk", "history_suspicious", "regulatory_focus"]
}

Response:
{
  "code": 200,
  "msg": "更新成功"
}
```

#### 5.1.6 执行数据质量检查

```typescript
POST /api/project/{projectId}/quality-check

Response:
{
  "code": 200,
  "data": {
    "checkId": "qc_123456",
    "status": "completed",
    "metrics": {
      "completeness": 98.5,
      "consistency": 95.2,
      "continuity": 92.8
    },
    "issues": [
      {
        "type": "format_inconsistency",
        "count": 23,
        "description": "条数据格式不一致"
      },
      {
        "type": "balance_anomaly",
        "count": 5,
        "description": "条余额连续性异常"
      },
      {
        "type": "missing_field",
        "count": 12,
        "description": "条缺失关键字段"
      }
    ]
  }
}
```

#### 5.1.7 拉取本行信息

```typescript
POST /api/project/{projectId}/pull-bank-info

Response:
{
  "code": 200,
  "msg": "拉取成功",
  "data": {
    "pulledRecords": 156,
    "pullTime": "2024-01-20 15:35:00"
  }
}
```

#### 5.1.8 生成报告

```typescript
POST /api/project/{projectId}/generate-report

Response:
{
  "code": 200,
  "data": {
    "reportId": "rpt_789012",
    "reportUrl": "/project/123/report/rpt_789012",
    "generateTime": "2024-01-20 15:40:00"
  }
}
```

### 5.2 前端数据模型

```typescript
// 上传文件类型
type UploadFileType = 'transaction' | 'inquiry' | 'family_relation';

// 上传文件状态
type UploadStatus = 'uploading' | 'success' | 'error';

// 上传的文件
interface UploadedFile {
  fileId: string;
  fileName: string;
  fileSize: number;
  uploadTime: string;
  status: UploadStatus;
  errorMessage?: string;
}

// 名单库选项
interface NameListOption {
  value: string;
  label: string;
  count: number;
  disabled?: boolean;
}

// 质量指标
interface QualityMetric {
  name: string;
  value: number;
  status: 'good' | 'warning' | 'error';
}

// 质量问题
interface QualityIssue {
  type: string;
  count: number;
  description: string;
  details?: any[];
}

// 项目上传数据状态
interface ProjectUploadStatus {
  projectStatus: string;
  lastUpdateTime: string;
  uploadedFiles: {
    transactionFiles: UploadedFile[];
    inquiryFiles: UploadedFile[];
    familyRelationFiles: UploadedFile[];
  };
  selectedNameLists: string[];
  qualityMetrics: {
    completeness: number;
    consistency: number;
    continuity: number;
  };
  qualityIssues: QualityIssue[];
}
```

---

## 6. 样式规范

### 6.1 颜色规范

```scss
// 主色
$primary-color: #409EFF;
$success-color: #67C23A;
$warning-color: #E6A23C;
$danger-color: #F56C6C;
$info-color: #909399;

// 中性色
$text-primary: #303133;
$text-regular: #606266;
$text-secondary: #909399;
$text-placeholder: #C0C4CC;

// 边框色
$border-base: #DCDFE6;
$border-light: #E4E7ED;
$border-lighter: #EBEEF5;
$border-extra-light: #F2F6FC;

// 背景色
$bg-color: #F5F7FA;
$card-bg: #FFFFFF;
```

### 6.2 间距规范

```scss
$spacing-xs: 4px;
$spacing-sm: 8px;
$spacing-md: 16px;
$spacing-lg: 24px;
$spacing-xl: 32px;
```

### 6.3 圆角规范

```scss
$border-radius-sm: 2px;
$border-radius-base: 4px;
$border-radius-lg: 8px;
$border-radius-circle: 50%;
```

### 6.4 阴影规范

```scss
$box-shadow-base: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
$box-shadow-dark: 0 2px 8px rgba(0, 0, 0, 0.15), 0 0 6px rgba(0, 0, 0, 0.1);
$box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
```

---

## 7. 组件样式代码

### 7.1 上传卡片样式

```scss
.upload-card {
  height: 100%;

  .card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;

    h3 {
      margin: 0;
      font-size: 16px;
      font-weight: 500;
      color: $text-primary;
    }

    .el-icon-info {
      color: $info-color;
      cursor: help;
    }
  }

  .upload-area {
    margin-bottom: $spacing-md;

    .el-upload-dragger {
      width: 100%;
      height: 180px;
      border: 2px dashed $border-base;
      border-radius: $border-radius-lg;
      background: $bg-color;
      transition: all 0.3s;

      &:hover {
        border-color: $primary-color;
        background: #F0F7FF;
      }
    }

    .upload-content {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 100%;

      .el-icon-upload {
        font-size: 48px;
        color: $primary-color;
        margin-bottom: $spacing-sm;
      }

      p {
        margin: $spacing-xs 0;
        font-size: 14px;
        color: $text-regular;
      }

      .upload-tip {
        font-size: 12px;
        color: $text-secondary;
      }
    }
  }

  .file-list {
    border-top: 1px solid $border-light;
    padding-top: $spacing-md;

    h4 {
      margin: 0 0 $spacing-sm 0;
      font-size: 14px;
      color: $text-primary;
    }
  }
}
```

### 7.2 数据质量面板样式

```scss
.quality-panel {
  .panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;

    h3 {
      margin: 0;
      font-size: 16px;
      font-weight: 500;
    }
  }

  .metrics-container {
    display: flex;
    justify-content: space-around;
    margin-bottom: $spacing-lg;

    .metric-item {
      display: flex;
      flex-direction: column;
      align-items: center;

      .el-progress {
        margin-bottom: $spacing-sm;
      }

      .metric-name {
        font-size: 14px;
        color: $text-regular;
      }
    }
  }

  .issues-section {
    border-top: 1px solid $border-light;
    padding-top: $spacing-md;

    h4 {
      margin: 0 0 $spacing-md 0;
      font-size: 14px;
      color: $text-primary;
    }

    .issue-item {
      margin-bottom: $spacing-sm;

      &:last-child {
        margin-bottom: 0;
      }
    }
  }
}
```

### 7.3 页面整体布局样式

```scss
.upload-data-page {
  padding: $spacing-lg;
  background: $bg-color;
  min-height: calc(100vh - 84px);

  .page-header {
    background: $card-bg;
    padding: $spacing-lg;
    border-radius: $border-radius-lg;
    margin-bottom: $spacing-lg;
    box-shadow: $box-shadow-base;

    h1 {
      margin: 0 0 $spacing-sm 0;
      font-size: 24px;
      font-weight: 500;
    }

    .page-info {
      display: flex;
      gap: $spacing-lg;
      font-size: 14px;
      color: $text-secondary;
      margin-top: $spacing-sm;

      .info-item {
        display: flex;
        align-items: center;

        .label {
          margin-right: $spacing-xs;
        }

        .status {
          color: $success-color;
        }
      }
    }

    .page-description {
      margin-top: $spacing-md;
      padding: $spacing-md;
      background: #F0F9FF;
      border-left: 3px solid $primary-color;
      border-radius: $border-radius-base;
      font-size: 14px;
      color: $text-regular;
    }
  }

  .upload-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: $spacing-lg;
    margin-bottom: $spacing-lg;

    @media (min-width: 1200px) {
      grid-template-columns: repeat(4, 1fr);
    }

    @media (max-width: 767px) {
      grid-template-columns: 1fr;
    }
  }

  .full-width {
    grid-column: 1 / -1;
  }

  .action-bar {
    display: flex;
    justify-content: center;
    gap: $spacing-lg;
    margin-top: $spacing-xl;
  }
}
```

---

## 8. 技术实现要点

### 8.1 文件上传

- 使用 Element UI 的 `el-upload` 组件
- 支持拖拽上传和点击上传
- 实现文件类型和大小校验
- 显示上传进度
- 支持断点续传（可选）

### 8.2 数据质量检查

- 异步执行检查任务
- 使用 WebSocket 或轮询获取检查进度
- 实时更新进度和结果

### 8.3 状态管理

- 使用 Vuex 管理上传状态
- 缓存已上传文件列表
- 同步名单库选择状态

### 8.4 性能优化

- 文件分片上传大文件
- 使用 Web Worker 处理文件预检查
- 虚拟滚动展示大量文件列表

---

## 9. 测试要点

### 9.1 功能测试

- 文件上传各种格式
- 文件大小限制验证
- 删除文件功能
- 名单库选择功能
- 数据质量检查准确性
- 报告生成功能

### 9.2 兼容性测试

- 主流浏览器兼容
- 不同屏幕尺寸适配
- 文件格式兼容性

### 9.3 性能测试

- 大文件上传性能
- 多文件同时上传
- 页面加载性能

### 9.4 异常处理测试

- 网络中断处理
- 文件上传失败处理
- 服务器错误处理
- 文件格式错误处理

---

## 10. 附录

### 10.1 相关页面

- 项目详情页：`/project/:id/detail`
- 参数配置页：`/project/:id/config`
- 初核结果页：`/project/:id/result`
- 报告页面：`/project/:id/report/:reportId`

### 10.2 权限要求

- 需要项目成员权限
- 上传操作需要编辑权限
- 删除操作需要删除权限
- 生成报告需要报告权限

### 10.3 相关文档

- [Element UI Upload 组件文档](https://element.eleme.cn/#/zh-CN/component/upload)
- [若依框架前端开发规范](../前端开发规范.md)
- [项目接口文档](../API文档/项目管理模块.md)

---

**文档版本**: v1.0
**创建时间**: 2024-01-30
**最后更新**: 2024-01-30
**文档状态**: 待评审
