Files
X-Financial/docs/plans/phase-4-frontend-pages/README.md
WIN-JHFT4D3SIVT\caoxiaozhu 7141e1d11a feat: refactor monolithic App.vue into modular Vue component architecture
- Extract 711-line App.vue into 15+ focused files across 5 directories
- Add data layer (icons, metrics, policies, auditTrail, requests)
- Add composables (useNavigation, useRequests, useChat, useToast)
- Add layout components (SidebarRail, TopBar, FilterBar)
- Add shared components (PanelHead, InfoRow, ToastNotification)
- Add business component (RequestTable) and 5 view components
- Extract global CSS to assets/styles/global.css
- Add start.sh with WSL/Windows cross-platform support
- Add .gitignore for node_modules, dist, and IDE dirs
2026-04-28 17:20:52 +08:00

501 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` 无报错