Files
ccdi/doc/plans/2026-03-05-async-file-upload-part4-frontend.md
wkc 656453ea50 refactor: 移除WebSocket,改为页面轮询机制
- 移除WebSocket相关设计
- 添加页面轮询机制设计
- 轮询间隔:5秒
- 自动启动/停止策略
- 支持手动刷新
2026-03-05 10:39:35 +08:00

356 lines
7.6 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.
# 异步文件上传功能实施计划 - Part 4: 前端开发
## 文档信息
- **创建日期**: 2026-03-05
- **版本**: v1.1
- **作者**: Claude
- **关联设计**: [前端设计文档](../design/2026-03-05-async-file-upload-frontend-design.md)
- **变更说明**: 移除WebSocket改为页面轮询机制
## 任务概述
根据前端设计文档扩展UploadData.vue组件实现异步批量上传功能。
**预计工时**: 4.5个工作日
## 任务清单
### 任务 1: API接口封装0.5天)
**文件**: `ruoyi-ui/src/api/ccdiProjectUpload.js`
**工作内容**:
```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天
**文件**: `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天)
**工作内容**:
1. 添加统计数据状态
2. 实现统计卡片组件
3. 实现点击筛选功能
**模板代码**:
```vue
<div class="statistics-section">
<div class="stat-card" @click="handleStatusFilter('uploading')">
<div class="stat-icon uploading">
<i class="el-icon-upload"></i>
</div>
<div class="stat-content">
<div class="stat-label">上传中</div>
<div class="stat-value">{{ statistics.uploading }}</div>
</div>
</div>
<!-- 其他3个统计卡片 -->
</div>
```
### 任务 4: 文件列表1天
**工作内容**:
1. 添加文件列表状态
2. 实现文件列表组件
3. 实现分页和筛选
4. 实现操作按钮
**关键方法**:
```javascript
// 加载文件列表
async loadFileList() {
this.listLoading = true
try {
const res = await getFileUploadList({
projectId: this.projectId,
fileStatus: this.queryParams.fileStatus,
pageNum: this.queryParams.pageNum,
pageSize: this.queryParams.pageSize
})
this.fileList = res.rows || []
this.total = res.total || 0
} finally {
this.listLoading = false
}
}
```
### 任务 5: 轮询机制0.5天)
**优先级**: P0
**依赖**: 任务2、任务3、任务4完成
**工作内容**:
1. **添加轮询状态**:
```javascript
data() {
return {
// 轮询相关
pollingTimer: null,
pollingEnabled: false,
pollingInterval: 5000 // 5秒轮询间隔
}
}
```
2. **生命周期钩子**:
```javascript
mounted() {
this.loadStatistics()
this.loadFileList()
// 检查是否需要启动轮询
this.$nextTick(() => {
if (this.statistics.uploading > 0 || this.statistics.parsing > 0) {
this.startPolling()
}
})
},
beforeDestroy() {
this.stopPolling()
}
```
3. **轮询方法**:
```javascript
methods: {
/**
* 启动轮询
*/
startPolling() {
if (this.pollingEnabled) {
return // 已经在轮询中
}
this.pollingEnabled = true
console.log('启动轮询')
const poll = () => {
if (!this.pollingEnabled) {
return
}
// 刷新统计数据和列表
Promise.all([
this.loadStatistics(),
this.loadFileList()
]).then(() => {
// 检查是否需要继续轮询
if (this.statistics.uploading === 0 &&
this.statistics.parsing === 0) {
this.stopPolling()
console.log('所有任务已完成,停止轮询')
return
}
// 继续下一次轮询
this.pollingTimer = setTimeout(poll, this.pollingInterval)
}).catch(error => {
console.error('轮询失败:', error)
// 发生错误时继续轮询
this.pollingTimer = setTimeout(poll, this.pollingInterval)
})
}
// 立即执行一次
poll()
},
/**
* 停止轮询
*/
stopPolling() {
this.pollingEnabled = false
if (this.pollingTimer) {
clearTimeout(this.pollingTimer)
this.pollingTimer = null
}
console.log('停止轮询')
},
/**
* 手动刷新
*/
async handleManualRefresh() {
await Promise.all([
this.loadStatistics(),
this.loadFileList()
])
this.$message.success('刷新成功')
// 如果有进行中的任务,启动轮询
if (this.statistics.uploading > 0 || this.statistics.parsing > 0) {
this.startPolling()
}
},
/**
* 状态筛选
*/
handleStatusFilter(status) {
this.queryParams.fileStatus = status
this.queryParams.pageNum = 1
this.loadFileList()
}
}
```
4. **在模板中添加刷新按钮**:
```vue
<el-button
icon="el-icon-refresh"
@click="handleManualRefresh"
>
刷新
</el-button>
```
#### 5.2 验证方式
1. **启动轮询测试**
- 上传文件后,检查控制台输出"启动轮询"
- 观察5秒后数据是否自动刷新
2. **停止轮询测试**
- 等待所有文件处理完成
- 检查控制台输出"停止轮询"
3. **手动刷新测试**
- 点击刷新按钮
- 验证数据立即更新
- 验证提示消息显示
4. **页面销毁测试**
- 切换到其他页面
- 检查控制台输出"停止轮询"
- 确认定时器被清除
### 任务 6: 联调测试1天
**测试项**:
1. 批量上传功能
2. 统计卡片展示和筛选
3. 文件列表展示和分页
4. 轮询机制(启动、停止、手动刷新)
5. 操作按钮(查看流水、查看错误)
## 验收标准
- [ ] 所有API接口正常调用
- [ ] 批量上传弹窗正常工作
- [ ] 统计卡片正常显示和筛选
- [ ] 文件列表正常展示和操作
- [ ] 轮询机制正常(自动启动/停止/手动刷新)
- [ ] 所有测试项通过
## 轮询优化建议(可选)
**智能轮询间隔**
```javascript
// 根据活跃任务数动态调整轮询间隔
getPollingInterval() {
const { uploading, parsing } = this.statistics
const activeCount = uploading + parsing
if (activeCount > 50) {
return 3000 // 大量任务时3秒轮询
} else if (activeCount > 10) {
return 5000 // 正常情况5秒轮询
} else {
return 10000 // 少量任务时10秒轮询
}
}
```
**用户体验优化**
- 在页面顶部显示"自动刷新中..."状态提示
- 支持用户手动开关轮询开关
---
**文档结束**