# Phase 4: 前端核心页面（W4-W5）

> **目标：** 实现所有核心前端页面和组件，完成用户交互界面。  
> **周期：** 第 4 ~ 5 周  
> **任务数：** 4 个  
> **可并行：** 4 个任务可由 1-2 名前端工程师并行开发  
> **前置依赖：** Phase 1（前端骨架）  
> **备注：** 可与 Phase 3 后半段并行开始

---

## 本阶段交付物

| 交付物 | 说明 |
|---|---|
| 报销入口页 | 对话式报销入口 + 快捷操作 |
| 票据上传页 | 文件上传组件 + 票据类型选择 |
| 报销草稿页 | 费用明细表格 + 可编辑字段 |
| 预审结果页 | 风险展示 + 规则命中详情 |
| 补件交互页 | 补件清单 + 上传/回复 |
| 提交确认页 | 最终确认 + 同步状态 |
| 审计日志页 | 操作时间线 |

---

## 任务清单

### Task 4.1: 报销入口页 + 上传组件

**负责人：** 前端工程师  
**预计工时：** 2 天  
**前置依赖：** Phase 1（前端骨架）

**Files:**
- Create: `frontend/src/views/HomeView.vue`
- Create: `frontend/src/views/UploadView.vue`
- Create: `frontend/src/components/FileUpload.vue`
- Create: `frontend/src/stores/task.ts`
- Create: `frontend/src/api/task.ts`
- Create: `frontend/src/api/document.ts`

- [ ] **Step 1: 实现 API 调用层**

`frontend/src/api/task.ts`:

```typescript
import api from './index'

export const createTask = (data: {
  userId: string
  companyId: string
  userIntent: string
  entryChannel?: string
}) => api.post('/reimbursement/tasks', data)

export const getTask = (taskId: string) =>
  api.get(`/reimbursement/tasks/${taskId}`)

export const listTasks = (params?: { userId?: string; status?: string; page?: number; size?: number }) =>
  api.get('/reimbursement/tasks', { params })

export const runAgent = (taskId: string, startFrom = 'intake', mode = 'precheck') =>
  api.post(`/reimbursement/tasks/${taskId}/agent/run`, { start_from: startFrom, mode })
```

`frontend/src/api/document.ts`:

```typescript
import api from './index'

export const uploadDocument = (taskId: string, file: File, documentType: string) => {
  const formData = new FormData()
  formData.append('file', file)
  formData.append('document_type', documentType)
  return api.post(`/reimbursement/tasks/${taskId}/documents`, formData, {
    headers: { 'Content-Type': 'multipart/form-data' },
  })
}

export const listDocuments = (taskId: string) =>
  api.get(`/reimbursement/tasks/${taskId}/documents`)
```

- [ ] **Step 2: 实现 Pinia Store**

`frontend/src/stores/task.ts`:

```typescript
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { createTask, getTask, listTasks, runAgent } from '@/api/task'

export const useTaskStore = defineStore('task', () => {
  const currentTask = ref<any>(null)
  const taskList = ref<any[]>([])
  const loading = ref(false)

  async function create(userIntent: string) {
    loading.value = true
    try {
      const { data } = await createTask({
        userId: 'U001', // TODO: 从登录态获取
        companyId: 'C001',
        userIntent,
      })
      currentTask.value = data
      return data
    } finally {
      loading.value = false
    }
  }

  async function fetchTask(taskId: string) {
    const { data } = await getTask(taskId)
    currentTask.value = data
    return data
  }

  async function startAgent(taskId: string, startFrom = 'intake') {
    loading.value = true
    try {
      const { data } = await runAgent(taskId, startFrom)
      return data
    } finally {
      loading.value = false
    }
  }

  return { currentTask, taskList, loading, create, fetchTask, startAgent }
})
```

- [ ] **Step 3: 实现报销入口页 HomeView**

按开发文档 9.2 节：

