完善小程序
This commit is contained in:
@@ -37,7 +37,7 @@ alwaysApply: true
|
||||
- 运维文档.md
|
||||
- 安全文档.md
|
||||
- 用户手册文档.md
|
||||
7. DB_DIALECT || 'mysql',
|
||||
7. 养殖端的数据库信息为:DB_DIALECT || 'mysql',
|
||||
DB_HOST = '129.211.213.226',
|
||||
DB_PORT = 9527,
|
||||
DB_DATABASE = 'nxxmdata',
|
||||
@@ -51,4 +51,10 @@ DB_PASSWORD = 'aiotAiot123!',
|
||||
13. 不要修改前后端端口号。发现端口占用先杀死端口,再打开,不要修改端口号。规定死保险端的后端端口为3000,前端端口为3001.
|
||||
14. 每次运行命令都要先看项目规则。
|
||||
15. PowerShell不支持&&操作符,请使用;符号1. 请保持对话语言为中文
|
||||
16. 开发养殖端微信小程序时后端的API接口全部请求到https://ad.ningmuyun.com/
|
||||
16. 开发养殖端微信小程序时后端的API接口全部请求到https://ad.ningmuyun.com/
|
||||
17. 保险端的数据库信息为:DB_DIALECT || 'mysql',
|
||||
DB_HOST = '129.211.213.226',
|
||||
DB_PORT = 9527,
|
||||
DB_DATABASE = 'insurance_data',
|
||||
DB_USER = 'root',
|
||||
DB_PASSWORD = 'aiotAiot123!',
|
||||
263
docs/导出功能修复总结.md
Normal file
263
docs/导出功能修复总结.md
Normal file
@@ -0,0 +1,263 @@
|
||||
# 保险端导出功能修复总结
|
||||
|
||||
## 修复时间
|
||||
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`
|
||||
|
||||
86
docs/待安装任务导出字段说明.md
Normal file
86
docs/待安装任务导出字段说明.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# 待安装任务导出字段映射说明
|
||||
|
||||
## 数据库字段对照表
|
||||
|
||||
| Excel列名 | 数据库字段 | 字段说明 | 是否必填 |
|
||||
|---------|----------|---------|---------|
|
||||
| 申请单号 | application_number | 申请单号 | 是 |
|
||||
| 保单编号 | policy_number | 保单编号 | 是 |
|
||||
| 产品名称 | product_name | 产品名称 | 是 |
|
||||
| 客户姓名 | customer_name | 客户姓名 | 是 |
|
||||
| 证件类型 | id_type | 证件类型 | 是 |
|
||||
| 证件号码 | id_number | 证件号码 | 是 |
|
||||
| 养殖生资种类 | livestock_supply_type | 养殖生资种类 | 否 |
|
||||
| 安装状态 | installation_status | 安装状态 | 是 |
|
||||
| 优先级 | priority | 任务优先级 | 是 |
|
||||
| 安装地址 | installation_address | 安装地址 | 否 |
|
||||
| 联系电话 | contact_phone | 联系电话 | 否 |
|
||||
| 任务生成时间 | task_generated_time | 任务生成时间 | 否 |
|
||||
| 安装完成时间 | installation_completed_time | 安装完成时间 | 否 |
|
||||
| 创建时间 | created_at | 创建时间 | 是 |
|
||||
| 更新时间 | updated_at | 更新时间 | 是 |
|
||||
|
||||
## 字段值说明
|
||||
|
||||
### 证件类型 (id_type)
|
||||
- 身份证
|
||||
- 护照
|
||||
- 军官证
|
||||
- 士兵证
|
||||
- 港澳台居民居住证
|
||||
- 其他
|
||||
|
||||
### 安装状态 (installation_status)
|
||||
- 待安装
|
||||
- 安装中
|
||||
- 已安装
|
||||
- 安装失败
|
||||
- 已取消
|
||||
|
||||
### 优先级 (priority)
|
||||
- 低
|
||||
- 中
|
||||
- 高
|
||||
- 紧急
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **数据库字段格式**:使用下划线命名(snake_case)
|
||||
2. **模型字段格式**:使用驼峰命名(camelCase)
|
||||
3. **raw查询返回**:返回的是数据库字段名(下划线格式)
|
||||
4. **空值处理**:
|
||||
- 字符串字段:显示为空字符串 `''`
|
||||
- 日期字段:通过 `ExcelExport.formatDate()` 处理,空值显示为空字符串
|
||||
5. **状态值**:直接使用数据库中的中文值,不需要额外映射
|
||||
|
||||
## 导出示例数据
|
||||
|
||||
```json
|
||||
{
|
||||
"applicationNumber": "APP001",
|
||||
"policyNumber": "POL001",
|
||||
"productName": "智能耳标监控系统",
|
||||
"customerName": "张三",
|
||||
"idType": "身份证",
|
||||
"idNumber": "110101199001011234",
|
||||
"livestockSupplyType": "牛养殖",
|
||||
"installationStatus": "待安装",
|
||||
"priority": "高",
|
||||
"installationAddress": null,
|
||||
"contactPhone": null,
|
||||
"taskGeneratedTime": "2025-09-22 18:28:58",
|
||||
"installationCompletedTime": null,
|
||||
"createdAt": "2025-09-22 18:28:58",
|
||||
"updatedAt": "2025-09-22 18:28:58"
|
||||
}
|
||||
```
|
||||
|
||||
## 修复历史
|
||||
|
||||
### 2025-10-09 修复内容
|
||||
1. ✅ 修复字段映射错误(驼峰 → 下划线)
|
||||
2. ✅ 添加缺失字段:证件类型、优先级、安装地址、联系电话、更新时间
|
||||
3. ✅ 移除不存在的字段映射
|
||||
4. ✅ 删除多余的状态映射逻辑
|
||||
5. ✅ 统一空值处理方式
|
||||
|
||||
313
docs/监管任务模块问题诊断报告.md
Normal file
313
docs/监管任务模块问题诊断报告.md
Normal file
@@ -0,0 +1,313 @@
|
||||
# 监管任务模块问题诊断报告
|
||||
|
||||
## 诊断时间
|
||||
2025年10月9日
|
||||
|
||||
## 问题描述
|
||||
用户反映:监管任务结项管理模块,表中有数据,但是前端页面没有数据,后端API接口返回也没数据。
|
||||
|
||||
## 诊断结果
|
||||
|
||||
### ✅ 后端状态 - 完全正常
|
||||
|
||||
#### 1. 数据库层面
|
||||
- **表名**:`supervisory_tasks`
|
||||
- **数据**:存在1条测试数据
|
||||
- **结构**:正确
|
||||
|
||||
```sql
|
||||
SELECT COUNT(*) FROM supervisory_tasks;
|
||||
-- 结果: 1条记录
|
||||
|
||||
SELECT * FROM supervisory_tasks LIMIT 1;
|
||||
-- 数据正常,包含完整字段
|
||||
```
|
||||
|
||||
#### 2. Model层面
|
||||
- **模型文件**:`models/SupervisoryTask.js`
|
||||
- **表映射**:正确 (`tableName: 'supervisory_tasks'`)
|
||||
- **查询测试**:✅ 通过
|
||||
```javascript
|
||||
// 直接通过Model查询成功
|
||||
const tasks = await SupervisoryTask.findAll();
|
||||
// 结果: 返回1条记录
|
||||
```
|
||||
|
||||
#### 3. API层面
|
||||
- **控制器**:`controllers/supervisoryTaskController.js`
|
||||
- **路由**:`routes/supervisoryTasks.js`
|
||||
- **注册**:✅ 正确注册在 `src/app.js`
|
||||
- `/api/supervisory-tasks` (备用路径)
|
||||
- `/api/supervision-tasks` (主要路径 - 前端使用)
|
||||
- **API测试**:✅ 完全正常
|
||||
|
||||
**API测试结果:**
|
||||
```bash
|
||||
GET /api/supervision-tasks?page=1&limit=10
|
||||
Authorization: Bearer <token>
|
||||
|
||||
响应:
|
||||
{
|
||||
"code": 200,
|
||||
"status": "success",
|
||||
"message": "获取监管任务列表成功",
|
||||
"data": {
|
||||
"list": [
|
||||
{
|
||||
"id": 1,
|
||||
"applicationNumber": "APP2025001",
|
||||
"policyNumber": "POL2025001",
|
||||
"productName": "农业保险产品",
|
||||
"customerName": "张三",
|
||||
"taskStatus": "待处理",
|
||||
"priority": "中",
|
||||
...
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"page": 1,
|
||||
"limit": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 后端服务状态
|
||||
- **端口**:3000
|
||||
- **状态**:✅ 正在运行
|
||||
- **PID**:19340
|
||||
|
||||
### ✅ 前端状态
|
||||
|
||||
#### 1. 服务状态
|
||||
- **端口**:3001
|
||||
- **状态**:✅ 正在运行
|
||||
- **PID**:18648
|
||||
|
||||
#### 2. 代理配置
|
||||
```javascript
|
||||
// vite.config.js
|
||||
proxy: {
|
||||
'/insurance/api': {
|
||||
target: 'http://localhost:3000',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/insurance/, '')
|
||||
}
|
||||
}
|
||||
```
|
||||
配置:✅ 正确
|
||||
|
||||
#### 3. API配置
|
||||
```javascript
|
||||
// src/utils/api.js
|
||||
export const supervisionTaskApi = {
|
||||
getList: (params) => api.get('/supervision-tasks', { params }),
|
||||
getStats: () => api.get('/supervision-tasks/stats'),
|
||||
export: (params) => api.get('/supervision-tasks/export', { params, responseType: 'blob' }),
|
||||
...
|
||||
}
|
||||
```
|
||||
配置:✅ 正确
|
||||
|
||||
#### 4. 环境配置
|
||||
```javascript
|
||||
// src/config/env.js
|
||||
const envConfig = {
|
||||
baseURL: '/insurance/api',
|
||||
timeout: 10000
|
||||
}
|
||||
```
|
||||
配置:✅ 正确
|
||||
|
||||
## 可能的问题原因
|
||||
|
||||
### 1. 认证Token问题 ⚠️
|
||||
前端可能没有正确发送认证token或token已过期。
|
||||
|
||||
**验证方法**:
|
||||
```javascript
|
||||
// 在浏览器控制台检查
|
||||
localStorage.getItem('accessToken')
|
||||
localStorage.getItem('refreshToken')
|
||||
```
|
||||
|
||||
### 2. 请求拦截器问题 ⚠️
|
||||
前端的请求拦截器可能没有正确添加Authorization头。
|
||||
|
||||
**检查文件**:`insurance_admin-system/src/utils/request.js`
|
||||
|
||||
### 3. 数据响应格式解析问题 ⚠️
|
||||
前端页面可能没有正确解析后端返回的数据格式。
|
||||
|
||||
**后端返回格式**:
|
||||
```javascript
|
||||
{
|
||||
data: {
|
||||
status: 'success',
|
||||
data: {
|
||||
list: [...],
|
||||
total: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**前端期望格式**:需要检查 `CompletedTaskManagement.vue` 和 `SupervisionTaskManagement.vue`
|
||||
|
||||
### 4. 路由路径不匹配 ⚠️
|
||||
如果前端使用了错误的API路径。
|
||||
|
||||
### 5. 跨域问题 ⚠️
|
||||
虽然后端配置了CORS,但可能存在配置问题。
|
||||
|
||||
## 建议的解决步骤
|
||||
|
||||
### 步骤1:检查用户登录状态
|
||||
```javascript
|
||||
// 在浏览器控制台执行
|
||||
console.log('Token:', localStorage.getItem('accessToken'));
|
||||
console.log('是否登录:', !!localStorage.getItem('accessToken'));
|
||||
```
|
||||
|
||||
### 步骤2:检查网络请求
|
||||
1. 打开浏览器开发者工具 (F12)
|
||||
2. 切换到 Network 标签
|
||||
3. 刷新页面
|
||||
4. 查找 `/supervision-tasks` 请求
|
||||
5. 检查:
|
||||
- 请求URL是否正确
|
||||
- 请求头是否包含 `Authorization: Bearer <token>`
|
||||
- 响应状态码
|
||||
- 响应内容
|
||||
|
||||
### 步骤3:检查控制台错误
|
||||
1. 打开浏览器开发者工具Console标签
|
||||
2. 查看是否有JavaScript错误
|
||||
3. 查看是否有API请求失败的错误信息
|
||||
|
||||
### 步骤4:强制刷新Token
|
||||
```javascript
|
||||
// 在前端代码中
|
||||
1. 清除现有token
|
||||
localStorage.clear()
|
||||
|
||||
2. 重新登录
|
||||
使用账号: admin
|
||||
密码: 123456
|
||||
```
|
||||
|
||||
### 步骤5:检查数据处理逻辑
|
||||
检查前端页面中的数据响应处理:
|
||||
|
||||
**CompletedTaskManagement.vue**:
|
||||
```javascript
|
||||
// 第466-476行
|
||||
const response = await supervisionTaskApi.getList(params)
|
||||
console.log('监管任务结项API响应:', response)
|
||||
|
||||
if (response.data && response.data.status === 'success') {
|
||||
taskList.value = response.data.data.list || []
|
||||
pagination.total = response.data.data.total || 0
|
||||
}
|
||||
```
|
||||
|
||||
**SupervisionTaskManagement.vue**:
|
||||
```javascript
|
||||
// 第513-522行
|
||||
const response = await supervisionTaskApi.getList(params)
|
||||
console.log('监管任务API响应:', response)
|
||||
|
||||
if (response.data && response.data.status === 'success') {
|
||||
tableData.value = response.data.data.list
|
||||
pagination.total = response.data.data.total
|
||||
}
|
||||
```
|
||||
|
||||
## 测试命令
|
||||
|
||||
### 测试数据库
|
||||
```bash
|
||||
cd insurance_backend
|
||||
node test-supervisory-table.js
|
||||
```
|
||||
|
||||
### 测试API(需要后端服务运行)
|
||||
```bash
|
||||
cd insurance_backend
|
||||
node test-full-api-flow.js
|
||||
```
|
||||
|
||||
### 启动后端服务
|
||||
```bash
|
||||
cd insurance_backend
|
||||
npm start
|
||||
```
|
||||
|
||||
### 启动前端服务
|
||||
```bash
|
||||
cd insurance_admin-system
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 前端调试代码
|
||||
|
||||
在浏览器控制台执行以下代码测试API:
|
||||
|
||||
```javascript
|
||||
// 1. 获取token
|
||||
const token = localStorage.getItem('accessToken');
|
||||
console.log('Token:', token);
|
||||
|
||||
// 2. 手动测试API
|
||||
fetch('/insurance/api/supervision-tasks?page=1&limit=10', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log('API响应:', data);
|
||||
if (data.status === 'success') {
|
||||
console.log('✓ 数据获取成功');
|
||||
console.log('记录数:', data.data.total);
|
||||
console.log('数据列表:', data.data.list);
|
||||
} else {
|
||||
console.log('✗ 响应状态异常');
|
||||
}
|
||||
})
|
||||
.catch(err => console.error('✗ 请求失败:', err));
|
||||
```
|
||||
|
||||
## 结论
|
||||
|
||||
**后端完全正常**,问题很可能出在前端的以下几个方面:
|
||||
|
||||
1. **认证token未正确发送**(最可能)
|
||||
2. **数据响应格式解析错误**
|
||||
3. **页面组件渲染逻辑问题**
|
||||
4. **浏览器缓存问题**
|
||||
|
||||
建议用户按照上述步骤逐一排查,特别是先检查浏览器Network标签中的实际请求情况。
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 后端文件
|
||||
- ✅ `models/SupervisoryTask.js` - 模型定义正确
|
||||
- ✅ `controllers/supervisoryTaskController.js` - 控制器逻辑正确
|
||||
- ✅ `routes/supervisoryTasks.js` - 路由配置正确
|
||||
- ✅ `src/app.js` - 路由注册正确
|
||||
|
||||
### 前端文件
|
||||
- ✅ `src/config/env.js` - 环境配置(新增)
|
||||
- ✅ `src/utils/api.js` - API封装正确
|
||||
- ✅ `src/utils/request.js` - 请求工具配置正确
|
||||
- ✅ `src/views/CompletedTaskManagement.vue` - 已更新使用API
|
||||
- ✅ `src/views/SupervisionTaskManagement.vue` - 已使用API
|
||||
- ✅ `vite.config.js` - 代理配置正确
|
||||
|
||||
### 测试文件(可删除)
|
||||
- `insurance_backend/test-supervisory-table.js`
|
||||
- `insurance_backend/test-api-response.js`
|
||||
- `insurance_backend/test-api-direct.js`
|
||||
- `insurance_backend/test-full-api-flow.js`
|
||||
|
||||
267
docs/监管任务结项管理API封装修复说明.md
Normal file
267
docs/监管任务结项管理API封装修复说明.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# 监管任务结项管理API封装修复说明
|
||||
|
||||
## 修复日期
|
||||
2025年10月9日
|
||||
|
||||
## 问题描述
|
||||
监管任务结项管理模块(`CompletedTaskManagement.vue`)中使用了模拟数据,没有调用真实的后端API接口。需要将其改为使用封装的API工具类。
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 创建环境配置文件 `src/config/env.js`
|
||||
|
||||
创建了统一的环境配置文件,用于管理所有API URL和环境变量:
|
||||
|
||||
**主要功能:**
|
||||
- 支持多环境配置(development、production、test)
|
||||
- 统一管理API基础URL、超时时间等配置
|
||||
- 定义了所有API端点的路径常量
|
||||
|
||||
**配置项:**
|
||||
```javascript
|
||||
{
|
||||
baseURL: '/insurance/api',
|
||||
timeout: 10000,
|
||||
wsURL: 'ws://localhost:3000'
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 更新 API 工具类 `src/utils/api.js`
|
||||
|
||||
在 `supervisionTaskApi` 中添加了两个新的API方法:
|
||||
|
||||
```javascript
|
||||
export const supervisionTaskApi = {
|
||||
// ... 现有方法
|
||||
archive: (id) => api.patch(`/supervision-tasks/${id}/archive`),
|
||||
downloadReport: (id) => api.get(`/supervision-tasks/${id}/report`, { responseType: 'blob' })
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 更新请求工具 `src/utils/request.js`
|
||||
|
||||
- 导入环境配置文件
|
||||
- 使用 `envConfig.baseURL` 和 `envConfig.timeout` 替代硬编码的配置
|
||||
|
||||
**修改前:**
|
||||
```javascript
|
||||
const API_CONFIG = {
|
||||
baseURL: '/insurance/api',
|
||||
timeout: 10000
|
||||
}
|
||||
```
|
||||
|
||||
**修改后:**
|
||||
```javascript
|
||||
import envConfig from '@/config/env'
|
||||
|
||||
const API_CONFIG = {
|
||||
baseURL: envConfig.baseURL,
|
||||
timeout: envConfig.timeout
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 更新用户状态管理 `src/stores/user.js`
|
||||
|
||||
- 导入环境配置文件
|
||||
- 在刷新token的fetch请求中使用 `envConfig.baseURL`
|
||||
|
||||
**修改前:**
|
||||
```javascript
|
||||
const response = await fetch('/insurance/api/auth/refresh', {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
**修改后:**
|
||||
```javascript
|
||||
import envConfig from '@/config/env'
|
||||
|
||||
const response = await fetch(`${envConfig.baseURL}/auth/refresh`, {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
### 5. 完善监管任务结项管理 `src/views/CompletedTaskManagement.vue`
|
||||
|
||||
#### 5.1 获取任务列表 `fetchTaskList`
|
||||
|
||||
**修改前:** 使用模拟数据
|
||||
|
||||
**修改后:** 调用真实API
|
||||
```javascript
|
||||
const fetchTaskList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = {
|
||||
page: pagination.current,
|
||||
limit: pagination.pageSize,
|
||||
taskStatus: 'completed' // 只获取已完成的任务
|
||||
}
|
||||
|
||||
// 添加搜索条件
|
||||
if (searchForm.taskName) {
|
||||
params.taskName = searchForm.taskName
|
||||
}
|
||||
if (searchForm.status) {
|
||||
params.status = searchForm.status
|
||||
}
|
||||
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
|
||||
params.startDate = searchForm.dateRange[0].format('YYYY-MM-DD')
|
||||
params.endDate = searchForm.dateRange[1].format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
const response = await supervisionTaskApi.getList(params)
|
||||
// 处理响应...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 5.2 获取统计数据 `fetchStats`
|
||||
|
||||
新增函数,调用统计API获取实时数据:
|
||||
```javascript
|
||||
const fetchStats = async () => {
|
||||
try {
|
||||
const response = await supervisionTaskApi.getStats()
|
||||
if (response.data && response.data.status === 'success') {
|
||||
const statsData = response.data.data
|
||||
stats.total = statsData.total || 0
|
||||
stats.thisMonth = statsData.thisMonth || 0
|
||||
stats.archived = statsData.archived || 0
|
||||
stats.avgDuration = statsData.avgDuration || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 5.3 下载报告 `handleDownload`
|
||||
|
||||
**修改前:** 仅显示提示信息
|
||||
|
||||
**修改后:** 调用API下载报告文件
|
||||
```javascript
|
||||
const handleDownload = async (record) => {
|
||||
try {
|
||||
message.loading(`正在下载 ${record.taskName} 的报告...`, 0)
|
||||
|
||||
const response = await supervisionTaskApi.downloadReport(record.id)
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `${record.taskName}_报告_${new Date().getTime()}.pdf`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.destroy()
|
||||
message.success('报告下载成功')
|
||||
} catch (error) {
|
||||
message.destroy()
|
||||
console.error('下载报告失败:', error)
|
||||
message.error('下载报告失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 5.4 归档任务 `handleArchive`
|
||||
|
||||
**修改前:** 仅显示成功提示
|
||||
|
||||
**修改后:** 调用API执行归档操作
|
||||
```javascript
|
||||
const handleArchive = async (record) => {
|
||||
try {
|
||||
const response = await supervisionTaskApi.archive(record.id)
|
||||
if (response.data && response.data.status === 'success') {
|
||||
message.success(`任务 ${record.taskName} 已归档`)
|
||||
fetchTaskList()
|
||||
} else {
|
||||
message.error(response.data?.message || '归档失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('归档任务失败:', error)
|
||||
message.error('归档任务失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 修复后的优势
|
||||
|
||||
1. **统一配置管理**:所有API URL通过env.js统一管理,便于维护和切换环境
|
||||
2. **代码规范**:所有API调用都使用封装的工具类,符合项目规范
|
||||
3. **功能完整**:实现了真实的数据获取、统计、导出、归档、下载报告等功能
|
||||
4. **错误处理**:完善的错误提示和异常处理
|
||||
5. **用户体验**:添加了加载提示和操作反馈
|
||||
|
||||
## 后端需要实现的接口
|
||||
|
||||
为了使前端功能完整可用,后端需要实现以下接口:
|
||||
|
||||
### 1. 获取监管任务列表(支持已完成任务筛选)
|
||||
- **路径**:`GET /supervision-tasks`
|
||||
- **参数**:
|
||||
- `page`: 页码
|
||||
- `limit`: 每页数量
|
||||
- `taskStatus`: 任务状态(completed)
|
||||
- `taskName`: 任务名称(可选)
|
||||
- `status`: 状态(completed/archived)(可选)
|
||||
- `startDate`: 开始日期(可选)
|
||||
- `endDate`: 结束日期(可选)
|
||||
|
||||
### 2. 获取统计数据
|
||||
- **路径**:`GET /supervision-tasks/stats`
|
||||
- **返回数据**:
|
||||
```javascript
|
||||
{
|
||||
total: 总结项任务数,
|
||||
thisMonth: 本月结项数,
|
||||
archived: 已归档任务数,
|
||||
avgDuration: 平均处理时长(天)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 归档任务
|
||||
- **路径**:`PATCH /supervision-tasks/:id/archive`
|
||||
- **说明**:将已完成的任务标记为已归档
|
||||
|
||||
### 4. 下载任务报告
|
||||
- **路径**:`GET /supervision-tasks/:id/report`
|
||||
- **返回**:PDF文件流
|
||||
- **说明**:生成并下载任务报告
|
||||
|
||||
### 5. 导出任务列表
|
||||
- **路径**:`GET /supervision-tasks/export`
|
||||
- **参数**:与获取列表接口相同的筛选参数
|
||||
- **返回**:Excel文件流
|
||||
|
||||
## 测试建议
|
||||
|
||||
1. 测试获取已完成任务列表功能
|
||||
2. 测试搜索和筛选功能
|
||||
3. 测试统计数据显示
|
||||
4. 测试任务归档功能
|
||||
5. 测试报告下载功能
|
||||
6. 测试导出Excel功能
|
||||
7. 测试不同环境下的API调用(开发、测试、生产)
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `insurance_admin-system/src/config/env.js` - 环境配置文件(新增)
|
||||
- `insurance_admin-system/src/utils/api.js` - API工具类(修改)
|
||||
- `insurance_admin-system/src/utils/request.js` - 请求工具(修改)
|
||||
- `insurance_admin-system/src/stores/user.js` - 用户状态管理(修改)
|
||||
- `insurance_admin-system/src/views/CompletedTaskManagement.vue` - 结项管理页面(修改)
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 所有fetch调用都已改为使用封装的API工具类
|
||||
2. 环境配置文件支持多环境切换,部署时需要根据实际情况配置
|
||||
3. 请确保后端API接口已实现并测试通过
|
||||
4. 导出和下载功能需要后端返回正确的文件流和Content-Disposition头
|
||||
|
||||
156
insurance_admin-system/src/config/env.js
Normal file
156
insurance_admin-system/src/config/env.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* 环境配置文件
|
||||
* 统一管理所有API URL和环境变量
|
||||
*/
|
||||
|
||||
// 获取环境变量
|
||||
const env = import.meta.env.MODE || 'development'
|
||||
|
||||
// 环境配置
|
||||
const envConfig = {
|
||||
development: {
|
||||
// 开发环境配置
|
||||
baseURL: '/insurance/api',
|
||||
timeout: 30000,
|
||||
// 其他开发环境配置
|
||||
wsURL: 'ws://localhost:3000',
|
||||
},
|
||||
production: {
|
||||
// 生产环境配置
|
||||
baseURL: '/insurance/api',
|
||||
timeout: 10000,
|
||||
// 其他生产环境配置
|
||||
wsURL: 'wss://production-domain.com',
|
||||
},
|
||||
test: {
|
||||
// 测试环境配置
|
||||
baseURL: '/insurance/api',
|
||||
timeout: 15000,
|
||||
// 其他测试环境配置
|
||||
wsURL: 'ws://test-server:3000',
|
||||
}
|
||||
}
|
||||
|
||||
// 导出当前环境配置
|
||||
export default envConfig[env] || envConfig.development
|
||||
|
||||
// 导出特定配置项,方便按需引入
|
||||
export const { baseURL, timeout, wsURL } = envConfig[env] || envConfig.development
|
||||
|
||||
// API端点配置
|
||||
export const API_ENDPOINTS = {
|
||||
// 认证相关
|
||||
AUTH: {
|
||||
LOGIN: '/auth/login',
|
||||
LOGOUT: '/auth/logout',
|
||||
REFRESH: '/auth/refresh',
|
||||
PROFILE: '/users/profile'
|
||||
},
|
||||
|
||||
// 用户管理
|
||||
USERS: {
|
||||
LIST: '/users',
|
||||
CREATE: '/users',
|
||||
UPDATE: (id) => `/users/${id}`,
|
||||
DELETE: (id) => `/users/${id}`,
|
||||
EXPORT: '/users/export'
|
||||
},
|
||||
|
||||
// 保险类型管理
|
||||
INSURANCE_TYPES: {
|
||||
LIST: '/insurance-types',
|
||||
CREATE: '/insurance-types',
|
||||
UPDATE: (id) => `/insurance-types/${id}`,
|
||||
DELETE: (id) => `/insurance-types/${id}`,
|
||||
EXPORT: '/insurance-types/export'
|
||||
},
|
||||
|
||||
// 申请管理
|
||||
APPLICATIONS: {
|
||||
LIST: '/insurance/applications',
|
||||
DETAIL: (id) => `/insurance/applications/${id}`,
|
||||
CREATE: '/insurance/applications',
|
||||
UPDATE: (id) => `/insurance/applications/${id}`,
|
||||
REVIEW: (id) => `/insurance/applications/${id}/review`,
|
||||
EXPORT: '/insurance/applications/export'
|
||||
},
|
||||
|
||||
// 保单管理
|
||||
POLICIES: {
|
||||
LIST: '/policies',
|
||||
DETAIL: (id) => `/policies/${id}`,
|
||||
CREATE: '/policies',
|
||||
UPDATE: (id) => `/policies/${id}`,
|
||||
EXPORT: '/policies/export'
|
||||
},
|
||||
|
||||
// 理赔管理
|
||||
CLAIMS: {
|
||||
LIST: '/claims',
|
||||
DETAIL: (id) => `/claims/${id}`,
|
||||
UPDATE_STATUS: (id) => `/claims/${id}/status`,
|
||||
EXPORT: '/claims/export'
|
||||
},
|
||||
|
||||
// 设备预警
|
||||
DEVICE_ALERTS: {
|
||||
LIST: '/device-alerts',
|
||||
DETAIL: (id) => `/device-alerts/${id}`,
|
||||
STATS: '/device-alerts/stats',
|
||||
MARK_READ: (id) => `/device-alerts/${id}/read`,
|
||||
EXPORT: '/device-alerts/export'
|
||||
},
|
||||
|
||||
// 监管任务
|
||||
SUPERVISION_TASKS: {
|
||||
LIST: '/supervision-tasks',
|
||||
DETAIL: (id) => `/supervision-tasks/${id}`,
|
||||
CREATE: '/supervision-tasks',
|
||||
UPDATE: (id) => `/supervision-tasks/${id}`,
|
||||
DELETE: (id) => `/supervision-tasks/${id}`,
|
||||
STATS: '/supervision-tasks/stats',
|
||||
EXPORT: '/supervision-tasks/export',
|
||||
ARCHIVE: (id) => `/supervision-tasks/${id}/archive`,
|
||||
DOWNLOAD_REPORT: (id) => `/supervision-tasks/${id}/report`
|
||||
},
|
||||
|
||||
// 待安装任务
|
||||
INSTALLATION_TASKS: {
|
||||
LIST: '/installation-tasks',
|
||||
DETAIL: (id) => `/installation-tasks/${id}`,
|
||||
CREATE: '/installation-tasks',
|
||||
UPDATE: (id) => `/installation-tasks/${id}`,
|
||||
DELETE: (id) => `/installation-tasks/${id}`,
|
||||
STATS: '/installation-tasks/stats',
|
||||
EXPORT: '/installation-tasks/export'
|
||||
},
|
||||
|
||||
// 生资保险
|
||||
LIVESTOCK: {
|
||||
TYPES: '/livestock-types',
|
||||
POLICIES: '/livestock-policies',
|
||||
CLAIMS: '/livestock-claims'
|
||||
},
|
||||
|
||||
// 操作日志
|
||||
OPERATION_LOGS: {
|
||||
LIST: '/operation-logs',
|
||||
STATS: '/operation-logs/stats',
|
||||
EXPORT: '/operation-logs/export'
|
||||
},
|
||||
|
||||
// 权限管理
|
||||
PERMISSIONS: {
|
||||
LIST: '/permissions',
|
||||
TREE: '/permissions/tree'
|
||||
},
|
||||
|
||||
// 角色权限
|
||||
ROLE_PERMISSIONS: {
|
||||
ROLES: '/role-permissions/roles',
|
||||
PERMISSIONS: '/role-permissions/permissions',
|
||||
ASSIGN: (roleId) => `/role-permissions/${roleId}/assign`,
|
||||
COPY: '/role-permissions/copy'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import RangePickerTest from '@/views/RangePickerTest.vue'
|
||||
import LoginTest from '@/views/LoginTest.vue'
|
||||
import LivestockPolicyManagement from '@/views/LivestockPolicyManagement.vue'
|
||||
import SystemSettings from '@/views/SystemSettings.vue'
|
||||
import TokenDebug from '@/views/TokenDebug.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -167,7 +166,7 @@ const routes = [
|
||||
{
|
||||
path: 'token-debug',
|
||||
name: 'TokenDebug',
|
||||
component: TokenDebug,
|
||||
component: () => import('@/views/TokenDebug.vue'),
|
||||
meta: { title: 'Token调试' }
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import envConfig from '@/config/env'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
// 状态
|
||||
@@ -102,7 +103,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/insurance/api/auth/refresh', {
|
||||
const response = await fetch(`${envConfig.baseURL}/auth/refresh`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -20,7 +20,8 @@ export const userAPI = {
|
||||
changePassword: (data) => api.put('/users/change-password', data),
|
||||
uploadAvatar: (formData) => api.post('/users/avatar', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
}),
|
||||
export: (params) => api.get('/users/export', { params, responseType: 'blob' })
|
||||
};
|
||||
|
||||
export const menuAPI = {
|
||||
@@ -39,7 +40,8 @@ export const insuranceTypeAPI = {
|
||||
create: (data) => api.post('/insurance-types', data),
|
||||
update: (id, data) => api.put(`/insurance-types/${id}`, data),
|
||||
delete: (id) => api.delete(`/insurance-types/${id}`),
|
||||
updateStatus: (id, data) => api.patch(`/insurance-types/${id}/status`, data)
|
||||
updateStatus: (id, data) => api.patch(`/insurance-types/${id}/status`, data),
|
||||
export: (params) => api.get('/insurance-types/export', { params, responseType: 'blob' })
|
||||
}
|
||||
|
||||
export const applicationAPI = {
|
||||
@@ -61,14 +63,16 @@ export const policyAPI = {
|
||||
create: (data) => api.post('/policies', data),
|
||||
update: (id, data) => api.put(`/policies/${id}`, data),
|
||||
updateStatus: (id, data) => api.put(`/policies/${id}/status`, data),
|
||||
delete: (id) => api.delete(`/policies/${id}`)
|
||||
delete: (id) => api.delete(`/policies/${id}`),
|
||||
export: (params) => api.get('/policies/export', { params, responseType: 'blob' })
|
||||
}
|
||||
|
||||
export const claimAPI = {
|
||||
getList: (params) => api.get('/claims', { params }),
|
||||
getDetail: (id) => api.get(`/claims/${id}`),
|
||||
updateStatus: (id, data) => api.put(`/claims/${id}/status`, data),
|
||||
delete: (id) => api.delete(`/claims/${id}`)
|
||||
delete: (id) => api.delete(`/claims/${id}`),
|
||||
export: (params) => api.get('/claims/export', { params, responseType: 'blob' })
|
||||
}
|
||||
|
||||
export const dashboardAPI = {
|
||||
@@ -84,7 +88,8 @@ export const deviceAlertAPI = {
|
||||
getDetail: (id) => api.get(`/device-alerts/${id}`),
|
||||
markAsRead: (id) => api.patch(`/device-alerts/${id}/read`),
|
||||
markAllAsRead: () => api.patch('/device-alerts/read-all'),
|
||||
handle: (id, data) => api.patch(`/device-alerts/${id}/handle`, data)
|
||||
handle: (id, data) => api.patch(`/device-alerts/${id}/handle`, data),
|
||||
export: (params) => api.get('/device-alerts/export', { params, responseType: 'blob' })
|
||||
}
|
||||
|
||||
// 数据览仓API
|
||||
@@ -104,7 +109,10 @@ export const supervisionTaskApi = {
|
||||
delete: (id) => api.delete(`/supervision-tasks/${id}`),
|
||||
getDetail: (id) => api.get(`/supervision-tasks/${id}`),
|
||||
batchOperate: (data) => api.post('/supervision-tasks/batch/operate', data),
|
||||
getStats: () => api.get('/supervision-tasks/stats')
|
||||
getStats: () => api.get('/supervision-tasks/stats'),
|
||||
export: (params) => api.get('/supervision-tasks/export', { params, responseType: 'blob' }),
|
||||
archive: (id) => api.patch(`/supervision-tasks/${id}/archive`),
|
||||
downloadReport: (id) => api.get(`/supervision-tasks/${id}/report`, { responseType: 'blob' })
|
||||
}
|
||||
|
||||
// 待安装任务API
|
||||
@@ -118,7 +126,11 @@ export const installationTaskApi = {
|
||||
getStats: () => api.get('/installation-tasks/stats'),
|
||||
assign: (id, data) => api.post(`/installation-tasks/${id}/assign`, data),
|
||||
complete: (id, data) => api.post(`/installation-tasks/${id}/complete`, data),
|
||||
getHistory: (id) => api.get(`/installation-tasks/${id}/history`)
|
||||
getHistory: (id) => api.get(`/installation-tasks/${id}/history`),
|
||||
export: (params) => api.get('/installation-tasks/export', { params, responseType: 'blob' }),
|
||||
batchDelete: (ids) => api.post('/installation-tasks/batch-delete', { ids }),
|
||||
batchUpdateStatus: (data) => api.post('/installation-tasks/batch-update-status', data),
|
||||
getDetail: (id) => api.get(`/installation-tasks/${id}`)
|
||||
}
|
||||
|
||||
// 生资保险相关API
|
||||
@@ -140,7 +152,8 @@ export const livestockPolicyApi = {
|
||||
getById: (id) => api.get(`/livestock-policies/${id}`),
|
||||
updateStatus: (id, data) => api.patch(`/livestock-policies/${id}/status`, data),
|
||||
getStats: () => api.get('/livestock-policies/stats'),
|
||||
getLivestockTypes: () => api.get('/livestock-types/active')
|
||||
getLivestockTypes: () => api.get('/livestock-types/active'),
|
||||
export: (params) => api.get('/livestock-policies/export', { params, responseType: 'blob' })
|
||||
}
|
||||
|
||||
export const livestockClaimApi = {
|
||||
@@ -152,7 +165,8 @@ export const livestockClaimApi = {
|
||||
approve: (id, data) => api.post(`/livestock-claims/${id}/approve`, data),
|
||||
reject: (id, data) => api.post(`/livestock-claims/${id}/reject`, data),
|
||||
updatePaymentStatus: (id, data) => api.patch(`/livestock-claims/${id}/payment`, data),
|
||||
getStats: () => api.get('/livestock-claims/stats')
|
||||
getStats: () => api.get('/livestock-claims/stats'),
|
||||
export: (params) => api.get('/livestock-claims/export', { params, responseType: 'blob' })
|
||||
}
|
||||
|
||||
// 操作日志API
|
||||
|
||||
@@ -14,7 +14,7 @@ export default {
|
||||
|
||||
// 更新安装任务
|
||||
updateInstallationTask: (id, data) => {
|
||||
return installationTaskApi.update({ ...data, id })
|
||||
return installationTaskApi.update(id, data)
|
||||
},
|
||||
|
||||
// 删除安装任务
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import router from '@/router'
|
||||
import envConfig from '@/config/env'
|
||||
|
||||
// API基础配置
|
||||
const API_CONFIG = {
|
||||
baseURL: '/insurance/api',
|
||||
timeout: 10000
|
||||
baseURL: envConfig.baseURL,
|
||||
timeout: envConfig.timeout
|
||||
}
|
||||
|
||||
// 是否正在刷新token的标志
|
||||
@@ -85,7 +86,11 @@ const handleResponse = async (response) => {
|
||||
|
||||
try {
|
||||
const contentType = response.headers.get('content-type')
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
|
||||
// 处理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()
|
||||
|
||||
@@ -640,7 +640,7 @@ const exportData = async () => {
|
||||
exportLoading.value = true
|
||||
try {
|
||||
const response = await applicationAPI.export(searchForm)
|
||||
const url = window.URL.createObjectURL(new Blob([response.data]))
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `申请数据_${new Date().toISOString().slice(0, 10)}.xlsx`)
|
||||
|
||||
@@ -5,10 +5,16 @@
|
||||
sub-title="管理系统所有理赔申请"
|
||||
>
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="showModal">
|
||||
<plus-outlined />
|
||||
新增理赔
|
||||
</a-button>
|
||||
<a-space>
|
||||
<a-button @click="handleExport">
|
||||
<download-outlined />
|
||||
导出Excel
|
||||
</a-button>
|
||||
<a-button type="primary" @click="showModal">
|
||||
<plus-outlined />
|
||||
新增理赔
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-page-header>
|
||||
|
||||
@@ -445,7 +451,8 @@ import {
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
RedoOutlined,
|
||||
FileTextOutlined
|
||||
FileTextOutlined,
|
||||
DownloadOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { livestockClaimApi } from '@/utils/api'
|
||||
import dayjs from 'dayjs'
|
||||
@@ -967,6 +974,42 @@ const handleDelete = async (id) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
search: searchForm.search || undefined,
|
||||
status: searchForm.status || undefined
|
||||
}
|
||||
|
||||
// 过滤掉undefined值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
const response = await livestockClaimApi.export(params)
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `理赔列表_${new Date().getTime()}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadClaims()
|
||||
})
|
||||
|
||||
@@ -39,6 +39,10 @@
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-space>
|
||||
<a-button @click="handleExport">
|
||||
<DownloadOutlined />
|
||||
导出Excel
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<SearchOutlined />
|
||||
搜索
|
||||
@@ -233,6 +237,7 @@ import {
|
||||
ExportOutlined,
|
||||
DownOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { supervisionTaskApi } from '@/utils/api'
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
@@ -370,6 +375,42 @@ const handleReset = () => {
|
||||
fetchTaskList()
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
taskName: searchForm.taskName || undefined,
|
||||
status: searchForm.status || undefined
|
||||
}
|
||||
|
||||
// 过滤掉undefined值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
const response = await supervisionTaskApi.export(params)
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `监管任务结项列表_${new Date().getTime()}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 表格变化处理
|
||||
const handleTableChange = (pag) => {
|
||||
pagination.current = pag.current
|
||||
@@ -384,76 +425,113 @@ const handleView = (record) => {
|
||||
}
|
||||
|
||||
// 下载报告
|
||||
const handleDownload = (record) => {
|
||||
message.info(`正在下载 ${record.taskName} 的报告...`)
|
||||
// 这里实现下载逻辑
|
||||
const handleDownload = async (record) => {
|
||||
try {
|
||||
message.loading(`正在下载 ${record.taskName} 的报告...`, 0)
|
||||
|
||||
const response = await supervisionTaskApi.downloadReport(record.id)
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `${record.taskName}_报告_${new Date().getTime()}.pdf`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.destroy()
|
||||
message.success('报告下载成功')
|
||||
} catch (error) {
|
||||
message.destroy()
|
||||
console.error('下载报告失败:', error)
|
||||
message.error('下载报告失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 归档任务
|
||||
const handleArchive = (record) => {
|
||||
message.success(`任务 ${record.taskName} 已归档`)
|
||||
fetchTaskList()
|
||||
const handleArchive = async (record) => {
|
||||
try {
|
||||
const response = await supervisionTaskApi.archive(record.id)
|
||||
if (response.data && response.data.status === 'success') {
|
||||
message.success(`任务 ${record.taskName} 已归档`)
|
||||
fetchTaskList()
|
||||
} else {
|
||||
message.error(response.data?.message || '归档失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('归档任务失败:', error)
|
||||
message.error('归档任务失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 导出任务
|
||||
const handleExport = (record) => {
|
||||
message.info(`正在导出 ${record.taskName} 的数据...`)
|
||||
// 这里实现导出逻辑
|
||||
}
|
||||
// 导出单个任务(此功能已合并到handleExport)
|
||||
// const handleExportSingle = (record) => {
|
||||
// message.info(`正在导出 ${record.taskName} 的数据...`)
|
||||
// }
|
||||
|
||||
// 获取任务列表
|
||||
const fetchTaskList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟API调用
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
const params = {
|
||||
page: pagination.current,
|
||||
limit: pagination.pageSize,
|
||||
taskStatus: 'completed' // 只获取已完成的任务
|
||||
}
|
||||
|
||||
// 模拟数据
|
||||
const mockData = [
|
||||
{
|
||||
id: 1,
|
||||
taskCode: 'RT001',
|
||||
taskName: '农场设备安全检查',
|
||||
priority: 'high',
|
||||
status: 'completed',
|
||||
assignee: '张三',
|
||||
createdAt: '2024-01-15',
|
||||
completedAt: '2024-01-20',
|
||||
duration: 5,
|
||||
description: '对农场所有设备进行安全检查,确保设备正常运行',
|
||||
completionNotes: '检查完成,发现3处需要维修的设备,已安排维修'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
taskCode: 'RT002',
|
||||
taskName: '环境监测数据审核',
|
||||
priority: 'medium',
|
||||
status: 'archived',
|
||||
assignee: '李四',
|
||||
createdAt: '2024-01-10',
|
||||
completedAt: '2024-01-18',
|
||||
duration: 8,
|
||||
description: '审核上月环境监测数据,确保数据准确性',
|
||||
completionNotes: '数据审核完成,所有数据符合标准'
|
||||
}
|
||||
]
|
||||
// 添加搜索条件
|
||||
if (searchForm.taskName) {
|
||||
params.taskName = searchForm.taskName
|
||||
}
|
||||
if (searchForm.status) {
|
||||
params.status = searchForm.status
|
||||
}
|
||||
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
|
||||
params.startDate = searchForm.dateRange[0].format('YYYY-MM-DD')
|
||||
params.endDate = searchForm.dateRange[1].format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
taskList.value = mockData
|
||||
pagination.total = mockData.length
|
||||
const response = await supervisionTaskApi.getList(params)
|
||||
console.log('监管任务结项API响应:', response)
|
||||
|
||||
// 更新统计数据
|
||||
stats.total = 156
|
||||
stats.thisMonth = 23
|
||||
stats.archived = 89
|
||||
stats.avgDuration = 6.5
|
||||
if (response.data && response.data.status === 'success') {
|
||||
taskList.value = response.data.data.list || []
|
||||
pagination.total = response.data.data.total || 0
|
||||
console.log('监管任务结项数据设置成功:', taskList.value.length, '条')
|
||||
} else {
|
||||
console.log('监管任务结项响应格式错误:', response)
|
||||
message.error(response.data?.message || '获取任务列表失败')
|
||||
}
|
||||
|
||||
// 获取统计数据
|
||||
await fetchStats()
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取任务列表失败:', error)
|
||||
message.error('获取任务列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取统计数据
|
||||
const fetchStats = async () => {
|
||||
try {
|
||||
const response = await supervisionTaskApi.getStats()
|
||||
if (response.data && response.data.status === 'success') {
|
||||
const statsData = response.data.data
|
||||
stats.total = statsData.total || 0
|
||||
stats.thisMonth = statsData.thisMonth || 0
|
||||
stats.archived = statsData.archived || 0
|
||||
stats.avgDuration = statsData.avgDuration || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据
|
||||
onMounted(() => {
|
||||
fetchTaskList()
|
||||
|
||||
@@ -110,11 +110,147 @@
|
||||
<div class="pagination-info">
|
||||
<span>共 {{ pagination.total }} 条记录</span>
|
||||
</div>
|
||||
|
||||
<!-- 查看/编辑安装任务弹窗 -->
|
||||
<a-modal
|
||||
:title="modalTitle"
|
||||
:open="modalVisible"
|
||||
:width="900"
|
||||
@ok="handleModalOk"
|
||||
@cancel="handleModalCancel"
|
||||
:confirmLoading="modalLoading"
|
||||
:footer="isViewMode ? null : undefined"
|
||||
>
|
||||
<a-form
|
||||
ref="modalFormRef"
|
||||
:model="modalForm"
|
||||
:rules="modalRules"
|
||||
layout="vertical"
|
||||
:disabled="isViewMode"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="申请单号" name="applicationNumber">
|
||||
<a-input v-model:value="modalForm.applicationNumber" placeholder="请输入申请单号" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保单编号" name="policyNumber">
|
||||
<a-input v-model:value="modalForm.policyNumber" placeholder="请输入保单编号" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="产品名称" name="productName">
|
||||
<a-input v-model:value="modalForm.productName" placeholder="请输入产品名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="客户姓名" name="customerName">
|
||||
<a-input v-model:value="modalForm.customerName" placeholder="请输入客户姓名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="证件类型" name="idType">
|
||||
<a-select v-model:value="modalForm.idType" placeholder="请选择证件类型">
|
||||
<a-select-option value="身份证">身份证</a-select-option>
|
||||
<a-select-option value="护照">护照</a-select-option>
|
||||
<a-select-option value="军官证">军官证</a-select-option>
|
||||
<a-select-option value="其他">其他</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="证件号码" name="idNumber">
|
||||
<a-input v-model:value="modalForm.idNumber" placeholder="请输入证件号码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="养殖生资种类" name="livestockSupplyType">
|
||||
<a-input v-model:value="modalForm.livestockSupplyType" placeholder="请输入养殖生资种类" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="待安装设备" name="pendingDevices">
|
||||
<a-input v-model:value="modalForm.pendingDevices" placeholder="请输入待安装设备" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="安装状态" name="installationStatus">
|
||||
<a-select v-model:value="modalForm.installationStatus" placeholder="请选择安装状态">
|
||||
<a-select-option value="待安装">待安装</a-select-option>
|
||||
<a-select-option value="安装中">安装中</a-select-option>
|
||||
<a-select-option value="已安装">已安装</a-select-option>
|
||||
<a-select-option value="安装失败">安装失败</a-select-option>
|
||||
<a-select-option value="已取消">已取消</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="生成安装任务时间" name="taskGeneratedTime">
|
||||
<a-date-picker
|
||||
v-model:value="modalForm.taskGeneratedTime"
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择生成任务时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="安装完成生效时间" name="installationCompletedTime">
|
||||
<a-date-picker
|
||||
v-model:value="modalForm.installationCompletedTime"
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择完成时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="安装人员" name="installer">
|
||||
<a-input v-model:value="modalForm.installer" placeholder="请输入安装人员" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item label="备注" name="remarks">
|
||||
<a-textarea
|
||||
v-model:value="modalForm.remarks"
|
||||
placeholder="请输入备注信息"
|
||||
:rows="3"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 查看模式下显示操作按钮 -->
|
||||
<div v-if="isViewMode" style="text-align: right; margin-top: 16px;">
|
||||
<a-space>
|
||||
<a-button @click="handleModalCancel">关闭</a-button>
|
||||
<a-button type="primary" @click="switchToEditMode">编辑</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ref, reactive, computed, onMounted } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||
import dayjs from 'dayjs';
|
||||
@@ -123,6 +259,11 @@ import installationTaskApi from '@/utils/installationTaskApi';
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const tableData = ref([]);
|
||||
const modalVisible = ref(false);
|
||||
const modalLoading = ref(false);
|
||||
const modalFormRef = ref();
|
||||
const editingId = ref(null);
|
||||
const isViewMode = ref(false);
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
@@ -141,6 +282,48 @@ const pagination = reactive({
|
||||
showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,共 ${total} 条`
|
||||
});
|
||||
|
||||
// 弹窗表单
|
||||
const modalForm = reactive({
|
||||
applicationNumber: '',
|
||||
policyNumber: '',
|
||||
productName: '',
|
||||
customerName: '',
|
||||
idType: '',
|
||||
idNumber: '',
|
||||
livestockSupplyType: '',
|
||||
pendingDevices: '',
|
||||
installationStatus: '',
|
||||
taskGeneratedTime: null,
|
||||
installationCompletedTime: null,
|
||||
installer: '',
|
||||
remarks: ''
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const modalRules = {
|
||||
applicationNumber: [
|
||||
{ required: true, message: '请输入申请单号', trigger: 'blur' }
|
||||
],
|
||||
policyNumber: [
|
||||
{ required: true, message: '请输入保单编号', trigger: 'blur' }
|
||||
],
|
||||
productName: [
|
||||
{ required: true, message: '请输入产品名称', trigger: 'blur' }
|
||||
],
|
||||
customerName: [
|
||||
{ required: true, message: '请输入客户姓名', trigger: 'blur' }
|
||||
],
|
||||
installationStatus: [
|
||||
{ required: true, message: '请选择安装状态', trigger: 'change' }
|
||||
]
|
||||
};
|
||||
|
||||
// 计算属性
|
||||
const modalTitle = computed(() => {
|
||||
if (isViewMode.value) return '查看安装任务';
|
||||
return editingId.value ? '编辑安装任务' : '新增安装任务';
|
||||
});
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
@@ -279,12 +462,106 @@ const handleTableChange = (paginationInfo) => {
|
||||
|
||||
const handleView = (record) => {
|
||||
console.log('查看记录:', record);
|
||||
message.info('查看功能开发中');
|
||||
isViewMode.value = true;
|
||||
editingId.value = record.id;
|
||||
fillModalForm(record);
|
||||
modalVisible.value = true;
|
||||
};
|
||||
|
||||
const handleEdit = (record) => {
|
||||
console.log('编辑记录:', record);
|
||||
message.info('编辑功能开发中');
|
||||
isViewMode.value = false;
|
||||
editingId.value = record.id;
|
||||
fillModalForm(record);
|
||||
modalVisible.value = true;
|
||||
};
|
||||
|
||||
const fillModalForm = (record) => {
|
||||
Object.assign(modalForm, {
|
||||
applicationNumber: record.applicationNumber || '',
|
||||
policyNumber: record.policyNumber || '',
|
||||
productName: record.productName || '',
|
||||
customerName: record.customerName || '',
|
||||
idType: record.idType || '',
|
||||
idNumber: record.idNumber || '',
|
||||
livestockSupplyType: record.livestockSupplyType || '',
|
||||
pendingDevices: record.pendingDevices || '',
|
||||
installationStatus: record.installationStatus || '',
|
||||
taskGeneratedTime: record.taskGeneratedTime ? dayjs(record.taskGeneratedTime) : null,
|
||||
installationCompletedTime: record.installationCompletedTime ? dayjs(record.installationCompletedTime) : null,
|
||||
installer: record.installer || '',
|
||||
remarks: record.remarks || ''
|
||||
});
|
||||
};
|
||||
|
||||
const resetModalForm = () => {
|
||||
Object.assign(modalForm, {
|
||||
applicationNumber: '',
|
||||
policyNumber: '',
|
||||
productName: '',
|
||||
customerName: '',
|
||||
idType: '',
|
||||
idNumber: '',
|
||||
livestockSupplyType: '',
|
||||
pendingDevices: '',
|
||||
installationStatus: '',
|
||||
taskGeneratedTime: null,
|
||||
installationCompletedTime: null,
|
||||
installer: '',
|
||||
remarks: ''
|
||||
});
|
||||
};
|
||||
|
||||
const switchToEditMode = () => {
|
||||
isViewMode.value = false;
|
||||
};
|
||||
|
||||
const handleModalOk = async () => {
|
||||
if (isViewMode.value) {
|
||||
modalVisible.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await modalFormRef.value.validate();
|
||||
modalLoading.value = true;
|
||||
|
||||
const submitData = {
|
||||
...modalForm,
|
||||
taskGeneratedTime: modalForm.taskGeneratedTime ?
|
||||
dayjs(modalForm.taskGeneratedTime).format('YYYY-MM-DD HH:mm:ss') : null,
|
||||
installationCompletedTime: modalForm.installationCompletedTime ?
|
||||
dayjs(modalForm.installationCompletedTime).format('YYYY-MM-DD HH:mm:ss') : null
|
||||
};
|
||||
|
||||
const response = editingId.value
|
||||
? await installationTaskApi.updateInstallationTask(editingId.value, submitData)
|
||||
: await installationTaskApi.createInstallationTask(submitData);
|
||||
|
||||
if (response.code === 200 || response.code === 201) {
|
||||
message.success(editingId.value ? '更新成功' : '创建成功');
|
||||
modalVisible.value = false;
|
||||
fetchInstallationTasks();
|
||||
} else {
|
||||
message.error(response.message || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
if (error.errorFields) {
|
||||
message.error('请完善表单信息');
|
||||
} else {
|
||||
message.error('操作失败');
|
||||
}
|
||||
} finally {
|
||||
modalLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleModalCancel = () => {
|
||||
modalVisible.value = false;
|
||||
isViewMode.value = false;
|
||||
editingId.value = null;
|
||||
resetModalForm();
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
@@ -306,16 +583,15 @@ const handleExportTasks = async () => {
|
||||
try {
|
||||
const response = await installationTaskApi.exportInstallationTasks(searchForm);
|
||||
|
||||
if (response) {
|
||||
// 处理文件下载
|
||||
const blob = new Blob([response], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
});
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
if (response && response.data) {
|
||||
// 处理文件下载 - response.data是Blob对象
|
||||
const url = window.URL.createObjectURL(response.data);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `安装任务导出_${dayjs().format('YYYY-MM-DD_HH-mm-ss')}.xlsx`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
message.success('导出成功');
|
||||
@@ -324,7 +600,7 @@ const handleExportTasks = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error);
|
||||
message.error('导出失败');
|
||||
message.error('导出失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -446,4 +722,22 @@ const getInstallationStatusColor = (status) => {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 查看模式下表单样式优化 */
|
||||
:deep(.ant-form-item-control-input-content) {
|
||||
.ant-input[disabled],
|
||||
.ant-select-disabled .ant-select-selector,
|
||||
.ant-picker-disabled,
|
||||
.ant-input-number-disabled {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
background-color: #fafafa;
|
||||
border-color: #d9d9d9;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ant-input-disabled,
|
||||
.ant-select-disabled .ant-select-selection-item {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -5,10 +5,16 @@
|
||||
sub-title="管理系统支持的保险产品险种"
|
||||
>
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="showModal">
|
||||
<PlusOutlined />
|
||||
新增险种
|
||||
</a-button>
|
||||
<a-space>
|
||||
<a-button @click="handleExport">
|
||||
<DownloadOutlined />
|
||||
导出Excel
|
||||
</a-button>
|
||||
<a-button type="primary" @click="showModal">
|
||||
<PlusOutlined />
|
||||
新增险种
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-page-header>
|
||||
|
||||
@@ -326,7 +332,8 @@ import { insuranceTypeAPI } from '@/utils/api'
|
||||
import {
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
RedoOutlined
|
||||
RedoOutlined,
|
||||
DownloadOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
|
||||
const loading = ref(false)
|
||||
@@ -1274,6 +1281,35 @@ const handleOnSaleStatusChange = (checked) => {
|
||||
console.log(`险种销售状态${statusText},时间:${new Date().toLocaleString()}`)
|
||||
}
|
||||
|
||||
// 导出Excel
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
...searchForm
|
||||
}
|
||||
|
||||
const response = await insuranceTypeAPI.export(params)
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `保险类型列表_${new Date().getTime()}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 组件卸载状态跟踪
|
||||
const isComponentMounted = ref(true)
|
||||
|
||||
|
||||
@@ -2,10 +2,16 @@
|
||||
<div class="livestock-policy-management">
|
||||
<div class="page-header">
|
||||
<h2>生资保单管理</h2>
|
||||
<a-button type="primary" @click="showCreateModal">
|
||||
<PlusOutlined />
|
||||
新建保单
|
||||
</a-button>
|
||||
<a-space>
|
||||
<a-button @click="handleExport">
|
||||
<DownloadOutlined />
|
||||
导出Excel
|
||||
</a-button>
|
||||
<a-button type="primary" @click="showCreateModal">
|
||||
<PlusOutlined />
|
||||
新建保单
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- 搜索筛选区域 -->
|
||||
@@ -332,7 +338,8 @@ import dayjs from 'dayjs'
|
||||
import {
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
DownOutlined
|
||||
DownOutlined,
|
||||
DownloadOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { livestockPolicyApi, livestockTypeApi } from '@/utils/api'
|
||||
|
||||
@@ -769,6 +776,44 @@ const getCurrentPremiumRate = () => {
|
||||
return '请先选择牲畜类型'
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
policy_no: searchForm.policy_no || undefined,
|
||||
policyholder_name: searchForm.policyholder_name || undefined,
|
||||
policy_status: searchForm.policy_status || undefined,
|
||||
payment_status: searchForm.payment_status || undefined
|
||||
}
|
||||
|
||||
// 过滤掉undefined值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
const response = await livestockPolicyApi.export(params)
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `生资保单列表_${new Date().getTime()}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
|
||||
@@ -117,6 +117,10 @@
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-space>
|
||||
<a-button @click="handleExport">
|
||||
<download-outlined />
|
||||
导出Excel
|
||||
</a-button>
|
||||
<a-button type="primary" @click="markAllAsRead" :disabled="!hasUnreadAlerts">
|
||||
<check-outlined />
|
||||
全部标记已读
|
||||
@@ -322,7 +326,8 @@ import {
|
||||
FireOutlined,
|
||||
CloudOutlined,
|
||||
WifiOutlined,
|
||||
ToolOutlined
|
||||
ToolOutlined,
|
||||
DownloadOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { deviceAlertAPI } from '@/utils/api'
|
||||
import dayjs from 'dayjs'
|
||||
@@ -605,6 +610,42 @@ const refreshData = async () => {
|
||||
message.success('数据已刷新')
|
||||
}
|
||||
|
||||
// 导出Excel
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
...filters
|
||||
}
|
||||
|
||||
// 移除undefined值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
const response = await deviceAlertAPI.export(params)
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `消息通知_${new Date().getTime()}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据
|
||||
onMounted(() => {
|
||||
fetchStats()
|
||||
|
||||
@@ -5,10 +5,16 @@
|
||||
sub-title="管理系统所有保单信息"
|
||||
>
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="showModal">
|
||||
<plus-outlined />
|
||||
新增保单
|
||||
</a-button>
|
||||
<a-space>
|
||||
<a-button @click="handleExport">
|
||||
<download-outlined />
|
||||
导出Excel
|
||||
</a-button>
|
||||
<a-button type="primary" @click="showModal">
|
||||
<plus-outlined />
|
||||
新增保单
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-page-header>
|
||||
|
||||
@@ -322,7 +328,8 @@ import dayjs from 'dayjs'
|
||||
import {
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
RedoOutlined
|
||||
RedoOutlined,
|
||||
DownloadOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { policyAPI } from '@/utils/api'
|
||||
|
||||
@@ -639,6 +646,44 @@ const handleDelete = async (id) => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
policy_number: searchForm.policy_number || undefined,
|
||||
policyholder_name: searchForm.policyholder_name || undefined,
|
||||
insurance_type_id: searchForm.insurance_type_id || undefined,
|
||||
status: searchForm.status || undefined
|
||||
}
|
||||
|
||||
// 过滤掉undefined值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
const response = await policyAPI.export(params)
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `保单列表_${new Date().getTime()}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadPolicies()
|
||||
})
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
<!-- 操作按钮区 -->
|
||||
<div class="action-buttons">
|
||||
<a-space>
|
||||
<a-button @click="handleExport">
|
||||
<DownloadOutlined />
|
||||
导出Excel
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleCreateTask">
|
||||
新增监管任务
|
||||
</a-button>
|
||||
@@ -108,6 +112,69 @@
|
||||
<span>页共 0 条</span>
|
||||
</div>
|
||||
|
||||
<!-- 查看详情弹窗 -->
|
||||
<a-modal
|
||||
title="查看监管任务详情"
|
||||
:open="viewModalVisible"
|
||||
:width="900"
|
||||
@cancel="handleViewModalCancel"
|
||||
:footer="null"
|
||||
>
|
||||
<a-descriptions bordered :column="2" size="middle">
|
||||
<a-descriptions-item label="申请单号">
|
||||
{{ viewRecord.applicationNumber || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="保单编号">
|
||||
{{ viewRecord.policyNumber || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="产品名称">
|
||||
{{ viewRecord.productName || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="保险期间">
|
||||
{{ viewRecord.insurancePeriod || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="客户姓名">
|
||||
{{ viewRecord.customerName || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="证件类型">
|
||||
{{ viewRecord.idType || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="证件号码">
|
||||
{{ viewRecord.idNumber || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="适用金额">
|
||||
{{ viewRecord.applicableAmount ? formatAmount(viewRecord.applicableAmount) : '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="监管生资数量">
|
||||
{{ viewRecord.supervisorySuppliesQuantity || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="任务类型">
|
||||
<a-tag :color="getTaskTypeColor(viewRecord.taskType)">
|
||||
{{ getTaskTypeText(viewRecord.taskType) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="任务状态">
|
||||
<a-tag :color="getStatusColor(viewRecord.taskStatus)">
|
||||
{{ getStatusText(viewRecord.taskStatus) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="优先级">
|
||||
<a-tag :color="getPriorityColor(viewRecord.priority)">
|
||||
{{ getPriorityText(viewRecord.priority) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间" :span="2">
|
||||
{{ viewRecord.createdAt || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="更新时间" :span="2">
|
||||
{{ viewRecord.updatedAt || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="备注" :span="2">
|
||||
{{ viewRecord.remarks || '-' }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-modal>
|
||||
|
||||
<!-- 创建/编辑监管任务弹窗 -->
|
||||
<a-modal
|
||||
:title="modalTitle"
|
||||
@@ -223,13 +290,14 @@
|
||||
<script>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { SearchOutlined } from '@ant-design/icons-vue'
|
||||
import { SearchOutlined, DownloadOutlined } from '@ant-design/icons-vue'
|
||||
import { supervisionTaskApi } from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'SupervisionTaskManagement',
|
||||
components: {
|
||||
SearchOutlined
|
||||
SearchOutlined,
|
||||
DownloadOutlined
|
||||
},
|
||||
setup() {
|
||||
const loading = ref(false)
|
||||
@@ -239,6 +307,8 @@ export default {
|
||||
const modalLoading = ref(false)
|
||||
const modalFormRef = ref()
|
||||
const editingId = ref(null)
|
||||
const viewModalVisible = ref(false)
|
||||
const viewRecord = ref({})
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
@@ -493,7 +563,13 @@ export default {
|
||||
|
||||
const handleView = (record) => {
|
||||
console.log('查看详情:', record)
|
||||
message.info('查看功能开发中...')
|
||||
viewRecord.value = { ...record }
|
||||
viewModalVisible.value = true
|
||||
}
|
||||
|
||||
const handleViewModalCancel = () => {
|
||||
viewModalVisible.value = false
|
||||
viewRecord.value = {}
|
||||
}
|
||||
|
||||
const handleEdit = (record) => {
|
||||
@@ -564,6 +640,42 @@ export default {
|
||||
resetModalForm()
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
policyNumber: searchForm.policyNumber || undefined,
|
||||
customerName: searchForm.customerName || undefined
|
||||
}
|
||||
|
||||
// 过滤掉undefined值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
const response = await supervisionTaskApi.export(params)
|
||||
|
||||
// 创建下载链接 - response.data已经是Blob对象,不需要再包装
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `监管任务列表_${new Date().getTime()}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
@@ -583,6 +695,8 @@ export default {
|
||||
modalFormRef,
|
||||
modalRules,
|
||||
modalTitle,
|
||||
viewModalVisible,
|
||||
viewRecord,
|
||||
getStatusColor,
|
||||
getStatusText,
|
||||
getTaskTypeColor,
|
||||
@@ -597,10 +711,12 @@ export default {
|
||||
handleTaskGuidance,
|
||||
handleBatchCreate,
|
||||
handleView,
|
||||
handleViewModalCancel,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleModalOk,
|
||||
handleModalCancel
|
||||
handleModalCancel,
|
||||
handleExport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,13 +92,19 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" @click="searchLogs">
|
||||
<SearchOutlined />
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button style="margin-left: 8px" @click="resetLogFilters">
|
||||
重置
|
||||
</a-button>
|
||||
<a-space>
|
||||
<a-button @click="handleExportLogs">
|
||||
<DownloadOutlined />
|
||||
导出Excel
|
||||
</a-button>
|
||||
<a-button type="primary" @click="searchLogs">
|
||||
<SearchOutlined />
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button @click="resetLogFilters">
|
||||
重置
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
@@ -170,7 +176,7 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { SearchOutlined } from '@ant-design/icons-vue'
|
||||
import { SearchOutlined, DownloadOutlined } from '@ant-design/icons-vue'
|
||||
import { operationLogAPI } from '@/utils/api'
|
||||
import dayjs from 'dayjs'
|
||||
import PermissionManagement from '@/components/PermissionManagement.vue'
|
||||
@@ -329,6 +335,48 @@ const resetLogFilters = () => {
|
||||
searchLogs()
|
||||
}
|
||||
|
||||
const handleExportLogs = async () => {
|
||||
try {
|
||||
logLoading.value = true
|
||||
const params = {
|
||||
username: logFilters.username || undefined,
|
||||
action: logFilters.action || undefined
|
||||
}
|
||||
|
||||
// 处理日期范围
|
||||
if (logFilters.dateRange && logFilters.dateRange.length === 2) {
|
||||
params.startDate = dayjs(logFilters.dateRange[0]).format('YYYY-MM-DD')
|
||||
params.endDate = dayjs(logFilters.dateRange[1]).format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
// 过滤掉undefined值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
const response = await operationLogAPI.export(params)
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `操作日志_${new Date().getTime()}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
logLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 表格变化处理
|
||||
const handleLogTableChange = (pagination) => {
|
||||
logPagination.current = pagination.current
|
||||
|
||||
@@ -5,10 +5,16 @@
|
||||
sub-title="管理系统用户和权限"
|
||||
>
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="showModal">
|
||||
<plus-outlined />
|
||||
新增用户
|
||||
</a-button>
|
||||
<a-space>
|
||||
<a-button @click="handleExport">
|
||||
<download-outlined />
|
||||
导出Excel
|
||||
</a-button>
|
||||
<a-button type="primary" @click="showModal">
|
||||
<plus-outlined />
|
||||
新增用户
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-page-header>
|
||||
|
||||
@@ -137,7 +143,8 @@ import { message } from 'ant-design-vue'
|
||||
import {
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
RedoOutlined
|
||||
RedoOutlined,
|
||||
DownloadOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { userAPI } from '@/utils/api'
|
||||
|
||||
@@ -406,6 +413,42 @@ const handleDelete = async (id) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
search: searchForm.username || undefined,
|
||||
status: searchForm.status || undefined
|
||||
}
|
||||
|
||||
// 过滤掉undefined值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
const response = await userAPI.export(params)
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `用户列表_${new Date().getTime()}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadUsers()
|
||||
})
|
||||
|
||||
@@ -18,10 +18,10 @@ export default defineConfig(({ mode }) => {
|
||||
port: parseInt(env.VITE_PORT) || 3001,
|
||||
historyApiFallback: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: env.VITE_API_BASE_URL || 'https://ad.ningmuyun.com/insurace/',
|
||||
'/insurance/api': {
|
||||
target: env.VITE_API_BASE_URL || 'http://localhost:3000',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path
|
||||
rewrite: (path) => path.replace(/^\/insurance/, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const { DeviceAlert, Device, User } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const logger = require('../utils/logger');
|
||||
const ExcelExport = require('../utils/excelExport');
|
||||
|
||||
/**
|
||||
* 设备预警控制器
|
||||
@@ -416,6 +417,103 @@ class DeviceAlertController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出设备预警列表到Excel
|
||||
*/
|
||||
static async exportToExcel(req, res) {
|
||||
try {
|
||||
const { alert_level, alert_type, status, is_read } = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
const where = {};
|
||||
if (alert_level) where.alert_level = alert_level;
|
||||
if (alert_type) where.alert_type = alert_type;
|
||||
if (status) where.status = status;
|
||||
if (is_read !== undefined) where.is_read = is_read === 'true';
|
||||
|
||||
// 查询所有符合条件的数据
|
||||
const alerts = await DeviceAlert.findAll({
|
||||
where,
|
||||
include: [{
|
||||
model: Device,
|
||||
as: 'Device',
|
||||
attributes: ['device_name', 'device_number', 'installation_location']
|
||||
}],
|
||||
order: [['alert_time', 'DESC']],
|
||||
raw: true,
|
||||
nest: true
|
||||
});
|
||||
|
||||
// 状态映射
|
||||
const levelMap = {
|
||||
info: '信息',
|
||||
warning: '警告',
|
||||
critical: '严重'
|
||||
};
|
||||
|
||||
const typeMap = {
|
||||
temperature: '温度异常',
|
||||
humidity: '湿度异常',
|
||||
offline: '设备离线',
|
||||
maintenance: '设备维护'
|
||||
};
|
||||
|
||||
const statusMap = {
|
||||
pending: '待处理',
|
||||
processing: '处理中',
|
||||
resolved: '已解决'
|
||||
};
|
||||
|
||||
// 准备导出数据
|
||||
const exportData = alerts.map(alert => ({
|
||||
alert_title: alert.alert_title || '',
|
||||
alert_level: ExcelExport.formatStatus(alert.alert_level, levelMap),
|
||||
alert_type: ExcelExport.formatStatus(alert.alert_type, typeMap),
|
||||
alert_content: alert.alert_content || '',
|
||||
device_name: alert.Device?.device_name || '',
|
||||
device_number: alert.Device?.device_number || '',
|
||||
installation_location: alert.Device?.installation_location || '',
|
||||
status: ExcelExport.formatStatus(alert.status, statusMap),
|
||||
is_read: alert.is_read ? '已读' : '未读',
|
||||
alert_time: ExcelExport.formatDate(alert.alert_time),
|
||||
read_time: ExcelExport.formatDate(alert.read_time)
|
||||
}));
|
||||
|
||||
// 定义列
|
||||
const columns = [
|
||||
{ header: '预警标题', key: 'alert_title', width: 30 },
|
||||
{ header: '预警级别', key: 'alert_level', width: 12 },
|
||||
{ header: '预警类型', key: 'alert_type', width: 15 },
|
||||
{ header: '预警内容', key: 'alert_content', width: 40 },
|
||||
{ header: '设备名称', key: 'device_name', width: 20 },
|
||||
{ header: '设备编号', key: 'device_number', width: 20 },
|
||||
{ header: '安装位置', key: 'installation_location', width: 30 },
|
||||
{ header: '处理状态', key: 'status', width: 12 },
|
||||
{ header: '阅读状态', key: 'is_read', width: 10 },
|
||||
{ header: '预警时间', key: 'alert_time', width: 20 },
|
||||
{ header: '阅读时间', key: 'read_time', width: 20 }
|
||||
];
|
||||
|
||||
// 生成Excel
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '设备预警列表');
|
||||
|
||||
// 设置响应头
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=device_alerts_${Date.now()}.xlsx`);
|
||||
|
||||
// 发送文件
|
||||
res.send(buffer);
|
||||
|
||||
} catch (error) {
|
||||
logger.error('导出设备预警失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '导出失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DeviceAlertController;
|
||||
@@ -2,6 +2,7 @@ const InstallationTask = require('../models/InstallationTask');
|
||||
const User = require('../models/User');
|
||||
const { Op, sequelize } = require('sequelize');
|
||||
const logger = require('../utils/logger');
|
||||
const ExcelExport = require('../utils/excelExport');
|
||||
|
||||
class InstallationTaskController {
|
||||
|
||||
@@ -472,6 +473,99 @@ class InstallationTaskController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 导出待安装任务列表到Excel
|
||||
async exportToExcel(req, res) {
|
||||
try {
|
||||
const { policyNumber, keyword } = req.query;
|
||||
|
||||
// 构建查询条件(使用数据库字段名,因为raw:true模式)
|
||||
const whereConditions = {};
|
||||
|
||||
if (policyNumber) {
|
||||
whereConditions.policy_number = { [Op.like]: `%${policyNumber}%` };
|
||||
}
|
||||
|
||||
// 关键字搜索
|
||||
if (keyword) {
|
||||
whereConditions[Op.or] = [
|
||||
{ application_number: { [Op.like]: `%${keyword}%` } },
|
||||
{ policy_number: { [Op.like]: `%${keyword}%` } },
|
||||
{ customer_name: { [Op.like]: `%${keyword}%` } },
|
||||
{ installation_address: { [Op.like]: `%${keyword}%` } }
|
||||
];
|
||||
}
|
||||
|
||||
// 查询所有符合条件的数据
|
||||
const tasks = await InstallationTask.findAll({
|
||||
where: whereConditions,
|
||||
order: [['created_at', 'DESC']]
|
||||
});
|
||||
|
||||
console.log(`导出查询到 ${tasks.length} 条数据`);
|
||||
|
||||
// 准备导出数据(Sequelize自动转换为驼峰格式)
|
||||
const exportData = tasks.map(task => {
|
||||
const data = task.toJSON ? task.toJSON() : task;
|
||||
return {
|
||||
applicationNumber: data.applicationNumber || '',
|
||||
policyNumber: data.policyNumber || '',
|
||||
productName: data.productName || '',
|
||||
customerName: data.customerName || '',
|
||||
idType: data.idType || '',
|
||||
idNumber: data.idNumber || '',
|
||||
livestockSupplyType: data.livestockSupplyType || '',
|
||||
installationStatus: data.installationStatus || '',
|
||||
priority: data.priority || '',
|
||||
installationAddress: data.installationAddress || '',
|
||||
contactPhone: data.contactPhone || '',
|
||||
taskGeneratedTime: ExcelExport.formatDate(data.taskGeneratedTime),
|
||||
installationCompletedTime: ExcelExport.formatDate(data.installationCompletedTime),
|
||||
createdAt: ExcelExport.formatDate(data.created_at || data.createdAt),
|
||||
updatedAt: ExcelExport.formatDate(data.updated_at || data.updatedAt)
|
||||
};
|
||||
});
|
||||
|
||||
// 定义列
|
||||
const columns = [
|
||||
{ header: '申请单号', key: 'applicationNumber', width: 20 },
|
||||
{ header: '保单编号', key: 'policyNumber', width: 20 },
|
||||
{ header: '产品名称', key: 'productName', width: 25 },
|
||||
{ header: '客户姓名', key: 'customerName', width: 15 },
|
||||
{ header: '证件类型', key: 'idType', width: 12 },
|
||||
{ header: '证件号码', key: 'idNumber', width: 20 },
|
||||
{ header: '养殖生资种类', key: 'livestockSupplyType', width: 20 },
|
||||
{ header: '安装状态', key: 'installationStatus', width: 12 },
|
||||
{ header: '优先级', key: 'priority', width: 10 },
|
||||
{ header: '安装地址', key: 'installationAddress', width: 30 },
|
||||
{ header: '联系电话', key: 'contactPhone', width: 15 },
|
||||
{ header: '任务生成时间', key: 'taskGeneratedTime', width: 20 },
|
||||
{ header: '安装完成时间', key: 'installationCompletedTime', width: 20 },
|
||||
{ header: '创建时间', key: 'createdAt', width: 20 },
|
||||
{ header: '更新时间', key: 'updatedAt', width: 20 }
|
||||
];
|
||||
|
||||
// 生成Excel
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '待安装任务列表');
|
||||
|
||||
// 设置响应头
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=installation_tasks_${Date.now()}.xlsx`);
|
||||
|
||||
// 发送文件
|
||||
res.send(buffer);
|
||||
|
||||
} catch (error) {
|
||||
console.error('导出待安装任务失败:', error);
|
||||
logger.error('导出待安装任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '导出失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new InstallationTaskController();
|
||||
@@ -1,6 +1,7 @@
|
||||
const { InsuranceType } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const responseFormat = require('../utils/response');
|
||||
const ExcelExport = require('../utils/excelExport');
|
||||
|
||||
// 获取保险类型列表
|
||||
const getInsuranceTypes = async (req, res) => {
|
||||
@@ -457,11 +458,59 @@ const updateInsuranceTypeStatus = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 导出保险类型列表到Excel
|
||||
const exportToExcel = async (req, res) => {
|
||||
try {
|
||||
const { name, status } = req.query;
|
||||
|
||||
const where = {};
|
||||
if (name) where.name = { [Op.like]: `%${name}%` };
|
||||
if (status) where.status = status;
|
||||
|
||||
const types = await InsuranceType.findAll({
|
||||
where,
|
||||
order: [['created_at', 'DESC']],
|
||||
raw: true
|
||||
});
|
||||
|
||||
const statusMap = { active: '启用', inactive: '停用' };
|
||||
|
||||
const exportData = types.map(type => ({
|
||||
name: type.name || '',
|
||||
coverage_amount: type.coverage_amount || 0,
|
||||
premium_rate: type.premium_rate || 0,
|
||||
applicable_livestock: type.applicable_livestock || '',
|
||||
description: type.description || '',
|
||||
status: ExcelExport.formatStatus(type.status, statusMap),
|
||||
created_at: ExcelExport.formatDate(type.created_at)
|
||||
}));
|
||||
|
||||
const columns = [
|
||||
{ header: '险种名称', key: 'name', width: 20 },
|
||||
{ header: '保额', key: 'coverage_amount', width: 15 },
|
||||
{ header: '费率', key: 'premium_rate', width: 10 },
|
||||
{ header: '适用牲畜', key: 'applicable_livestock', width: 20 },
|
||||
{ header: '描述', key: 'description', width: 40 },
|
||||
{ header: '状态', key: 'status', width: 10 },
|
||||
{ header: '创建时间', key: 'created_at', width: 20 }
|
||||
];
|
||||
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '保险类型列表');
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=insurance_types_${Date.now()}.xlsx`);
|
||||
res.send(buffer);
|
||||
} catch (error) {
|
||||
console.error('导出保险类型失败:', error);
|
||||
res.status(500).json(responseFormat.error('导出失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getInsuranceTypes,
|
||||
getInsuranceTypeById,
|
||||
createInsuranceType,
|
||||
updateInsuranceType,
|
||||
deleteInsuranceType,
|
||||
updateInsuranceTypeStatus
|
||||
updateInsuranceTypeStatus,
|
||||
exportToExcel
|
||||
};
|
||||
@@ -4,6 +4,7 @@ const LivestockType = require('../models/LivestockType');
|
||||
const User = require('../models/User');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
const ExcelExport = require('../utils/excelExport');
|
||||
|
||||
// 获取生资理赔列表
|
||||
const getLivestockClaims = async (req, res) => {
|
||||
@@ -540,6 +541,66 @@ const deleteLivestockClaim = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 导出理赔列表到Excel
|
||||
const exportToExcel = async (req, res) => {
|
||||
try {
|
||||
const { claim_number, claimant_name, status } = req.query;
|
||||
|
||||
const where = {};
|
||||
if (claim_number) where.claim_number = { [Op.like]: `%${claim_number}%` };
|
||||
if (claimant_name) where.claimant_name = { [Op.like]: `%${claimant_name}%` };
|
||||
if (status) where.status = status;
|
||||
|
||||
const claims = await LivestockClaim.findAll({
|
||||
where,
|
||||
include: [{
|
||||
model: LivestockPolicy,
|
||||
as: 'policy',
|
||||
attributes: ['policy_number']
|
||||
}],
|
||||
order: [['createdAt', 'DESC']],
|
||||
raw: true,
|
||||
nest: true
|
||||
});
|
||||
|
||||
const statusMap = { pending: '待审核', approved: '已批准', rejected: '已拒绝', settled: '已理赔' };
|
||||
|
||||
const exportData = claims.map(claim => ({
|
||||
claim_number: claim.claim_number || '',
|
||||
policy_number: claim.policy?.policy_number || '',
|
||||
claimant_name: claim.claimant_name || '',
|
||||
claim_amount: claim.claim_amount || 0,
|
||||
approved_amount: claim.approved_amount || 0,
|
||||
claim_reason: claim.claim_reason || '',
|
||||
status: ExcelExport.formatStatus(claim.status, statusMap),
|
||||
claim_date: ExcelExport.formatDate(claim.claim_date),
|
||||
settled_date: ExcelExport.formatDate(claim.settled_date),
|
||||
createdAt: ExcelExport.formatDate(claim.createdAt)
|
||||
}));
|
||||
|
||||
const columns = [
|
||||
{ header: '理赔编号', key: 'claim_number', width: 20 },
|
||||
{ header: '保单编号', key: 'policy_number', width: 20 },
|
||||
{ header: '理赔人', key: 'claimant_name', width: 15 },
|
||||
{ header: '理赔金额', key: 'claim_amount', width: 15 },
|
||||
{ header: '批准金额', key: 'approved_amount', width: 15 },
|
||||
{ header: '理赔原因', key: 'claim_reason', width: 30 },
|
||||
{ header: '状态', key: 'status', width: 12 },
|
||||
{ header: '理赔日期', key: 'claim_date', width: 15 },
|
||||
{ header: '结算日期', key: 'settled_date', width: 15 },
|
||||
{ header: '创建时间', key: 'createdAt', width: 20 }
|
||||
];
|
||||
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '理赔列表');
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=livestock_claims_${Date.now()}.xlsx`);
|
||||
res.send(buffer);
|
||||
} catch (error) {
|
||||
console.error('导出理赔记录失败:', error);
|
||||
res.status(500).json(responseFormat.error('导出失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getLivestockClaims,
|
||||
createLivestockClaim,
|
||||
@@ -548,5 +609,6 @@ module.exports = {
|
||||
deleteLivestockClaim,
|
||||
reviewLivestockClaim,
|
||||
updateLivestockClaimPayment,
|
||||
getLivestockClaimStats
|
||||
getLivestockClaimStats,
|
||||
exportToExcel
|
||||
};
|
||||
@@ -3,6 +3,7 @@ const LivestockType = require('../models/LivestockType');
|
||||
const User = require('../models/User');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
const ExcelExport = require('../utils/excelExport');
|
||||
|
||||
// 获取生资保单列表
|
||||
const getLivestockPolicies = async (req, res) => {
|
||||
@@ -337,6 +338,69 @@ const deleteLivestockPolicy = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 导出生资保单列表到Excel
|
||||
const exportToExcel = async (req, res) => {
|
||||
try {
|
||||
const { policy_no, farmer_name, policy_status } = req.query;
|
||||
|
||||
const where = {};
|
||||
if (policy_no) where.policy_no = { [Op.like]: `%${policy_no}%` };
|
||||
if (farmer_name) where.farmer_name = { [Op.like]: `%${farmer_name}%` };
|
||||
if (policy_status) where.policy_status = policy_status;
|
||||
|
||||
const policies = await LivestockPolicy.findAll({
|
||||
where,
|
||||
include: [{
|
||||
model: LivestockType,
|
||||
as: 'livestockType',
|
||||
attributes: ['name']
|
||||
}],
|
||||
order: [['created_at', 'DESC']],
|
||||
raw: true,
|
||||
nest: true
|
||||
});
|
||||
|
||||
const statusMap = { active: '生效中', pending: '待生效', expired: '已过期', cancelled: '已取消' };
|
||||
const paymentMap = { unpaid: '未支付', paid: '已支付', partial: '部分支付' };
|
||||
|
||||
const exportData = policies.map(policy => ({
|
||||
policy_no: policy.policy_no || '',
|
||||
farmer_name: policy.farmer_name || '',
|
||||
farmer_phone: policy.farmer_phone || '',
|
||||
livestock_type: policy.livestockType?.name || '',
|
||||
insured_amount: policy.insured_amount || 0,
|
||||
premium_amount: policy.premium_amount || 0,
|
||||
policy_status: ExcelExport.formatStatus(policy.policy_status, statusMap),
|
||||
payment_status: ExcelExport.formatStatus(policy.payment_status, paymentMap),
|
||||
start_date: ExcelExport.formatDate(policy.start_date),
|
||||
end_date: ExcelExport.formatDate(policy.end_date),
|
||||
created_at: ExcelExport.formatDate(policy.created_at)
|
||||
}));
|
||||
|
||||
const columns = [
|
||||
{ header: '保单号', key: 'policy_no', width: 20 },
|
||||
{ header: '农户姓名', key: 'farmer_name', width: 15 },
|
||||
{ header: '联系电话', key: 'farmer_phone', width: 15 },
|
||||
{ header: '牲畜类型', key: 'livestock_type', width: 15 },
|
||||
{ header: '保额', key: 'insured_amount', width: 15 },
|
||||
{ header: '保费', key: 'premium_amount', width: 15 },
|
||||
{ header: '保单状态', key: 'policy_status', width: 12 },
|
||||
{ header: '支付状态', key: 'payment_status', width: 12 },
|
||||
{ header: '起始日期', key: 'start_date', width: 15 },
|
||||
{ header: '结束日期', key: 'end_date', width: 15 },
|
||||
{ header: '创建时间', key: 'created_at', width: 20 }
|
||||
];
|
||||
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '生资保单列表');
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=livestock_policies_${Date.now()}.xlsx`);
|
||||
res.send(buffer);
|
||||
} catch (error) {
|
||||
console.error('导出生资保单失败:', error);
|
||||
res.status(500).json(responseFormat.error('导出失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getLivestockPolicies,
|
||||
createLivestockPolicy,
|
||||
@@ -344,5 +408,6 @@ module.exports = {
|
||||
updateLivestockPolicy,
|
||||
updateLivestockPolicyStatus,
|
||||
deleteLivestockPolicy,
|
||||
getLivestockPolicyStats
|
||||
getLivestockPolicyStats,
|
||||
exportToExcel
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
const { OperationLog, User } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const ExcelJS = require('exceljs');
|
||||
const ExcelExport = require('../utils/excelExport');
|
||||
|
||||
/**
|
||||
* 操作日志控制器
|
||||
@@ -339,6 +340,99 @@ class OperationLogController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出操作日志列表到Excel
|
||||
*/
|
||||
async exportToExcel(req, res) {
|
||||
try {
|
||||
const { username, operation_type, operation_module, startDate, endDate } = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
const where = {};
|
||||
if (username) {
|
||||
where['$User.username$'] = { [Op.like]: `%${username}%` };
|
||||
}
|
||||
if (operation_type) where.operation_type = operation_type;
|
||||
if (operation_module) where.operation_module = operation_module;
|
||||
if (startDate && endDate) {
|
||||
where.created_at = {
|
||||
[Op.between]: [new Date(startDate), new Date(endDate)]
|
||||
};
|
||||
}
|
||||
|
||||
// 查询所有符合条件的数据
|
||||
const logs = await OperationLog.findAll({
|
||||
where,
|
||||
include: [{
|
||||
model: User,
|
||||
as: 'User',
|
||||
attributes: ['username', 'real_name']
|
||||
}],
|
||||
order: [['created_at', 'DESC']],
|
||||
raw: true,
|
||||
nest: true
|
||||
});
|
||||
|
||||
// 操作类型映射
|
||||
const typeMap = {
|
||||
login: '登录',
|
||||
logout: '登出',
|
||||
create: '创建',
|
||||
update: '更新',
|
||||
delete: '删除',
|
||||
view: '查看',
|
||||
export: '导出'
|
||||
};
|
||||
|
||||
const statusMap = {
|
||||
success: '成功',
|
||||
failure: '失败'
|
||||
};
|
||||
|
||||
// 准备导出数据
|
||||
const exportData = logs.map(log => ({
|
||||
username: log.User?.username || '',
|
||||
real_name: log.User?.real_name || '',
|
||||
operation_type: ExcelExport.formatStatus(log.operation_type, typeMap),
|
||||
operation_module: log.operation_module || '',
|
||||
operation_desc: log.operation_desc || '',
|
||||
ip_address: log.ip_address || '',
|
||||
status: ExcelExport.formatStatus(log.status, statusMap),
|
||||
created_at: ExcelExport.formatDate(log.created_at)
|
||||
}));
|
||||
|
||||
// 定义列
|
||||
const columns = [
|
||||
{ header: '用户名', key: 'username', width: 15 },
|
||||
{ header: '真实姓名', key: 'real_name', width: 15 },
|
||||
{ header: '操作类型', key: 'operation_type', width: 12 },
|
||||
{ header: '操作模块', key: 'operation_module', width: 20 },
|
||||
{ header: '操作描述', key: 'operation_desc', width: 40 },
|
||||
{ header: 'IP地址', key: 'ip_address', width: 20 },
|
||||
{ header: '状态', key: 'status', width: 10 },
|
||||
{ header: '操作时间', key: 'created_at', width: 20 }
|
||||
];
|
||||
|
||||
// 生成Excel
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '操作日志');
|
||||
|
||||
// 设置响应头
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=operation_logs_${Date.now()}.xlsx`);
|
||||
|
||||
// 发送文件
|
||||
res.send(buffer);
|
||||
|
||||
} catch (error) {
|
||||
console.error('导出操作日志失败:', error);
|
||||
res.status(500).json({
|
||||
status: 'error',
|
||||
message: '导出失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new OperationLogController();
|
||||
@@ -1,6 +1,7 @@
|
||||
const { Policy, InsuranceApplication, InsuranceType, User } = require('../models');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
const ExcelExport = require('../utils/excelExport');
|
||||
|
||||
// 获取保单列表
|
||||
const getPolicies = async (req, res) => {
|
||||
@@ -363,6 +364,64 @@ const deletePolicy = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 导出保单列表到Excel
|
||||
const exportToExcel = async (req, res) => {
|
||||
try {
|
||||
const { policy_number, policyholder_name, status } = req.query;
|
||||
|
||||
const where = {};
|
||||
if (policy_number) where.policy_number = { [Op.like]: `%${policy_number}%` };
|
||||
if (policyholder_name) where.policyholder_name = { [Op.like]: `%${policyholder_name}%` };
|
||||
if (status) where.status = status;
|
||||
|
||||
const policies = await Policy.findAll({
|
||||
where,
|
||||
include: [{
|
||||
model: InsuranceType,
|
||||
as: 'InsuranceType',
|
||||
attributes: ['name']
|
||||
}],
|
||||
order: [['created_at', 'DESC']],
|
||||
raw: true,
|
||||
nest: true
|
||||
});
|
||||
|
||||
const statusMap = { active: '生效中', pending: '待生效', expired: '已过期', cancelled: '已取消' };
|
||||
|
||||
const exportData = policies.map(policy => ({
|
||||
policy_number: policy.policy_number || '',
|
||||
policyholder_name: policy.policyholder_name || '',
|
||||
insurance_type_name: policy.InsuranceType?.name || '',
|
||||
coverage_amount: policy.coverage_amount || 0,
|
||||
premium_amount: policy.premium_amount || 0,
|
||||
start_date: ExcelExport.formatDate(policy.start_date),
|
||||
end_date: ExcelExport.formatDate(policy.end_date),
|
||||
status: ExcelExport.formatStatus(policy.status, statusMap),
|
||||
created_at: ExcelExport.formatDate(policy.created_at)
|
||||
}));
|
||||
|
||||
const columns = [
|
||||
{ header: '保单编号', key: 'policy_number', width: 20 },
|
||||
{ header: '投保人', key: 'policyholder_name', width: 15 },
|
||||
{ header: '险种', key: 'insurance_type_name', width: 20 },
|
||||
{ header: '保额', key: 'coverage_amount', width: 15 },
|
||||
{ header: '保费', key: 'premium_amount', width: 15 },
|
||||
{ header: '开始日期', key: 'start_date', width: 15 },
|
||||
{ header: '结束日期', key: 'end_date', width: 15 },
|
||||
{ header: '状态', key: 'status', width: 12 },
|
||||
{ header: '创建时间', key: 'created_at', width: 20 }
|
||||
];
|
||||
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '保单列表');
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=policies_${Date.now()}.xlsx`);
|
||||
res.send(buffer);
|
||||
} catch (error) {
|
||||
console.error('导出保单失败:', error);
|
||||
res.status(500).json(responseFormat.error('导出失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getPolicies,
|
||||
createPolicy,
|
||||
@@ -370,5 +429,6 @@ module.exports = {
|
||||
updatePolicy,
|
||||
updatePolicyStatus,
|
||||
deletePolicy,
|
||||
getPolicyStats
|
||||
getPolicyStats,
|
||||
exportToExcel
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
const { SupervisoryTask, User } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const ExcelExport = require('../utils/excelExport');
|
||||
|
||||
/**
|
||||
* 监管任务控制器
|
||||
@@ -522,6 +523,93 @@ class SupervisoryTaskController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出监管任务列表到Excel
|
||||
*/
|
||||
static async exportToExcel(req, res) {
|
||||
try {
|
||||
const { policyNumber, customerName } = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
const where = {};
|
||||
|
||||
if (policyNumber) {
|
||||
where.policyNumber = { [Op.like]: `%${policyNumber}%` };
|
||||
}
|
||||
|
||||
if (customerName) {
|
||||
where.customerName = { [Op.like]: `%${customerName}%` };
|
||||
}
|
||||
|
||||
// 查询所有符合条件的数据
|
||||
const tasks = await SupervisoryTask.findAll({
|
||||
where,
|
||||
order: [['createdAt', 'DESC']],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// 状态映射
|
||||
const statusMap = {
|
||||
pending: '待处理',
|
||||
processing: '处理中',
|
||||
completed: '已完成',
|
||||
rejected: '已拒绝'
|
||||
};
|
||||
|
||||
const priorityMap = {
|
||||
low: '低',
|
||||
medium: '中',
|
||||
high: '高',
|
||||
urgent: '紧急'
|
||||
};
|
||||
|
||||
// 准备导出数据
|
||||
const exportData = tasks.map(task => ({
|
||||
policyNumber: task.policyNumber || '',
|
||||
customerName: task.customerName || '',
|
||||
productName: task.productName || '',
|
||||
insurancePeriod: task.insurancePeriod || '',
|
||||
applicableAmount: task.applicableAmount || '',
|
||||
taskStatus: ExcelExport.formatStatus(task.taskStatus, statusMap),
|
||||
priority: ExcelExport.formatStatus(task.priority, priorityMap),
|
||||
createdAt: ExcelExport.formatDate(task.createdAt),
|
||||
updatedAt: ExcelExport.formatDate(task.updatedAt)
|
||||
}));
|
||||
|
||||
// 定义列
|
||||
const columns = [
|
||||
{ header: '保单编号', key: 'policyNumber', width: 20 },
|
||||
{ header: '客户姓名', key: 'customerName', width: 15 },
|
||||
{ header: '产品名称', key: 'productName', width: 25 },
|
||||
{ header: '保险期间', key: 'insurancePeriod', width: 25 },
|
||||
{ header: '适用金额', key: 'applicableAmount', width: 15 },
|
||||
{ header: '任务状态', key: 'taskStatus', width: 12 },
|
||||
{ header: '优先级', key: 'priority', width: 12 },
|
||||
{ header: '创建时间', key: 'createdAt', width: 20 },
|
||||
{ header: '更新时间', key: 'updatedAt', width: 20 }
|
||||
];
|
||||
|
||||
// 生成Excel
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '监管任务列表');
|
||||
|
||||
// 设置响应头
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=supervision_tasks_${Date.now()}.xlsx`);
|
||||
|
||||
// 发送文件
|
||||
res.send(buffer);
|
||||
|
||||
} catch (error) {
|
||||
console.error('导出监管任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '导出失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SupervisoryTaskController;
|
||||
@@ -2,6 +2,7 @@ const { User, Role } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const responseFormat = require('../utils/response');
|
||||
const crypto = require('crypto');
|
||||
const ExcelExport = require('../utils/excelExport');
|
||||
|
||||
// 获取用户列表
|
||||
const getUsers = async (req, res) => {
|
||||
@@ -486,6 +487,84 @@ const getFixedTokenInfo = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 导出用户列表到Excel
|
||||
const exportToExcel = async (req, res) => {
|
||||
try {
|
||||
const { search, status } = req.query;
|
||||
|
||||
const whereClause = {};
|
||||
if (search) {
|
||||
whereClause[Op.or] = [
|
||||
{ username: { [Op.like]: `%${search}%` } },
|
||||
{ real_name: { [Op.like]: `%${search}%` } },
|
||||
{ email: { [Op.like]: `%${search}%` } },
|
||||
{ phone: { [Op.like]: `%${search}%` } }
|
||||
];
|
||||
}
|
||||
if (status) whereClause.status = status;
|
||||
|
||||
// 查询所有符合条件的数据
|
||||
const users = await User.findAll({
|
||||
where: whereClause,
|
||||
include: [{
|
||||
model: Role,
|
||||
as: 'role',
|
||||
attributes: ['name']
|
||||
}],
|
||||
order: [['created_at', 'DESC']],
|
||||
raw: true,
|
||||
nest: true
|
||||
});
|
||||
|
||||
// 状态映射
|
||||
const statusMap = {
|
||||
active: '活跃',
|
||||
inactive: '禁用',
|
||||
suspended: '暂停'
|
||||
};
|
||||
|
||||
// 准备导出数据
|
||||
const exportData = users.map(user => ({
|
||||
id: user.id || '',
|
||||
username: user.username || '',
|
||||
real_name: user.real_name || '',
|
||||
email: user.email || '',
|
||||
phone: user.phone || '',
|
||||
role_name: user.role?.name || '',
|
||||
status: ExcelExport.formatStatus(user.status, statusMap),
|
||||
created_at: ExcelExport.formatDate(user.created_at),
|
||||
updated_at: ExcelExport.formatDate(user.updated_at)
|
||||
}));
|
||||
|
||||
// 定义列
|
||||
const columns = [
|
||||
{ header: 'ID', key: 'id', width: 10 },
|
||||
{ header: '用户名', key: 'username', width: 15 },
|
||||
{ header: '真实姓名', key: 'real_name', width: 15 },
|
||||
{ header: '邮箱', key: 'email', width: 25 },
|
||||
{ header: '手机号', key: 'phone', width: 15 },
|
||||
{ header: '角色', key: 'role_name', width: 12 },
|
||||
{ header: '状态', key: 'status', width: 10 },
|
||||
{ header: '创建时间', key: 'created_at', width: 20 },
|
||||
{ header: '更新时间', key: 'updated_at', width: 20 }
|
||||
];
|
||||
|
||||
// 生成Excel
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '用户列表');
|
||||
|
||||
// 设置响应头
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=users_${Date.now()}.xlsx`);
|
||||
|
||||
// 发送文件
|
||||
res.send(buffer);
|
||||
|
||||
} catch (error) {
|
||||
console.error('导出用户列表失败:', error);
|
||||
res.status(500).json(responseFormat.error('导出失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getUsers,
|
||||
getUser,
|
||||
@@ -500,5 +579,6 @@ module.exports = {
|
||||
generateFixedToken,
|
||||
regenerateFixedToken,
|
||||
deleteFixedToken,
|
||||
getFixedTokenInfo
|
||||
getFixedTokenInfo,
|
||||
exportToExcel
|
||||
};
|
||||
326
insurance_backend/package-lock.json
generated
326
insurance_backend/package-lock.json
generated
@@ -2386,6 +2386,42 @@
|
||||
"node": ">=0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/builtin-modules": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz",
|
||||
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/builtins": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/builtins/-/builtins-5.1.0.tgz",
|
||||
"integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"semver": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/builtins/node_modules/semver": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz",
|
||||
@@ -3645,6 +3681,35 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-compat-utils": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
|
||||
"integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-compat-utils/node_modules/semver": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-standard": {
|
||||
"version": "17.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz",
|
||||
@@ -3739,6 +3804,28 @@
|
||||
"eslint": ">=4.19.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-es-x": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz",
|
||||
"integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/sponsors/ota-meshi",
|
||||
"https://opencollective.com/eslint"
|
||||
],
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.1.2",
|
||||
"@eslint-community/regexpp": "^4.11.0",
|
||||
"eslint-compat-utils": "^0.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-import": {
|
||||
"version": "2.32.0",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
|
||||
@@ -3793,6 +3880,48 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-n": {
|
||||
"version": "16.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz",
|
||||
"integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"builtins": "^5.0.1",
|
||||
"eslint-plugin-es-x": "^7.5.0",
|
||||
"get-tsconfig": "^4.7.0",
|
||||
"globals": "^13.24.0",
|
||||
"ignore": "^5.2.4",
|
||||
"is-builtin-module": "^3.2.1",
|
||||
"is-core-module": "^2.12.1",
|
||||
"minimatch": "^3.1.2",
|
||||
"resolve": "^1.22.2",
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mysticatea"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-n/node_modules/semver": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-node": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
|
||||
@@ -4637,6 +4766,19 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.12.0.tgz",
|
||||
"integrity": "sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
|
||||
@@ -5113,6 +5255,22 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-builtin-module": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
|
||||
"integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"builtin-modules": "^3.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-callable": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz",
|
||||
@@ -7225,6 +7383,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/openapi-types": {
|
||||
"version": "12.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/openapi-types/-/openapi-types-12.1.3.tgz",
|
||||
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz",
|
||||
@@ -7995,6 +8159,16 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve.exports": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/resolve.exports/-/resolve.exports-2.0.3.tgz",
|
||||
@@ -10869,7 +11043,8 @@
|
||||
"@redis/bloom": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@redis/bloom/-/bloom-1.2.0.tgz",
|
||||
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg=="
|
||||
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@redis/client": {
|
||||
"version": "1.6.1",
|
||||
@@ -10891,22 +11066,26 @@
|
||||
"@redis/graph": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/@redis/graph/-/graph-1.1.1.tgz",
|
||||
"integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw=="
|
||||
"integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@redis/json": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/@redis/json/-/json-1.0.7.tgz",
|
||||
"integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ=="
|
||||
"integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@redis/search": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@redis/search/-/search-1.2.0.tgz",
|
||||
"integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw=="
|
||||
"integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@redis/time-series": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@redis/time-series/-/time-series-1.1.0.tgz",
|
||||
"integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g=="
|
||||
"integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==",
|
||||
"requires": {}
|
||||
},
|
||||
"@rtsao/scc": {
|
||||
"version": "1.1.0",
|
||||
@@ -11127,7 +11306,8 @@
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"agent-base": {
|
||||
"version": "6.0.2",
|
||||
@@ -11654,6 +11834,32 @@
|
||||
"resolved": "https://registry.npmmirror.com/buffers/-/buffers-0.1.1.tgz",
|
||||
"integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ=="
|
||||
},
|
||||
"builtin-modules": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz",
|
||||
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"builtins": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/builtins/-/builtins-5.1.0.tgz",
|
||||
"integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"semver": "^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz",
|
||||
@@ -12129,7 +12335,8 @@
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmmirror.com/dedent/-/dedent-1.7.0.tgz",
|
||||
"integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"deep-is": {
|
||||
"version": "0.1.4",
|
||||
@@ -12593,11 +12800,31 @@
|
||||
"text-table": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"eslint-compat-utils": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
|
||||
"integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-config-standard": {
|
||||
"version": "17.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz",
|
||||
"integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"eslint-import-resolver-node": {
|
||||
"version": "0.3.9",
|
||||
@@ -12651,6 +12878,18 @@
|
||||
"regexpp": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"eslint-plugin-es-x": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz",
|
||||
"integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@eslint-community/eslint-utils": "^4.1.2",
|
||||
"@eslint-community/regexpp": "^4.11.0",
|
||||
"eslint-compat-utils": "^0.5.1"
|
||||
}
|
||||
},
|
||||
"eslint-plugin-import": {
|
||||
"version": "2.32.0",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
|
||||
@@ -12698,6 +12937,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-plugin-n": {
|
||||
"version": "16.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz",
|
||||
"integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"builtins": "^5.0.1",
|
||||
"eslint-plugin-es-x": "^7.5.0",
|
||||
"get-tsconfig": "^4.7.0",
|
||||
"globals": "^13.24.0",
|
||||
"ignore": "^5.2.4",
|
||||
"is-builtin-module": "^3.2.1",
|
||||
"is-core-module": "^2.12.1",
|
||||
"minimatch": "^3.1.2",
|
||||
"resolve": "^1.22.2",
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-plugin-node": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
|
||||
@@ -12716,7 +12984,8 @@
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz",
|
||||
"integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "7.2.2",
|
||||
@@ -13314,6 +13583,16 @@
|
||||
"get-intrinsic": "^1.2.6"
|
||||
}
|
||||
},
|
||||
"get-tsconfig": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.12.0.tgz",
|
||||
"integrity": "sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
|
||||
@@ -13632,6 +13911,16 @@
|
||||
"has-tostringtag": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"is-builtin-module": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
|
||||
"integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"builtin-modules": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"is-callable": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz",
|
||||
@@ -14156,7 +14445,8 @@
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
|
||||
"integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"jest-regex-util": {
|
||||
"version": "29.6.3",
|
||||
@@ -15219,6 +15509,12 @@
|
||||
"mimic-fn": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"openapi-types": {
|
||||
"version": "12.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/openapi-types/-/openapi-types-12.1.3.tgz",
|
||||
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
|
||||
"peer": true
|
||||
},
|
||||
"optionator": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz",
|
||||
@@ -15768,6 +16064,13 @@
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"resolve.exports": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/resolve.exports/-/resolve.exports-2.0.3.tgz",
|
||||
@@ -17020,7 +17323,8 @@
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.13.0.tgz",
|
||||
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"xmlchars": {
|
||||
"version": "2.2.0",
|
||||
|
||||
@@ -83,6 +83,47 @@ const { jwtAuth } = require('../middleware/auth');
|
||||
*/
|
||||
router.get('/stats', jwtAuth, deviceAlertController.getAlertStats);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/device-alerts/export:
|
||||
* get:
|
||||
* tags:
|
||||
* - 设备预警
|
||||
* summary: 导出设备预警列表到Excel
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: alert_level
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 预警级别
|
||||
* - in: query
|
||||
* name: alert_type
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 预警类型
|
||||
* - in: query
|
||||
* name: status
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 处理状态
|
||||
* - in: query
|
||||
* name: is_read
|
||||
* schema:
|
||||
* type: boolean
|
||||
* description: 是否已读
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 导出成功
|
||||
* content:
|
||||
* application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
|
||||
* schema:
|
||||
* type: string
|
||||
* format: binary
|
||||
*/
|
||||
router.get('/export', jwtAuth, deviceAlertController.exportToExcel);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/device-alerts:
|
||||
|
||||
@@ -10,6 +10,50 @@ const { jwtAuth, requirePermission } = require('../middleware/auth');
|
||||
* description: 待安装任务管理
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks/stats:
|
||||
* get:
|
||||
* summary: 获取安装任务统计数据
|
||||
* tags: [InstallationTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
*/
|
||||
router.get('/stats', jwtAuth, requirePermission('installation_tasks:read'), installationTaskController.getInstallationTaskStats);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks/export:
|
||||
* get:
|
||||
* summary: 导出待安装任务到Excel
|
||||
* tags: [InstallationTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: policyNumber
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 保单编号
|
||||
* - in: query
|
||||
* name: keyword
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 关键字搜索
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 导出成功
|
||||
* content:
|
||||
* application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
|
||||
* schema:
|
||||
* type: string
|
||||
* format: binary
|
||||
*/
|
||||
router.get('/export', jwtAuth, requirePermission('installation_tasks:read'), installationTaskController.exportToExcel);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks:
|
||||
@@ -230,38 +274,4 @@ router.delete('/:id', jwtAuth, requirePermission('installation_tasks:delete'), i
|
||||
*/
|
||||
router.post('/batch/operate', jwtAuth, requirePermission('installation_tasks:update'), installationTaskController.batchOperateInstallationTasks);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks/export:
|
||||
* get:
|
||||
* summary: 导出待安装任务数据
|
||||
* tags: [InstallationTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: ids
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 任务ID列表(逗号分隔)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 导出成功
|
||||
*/
|
||||
router.get('/export', jwtAuth, requirePermission('installation_tasks:read'), installationTaskController.exportInstallationTasks);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks/stats:
|
||||
* get:
|
||||
* summary: 获取安装任务统计数据
|
||||
* tags: [InstallationTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
*/
|
||||
router.get('/stats', jwtAuth, requirePermission('installation_tasks:read'), installationTaskController.getInstallationTaskStats);
|
||||
|
||||
module.exports = router;
|
||||
@@ -174,6 +174,9 @@ router.get('/', jwtAuth, checkPermission('insurance_type', 'read'),
|
||||
* '500':
|
||||
* description: 服务器内部错误
|
||||
*/
|
||||
// 导出险种列表到Excel
|
||||
router.get('/export', jwtAuth, checkPermission('insurance_type', 'read'), insuranceTypeController.exportToExcel);
|
||||
|
||||
// 获取单个险种详情
|
||||
router.get('/:id', jwtAuth, checkPermission('insurance_type', 'read'),
|
||||
insuranceTypeController.getInsuranceTypeById
|
||||
|
||||
@@ -8,7 +8,8 @@ const {
|
||||
updateLivestockClaimPayment,
|
||||
getLivestockClaimStats,
|
||||
updateLivestockClaim,
|
||||
deleteLivestockClaim
|
||||
deleteLivestockClaim,
|
||||
exportToExcel
|
||||
} = require('../controllers/livestockClaimController');
|
||||
const { authenticateToken, requirePermission } = require('../middleware/auth');
|
||||
|
||||
@@ -33,6 +34,9 @@ router.get('/', authenticateToken, requirePermission('livestock_claim:read'), ge
|
||||
// 获取生资理赔统计
|
||||
router.get('/stats', authenticateToken, requirePermission('livestock_claim:read'), getLivestockClaimStats);
|
||||
|
||||
// 导出理赔列表到Excel
|
||||
router.get('/export', authenticateToken, requirePermission('livestock_claim:read'), exportToExcel);
|
||||
|
||||
// 获取单个生资理赔详情
|
||||
router.get('/:id', authenticateToken, requirePermission('livestock_claim:read'), getLivestockClaimById);
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ const {
|
||||
updateLivestockPolicy,
|
||||
updateLivestockPolicyStatus,
|
||||
deleteLivestockPolicy,
|
||||
getLivestockPolicyStats
|
||||
getLivestockPolicyStats,
|
||||
exportToExcel
|
||||
} = require('../controllers/livestockPolicyController');
|
||||
const { jwtAuth, checkPermission } = require('../middleware/auth');
|
||||
|
||||
@@ -17,6 +18,9 @@ router.get('/', jwtAuth, checkPermission('insurance:policy', 'view'), getLivesto
|
||||
// 获取生资保单统计
|
||||
router.get('/stats', jwtAuth, checkPermission('insurance:policy', 'view'), getLivestockPolicyStats);
|
||||
|
||||
// 导出生资保单列表到Excel
|
||||
router.get('/export', jwtAuth, checkPermission('insurance:policy', 'view'), exportToExcel);
|
||||
|
||||
// 获取单个生资保单详情
|
||||
router.get('/:id', jwtAuth, checkPermission('insurance:policy', 'view'), getLivestockPolicyById);
|
||||
|
||||
|
||||
@@ -169,6 +169,9 @@ router.get('/', jwtAuth, checkPermission('system', 'read'), operationLogControll
|
||||
*/
|
||||
router.get('/stats', jwtAuth, checkPermission('system', 'read'), operationLogController.getOperationStats);
|
||||
|
||||
// 导出操作日志到Excel
|
||||
router.get('/export', jwtAuth, checkPermission('system', 'read'), operationLogController.exportToExcel);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/operation-logs/{id}:
|
||||
|
||||
@@ -18,6 +18,9 @@ router.post('/', jwtAuth, checkPermission('insurance:policy', 'create'),
|
||||
policyController.createPolicy
|
||||
);
|
||||
|
||||
// 导出保单列表到Excel
|
||||
router.get('/export', jwtAuth, checkPermission('insurance:policy', 'view'), policyController.exportToExcel);
|
||||
|
||||
// 获取单个保单详情
|
||||
router.get('/:id', jwtAuth, checkPermission('insurance:policy', 'view'),
|
||||
policyController.getPolicyById
|
||||
|
||||
@@ -10,6 +10,50 @@ const { jwtAuth, requirePermission } = require('../middleware/auth');
|
||||
* description: 监管任务管理
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks/stats:
|
||||
* get:
|
||||
* summary: 获取监管任务统计信息
|
||||
* tags: [SupervisionTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
*/
|
||||
router.get('/stats', jwtAuth, requirePermission('supervision_tasks:read'), SupervisoryTaskController.getStatistics);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks/export:
|
||||
* get:
|
||||
* summary: 导出监管任务列表到Excel
|
||||
* tags: [SupervisionTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: policyNumber
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 保单编号
|
||||
* - in: query
|
||||
* name: customerName
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 客户姓名
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 导出成功
|
||||
* content:
|
||||
* application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
|
||||
* schema:
|
||||
* type: string
|
||||
* format: binary
|
||||
*/
|
||||
router.get('/export', jwtAuth, requirePermission('supervision_tasks:read'), SupervisoryTaskController.exportToExcel);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks:
|
||||
@@ -253,18 +297,4 @@ router.delete('/:id', jwtAuth, requirePermission('supervision_tasks:delete'), Su
|
||||
*/
|
||||
router.post('/batch/operate', jwtAuth, requirePermission('supervision_tasks:create'), SupervisoryTaskController.bulkCreate);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks/stats:
|
||||
* get:
|
||||
* summary: 获取监管任务统计信息
|
||||
* tags: [SupervisionTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
*/
|
||||
router.get('/stats', jwtAuth, requirePermission('supervision_tasks:read'), SupervisoryTaskController.getStatistics);
|
||||
|
||||
module.exports = router;
|
||||
@@ -13,6 +13,9 @@ router.get('/profile', jwtAuth, userController.getProfile);
|
||||
// 更新个人资料(不需要特殊权限,用户可以更新自己的资料)
|
||||
router.put('/profile', jwtAuth, userController.updateProfile);
|
||||
|
||||
// 导出用户列表(必须在 /:id 之前)
|
||||
router.get('/export', jwtAuth, checkPermission('user', 'read'), userController.exportToExcel);
|
||||
|
||||
// 获取单个用户信息
|
||||
router.get('/:id', jwtAuth, checkPermission('user', 'read'), userController.getUser);
|
||||
|
||||
|
||||
121
insurance_backend/scripts/add-export-routes.js
Normal file
121
insurance_backend/scripts/add-export-routes.js
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 批量为各个控制器添加导出路由的脚本
|
||||
* 使用说明:node scripts/add-export-routes.js
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 需要添加导出功能的路由配置
|
||||
const routeConfigs = [
|
||||
{
|
||||
routeFile: 'routes/users.js',
|
||||
exportPath: '/export',
|
||||
controllerMethod: 'exportToExcel',
|
||||
permission: 'users:read',
|
||||
description: '导出用户列表到Excel'
|
||||
},
|
||||
{
|
||||
routeFile: 'routes/deviceAlerts.js',
|
||||
exportPath: '/export',
|
||||
controllerMethod: 'exportToExcel',
|
||||
permission: 'device_alerts:read',
|
||||
description: '导出设备预警列表到Excel'
|
||||
},
|
||||
{
|
||||
routeFile: 'routes/insuranceTypes.js',
|
||||
exportPath: '/export',
|
||||
controllerMethod: 'exportToExcel',
|
||||
permission: 'insurance_types:read',
|
||||
description: '导出保险类型列表到Excel'
|
||||
},
|
||||
{
|
||||
routeFile: 'routes/policies.js',
|
||||
exportPath: '/export',
|
||||
controllerMethod: 'exportToExcel',
|
||||
permission: 'policies:read',
|
||||
description: '导出保单列表到Excel'
|
||||
},
|
||||
{
|
||||
routeFile: 'routes/livestockClaims.js',
|
||||
exportPath: '/export',
|
||||
controllerMethod: 'exportToExcel',
|
||||
permission: 'livestock_claims:read',
|
||||
description: '导出理赔列表到Excel'
|
||||
},
|
||||
{
|
||||
routeFile: 'routes/installationTasks.js',
|
||||
exportPath: '/export',
|
||||
controllerMethod: 'exportToExcel',
|
||||
permission: 'installation_tasks:read',
|
||||
description: '导出待安装任务列表到Excel'
|
||||
},
|
||||
{
|
||||
routeFile: 'routes/livestockPolicies.js',
|
||||
exportPath: '/export',
|
||||
controllerMethod: 'exportToExcel',
|
||||
permission: 'livestock_policies:read',
|
||||
description: '导出生资保单列表到Excel'
|
||||
},
|
||||
{
|
||||
routeFile: 'routes/operationLogs.js',
|
||||
exportPath: '/export',
|
||||
controllerMethod: 'exportToExcel',
|
||||
permission: 'operation_logs:read',
|
||||
description: '导出操作日志到Excel'
|
||||
}
|
||||
];
|
||||
|
||||
console.log('🚀 开始为各个路由添加导出功能...\n');
|
||||
|
||||
routeConfigs.forEach(config => {
|
||||
const filePath = path.join(__dirname, '..', config.routeFile);
|
||||
|
||||
try {
|
||||
// 读取路由文件
|
||||
let content = fs.readFileSync(filePath, 'utf8');
|
||||
|
||||
// 检查是否已经存在导出路由
|
||||
if (content.includes(`router.get('${config.exportPath}'`) || content.includes(`router.get("${config.exportPath}"`)) {
|
||||
console.log(`⏭️ ${config.routeFile} - 导出路由已存在,跳过`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 在module.exports之前添加导出路由
|
||||
const exportRoute = `
|
||||
/**
|
||||
* @swagger
|
||||
* ${config.exportPath}:
|
||||
* get:
|
||||
* summary: ${config.description}
|
||||
* tags: [Export]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 导出成功
|
||||
* content:
|
||||
* application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
|
||||
* schema:
|
||||
* type: string
|
||||
* format: binary
|
||||
*/
|
||||
router.get('${config.exportPath}', jwtAuth, requirePermission('${config.permission}'), controller.${config.controllerMethod});
|
||||
|
||||
`;
|
||||
|
||||
// 在module.exports之前插入
|
||||
content = content.replace(/module\.exports\s*=\s*router;/, exportRoute + 'module.exports = router;');
|
||||
|
||||
// 写回文件
|
||||
fs.writeFileSync(filePath, content, 'utf8');
|
||||
console.log(`✅ ${config.routeFile} - 导出路由添加成功`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ ${config.routeFile} - 添加失败: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n✨ 路由添加完成!');
|
||||
console.log('⚠️ 注意:还需要为各个控制器添加 exportToExcel 方法');
|
||||
|
||||
31
insurance_backend/test-db-connection.js
Normal file
31
insurance_backend/test-db-connection.js
Normal file
@@ -0,0 +1,31 @@
|
||||
require('dotenv').config();
|
||||
const { sequelize, testConnection } = require('./config/database');
|
||||
|
||||
async function test() {
|
||||
console.log('测试数据库连接...');
|
||||
console.log('数据库配置:');
|
||||
console.log('- Host:', process.env.DB_HOST || '129.211.213.226');
|
||||
console.log('- Port:', process.env.DB_PORT || 9527);
|
||||
console.log('- Database:', process.env.DB_DATABASE || 'insurance_data');
|
||||
console.log('- User:', process.env.DB_USER || 'root');
|
||||
console.log('');
|
||||
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功!');
|
||||
|
||||
// 测试查询
|
||||
const [results] = await sequelize.query('SELECT 1 as test');
|
||||
console.log('✅ 测试查询成功:', results);
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('❌ 数据库连接失败:');
|
||||
console.error('错误信息:', error.message);
|
||||
console.error('错误详情:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
test();
|
||||
|
||||
52
insurance_backend/test-nxxmdata.js
Normal file
52
insurance_backend/test-nxxmdata.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const { Sequelize } = require('sequelize');
|
||||
|
||||
// 测试连接到 nxxmdata 数据库
|
||||
const sequelize = new Sequelize({
|
||||
dialect: 'mysql',
|
||||
host: '129.211.213.226',
|
||||
port: 9527,
|
||||
database: 'nxxmdata',
|
||||
username: 'root',
|
||||
password: 'aiotAiot123!',
|
||||
logging: console.log,
|
||||
pool: {
|
||||
max: 5,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000
|
||||
}
|
||||
});
|
||||
|
||||
async function test() {
|
||||
console.log('测试连接到 nxxmdata 数据库...');
|
||||
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 连接 nxxmdata 数据库成功!');
|
||||
|
||||
// 列出所有表
|
||||
const [tables] = await sequelize.query('SHOW TABLES');
|
||||
console.log('数据库中的表:', tables.length);
|
||||
|
||||
// 检查是否有 users 表
|
||||
const [userTables] = await sequelize.query("SHOW TABLES LIKE 'users'");
|
||||
if (userTables.length > 0) {
|
||||
console.log('✅ 找到 users 表');
|
||||
|
||||
// 查询用户数据
|
||||
const [users] = await sequelize.query('SELECT id, username, email FROM users LIMIT 5');
|
||||
console.log('用户数据:', users);
|
||||
} else {
|
||||
console.log('❌ 没有找到 users 表');
|
||||
}
|
||||
|
||||
await sequelize.close();
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('❌ 连接失败:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
test();
|
||||
|
||||
94
insurance_backend/utils/excelExport.js
Normal file
94
insurance_backend/utils/excelExport.js
Normal file
@@ -0,0 +1,94 @@
|
||||
const ExcelJS = require('exceljs');
|
||||
|
||||
/**
|
||||
* Excel导出工具类
|
||||
*/
|
||||
class ExcelExport {
|
||||
/**
|
||||
* 创建Excel工作簿并导出数据
|
||||
* @param {Array} data - 要导出的数据
|
||||
* @param {Array} columns - 列定义 [{ header: '列名', key: '字段名', width: 宽度 }]
|
||||
* @param {String} sheetName - 工作表名称
|
||||
* @returns {Promise<Buffer>} Excel文件Buffer
|
||||
*/
|
||||
static async exportToExcel(data, columns, sheetName = 'Sheet1') {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet(sheetName);
|
||||
|
||||
// 设置列
|
||||
worksheet.columns = columns;
|
||||
|
||||
// 设置表头样式
|
||||
worksheet.getRow(1).eachCell((cell) => {
|
||||
cell.font = { bold: true, color: { argb: 'FFFFFFFF' } };
|
||||
cell.fill = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FF4472C4' }
|
||||
};
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||
});
|
||||
|
||||
// 添加数据
|
||||
data.forEach((item) => {
|
||||
worksheet.addRow(item);
|
||||
});
|
||||
|
||||
// 自动调整列宽
|
||||
worksheet.columns.forEach((column) => {
|
||||
if (!column.width) {
|
||||
const maxLength = Math.max(
|
||||
column.header.length,
|
||||
...worksheet.getColumn(column.key).values.map(v =>
|
||||
v ? String(v).length : 0
|
||||
)
|
||||
);
|
||||
column.width = Math.min(Math.max(maxLength + 2, 10), 50);
|
||||
}
|
||||
});
|
||||
|
||||
// 生成Excel文件Buffer
|
||||
const buffer = await workbook.xlsx.writeBuffer();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日期
|
||||
* @param {Date|String} date - 日期
|
||||
* @returns {String} 格式化后的日期字符串
|
||||
*/
|
||||
static formatDate(date) {
|
||||
if (!date) return '';
|
||||
const d = new Date(date);
|
||||
const year = d.getFullYear();
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
const hours = String(d.getHours()).padStart(2, '0');
|
||||
const minutes = String(d.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(d.getSeconds()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化金额
|
||||
* @param {Number} amount - 金额
|
||||
* @returns {String} 格式化后的金额字符串
|
||||
*/
|
||||
static formatAmount(amount) {
|
||||
if (!amount && amount !== 0) return '';
|
||||
return `¥${Number(amount).toFixed(2)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化状态
|
||||
* @param {String} status - 状态值
|
||||
* @param {Object} statusMap - 状态映射表
|
||||
* @returns {String} 状态文本
|
||||
*/
|
||||
static formatStatus(status, statusMap) {
|
||||
return statusMap[status] || status || '';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExcelExport;
|
||||
|
||||
Reference in New Issue
Block a user