264 lines
7.8 KiB
Markdown
264 lines
7.8 KiB
Markdown
|
|
# 保险端导出功能修复总结
|
|||
|
|
|
|||
|
|
## 修复时间
|
|||
|
|
2025年10月9日
|
|||
|
|
|
|||
|
|
## 问题描述
|
|||
|
|
前端导出Excel功能虽然能收到后端返回的Excel文件数据(Blob格式),但文件无法正常下载或内容异常。
|
|||
|
|
|
|||
|
|
## 问题根源
|
|||
|
|
|
|||
|
|
### 1. 后端问题
|
|||
|
|
- **路由顺序错误**:部分模块的 `/export` 路由放在动态路由 `/:id` 之后,导致404错误
|
|||
|
|
- **缺少导出方法**:部分控制器缺少 `exportToExcel` 方法
|
|||
|
|
|
|||
|
|
### 2. 前端问题
|
|||
|
|
- **request.js 响应处理**:`handleResponse` 函数不支持 blob 类型响应
|
|||
|
|
- **Blob 包装错误**:前端代码使用 `new Blob([response.data])` 重复包装已经是 Blob 的数据
|
|||
|
|
|
|||
|
|
## 修复方案
|
|||
|
|
|
|||
|
|
### 后端修复(insurance_backend)
|
|||
|
|
|
|||
|
|
#### 1. 添加导出方法到控制器
|
|||
|
|
|
|||
|
|
为以下控制器添加了 `exportToExcel` 方法:
|
|||
|
|
- ✅ `userController.js` - 用户管理导出
|
|||
|
|
- ✅ `supervisoryTaskController.js` - 监管任务导出
|
|||
|
|
- ✅ `installationTaskController.js` - 待安装任务导出
|
|||
|
|
- ✅ `deviceAlertController.js` - 设备预警导出
|
|||
|
|
- ✅ `insuranceTypeController.js` - 保险类型导出
|
|||
|
|
- ✅ `policyController.js` - 保单管理导出
|
|||
|
|
- ✅ `livestockClaimController.js` - 理赔管理导出
|
|||
|
|
- ✅ `livestockPolicyController.js` - 生资保单管理导出
|
|||
|
|
- ✅ `operationLogController.js` - 操作日志导出
|
|||
|
|
|
|||
|
|
#### 2. 修复路由顺序
|
|||
|
|
|
|||
|
|
在以下路由文件中,将 `/export` 和 `/stats` 路由移到 `/:id` 之前:
|
|||
|
|
- ✅ `routes/users.js`
|
|||
|
|
- ✅ `routes/supervisoryTasks.js`
|
|||
|
|
- ✅ `routes/installationTasks.js`
|
|||
|
|
- ✅ `routes/deviceAlerts.js`
|
|||
|
|
- ✅ `routes/insuranceTypes.js`
|
|||
|
|
- ✅ `routes/policies.js`
|
|||
|
|
- ✅ `routes/livestockClaims.js`
|
|||
|
|
- ✅ `routes/livestockPolicies.js`
|
|||
|
|
- ✅ `routes/operationLogs.js`
|
|||
|
|
|
|||
|
|
**正确的路由顺序示例:**
|
|||
|
|
```javascript
|
|||
|
|
// 1. 统计接口
|
|||
|
|
router.get('/stats', jwtAuth, requirePermission('xxx:read'), controller.getStats);
|
|||
|
|
|
|||
|
|
// 2. 导出接口
|
|||
|
|
router.get('/export', jwtAuth, requirePermission('xxx:read'), controller.exportToExcel);
|
|||
|
|
|
|||
|
|
// 3. 列表接口
|
|||
|
|
router.get('/', jwtAuth, requirePermission('xxx:read'), controller.getList);
|
|||
|
|
|
|||
|
|
// 4. 详情接口(动态路由放最后)
|
|||
|
|
router.get('/:id', jwtAuth, requirePermission('xxx:read'), controller.getById);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3. 导出工具类
|
|||
|
|
|
|||
|
|
创建了通用的导出工具 `utils/excelExport.js`,提供:
|
|||
|
|
- `exportToExcel(data, columns, sheetName)` - 生成Excel文件
|
|||
|
|
- `formatDate(date)` - 格式化日期
|
|||
|
|
- `formatStatus(status, statusMap)` - 格式化状态
|
|||
|
|
|
|||
|
|
### 前端修复(insurance_admin-system)
|
|||
|
|
|
|||
|
|
#### 1. 修复 request.js
|
|||
|
|
|
|||
|
|
修改 `src/utils/request.js` 中的 `handleResponse` 函数,添加对 blob 类型的支持:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const handleResponse = async (response) => {
|
|||
|
|
let data
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const contentType = response.headers.get('content-type')
|
|||
|
|
|
|||
|
|
// 处理Excel文件下载
|
|||
|
|
if (contentType && contentType.includes('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')) {
|
|||
|
|
data = await response.blob()
|
|||
|
|
} else if (contentType && contentType.includes('application/json')) {
|
|||
|
|
data = await response.json()
|
|||
|
|
} else {
|
|||
|
|
data = await response.text()
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
data = null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
const error = new Error(data?.message || `HTTP ${response.status}: ${response.statusText}`)
|
|||
|
|
error.response = {
|
|||
|
|
status: response.status,
|
|||
|
|
statusText: response.statusText,
|
|||
|
|
data: data
|
|||
|
|
}
|
|||
|
|
throw error
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { data, status: response.status, statusText: response.statusText }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2. 修复前端页面的 Blob 处理
|
|||
|
|
|
|||
|
|
修改以下页面中的导出方法,将:
|
|||
|
|
```javascript
|
|||
|
|
const url = window.URL.createObjectURL(new Blob([response.data]))
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
改为:
|
|||
|
|
```javascript
|
|||
|
|
const url = window.URL.createObjectURL(response.data)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
已修复的页面:
|
|||
|
|
- ✅ `UserManagement.vue` - 用户管理
|
|||
|
|
- ✅ `MessageNotification.vue` - 消息通知(设备预警)
|
|||
|
|
- ✅ `InsuranceTypeManagement.vue` - 保险类型管理
|
|||
|
|
- ✅ `PolicyManagement.vue` - 保单管理
|
|||
|
|
- ✅ `ClaimManagement.vue` - 理赔管理
|
|||
|
|
- ✅ `SupervisionTaskManagement.vue` - 监管任务管理
|
|||
|
|
- ✅ `CompletedTaskManagement.vue` - 监管任务结项
|
|||
|
|
- ✅ `InstallationTaskManagement.vue` - 待安装任务
|
|||
|
|
- ✅ `LivestockPolicyManagement.vue` - 生资保单管理
|
|||
|
|
- ✅ `SystemSettings.vue` - 系统设置(操作日志)
|
|||
|
|
- ✅ `ApplicationManagement.vue` - 申请管理
|
|||
|
|
|
|||
|
|
## 导出功能支持的筛选参数
|
|||
|
|
|
|||
|
|
### 用户管理
|
|||
|
|
- `search` - 用户名搜索
|
|||
|
|
- `status` - 状态筛选
|
|||
|
|
|
|||
|
|
### 监管任务
|
|||
|
|
- `policyNumber` - 保单编号
|
|||
|
|
- `customerName` - 客户姓名
|
|||
|
|
|
|||
|
|
### 待安装任务
|
|||
|
|
- `policyNumber` - 保单编号
|
|||
|
|
- `keyword` - 关键字搜索
|
|||
|
|
|
|||
|
|
### 设备预警
|
|||
|
|
- `alert_level` - 预警级别
|
|||
|
|
- `alert_type` - 预警类型
|
|||
|
|
- `status` - 处理状态
|
|||
|
|
- `is_read` - 是否已读
|
|||
|
|
|
|||
|
|
### 保险类型
|
|||
|
|
- `name` - 险种名称
|
|||
|
|
- `status` - 状态
|
|||
|
|
|
|||
|
|
### 保单管理
|
|||
|
|
- `policy_number` - 保单编号
|
|||
|
|
- `policyholder_name` - 投保人姓名
|
|||
|
|
- `status` - 状态
|
|||
|
|
|
|||
|
|
### 理赔管理
|
|||
|
|
- `claim_number` - 理赔编号
|
|||
|
|
- `claimant_name` - 理赔人姓名
|
|||
|
|
- `status` - 状态
|
|||
|
|
|
|||
|
|
### 生资保单
|
|||
|
|
- `policy_no` - 保单号
|
|||
|
|
- `farmer_name` - 农户姓名
|
|||
|
|
- `policy_status` - 保单状态
|
|||
|
|
|
|||
|
|
### 操作日志
|
|||
|
|
- `username` - 用户名
|
|||
|
|
- `operation_type` - 操作类型
|
|||
|
|
- `operation_module` - 操作模块
|
|||
|
|
- `startDate` - 开始日期
|
|||
|
|
- `endDate` - 结束日期
|
|||
|
|
|
|||
|
|
## 技术要点
|
|||
|
|
|
|||
|
|
### 1. Excel 文件生成
|
|||
|
|
- 使用 `exceljs` 库生成 Excel 文件
|
|||
|
|
- 支持自定义列宽、表头、数据格式化
|
|||
|
|
- 自动设置表头样式(加粗、边框)
|
|||
|
|
|
|||
|
|
### 2. 数据格式化
|
|||
|
|
- 日期格式:`YYYY-MM-DD HH:mm:ss`
|
|||
|
|
- 状态映射:使用中文映射英文状态值
|
|||
|
|
- 空值处理:显示为空字符串或0
|
|||
|
|
|
|||
|
|
### 3. 文件下载
|
|||
|
|
- Content-Type: `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
|
|||
|
|
- Content-Disposition: `attachment; filename=xxx_${timestamp}.xlsx`
|
|||
|
|
- 前端使用 `window.URL.createObjectURL` 创建下载链接
|
|||
|
|
|
|||
|
|
### 4. 权限控制
|
|||
|
|
- 所有导出接口都需要 JWT 认证
|
|||
|
|
- 需要相应模块的读取权限
|
|||
|
|
|
|||
|
|
## 测试步骤
|
|||
|
|
|
|||
|
|
1. **启动后端服务**
|
|||
|
|
```bash
|
|||
|
|
cd insurance_backend
|
|||
|
|
node src/app.js
|
|||
|
|
```
|
|||
|
|
确保服务运行在 `http://localhost:3000`
|
|||
|
|
|
|||
|
|
2. **启动前端服务**
|
|||
|
|
```bash
|
|||
|
|
cd insurance_admin-system
|
|||
|
|
npm run dev
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **测试导出功能**
|
|||
|
|
- 登录系统(admin / 123456)
|
|||
|
|
- 进入各个功能模块
|
|||
|
|
- 点击"导出Excel"按钮
|
|||
|
|
- 验证文件能正常下载且内容正确
|
|||
|
|
|
|||
|
|
## 常见问题排查
|
|||
|
|
|
|||
|
|
### 1. 404 错误
|
|||
|
|
- **原因**:导出路由在动态路由之后
|
|||
|
|
- **解决**:将 `/export` 路由移到 `/:id` 之前
|
|||
|
|
|
|||
|
|
### 2. Excel 文件损坏或无法打开
|
|||
|
|
- **原因**:前端重复包装 Blob 数据
|
|||
|
|
- **解决**:直接使用 `response.data`,不要用 `new Blob([response.data])`
|
|||
|
|
|
|||
|
|
### 3. 导出的 Excel 为空
|
|||
|
|
- **原因**:数据库查询条件有误或数据为空
|
|||
|
|
- **解决**:检查控制器中的查询逻辑和where条件
|
|||
|
|
|
|||
|
|
### 4. 权限错误
|
|||
|
|
- **原因**:用户没有导出权限
|
|||
|
|
- **解决**:在数据库中为用户角色添加相应的导出权限
|
|||
|
|
|
|||
|
|
## 注意事项
|
|||
|
|
|
|||
|
|
1. **不要修改端口号**:后端固定3000端口,前端固定3001端口
|
|||
|
|
2. **测试文件清理**:所有测试文件会自动删除
|
|||
|
|
3. **权限验证**:确保每个导出接口都有权限中间件
|
|||
|
|
4. **数据量控制**:大量数据导出时注意性能和内存占用
|
|||
|
|
5. **文件命名**:使用时间戳避免文件名冲突
|
|||
|
|
|
|||
|
|
## 后续优化建议
|
|||
|
|
|
|||
|
|
1. **分页导出**:对于大量数据,支持分批导出
|
|||
|
|
2. **异步导出**:数据量大时改为异步任务,生成后通知用户下载
|
|||
|
|
3. **导出模板**:提供 Excel 模板下载功能
|
|||
|
|
4. **导出历史**:记录导出操作日志
|
|||
|
|
5. **格式选择**:支持导出为 CSV、PDF 等其他格式
|
|||
|
|
6. **数据汇总**:在 Excel 中添加汇总统计信息
|
|||
|
|
|
|||
|
|
## 相关文档
|
|||
|
|
|
|||
|
|
- 后端接口文档:`insurance_backend/docs/EXPORT_IMPLEMENTATION_GUIDE.md`
|
|||
|
|
- API 文档:Swagger UI `http://localhost:3000/api-docs`
|
|||
|
|
- 前端环境配置:`insurance_admin-system/ENV_CONFIG.md`
|
|||
|
|
|