- **对话输入框**：用户输入报销意图（如"我要报这次北京出差的费用"）
- **上传按钮**：直接跳转到上传页
- **最近任务列表**：显示用户最近的报销任务和状态
- **常用报销类型快捷按钮**：
  - "报差旅"
  - "看发票能不能报"
  - "帮我生成报销单"
  - "这张发票为什么不合规？"
- **智能引导提示**：根据用户输入实时提示

交互流程：用户输入意图 → 调用 `taskStore.create()` → 跳转到 `/task/{taskId}/upload`

- [ ] **Step 4: 实现文件上传组件 FileUpload**

`frontend/src/components/FileUpload.vue`：

Ant Design Vue 的 `a-upload-dragger` 封装：
- 支持拖拽上传
- 支持多文件选择
- 文件类型校验（PDF、JPG、PNG、OFD）
- 单文件大小限制（默认 10MB）
- 上传进度条
- 预览缩略图
- 已上传文件列表
- 删除已上传文件

Props：
```typescript
interface Props {
  taskId: string
  accept?: string        // 默认 '.pdf,.jpg,.jpeg,.png,.ofd'
  maxFileSize?: number   // MB，默认 10
  maxCount?: number      // 默认 10
}
```

- [ ] **Step 5: 实现票据上传页 UploadView**

- 引用 FileUpload 组件
- 票据类型下拉选择（Ant Design Select）：
  - 增值税发票 (vat_invoice)
  - 火车票 (train_ticket)
  - 机票行程单 (flight_itinerary)
  - 打车票据 (taxi_receipt)
  - 酒店流水 (hotel_bill)
  - 支付截图 (payment_screenshot)
  - 其他附件 (other_attachment)
- 已上传文件列表展示
- "开始识别" 按钮 → 调用 `taskStore.startAgent()` → 跳转到草稿页
- 返回按钮 → 回到首页

- [ ] **Step 6: Commit**

```bash
git add frontend/
git commit -m "feat: 实现报销入口页和票据上传页"
```

---

### Task 4.2: 报销草稿页

**负责人：** 前端工程师  
**预计工时：** 2 天  
**前置依赖：** Task 4.1  
**可并行于：** Task 4.3（如果两人并行）

**Files:**
- Create: `frontend/src/views/DraftView.vue`
- Create: `frontend/src/components/ExpenseTable.vue`
- Create: `frontend/src/api/precheck.ts`

- [ ] **Step 1: 添加预审 API**

`frontend/src/api/precheck.ts`:

```typescript
import api from './index'

export const getDraft = (taskId: string) =>
  api.get(`/reimbursement/tasks/${taskId}/draft`)

export const getPrecheckResult = (taskId: string) =>
  api.get(`/reimbursement/tasks/${taskId}/precheck-result`)
```

- [ ] **Step 2: 实现 ExpenseTable 组件**

`frontend/src/components/ExpenseTable.vue`：

Ant Design Table 展示费用明细：

| 列名 | 字段 | 可编辑 | 说明 |
|---|---|---|---|
| 费用类型 | expense_type | ✅ | 下拉选择 |
| 金额 | amount | ✅ | 数字输入 |
| 税额 | tax_amount | ✅ | 数字输入 |
| 发生日期 | occurred_at | ✅ | 日期选择 |
| 城市 | city | ✅ | 文本输入 |
| 商户 | vendor_name | ✅ | 文本输入 |
| 风险等级 | risk_level | ❌ | 彩色标签 |

风险等级标签颜色映射：
- `low` → 绿色 `green`
- `medium` → 橙色 `orange`
- `high` → 红色 `red`
- `blocked` → 深红 `#cf1322`

支持行内编辑（点击单元格进入编辑模式）。

- [ ] **Step 3: 实现报销草稿页 DraftView**

按开发文档 9.3 节布局：

