diff --git a/doc/design/2026-03-05-async-file-upload-frontend-design.md b/doc/design/2026-03-05-async-file-upload-frontend-design.md index 2a4dcdd..2583d26 100644 --- a/doc/design/2026-03-05-async-file-upload-frontend-design.md +++ b/doc/design/2026-03-05-async-file-upload-frontend-design.md @@ -1,211 +1,149 @@ -# 项目异步文件上传功能 - 前端设计文档 +# 项目异步文件上传功能 - 前端设计文档(轮询版本) ## 文档信息 - **创建日期**: 2026-03-05 -- **版本**: v1.0 +- **版本**: v1.1 - **作者**: Claude - **状态**: 已批准 - **关联文档**: [后端设计文档](./2026-03-05-async-file-upload-design.md) +- **变更说明**: 移除WebSocket,改为页面轮询机制 ## 1. 设计概述 ### 1.1 功能描述 -基于现有项目管理模块的上传数据组件(UploadData.vue),扩展实现流水文件的异步批量上传功能,提供直观的用户交互界面和实时状态跟踪能力。 +基于现有项目管理模块的上传数据组件(UploadData.vue),扩展实现流水文件的异步批量上传功能。 -### 1.2 设计原则 -- **渐进式增强**:在现有组件基础上扩展,不破坏现有功能 -- **实时反馈**:通过 WebSocket 推送状态更新,用户无需手动刷新 -- **用户友好**:清晰的状态展示、简洁的操作流程、友好的错误提示 -- **响应式设计**:适配不同屏幕尺寸 - -### 1.3 技术栈 +### 1.2 技术栈 - Vue.js 2.6.12 - Element UI 2.15.14 -- WebSocket(实时通信) - Axios(HTTP 请求) +- 页面轮询(定时刷新) +## 2. 核心变更 -## 2. 页面布局设计 +### 2.1 移除WebSocket +- 不再使用WebSocket实时推送 +- 改用HTTP轮询机制定时刷新 -### 2.1 整体结构 +### 2.2 轮询机制 -UploadData.vue 组件新增以下模块: -1. **统计卡片区域**:显示上传中、解析中、成功、失败的数量 -2. **文件上传记录列表**:展示文件上传历史和状态 -3. **批量上传弹窗**:支持批量选择和上传文件 +**启动条件**: +- 上传文件后立即启动 +- 检测到有uploading或parsing状态文件时自动启动 -### 2.2 交互流程 +**停止条件**: +- 所有文件处理完成(无uploading和parsing状态) +- 组件销毁时 +- 用户手动停止 -1. 用户点击"流水导入"卡片的"上传流水"按钮 -2. 打开批量上传弹窗 -3. 用户选择/拖拽多个文件(显示已选文件列表,支持删除) -4. 点击"开始上传",弹窗关闭,显示"上传任务已提交"提示 -5. 返回主页面,可以看到统计卡片和列表实时更新(通过WebSocket) +**轮询间隔**: +- 默认5秒 +- 可根据活跃任务数量动态调整 -## 3. 技术架构设计 +## 3. 轮询实现 -### 3.1 组件数据结构 +### 3.1 数据结构 ```javascript data() { return { - // 批量上传相关 - batchUploadDialogVisible: false, - selectedFiles: [], - uploadLoading: false, - - // 统计数据 - statistics: { - uploading: 0, - parsing: 0, - parsed_success: 0, - parsed_failed: 0 - }, - - // 文件列表相关 - fileList: [], - listLoading: false, - queryParams: { - projectId: null, - fileStatus: null, - pageNum: 1, - pageSize: 20 - }, - total: 0, - - // WebSocket相关 - websocket: null, - wsReconnectCount: 0 + // 轮询相关 + pollingTimer: null, + pollingEnabled: false, + pollingInterval: 5000 // 5秒 } } ``` -### 3.2 API 接口封装 - -在 src/api/ccdiProjectUpload.js 中新增: +### 3.2 核心方法 ```javascript -// 批量上传文件 -export function batchUploadFiles(projectId, files) { - const formData = new FormData() - files.forEach(file => formData.append('files', file)) - formData.append('projectId', projectId) +methods: { + // 启动轮询 + startPolling() { + if (this.pollingEnabled) return + + this.pollingEnabled = true + + const poll = () => { + if (!this.pollingEnabled) return + + Promise.all([ + this.loadStatistics(), + this.loadFileList() + ]).then(() => { + // 检查是否需要继续轮询 + if (this.statistics.uploading === 0 && + this.statistics.parsing === 0) { + this.stopPolling() + return + } + + this.pollingTimer = setTimeout(poll, this.pollingInterval) + }) + } + + poll() + }, - return request({ - url: '/ccdi/file-upload/batch', - method: 'post', - data: formData, - timeout: 300000 - }) -} - -// 查询文件上传记录列表 -export function getFileUploadList(params) { - return request({ - url: '/ccdi/file-upload/list', - method: 'get', - params - }) -} - -// 查询文件上传统计 -export function getFileUploadStatistics(projectId) { - return request({ - url: `/ccdi/file-upload/statistics/${projectId}`, - method: 'get' - }) + // 停止轮询 + stopPolling() { + this.pollingEnabled = false + if (this.pollingTimer) { + clearTimeout(this.pollingTimer) + this.pollingTimer = null + } + }, + + // 上传成功后启动轮询 + async handleBatchUpload() { + // ... 上传逻辑 ... + + // 刷新数据并启动轮询 + await Promise.all([ + this.loadStatistics(), + this.loadFileList() + ]) + + this.startPolling() + } } ``` -### 3.3 WebSocket 集成 +### 3.3 生命周期管理 -#### 连接配置 -- 连接地址:ws://localhost:8080/ws/file-upload/{projectId} -- 连接时机:组件 mounted -- 断开时机:组件 beforeDestroy -- 重连机制:最多3次,间隔5秒 +```javascript +mounted() { + this.loadStatistics() + this.loadFileList() + + // 检查是否需要启动轮询 + if (this.statistics.uploading > 0 || this.statistics.parsing > 0) { + this.startPolling() + } +}, -#### 消息格式 - -状态更新: -```json -{ - "type": "status_update", - "record": { "id": 1, "fileStatus": "parsed_success", ... } +beforeDestroy() { + this.stopPolling() } ``` -统计更新: -```json -{ - "type": "statistics_update", - "statistics": { "uploading": 2, "parsing": 3, ... } -} -``` +## 4. 其他功能 -## 4. 核心功能逻辑 +批量上传弹窗、统计卡片、文件列表等功能保持不变,详见原设计文档。 -### 4.1 批量上传流程 - -1. 文件选择校验(数量≤100,格式.xlsx/.xls,大小≤50MB) -2. 调用 batchUploadFiles API -3. 显示"上传任务已提交"提示 -4. 刷新列表和统计 - -### 4.2 文件列表管理 - -- 支持按状态筛选 -- 支持分页(每页20条) -- 支持手动刷新 - -### 4.3 操作按钮 - -- **查看流水**:解析成功时显示,跳转到流水明细页面 -- **查看错误**:解析失败时显示,弹窗显示错误信息 - -## 5. 异常处理 - -### 5.1 文件上传异常 - -| 异常场景 | 处理方式 | 用户提示 | -|---------|---------|---------| -| 文件数量超限 | 前端校验 | "最多上传100个文件" | -| 文件格式错误 | 前端校验 | "仅支持 .xlsx, .xls 格式文件" | -| 文件大小超限 | 前端校验 | "单个文件不能超过50MB" | -| 网络请求失败 | 捕获异常 | "上传失败:网络错误" | - -### 5.2 WebSocket 异常 - -- 连接失败:记录日志,不影响主要功能 -- 连接断开:自动重连(最多3次,间隔5秒) -- 消息解析失败:忽略该消息 - -## 6. 测试要点 - -### 6.1 功能测试 - -- 文件选择和校验(数量、格式、大小) -- 批量上传流程 -- 状态筛选和分页 -- 操作按钮(查看流水、查看错误) - -### 6.2 WebSocket 测试 - -- 连接建立和断开 -- 实时状态更新 -- 断线重连机制 - -## 7. 开发计划 +## 5. 开发计划 1. **API 接口封装**(0.5天) 2. **批量上传弹窗**(1天) 3. **统计卡片组件**(0.5天) 4. **文件列表组件**(1天) -5. **WebSocket 集成**(1天) +5. **轮询机制**(0.5天) 6. **联调测试**(1天) -**总计**:5个工作日 +**总计**:4.5个工作日 --- **文档结束** +``` diff --git a/doc/plans/2026-03-05-async-file-upload-part4-frontend.md b/doc/plans/2026-03-05-async-file-upload-part4-frontend.md index 39efc1d..f31287a 100644 --- a/doc/plans/2026-03-05-async-file-upload-part4-frontend.md +++ b/doc/plans/2026-03-05-async-file-upload-part4-frontend.md @@ -2,68 +2,353 @@ ## 文档信息 - **创建日期**: 2026-03-05 -- **版本**: v1.0 +- **版本**: v1.1 - **作者**: Claude - **关联设计**: [前端设计文档](../design/2026-03-05-async-file-upload-frontend-design.md) +- **变更说明**: 移除WebSocket,改为页面轮询机制 ## 任务概述 根据前端设计文档,扩展UploadData.vue组件实现异步批量上传功能。 -**预计工时**: 5个工作日 +**预计工时**: 4.5个工作日 ## 任务清单 ### 任务 1: API接口封装(0.5天) -文件: ruoyi-ui/src/api/ccdiProjectUpload.js +**文件**: `ruoyi-ui/src/api/ccdiProjectUpload.js` -新增4个接口函数: -- batchUploadFiles - 批量上传 -- getFileUploadList - 查询列表 -- getFileUploadStatistics - 查询统计 -- getFileUploadDetail - 查询详情 +**工作内容**: +```javascript +// 批量上传文件 +export function batchUploadFiles(projectId, files) { + const formData = new FormData() + files.forEach(file => formData.append('files', file)) + formData.append('projectId', projectId) + + return request({ + url: '/ccdi/file-upload/batch', + method: 'post', + data: formData, + timeout: 300000 + }) +} + +// 查询文件上传记录列表 +export function getFileUploadList(params) { + return request({ + url: '/ccdi/file-upload/list', + method: 'get', + params + }) +} + +// 查询文件上传统计 +export function getFileUploadStatistics(projectId) { + return request({ + url: `/ccdi/file-upload/statistics/${projectId}`, + method: 'get' + }) +} +``` ### 任务 2: 批量上传弹窗(1天) -修改UploadData.vue组件: -- 添加batchUploadDialogVisible状态 -- 修改handleUploadClick方法 -- 实现文件选择和校验 -- 实现批量上传逻辑 +**文件**: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue` + +**主要修改**: +1. 添加批量上传弹窗状态 +2. 修改`handleUploadClick`方法 +3. 实现文件选择和校验逻辑 +4. 实现批量上传功能 + +**关键代码**: +```javascript +// 批量上传 +async handleBatchUpload() { + if (this.selectedFiles.length === 0) { + this.$message.warning('请选择要上传的文件') + return + } + + this.uploadLoading = true + + try { + await batchUploadFiles( + this.projectId, + this.selectedFiles.map(f => f.raw) + ) + + this.uploadLoading = false + this.batchUploadDialogVisible = false + + this.$message.success('上传任务已提交,请查看处理进度') + + // 刷新数据并启动轮询 + await Promise.all([ + this.loadStatistics(), + this.loadFileList() + ]) + + this.startPolling() + + } catch (error) { + this.uploadLoading = false + this.$message.error('上传失败:' + (error.msg || '未知错误')) + } +} +``` ### 任务 3: 统计卡片(0.5天) -添加统计卡片展示: -- 显示4种状态数量 -- 点击筛选功能 +**工作内容**: +1. 添加统计数据状态 +2. 实现统计卡片组件 +3. 实现点击筛选功能 + +**模板代码**: +```vue +