```
┌─────────────────────────────────────────────┐
│  报销草稿                          [预审按钮] │
├─────────────────────────────────────────────┤
│  基本信息                                     │
│  ┌──────────┬──────────┬──────────┐          │
│  │ 报销人    │ 部门      │ 成本中心  │          │
│  └──────────┴──────────┴──────────┘          │
│  ┌──────────┬──────────┐                     │
│  │ 项目      │ 报销事由   │                     │
│  └──────────┴──────────┘                     │
├─────────────────────────────────────────────┤
│  费用明细                                     │
│  ┌─────────────────────────────────────────┐ │
│  │         ExpenseTable 组件               │ │
│  └─────────────────────────────────────────┘ │
│  总金额：¥ 2,380.00                           │
├─────────────────────────────────────────────┤
│  票据附件                                     │
│  ┌────┐ ┌────┐ ┌────┐                       │
│  │ 📄 │ │ 📄 │ │ 📄 │  （缩略图 + 文件名）    │
│  └────┘ └────┘ └────┘                       │
├─────────────────────────────────────────────┤
│  预审状态：⏳ 待预审                           │
│                                    [执行预审] │
└─────────────────────────────────────────────┘
```

交互：
- 页面加载时调用 `getDraft(taskId)` 获取草稿数据
- 编辑字段后暂存到本地 state
- 点击"执行预审" → 调用 `taskStore.startAgent(taskId, 'precheck')` → 跳转到预审结果页

- [ ] **Step 4: Commit**

```bash
git add frontend/
git commit -m "feat: 实现报销草稿页和费用明细表格组件"
```

---

### Task 4.3: 预审结果页 + 补件交互页

**负责人：** 前端工程师  
**预计工时：** 2.5 天  
**前置依赖：** Task 4.1  
**可并行于：** Task 4.2（如果两人并行）

**Files:**
- Create: `frontend/src/views/PrecheckView.vue`
- Create: `frontend/src/views/SupplementView.vue`
- Create: `frontend/src/components/RuleHitCard.vue`
- Create: `frontend/src/api/supplement.ts`

- [ ] **Step 1: 添加补件 API**

`frontend/src/api/supplement.ts`:

```typescript
import api from './index'

export const respondSupplement = (taskId: string, supplementRequestId: string, data: {
  responseText: string
  documentIds?: string[]
}) => api.post(`/reimbursement/tasks/${taskId}/supplements`, {
  supplement_request_id: supplementRequestId,
  ...data,
})

export const submitTask = (taskId: string, submitTo: string = 'expense_system') =>
  api.post(`/reimbursement/tasks/${taskId}/submit`, { confirmed: true, submit_to: submitTo })

export const getSyncStatus = (taskId: string) =>
  api.get(`/reimbursement/tasks/${taskId}/sync-status`)
```

- [ ] **Step 2: 实现 RuleHitCard 组件**

`frontend/src/components/RuleHitCard.vue`：

Ant Design Card 展示单条规则命中：

```
┌─────────────────────────────────────────┐
│ 🔴 住宿费超标                  TRAVEL_HOTEL_LIMIT │
├─────────────────────────────────────────┤
│ 问题：住宿费超出当前城市和职级标准          │
│ 制度依据：差旅报销制度-住宿标准             │
│ 建议：请补充超标说明或发起特殊审批          │
│                                [展开详情] │
└─────────────────────────────────────────┘
```

Props：
```typescript
interface Props {
  ruleCode: string
  ruleName: string
  severity: string      // low / medium / high / blocked
  action: string
  message: string
  suggestion: string
  policyRef: string
  hitDetail?: object
}
```

- [ ] **Step 3: 实现预审结果页 PrecheckView**

按开发文档 9.4 节：

- **总体结论卡片**：
  - ✅ 通过 → 绿色
  - ⚠️ 需补件 → 橙色
  - 🚫 有阻断 → 红色
- **风险等级指示**：彩色 Badge
- **通过项列表**：绿色勾选图标 + 规则名称
- **风险项列表**：使用 RuleHitCard 组件
- **缺件项列表**：橙色提示 + 补件按钮
- **操作按钮**：
  - "一键补件" → 跳转到补件页（仅在有缺件时显示）
  - "确认提交" → 跳转到确认页（仅预审通过时可用）

交互：
- 页面加载时调用 `getPrecheckResult(taskId)` 获取预审结果
- 根据结果渲染不同状态

- [ ] **Step 4: 实现补件交互页 SupplementView**

- **待补件清单**：从预审结果的 rule_hits 中过滤出 `require_attachment` / `require_explanation` 类型
- **每个补件项**：
  - 类型标签（补充附件 / 补充说明 / 修改字段）
  - 提示文案
  - 操作区域：
    - 补充附件：调用 FileUpload 组件
    - 补充说明：文本输入框
- **提交补件按钮** → 调用 `respondSupplement()` → 跳转回预审页重新预审

- [ ] **Step 5: Commit**

```bash
git add frontend/
git commit -m "feat: 实现预审结果页和补件交互页"
```

---

### Task 4.4: 提交确认页 + 审计日志页

**负责人：** 前端工程师  
**预计工时：** 1.5 天  
**前置依赖：** Task 4.3  

**Files:**
- Create: `frontend/src/views/ConfirmView.vue`
- Create: `frontend/src/views/AuditView.vue`
- Create: `frontend/src/api/audit.ts`

- [ ] **Step 1: 添加审计 API**

`frontend/src/api/audit.ts`:

```typescript
import api from './index'

export const getAuditLogs = (params?: {
  target_type?: string
  target_id?: string
  actor?: string
  page?: number
  size?: number
}) => api.get('/audit/logs', { params })
```

- [ ] **Step 2: 实现提交确认页 ConfirmView**

```
┌─────────────────────────────────────────────┐
│  提交确认                                     │
├─────────────────────────────────────────────┤
│  报销单摘要（不可编辑）                        │
│  ┌───────────────────────────────────────┐  │
│  │ 报销人：张三        部门：技术部         │  │
│  │ 事由：北京出差       总金额：¥2,380.00   │  │
│  └───────────────────────────────────────┘  │
│                                              │
│  费用明细汇总：                               │
│  ┌───────────────────────────────────────┐  │
│  │ 差旅住宿费    ¥1,200.00               │  │
│  │ 差旅交通费    ¥  553.00               │  │
│  │ 差旅餐补      ¥  627.00               │  │
│  └───────────────────────────────────────┘  │
│                                              │
│  附件清单：3 个文件                           │
│  同步目标：费控系统                           │
│                                              │
│                          [返回修改] [确认提交] │
├─────────────────────────────────────────────┤
│  同步状态：⏳ 提交中...                       │
└─────────────────────────────────────────────┘
```

交互：
- "确认提交" → 调用 `submitTask(taskId)` 
- 提交后轮询 `getSyncStatus(taskId)`，展示同步进度
- 同步成功 → 显示后端单据号
- 同步失败 → 显示错误信息 + 重试按钮

- [ ] **Step 3: 实现审计日志页 AuditView**

按开发文档 9.5 节（简化版）：

- **Ant Design Timeline** 展示操作记录
- **筛选栏**：按任务ID、操作类型、时间范围筛选
- **每条日志**：
  - 时间戳
  - 操作人
  - 操作类型（彩色标签）
  - 详情（可展开）

操作类型颜色映射：
- `file_upload` → 蓝色
- `ocr_recognize` → 青色
- `agent_call` → 紫色
- `rule_hit` → 橙色
- `supplement_request` / `supplement_respond` → 绿色
- `user_confirm` → 金色
- `backend_sync` → 灰色

- [ ] **Step 4: Commit**

```bash
git add frontend/
git commit -m "feat: 实现提交确认页和审计日志页"
```

---

## 本阶段完成检查

- [ ] 首页能创建任务并跳转到上传页
- [ ] 上传页能上传文件并开始识别
- [ ] 草稿页能展示费用明细并支持编辑
- [ ] 预审结果页能展示风险项和缺件项
- [ ] 补件页能上传附件和填写说明
- [ ] 确认页能展示摘要和同步状态
- [ ] 审计日志页能展示操作时间线
- [ ] 所有页面响应式布局正常
- [ ] 前端 `npm run build` 无报错
