Compare commits
72 Commits
Author | SHA1 | Date |
---|---|---|
|
895bfcbeaf | |
|
7f654625aa | |
|
864f926f2e | |
|
69b4bf86bd | |
|
48f4020fc8 | |
|
0eb12b4df9 | |
|
77238c3d03 | |
|
7e006acaa6 | |
|
2065389094 | |
|
4972dcaf4d | |
|
c7043dbdb1 | |
|
02c705ae3d | |
|
69ff1bba7d | |
|
c47c8e48aa | |
|
c64166a7c0 | |
|
1307eaf651 | |
|
d25a34e579 | |
|
fcd9604272 | |
|
4ca2696896 | |
|
11c9d33f94 | |
|
2492a57b88 | |
|
a72aaafdfb | |
|
1648f3df96 | |
|
c76d6beeb0 | |
|
2bf9c6a5eb | |
|
ca1c3947c1 | |
|
011e1c5337 | |
|
7f8ccb76cb | |
|
b8ffc08513 | |
|
23fb041a08 | |
|
cc65882b8d | |
|
54d9557a89 | |
|
327064b7e5 | |
|
1d96aad163 | |
|
64a82a6775 | |
|
124a04c43d | |
|
1dc321ba5c | |
|
1feb4832c5 | |
|
1f56b0a30f | |
|
db047b4e60 | |
|
9741192bee | |
|
8c202c45dc | |
|
38d888f24e | |
|
fae3b7b05d | |
|
236cf1489b | |
|
0108dd6068 | |
|
5b673f9525 | |
|
708e8b47d1 | |
|
7728cfd8aa | |
|
fe6e84f5f7 | |
|
70fca26e10 | |
|
7f24af3272 | |
|
cf780323e0 | |
|
b028f2a7bd | |
|
15d50b997d | |
|
c3fb37aa6b | |
|
b9b1da2186 | |
|
b7e5a4a82f | |
|
d669624fbe | |
|
f8aeb0caac | |
|
afe4a6d5ba | |
|
ab8afb02eb | |
|
30eca5da60 | |
|
a6b7b343a9 | |
|
253b6ffcca | |
|
a3b11c9971 | |
|
ba041b3f3a | |
|
e6ad6ad6fb | |
|
3378ab4a49 | |
|
41b9474300 | |
|
edb8aad10f | |
|
55ebab5b69 |
|
@ -2,6 +2,5 @@
|
|||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/Industrial-image-management-system---web" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,91 @@
|
|||
# 制度类型搜索接口实现指南
|
||||
|
||||
## 接口定义
|
||||
|
||||
### 请求接口
|
||||
```
|
||||
GET /api/regulation/types
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### 请求参数
|
||||
```json
|
||||
{
|
||||
"page": number, // 页码(可选,默认1)
|
||||
"size": number, // 每页大小(可选,默认10)
|
||||
"typeName": "string", // 类型名称(模糊搜索,可选)
|
||||
"status": "string", // 状态筛选("1"启用,"0"禁用,可选)
|
||||
"remark": "string" // 备注内容(模糊搜索,可选)
|
||||
}
|
||||
```
|
||||
|
||||
### 响应格式
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"typeId": "string",
|
||||
"typeName": "string",
|
||||
"sortOrder": number,
|
||||
"isEnabled": "string",
|
||||
"remark": "string",
|
||||
"createBy": "string",
|
||||
"createTime": "string",
|
||||
"updateBy": "string",
|
||||
"updateTime": "string",
|
||||
"delFlag": "string"
|
||||
}
|
||||
],
|
||||
"total": number, // 总记录数
|
||||
"current": number, // 当前页码
|
||||
"size": number, // 每页大小
|
||||
"pages": number // 总页数
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 后端实现说明
|
||||
|
||||
后端已实现以下接口:
|
||||
```java
|
||||
@ApiOperation(value = "获取制度类型列表", httpMethod = "GET")
|
||||
@GetMapping
|
||||
public Result getRegulationTypes(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String typeName,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String remark
|
||||
) {
|
||||
return regulationTypeService.getRegulationTypes(page, size, typeName, status, remark);
|
||||
}
|
||||
```
|
||||
|
||||
## 前端集成说明
|
||||
|
||||
前端已完成以下功能:
|
||||
1. ✅ 调整为GET请求接口
|
||||
2. ✅ 参数名匹配后端接口(isEnabled → status)
|
||||
3. ✅ 移除排序参数(后端不支持)
|
||||
4. ✅ 简化搜索表单,只支持手动搜索
|
||||
5. ✅ 保持原有功能不受影响
|
||||
|
||||
## 搜索流程
|
||||
|
||||
1. 用户在搜索表单中输入条件
|
||||
2. 点击"搜索"按钮触发搜索
|
||||
3. 调用后端GET接口 `/api/regulation/types`
|
||||
4. 后端返回搜索结果
|
||||
5. 前端展示搜索结果
|
||||
|
||||
## 参数说明
|
||||
|
||||
- **page**: 页码,默认1
|
||||
- **size**: 每页大小,默认10
|
||||
- **typeName**: 类型名称,支持模糊搜索
|
||||
- **status**: 状态筛选,"1"表示启用,"0"表示禁用
|
||||
- **remark**: 备注内容,支持模糊搜索
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
# 设备中心模块问题修复和改进总结
|
||||
|
||||
## 发现的问题
|
||||
|
||||
### 1. 弹窗组件问题
|
||||
- **缺少查看模式**:原弹窗组件没有区分查看、编辑、新增模式
|
||||
- **表单验证不完善**:缺少详细的验证规则和错误处理
|
||||
- **缺少加载状态**:提交时没有loading状态管理
|
||||
- **表单重置逻辑不完善**:取消时没有正确重置表单
|
||||
- **缺少调试功能**:开发环境下缺少调试工具
|
||||
|
||||
### 2. 主页面问题
|
||||
- **数据转换逻辑复杂**:存在过多的兼容性处理,可能导致数据不一致
|
||||
- **错误处理不够详细**:错误信息不够具体
|
||||
- **缺少详情页面跳转**:没有设备详情页面的入口
|
||||
- **对象引用问题**:直接传递对象引用可能导致数据污染
|
||||
|
||||
### 3. 功能缺失
|
||||
- **缺少设备详情页面**:没有专门的设备详情展示页面
|
||||
- **缺少维护记录管理**:没有设备维护记录的展示和管理
|
||||
- **缺少使用记录**:没有设备使用历史的展示
|
||||
- **缺少文件管理**:没有设备相关文件的管理功能
|
||||
|
||||
## 修复和改进内容
|
||||
|
||||
### 1. 弹窗组件优化 (`DeviceModal.vue`)
|
||||
|
||||
#### 新增功能
|
||||
- ✅ **查看模式支持**:添加了查看模式,禁用所有输入框
|
||||
- ✅ **完善的表单验证**:添加了详细的验证规则和长度限制
|
||||
- ✅ **加载状态管理**:添加了提交时的loading状态
|
||||
- ✅ **表单重置优化**:完善了表单重置逻辑
|
||||
- ✅ **调试功能**:开发环境下添加了调试按钮
|
||||
- ✅ **错误处理优化**:更详细的错误信息处理
|
||||
|
||||
#### 技术改进
|
||||
- ✅ **响应式表单数据**:使用reactive管理表单数据
|
||||
- ✅ **计算属性优化**:添加了表单有效性计算
|
||||
- ✅ **监听器优化**:优化了数据变化监听逻辑
|
||||
- ✅ **类型安全**:完善了TypeScript类型定义
|
||||
|
||||
### 2. 主页面优化 (`index.vue`)
|
||||
|
||||
#### 功能改进
|
||||
- ✅ **数据转换优化**:简化了数据转换逻辑,提高一致性
|
||||
- ✅ **错误处理增强**:添加了更详细的错误信息处理
|
||||
- ✅ **详情页面跳转**:添加了设备详情页面的跳转功能
|
||||
- ✅ **对象深拷贝**:使用展开运算符避免对象引用问题
|
||||
|
||||
#### 用户体验改进
|
||||
- ✅ **删除确认优化**:添加了更明确的删除确认提示
|
||||
- ✅ **操作反馈优化**:改进了操作成功/失败的提示信息
|
||||
|
||||
### 3. 新增设备详情页面 (`detail.vue`)
|
||||
|
||||
#### 功能特性
|
||||
- ✅ **基本信息展示**:设备的基本信息展示
|
||||
- ✅ **状态信息展示**:设备的各种状态信息
|
||||
- ✅ **维护记录管理**:设备维护记录的展示和管理
|
||||
- ✅ **使用记录展示**:设备使用历史的展示
|
||||
- ✅ **位置变更记录**:设备位置变更历史
|
||||
- ✅ **文件管理**:设备相关文件的上传和管理
|
||||
|
||||
#### 技术实现
|
||||
- ✅ **响应式数据管理**:使用ref管理页面数据
|
||||
- ✅ **路由参数处理**:正确处理路由参数获取设备ID
|
||||
- ✅ **API集成**:集成设备详情API
|
||||
- ✅ **状态文本转换**:统一的状态文本转换函数
|
||||
|
||||
### 4. 路由配置优化
|
||||
|
||||
#### 新增路由
|
||||
- ✅ **设备详情路由**:添加了设备详情页面的路由配置
|
||||
- ✅ **参数传递**:支持通过URL参数传递设备ID
|
||||
|
||||
## 参考training模块的实现
|
||||
|
||||
### 借鉴的设计模式
|
||||
1. **弹窗组件设计**:参考了TrainingPlanModal的弹窗设计模式
|
||||
2. **表单验证机制**:采用了相同的表单验证和错误处理机制
|
||||
3. **数据管理方式**:使用了相同的响应式数据管理方式
|
||||
4. **调试功能**:借鉴了开发环境下的调试工具设计
|
||||
5. **详情页面设计**:参考了TrainingDetail页面的布局和功能设计
|
||||
|
||||
### 技术实现对比
|
||||
| 功能 | Training模块 | 设备管理模块 | 改进状态 |
|
||||
|------|-------------|-------------|----------|
|
||||
| 弹窗模式 | 查看/编辑/新增 | 查看/编辑/新增 | ✅ 已实现 |
|
||||
| 表单验证 | 完善 | 完善 | ✅ 已实现 |
|
||||
| 加载状态 | 有 | 有 | ✅ 已实现 |
|
||||
| 调试功能 | 有 | 有 | ✅ 已实现 |
|
||||
| 详情页面 | 有 | 有 | ✅ 已实现 |
|
||||
| 错误处理 | 详细 | 详细 | ✅ 已实现 |
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 1. 设备列表页面
|
||||
- **搜索功能**:支持按设备名称、类型、状态等条件搜索
|
||||
- **新增设备**:点击"新增设备"按钮打开新增弹窗
|
||||
- **查看设备**:点击"查看"按钮以只读模式查看设备信息
|
||||
- **编辑设备**:点击"编辑"按钮修改设备信息
|
||||
- **详情页面**:点击"详情"按钮跳转到设备详情页面
|
||||
- **设备操作**:支持分配、归还、删除等操作
|
||||
|
||||
### 2. 设备详情页面
|
||||
- **基本信息**:展示设备的基本信息
|
||||
- **状态信息**:展示设备的各种状态
|
||||
- **维护记录**:查看和管理设备维护记录
|
||||
- **使用记录**:查看设备使用历史
|
||||
- **位置变更**:查看设备位置变更历史
|
||||
- **文件管理**:上传和管理设备相关文件
|
||||
|
||||
### 3. 开发调试
|
||||
- **调试按钮**:开发环境下显示调试按钮
|
||||
- **表单测试**:可以测试表单数据绑定
|
||||
- **测试数据**:可以填充测试数据
|
||||
- **数据清空**:可以清空表单数据
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
### 1. 功能扩展
|
||||
- [ ] 添加设备维护记录的增删改功能
|
||||
- [ ] 实现设备使用记录的实时更新
|
||||
- [ ] 添加设备状态变更的审批流程
|
||||
- [ ] 实现设备文件的在线预览功能
|
||||
|
||||
### 2. 性能优化
|
||||
- [ ] 添加数据缓存机制
|
||||
- [ ] 实现分页加载优化
|
||||
- [ ] 添加数据预加载功能
|
||||
|
||||
### 3. 用户体验
|
||||
- [ ] 添加操作确认的快捷键支持
|
||||
- [ ] 实现批量操作功能
|
||||
- [ ] 添加数据导出功能
|
||||
- [ ] 实现高级搜索功能
|
||||
|
||||
## 总结
|
||||
|
||||
通过参考training模块的实现,成功修复了设备中心模块的主要问题,并添加了缺失的功能。主要改进包括:
|
||||
|
||||
1. **弹窗组件**:完善了查看、编辑、新增模式,添加了完善的表单验证和错误处理
|
||||
2. **主页面**:优化了数据转换逻辑,改进了错误处理,添加了详情页面跳转
|
||||
3. **详情页面**:新增了完整的设备详情展示页面,包含维护记录、使用记录等功能
|
||||
4. **路由配置**:添加了设备详情页面的路由配置
|
||||
|
||||
这些改进大大提升了设备中心模块的功能完整性和用户体验,使其与training模块保持了一致的设计标准和实现质量。
|
|
@ -0,0 +1,125 @@
|
|||
# 制度公告搜索接口实现指南
|
||||
|
||||
## 接口定义
|
||||
|
||||
### 请求接口
|
||||
```
|
||||
GET /api/regulation
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### 请求参数
|
||||
```json
|
||||
{
|
||||
"page": number, // 页码(可选,默认1)
|
||||
"size": number, // 每页大小(可选,默认10)
|
||||
"status": "string", // 状态筛选(精确匹配,固定为"PUBLISHED")
|
||||
"title": "string", // 制度标题(模糊搜索,可选)
|
||||
"proposer": "string", // 公示人(模糊搜索,可选)
|
||||
"confirmStatus": "string" // 确认状态(精确匹配,可选)
|
||||
}
|
||||
```
|
||||
|
||||
### 响应格式
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"regulationId": "string",
|
||||
"title": "string",
|
||||
"content": "string",
|
||||
"regulationType": "string",
|
||||
"status": "string",
|
||||
"publishTime": "string",
|
||||
"effectiveTime": "string",
|
||||
"expireTime": "string",
|
||||
"scope": "string",
|
||||
"level": "string",
|
||||
"version": "string",
|
||||
"remark": "string",
|
||||
"createBy": "string",
|
||||
"updateBy": "string",
|
||||
"createTime": "string",
|
||||
"updateTime": "string",
|
||||
"delFlag": "string",
|
||||
"confirmStatus": "string"
|
||||
}
|
||||
],
|
||||
"total": number, // 总记录数
|
||||
"current": number, // 当前页码
|
||||
"size": number, // 每页大小
|
||||
"pages": number // 总页数
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 后端实现说明
|
||||
|
||||
后端已实现以下接口:
|
||||
```java
|
||||
@ApiOperation(value = "获取制度列表", httpMethod = "GET")
|
||||
@GetMapping
|
||||
public Result getRegulationList(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) String title,
|
||||
@RequestParam(required = false) String proposer,
|
||||
@RequestParam(required = false) String confirmStatus
|
||||
) {
|
||||
return regulationService.getRegulationList(page, size, status, type, title, proposer, confirmStatus);
|
||||
}
|
||||
```
|
||||
|
||||
## 前端集成说明
|
||||
|
||||
前端已完成以下功能:
|
||||
1. ✅ 删除前端搜索逻辑(防抖、实时搜索等)
|
||||
2. ✅ 调整为GET请求接口
|
||||
3. ✅ 参数名匹配后端接口(createByName → proposer)
|
||||
4. ✅ 简化搜索表单,只支持手动搜索
|
||||
5. ✅ 移除前端过滤逻辑,由后端处理状态筛选
|
||||
6. ✅ 保持原有功能不受影响
|
||||
|
||||
## 搜索流程
|
||||
|
||||
1. 用户在搜索表单中输入条件
|
||||
2. 点击"搜索"按钮触发搜索
|
||||
3. 调用后端GET接口 `/api/regulation`
|
||||
4. 后端返回搜索结果(已过滤为PUBLISHED状态的制度)
|
||||
5. 前端展示搜索结果
|
||||
|
||||
## 参数说明
|
||||
|
||||
- **page**: 页码,默认1
|
||||
- **size**: 每页大小,默认10
|
||||
- **status**: 状态筛选,固定为"PUBLISHED"(已公告状态)
|
||||
- **title**: 制度标题,支持模糊搜索
|
||||
- **proposer**: 公示人,支持模糊搜索
|
||||
- **confirmStatus**: 确认状态,精确匹配(如:confirmed、pending)
|
||||
|
||||
## 搜索功能特性
|
||||
|
||||
- **模糊搜索**:title 和 proposer 字段支持模糊匹配
|
||||
- **精确筛选**:confirmStatus 字段精确匹配
|
||||
- **分页查询**:支持分页和排序
|
||||
- **状态过滤**:后端自动过滤为PUBLISHED状态的制度
|
||||
- **性能优化**:使用数据库索引提升查询性能
|
||||
|
||||
## 业务逻辑说明
|
||||
|
||||
制度公告页面专门用于展示已经公告的制度,因此:
|
||||
- 后端需要自动过滤为 `status = 'PUBLISHED'` 的制度
|
||||
- 支持按确认状态(confirmStatus)进行筛选
|
||||
- 前端不再需要手动过滤,完全依赖后端处理
|
||||
- 用户可以查看制度详情、下载PDF文件、确认知晓制度
|
||||
|
||||
## 确认状态说明
|
||||
|
||||
- **confirmed**: 已确认 - 用户已确认知晓并遵守该制度
|
||||
- **pending**: 待确认 - 用户尚未确认知晓该制度
|
||||
- 空值: 全部 - 显示所有确认状态的制度
|
|
@ -0,0 +1,117 @@
|
|||
# 制度公示搜索接口实现指南
|
||||
|
||||
## 接口定义
|
||||
|
||||
### 请求接口
|
||||
```
|
||||
GET /api/regulation
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### 请求参数
|
||||
```json
|
||||
{
|
||||
"page": number, // 页码(可选,默认1)
|
||||
"size": number, // 每页大小(可选,默认10)
|
||||
"status": "string", // 状态筛选(精确匹配,可选)
|
||||
"type": "string", // 提案类型(精确匹配,可选)
|
||||
"title": "string", // 提案标题(模糊搜索,可选)
|
||||
"proposer": "string" // 提案人(模糊搜索,可选)
|
||||
}
|
||||
```
|
||||
|
||||
### 响应格式
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"regulationId": "string",
|
||||
"title": "string",
|
||||
"content": "string",
|
||||
"regulationType": "string",
|
||||
"status": "string",
|
||||
"publishTime": "string",
|
||||
"effectiveTime": "string",
|
||||
"expireTime": "string",
|
||||
"scope": "string",
|
||||
"level": "string",
|
||||
"version": "string",
|
||||
"remark": "string",
|
||||
"createBy": "string",
|
||||
"updateBy": "string",
|
||||
"createTime": "string",
|
||||
"updateTime": "string",
|
||||
"delFlag": "string",
|
||||
"confirmStatus": "string"
|
||||
}
|
||||
],
|
||||
"total": number, // 总记录数
|
||||
"current": number, // 当前页码
|
||||
"size": number, // 每页大小
|
||||
"pages": number // 总页数
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 后端实现说明
|
||||
|
||||
后端已实现以下接口:
|
||||
```java
|
||||
@ApiOperation(value = "获取制度列表", httpMethod = "GET")
|
||||
@GetMapping
|
||||
public Result getRegulationList(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) String title,
|
||||
@RequestParam(required = false) String proposer
|
||||
) {
|
||||
return regulationService.getRegulationList(page, size, status, type, title, proposer);
|
||||
}
|
||||
```
|
||||
|
||||
## 前端集成说明
|
||||
|
||||
前端已完成以下功能:
|
||||
1. ✅ 删除前端搜索逻辑(防抖、实时搜索等)
|
||||
2. ✅ 调整为GET请求接口
|
||||
3. ✅ 参数名匹配后端接口(createByName → proposer, regulationType → type)
|
||||
4. ✅ 简化搜索表单,只支持手动搜索
|
||||
5. ✅ 移除前端过滤逻辑,由后端处理状态筛选
|
||||
6. ✅ 保持原有功能不受影响
|
||||
|
||||
## 搜索流程
|
||||
|
||||
1. 用户在搜索表单中输入条件
|
||||
2. 点击"搜索"按钮触发搜索
|
||||
3. 调用后端GET接口 `/api/regulation`
|
||||
4. 后端返回搜索结果(已过滤掉草稿状态的提案)
|
||||
5. 前端展示搜索结果
|
||||
|
||||
## 参数说明
|
||||
|
||||
- **page**: 页码,默认1
|
||||
- **size**: 每页大小,默认10
|
||||
- **status**: 状态筛选,精确匹配(如:PUBLISHED、APPROVED等)
|
||||
- **type**: 提案类型,精确匹配(如:管理规范、操作流程、安全制度、其他)
|
||||
- **title**: 提案标题,支持模糊搜索
|
||||
- **proposer**: 提案人,支持模糊搜索
|
||||
|
||||
## 搜索功能特性
|
||||
|
||||
- **模糊搜索**:title 和 proposer 字段支持模糊匹配
|
||||
- **精确筛选**:type 和 status 字段精确匹配
|
||||
- **分页查询**:支持分页和排序
|
||||
- **状态过滤**:后端自动过滤掉草稿状态的提案,只显示已公示及以上的提案
|
||||
- **性能优化**:使用数据库索引提升查询性能
|
||||
|
||||
## 业务逻辑说明
|
||||
|
||||
制度公示页面专门用于展示已经公示或已通过的制度提案,因此:
|
||||
- 后端需要自动过滤掉 `status = 'DRAFT'` 的提案
|
||||
- 只返回 `status = 'PUBLISHED'` 或 `status = 'APPROVED'` 的提案
|
||||
- 前端不再需要手动过滤,完全依赖后端处理
|
|
@ -0,0 +1,108 @@
|
|||
# 制度提案搜索接口实现指南
|
||||
|
||||
## 接口定义
|
||||
|
||||
### 请求接口
|
||||
```
|
||||
GET /api/regulation
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### 请求参数
|
||||
```json
|
||||
{
|
||||
"page": number, // 页码(可选,默认1)
|
||||
"size": number, // 每页大小(可选,默认10)
|
||||
"status": "string", // 状态筛选(精确匹配,可选)
|
||||
"type": "string", // 提案类型(精确匹配,可选)
|
||||
"title": "string", // 提案标题(模糊搜索,可选)
|
||||
"proposer": "string" // 提案人(模糊搜索,可选)
|
||||
}
|
||||
```
|
||||
|
||||
### 响应格式
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"regulationId": "string",
|
||||
"title": "string",
|
||||
"content": "string",
|
||||
"regulationType": "string",
|
||||
"status": "string",
|
||||
"publishTime": "string",
|
||||
"effectiveTime": "string",
|
||||
"expireTime": "string",
|
||||
"scope": "string",
|
||||
"level": "string",
|
||||
"version": "string",
|
||||
"remark": "string",
|
||||
"createBy": "string",
|
||||
"updateBy": "string",
|
||||
"createTime": "string",
|
||||
"updateTime": "string",
|
||||
"delFlag": "string",
|
||||
"confirmStatus": "string"
|
||||
}
|
||||
],
|
||||
"total": number, // 总记录数
|
||||
"current": number, // 当前页码
|
||||
"size": number, // 每页大小
|
||||
"pages": number // 总页数
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 后端实现说明
|
||||
|
||||
后端已实现以下接口:
|
||||
```java
|
||||
@ApiOperation(value = "获取制度列表", httpMethod = "GET")
|
||||
@GetMapping
|
||||
public Result getRegulationList(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) String title,
|
||||
@RequestParam(required = false) String proposer
|
||||
) {
|
||||
return regulationService.getRegulationList(page, size, status, type, title, proposer);
|
||||
}
|
||||
```
|
||||
|
||||
## 前端集成说明
|
||||
|
||||
前端已完成以下功能:
|
||||
1. ✅ 删除前端搜索逻辑(防抖、实时搜索等)
|
||||
2. ✅ 调整为GET请求接口
|
||||
3. ✅ 参数名匹配后端接口(createByName → proposer, regulationType → type)
|
||||
4. ✅ 简化搜索表单,只支持手动搜索
|
||||
5. ✅ 保持原有功能不受影响
|
||||
|
||||
## 搜索流程
|
||||
|
||||
1. 用户在搜索表单中输入条件
|
||||
2. 点击"搜索"按钮触发搜索
|
||||
3. 调用后端GET接口 `/api/regulation`
|
||||
4. 后端返回搜索结果
|
||||
5. 前端展示搜索结果
|
||||
|
||||
## 参数说明
|
||||
|
||||
- **page**: 页码,默认1
|
||||
- **size**: 每页大小,默认10
|
||||
- **status**: 状态筛选,精确匹配(如:DRAFT、PUBLISHED、APPROVED等)
|
||||
- **type**: 提案类型,精确匹配(如:管理规范、操作流程、安全制度、其他)
|
||||
- **title**: 提案标题,支持模糊搜索
|
||||
- **proposer**: 提案人,支持模糊搜索
|
||||
|
||||
## 搜索功能特性
|
||||
|
||||
- **模糊搜索**:title 和 proposer 字段支持模糊匹配
|
||||
- **精确筛选**:type 和 status 字段精确匹配
|
||||
- **分页查询**:支持分页和排序
|
||||
- **性能优化**:使用数据库索引提升查询性能
|
|
@ -8,7 +8,7 @@ export default function appInfo(): Plugin {
|
|||
apply: 'serve',
|
||||
async buildStart() {
|
||||
const { bold, green, cyan, bgGreen, underline } = picocolors
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
boxen(
|
||||
`${bold(green(`${bgGreen('ContiNew Admin v4.0.0-SNAPSHOT')}`))}\n${cyan('在线文档:')}${underline('https://continew.top')}\n${cyan('常见问题:')}${underline('https://continew.top/admin/faq.html')}\n${cyan('持续迭代优化的前后端分离中后台管理系统框架。')}`,
|
||||
|
|
|
@ -35,7 +35,7 @@ export default antfu(
|
|||
rules: {
|
||||
'curly': ['off', 'all'], // 对所有控制语句强制使用一致的大括号样式
|
||||
'no-new': 'off', // 不允许在赋值或比较之外使用 new 运算符
|
||||
'no-console': 'off', // 允许使用 console
|
||||
// 'no-console': 'error', // 禁止使用 console
|
||||
'style/arrow-parens': ['error', 'always'], // 箭头函数参数需要括号
|
||||
'style/brace-style': ['error', '1tbs', { allowSingleLine: true }], // 对块执行一致的大括号样式
|
||||
'regexp/no-unused-capturing-group': 'off',
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -34,7 +34,9 @@
|
|||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.4",
|
||||
"echarts": "^5.4.2",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"jspdf": "^3.0.1",
|
||||
"lint-staged": "^15.2.10",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.0",
|
||||
|
|
152
pnpm-lock.yaml
152
pnpm-lock.yaml
|
@ -68,9 +68,15 @@ importers:
|
|||
echarts:
|
||||
specifier: ^5.4.2
|
||||
version: 5.5.0
|
||||
html2canvas:
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1
|
||||
jsencrypt:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.2
|
||||
jspdf:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
lint-staged:
|
||||
specifier: ^15.2.10
|
||||
version: 15.2.10
|
||||
|
@ -463,6 +469,10 @@ packages:
|
|||
resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/runtime@7.28.2':
|
||||
resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/template@7.24.0':
|
||||
resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
@ -1192,12 +1202,18 @@ packages:
|
|||
resolution: {integrity: sha512-yuIv/WRffRzL7cBW+sla4HwBZrEXRNf1MKQ5SklPEadth+BKbDxiVG8A3iISN5B3yC4EeSCzMZP8llHTcUhOzQ==}
|
||||
deprecated: This is a stub types definition. query-string provides its own type definitions, so you do not need this installed.
|
||||
|
||||
'@types/raf@3.4.3':
|
||||
resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==}
|
||||
|
||||
'@types/sortablejs@1.15.8':
|
||||
resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==}
|
||||
|
||||
'@types/svgo@2.6.4':
|
||||
resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
|
||||
'@types/unist@2.0.10':
|
||||
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
|
||||
|
||||
|
@ -1608,6 +1624,10 @@ packages:
|
|||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
base64-arraybuffer@1.0.2:
|
||||
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
base@0.11.2:
|
||||
resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -1652,6 +1672,11 @@ packages:
|
|||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
hasBin: true
|
||||
|
||||
btoa@1.2.1:
|
||||
resolution: {integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
|
@ -1692,6 +1717,10 @@ packages:
|
|||
caniuse-lite@1.0.30001620:
|
||||
resolution: {integrity: sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==}
|
||||
|
||||
canvg@3.0.11:
|
||||
resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
capital-case@1.0.4:
|
||||
resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
|
||||
|
||||
|
@ -1883,6 +1912,9 @@ packages:
|
|||
crypto-js@4.2.0:
|
||||
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
|
||||
|
||||
css-line-break@2.1.0:
|
||||
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
|
||||
|
||||
css-select@4.3.0:
|
||||
resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
|
||||
|
||||
|
@ -2059,6 +2091,9 @@ packages:
|
|||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
dompurify@3.2.6:
|
||||
resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==}
|
||||
|
||||
domutils@1.7.0:
|
||||
resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
|
||||
|
||||
|
@ -2612,6 +2647,9 @@ packages:
|
|||
fastq@1.17.1:
|
||||
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
|
||||
|
||||
fflate@0.8.2:
|
||||
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
@ -2865,6 +2903,10 @@ packages:
|
|||
resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
html2canvas@1.4.1:
|
||||
resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
htmlparser2@3.10.1:
|
||||
resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
|
||||
|
||||
|
@ -3168,6 +3210,9 @@ packages:
|
|||
jsonfile@6.1.0:
|
||||
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||
|
||||
jspdf@3.0.1:
|
||||
resolution: {integrity: sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==}
|
||||
|
||||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
|
@ -3633,6 +3678,9 @@ packages:
|
|||
perfect-debounce@1.0.0:
|
||||
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
|
||||
|
||||
performance-now@2.1.0:
|
||||
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
|
||||
|
||||
picocolors@1.0.1:
|
||||
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
|
||||
|
||||
|
@ -3814,6 +3862,9 @@ packages:
|
|||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
raf@3.4.1:
|
||||
resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
|
||||
|
||||
randombytes@2.1.0:
|
||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||
|
||||
|
@ -3837,6 +3888,9 @@ packages:
|
|||
resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
|
||||
regenerator-runtime@0.13.11:
|
||||
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
||||
|
||||
regenerator-runtime@0.14.1:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
|
||||
|
@ -3911,6 +3965,10 @@ packages:
|
|||
rfdc@1.4.1:
|
||||
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
|
||||
|
||||
rgbcolor@1.0.1:
|
||||
resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==}
|
||||
engines: {node: '>= 0.8.15'}
|
||||
|
||||
rollup@4.17.2:
|
||||
resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
|
@ -4138,6 +4196,10 @@ packages:
|
|||
resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==}
|
||||
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
|
||||
|
||||
stackblur-canvas@2.7.0:
|
||||
resolution: {integrity: sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==}
|
||||
engines: {node: '>=0.1.14'}
|
||||
|
||||
static-extend@0.1.2:
|
||||
resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -4237,6 +4299,10 @@ packages:
|
|||
svg-baker@1.7.0:
|
||||
resolution: {integrity: sha512-nibslMbkXOIkqKVrfcncwha45f97fGuAOn1G99YwnwTj8kF9YiM6XexPcUso97NxOm6GsP0SIvYVIosBis1xLg==}
|
||||
|
||||
svg-pathdata@6.0.3:
|
||||
resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
svg-tags@1.0.0:
|
||||
resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
|
||||
|
||||
|
@ -4274,6 +4340,9 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
text-segmentation@1.0.3:
|
||||
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
|
||||
|
||||
text-table@0.2.0:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||
|
||||
|
@ -4491,6 +4560,9 @@ packages:
|
|||
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
utrie@1.0.2:
|
||||
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
|
||||
|
||||
v-viewer@3.0.13:
|
||||
resolution: {integrity: sha512-T8pgGzlF0ZCHVpD/32OKsD8MlpI6tqYP3n1XLcSjvGQMc0ABn8nJ4AumxvzAKVQrLRWtDTG6qRGAyCPCmi7ceA==}
|
||||
peerDependencies:
|
||||
|
@ -5071,6 +5143,8 @@ snapshots:
|
|||
dependencies:
|
||||
regenerator-runtime: 0.14.1
|
||||
|
||||
'@babel/runtime@7.28.2': {}
|
||||
|
||||
'@babel/template@7.24.0':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.24.2
|
||||
|
@ -5797,12 +5871,18 @@ snapshots:
|
|||
dependencies:
|
||||
query-string: 9.0.0
|
||||
|
||||
'@types/raf@3.4.3':
|
||||
optional: true
|
||||
|
||||
'@types/sortablejs@1.15.8': {}
|
||||
|
||||
'@types/svgo@2.6.4':
|
||||
dependencies:
|
||||
'@types/node': 20.12.12
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
optional: true
|
||||
|
||||
'@types/unist@2.0.10': {}
|
||||
|
||||
'@types/web-bluetooth@0.0.20': {}
|
||||
|
@ -6347,6 +6427,8 @@ snapshots:
|
|||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
base64-arraybuffer@1.0.2: {}
|
||||
|
||||
base@0.11.2:
|
||||
dependencies:
|
||||
cache-base: 1.0.1
|
||||
|
@ -6415,6 +6497,8 @@ snapshots:
|
|||
node-releases: 2.0.14
|
||||
update-browserslist-db: 1.0.16(browserslist@4.23.0)
|
||||
|
||||
btoa@1.2.1: {}
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
builtin-modules@3.3.0: {}
|
||||
|
@ -6458,6 +6542,18 @@ snapshots:
|
|||
|
||||
caniuse-lite@1.0.30001620: {}
|
||||
|
||||
canvg@3.0.11:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@types/raf': 3.4.3
|
||||
core-js: 3.40.0
|
||||
raf: 3.4.1
|
||||
regenerator-runtime: 0.13.11
|
||||
rgbcolor: 1.0.1
|
||||
stackblur-canvas: 2.7.0
|
||||
svg-pathdata: 6.0.3
|
||||
optional: true
|
||||
|
||||
capital-case@1.0.4:
|
||||
dependencies:
|
||||
no-case: 3.0.4
|
||||
|
@ -6674,6 +6770,10 @@ snapshots:
|
|||
|
||||
crypto-js@4.2.0: {}
|
||||
|
||||
css-line-break@2.1.0:
|
||||
dependencies:
|
||||
utrie: 1.0.2
|
||||
|
||||
css-select@4.3.0:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
@ -6843,6 +6943,11 @@ snapshots:
|
|||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
|
||||
dompurify@3.2.6:
|
||||
optionalDependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
optional: true
|
||||
|
||||
domutils@1.7.0:
|
||||
dependencies:
|
||||
dom-serializer: 0.2.2
|
||||
|
@ -7530,6 +7635,8 @@ snapshots:
|
|||
dependencies:
|
||||
reusify: 1.0.4
|
||||
|
||||
fflate@0.8.2: {}
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
dependencies:
|
||||
flat-cache: 4.0.1
|
||||
|
@ -7777,6 +7884,11 @@ snapshots:
|
|||
|
||||
html-tags@3.3.1: {}
|
||||
|
||||
html2canvas@1.4.1:
|
||||
dependencies:
|
||||
css-line-break: 2.1.0
|
||||
text-segmentation: 1.0.3
|
||||
|
||||
htmlparser2@3.10.1:
|
||||
dependencies:
|
||||
domelementtype: 1.3.1
|
||||
|
@ -8039,6 +8151,18 @@ snapshots:
|
|||
optionalDependencies:
|
||||
graceful-fs: 4.2.11
|
||||
|
||||
jspdf@3.0.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
atob: 2.1.2
|
||||
btoa: 1.2.1
|
||||
fflate: 0.8.2
|
||||
optionalDependencies:
|
||||
canvg: 3.0.11
|
||||
core-js: 3.40.0
|
||||
dompurify: 3.2.6
|
||||
html2canvas: 1.4.1
|
||||
|
||||
keyv@4.5.4:
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
|
@ -8548,6 +8672,9 @@ snapshots:
|
|||
|
||||
perfect-debounce@1.0.0: {}
|
||||
|
||||
performance-now@2.1.0:
|
||||
optional: true
|
||||
|
||||
picocolors@1.0.1: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
@ -8764,6 +8891,11 @@ snapshots:
|
|||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
raf@3.4.1:
|
||||
dependencies:
|
||||
performance-now: 2.1.0
|
||||
optional: true
|
||||
|
||||
randombytes@2.1.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
@ -8795,6 +8927,9 @@ snapshots:
|
|||
dependencies:
|
||||
'@eslint-community/regexpp': 4.10.0
|
||||
|
||||
regenerator-runtime@0.13.11:
|
||||
optional: true
|
||||
|
||||
regenerator-runtime@0.14.1: {}
|
||||
|
||||
regex-not@1.0.2:
|
||||
|
@ -8855,6 +8990,9 @@ snapshots:
|
|||
|
||||
rfdc@1.4.1: {}
|
||||
|
||||
rgbcolor@1.0.1:
|
||||
optional: true
|
||||
|
||||
rollup@4.17.2:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
|
@ -9109,6 +9247,9 @@ snapshots:
|
|||
|
||||
stable@0.1.8: {}
|
||||
|
||||
stackblur-canvas@2.7.0:
|
||||
optional: true
|
||||
|
||||
static-extend@0.1.2:
|
||||
dependencies:
|
||||
define-property: 0.2.5
|
||||
|
@ -9225,6 +9366,9 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
svg-pathdata@6.0.3:
|
||||
optional: true
|
||||
|
||||
svg-tags@1.0.0: {}
|
||||
|
||||
svgo@2.8.0:
|
||||
|
@ -9259,6 +9403,10 @@ snapshots:
|
|||
commander: 2.20.3
|
||||
source-map-support: 0.5.21
|
||||
|
||||
text-segmentation@1.0.3:
|
||||
dependencies:
|
||||
utrie: 1.0.2
|
||||
|
||||
text-table@0.2.0: {}
|
||||
|
||||
tippy.js@6.3.7:
|
||||
|
@ -9506,6 +9654,10 @@ snapshots:
|
|||
|
||||
utils-merge@1.0.1: {}
|
||||
|
||||
utrie@1.0.2:
|
||||
dependencies:
|
||||
base64-arraybuffer: 1.0.2
|
||||
|
||||
v-viewer@3.0.13(viewerjs@1.11.6)(vue@3.5.12(typescript@5.0.4)):
|
||||
dependencies:
|
||||
lodash-es: 4.17.21
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore, useUserStore } from '@/stores'
|
||||
|
||||
// 1
|
||||
defineOptions({ name: 'App' })
|
||||
const userStore = useUserStore()
|
||||
const appStore = useAppStore()
|
||||
appStore.initTheme()
|
||||
appStore.initSiteConfig()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.loading-icon {
|
||||
animation: arco-loading-circle 1s infinite cubic-bezier(0,0,1,1);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import type { AttachInfoData, BusinessTypeResult } from './type'
|
||||
import http from '@/utils/http'
|
||||
|
||||
const { request } = http
|
||||
import type { AttachInfoData, BusinessTypeResult } from './type'
|
||||
|
||||
/**
|
||||
* 批量新增附件信息
|
||||
* @param businessType 业务类型
|
||||
* @param files 文件列表
|
||||
* @returns
|
||||
*/
|
||||
export function batchAddAttachment(businessType: string, formData: FormData) {
|
||||
return request<AttachInfoData[]>({
|
||||
|
@ -14,8 +14,8 @@ export function batchAddAttachment(businessType: string, formData: FormData) {
|
|||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -30,8 +30,8 @@ export function addAttachment(formData: FormData) {
|
|||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
|
@ -45,8 +45,8 @@ export function addAttachmentByDefectMarkPic(formData: FormData) {
|
|||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
|
@ -60,8 +60,8 @@ export function addAttachInsurance(formData: FormData) {
|
|||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
|
@ -70,7 +70,7 @@ export function addAttachInsurance(formData: FormData) {
|
|||
export function getAttachBusinessTypes() {
|
||||
return request<BusinessTypeResult>({
|
||||
url: '/common/list/attach-business_type',
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ export function getAttachBusinessTypes() {
|
|||
export function getAttachmentList(businessType: string) {
|
||||
return request<AttachInfoData[]>({
|
||||
url: `/attach-info/list/${businessType}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,6 @@ export function getAttachmentList(businessType: string) {
|
|||
export function deleteAttachment(id: string | number) {
|
||||
return request<boolean>({
|
||||
url: `/attach-info/${id}`,
|
||||
method: 'delete',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
}
|
|
@ -31,4 +31,4 @@ export interface BusinessType {
|
|||
name: string
|
||||
code: string
|
||||
description?: string
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import type { AttendanceRecordReq, AttendanceRecordResp } from './type'
|
||||
import http from '@/utils/http'
|
||||
import type { AttendanceRecordReq, AttendanceRecordResp } from './type'
|
||||
|
||||
const BASE_URL = '/attendance-record'
|
||||
|
||||
|
@ -7,7 +7,7 @@ const BASE_URL = '/attendance-record'
|
|||
export function addAttendanceRecord(data: AttendanceRecordReq) {
|
||||
return http.post<AttendanceRecordResp>(BASE_URL, data, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
/** 新增考勤记录请求体 */
|
||||
export interface AttendanceRecordReq {
|
||||
recordImage?: string
|
||||
recordPosition?: string
|
||||
recordPositionLabel?: string
|
||||
}
|
||||
|
||||
/** 新增考勤记录响应体 */
|
||||
export interface AttendanceRecordResp {
|
||||
code: number
|
||||
data: object
|
||||
msg: string
|
||||
status: number
|
||||
success: boolean
|
||||
}
|
||||
recordImage?: string
|
||||
recordPosition?: string
|
||||
recordPositionLabel?: string
|
||||
}
|
||||
|
||||
/** 新增考勤记录响应体 */
|
||||
export interface AttendanceRecordResp {
|
||||
code: number
|
||||
data: object
|
||||
msg: string
|
||||
status: number
|
||||
success: boolean
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import type * as T from './type'
|
||||
import http from '@/utils/http'
|
||||
import { type ApiMenuItem, convertMenuData } from '@/utils/menuConverter'
|
||||
import { convertMenuData, type ApiMenuItem } from '@/utils/menuConverter'
|
||||
|
||||
export type * from './type'
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import http from '@/utils/http'
|
||||
|
||||
const { request } = http
|
||||
|
||||
// 文件信息
|
||||
export interface KnowledgeFile {
|
||||
id: string
|
||||
name: string
|
||||
size: string
|
||||
type: string
|
||||
uploadTime: string
|
||||
}
|
||||
|
||||
// 文件夹信息
|
||||
export interface KnowledgeFolder {
|
||||
id: string
|
||||
name: string
|
||||
children?: KnowledgeFolder[]
|
||||
}
|
||||
|
||||
// 获取文件夹树
|
||||
export function getFolderTreeApi() {
|
||||
return request<KnowledgeFolder[]>({
|
||||
url: '/knowledge/folders',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 获取文件列表(按文件夹)
|
||||
export function getFilesApi(folderId: string) {
|
||||
return request<KnowledgeFile[]>({
|
||||
url: '/knowledge/files',
|
||||
method: 'get',
|
||||
params: { folderId },
|
||||
})
|
||||
}
|
||||
|
||||
// 创建文件夹
|
||||
export function createFolderApi(data: { name: string; parentId?: string }) {
|
||||
return request({
|
||||
url: '/knowledge/create-folder',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
export function deleteFileApi(fileId: string) {
|
||||
return request({
|
||||
url: `/knowledge/delete-file/${fileId}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
export function downloadFileApi(fileId: string) {
|
||||
return request<Blob>({
|
||||
url: `/knowledge/download/${fileId}`,
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
})
|
||||
}
|
|
@ -66,9 +66,9 @@ export interface DefectTypeResp {
|
|||
|
||||
/** 缺陷类型选项类型 - 用于前端组件 */
|
||||
export interface DefectTypeOption {
|
||||
code: string
|
||||
label: string
|
||||
value: string
|
||||
name?: string // 兼容性字段
|
||||
sort?: number // 兼容性字段
|
||||
code: string;
|
||||
label: string;
|
||||
value: string;
|
||||
name?: string; // 兼容性字段
|
||||
sort?: number; // 兼容性字段
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { CertificationInfo, CertificationListParams, CertificationListResponse, CertificationPageResponse, CertificationReq, SimpleUserInfo } from './type'
|
||||
import http from '@/utils/http'
|
||||
import type { CertificationInfo, CertificationListParams, CertificationListResponse, SimpleUserInfo,CertificationPageResponse, CertificationReq } from './type'
|
||||
|
||||
const { request } = http
|
||||
|
||||
|
@ -11,7 +11,7 @@ export function createCertification(data: CertificationReq) {
|
|||
return request({
|
||||
url: '/certification',
|
||||
method: 'post',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ export function getCertificationList(params: CertificationListParams) {
|
|||
return request<CertificationListResponse>({
|
||||
url: '/certification/list',
|
||||
method: 'get',
|
||||
params,
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ export function getCertificationList(params: CertificationListParams) {
|
|||
export function getCertificationDetail(certificationId: string) {
|
||||
return request<CertificationInfo>({
|
||||
url: `/certification/detail/${certificationId}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ export function updateCertification(certificationId: string, data: Certification
|
|||
return request({
|
||||
url: `/certification/${certificationId}`,
|
||||
method: 'put',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ export function updateCertification(certificationId: string, data: Certification
|
|||
export function deleteCertification(certificationId: string) {
|
||||
return request({
|
||||
url: `/certification/${certificationId}`,
|
||||
method: 'delete',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ export function batchDeleteCertification(ids: string[]) {
|
|||
return request({
|
||||
url: '/certification/batch',
|
||||
method: 'delete',
|
||||
data: { ids },
|
||||
data: { ids }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ export function exportCertification(params: CertificationListParams) {
|
|||
url: '/certification/export',
|
||||
method: 'get',
|
||||
params,
|
||||
responseType: 'blob',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -72,14 +72,14 @@ export function exportCertification(params: CertificationListParams) {
|
|||
export function getUserList() {
|
||||
return request<SimpleUserInfo[]>({
|
||||
url: '/user/list',
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
}
|
||||
// 查询人员资质信息分页列表(新接口)
|
||||
export function getCertificationPage(params: CertificationListParams) {
|
||||
return request<CertificationPageResponse>({
|
||||
url: '/certification/page',
|
||||
method: 'get',
|
||||
params,
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ export interface SimpleUserInfo {
|
|||
userId: string
|
||||
name: string
|
||||
account: string
|
||||
}
|
||||
}
|
||||
export interface CertificationInfo {
|
||||
certificationId?: string
|
||||
certificationCode: string
|
||||
|
@ -56,4 +56,4 @@ export interface CertificationPageResponse {
|
|||
code?: number
|
||||
msg?: string
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
|
@ -5,45 +5,45 @@ const BASE_URL = '/equipment'
|
|||
|
||||
/** @desc 分页查询设备列表 */
|
||||
export function pageEquipment(query: T.EquipmentPageQuery) {
|
||||
return http.get<T.EquipmentResp[]>(`${BASE_URL}/page`, query)
|
||||
return http.get<T.EquipmentResp[]>(`${BASE_URL}/page`, query)
|
||||
}
|
||||
|
||||
/** @desc 查询设备列表 */
|
||||
export function listEquipment(query?: T.EquipmentPageQuery) {
|
||||
return http.get<T.EquipmentResp[]>(`${BASE_URL}/list`, query)
|
||||
return http.get<T.EquipmentResp[]>(`${BASE_URL}/list`, query)
|
||||
}
|
||||
|
||||
/** @desc 查询设备详情 */
|
||||
export function getEquipmentDetail(equipmentId: string) {
|
||||
return http.get<T.EquipmentResp>(`${BASE_URL}/detail/${equipmentId}`)
|
||||
return http.get<T.EquipmentResp>(`${BASE_URL}/detail/${equipmentId}`)
|
||||
}
|
||||
|
||||
/** @desc 新增设备 */
|
||||
export function createEquipment(data: T.EquipmentReq) {
|
||||
return http.post(`${BASE_URL}`, data)
|
||||
return http.post(`${BASE_URL}`, data)
|
||||
}
|
||||
|
||||
/** @desc 更新设备 */
|
||||
export function updateEquipment(equipmentId: string, data: T.EquipmentReq) {
|
||||
return http.put(`${BASE_URL}/${equipmentId}`, data)
|
||||
return http.put(`${BASE_URL}/${equipmentId}`, data)
|
||||
}
|
||||
|
||||
/** @desc 删除设备 */
|
||||
export function deleteEquipment(equipmentId: string) {
|
||||
return http.del(`${BASE_URL}/${equipmentId}`)
|
||||
return http.del(`${BASE_URL}/${equipmentId}`)
|
||||
}
|
||||
|
||||
/** @desc 设备状态变更 */
|
||||
export function changeEquipmentStatus(equipmentId: string, status: string) {
|
||||
return http.put(`${BASE_URL}/${equipmentId}/status`, { status })
|
||||
return http.put(`${BASE_URL}/${equipmentId}/status`, { status })
|
||||
}
|
||||
|
||||
/** @desc 设备分配 */
|
||||
export function assignEquipment(equipmentId: string, userId: string) {
|
||||
return http.put(`${BASE_URL}/${equipmentId}/assign`, { userId })
|
||||
return http.put(`${BASE_URL}/${equipmentId}/assign`, { userId })
|
||||
}
|
||||
|
||||
/** @desc 设备归还 */
|
||||
export function returnEquipment(equipmentId: string) {
|
||||
return http.put(`${BASE_URL}/${equipmentId}/return`)
|
||||
}
|
||||
return http.put(`${BASE_URL}/${equipmentId}/return`)
|
||||
}
|
|
@ -2,206 +2,276 @@
|
|||
* 设备列表查询请求
|
||||
*/
|
||||
export interface EquipmentListReq {
|
||||
/** 设备名称 */
|
||||
equipmentName?: string
|
||||
/** 设备类型 */
|
||||
equipmentType?: string
|
||||
/** 设备状态 */
|
||||
equipmentStatus?: string
|
||||
/** 设备序列号 */
|
||||
equipmentSn?: string
|
||||
/** 资产编号 */
|
||||
assetCode?: string
|
||||
/** 品牌 */
|
||||
brand?: string
|
||||
/** 位置状态 */
|
||||
locationStatus?: string
|
||||
/** 健康状态 */
|
||||
healthStatus?: string
|
||||
/** 负责人 */
|
||||
responsiblePerson?: string
|
||||
/** 使用状态 */
|
||||
useStatus?: string
|
||||
/** 项目ID */
|
||||
projectId?: string
|
||||
/** 使用人ID */
|
||||
userId?: string
|
||||
/** 当前页码 */
|
||||
pageNum?: number
|
||||
/** 每页大小 */
|
||||
pageSize?: number
|
||||
/** 排序字段 */
|
||||
orderBy?: string
|
||||
/** 排序方向 */
|
||||
orderDirection?: string
|
||||
/** 最低价格 */
|
||||
minPrice?: number
|
||||
/** 最高价格 */
|
||||
maxPrice?: number
|
||||
/** 设备名称 */
|
||||
equipmentName?: string
|
||||
/** 设备类型 */
|
||||
equipmentType?: string
|
||||
/** 设备状态 */
|
||||
equipmentStatus?: string
|
||||
/** 设备序列号 */
|
||||
equipmentSn?: string
|
||||
/** 资产编号 */
|
||||
assetCode?: string
|
||||
/** 品牌 */
|
||||
brand?: string
|
||||
/** 位置状态 */
|
||||
locationStatus?: string
|
||||
/** 健康状态 */
|
||||
healthStatus?: string
|
||||
/** 负责人 */
|
||||
responsiblePerson?: string
|
||||
/** 使用状态 */
|
||||
useStatus?: string
|
||||
/** 项目ID */
|
||||
projectId?: string
|
||||
/** 使用人ID */
|
||||
userId?: string
|
||||
/** 设备型号 */
|
||||
equipmentModel?: string
|
||||
/** 配置规格/参数 */
|
||||
specification?: string
|
||||
/** 设备当前物理位置 */
|
||||
physicalLocation?: string
|
||||
/** 供应商名称 */
|
||||
supplierName?: string
|
||||
/** 采购订单号 */
|
||||
purchaseOrder?: string
|
||||
/** 维护人员 */
|
||||
maintenancePerson?: string
|
||||
/** 次户号 */
|
||||
accountNumber?: string
|
||||
/** 数量 */
|
||||
quantity?: number
|
||||
/** 单价 */
|
||||
unitPrice?: number
|
||||
/** 总价 */
|
||||
totalPrice?: number
|
||||
/** 盘点依据 */
|
||||
inventoryBasis?: string
|
||||
/** 动态记录 */
|
||||
dynamicRecord?: string
|
||||
/** 资产备注 */
|
||||
assetRemark?: string
|
||||
/** 采购时间开始 */
|
||||
purchaseTimeStart?: string
|
||||
/** 采购时间结束 */
|
||||
purchaseTimeEnd?: string
|
||||
/** 入库时间开始 */
|
||||
inStockTimeStart?: string
|
||||
/** 入库时间结束 */
|
||||
inStockTimeEnd?: string
|
||||
/** 启用时间开始 */
|
||||
activationTimeStart?: string
|
||||
/** 启用时间结束 */
|
||||
activationTimeEnd?: string
|
||||
/** 当前页码 */
|
||||
pageNum?: number
|
||||
/** 每页大小 */
|
||||
pageSize?: number
|
||||
/** 排序字段 */
|
||||
orderBy?: string
|
||||
/** 排序方向 */
|
||||
orderDirection?: string
|
||||
/** 页码 */
|
||||
page?: number
|
||||
/** 库存条码 */
|
||||
inventoryBarcode?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页响应格式
|
||||
*/
|
||||
export interface PageResult<T> {
|
||||
code: number
|
||||
msg: string
|
||||
rows: T[]
|
||||
total: number
|
||||
code: number
|
||||
msg: string
|
||||
rows: T[]
|
||||
total: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备信息响应
|
||||
*/
|
||||
export interface EquipmentResp {
|
||||
/** 设备ID */
|
||||
equipmentId: string
|
||||
/** 资产编号 */
|
||||
assetCode?: string
|
||||
/** 设备名称 */
|
||||
equipmentName: string
|
||||
/** 设备类型 */
|
||||
equipmentType: string
|
||||
/** 设备类型描述 */
|
||||
equipmentTypeLabel?: string
|
||||
/** 设备型号 */
|
||||
equipmentModel: string
|
||||
/** 设备SN */
|
||||
equipmentSn: string
|
||||
/** 品牌 */
|
||||
brand?: string
|
||||
/** 配置规格/参数 */
|
||||
specification?: string
|
||||
/** 设备状态 */
|
||||
equipmentStatus: string
|
||||
/** 设备状态描述 */
|
||||
equipmentStatusLabel?: string
|
||||
/** 使用状态 */
|
||||
useStatus: string
|
||||
/** 位置状态 */
|
||||
locationStatus?: string
|
||||
/** 位置状态描述 */
|
||||
locationStatusLabel?: string
|
||||
/** 设备当前物理位置 */
|
||||
physicalLocation?: string
|
||||
/** 负责人 */
|
||||
responsiblePerson?: string
|
||||
/** 健康状态 */
|
||||
healthStatus?: string
|
||||
/** 健康状态描述 */
|
||||
healthStatusLabel?: string
|
||||
/** 采购时间 */
|
||||
purchaseTime?: string
|
||||
/** 入库时间 */
|
||||
inStockTime?: string
|
||||
/** 启用时间 */
|
||||
activationTime?: string
|
||||
/** 预计报废时间 */
|
||||
expectedScrapTime?: string
|
||||
/** 实际报废时间 */
|
||||
actualScrapTime?: string
|
||||
/** 状态变更时间 */
|
||||
statusChangeTime?: string
|
||||
/** 采购订单 */
|
||||
purchaseOrder?: string
|
||||
/** 供应商名称 */
|
||||
supplierName?: string
|
||||
/** 采购价格 */
|
||||
purchasePrice?: number
|
||||
/** 当前净值 */
|
||||
currentNetValue?: number
|
||||
/** 折旧方法 */
|
||||
depreciationMethod?: string
|
||||
/** 折旧年限 */
|
||||
depreciationYears?: number
|
||||
/** 残值 */
|
||||
salvageValue?: number
|
||||
/** 保修截止日期 */
|
||||
warrantyExpireDate?: string
|
||||
/** 上次维护日期 */
|
||||
lastMaintenanceDate?: string
|
||||
/** 下次维护日期 */
|
||||
nextMaintenanceDate?: string
|
||||
/** 维护人员 */
|
||||
maintenancePerson?: string
|
||||
/** 库存条码 */
|
||||
inventoryBarcode?: string
|
||||
/** 资产备注 */
|
||||
assetRemark?: string
|
||||
/** 项目ID */
|
||||
projectId?: string
|
||||
/** 项目名称 */
|
||||
projectName?: string
|
||||
/** 使用人ID */
|
||||
userId?: string
|
||||
/** 使用人 */
|
||||
name?: string
|
||||
/** 创建时间 */
|
||||
createTime?: string
|
||||
/** 更新时间 */
|
||||
updateTime?: string
|
||||
/** 设备ID */
|
||||
equipmentId: string
|
||||
/** 资产编号 */
|
||||
assetCode?: string
|
||||
/** 设备名称 */
|
||||
equipmentName: string
|
||||
/** 设备类型 */
|
||||
equipmentType: string
|
||||
/** 设备类型描述 */
|
||||
equipmentTypeLabel?: string
|
||||
/** 设备型号 */
|
||||
equipmentModel: string
|
||||
/** 设备SN */
|
||||
equipmentSn: string
|
||||
/** 品牌 */
|
||||
brand?: string
|
||||
/** 配置规格/参数 */
|
||||
specification?: string
|
||||
/** 设备状态 */
|
||||
equipmentStatus: string
|
||||
/** 设备状态描述 */
|
||||
equipmentStatusLabel?: string
|
||||
/** 使用状态 */
|
||||
useStatus: string
|
||||
/** 位置状态 */
|
||||
locationStatus?: string
|
||||
/** 位置状态描述 */
|
||||
locationStatusLabel?: string
|
||||
/** 设备当前物理位置 */
|
||||
physicalLocation?: string
|
||||
/** 负责人 */
|
||||
responsiblePerson?: string
|
||||
/** 健康状态 */
|
||||
healthStatus?: string
|
||||
/** 健康状态描述 */
|
||||
healthStatusLabel?: string
|
||||
/** 采购时间 */
|
||||
purchaseTime?: string
|
||||
/** 入库时间 */
|
||||
inStockTime?: string
|
||||
/** 启用时间 */
|
||||
activationTime?: string
|
||||
/** 预计报废时间 */
|
||||
expectedScrapTime?: string
|
||||
/** 实际报废时间 */
|
||||
actualScrapTime?: string
|
||||
/** 状态变更时间 */
|
||||
statusChangeTime?: string
|
||||
/** 采购订单 */
|
||||
purchaseOrder?: string
|
||||
/** 供应商名称 */
|
||||
supplierName?: string
|
||||
/** 采购价格 */
|
||||
purchasePrice?: number
|
||||
/** 当前净值 */
|
||||
currentNetValue?: number
|
||||
/** 折旧方法 */
|
||||
depreciationMethod?: string
|
||||
/** 折旧年限 */
|
||||
depreciationYears?: number
|
||||
/** 残值 */
|
||||
salvageValue?: number
|
||||
/** 保修截止日期 */
|
||||
warrantyExpireDate?: string
|
||||
/** 上次维护日期 */
|
||||
lastMaintenanceDate?: string
|
||||
/** 下次维护日期 */
|
||||
nextMaintenanceDate?: string
|
||||
/** 维护人员 */
|
||||
maintenancePerson?: string
|
||||
/** 库存条码 */
|
||||
inventoryBarcode?: string
|
||||
/** 资产备注 */
|
||||
assetRemark?: string
|
||||
/** 项目ID */
|
||||
projectId?: string
|
||||
/** 项目名称 */
|
||||
projectName?: string
|
||||
/** 使用人ID */
|
||||
userId?: string
|
||||
/** 使用人 */
|
||||
name?: string
|
||||
/** 创建时间 */
|
||||
createTime?: string
|
||||
/** 更新时间 */
|
||||
updateTime?: string
|
||||
/** 次户号 */
|
||||
accountNumber?: string
|
||||
/** 数量 */
|
||||
quantity?: number
|
||||
/** 单价 */
|
||||
unitPrice?: number
|
||||
/** 总价 */
|
||||
totalPrice?: number
|
||||
/** 盘点依据 */
|
||||
inventoryBasis?: string
|
||||
/** 动态记录 */
|
||||
dynamicRecord?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备请求
|
||||
*/
|
||||
export interface EquipmentReq {
|
||||
/** 设备名称 */
|
||||
equipmentName: string
|
||||
/** 设备型号 */
|
||||
equipmentModel: string
|
||||
/** 设备类型 */
|
||||
equipmentType: string
|
||||
/** 设备状态 */
|
||||
equipmentStatus: string
|
||||
/** 使用状态 */
|
||||
useStatus: string
|
||||
/** 设备序列号 */
|
||||
equipmentSn: string
|
||||
/** 资产编号 */
|
||||
assetCode?: string
|
||||
/** 品牌 */
|
||||
brand?: string
|
||||
/** 配置规格/参数 */
|
||||
specification?: string
|
||||
/** 位置状态 */
|
||||
locationStatus?: string
|
||||
/** 设备当前物理位置 */
|
||||
physicalLocation?: string
|
||||
/** 负责人 */
|
||||
responsiblePerson?: string
|
||||
/** 健康状态 */
|
||||
healthStatus?: string
|
||||
/** 采购时间 */
|
||||
purchaseTime?: string
|
||||
/** 入库时间 */
|
||||
inStockTime?: string
|
||||
/** 启用时间 */
|
||||
activationTime?: string
|
||||
/** 预计报废时间 */
|
||||
expectedScrapTime?: string
|
||||
/** 实际报废时间 */
|
||||
actualScrapTime?: string
|
||||
/** 采购订单 */
|
||||
purchaseOrder?: string
|
||||
/** 供应商名称 */
|
||||
supplierName?: string
|
||||
/** 采购价格 */
|
||||
purchasePrice?: number
|
||||
/** 当前净值 */
|
||||
currentNetValue?: number
|
||||
/** 折旧方法 */
|
||||
depreciationMethod?: string
|
||||
/** 折旧年限 */
|
||||
depreciationYears?: number
|
||||
/** 残值 */
|
||||
salvageValue?: number
|
||||
/** 保修截止日期 */
|
||||
warrantyExpireDate?: string
|
||||
/** 上次维护日期 */
|
||||
lastMaintenanceDate?: string
|
||||
/** 下次维护日期 */
|
||||
nextMaintenanceDate?: string
|
||||
/** 维护人员 */
|
||||
maintenancePerson?: string
|
||||
/** 库存条码 */
|
||||
inventoryBarcode?: string
|
||||
/** 资产备注 */
|
||||
assetRemark?: string
|
||||
/** 设备名称 */
|
||||
equipmentName: string
|
||||
/** 设备型号 */
|
||||
equipmentModel: string
|
||||
/** 设备类型 */
|
||||
equipmentType: string
|
||||
/** 设备状态 */
|
||||
equipmentStatus: string
|
||||
/** 使用状态 */
|
||||
useStatus: string
|
||||
/** 设备序列号 */
|
||||
equipmentSn: string
|
||||
/** 资产编号 */
|
||||
assetCode?: string
|
||||
/** 品牌 */
|
||||
brand?: string
|
||||
/** 配置规格/参数 */
|
||||
specification?: string
|
||||
/** 位置状态 */
|
||||
locationStatus?: string
|
||||
/** 设备当前物理位置 */
|
||||
physicalLocation?: string
|
||||
/** 负责人 */
|
||||
responsiblePerson?: string
|
||||
/** 健康状态 */
|
||||
healthStatus?: string
|
||||
/** 采购时间 */
|
||||
purchaseTime?: string
|
||||
/** 入库时间 */
|
||||
inStockTime?: string
|
||||
/** 启用时间 */
|
||||
activationTime?: string
|
||||
/** 预计报废时间 */
|
||||
expectedScrapTime?: string
|
||||
/** 实际报废时间 */
|
||||
actualScrapTime?: string
|
||||
/** 采购订单 */
|
||||
purchaseOrder?: string
|
||||
/** 供应商名称 */
|
||||
supplierName?: string
|
||||
/** 采购价格 */
|
||||
purchasePrice?: number
|
||||
/** 当前净值 */
|
||||
currentNetValue?: number
|
||||
/** 折旧方法 */
|
||||
depreciationMethod?: string
|
||||
/** 折旧年限 */
|
||||
depreciationYears?: number
|
||||
/** 残值 */
|
||||
salvageValue?: number
|
||||
/** 保修截止日期 */
|
||||
warrantyExpireDate?: string
|
||||
/** 上次维护日期 */
|
||||
lastMaintenanceDate?: string
|
||||
/** 下次维护日期 */
|
||||
nextMaintenanceDate?: string
|
||||
/** 维护人员 */
|
||||
maintenancePerson?: string
|
||||
/** 库存条码 */
|
||||
inventoryBarcode?: string
|
||||
/** 资产备注 */
|
||||
assetRemark?: string
|
||||
/** 次户号 */
|
||||
accountNumber?: string
|
||||
/** 数量 */
|
||||
quantity?: number
|
||||
/** 单价 */
|
||||
unitPrice?: number
|
||||
/** 总价 */
|
||||
totalPrice?: number
|
||||
/** 盘点依据 */
|
||||
inventoryBasis?: string
|
||||
/** 动态记录 */
|
||||
dynamicRecord?: string
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ export function createHealthRecord(data: HealthRecord) {
|
|||
return request({
|
||||
url: '/health-record',
|
||||
method: 'post',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ export function getHealthRecordList(params: HealthRecordListParams) {
|
|||
return request<HealthRecordListResponse>({
|
||||
url: '/health-record/list',
|
||||
method: 'get',
|
||||
params,
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ export function getHealthRecordList(params: HealthRecordListParams) {
|
|||
export function getHealthRecordDetail(id: string) {
|
||||
return request<HealthRecord>({
|
||||
url: `/health-record/detail/${id}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ export function updateHealthRecord(id: string, data: HealthRecord) {
|
|||
return request({
|
||||
url: `/health-record/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ export function updateHealthRecord(id: string, data: HealthRecord) {
|
|||
export function deleteHealthRecord(id: string) {
|
||||
return request({
|
||||
url: `/health-record/${id}`,
|
||||
method: 'delete',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -83,14 +83,14 @@ export function uploadHealthReport(file: File, recordId: string) {
|
|||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
formData.append('recordId', recordId)
|
||||
|
||||
|
||||
return request({
|
||||
url: '/health-record/upload-report',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ export function downloadHealthReport(fileId: string) {
|
|||
return request({
|
||||
url: `/health-record/download-report/${fileId}`,
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ export function downloadHealthReport(fileId: string) {
|
|||
export function getEmployeeHealthHistory(employeeId: string) {
|
||||
return request<HealthRecord[]>({
|
||||
url: `/health-record/employee/${employeeId}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ export function exportHealthRecords(params: HealthRecordListParams) {
|
|||
url: '/health-record/export',
|
||||
method: 'get',
|
||||
params,
|
||||
responseType: 'blob',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -131,6 +131,6 @@ export function scheduleHealthCheck(data: {
|
|||
return request({
|
||||
url: '/health-record/schedule',
|
||||
method: 'post',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ export * as InsuranceTypeAPI from './insurance-type'
|
|||
export * as HealthRecordAPI from './health-record'
|
||||
export * as InsuranceFileAPI from './insurance-file'
|
||||
export * as EmployeeAPI from './employee'
|
||||
export * as RegulationAPI from './regulation'
|
||||
|
||||
export * from './area/type'
|
||||
export * from './auth/type'
|
||||
|
|
|
@ -87,7 +87,7 @@ export const detectDefects = (params: DefectDetectionRequest) => {
|
|||
}
|
||||
|
||||
/** @desc 手动添加缺陷记录 */
|
||||
export const addManualDefect = (params: ManualDefectAddRequest, imageId: string) => {
|
||||
export const addManualDefect = (params: ManualDefectAddRequest,imageId:string) => {
|
||||
return http.post<ManualDefectAddResponse>(`/defect/${imageId}`, params)
|
||||
}
|
||||
|
||||
|
@ -95,12 +95,12 @@ export const addManualDefect = (params: ManualDefectAddRequest, imageId: string)
|
|||
|
||||
// 缺陷列表查询参数接口
|
||||
export interface DefectListParams {
|
||||
defectId?: string
|
||||
defectLevel?: string
|
||||
defectType?: string
|
||||
keyword?: string
|
||||
turbineId?: string
|
||||
imageId?: string // 添加imageId参数,用于按图像筛选缺陷
|
||||
defectId?: string;
|
||||
defectLevel?: string;
|
||||
defectType?: string;
|
||||
keyword?: string;
|
||||
turbineId?: string;
|
||||
imageId?: string; // 添加imageId参数,用于按图像筛选缺陷
|
||||
}
|
||||
|
||||
/** @desc 获取缺陷列表 */
|
||||
|
@ -111,7 +111,7 @@ export const getDefectList = (params: DefectListParams) => {
|
|||
msg: string
|
||||
status: number
|
||||
success: boolean
|
||||
}>('/defect/list', params)
|
||||
}>('/defect/list', params )
|
||||
}
|
||||
|
||||
/** @desc 添加缺陷 */
|
||||
|
@ -150,7 +150,7 @@ export const deleteDefect = (defectId: string) => {
|
|||
export const uploadAnnotatedImage = (imageBlob: Blob, fileName: string) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', imageBlob, fileName)
|
||||
|
||||
|
||||
return http.post<{
|
||||
code: number
|
||||
data: AttachInfoData
|
||||
|
@ -159,8 +159,8 @@ export const uploadAnnotatedImage = (imageBlob: Blob, fileName: string) => {
|
|||
success: boolean
|
||||
}>('/attach-info/defect_mark_pic', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -177,63 +177,63 @@ export interface AttachInfoData {
|
|||
|
||||
// 缺陷信息接口
|
||||
export interface DefectInfo {
|
||||
id: string
|
||||
defectId?: string
|
||||
defectName?: string
|
||||
defectLevel?: string
|
||||
defectType?: string
|
||||
defectPosition?: string
|
||||
detectionDate?: string
|
||||
description?: string
|
||||
repairStatus?: string
|
||||
repairIdea?: string
|
||||
labelInfo?: string
|
||||
id: string;
|
||||
defectId?: string;
|
||||
defectName?: string;
|
||||
defectLevel?: string;
|
||||
defectType?: string;
|
||||
defectPosition?: string;
|
||||
detectionDate?: string;
|
||||
description?: string;
|
||||
repairStatus?: string;
|
||||
repairIdea?: string;
|
||||
labelInfo?: string;
|
||||
markInfo?: {
|
||||
bbox?: number[]
|
||||
clsId?: number
|
||||
confidence?: number
|
||||
label?: string
|
||||
[key: string]: any
|
||||
}
|
||||
[key: string]: any
|
||||
bbox?: number[];
|
||||
clsId?: number;
|
||||
confidence?: number;
|
||||
label?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 缺陷等级类型
|
||||
export interface DefectLevelType {
|
||||
code: string
|
||||
name: string
|
||||
value: string
|
||||
sort: number
|
||||
description?: string
|
||||
code: string;
|
||||
name: string;
|
||||
value: string;
|
||||
sort: number;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// 缺陷类型
|
||||
export interface DefectType {
|
||||
code: string
|
||||
name: string
|
||||
value: string
|
||||
sort: number
|
||||
description?: string
|
||||
code: string;
|
||||
name: string;
|
||||
value: string;
|
||||
sort: number;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// 获取缺陷等级列表
|
||||
export const getDefectLevels = () => {
|
||||
return http.get<{
|
||||
code: number
|
||||
data: DefectLevelType[]
|
||||
msg: string
|
||||
status: number
|
||||
success: boolean
|
||||
code: number;
|
||||
data: DefectLevelType[];
|
||||
msg: string;
|
||||
status: number;
|
||||
success: boolean;
|
||||
}>('/common/list/defect-level')
|
||||
}
|
||||
|
||||
// 获取缺陷类型列表
|
||||
export const getDefectTypes = () => {
|
||||
return http.get<{
|
||||
code: number
|
||||
data: DefectType[]
|
||||
msg: string
|
||||
status: number
|
||||
success: boolean
|
||||
code: number;
|
||||
data: DefectType[];
|
||||
msg: string;
|
||||
status: number;
|
||||
success: boolean;
|
||||
}>('/common/list/defect-type')
|
||||
}
|
||||
}
|
|
@ -114,7 +114,7 @@ export const uploadSingleImage = (imageSource: string, file: File, params?: {
|
|||
}) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
|
||||
// 构建查询参数
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params?.altitude) queryParams.append('altitude', params.altitude)
|
||||
|
@ -122,13 +122,13 @@ export const uploadSingleImage = (imageSource: string, file: File, params?: {
|
|||
if (params?.longitude) queryParams.append('longitude', params.longitude)
|
||||
if (params?.partId) queryParams.append('partId', params.partId)
|
||||
if (params?.uploadUser) queryParams.append('uploadUser', params.uploadUser)
|
||||
|
||||
const url = `/common/upload-image/${imageSource}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`
|
||||
|
||||
|
||||
const url = `/common/upload-image/${imageSource}${queryParams.toString() ? '?' + queryParams.toString() : ''}`
|
||||
|
||||
return http.post(url, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -141,12 +141,12 @@ export const batchUploadImages = (imageSource: string, files: File[], params?: {
|
|||
uploadUser?: string
|
||||
}) => {
|
||||
const formData = new FormData()
|
||||
|
||||
|
||||
// 添加文件
|
||||
files.forEach((file) => {
|
||||
files.forEach(file => {
|
||||
formData.append('files', file)
|
||||
})
|
||||
|
||||
|
||||
// 构建查询参数
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params?.altitude) queryParams.append('altitude', params.altitude)
|
||||
|
@ -154,13 +154,13 @@ export const batchUploadImages = (imageSource: string, files: File[], params?: {
|
|||
if (params?.longitude) queryParams.append('longitude', params.longitude)
|
||||
if (params?.partId) queryParams.append('partId', params.partId)
|
||||
if (params?.uploadUser) queryParams.append('uploadUser', params.uploadUser)
|
||||
|
||||
const url = `/common/batch-upload-image/${imageSource}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`
|
||||
|
||||
|
||||
const url = `/common/batch-upload-image/${imageSource}${queryParams.toString() ? '?' + queryParams.toString() : ''}`
|
||||
|
||||
return http.post(url, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -173,8 +173,8 @@ export const detectDefects = (params: {
|
|||
}) => {
|
||||
return http.post('/defect/detect', params, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -183,15 +183,15 @@ export const uploadImageToPartV2 = (
|
|||
imageSource: string,
|
||||
partId: string,
|
||||
files: File[],
|
||||
params: Partial<T.ImageUploadParams>,
|
||||
params: Partial<T.ImageUploadParams>
|
||||
) => {
|
||||
const formData = new FormData()
|
||||
|
||||
|
||||
// 添加文件
|
||||
files.forEach((file) => {
|
||||
files.forEach(file => {
|
||||
formData.append('files', file)
|
||||
})
|
||||
|
||||
|
||||
// 添加其他参数
|
||||
if (params.collectorId) formData.append('collectorId', params.collectorId)
|
||||
if (params.collectorName) formData.append('collectorName', params.collectorName)
|
||||
|
@ -223,23 +223,23 @@ export const uploadImageToPartV2 = (
|
|||
if (params.temperatureMin !== undefined) formData.append('temperatureMin', params.temperatureMin.toString())
|
||||
if (params.weather) formData.append('weather', params.weather)
|
||||
if (params.windLevel !== undefined) formData.append('windLevel', params.windLevel.toString())
|
||||
|
||||
|
||||
return http.post(`/image/${imageSource}/upload/${partId}`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** @desc 图像导入接口(更新为使用真实接口) */
|
||||
export const importImages = (files: FileList | File[], params: T.ImageImportParams) => {
|
||||
const fileArray = Array.from(files)
|
||||
|
||||
|
||||
// 使用批量上传接口
|
||||
return batchUploadImages(params.imageSource || 'default', fileArray, {
|
||||
partId: params.componentId,
|
||||
uploadUser: params.uploadUser,
|
||||
}).then((response) => {
|
||||
uploadUser: params.uploadUser
|
||||
}).then(response => {
|
||||
// 如果需要自动标注
|
||||
if (params.autoAnnotate && params.annotationTypes && params.annotationTypes.length > 0) {
|
||||
// 这里可以添加自动标注逻辑
|
||||
|
@ -255,7 +255,7 @@ export const autoAnnotateImage = (params: T.AutoAnnotationParams) => {
|
|||
confThreshold: params.confidenceThreshold || 0.5,
|
||||
defectTypeList: params.annotationTypes,
|
||||
imageId: params.imageId,
|
||||
modelId: params.params?.modelId || 'default',
|
||||
modelId: params.params?.modelId || 'default'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -275,10 +275,10 @@ export const confirmAnnotation = (imageId: string, annotationId: string) => {
|
|||
}
|
||||
|
||||
/** @desc 上传图像(保留旧接口兼容性) */
|
||||
export const uploadImage = (file: File, params: { projectId: string, componentId?: string }) => {
|
||||
export const uploadImage = (file: File, params: { projectId: string; componentId?: string }) => {
|
||||
return uploadSingleImage('default', file, {
|
||||
partId: params.componentId,
|
||||
uploadUser: 'current-user',
|
||||
uploadUser: 'current-user'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -321,20 +321,21 @@ export function reprocessImage(params: T.ImageProcessParams) {
|
|||
export const batchProcessImages = (imageIds: string[], processType: string) => {
|
||||
return http.post<T.ImageProcessResult[]>(`/industrial-image/batch-process`, {
|
||||
imageIds,
|
||||
processType,
|
||||
processType
|
||||
})
|
||||
}
|
||||
|
||||
/** @desc 导出处理结果 */
|
||||
export function exportProcessResults(query: T.ImageQuery) {
|
||||
return http.get(`/industrial-image/export/results`, query, {
|
||||
responseType: 'blob',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
/** @desc 生成检测报告 */
|
||||
export function generateReport(projectId: string) {
|
||||
return http.post(`/industrial-image/report/generate`, { projectId }, {
|
||||
responseType: 'blob',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ export interface IndustrialImage {
|
|||
name: string
|
||||
/** 图像路径 */
|
||||
path: string
|
||||
/** 图像路径(API返回字段) */
|
||||
/** 图像路径(API返回字段)*/
|
||||
imagePath?: string
|
||||
/** 缩略图路径 */
|
||||
thumbnailPath?: string
|
||||
|
@ -185,7 +185,7 @@ export interface IndustrialImage {
|
|||
createTime?: string
|
||||
/** 更新时间 */
|
||||
updateTime?: string
|
||||
|
||||
|
||||
// 扩展字段 - 来自真实API
|
||||
/** 部件名称 */
|
||||
partName?: string
|
||||
|
@ -412,4 +412,4 @@ export interface ImageProcessResult {
|
|||
createTime?: string
|
||||
/** 完成时间 */
|
||||
completeTime?: string
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ export function createInsuranceCompany(data: InsuranceCompany) {
|
|||
return request({
|
||||
url: '/insurance-company',
|
||||
method: 'post',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ export function getInsuranceCompanyList(params: InsuranceCompanyListParams) {
|
|||
return request<InsuranceCompanyListResponse>({
|
||||
url: '/insurance-company/list',
|
||||
method: 'get',
|
||||
params,
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ export function getInsuranceCompanyList(params: InsuranceCompanyListParams) {
|
|||
export function getInsuranceCompanyDetail(id: string) {
|
||||
return request<InsuranceCompany>({
|
||||
url: `/insurance-company/detail/${id}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ export function updateInsuranceCompany(id: string, data: InsuranceCompany) {
|
|||
return request({
|
||||
url: `/insurance-company/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ export function updateInsuranceCompany(id: string, data: InsuranceCompany) {
|
|||
export function deleteInsuranceCompany(id: string) {
|
||||
return request({
|
||||
url: `/insurance-company/${id}`,
|
||||
method: 'delete',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ export function deleteInsuranceCompany(id: string) {
|
|||
export function terminateCooperation(id: string) {
|
||||
return request({
|
||||
url: `/insurance-company/terminate/${id}`,
|
||||
method: 'post',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ export function terminateCooperation(id: string) {
|
|||
export function resumeCooperation(id: string) {
|
||||
return request({
|
||||
url: `/insurance-company/resume/${id}`,
|
||||
method: 'post',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -48,14 +48,14 @@ export function uploadInsuranceFile(data: UploadInsuranceFileParams) {
|
|||
if (data.description) {
|
||||
formData.append('description', data.description)
|
||||
}
|
||||
|
||||
|
||||
return request({
|
||||
url: '/insurance-file/upload',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ export function getInsuranceFileList(params: InsuranceFileListParams) {
|
|||
return request<InsuranceFileListResponse>({
|
||||
url: '/insurance-file/list',
|
||||
method: 'get',
|
||||
params,
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ export function getInsuranceFileList(params: InsuranceFileListParams) {
|
|||
export function getInsuranceFileDetail(id: string) {
|
||||
return request<InsuranceFile>({
|
||||
url: `/insurance-file/detail/${id}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ export function updateInsuranceFile(id: string, data: Partial<InsuranceFile>) {
|
|||
return request({
|
||||
url: `/insurance-file/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ export function updateInsuranceFile(id: string, data: Partial<InsuranceFile>) {
|
|||
export function deleteInsuranceFile(id: string) {
|
||||
return request({
|
||||
url: `/insurance-file/${id}`,
|
||||
method: 'delete',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ export function batchDeleteInsuranceFiles(ids: string[]) {
|
|||
return request({
|
||||
url: '/insurance-file/batch',
|
||||
method: 'delete',
|
||||
data: { ids },
|
||||
data: { ids }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ export function downloadInsuranceFile(id: string) {
|
|||
return request({
|
||||
url: `/insurance-file/download/${id}`,
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ export function previewInsuranceFile(id: string) {
|
|||
return request({
|
||||
url: `/insurance-file/preview/${id}`,
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ export function previewInsuranceFile(id: string) {
|
|||
export function getEmployeeFiles(employeeId: string) {
|
||||
return request<InsuranceFile[]>({
|
||||
url: `/insurance-file/employee/${employeeId}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,7 @@ export function getInsuranceFileStatistics() {
|
|||
totalSize: number
|
||||
}[]>({
|
||||
url: '/insurance-file/statistics',
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -156,13 +156,13 @@ export function batchUploadFiles(data: {
|
|||
if (data.description) {
|
||||
formData.append('description', data.description)
|
||||
}
|
||||
|
||||
|
||||
return request({
|
||||
url: '/insurance-file/batch-upload',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ export function createInsuranceType(data: InsuranceType) {
|
|||
return request({
|
||||
url: '/insurance-type',
|
||||
method: 'post',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ export function getInsuranceTypeList(params?: InsuranceTypeListParams) {
|
|||
return request<InsuranceTypeListResponse>({
|
||||
url: '/insurance-type/list',
|
||||
method: 'get',
|
||||
params,
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ export function getInsuranceTypeList(params?: InsuranceTypeListParams) {
|
|||
export function getInsuranceTypeDetail(insuranceTypeId: string) {
|
||||
return request<InsuranceType>({
|
||||
url: `/insurance-type/detail/${insuranceTypeId}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ export function updateInsuranceType(id: string, data: InsuranceType) {
|
|||
return request({
|
||||
url: `/insurance-type/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ export function updateInsuranceType(id: string, data: InsuranceType) {
|
|||
export function deleteInsuranceType(id: string) {
|
||||
return request({
|
||||
url: `/insurance-type/${id}`,
|
||||
method: 'delete',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,6 @@ export function batchDeleteInsuranceType(ids: string[]) {
|
|||
return request({
|
||||
url: '/insurance-type/batch',
|
||||
method: 'delete',
|
||||
data: { ids },
|
||||
data: { ids }
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { InsuranceInfo, InsuranceListParams, InsuranceListResponse, RenewInsuranceParams } from './type'
|
||||
import http from '@/utils/http'
|
||||
import type { InsuranceInfo, InsuranceListParams, InsuranceListResponse, RenewInsuranceParams } from './type'
|
||||
|
||||
const { request } = http
|
||||
|
||||
|
@ -11,7 +11,7 @@ export function createInsurance(data: InsuranceInfo) {
|
|||
return request({
|
||||
url: '/insurance-info',
|
||||
method: 'post',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ export function getInsuranceList(params: InsuranceListParams) {
|
|||
return request<InsuranceListResponse>({
|
||||
url: '/insurance-info/list',
|
||||
method: 'get',
|
||||
params,
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ export function getInsuranceList(params: InsuranceListParams) {
|
|||
export function getInsuranceDetail(id: string) {
|
||||
return request<InsuranceInfo>({
|
||||
url: `/insurance-info/detail/${id}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ export function updateInsurance(id: string, data: InsuranceInfo) {
|
|||
return request({
|
||||
url: `/insurance-info/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ export function updateInsurance(id: string, data: InsuranceInfo) {
|
|||
export function deleteInsurance(id: string) {
|
||||
return request({
|
||||
url: `/insurance-info/${id}`,
|
||||
method: 'delete',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ export function renewInsurance(id: string, data: RenewInsuranceParams) {
|
|||
return request({
|
||||
url: `/insurance-info/renew/${id}`,
|
||||
method: 'post',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ export function batchDeleteInsurance(ids: string[]) {
|
|||
return request({
|
||||
url: '/insurance-info/batch',
|
||||
method: 'delete',
|
||||
data: { ids },
|
||||
data: { ids }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,6 @@ export function exportInsurance(params: InsuranceListParams) {
|
|||
url: '/insurance-info/export',
|
||||
method: 'get',
|
||||
params,
|
||||
responseType: 'blob',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/** 保险信息接口 */
|
||||
export interface InsuranceInfo {
|
||||
id?: string
|
||||
attachInfoId: string
|
||||
attachInfoId:string
|
||||
insuranceCompanyId: string
|
||||
insuranceTypeId: string
|
||||
userId: string
|
||||
|
@ -38,4 +38,4 @@ export interface InsuranceListResponse {
|
|||
export interface RenewInsuranceParams {
|
||||
expireDate: string
|
||||
insurancePremium: number
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import type { ModelConfigDetailResponse, ModelConfigListResponse, ModelConfigRequest, ModelConfigResponse } from './type'
|
||||
import http from '@/utils/http'
|
||||
import type { ModelConfigRequest, ModelConfigResponse, ModelConfigListResponse, ModelConfigDetailResponse } from './type'
|
||||
|
||||
const { request } = http
|
||||
|
||||
|
@ -11,7 +11,7 @@ export function createModelConfig(data: ModelConfigRequest) {
|
|||
return request<ModelConfigResponse>({
|
||||
url: '/model-config',
|
||||
method: 'post',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ export function updateModelConfig(data: ModelConfigRequest) {
|
|||
return request<ModelConfigResponse>({
|
||||
url: '/model-config',
|
||||
method: 'put',
|
||||
data,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ export function getModelConfigList(params?: {
|
|||
return request<ModelConfigListResponse>({
|
||||
url: '/model-config/list',
|
||||
method: 'get',
|
||||
params,
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ export function getModelConfigList(params?: {
|
|||
export function getModelConfigDetail(modelId: string) {
|
||||
return request<ModelConfigDetailResponse>({
|
||||
url: `/model-config/${modelId}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,6 @@ export function getModelConfigDetail(modelId: string) {
|
|||
export function deleteModelConfig(modelId: string) {
|
||||
return request<any>({
|
||||
url: `/model-config/${modelId}`,
|
||||
method: 'delete',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
}
|
|
@ -49,4 +49,4 @@ export interface ModelConfigDetailResponse {
|
|||
data: ModelConfigResponse
|
||||
msg: string
|
||||
status: number
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import type { DimensionQuery, PerformanceDimension, PerformanceRule, RuleQuery } from './type'
|
||||
import http from '@/utils/http'
|
||||
import type { PerformanceDimension, PerformanceRule, DimensionQuery, RuleQuery } from './type'
|
||||
|
||||
/** 维度相关 */
|
||||
export function getDimensionList(params?: DimensionQuery) {
|
||||
|
@ -36,5 +36,5 @@ export function deleteRule(id: string) {
|
|||
}
|
||||
// 我的绩效
|
||||
export function getMyEvaluation() {
|
||||
return http.get('/performance-evaluation/my')
|
||||
}
|
||||
return http.get('/performance-evaluation/my')
|
||||
}
|
|
@ -1,39 +1,39 @@
|
|||
/** 绩效维度 */
|
||||
export interface PerformanceDimension {
|
||||
dimensionId: string
|
||||
dimensionName: string
|
||||
description?: string
|
||||
deptName: string
|
||||
status: 0 | 1
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/** 绩效细则 */
|
||||
export interface PerformanceRule {
|
||||
ruleId: string
|
||||
ruleName: string
|
||||
description?: string
|
||||
dimensionName: string
|
||||
bonus?: string
|
||||
score?: number
|
||||
weight?: number
|
||||
status: 0 | 1
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/** 查询参数 */
|
||||
export interface DimensionQuery {
|
||||
dimensionName?: string
|
||||
status?: 0 | 1
|
||||
}
|
||||
export interface RuleQuery {
|
||||
dimensionName?: string
|
||||
ruleName?: string
|
||||
status?: 0 | 1
|
||||
}
|
||||
dimensionId: string
|
||||
dimensionName: string
|
||||
description?: string
|
||||
deptName: string
|
||||
status: 0 | 1
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/** 绩效细则 */
|
||||
export interface PerformanceRule {
|
||||
ruleId: string
|
||||
ruleName: string
|
||||
description?: string
|
||||
dimensionName: string
|
||||
bonus?: string
|
||||
score?: number
|
||||
weight?: number
|
||||
status: 0 | 1
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/** 查询参数 */
|
||||
export interface DimensionQuery {
|
||||
dimensionName?: string
|
||||
status?: 0 | 1
|
||||
}
|
||||
export interface RuleQuery {
|
||||
dimensionName?: string
|
||||
ruleName?: string
|
||||
status?: 0 | 1
|
||||
}
|
|
@ -89,17 +89,17 @@ export function auditBudget(id: string, data: BudgetAuditReq) {
|
|||
|
||||
/** @desc 获取预算类型选项 */
|
||||
export function getBudgetTypes() {
|
||||
return http.get<Array<{ label: string, value: string }>>(`${BASE_URL}/types`)
|
||||
return http.get<Array<{ label: string; value: string }>>(`${BASE_URL}/types`)
|
||||
}
|
||||
|
||||
/** @desc 上传预算附件 */
|
||||
export function uploadBudgetAttachment(file: File) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return http.post<{ id: string, name: string, url: string }>(`${BASE_URL}/upload`, formData, {
|
||||
return http.post<{ id: string; name: string; url: string }>(`${BASE_URL}/upload`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,6 @@ export function deleteBudgetAttachment(id: string) {
|
|||
/** @desc 导出预算记录 */
|
||||
export function exportBudgetRecord(query: BudgetQuery) {
|
||||
return http.get(`${BASE_URL}/export`, query, {
|
||||
responseType: 'blob',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
}
|
|
@ -41,12 +41,12 @@ export function importProject(file: File) {
|
|||
formData.append('file', file)
|
||||
return http.post(`${BASE_URL}/import`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** @desc 导出项目 */
|
||||
export function exportProject(query: T.ProjectQuery) {
|
||||
return http.download(`${BASE_URL}/export`, query)
|
||||
}
|
||||
}
|
|
@ -23,9 +23,9 @@ export function deleteTaskGroup(id: number) {
|
|||
return http.del(`${BASE_URL}/group/${id}`)
|
||||
}
|
||||
|
||||
/** @desc 查询任务列表 */
|
||||
export function listTask(query: T.TaskPageQuery) {
|
||||
return http.get<PageRes<T.TaskResp[]>>(`${BASE_URL}`, query)
|
||||
/** @desc 查询任务列表(标准导出) */
|
||||
export const listTask = (params: any) => {
|
||||
return http.get('/project-task/list', params)
|
||||
}
|
||||
|
||||
/** @desc 获取任务详情 */
|
||||
|
@ -65,7 +65,7 @@ export function importTask(file: File, projectId: number) {
|
|||
formData.append('projectId', projectId.toString())
|
||||
return http.post(`${BASE_URL}/import`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
/** 项目类型 */
|
||||
export interface ProjectResp {
|
||||
projectId: string // 项目ID (API返回的是字符串)
|
||||
projectCode?: string // 项目编号
|
||||
projectName: string // 项目名称
|
||||
projectCode?: string // 项目编号
|
||||
projectName: string // 项目名称
|
||||
projectIntro?: string // 项目简介
|
||||
farmName: string // 风场名称 (API字段名是farmName)
|
||||
farmName: string // 风场名称 (API字段名是farmName)
|
||||
farmAddress?: string // 风场地址 (API字段名是farmAddress)
|
||||
client?: string // 委托单位
|
||||
clientContact?: string // 委托单位联系人
|
||||
|
@ -28,10 +28,10 @@ export interface ProjectResp {
|
|||
coverUrl?: string // 封面URL
|
||||
createDt?: Date
|
||||
updateDt?: Date
|
||||
|
||||
|
||||
// 为了保持向后兼容,添加一些别名字段
|
||||
id?: string // projectId的别名
|
||||
fieldName?: string // farmName的别名
|
||||
fieldName?: string // farmName的别名
|
||||
fieldLocation?: string // farmAddress的别名
|
||||
commissionUnit?: string // client的别名
|
||||
commissionContact?: string // clientContact的别名
|
||||
|
@ -85,4 +85,4 @@ export interface TaskQuery {
|
|||
status?: string
|
||||
}
|
||||
|
||||
export interface TaskPageQuery extends TaskQuery, PageQuery {}
|
||||
export interface TaskPageQuery extends TaskQuery, PageQuery {}
|
|
@ -0,0 +1,108 @@
|
|||
import http from '@/utils/http'
|
||||
import {
|
||||
type RegulationTypeSearchRequest,
|
||||
type RegulationTypeSearchResponse,
|
||||
type RegulationProposalSearchRequest,
|
||||
type RegulationProposalSearchResponse
|
||||
} from './type'
|
||||
|
||||
// 制度管理API接口
|
||||
export const regulationApi = {
|
||||
// 获取制度列表
|
||||
getRegulationList: (params: {
|
||||
page?: number
|
||||
size?: number
|
||||
status?: string
|
||||
type?: string
|
||||
title?: string
|
||||
proposer?: string
|
||||
}): Promise<RegulationProposalSearchResponse> => {
|
||||
return http.get('/regulation', params)
|
||||
},
|
||||
|
||||
// 获取制度详情
|
||||
getRegulationDetail: (regulationId: string) => {
|
||||
return http.get(`/regulation/${regulationId}`)
|
||||
},
|
||||
|
||||
// 创建制度提案
|
||||
createProposal: (data: {
|
||||
title: string
|
||||
content: string
|
||||
regulationType: string
|
||||
scope: string
|
||||
level: string
|
||||
remark?: string
|
||||
createBy?: string
|
||||
}) => {
|
||||
return http.post('/regulation/proposal', data)
|
||||
},
|
||||
|
||||
// 更新制度提案
|
||||
updateProposal: (regulationId: string, data: any) => {
|
||||
return http.put(`/regulation/proposal/${regulationId}`, data)
|
||||
},
|
||||
|
||||
// 公示提案
|
||||
publishProposal: (regulationId: string) => {
|
||||
return http.post(`/regulation/proposal/${regulationId}/publish`)
|
||||
},
|
||||
|
||||
// 删除制度提案
|
||||
deleteProposal: (regulationId: string) => {
|
||||
return http.del(`/regulation/proposal/${regulationId}`)
|
||||
},
|
||||
|
||||
// 公示制度
|
||||
publishRegulation: (regulationId: string) => {
|
||||
return http.post(`/regulation/${regulationId}/approve`)
|
||||
},
|
||||
|
||||
// 获取已公示制度列表
|
||||
getPublishedRegulationList: (params: {
|
||||
page: number
|
||||
size: number
|
||||
status: string
|
||||
}) => {
|
||||
return http.get('/regulation', params)
|
||||
},
|
||||
|
||||
// 确认制度知晓
|
||||
confirmRegulation: (regulationId: string) => {
|
||||
return http.post(`/regulation/${regulationId}/confirm`)
|
||||
},
|
||||
|
||||
// 搜索制度类型(后端搜索接口)
|
||||
searchRegulationTypes: (params: {
|
||||
page?: number
|
||||
size?: number
|
||||
typeName?: string
|
||||
status?: string
|
||||
remark?: string
|
||||
}) => {
|
||||
return http.get('/regulation/types', params)
|
||||
},
|
||||
|
||||
// 创建制度类型
|
||||
createRegulationType: (data: {
|
||||
typeName: string
|
||||
sortOrder?: number
|
||||
isEnabled?: string
|
||||
}) => {
|
||||
return http.post('/regulation/types', data)
|
||||
},
|
||||
|
||||
// 更新制度类型
|
||||
updateRegulationType: (typeId: string, data: {
|
||||
typeName: string
|
||||
sortOrder?: number
|
||||
isEnabled?: string
|
||||
}) => {
|
||||
return http.put(`/regulation/types/${typeId}`, data)
|
||||
},
|
||||
|
||||
// 删除制度类型
|
||||
deleteRegulationType: (typeId: string) => {
|
||||
return http.del(`/regulation/types/${typeId}`)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
// 制度状态枚举
|
||||
export enum RegulationStatus {
|
||||
DRAFT = 'DRAFT', // 草稿
|
||||
APPROVED = 'APPROVED', // 已通过
|
||||
PUBLISHED = 'PUBLISHED', // 已公示
|
||||
}
|
||||
|
||||
// 制度级别枚举
|
||||
export enum RegulationLevel {
|
||||
LOW = 'LOW', // 低
|
||||
MEDIUM = 'MEDIUM', // 中
|
||||
HIGH = 'HIGH' // 高
|
||||
}
|
||||
|
||||
// 制度信息接口
|
||||
export interface Regulation {
|
||||
regulationId: string
|
||||
title: string
|
||||
content: string
|
||||
regulationType: string
|
||||
status: RegulationStatus
|
||||
publishTime: string // 公示时间
|
||||
effectiveTime: string
|
||||
expireTime: string
|
||||
scope: string
|
||||
level: RegulationLevel
|
||||
version: string
|
||||
remark?: string
|
||||
createBy: string
|
||||
updateBy: string
|
||||
createTime: string
|
||||
updateTime: string
|
||||
page: number
|
||||
pageSize: number
|
||||
delFlag: string
|
||||
confirmStatus?: string
|
||||
}
|
||||
|
||||
// 创建提案请求接口
|
||||
export interface CreateProposalRequest {
|
||||
title: string
|
||||
content: string
|
||||
regulationType: string
|
||||
scope: string
|
||||
level: RegulationLevel
|
||||
remark?: string
|
||||
createBy?: string
|
||||
}
|
||||
|
||||
// 分页参数接口
|
||||
export interface PaginationParams {
|
||||
page: number
|
||||
size: number
|
||||
}
|
||||
|
||||
// 制度类型接口
|
||||
export interface RegulationType {
|
||||
typeId: string
|
||||
typeName: string
|
||||
sortOrder: number
|
||||
isEnabled: string
|
||||
remark?: string
|
||||
createBy: string
|
||||
createTime: string
|
||||
updateBy: string
|
||||
updateTime: string
|
||||
delFlag: string
|
||||
}
|
||||
|
||||
// 创建制度类型请求接口
|
||||
export interface CreateRegulationTypeRequest {
|
||||
typeName: string
|
||||
sortOrder?: number
|
||||
isEnabled?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
// 更新制度类型请求接口
|
||||
export interface UpdateRegulationTypeRequest {
|
||||
typeName: string
|
||||
sortOrder?: number
|
||||
isEnabled?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
// 制度类型搜索请求接口
|
||||
export interface RegulationTypeSearchRequest {
|
||||
page?: number
|
||||
size?: number
|
||||
typeName?: string
|
||||
status?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
// 制度类型搜索响应接口
|
||||
export interface RegulationTypeSearchResponse {
|
||||
records: RegulationType[]
|
||||
total: number
|
||||
current: number
|
||||
size: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
// 制度提案搜索请求接口
|
||||
export interface RegulationProposalSearchRequest {
|
||||
page?: number
|
||||
size?: number
|
||||
status?: string
|
||||
type?: string
|
||||
title?: string
|
||||
proposer?: string
|
||||
}
|
||||
|
||||
// 制度提案搜索响应接口
|
||||
export interface RegulationProposalSearchResponse {
|
||||
records: Regulation[]
|
||||
total: number
|
||||
current: number
|
||||
size: number
|
||||
pages: number
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import type { SalaryCreateRequest, SalaryQuery, SalaryRecord } from '@/views/salary-management/types'
|
||||
import type { SalaryRecord, SalaryQuery, SalaryCreateRequest } from '@/views/salary-management/types'
|
||||
import http from '@/utils/http'
|
||||
|
||||
const BASE_URL = '/salary'
|
||||
|
@ -34,7 +34,7 @@ export const submitApproval = (id: string) => {
|
|||
}
|
||||
|
||||
// 审批工资单
|
||||
export const approveSalary = (id: string, data: { status: string, comment?: string }) => {
|
||||
export const approveSalary = (id: string, data: { status: string; comment?: string }) => {
|
||||
return http.put<boolean>(`${BASE_URL}/${id}/approve`, data)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import type * as T from './type'
|
||||
import http from '@/utils/http'
|
||||
import { convertMenuData } from '@/utils/menuConverter'
|
||||
|
||||
|
@ -5,13 +6,13 @@ import { convertMenuData } from '@/utils/menuConverter'
|
|||
* 从新的API获取菜单树形数据,并转换为角色管理所需的格式
|
||||
*/
|
||||
export function getMenuTreeForRole(query?: { terminalType?: string }) {
|
||||
return http.get<any[]>('/menu/tree', query).then((res) => {
|
||||
return http.get<any[]>('/menu/tree', query).then(res => {
|
||||
// 假设响应格式为 { data: [...菜单数据], success: true, msg: "", code: 200 }
|
||||
const data = res.data || []
|
||||
const data = res.data || [];
|
||||
// 转换菜单数据为角色管理组件需要的格式
|
||||
const convertedData = convertMenuData(data)
|
||||
return convertedData
|
||||
})
|
||||
const convertedData = convertMenuData(data);
|
||||
return convertedData;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,31 +23,31 @@ export function getMenuTreeForRole(query?: { terminalType?: string }) {
|
|||
*/
|
||||
export function transformMenusWithPermissions(menus: any[], selectedMenuIds: string[] = []) {
|
||||
// 深拷贝菜单数据,避免修改原始数据
|
||||
const result = JSON.parse(JSON.stringify(menus))
|
||||
|
||||
const result = JSON.parse(JSON.stringify(menus));
|
||||
|
||||
// 递归处理菜单树,添加权限标记
|
||||
const processMenus = (items: any[]) => {
|
||||
return items.map((item) => {
|
||||
return items.map(item => {
|
||||
// 设置选中状态
|
||||
item.isChecked = selectedMenuIds.includes(item.id.toString())
|
||||
|
||||
item.isChecked = selectedMenuIds.includes(item.id.toString());
|
||||
|
||||
// 如果有子菜单,递归处理
|
||||
if (item.children && item.children.length > 0) {
|
||||
item.children = processMenus(item.children)
|
||||
item.children = processMenus(item.children);
|
||||
}
|
||||
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
return processMenus(result)
|
||||
|
||||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
return processMenus(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色已分配的菜单ID列表
|
||||
*/
|
||||
export function getRoleMenuIds(roleId: string) {
|
||||
return http.get<string[]>(`/role/get-menus/${roleId}`)
|
||||
return http.get<string[]>(`/role/get-menus/${roleId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,6 +56,6 @@ export function getRoleMenuIds(roleId: string) {
|
|||
export function assignRoleMenus(roleId: string, menuIds: string[]) {
|
||||
return http.post('/role/bind-menu', {
|
||||
roleId,
|
||||
menuIds,
|
||||
})
|
||||
}
|
||||
menuIds
|
||||
});
|
||||
}
|
|
@ -1,46 +1,52 @@
|
|||
import type * as T from './type'
|
||||
import http from '@/utils/http'
|
||||
import type * as T from './type';
|
||||
import http from '@/utils/http';
|
||||
|
||||
const BASE_URL = '/post'
|
||||
const BASE_URL = '/post';
|
||||
|
||||
/**
|
||||
* 添加岗位
|
||||
*/
|
||||
export function addPost(data: T.PostAddReq) {
|
||||
return http.post<any>(BASE_URL, data)
|
||||
return http.post<any>(BASE_URL, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询岗位详情
|
||||
*/
|
||||
export function getPostDetail(postId: string) {
|
||||
return http.get<T.PostVO>(`${BASE_URL}/detail/${postId}`)
|
||||
return http.get<T.PostVO>(`${BASE_URL}/detail/${postId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询岗位列表
|
||||
*/
|
||||
export function listPost(params?: T.PostPageQuery) {
|
||||
return http.get<T.PostVO[]>(`${BASE_URL}/list`, params)
|
||||
return http.get<T.PostVO[]>(`${BASE_URL}/list`, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询岗位列表
|
||||
*/
|
||||
export function pagePost(params?: T.PostPageQuery) {
|
||||
return http.get<PageRes<T.PostVO[]>>(`${BASE_URL}/page`, params)
|
||||
return http.get<PageRes<T.PostVO[]>>(`${BASE_URL}/page`, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改岗位
|
||||
*/
|
||||
export function updatePost(postId: string, data: T.PostUpdateReq) {
|
||||
return http.put<any>(`${BASE_URL}/${postId}`, data)
|
||||
return http.put<any>(`${BASE_URL}/${postId}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除岗位
|
||||
*/
|
||||
export function deletePost(postId: string) {
|
||||
return http.del<any>(`${BASE_URL}/${postId}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询岗位下的用户信息
|
||||
*/
|
||||
export function getPostUsers(postId: string) {
|
||||
return http.get<T.UserNewResp[]>(`${BASE_URL}/${postId}/user`);
|
||||
}
|
|
@ -4,6 +4,7 @@ import http from '@/utils/http'
|
|||
export type * from './type'
|
||||
|
||||
const BASE_URL = '/system/role'
|
||||
const BASE_URL_NEW = '/role'
|
||||
|
||||
/** @desc 查询角色列表(已废弃) */
|
||||
export function listRole(query: T.RoleQuery) {
|
||||
|
@ -72,7 +73,7 @@ export function updateRolePermission(id: string, data: any) {
|
|||
|
||||
/** @desc 查询角色关联用户 */
|
||||
export function listRoleUser(id: string, query: T.RoleUserPageQuery) {
|
||||
return http.get<PageRes<T.RoleUserResp[]>>(`${BASE_URL}/${id}/user`, query)
|
||||
return http.get<PageRes<T.RoleUserResp[]>>(`${BASE_URL_NEW}/${id}/user`, query)
|
||||
}
|
||||
|
||||
/** @desc 分配角色给用户 */
|
||||
|
@ -87,5 +88,5 @@ export function unassignFromUsers(userRoleIds: Array<string | number>) {
|
|||
|
||||
/** @desc 查询角色关联用户 ID */
|
||||
export function listRoleUserId(id: string) {
|
||||
return http.get(`${BASE_URL}/${id}/user/id`)
|
||||
return http.get(`${BASE_URL_NEW}/${id}/user`)
|
||||
}
|
||||
|
|
|
@ -564,113 +564,113 @@ export interface MessagePageQuery extends MessageQuery, PageQuery {
|
|||
|
||||
/** 新增菜单请求参数 */
|
||||
export interface MenuAddReq {
|
||||
menuName: string
|
||||
menuType: string
|
||||
orderNum: number
|
||||
parentId: string
|
||||
perms: string
|
||||
terminalType: string
|
||||
url: string
|
||||
visible: string
|
||||
menuName: string;
|
||||
menuType: string;
|
||||
orderNum: number;
|
||||
parentId: string;
|
||||
perms: string;
|
||||
terminalType: string;
|
||||
url: string;
|
||||
visible: string;
|
||||
}
|
||||
|
||||
/** 新菜单树查询参数 */
|
||||
export interface MenuTreeQuery {
|
||||
menuName?: string
|
||||
terminalType?: string
|
||||
menuName?: string;
|
||||
terminalType?: string;
|
||||
}
|
||||
|
||||
/** 新菜单详情响应类型 */
|
||||
export interface MenuDetailResp {
|
||||
menuId: string
|
||||
menuName: string
|
||||
menuType: string
|
||||
orderNum: number
|
||||
parentId: string
|
||||
perms: string
|
||||
url: string
|
||||
visible: string
|
||||
menuId: string;
|
||||
menuName: string;
|
||||
menuType: string;
|
||||
orderNum: number;
|
||||
parentId: string;
|
||||
perms: string;
|
||||
url: string;
|
||||
visible: string;
|
||||
}
|
||||
|
||||
/** 菜单更新请求参数 */
|
||||
export interface MenuUpdateReq {
|
||||
menuName: string
|
||||
menuType: string
|
||||
orderNum: number
|
||||
parentId: string
|
||||
perms: string
|
||||
terminalType: string
|
||||
url: string
|
||||
visible: string
|
||||
menuName: string;
|
||||
menuType: string;
|
||||
orderNum: number;
|
||||
parentId: string;
|
||||
perms: string;
|
||||
terminalType: string;
|
||||
url: string;
|
||||
visible: string;
|
||||
}
|
||||
|
||||
/** 新角色信息请求实体 */
|
||||
export interface RoleAddReq {
|
||||
remark: string
|
||||
roleCode: string
|
||||
roleKey: string
|
||||
roleName: string
|
||||
status: number
|
||||
remark: string;
|
||||
roleCode: string;
|
||||
roleKey: string;
|
||||
roleName: string;
|
||||
status: number;
|
||||
}
|
||||
|
||||
/** 角色信息更新请求实体 */
|
||||
export interface RoleUpdateReq {
|
||||
remark: string
|
||||
roleCode: string
|
||||
roleKey: string
|
||||
roleName: string
|
||||
status: number
|
||||
remark: string;
|
||||
roleCode: string;
|
||||
roleKey: string;
|
||||
roleName: string;
|
||||
status: number;
|
||||
}
|
||||
|
||||
/** 新角色信息响应实体 */
|
||||
export interface RoleNewResp {
|
||||
remark: string
|
||||
roleCode: string
|
||||
roleId: string
|
||||
roleKey: string
|
||||
roleName: string
|
||||
status: string
|
||||
isSystem?: boolean
|
||||
remark: string;
|
||||
roleCode: string;
|
||||
roleId: string;
|
||||
roleKey: string;
|
||||
roleName: string;
|
||||
status: string;
|
||||
isSystem?: boolean;
|
||||
}
|
||||
|
||||
/** 角色菜单绑定请求 */
|
||||
export interface RoleBindMenuReq {
|
||||
menuIds: string[]
|
||||
roleId: string
|
||||
menuIds: string[];
|
||||
roleId: string;
|
||||
}
|
||||
|
||||
/** 角色查询参数(新接口) */
|
||||
export interface RoleNewQuery {
|
||||
roleName?: string
|
||||
roleName?: string;
|
||||
}
|
||||
|
||||
// 岗位相关类型定义
|
||||
export interface PostVO {
|
||||
postId: string
|
||||
postName: string
|
||||
postSort: number
|
||||
remark: string
|
||||
status: string | number
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
postId: string;
|
||||
postName: string;
|
||||
postSort: number;
|
||||
remark: string;
|
||||
status: string | number;
|
||||
createTime?: string;
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
export interface PostPageQuery {
|
||||
postName?: string
|
||||
page?: number
|
||||
size?: number
|
||||
postName?: string;
|
||||
page?: number;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export interface PostAddReq {
|
||||
postName: string
|
||||
postSort: number
|
||||
remark: string
|
||||
status: number
|
||||
postName: string;
|
||||
postSort: number;
|
||||
remark: string;
|
||||
status: number;
|
||||
}
|
||||
|
||||
export interface PostUpdateReq {
|
||||
postName: string
|
||||
postSort: number
|
||||
remark: string
|
||||
status: number
|
||||
postName: string;
|
||||
postSort: number;
|
||||
remark: string;
|
||||
status: number;
|
||||
}
|
||||
|
|
|
@ -36,4 +36,4 @@ export function updateUserNew(userId: string, data: T.UserNewUpdateReq) {
|
|||
/** @desc 删除用户信息 */
|
||||
export function deleteUserNew(userId: string) {
|
||||
return http.del(`${BASE_URL}/${userId}`)
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
import http from '@/utils/http'
|
||||
import type * as T from '@/types/training.d'
|
||||
|
||||
const BASE_URL = '/training'
|
||||
|
||||
/** @desc 分页查询培训计划列表 */
|
||||
export function pageTrainingPlan(query: T.TrainingPlanPageQuery) {
|
||||
return http.get<T.TrainingPlanResp[]>(`${BASE_URL}/plan/page`, query)
|
||||
}
|
||||
|
||||
/** @desc 查询培训计划列表 */
|
||||
export function listTrainingPlan(query?: T.TrainingPlanPageQuery) {
|
||||
return http.get<T.TrainingPlanResp[]>(`${BASE_URL}/plan/list`, query)
|
||||
}
|
||||
|
||||
/** @desc 查询培训计划详情 */
|
||||
export function getTrainingPlanDetail(planId: string) {
|
||||
return http.get<T.TrainingPlanResp>(`${BASE_URL}/plan/detail/${planId}`)
|
||||
}
|
||||
|
||||
/** @desc 新增培训计划 */
|
||||
export function createTrainingPlan(data: T.TrainingPlanReq) {
|
||||
return http.post(`${BASE_URL}/plan`, data)
|
||||
}
|
||||
|
||||
/** @desc 更新培训计划 */
|
||||
export function updateTrainingPlan(planId: string, data: T.TrainingPlanReq) {
|
||||
return http.put(`${BASE_URL}/plan/${planId}`, data)
|
||||
}
|
||||
|
||||
/** @desc 删除培训计划 */
|
||||
export function deleteTrainingPlan(planId: string) {
|
||||
return http.del(`${BASE_URL}/plan/${planId}`)
|
||||
}
|
||||
|
||||
/** @desc 发布培训计划 */
|
||||
export function publishTrainingPlan(planId: string) {
|
||||
return http.put(`${BASE_URL}/plan/${planId}/publish`)
|
||||
}
|
||||
|
||||
/** @desc 取消培训计划 */
|
||||
export function cancelTrainingPlan(planId: string) {
|
||||
return http.put(`${BASE_URL}/plan/${planId}/cancel`)
|
||||
}
|
|
@ -39,18 +39,18 @@ const breadcrumbList = computed(() => {
|
|||
if (route.path.startsWith('/redirect/')) {
|
||||
return []
|
||||
}
|
||||
|
||||
|
||||
const cloneRoutes = JSON.parse(JSON.stringify(routes)) as RouteLocationMatched[]
|
||||
const obj = findTree(cloneRoutes, (i) => i.path === route.path)
|
||||
|
||||
|
||||
// 获取当前节点的所有上级节点集合,包含当前节点
|
||||
const arr = obj ? obj.nodes.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false) : []
|
||||
|
||||
|
||||
// 如果有home路由且不是重复的,则添加到开头
|
||||
if (home.value && !arr.some((item) => item.path === home.value?.path)) {
|
||||
if (home.value && !arr.some(item => item.path === home.value?.path)) {
|
||||
return [home.value, ...arr]
|
||||
}
|
||||
|
||||
|
||||
return arr
|
||||
})
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
:loading="loadingImageSources"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
|
||||
<a-form-item label="目标项目" required>
|
||||
<a-tree-select
|
||||
v-model="form.projectId"
|
||||
|
@ -31,7 +31,7 @@
|
|||
@change="onProjectChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
|
||||
<a-form-item label="目标组件">
|
||||
<a-select
|
||||
v-model="form.componentId"
|
||||
|
@ -40,11 +40,11 @@
|
|||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
|
||||
<a-form-item label="上传用户">
|
||||
<a-input v-model="form.uploadUser" placeholder="请输入上传用户" />
|
||||
</a-form-item>
|
||||
|
||||
|
||||
<a-form-item label="位置信息">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
|
@ -58,15 +58,15 @@
|
|||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
|
||||
|
||||
<a-form-item label="导入设置">
|
||||
<a-checkbox-group v-model="form.settings">
|
||||
<a-checkbox value="autoAnnotate">导入后自动标注</a-checkbox>
|
||||
<a-checkbox value="overwrite">覆盖同名文件</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="form.settings.includes('autoAnnotate')" label="标注类型">
|
||||
|
||||
<a-form-item label="标注类型" v-if="form.settings.includes('autoAnnotate')">
|
||||
<a-select
|
||||
v-model="form.annotationTypes"
|
||||
:options="defectTypeOptions"
|
||||
|
@ -75,7 +75,7 @@
|
|||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
|
||||
<!-- 文件上传区域 -->
|
||||
<div class="upload-section">
|
||||
<a-upload
|
||||
|
@ -88,9 +88,9 @@
|
|||
>
|
||||
<template #upload-button>
|
||||
<div class="upload-area">
|
||||
<div class="upload-drag-icon">
|
||||
<IconUpload size="48" />
|
||||
</div>
|
||||
<div class="upload-drag-icon">
|
||||
<icon-upload size="48" />
|
||||
</div>
|
||||
<div class="upload-text">
|
||||
<p>点击或拖拽图像文件到此区域</p>
|
||||
<p class="upload-hint">支持 JPG、PNG、JPEG 格式,可同时选择多个文件</p>
|
||||
|
@ -99,14 +99,14 @@
|
|||
</template>
|
||||
</a-upload>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 文件列表 -->
|
||||
<div v-if="fileList.length > 0" class="file-list">
|
||||
<div class="file-list" v-if="fileList.length > 0">
|
||||
<div class="list-header">
|
||||
<h4>待导入文件 ({{ fileList.length }})</h4>
|
||||
<a-button type="text" @click="clearFiles">
|
||||
<template #icon>
|
||||
<IconDelete />
|
||||
<icon-delete />
|
||||
</template>
|
||||
清空
|
||||
</a-button>
|
||||
|
@ -133,16 +133,16 @@
|
|||
@click="removeFile(index)"
|
||||
>
|
||||
<template #icon>
|
||||
<IconClose />
|
||||
<icon-close />
|
||||
</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 导入进度 -->
|
||||
<div v-if="importing" class="import-progress">
|
||||
<div class="import-progress" v-if="importing">
|
||||
<a-progress
|
||||
:percent="importProgress"
|
||||
:status="importStatus"
|
||||
|
@ -150,17 +150,17 @@
|
|||
/>
|
||||
<p class="progress-text">{{ progressText }}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 导入结果 -->
|
||||
<div v-if="importResult" class="import-result">
|
||||
<div class="import-result" v-if="importResult">
|
||||
<a-alert
|
||||
:type="importResult.failed.length > 0 ? 'warning' : 'success'"
|
||||
:title="getResultTitle()"
|
||||
:description="getResultDescription()"
|
||||
show-icon
|
||||
/>
|
||||
|
||||
<div v-if="importResult.failed.length > 0" class="result-details">
|
||||
|
||||
<div class="result-details" v-if="importResult.failed.length > 0">
|
||||
<h4>失败文件列表:</h4>
|
||||
<div class="failed-list">
|
||||
<div
|
||||
|
@ -180,22 +180,22 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import {
|
||||
IconClose,
|
||||
IconDelete,
|
||||
IconUpload,
|
||||
import {
|
||||
IconUpload,
|
||||
IconDelete,
|
||||
IconClose
|
||||
} from '@arco-design/web-vue/es/icon'
|
||||
import {
|
||||
getImageSources,
|
||||
getProjectTree,
|
||||
importImages,
|
||||
import {
|
||||
getProjectTree,
|
||||
importImages,
|
||||
getImageSources
|
||||
} from '@/apis/industrial-image'
|
||||
import type {
|
||||
ImageImportParams,
|
||||
import type {
|
||||
ProjectTreeNode,
|
||||
IndustrialImage,
|
||||
ProjectTreeNode,
|
||||
ImageImportParams
|
||||
} from '@/apis/industrial-image/type'
|
||||
|
||||
interface Props {
|
||||
|
@ -233,54 +233,54 @@ const form = ref({
|
|||
latitude: '',
|
||||
longitude: '',
|
||||
settings: [] as string[],
|
||||
annotationTypes: [] as string[],
|
||||
annotationTypes: [] as string[]
|
||||
})
|
||||
|
||||
const fileList = ref<FileItem[]>([])
|
||||
const projectTree = ref<ProjectTreeNode[]>([])
|
||||
const defectTypes = ref<Array<{ id: string, name: string, description?: string, color?: string }>>([])
|
||||
const imageSources = ref<Array<{ id: string, name: string, code: string }>>([])
|
||||
const defectTypes = ref<Array<{ id: string; name: string; description?: string; color?: string }>>([])
|
||||
const imageSources = ref<Array<{ id: string; name: string; code: string }>>([])
|
||||
const loadingImageSources = ref(false)
|
||||
|
||||
// 计算属性
|
||||
const visible = computed({
|
||||
get: () => props.visible,
|
||||
set: (value) => emit('update:visible', value),
|
||||
set: (value) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const componentOptions = computed(() => {
|
||||
const findComponents = (nodes: ProjectTreeNode[]): Array<{ label: string, value: string }> => {
|
||||
const options: Array<{ label: string, value: string }> = []
|
||||
|
||||
nodes.forEach((node) => {
|
||||
const findComponents = (nodes: ProjectTreeNode[]): Array<{ label: string; value: string }> => {
|
||||
const options: Array<{ label: string; value: string }> = []
|
||||
|
||||
nodes.forEach(node => {
|
||||
if (node.type === 'component' || node.type === 'blade' || node.type === 'tower') {
|
||||
options.push({
|
||||
label: node.name,
|
||||
value: node.id,
|
||||
value: node.id
|
||||
})
|
||||
}
|
||||
if (node.children) {
|
||||
options.push(...findComponents(node.children))
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
|
||||
return form.value.projectId ? findComponents(projectTree.value) : []
|
||||
})
|
||||
|
||||
const defectTypeOptions = computed(() => {
|
||||
return defectTypes.value.map((type) => ({
|
||||
return defectTypes.value.map(type => ({
|
||||
label: type.name,
|
||||
value: type.id,
|
||||
value: type.id
|
||||
}))
|
||||
})
|
||||
|
||||
const imageSourceOptions = computed(() => {
|
||||
return imageSources.value.map((source) => ({
|
||||
return imageSources.value.map(source => ({
|
||||
label: source.name,
|
||||
value: source.code,
|
||||
value: source.code
|
||||
}))
|
||||
})
|
||||
|
||||
|
@ -326,25 +326,25 @@ const onProjectChange = (value: string) => {
|
|||
|
||||
const handleFileChange = (fileList: any) => {
|
||||
const files = Array.from(fileList.target?.files || []) as File[]
|
||||
|
||||
files.forEach((file) => {
|
||||
|
||||
files.forEach(file => {
|
||||
if (!file.type.startsWith('image/')) {
|
||||
Message.warning(`文件 ${file.name} 不是图像文件`)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (file.size > 10 * 1024 * 1024) { // 10MB
|
||||
Message.warning(`文件 ${file.name} 大小超过10MB`)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
const fileItem: FileItem = {
|
||||
file,
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
preview: e.target?.result as string,
|
||||
preview: e.target?.result as string
|
||||
}
|
||||
fileList.value.push(fileItem)
|
||||
}
|
||||
|
@ -365,7 +365,7 @@ const formatFileSize = (bytes: number): string => {
|
|||
const k = 1024
|
||||
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
const handleImport = async () => {
|
||||
|
@ -373,25 +373,25 @@ const handleImport = async () => {
|
|||
Message.warning('请选择图像来源')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!form.value.projectId) {
|
||||
Message.warning('请选择目标项目')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (fileList.value.length === 0) {
|
||||
Message.warning('请选择要导入的图像文件')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
importing.value = true
|
||||
importProgress.value = 0
|
||||
importStatus.value = 'normal'
|
||||
progressText.value = '正在导入图像...'
|
||||
importResult.value = null
|
||||
|
||||
|
||||
try {
|
||||
const files = fileList.value.map((item) => item.file)
|
||||
const files = fileList.value.map(item => item.file)
|
||||
const params: ImageImportParams = {
|
||||
imageSource: form.value.imageSource,
|
||||
projectId: form.value.projectId,
|
||||
|
@ -401,23 +401,23 @@ const handleImport = async () => {
|
|||
latitude: form.value.latitude || undefined,
|
||||
longitude: form.value.longitude || undefined,
|
||||
autoAnnotate: form.value.settings.includes('autoAnnotate'),
|
||||
annotationTypes: form.value.settings.includes('autoAnnotate') ? form.value.annotationTypes : undefined,
|
||||
annotationTypes: form.value.settings.includes('autoAnnotate') ? form.value.annotationTypes : undefined
|
||||
}
|
||||
|
||||
|
||||
// 模拟进度更新
|
||||
const progressInterval = setInterval(() => {
|
||||
if (importProgress.value < 90) {
|
||||
importProgress.value += 10
|
||||
}
|
||||
}, 200)
|
||||
|
||||
|
||||
const res = await importImages(files, params)
|
||||
|
||||
|
||||
clearInterval(progressInterval)
|
||||
importProgress.value = 100
|
||||
importStatus.value = 'success'
|
||||
progressText.value = '导入完成!'
|
||||
|
||||
|
||||
// 注意:真实API返回的数据结构可能不同,需要根据实际情况调整
|
||||
const mockResult = {
|
||||
success: files.map((file, index) => ({
|
||||
|
@ -428,15 +428,16 @@ const handleImport = async () => {
|
|||
type: file.type,
|
||||
projectId: form.value.projectId,
|
||||
componentId: form.value.componentId,
|
||||
createTime: new Date().toISOString(),
|
||||
createTime: new Date().toISOString()
|
||||
})),
|
||||
failed: [],
|
||||
failed: []
|
||||
}
|
||||
|
||||
|
||||
importResult.value = mockResult
|
||||
emit('importSuccess', mockResult)
|
||||
|
||||
|
||||
Message.success(`成功导入 ${files.length} 个图像文件`)
|
||||
|
||||
} catch (error) {
|
||||
console.error('导入失败:', error)
|
||||
importProgress.value = 100
|
||||
|
@ -453,7 +454,7 @@ const handleCancel = () => {
|
|||
Message.warning('正在导入中,请稍后再试')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
resetForm()
|
||||
visible.value = false
|
||||
}
|
||||
|
@ -468,7 +469,7 @@ const resetForm = () => {
|
|||
latitude: '',
|
||||
longitude: '',
|
||||
settings: [],
|
||||
annotationTypes: [],
|
||||
annotationTypes: []
|
||||
}
|
||||
fileList.value = []
|
||||
importResult.value = null
|
||||
|
@ -479,7 +480,7 @@ const resetForm = () => {
|
|||
|
||||
const getResultTitle = () => {
|
||||
if (!importResult.value) return ''
|
||||
|
||||
|
||||
const { success, failed } = importResult.value
|
||||
if (failed.length === 0) {
|
||||
return `导入成功!共导入 ${success.length} 个图像文件`
|
||||
|
@ -490,7 +491,7 @@ const getResultTitle = () => {
|
|||
|
||||
const getResultDescription = () => {
|
||||
if (!importResult.value) return ''
|
||||
|
||||
|
||||
const { success, failed } = importResult.value
|
||||
if (failed.length === 0) {
|
||||
return '所有图像文件都已成功导入到指定项目中'
|
||||
|
@ -517,11 +518,11 @@ watch(visible, (newValue) => {
|
|||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
.upload-section {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
|
||||
.upload-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -533,24 +534,24 @@ watch(visible, (newValue) => {
|
|||
background: #fafafa;
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
|
||||
|
||||
&:hover {
|
||||
border-color: #1890ff;
|
||||
background: #f0f8ff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.upload-drag-icon {
|
||||
margin-bottom: 16px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
|
||||
.upload-text {
|
||||
text-align: center;
|
||||
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
|
||||
|
||||
&.upload-hint {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
|
@ -558,14 +559,14 @@ watch(visible, (newValue) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.file-list {
|
||||
margin-top: 20px;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
.list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -573,37 +574,37 @@ watch(visible, (newValue) => {
|
|||
padding: 12px 16px;
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.list-content {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.file-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
.file-preview {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
@ -611,81 +612,81 @@ watch(visible, (newValue) => {
|
|||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e8e8e8;
|
||||
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.file-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
.file-name {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
|
||||
.file-size {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
|
||||
.import-progress {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.progress-text {
|
||||
margin-top: 8px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
.import-result {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
|
||||
.result-details {
|
||||
margin-top: 16px;
|
||||
|
||||
|
||||
h4 {
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.failed-list {
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
|
||||
.failed-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.filename {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
|
||||
.error {
|
||||
font-size: 12px;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -8,7 +8,7 @@
|
|||
<button class="dialog-close-btn" @click="closeDialog">×</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="dialog-body">
|
||||
<!-- 左侧步骤指示器 -->
|
||||
<div class="steps-sidebar">
|
||||
|
@ -28,22 +28,22 @@
|
|||
<span class="step-text">设置信息</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 右侧内容区域 -->
|
||||
<div class="dialog-content-container">
|
||||
<!-- 步骤1: 选择部件 -->
|
||||
<div v-if="currentStep === 1" class="dialog-content">
|
||||
<div class="select-part-content">
|
||||
<div class="section-title">选择要导入图像的部件:</div>
|
||||
|
||||
|
||||
<div class="parts-container">
|
||||
<div
|
||||
v-for="part in availableParts"
|
||||
:key="getPartId(part)"
|
||||
<div
|
||||
v-for="part in availableParts"
|
||||
:key="getPartId(part)"
|
||||
class="part-item"
|
||||
:class="{ selected: String(selectedPartId) === String(getPartId(part)) }"
|
||||
:title="`部件ID: ${getPartId(part)}, 选中: ${String(selectedPartId) === String(getPartId(part))}`"
|
||||
@click="selectPart(part)"
|
||||
:title="`部件ID: ${getPartId(part)}, 选中: ${String(selectedPartId) === String(getPartId(part))}`"
|
||||
>
|
||||
<div class="part-icon">
|
||||
<svg v-if="part.partType === 'engine'" width="40" height="40" viewBox="0 0 70 70" xmlns="http://www.w3.org/2000/svg">
|
||||
|
@ -66,8 +66,8 @@
|
|||
<div class="part-name">{{ getPartName(part) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedPart" class="part-info">
|
||||
|
||||
<div class="part-info" v-if="selectedPart">
|
||||
<div class="info-line">
|
||||
<div class="info-label">部件:</div>
|
||||
<div class="info-value">{{ getPartName(selectedPart) }}</div>
|
||||
|
@ -79,38 +79,38 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 步骤2: 导入图像 -->
|
||||
<div v-if="currentStep === 2" class="dialog-content">
|
||||
<div class="import-image-content">
|
||||
<div class="section-title">导入图像到部件"{{ selectedPart ? getPartName(selectedPart) : '' }}"</div>
|
||||
|
||||
|
||||
<div class="image-actions">
|
||||
<button class="action-button" @click="handleAddImages">
|
||||
<span class="button-icon">+</span>
|
||||
添加图像
|
||||
</button>
|
||||
<button class="action-button" :disabled="!hasSelectedImages" @click="handleRemoveImages">
|
||||
<button class="action-button" @click="handleRemoveImages" :disabled="!hasSelectedImages">
|
||||
<span class="button-icon">-</span>
|
||||
移除图像
|
||||
</button>
|
||||
<!-- 隐藏的文件输入框 -->
|
||||
<input
|
||||
ref="fileInput"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
style="display: none;"
|
||||
<input
|
||||
type="file"
|
||||
ref="fileInput"
|
||||
accept="image/*"
|
||||
style="display: none;"
|
||||
multiple
|
||||
@change="handleFileSelected"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="image-list-container">
|
||||
<table class="image-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="checkbox-column">
|
||||
<input type="checkbox" :checked="allImagesSelected" @change="toggleSelectAll">
|
||||
<input type="checkbox" @change="toggleSelectAll" :checked="allImagesSelected">
|
||||
</th>
|
||||
<th class="preview-column">预览</th>
|
||||
<th>图像名称</th>
|
||||
|
@ -123,7 +123,7 @@
|
|||
<tbody>
|
||||
<tr v-for="(image, index) in importImages" :key="index" @click="toggleImageSelection(image)">
|
||||
<td>
|
||||
<input v-model="image.selected" type="checkbox" @click.stop>
|
||||
<input type="checkbox" v-model="image.selected" @click.stop>
|
||||
</td>
|
||||
<td class="preview-cell">
|
||||
<img v-if="image.previewUrl" :src="image.previewUrl" class="preview-thumbnail" alt="预览">
|
||||
|
@ -140,22 +140,22 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 步骤3: 设置图像采集信息 -->
|
||||
<div v-if="currentStep === 3" class="dialog-content">
|
||||
<div class="image-info-content">
|
||||
<div class="section-title">步骤3: 设置图像采集信息</div>
|
||||
|
||||
|
||||
<div class="form-container">
|
||||
<div class="form-row">
|
||||
<div class="form-label">拍摄时间范围</div>
|
||||
<div class="form-input datetime-range">
|
||||
<input v-model="imageInfo.startTime" type="text" placeholder="开始时间">
|
||||
<input type="text" v-model="imageInfo.startTime" placeholder="开始时间">
|
||||
<span class="range-separator">至</span>
|
||||
<input v-model="imageInfo.endTime" type="text" placeholder="结束时间">
|
||||
<input type="text" v-model="imageInfo.endTime" placeholder="结束时间">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-label">天气</div>
|
||||
<div class="form-input">
|
||||
|
@ -167,35 +167,35 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-label">环境温度(°C)</div>
|
||||
<div class="form-input temperature-range">
|
||||
<div class="range-input-group">
|
||||
<button class="range-btn" @click="imageInfo.minTemperature = Math.max(0, imageInfo.minTemperature - 1)">-</button>
|
||||
<input v-model="imageInfo.minTemperature" type="number" step="0.1" min="0" max="50">
|
||||
<input type="number" v-model="imageInfo.minTemperature" step="0.1" min="0" max="50">
|
||||
<button class="range-btn" @click="imageInfo.minTemperature = Math.min(50, imageInfo.minTemperature + 1)">+</button>
|
||||
</div>
|
||||
<span class="range-separator">~</span>
|
||||
<div class="range-input-group">
|
||||
<button class="range-btn" @click="imageInfo.maxTemperature = Math.max(0, imageInfo.maxTemperature - 1)">-</button>
|
||||
<input v-model="imageInfo.maxTemperature" type="number" step="0.1" min="0" max="50">
|
||||
<input type="number" v-model="imageInfo.maxTemperature" step="0.1" min="0" max="50">
|
||||
<button class="range-btn" @click="imageInfo.maxTemperature = Math.min(50, imageInfo.maxTemperature + 1)">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-label">湿度(%)</div>
|
||||
<div class="form-input">
|
||||
<div class="range-input-group">
|
||||
<button class="range-btn" @click="imageInfo.humidity = Math.max(0, imageInfo.humidity - 1)">-</button>
|
||||
<input v-model="imageInfo.humidity" type="number" min="0" max="100">
|
||||
<input type="number" v-model="imageInfo.humidity" min="0" max="100">
|
||||
<button class="range-btn" @click="imageInfo.humidity = Math.min(100, imageInfo.humidity + 1)">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-label">风力</div>
|
||||
<div class="form-input">
|
||||
|
@ -212,43 +212,43 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-label">拍摄方式</div>
|
||||
<div class="form-input capture-method">
|
||||
<label class="radio-option">
|
||||
<input v-model="imageInfo.captureMethod" type="radio" value="无人机航拍">
|
||||
<input type="radio" v-model="imageInfo.captureMethod" value="无人机航拍">
|
||||
<span class="radio-label">无人机航拍</span>
|
||||
</label>
|
||||
<label class="radio-option">
|
||||
<input v-model="imageInfo.captureMethod" type="radio" value="人工拍摄">
|
||||
<input type="radio" v-model="imageInfo.captureMethod" value="人工拍摄">
|
||||
<span class="radio-label">人工拍摄</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-label">拍摄距离(米)</div>
|
||||
<div class="form-input">
|
||||
<div class="range-input-group">
|
||||
<button class="range-btn" @click="imageInfo.captureDistance = Math.max(0, imageInfo.captureDistance - 1)">-</button>
|
||||
<input v-model="imageInfo.captureDistance" type="number" min="0">
|
||||
<input type="number" v-model="imageInfo.captureDistance" min="0">
|
||||
<button class="range-btn" @click="imageInfo.captureDistance = imageInfo.captureDistance + 1">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-label">采集员</div>
|
||||
<div class="form-input">
|
||||
<input v-model="imageInfo.operator" type="text">
|
||||
<input type="text" v-model="imageInfo.operator">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-label">相机型号</div>
|
||||
<div class="form-input">
|
||||
<input v-model="imageInfo.cameraModel" type="text">
|
||||
<input type="text" v-model="imageInfo.cameraModel">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -256,30 +256,24 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="dialog-footer">
|
||||
<button
|
||||
v-if="currentStep > 1"
|
||||
class="dialog-button"
|
||||
<button
|
||||
v-if="currentStep > 1"
|
||||
class="dialog-button"
|
||||
@click="currentStep--"
|
||||
>
|
||||
上一步
|
||||
</button>
|
||||
<button
|
||||
v-if="currentStep < 3"
|
||||
class="dialog-button"
|
||||
:disabled="!canGoNext"
|
||||
>上一步</button>
|
||||
<button
|
||||
v-if="currentStep < 3"
|
||||
class="dialog-button"
|
||||
@click="nextStep"
|
||||
>
|
||||
下一步
|
||||
</button>
|
||||
<button
|
||||
v-if="currentStep === 3"
|
||||
class="dialog-button primary"
|
||||
:disabled="!canGoNext"
|
||||
>下一步</button>
|
||||
<button
|
||||
v-if="currentStep === 3"
|
||||
class="dialog-button primary"
|
||||
@click="finishImport"
|
||||
>
|
||||
完成导入
|
||||
</button>
|
||||
>完成导入</button>
|
||||
<button class="dialog-button" @click="closeDialog">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -287,7 +281,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, reactive, ref } from 'vue'
|
||||
import { ref, computed, reactive, onBeforeUnmount } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
|
||||
// 定义部件接口
|
||||
|
@ -358,7 +352,7 @@ const selectedPartId = ref('')
|
|||
// 计算选中的部件对象
|
||||
const selectedPart = computed(() => {
|
||||
if (!selectedPartId.value) return null
|
||||
return props.availableParts?.find((part) => String(getPartId(part)) === String(selectedPartId.value))
|
||||
return props.availableParts?.find(part => String(getPartId(part)) === String(selectedPartId.value))
|
||||
})
|
||||
|
||||
// 待导入的图像列表
|
||||
|
@ -366,8 +360,8 @@ const importImages = ref<ImportImage[]>([])
|
|||
|
||||
// 图像采集信息
|
||||
const imageInfo = reactive<ImageInfo>({
|
||||
startTime: `${formatCurrentDate()} 00:00`,
|
||||
endTime: `${formatCurrentDate()} 23:59`,
|
||||
startTime: formatCurrentDate() + ' 00:00',
|
||||
endTime: formatCurrentDate() + ' 23:59',
|
||||
weather: '晴天',
|
||||
humidity: 50,
|
||||
minTemperature: 15,
|
||||
|
@ -377,7 +371,7 @@ const imageInfo = reactive<ImageInfo>({
|
|||
captureMethod: '无人机航拍',
|
||||
captureDistance: 50,
|
||||
operator: '',
|
||||
cameraModel: 'ILCE-7RM4',
|
||||
cameraModel: 'ILCE-7RM4'
|
||||
})
|
||||
|
||||
// 格式化当前日期
|
||||
|
@ -422,12 +416,12 @@ function getPartName(part: any): string {
|
|||
// 选择部件
|
||||
function selectPart(part: any) {
|
||||
const partId = getPartId(part)
|
||||
|
||||
|
||||
if (!part || !partId) {
|
||||
console.error('部件数据无效:', part)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
selectedPartId.value = String(partId)
|
||||
}
|
||||
|
||||
|
@ -440,10 +434,10 @@ function handleAddImages() {
|
|||
function handleFileSelected(event: Event) {
|
||||
const target = event.target as HTMLInputElement
|
||||
if (target.files && target.files.length > 0) {
|
||||
const newImages: ImportImage[] = Array.from(target.files).map((file) => {
|
||||
const newImages: ImportImage[] = Array.from(target.files).map(file => {
|
||||
// 生成文件的本地URL预览
|
||||
const previewUrl = URL.createObjectURL(file)
|
||||
|
||||
|
||||
// 创建一个新的图像对象
|
||||
return {
|
||||
file,
|
||||
|
@ -454,14 +448,14 @@ function handleFileSelected(event: Event) {
|
|||
timestamp: new Date().toISOString(),
|
||||
pixelSize: '155.00',
|
||||
selected: false,
|
||||
previewUrl,
|
||||
previewUrl
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// 添加到图像列表
|
||||
importImages.value = [...importImages.value, ...newImages]
|
||||
}
|
||||
|
||||
|
||||
// 重置文件输入,允许再次选择相同文件
|
||||
if (fileInput.value) fileInput.value.value = ''
|
||||
}
|
||||
|
@ -469,13 +463,13 @@ function handleFileSelected(event: Event) {
|
|||
// 移除选中的图像
|
||||
function handleRemoveImages() {
|
||||
// 清理被移除图像的URL对象
|
||||
importImages.value.filter((image) => image.selected).forEach((image) => {
|
||||
importImages.value.filter(image => image.selected).forEach(image => {
|
||||
if (image.previewUrl) {
|
||||
URL.revokeObjectURL(image.previewUrl)
|
||||
}
|
||||
})
|
||||
|
||||
importImages.value = importImages.value.filter((image) => !image.selected)
|
||||
|
||||
importImages.value = importImages.value.filter(image => !image.selected)
|
||||
}
|
||||
|
||||
// 切换图像选择状态
|
||||
|
@ -486,17 +480,17 @@ function toggleImageSelection(image: ImportImage) {
|
|||
// 全选/取消全选图像
|
||||
function toggleSelectAll(event: Event) {
|
||||
const checked = (event.target as HTMLInputElement).checked
|
||||
importImages.value.forEach((image) => image.selected = checked)
|
||||
importImages.value.forEach(image => image.selected = checked)
|
||||
}
|
||||
|
||||
// 判断是否有选中的图像
|
||||
const hasSelectedImages = computed(() => {
|
||||
return importImages.value.some((image) => image.selected)
|
||||
return importImages.value.some(image => image.selected)
|
||||
})
|
||||
|
||||
// 判断是否所有图像都被选中
|
||||
const allImagesSelected = computed(() => {
|
||||
return importImages.value.length > 0 && importImages.value.every((image) => image.selected)
|
||||
return importImages.value.length > 0 && importImages.value.every(image => image.selected)
|
||||
})
|
||||
|
||||
// 格式化日期
|
||||
|
@ -531,33 +525,33 @@ function finishImport() {
|
|||
Message.error('请选择部件')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (importImages.value.length === 0) {
|
||||
Message.error('请添加图像')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 准备导入的数据
|
||||
const files = importImages.value.map((image) => image.file!).filter(Boolean)
|
||||
|
||||
const files = importImages.value.map(image => image.file!).filter(Boolean)
|
||||
|
||||
// 构建统一的部件数据格式
|
||||
const partData = {
|
||||
partId: getPartId(selectedPart.value),
|
||||
id: getPartId(selectedPart.value), // 兼容字段
|
||||
name: getPartName(selectedPart.value),
|
||||
partName: getPartName(selectedPart.value), // 兼容字段
|
||||
partType: selectedPart.value.partType,
|
||||
partType: selectedPart.value.partType
|
||||
}
|
||||
|
||||
|
||||
// 发送导入事件
|
||||
emit('import-success', {
|
||||
part: partData,
|
||||
images: files,
|
||||
imageInfo: { ...imageInfo },
|
||||
imageInfo: { ...imageInfo }
|
||||
})
|
||||
|
||||
|
||||
Message.success('图像导入成功')
|
||||
|
||||
|
||||
// 关闭对话框
|
||||
closeDialog()
|
||||
}
|
||||
|
@ -573,19 +567,19 @@ function closeDialog() {
|
|||
function resetState() {
|
||||
currentStep.value = 1
|
||||
selectedPartId.value = ''
|
||||
|
||||
|
||||
// 清理图像URL对象
|
||||
importImages.value.forEach((image) => {
|
||||
importImages.value.forEach(image => {
|
||||
if (image.previewUrl) {
|
||||
URL.revokeObjectURL(image.previewUrl)
|
||||
}
|
||||
})
|
||||
importImages.value = []
|
||||
|
||||
|
||||
// 重置图像信息
|
||||
Object.assign(imageInfo, {
|
||||
startTime: `${formatCurrentDate()} 00:00`,
|
||||
endTime: `${formatCurrentDate()} 23:59`,
|
||||
startTime: formatCurrentDate() + ' 00:00',
|
||||
endTime: formatCurrentDate() + ' 23:59',
|
||||
weather: '晴天',
|
||||
humidity: 70,
|
||||
minTemperature: 20,
|
||||
|
@ -593,14 +587,14 @@ function resetState() {
|
|||
windPower: 0,
|
||||
captureMethod: '无人机拍摄',
|
||||
captureDistance: 15,
|
||||
operator: '',
|
||||
operator: ''
|
||||
})
|
||||
}
|
||||
|
||||
// 在组件卸载时清理URL对象
|
||||
onBeforeUnmount(() => {
|
||||
// 清理所有创建的对象URL以防止内存泄漏
|
||||
importImages.value.forEach((image) => {
|
||||
importImages.value.forEach(image => {
|
||||
if (image.previewUrl) {
|
||||
URL.revokeObjectURL(image.previewUrl)
|
||||
}
|
||||
|
@ -1133,4 +1127,4 @@ onBeforeUnmount(() => {
|
|||
.dialog-button.primary:hover {
|
||||
background-color: #2563eb;
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -1,4 +1,4 @@
|
|||
import IndustrialImageList from './index.vue'
|
||||
|
||||
export type { IndustrialImage } from './index.vue'
|
||||
export default IndustrialImageList
|
||||
export default IndustrialImageList
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<div class="industrial-image-list" :class="{ collapsed: isCollapsed }">
|
||||
<div v-if="!isCollapsed" class="header-actions">
|
||||
<div class="industrial-image-list" :class="{ 'collapsed': isCollapsed }">
|
||||
<div class="header-actions" v-if="!isCollapsed">
|
||||
<slot name="header-left">
|
||||
<a-button v-if="showImportButton" type="primary" @click="handleImportImages">
|
||||
<template #icon><IconUpload /></template>
|
||||
<template #icon><icon-upload /></template>
|
||||
导入图像
|
||||
</a-button>
|
||||
</slot>
|
||||
<div v-if="showSearch" class="search-bar">
|
||||
<div class="search-bar" v-if="showSearch">
|
||||
<a-input-search
|
||||
v-model="searchKeyword"
|
||||
placeholder="输入关键字搜索"
|
||||
|
@ -18,24 +18,24 @@
|
|||
</div>
|
||||
<slot name="header-right"></slot>
|
||||
<div class="collapse-button">
|
||||
<a-button
|
||||
type="text"
|
||||
<a-button
|
||||
type="text"
|
||||
@click="toggleCollapse"
|
||||
>
|
||||
<template #icon>
|
||||
<IconUp />
|
||||
<icon-up />
|
||||
</template>
|
||||
收起
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="!isCollapsed" class="image-grid">
|
||||
|
||||
<div class="image-grid" v-show="!isCollapsed">
|
||||
<div v-if="imageList.length === 0" class="empty-data">
|
||||
<IconImage class="empty-icon" />
|
||||
<icon-image class="empty-icon" />
|
||||
<p>{{ emptyText }}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="image-thumbnails">
|
||||
<div
|
||||
v-for="image in imageList"
|
||||
|
@ -45,14 +45,14 @@
|
|||
@click="handleImageSelect(image)"
|
||||
>
|
||||
<div class="thumbnail-image">
|
||||
<img
|
||||
:src="getImageUrl(image.imagePath)"
|
||||
:alt="image.imageName"
|
||||
<img
|
||||
:src="getImageUrl(image.imagePath)"
|
||||
:alt="image.imageName"
|
||||
@error="handleImageError"
|
||||
@load="handleImageLoad"
|
||||
/>
|
||||
<div v-if="!image.imagePath" class="image-placeholder">
|
||||
<IconImage />
|
||||
<div class="image-placeholder" v-if="!image.imagePath">
|
||||
<icon-image />
|
||||
<span>暂无图像</span>
|
||||
</div>
|
||||
<div class="thumbnail-overlay">
|
||||
|
@ -62,13 +62,13 @@
|
|||
</div>
|
||||
<div class="image-actions">
|
||||
<a-button v-if="showPreviewAction" type="text" size="small" @click.stop="handleImagePreview(image)">
|
||||
<IconEye />
|
||||
<icon-eye />
|
||||
</a-button>
|
||||
<a-button v-if="showProcessAction" type="text" size="small" @click.stop="handleImageProcess(image)">
|
||||
<IconSettings />
|
||||
<icon-settings />
|
||||
</a-button>
|
||||
<a-button v-if="showDeleteAction" type="text" size="small" status="danger" @click.stop="handleImageDelete(image)">
|
||||
<IconDelete />
|
||||
<icon-delete />
|
||||
</a-button>
|
||||
<slot name="item-actions" :image="image"></slot>
|
||||
</div>
|
||||
|
@ -81,7 +81,7 @@
|
|||
<span v-if="image.defectCount" class="defect-count">缺陷: {{ image.defectCount }}</span>
|
||||
<slot name="item-meta" :image="image"></slot>
|
||||
</div>
|
||||
<div v-if="image.partName || image.shootingTime" class="thumbnail-extra">
|
||||
<div class="thumbnail-extra" v-if="image.partName || image.shootingTime">
|
||||
<span v-if="image.partName" class="part-name">{{ image.partName }}</span>
|
||||
<span v-if="image.shootingTime" class="capture-time">{{ formatTime(image.shootingTime) }}</span>
|
||||
</div>
|
||||
|
@ -90,15 +90,15 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 收起状态下的展开按钮 -->
|
||||
<div v-if="isCollapsed" class="expand-button-container">
|
||||
<a-button
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="toggleCollapse"
|
||||
>
|
||||
<template #icon>
|
||||
<IconDown />
|
||||
<icon-down />
|
||||
</template>
|
||||
展开图像列表
|
||||
</a-button>
|
||||
|
@ -108,14 +108,14 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
IconDelete,
|
||||
IconDown,
|
||||
IconEye,
|
||||
IconImage,
|
||||
IconSettings,
|
||||
IconUp,
|
||||
import {
|
||||
IconUpload,
|
||||
IconImage,
|
||||
IconEye,
|
||||
IconSettings,
|
||||
IconDelete,
|
||||
IconUp,
|
||||
IconDown
|
||||
} from '@arco-design/web-vue/es/icon'
|
||||
|
||||
export interface IndustrialImage {
|
||||
|
@ -133,40 +133,40 @@ export interface IndustrialImage {
|
|||
const props = defineProps({
|
||||
imageList: {
|
||||
type: Array as () => IndustrialImage[],
|
||||
default: () => [],
|
||||
default: () => []
|
||||
},
|
||||
selectedImageId: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
baseUrl: {
|
||||
type: String,
|
||||
default: 'http://localhost:8080',
|
||||
default: 'http://pms.dtyx.net:9158'
|
||||
},
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: '暂无图像数据',
|
||||
default: '暂无图像数据'
|
||||
},
|
||||
showImportButton: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
showSearch: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
showPreviewAction: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
showProcessAction: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
showDeleteAction: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -252,7 +252,7 @@ const formatTime = (timeString: string): string => {
|
|||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
} catch {
|
||||
return timeString
|
||||
|
@ -297,7 +297,7 @@ const formatTime = (timeString: string): string => {
|
|||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 收起状态下的展开按钮容器 */
|
||||
.expand-button-container {
|
||||
position: absolute;
|
||||
|
@ -312,21 +312,21 @@ const formatTime = (timeString: string): string => {
|
|||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
height: calc(100% - 60px); /* 减去header-actions的高度 */
|
||||
|
||||
|
||||
// 美化滚动条
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f1f5f9;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 4px;
|
||||
|
||||
|
||||
&:hover {
|
||||
background: #94a3b8;
|
||||
}
|
||||
|
@ -526,4 +526,4 @@ const formatTime = (timeString: string): string => {
|
|||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -1,105 +1,102 @@
|
|||
<template>
|
||||
<div class="turbine-grid-container">
|
||||
<div class="turbine-grid">
|
||||
<div
|
||||
v-for="turbine in turbines" :key="turbine.id" class="turbine-card"
|
||||
:class="getStatusClass(turbine.status)"
|
||||
>
|
||||
<div class="turbine-status-badge" :class="`status-${turbine.status}`">
|
||||
{{ getStatusText(turbine.status) }}
|
||||
<div class="turbine-grid-container">
|
||||
<div class="turbine-grid">
|
||||
<div v-for="turbine in turbines" :key="turbine.id" class="turbine-card"
|
||||
:class="getStatusClass(turbine.status)">
|
||||
<div class="turbine-status-badge" :class="`status-${turbine.status}`">
|
||||
{{ getStatusText(turbine.status) }}
|
||||
</div>
|
||||
|
||||
<div class="turbine-icon">
|
||||
<img src="/static/images/wind-turbine-icon.svg" alt="风机图标" class="turbine-image" />
|
||||
</div>
|
||||
|
||||
<div class="turbine-info">
|
||||
<div class="turbine-number">
|
||||
<a-input v-model="turbine.turbineNo" size="small" class="turbine-input" placeholder="请输入机组编号"
|
||||
@change="handleTurbineNoChange(turbine)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="turbine-actions">
|
||||
<a-button type="text" size="mini" @click="openMapModal(turbine)" title="地图选点">
|
||||
<template #icon><icon-location /></template>
|
||||
</a-button>
|
||||
<a-button type="text" size="mini" @click="editTurbine(turbine)" title="编辑">
|
||||
<template #icon><icon-edit /></template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="turbine-icon">
|
||||
<img src="/static/images/wind-turbine-icon.svg" alt="风机图标" class="turbine-image" />
|
||||
<!-- 添加新机组按钮 -->
|
||||
<div v-if="showAddButton" class="turbine-card add-turbine-card" @click="addTurbine">
|
||||
<div class="add-icon">
|
||||
<icon-plus />
|
||||
</div>
|
||||
<div class="add-text">添加机组</div>
|
||||
</div>
|
||||
|
||||
<div class="turbine-info">
|
||||
<div class="turbine-number">
|
||||
<a-input
|
||||
v-model="turbine.turbineNo" size="small" class="turbine-input" placeholder="请输入机组编号"
|
||||
@change="handleTurbineNoChange(turbine)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="turbine-actions">
|
||||
<a-button type="text" size="mini" title="地图选点" @click="openMapModal(turbine)">
|
||||
<template #icon><icon-location /></template>
|
||||
</a-button>
|
||||
<a-button type="text" size="mini" title="编辑" @click="editTurbine(turbine)">
|
||||
<template #icon><icon-edit /></template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加新机组按钮 -->
|
||||
<div v-if="showAddButton" class="turbine-card add-turbine-card" @click="addTurbine">
|
||||
<div class="add-icon">
|
||||
<icon-plus />
|
||||
</div>
|
||||
<div class="add-text">添加机组</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
|
||||
interface Turbine {
|
||||
id: number
|
||||
turbineNo: string
|
||||
status: 0 | 1 | 2 // 0: 待施工, 1: 施工中, 2: 已完成
|
||||
lat?: number
|
||||
lng?: number
|
||||
id: number
|
||||
turbineNo: string
|
||||
status: 0 | 1 | 2 // 0: 待施工, 1: 施工中, 2: 已完成
|
||||
lat?: number
|
||||
lng?: number
|
||||
}
|
||||
|
||||
interface Props {
|
||||
turbines: Turbine[]
|
||||
showAddButton?: boolean
|
||||
turbines: Turbine[]
|
||||
showAddButton?: boolean
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:turbines', turbines: Turbine[]): void
|
||||
(e: 'turbine-change', turbine: Turbine): void
|
||||
(e: 'add-turbine'): void
|
||||
(e: 'update:turbines', turbines: Turbine[]): void
|
||||
(e: 'turbine-change', turbine: Turbine): void
|
||||
(e: 'add-turbine'): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showAddButton: false,
|
||||
showAddButton: false
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const getStatusText = (status: number) => {
|
||||
const statusMap = {
|
||||
0: '待施工',
|
||||
1: '施工中',
|
||||
2: '已完成',
|
||||
}
|
||||
return statusMap[status] || '未知状态'
|
||||
const statusMap = {
|
||||
0: '待施工',
|
||||
1: '施工中',
|
||||
2: '已完成'
|
||||
}
|
||||
return statusMap[status] || '未知状态'
|
||||
}
|
||||
|
||||
const getStatusClass = (status: number) => {
|
||||
return `status-${status}`
|
||||
return `status-${status}`
|
||||
}
|
||||
|
||||
const handleTurbineNoChange = (turbine: Turbine) => {
|
||||
emit('turbine-change', turbine)
|
||||
emit('update:turbines', props.turbines)
|
||||
emit('turbine-change', turbine)
|
||||
emit('update:turbines', props.turbines)
|
||||
}
|
||||
|
||||
const openMapModal = (turbine: Turbine) => {
|
||||
Message.info(`地图选点功能待开发,当前机组编号:${turbine.turbineNo}`)
|
||||
Message.info(`地图选点功能待开发,当前机组编号:${turbine.turbineNo}`)
|
||||
}
|
||||
|
||||
const editTurbine = (turbine: Turbine) => {
|
||||
// 可以打开编辑弹窗
|
||||
Message.info(`编辑机组:${turbine.turbineNo}`)
|
||||
// 可以打开编辑弹窗
|
||||
Message.info(`编辑机组:${turbine.turbineNo}`)
|
||||
}
|
||||
|
||||
const addTurbine = () => {
|
||||
emit('add-turbine')
|
||||
emit('add-turbine')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<a-row :gutter="16">
|
||||
<a-col :span="24" :md="17">
|
||||
<GiTable
|
||||
v-model:selected-keys="selectedKeys"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
row-key="id"
|
||||
:data="dataList"
|
||||
:columns="listColumns"
|
||||
|
|
|
@ -225,9 +225,9 @@ export default {
|
|||
// 发送后端请求
|
||||
const captchaVerification = secretKey.value
|
||||
? encryptByAes(
|
||||
`${backToken.value}---${JSON.stringify(checkPosArr)}`,
|
||||
secretKey.value,
|
||||
)
|
||||
`${backToken.value}---${JSON.stringify(checkPosArr)}`,
|
||||
secretKey.value,
|
||||
)
|
||||
: `${backToken.value}---${JSON.stringify(checkPosArr)}`
|
||||
const data = {
|
||||
captchaType: captchaType.value,
|
||||
|
|
|
@ -231,7 +231,7 @@ export default {
|
|||
) {
|
||||
move_block_left
|
||||
= barArea.value.offsetWidth
|
||||
- Number.parseInt(blockSize.value.width, 10) / 2 - 2
|
||||
- Number.parseInt(blockSize.value.width, 10) / 2 - 2
|
||||
}
|
||||
if (move_block_left <= 0) {
|
||||
move_block_left = Number.parseInt(blockSize.value.width, 10) / 2
|
||||
|
@ -281,9 +281,9 @@ export default {
|
|||
captchaType: captchaType.value,
|
||||
pointJson: secretKey.value
|
||||
? encryptByAes(
|
||||
JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
|
||||
secretKey.value,
|
||||
)
|
||||
JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
|
||||
secretKey.value,
|
||||
)
|
||||
: JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
|
||||
token: backToken.value,
|
||||
}
|
||||
|
@ -303,21 +303,21 @@ export default {
|
|||
}
|
||||
passFlag.value = true
|
||||
tipWords.value = `${(
|
||||
(endMovetime.value - startMoveTime.value)
|
||||
/ 1000
|
||||
(endMovetime.value - startMoveTime.value)
|
||||
/ 1000
|
||||
).toFixed(2)}s验证成功`
|
||||
const captchaVerification = secretKey.value
|
||||
? encryptByAes(
|
||||
`${backToken.value}---${JSON.stringify({
|
||||
x: moveLeftDistance,
|
||||
y: 5.0,
|
||||
})}`,
|
||||
secretKey.value,
|
||||
)
|
||||
`${backToken.value}---${JSON.stringify({
|
||||
x: moveLeftDistance,
|
||||
y: 5.0,
|
||||
})}`,
|
||||
secretKey.value,
|
||||
)
|
||||
: `${backToken.value}---${JSON.stringify({
|
||||
x: moveLeftDistance,
|
||||
y: 5.0,
|
||||
})}`
|
||||
x: moveLeftDistance,
|
||||
y: 5.0,
|
||||
})}`
|
||||
setTimeout(() => {
|
||||
tipWords.value = ''
|
||||
proxy.$parent.closeBox()
|
||||
|
|
|
@ -11,18 +11,18 @@ export function useDept(options?: { onSuccess?: () => void }) {
|
|||
try {
|
||||
loading.value = true
|
||||
const res = await getDeptTree({ deptName })
|
||||
|
||||
|
||||
// 处理部门树数据,确保有title字段用于显示
|
||||
const processDeptData = (data: any[]): TreeNodeData[] => {
|
||||
if (!data || !data.length) return []
|
||||
|
||||
return data.map((item) => ({
|
||||
|
||||
return data.map(item => ({
|
||||
key: item.deptId,
|
||||
title: item.deptName || '未命名部门', // 将deptName映射为title
|
||||
children: item.children ? processDeptData(item.children) : [],
|
||||
title: item.deptName || '未命名部门', // 将deptName映射为title
|
||||
children: item.children ? processDeptData(item.children) : []
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
deptList.value = processDeptData(res.data || [])
|
||||
options?.onSuccess && options.onSuccess()
|
||||
} finally {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { listPost } from '@/apis/system/post'
|
|||
import type { PostVO } from '@/apis/system/type'
|
||||
|
||||
export function usePost() {
|
||||
const postList = ref<{ label: string, value: string }[]>([])
|
||||
const postList = ref<{ label: string; value: string }[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
// 获取岗位列表
|
||||
|
@ -24,6 +24,6 @@ export function usePost() {
|
|||
return {
|
||||
postList,
|
||||
loading,
|
||||
getPostList,
|
||||
getPostList
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,16 +11,16 @@ export function useRole(options?: { onSuccess?: () => void }) {
|
|||
try {
|
||||
loading.value = true
|
||||
const res = await fetchRoleList()
|
||||
|
||||
|
||||
// 将新的角色数据格式转换为表单需要的 LabelValueState 格式
|
||||
if (res && res.data) {
|
||||
roleList.value = (res.data || []).map((role) => ({
|
||||
roleList.value = (res.data || []).map(role => ({
|
||||
label: role.roleName,
|
||||
value: role.roleId,
|
||||
disabled: role.status !== '1', // 假设状态为1表示启用
|
||||
disabled: role.status !== '1' // 假设状态为1表示启用
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
options?.onSuccess && options.onSuccess()
|
||||
} finally {
|
||||
loading.value = false
|
||||
|
|
|
@ -19,6 +19,7 @@ import Asider from './components/Asider/index.vue'
|
|||
import Header from './components/Header/index.vue'
|
||||
import Main from './components/Main.vue'
|
||||
import Tabs from './components/Tabs/index.vue'
|
||||
import GiFooter from '@/components/GiFooter/index.vue'
|
||||
import NoticePopup from '@/views/user/message/components/NoticePopup.vue'
|
||||
import { useAppStore } from '@/stores'
|
||||
import { useDevice } from '@/hooks'
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
<script setup lang="ts">
|
||||
import Menu from '../Menu/index.vue'
|
||||
import Logo from '../Logo.vue'
|
||||
import WwAds from '../WwAds.vue'
|
||||
import { useAppStore } from '@/stores'
|
||||
import { useDevice } from '@/hooks'
|
||||
|
||||
|
|
|
@ -76,10 +76,11 @@
|
|||
<script setup lang="ts">
|
||||
import { Modal } from '@arco-design/web-vue'
|
||||
import { useFullscreen } from '@vueuse/core'
|
||||
import { nextTick, onMounted, ref } from 'vue'
|
||||
import { onMounted, ref, nextTick } from 'vue'
|
||||
import Message from './Message.vue'
|
||||
import SettingDrawer from './SettingDrawer.vue'
|
||||
import Search from './Search.vue'
|
||||
import { getUnreadMessageCount } from '@/apis'
|
||||
import { useUserStore } from '@/stores'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { useBreakpoint, useDevice } from '@/hooks'
|
||||
|
@ -106,17 +107,17 @@ const initWebSocket = (token: string) => {
|
|||
if (initTimer) {
|
||||
clearTimeout(initTimer)
|
||||
}
|
||||
|
||||
|
||||
initTimer = setTimeout(() => {
|
||||
// 如果已有连接,先关闭
|
||||
if (socket) {
|
||||
socket.close()
|
||||
socket = null
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
socket = new WebSocket(`${import.meta.env.VITE_API_WS_URL}/websocket?token=${token}`)
|
||||
|
||||
|
||||
socket.onopen = () => {
|
||||
// console.log('WebSocket connection opened')
|
||||
}
|
||||
|
@ -139,7 +140,7 @@ const initWebSocket = (token: string) => {
|
|||
} catch (error) {
|
||||
console.error('Failed to create WebSocket connection:', error)
|
||||
}
|
||||
|
||||
|
||||
initTimer = null
|
||||
}, 100) // 100ms防抖
|
||||
}
|
||||
|
|
|
@ -18,9 +18,10 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
})
|
||||
const appStore = useAppStore()
|
||||
// const title = computed(() => appStore.getTitle())
|
||||
const title = '数智平台'
|
||||
const logo = '/logo.png'
|
||||
// computed(() => appStore.getLogo())
|
||||
const title = "数智平台"
|
||||
const logo = "/logo.png"
|
||||
//computed(() => appStore.getLogo())
|
||||
|
||||
|
||||
interface Props {
|
||||
collapsed?: boolean
|
||||
|
|
|
@ -52,7 +52,7 @@ watchEffect(() => {
|
|||
const children = props.item?.children?.length ? props.item.children : []
|
||||
// 判断是否只有一个显示的子项
|
||||
const showingChildren = children.filter((i) => i.meta?.hidden === false)
|
||||
|
||||
|
||||
if (showingChildren.length) {
|
||||
// 保存子项最后一个hidden: false的元素
|
||||
onlyOneChild.value = showingChildren[showingChildren.length - 1]
|
||||
|
|
|
@ -2,7 +2,7 @@ import { createApp } from 'vue'
|
|||
import ArcoVue, { Card, Drawer, Modal } from '@arco-design/web-vue'
|
||||
import '@/styles/arco-ui/index.less'
|
||||
// import '@arco-themes/vue-gi-demo/index.less'
|
||||
import '@arco-design/web-vue/dist/arco.css'
|
||||
// import '@arco-design/web-vue/dist/arco.css'
|
||||
|
||||
// 额外引入 Arco Design Icon图标库
|
||||
import ArcoVueIcon from '@arco-design/web-vue/es/icon'
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { computed, reactive, toRefs, watchEffect } from 'vue'
|
||||
import { computed, reactive, toRefs, watch, watchEffect } from 'vue'
|
||||
import { generate, getRgbStr } from '@arco-design/color'
|
||||
import type { BasicConfig } from '@/apis'
|
||||
import { type BasicConfig, listSiteOptionDict } from '@/apis'
|
||||
import { getSettings } from '@/config/setting'
|
||||
|
||||
const storeSetup = () => {
|
||||
|
@ -81,18 +81,18 @@ const storeSetup = () => {
|
|||
document.title = config.SITE_TITLE || ''
|
||||
document.querySelector('link[rel="shortcut icon"]')?.setAttribute('href', config.SITE_FAVICON || '/favicon.ico')
|
||||
}
|
||||
|
||||
|
||||
// 使用watchEffect优化监听,避免递归更新
|
||||
watchEffect(() => {
|
||||
const filters = [] as string[]
|
||||
|
||||
|
||||
if (settingConfig.enableMourningMode) {
|
||||
filters.push('grayscale(100%)')
|
||||
}
|
||||
if (settingConfig.enableColorWeaknessMode) {
|
||||
filters.push('invert(80%)')
|
||||
}
|
||||
|
||||
|
||||
// 如果没有任何滤镜条件,移除 `filter` 样式
|
||||
if (filters.length === 0) {
|
||||
document.documentElement.style.removeProperty('filter')
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export interface Regulation {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
typeName: string
|
||||
publisher: string
|
||||
publishTime: string
|
||||
effectiveDate: string
|
||||
confirmStatus: 'pending' | 'confirmed'
|
||||
content: string
|
||||
scope: string
|
||||
requirements: string
|
||||
notes: string
|
||||
}
|
||||
|
||||
export const useRegulationStore = defineStore('regulation', () => {
|
||||
// 已发布的制度列表
|
||||
const publishedRegulations = ref<Regulation[]>([])
|
||||
|
||||
// 添加新发布的制度
|
||||
const addPublishedRegulation = (regulation: Regulation) => {
|
||||
publishedRegulations.value.unshift(regulation)
|
||||
}
|
||||
|
||||
// 更新制度确认状态
|
||||
const updateRegulationConfirmStatus = (id: string, status: 'pending' | 'confirmed') => {
|
||||
const regulation = publishedRegulations.value.find(item => item.id === id)
|
||||
if (regulation) {
|
||||
regulation.confirmStatus = status
|
||||
}
|
||||
}
|
||||
|
||||
// 批量确认所有制度
|
||||
const confirmAllRegulations = () => {
|
||||
publishedRegulations.value.forEach(regulation => {
|
||||
regulation.confirmStatus = 'confirmed'
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
publishedRegulations,
|
||||
addPublishedRegulation,
|
||||
updateRegulationConfirmStatus,
|
||||
confirmAllRegulations
|
||||
}
|
||||
})
|
|
@ -152,31 +152,17 @@ const storeSetup = () => {
|
|||
{
|
||||
id: 1070,
|
||||
parentId: 1000,
|
||||
title: '部门管理',
|
||||
title: '个人中心',
|
||||
type: 2,
|
||||
path: '/system/dept',
|
||||
name: 'SystemDept',
|
||||
component: 'system/dept/index',
|
||||
icon: 'mind-mapping',
|
||||
path: '/user/profile',
|
||||
name: 'UserProfile',
|
||||
component: 'user/profile/index',
|
||||
icon: 'user',
|
||||
isExternal: false,
|
||||
isCache: false,
|
||||
isHidden: false,
|
||||
sort: 4,
|
||||
},
|
||||
{
|
||||
id: 1090,
|
||||
parentId: 1000,
|
||||
title: '岗位管理',
|
||||
type: 2,
|
||||
path: '/system/post',
|
||||
name: 'SystemPost',
|
||||
component: 'system/post/index',
|
||||
icon: 'settings',
|
||||
isExternal: false,
|
||||
isCache: false,
|
||||
isHidden: false,
|
||||
sort: 5,
|
||||
},
|
||||
],
|
||||
}]
|
||||
// 使用已转换的数据生成路由
|
||||
|
|
|
@ -5,10 +5,10 @@ import {
|
|||
type AccountLoginReq,
|
||||
AuthTypeConstants,
|
||||
|
||||
type DeptDetail,
|
||||
type PhoneLoginReq,
|
||||
type RoleDetail,
|
||||
type UserDetail,
|
||||
type DeptDetail,
|
||||
type RoleDetail,
|
||||
type UserInfo,
|
||||
accountLogin as accountLoginApi,
|
||||
|
||||
|
@ -21,10 +21,10 @@ import { clearToken, getToken, setToken } from '@/utils/auth'
|
|||
import { resetHasRouteFlag } from '@/router/guard'
|
||||
|
||||
interface NewUserInfoData {
|
||||
user: UserDetail
|
||||
dept: DeptDetail
|
||||
roles: RoleDetail[]
|
||||
posts: any[]
|
||||
user: UserDetail;
|
||||
dept: DeptDetail;
|
||||
roles: RoleDetail[];
|
||||
posts: any[];
|
||||
}
|
||||
|
||||
const storeSetup = () => {
|
||||
|
@ -43,7 +43,7 @@ const storeSetup = () => {
|
|||
deptName: '',
|
||||
avatar: '',
|
||||
roles: [] as string[],
|
||||
permissions: [] as string[],
|
||||
permissions: [] as string[]
|
||||
})
|
||||
const nickname = computed(() => userInfo.name)
|
||||
const username = computed(() => userInfo.account)
|
||||
|
@ -68,6 +68,8 @@ const storeSetup = () => {
|
|||
token.value = res.data.tokenValue
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 手机号登录
|
||||
const phoneLogin = async (req: PhoneLoginReq) => {
|
||||
const res = await phoneLoginApi({ ...req, clientId: import.meta.env.VITE_CLIENT_ID, authType: AuthTypeConstants.PHONE })
|
||||
|
@ -75,6 +77,8 @@ const storeSetup = () => {
|
|||
token.value = res.data.token
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 退出登录回调
|
||||
const logoutCallBack = async () => {
|
||||
roles.value = []
|
||||
|
@ -98,12 +102,12 @@ const storeSetup = () => {
|
|||
// 获取用户信息
|
||||
const getInfo = async () => {
|
||||
const res = await getUserInfoApi()
|
||||
|
||||
|
||||
// 检查返回数据格式,适配新旧API
|
||||
if (res.data && 'user' in res.data) {
|
||||
// 新API结构
|
||||
const { user, dept, roles: userRoles } = res.data as unknown as NewUserInfoData
|
||||
|
||||
|
||||
// 更新用户基本信息
|
||||
userInfo.userId = user.userId
|
||||
userInfo.account = user.account
|
||||
|
@ -114,7 +118,7 @@ const storeSetup = () => {
|
|||
userInfo.userType = user.userType
|
||||
userInfo.mobile = user.mobile
|
||||
userInfo.createTime = user.createTime
|
||||
|
||||
|
||||
// 更新部门信息
|
||||
if (dept) {
|
||||
userInfo.deptId = dept.deptId
|
||||
|
@ -124,23 +128,23 @@ const storeSetup = () => {
|
|||
// 处理角色信息
|
||||
if (userRoles && userRoles.length) {
|
||||
// 提取角色键作为权限标识
|
||||
const roleKeys = userRoles.map((role) => role.roleKey).filter(Boolean) as string[]
|
||||
const roleKeys = userRoles.map(role => role.roleKey).filter(Boolean) as string[]
|
||||
roles.value = roleKeys
|
||||
|
||||
|
||||
// 由于新API没有直接提供permissions,这里默认给管理员全部权限
|
||||
permissions.value = roleKeys.includes('admin') ? ['*:*:*'] : []
|
||||
}
|
||||
} else if (res.data) {
|
||||
// 旧API结构,保留兼容
|
||||
const oldData = res.data as unknown as UserInfo
|
||||
|
||||
|
||||
// 映射旧结构到新结构
|
||||
userInfo.userId = oldData.id
|
||||
userInfo.account = oldData.username
|
||||
userInfo.name = oldData.nickname
|
||||
userInfo.avatar = oldData.avatar
|
||||
userInfo.deptName = oldData.deptName
|
||||
|
||||
|
||||
if (oldData.roles && oldData.roles.length) {
|
||||
roles.value = oldData.roles
|
||||
permissions.value = oldData.permissions
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@use './var.scss' as *;
|
||||
@import './var.scss';
|
||||
|
||||
body {
|
||||
--margin: 14px; // 通用外边距
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* 全局样式 */
|
||||
@use './var.scss' as *;
|
||||
@import './var.scss';
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
// 基础样式
|
||||
@use './base.scss';
|
||||
@import './base.scss';
|
||||
|
||||
// 全局类名样式
|
||||
@use './global.scss';
|
||||
@import './global.scss';
|
||||
|
||||
// 自定义原生滚动条样式
|
||||
@use './scrollbar-reset.scss';
|
||||
@import './scrollbar-reset.scss';
|
||||
|
||||
// 自定义 nprogress 插件进度条颜色
|
||||
@use './nprogress.scss';
|
||||
@import './nprogress.scss';
|
||||
|
||||
// 富文本的css主题颜色变量
|
||||
@use './editor.scss';
|
||||
@import './editor.scss';
|
||||
|
||||
// 动画类名
|
||||
@use './animated.scss';
|
||||
@import './animated.scss';
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<h2>Console.log 测试</h2>
|
||||
<button @click="testConsole">测试 Console.log</button>
|
||||
<p>{{ message }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const message = ref('')
|
||||
|
||||
const testConsole = () => {
|
||||
console.log('测试 console.log 是否正常工作')
|
||||
console.warn('测试 console.warn')
|
||||
console.error('测试 console.error')
|
||||
message.value = '请查看浏览器控制台,应该能看到上述日志信息'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
margin: 10px;
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #40a9ff;
|
||||
}
|
||||
</style>
|
|
@ -1,10 +1,10 @@
|
|||
/** API响应通用类型 */
|
||||
interface ApiRes<T> {
|
||||
code: number | string
|
||||
status?: number
|
||||
success: boolean
|
||||
msg: string
|
||||
data: T
|
||||
code: number | string;
|
||||
status?: number;
|
||||
success: boolean;
|
||||
msg: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
/** 分页响应数据格式 */
|
||||
|
|
|
@ -70,6 +70,6 @@ declare global {
|
|||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
|
|
|
@ -1,179 +1,179 @@
|
|||
export interface EquipmentPageQuery {
|
||||
equipmentName?: string
|
||||
equipmentType?: string
|
||||
equipmentStatus?: string
|
||||
equipmentSn?: string
|
||||
assetCode?: string
|
||||
brand?: string
|
||||
locationStatus?: string
|
||||
healthStatus?: string
|
||||
responsiblePerson?: string
|
||||
useStatus?: string
|
||||
projectId?: string
|
||||
userId?: string
|
||||
equipmentModel?: string
|
||||
specification?: string
|
||||
physicalLocation?: string
|
||||
supplierName?: string
|
||||
maintenancePerson?: string
|
||||
inventoryBarcode?: string
|
||||
assetRemark?: string
|
||||
// 新增搜索字段
|
||||
usingDepartment?: string
|
||||
invoice?: string
|
||||
barcode?: string
|
||||
importer?: string
|
||||
page?: number
|
||||
pageSize?: number
|
||||
orderBy?: string
|
||||
orderDirection?: string
|
||||
equipmentName?: string
|
||||
equipmentType?: string
|
||||
equipmentStatus?: string
|
||||
equipmentSn?: string
|
||||
assetCode?: string
|
||||
brand?: string
|
||||
locationStatus?: string
|
||||
healthStatus?: string
|
||||
responsiblePerson?: string
|
||||
useStatus?: string
|
||||
projectId?: string
|
||||
userId?: string
|
||||
equipmentModel?: string
|
||||
specification?: string
|
||||
physicalLocation?: string
|
||||
supplierName?: string
|
||||
maintenancePerson?: string
|
||||
inventoryBarcode?: string
|
||||
assetRemark?: string
|
||||
// 新增搜索字段
|
||||
usingDepartment?: string
|
||||
invoice?: string
|
||||
barcode?: string
|
||||
importer?: string
|
||||
page?: number
|
||||
pageSize?: number
|
||||
orderBy?: string
|
||||
orderDirection?: string
|
||||
}
|
||||
|
||||
export interface EquipmentReq {
|
||||
equipmentName: string
|
||||
equipmentModel: string
|
||||
equipmentType: string
|
||||
equipmentStatus: string
|
||||
useStatus: string
|
||||
equipmentSn: string
|
||||
assetCode?: string
|
||||
brand?: string
|
||||
specification?: string
|
||||
locationStatus?: string
|
||||
physicalLocation?: string
|
||||
responsiblePerson?: string
|
||||
healthStatus?: string
|
||||
purchaseTime?: string
|
||||
inStockTime?: string
|
||||
activationTime?: string
|
||||
expectedScrapTime?: string
|
||||
actualScrapTime?: string
|
||||
statusChangeTime?: string
|
||||
purchaseOrder?: string
|
||||
supplierName?: string
|
||||
purchasePrice?: number
|
||||
currentNetValue?: number
|
||||
depreciationMethod?: string
|
||||
depreciationYears?: number
|
||||
salvageValue?: number
|
||||
warrantyExpireDate?: string
|
||||
lastMaintenanceDate?: string
|
||||
nextMaintenanceDate?: string
|
||||
maintenancePerson?: string
|
||||
inventoryBarcode?: string
|
||||
assetRemark?: string
|
||||
// 新增字段
|
||||
usingDepartment?: string
|
||||
borrowingTime?: string
|
||||
returnTime?: string
|
||||
outStockTime?: string
|
||||
totalUsageTime?: string
|
||||
depreciationRate?: number
|
||||
depreciationMethodDesc?: string
|
||||
invoice?: string
|
||||
invoiceStatus?: string
|
||||
attachments?: string
|
||||
photos?: string
|
||||
barcode?: string
|
||||
importer?: string
|
||||
inventoryTimeStatus1?: string
|
||||
inventoryTimeStatus2?: string
|
||||
inventoryTimeStatus3?: string
|
||||
inventoryCheckTimeStatus1?: string
|
||||
inventoryCheckTimeStatus2?: string
|
||||
inventoryCheckTimeStatus3?: string
|
||||
equipmentName: string
|
||||
equipmentModel: string
|
||||
equipmentType: string
|
||||
equipmentStatus: string
|
||||
useStatus: string
|
||||
equipmentSn: string
|
||||
assetCode?: string
|
||||
brand?: string
|
||||
specification?: string
|
||||
locationStatus?: string
|
||||
physicalLocation?: string
|
||||
responsiblePerson?: string
|
||||
healthStatus?: string
|
||||
purchaseTime?: string
|
||||
inStockTime?: string
|
||||
activationTime?: string
|
||||
expectedScrapTime?: string
|
||||
actualScrapTime?: string
|
||||
statusChangeTime?: string
|
||||
purchaseOrder?: string
|
||||
supplierName?: string
|
||||
purchasePrice?: number
|
||||
currentNetValue?: number
|
||||
depreciationMethod?: string
|
||||
depreciationYears?: number
|
||||
salvageValue?: number
|
||||
warrantyExpireDate?: string
|
||||
lastMaintenanceDate?: string
|
||||
nextMaintenanceDate?: string
|
||||
maintenancePerson?: string
|
||||
inventoryBarcode?: string
|
||||
assetRemark?: string
|
||||
// 新增字段
|
||||
usingDepartment?: string
|
||||
borrowingTime?: string
|
||||
returnTime?: string
|
||||
outStockTime?: string
|
||||
totalUsageTime?: string
|
||||
depreciationRate?: number
|
||||
depreciationMethodDesc?: string
|
||||
invoice?: string
|
||||
invoiceStatus?: string
|
||||
attachments?: string
|
||||
photos?: string
|
||||
barcode?: string
|
||||
importer?: string
|
||||
inventoryTimeStatus1?: string
|
||||
inventoryTimeStatus2?: string
|
||||
inventoryTimeStatus3?: string
|
||||
inventoryCheckTimeStatus1?: string
|
||||
inventoryCheckTimeStatus2?: string
|
||||
inventoryCheckTimeStatus3?: string
|
||||
}
|
||||
|
||||
export interface EquipmentResp {
|
||||
equipmentId: string
|
||||
assetCode?: string
|
||||
equipmentName: string
|
||||
equipmentType: string
|
||||
equipmentTypeLabel?: string
|
||||
equipmentModel: string
|
||||
equipmentSn: string
|
||||
brand?: string
|
||||
specification?: string
|
||||
equipmentStatus: string
|
||||
equipmentStatusLabel?: string
|
||||
useStatus: string
|
||||
locationStatus?: string
|
||||
locationStatusLabel?: string
|
||||
physicalLocation?: string
|
||||
responsiblePerson?: string
|
||||
healthStatus?: string
|
||||
healthStatusLabel?: string
|
||||
purchaseTime?: string
|
||||
inStockTime?: string
|
||||
activationTime?: string
|
||||
expectedScrapTime?: string
|
||||
actualScrapTime?: string
|
||||
statusChangeTime?: string
|
||||
purchaseOrder?: string
|
||||
supplierName?: string
|
||||
purchasePrice?: number
|
||||
currentNetValue?: number
|
||||
depreciationMethod?: string
|
||||
depreciationYears?: number
|
||||
salvageValue?: number
|
||||
warrantyExpireDate?: string
|
||||
lastMaintenanceDate?: string
|
||||
nextMaintenanceDate?: string
|
||||
maintenancePerson?: string
|
||||
inventoryBarcode?: string
|
||||
assetRemark?: string
|
||||
// 新增字段
|
||||
usingDepartment?: string
|
||||
borrowingTime?: string
|
||||
returnTime?: string
|
||||
outStockTime?: string
|
||||
totalUsageTime?: string
|
||||
depreciationRate?: number
|
||||
depreciationMethodDesc?: string
|
||||
invoice?: string
|
||||
invoiceStatus?: string
|
||||
attachments?: string
|
||||
photos?: string
|
||||
barcode?: string
|
||||
importer?: string
|
||||
inventoryTimeStatus1?: string
|
||||
inventoryTimeStatus2?: string
|
||||
inventoryTimeStatus3?: string
|
||||
inventoryCheckTimeStatus1?: string
|
||||
inventoryCheckTimeStatus2?: string
|
||||
inventoryCheckTimeStatus3?: string
|
||||
projectId?: string
|
||||
projectName?: string
|
||||
userId?: string
|
||||
name?: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
equipmentId: string
|
||||
assetCode?: string
|
||||
equipmentName: string
|
||||
equipmentType: string
|
||||
equipmentTypeLabel?: string
|
||||
equipmentModel: string
|
||||
equipmentSn: string
|
||||
brand?: string
|
||||
specification?: string
|
||||
equipmentStatus: string
|
||||
equipmentStatusLabel?: string
|
||||
useStatus: string
|
||||
locationStatus?: string
|
||||
locationStatusLabel?: string
|
||||
physicalLocation?: string
|
||||
responsiblePerson?: string
|
||||
healthStatus?: string
|
||||
healthStatusLabel?: string
|
||||
purchaseTime?: string
|
||||
inStockTime?: string
|
||||
activationTime?: string
|
||||
expectedScrapTime?: string
|
||||
actualScrapTime?: string
|
||||
statusChangeTime?: string
|
||||
purchaseOrder?: string
|
||||
supplierName?: string
|
||||
purchasePrice?: number
|
||||
currentNetValue?: number
|
||||
depreciationMethod?: string
|
||||
depreciationYears?: number
|
||||
salvageValue?: number
|
||||
warrantyExpireDate?: string
|
||||
lastMaintenanceDate?: string
|
||||
nextMaintenanceDate?: string
|
||||
maintenancePerson?: string
|
||||
inventoryBarcode?: string
|
||||
assetRemark?: string
|
||||
// 新增字段
|
||||
usingDepartment?: string
|
||||
borrowingTime?: string
|
||||
returnTime?: string
|
||||
outStockTime?: string
|
||||
totalUsageTime?: string
|
||||
depreciationRate?: number
|
||||
depreciationMethodDesc?: string
|
||||
invoice?: string
|
||||
invoiceStatus?: string
|
||||
attachments?: string
|
||||
photos?: string
|
||||
barcode?: string
|
||||
importer?: string
|
||||
inventoryTimeStatus1?: string
|
||||
inventoryTimeStatus2?: string
|
||||
inventoryTimeStatus3?: string
|
||||
inventoryCheckTimeStatus1?: string
|
||||
inventoryCheckTimeStatus2?: string
|
||||
inventoryCheckTimeStatus3?: string
|
||||
projectId?: string
|
||||
projectName?: string
|
||||
userId?: string
|
||||
name?: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
export interface EquipmentTypeOption {
|
||||
label: string
|
||||
value: string
|
||||
label: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface EquipmentStatusOption {
|
||||
label: string
|
||||
value: string
|
||||
color: string
|
||||
label: string
|
||||
value: string
|
||||
color: string
|
||||
}
|
||||
|
||||
export interface LocationStatusOption {
|
||||
label: string
|
||||
value: string
|
||||
color: string
|
||||
label: string
|
||||
value: string
|
||||
color: string
|
||||
}
|
||||
|
||||
export interface HealthStatusOption {
|
||||
label: string
|
||||
value: string
|
||||
color: string
|
||||
label: string
|
||||
value: string
|
||||
color: string
|
||||
}
|
||||
|
||||
export interface DepreciationMethodOption {
|
||||
label: string
|
||||
value: string
|
||||
}
|
||||
label: string
|
||||
value: string
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
export interface TrainingPlanPageQuery {
|
||||
planName?: string
|
||||
trainingType?: string
|
||||
trainingLevel?: string
|
||||
status?: string
|
||||
trainer?: string
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
page?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
export interface TrainingPlanReq {
|
||||
planName: string
|
||||
trainingType: string
|
||||
trainingLevel: string
|
||||
trainingContent?: string
|
||||
trainer?: string
|
||||
trainingLocation?: string
|
||||
startTime: string
|
||||
endTime: string
|
||||
status?: string
|
||||
maxParticipants?: number
|
||||
requirements?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export interface TrainingPlanResp {
|
||||
planId: string
|
||||
planName: string
|
||||
trainingType: string
|
||||
trainingLevel: string
|
||||
trainingContent?: string
|
||||
trainer?: string
|
||||
trainingLocation?: string
|
||||
startTime: string
|
||||
endTime: string
|
||||
status: string
|
||||
maxParticipants?: number
|
||||
currentParticipants?: number
|
||||
requirements?: string
|
||||
remark?: string
|
||||
createTime: string
|
||||
createBy: string
|
||||
materials?: TrainingMaterialResp[]
|
||||
records?: TrainingRecordResp[]
|
||||
}
|
||||
|
||||
export interface TrainingMaterialResp {
|
||||
materialId: string
|
||||
materialName: string
|
||||
materialType: string
|
||||
materialPath?: string
|
||||
materialSize?: number
|
||||
description?: string
|
||||
sortOrder?: number
|
||||
}
|
||||
|
||||
export interface TrainingRecordResp {
|
||||
recordId: string
|
||||
userId: string
|
||||
userName: string
|
||||
deptId?: string
|
||||
deptName?: string
|
||||
attendanceStatus: string
|
||||
signInTime?: string
|
||||
signOutTime?: string
|
||||
score?: number
|
||||
feedback?: string
|
||||
certificateId?: string
|
||||
}
|
|
@ -18,7 +18,7 @@ export function encryptByMd5(txt: string) {
|
|||
|
||||
const publicKey
|
||||
= 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM51dgYtMyF+tTQt80sfFOpSV27a7t9u'
|
||||
+ 'aUVeFrdGiVxscuizE7H8SMntYqfn9lp8a5GH5P1/GGehVjUD2gF/4kcCAwEAAQ=='
|
||||
+ 'aUVeFrdGiVxscuizE7H8SMntYqfn9lp8a5GH5P1/GGehVjUD2gF/4kcCAwEAAQ=='
|
||||
|
||||
export function encryptByRsa(txt: string) {
|
||||
const encryptor = new JSEncrypt()
|
||||
|
@ -36,7 +36,7 @@ export function encryptByAes(word: string, account: string) {
|
|||
// 对账号做md5计算,然后取8-24位作为密钥(16个字符)
|
||||
const accountMd5 = md5(account).toString()
|
||||
const keyWord = accountMd5.substring(8, 24) // 取8-24位(索引8-23,共16位)
|
||||
|
||||
|
||||
const key = CryptoJS.enc.Utf8.parse(keyWord)
|
||||
const arcs = CryptoJS.enc.Utf8.parse(word)
|
||||
const encrypted = CryptoJS.AES.encrypt(arcs, key, {
|
||||
|
|
|
@ -30,7 +30,7 @@ const StatusCodeMessage: ICodeMessage = {
|
|||
}
|
||||
|
||||
const http: AxiosInstance = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL,
|
||||
baseURL: import.meta.env.VITE_API_PREFIX ?? import.meta.env.VITE_API_BASE_URL,
|
||||
timeout: 30 * 1000,
|
||||
})
|
||||
|
||||
|
@ -70,10 +70,10 @@ http.interceptors.response.use(
|
|||
if (data && data.rows !== undefined && data.data === undefined) {
|
||||
data.data = data.rows
|
||||
}
|
||||
|
||||
|
||||
// 兼容不同的API响应结构
|
||||
const { success, code, msg } = data
|
||||
|
||||
|
||||
// 检查响应类型是否是blob
|
||||
if (response.request.responseType === 'blob') {
|
||||
const contentType = data.type
|
||||
|
@ -96,7 +96,7 @@ http.interceptors.response.use(
|
|||
return response
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 判断请求是否成功:明确的success字段为true,或者code为200都视为成功
|
||||
const isSuccess = success !== undefined ? success : (code === 200 || code === '200')
|
||||
if (isSuccess) {
|
||||
|
@ -139,22 +139,17 @@ const request = async <T = unknown>(config: AxiosRequestConfig): Promise<ApiRes<
|
|||
.then((res: AxiosResponse) => {
|
||||
// 处理返回数据结构,兼容rows和data字段
|
||||
const responseData = res.data
|
||||
|
||||
|
||||
// 如果返回的数据中有rows字段但没有data字段,将rows赋值给data
|
||||
if (responseData.rows !== undefined && responseData.data === undefined) {
|
||||
responseData.data = responseData.rows
|
||||
}
|
||||
|
||||
// 兼容后端返回的 status/code 格式
|
||||
if (responseData.status === 200 || responseData.code === 200 || responseData.code === '200') {
|
||||
responseData.success = true
|
||||
}
|
||||
|
||||
|
||||
// 如果返回的code是200但没有设置success字段,将success设置为true
|
||||
if ((responseData.code === 200 || responseData.code === '200') && responseData.success === undefined) {
|
||||
responseData.success = true
|
||||
}
|
||||
|
||||
|
||||
return responseData
|
||||
})
|
||||
.catch((err: { msg: string }) => Promise.reject(err))
|
||||
|
|
|
@ -4,31 +4,31 @@
|
|||
|
||||
// API返回的菜单项类型
|
||||
export interface ApiMenuItem {
|
||||
menuId: string
|
||||
parentId: string
|
||||
menuName: string
|
||||
menuType: string // 'catalog' | 'route'
|
||||
orderNum: number
|
||||
visible: string
|
||||
children?: ApiMenuItem[]
|
||||
[key: string]: any // 其他可能的字段
|
||||
menuId: string;
|
||||
parentId: string;
|
||||
menuName: string;
|
||||
menuType: string; // 'catalog' | 'route'
|
||||
orderNum: number;
|
||||
visible: string;
|
||||
children?: ApiMenuItem[];
|
||||
[key: string]: any; // 其他可能的字段
|
||||
}
|
||||
|
||||
// 前端需要的菜单项类型
|
||||
export interface FrontendMenuItem {
|
||||
id: number | string
|
||||
parentId: number | string
|
||||
title: string
|
||||
type: number // 1表示目录,2表示菜单
|
||||
path: string
|
||||
name: string
|
||||
component: string
|
||||
icon: string
|
||||
isExternal: boolean
|
||||
isCache: boolean
|
||||
isHidden: boolean
|
||||
sort: number
|
||||
children?: FrontendMenuItem[]
|
||||
id: number | string;
|
||||
parentId: number | string;
|
||||
title: string;
|
||||
type: number; // 1表示目录,2表示菜单
|
||||
path: string;
|
||||
name: string;
|
||||
component: string;
|
||||
icon: string;
|
||||
isExternal: boolean;
|
||||
isCache: boolean;
|
||||
isHidden: boolean;
|
||||
sort: number;
|
||||
children?: FrontendMenuItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,14 +37,14 @@ export interface FrontendMenuItem {
|
|||
const convertMenuType = (menuType: string): number => {
|
||||
switch (menuType.toLowerCase()) {
|
||||
case 'catalog':
|
||||
return 1
|
||||
return 1;
|
||||
case 'route':
|
||||
return 2
|
||||
return 2;
|
||||
case 'button':
|
||||
return 3
|
||||
return 3;
|
||||
default:
|
||||
// 默认为菜单类型
|
||||
return 2
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ const convertMenuType = (menuType: string): number => {
|
|||
* 是否隐藏: '0' -> false, '1' -> true
|
||||
*/
|
||||
const convertVisible = (visible: string): boolean => {
|
||||
return visible === '1' // '1'为隐藏,'0'为显示
|
||||
return visible === '1'; // '1'为隐藏,'0'为显示
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,27 +60,27 @@ const convertVisible = (visible: string): boolean => {
|
|||
*/
|
||||
const convertMenuItem = (apiItem: ApiMenuItem): FrontendMenuItem => {
|
||||
// 根据menuType生成默认的path和component
|
||||
let path = ''
|
||||
let component = ''
|
||||
let name = ''
|
||||
|
||||
let path = '';
|
||||
let component = '';
|
||||
let name = '';
|
||||
|
||||
// 简单的名称生成,去掉空格,保持首字母大写,非首字母小写
|
||||
const generateName = (menuName: string): string => {
|
||||
return menuName.replace(/\s+/g, '')
|
||||
.replace(/^./, (match) => match.toUpperCase())
|
||||
.replace(/[\u4E00-\u9FA5]/g, '') // 移除中文字符
|
||||
}
|
||||
.replace(/[\u4e00-\u9fa5]/g, ''); // 移除中文字符
|
||||
};
|
||||
|
||||
if (apiItem.menuType.toLowerCase() === 'catalog') {
|
||||
path = `/${apiItem.menuName.toLowerCase().replace(/\s+/g, '-')}`
|
||||
component = 'Layout'
|
||||
name = generateName(apiItem.menuName)
|
||||
path = `/${apiItem.menuName.toLowerCase().replace(/\s+/g, '-')}`;
|
||||
component = 'Layout';
|
||||
name = generateName(apiItem.menuName);
|
||||
} else {
|
||||
// 假设route类型菜单都在某个catalog下
|
||||
const parentName = apiItem.menuName.toLowerCase().replace(/\s+/g, '-')
|
||||
path = `/system/${parentName}`
|
||||
component = `system/${parentName}/index`
|
||||
name = `System${generateName(apiItem.menuName)}`
|
||||
const parentName = apiItem.menuName.toLowerCase().replace(/\s+/g, '-');
|
||||
path = `/system/${parentName}`;
|
||||
component = `system/${parentName}/index`;
|
||||
name = `System${generateName(apiItem.menuName)}`;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -88,21 +88,21 @@ const convertMenuItem = (apiItem: ApiMenuItem): FrontendMenuItem => {
|
|||
parentId: apiItem.parentId,
|
||||
title: apiItem.menuName,
|
||||
type: convertMenuType(apiItem.menuType),
|
||||
path,
|
||||
name,
|
||||
component,
|
||||
path: path,
|
||||
name: name,
|
||||
component: component,
|
||||
icon: 'settings', // 默认图标
|
||||
isExternal: false,
|
||||
isCache: false,
|
||||
isHidden: convertVisible(apiItem.visible),
|
||||
sort: apiItem.orderNum || 0,
|
||||
children: apiItem.children ? apiItem.children.map((child) => convertMenuItem(child)) : [],
|
||||
}
|
||||
children: apiItem.children ? apiItem.children.map(child => convertMenuItem(child)) : []
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换API返回的菜单数据为前端需要的格式
|
||||
*/
|
||||
export const convertMenuData = (apiMenuData: ApiMenuItem[]): FrontendMenuItem[] => {
|
||||
return apiMenuData.map((item) => convertMenuItem(item))
|
||||
}
|
||||
return apiMenuData.map(item => convertMenuItem(item));
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
import jsPDF from 'jspdf'
|
||||
import html2canvas from 'html2canvas'
|
||||
|
||||
// PDF生成器类
|
||||
export class PDFGenerator {
|
||||
// 生成制度PDF
|
||||
static async generateRegulationPDF(regulation: any): Promise<Blob> {
|
||||
// 创建临时HTML元素
|
||||
const tempDiv = document.createElement('div')
|
||||
tempDiv.style.position = 'absolute'
|
||||
tempDiv.style.left = '-9999px'
|
||||
tempDiv.style.top = '-9999px'
|
||||
tempDiv.style.width = '800px'
|
||||
tempDiv.style.padding = '40px'
|
||||
tempDiv.style.backgroundColor = 'white'
|
||||
tempDiv.style.fontFamily = 'Arial, sans-serif'
|
||||
tempDiv.style.fontSize = '14px'
|
||||
tempDiv.style.lineHeight = '1.6'
|
||||
tempDiv.style.color = '#333'
|
||||
|
||||
// 添加水印样式
|
||||
tempDiv.style.position = 'relative'
|
||||
tempDiv.style.overflow = 'hidden'
|
||||
|
||||
// 生成HTML内容
|
||||
tempDiv.innerHTML = `
|
||||
<div style="position: relative; z-index: 1;">
|
||||
<!-- 水印 -->
|
||||
<div style="
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
font-size: 48px;
|
||||
color: rgba(200, 200, 200, 0.3);
|
||||
font-weight: bold;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
">迪特聚能科技</div>
|
||||
|
||||
<!-- 标题 -->
|
||||
<h1 style="
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 30px;
|
||||
color: #333;
|
||||
">${regulation.title || '制度文档'}</h1>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>制度类型:</strong>${regulation.regulationType || '-'}</td>
|
||||
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>发布人:</strong>${regulation.createByName || regulation.createBy || '-'}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>发布时间:</strong>${regulation.publishTime || '-'}</td>
|
||||
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>生效日期:</strong>${regulation.effectiveTime || '-'}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>适用范围:</strong>${regulation.scope || '-'}</td>
|
||||
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>制度级别:</strong>${regulation.level || '-'}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>版本:</strong>${regulation.version || '1.0'}</td>
|
||||
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<hr style="border: none; border-top: 2px solid #ddd; margin: 30px 0;">
|
||||
|
||||
<!-- 制度内容 -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<h2 style="font-size: 18px; font-weight: bold; margin-bottom: 15px; color: #333;">制度内容:</h2>
|
||||
<div style="
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
line-height: 1.8;
|
||||
text-align: justify;
|
||||
">${regulation.content || ''}</div>
|
||||
</div>
|
||||
|
||||
${regulation.remark ? `
|
||||
<!-- 备注信息 -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<h2 style="font-size: 18px; font-weight: bold; margin-bottom: 15px; color: #333;">备注:</h2>
|
||||
<div style="
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
line-height: 1.8;
|
||||
text-align: justify;
|
||||
">${regulation.remark}</div>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<!-- 页脚 -->
|
||||
<div style="
|
||||
margin-top: 50px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #ddd;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
">
|
||||
生成时间:${new Date().toLocaleString('zh-CN')} | 制度ID:${regulation.regulationId || '-'}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// 添加到DOM
|
||||
document.body.appendChild(tempDiv)
|
||||
|
||||
try {
|
||||
// 使用html2canvas转换为图片
|
||||
const canvas = await html2canvas(tempDiv, {
|
||||
scale: 2, // 提高清晰度
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
backgroundColor: '#ffffff',
|
||||
width: 800,
|
||||
height: tempDiv.scrollHeight
|
||||
})
|
||||
|
||||
// 创建PDF
|
||||
const imgData = canvas.toDataURL('image/png')
|
||||
const pdf = new jsPDF('p', 'mm', 'a4')
|
||||
|
||||
const imgWidth = 210 // A4宽度
|
||||
const pageHeight = 295 // A4高度
|
||||
const imgHeight = (canvas.height * imgWidth) / canvas.width
|
||||
let heightLeft = imgHeight
|
||||
let position = 0
|
||||
|
||||
// 添加第一页
|
||||
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight)
|
||||
heightLeft -= pageHeight
|
||||
|
||||
// 添加后续页面
|
||||
while (heightLeft >= 0) {
|
||||
position = heightLeft - imgHeight
|
||||
pdf.addPage()
|
||||
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight)
|
||||
heightLeft -= pageHeight
|
||||
}
|
||||
|
||||
// 返回PDF blob
|
||||
return pdf.output('blob')
|
||||
} finally {
|
||||
// 清理临时元素
|
||||
document.body.removeChild(tempDiv)
|
||||
}
|
||||
}
|
||||
|
||||
// 下载PDF文件
|
||||
static downloadPDF(blob: Blob, filename: string) {
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = filename
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<GiPageLayout>
|
||||
<GiTable
|
||||
v-model:selected-keys="selectedKeys"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
row-key="tableName"
|
||||
:data="dataList"
|
||||
:columns="columns"
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</a-descriptions>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
|
||||
<!-- 经营状况 -->
|
||||
<a-col :span="8">
|
||||
<a-card title="经营状况" size="small">
|
||||
|
@ -28,7 +28,7 @@
|
|||
</a-descriptions>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
|
||||
<!-- 发展目标 -->
|
||||
<a-col :span="8">
|
||||
<a-card title="发展目标" size="small">
|
||||
|
@ -73,4 +73,4 @@
|
|||
.company-overview {
|
||||
padding: 16px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
File diff suppressed because it is too large
Load Diff
|
@ -37,11 +37,11 @@
|
|||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<template #icon><IconSearch /></template>
|
||||
<template #icon><icon-search /></template>
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button @click="resetSearch">
|
||||
<template #icon><IconRefresh /></template>
|
||||
<template #icon><icon-refresh /></template>
|
||||
重置
|
||||
</a-button>
|
||||
</a-space>
|
||||
|
@ -55,17 +55,17 @@
|
|||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" :disabled="selectedRowKeys.length === 0">
|
||||
<template #icon><IconDownload /></template>
|
||||
<template #icon><icon-download /></template>
|
||||
批量下载
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
status="danger"
|
||||
size="small"
|
||||
<a-button
|
||||
type="primary"
|
||||
status="danger"
|
||||
size="small"
|
||||
:disabled="selectedRowKeys.length === 0"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
<template #icon><IconDelete /></template>
|
||||
<template #icon><icon-delete /></template>
|
||||
批量删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
|
@ -75,14 +75,14 @@
|
|||
:loading="loading"
|
||||
:data="tableData"
|
||||
:pagination="pagination"
|
||||
@page-change="onPageChange"
|
||||
row-key="id"
|
||||
:row-selection="{
|
||||
type: 'checkbox',
|
||||
showCheckedAll: true,
|
||||
selectedRowKeys,
|
||||
onChange: onSelectionChange,
|
||||
selectedRowKeys: selectedRowKeys,
|
||||
onChange: onSelectionChange
|
||||
}"
|
||||
@page-change="onPageChange"
|
||||
>
|
||||
<template #columns>
|
||||
<a-table-column title="文件名" data-index="fileName">
|
||||
|
@ -116,11 +116,11 @@
|
|||
<template #cell="{ record }">
|
||||
<a-space>
|
||||
<a-button size="small" @click="previewFile(record)">
|
||||
<template #icon><IconEye /></template>
|
||||
<template #icon><icon-eye /></template>
|
||||
预览
|
||||
</a-button>
|
||||
<a-button size="small" @click="downloadFile(record)">
|
||||
<template #icon><IconDownload /></template>
|
||||
<template #icon><icon-download /></template>
|
||||
下载
|
||||
</a-button>
|
||||
<a-popconfirm
|
||||
|
@ -128,7 +128,7 @@
|
|||
@ok="deleteFile(record)"
|
||||
>
|
||||
<a-button size="small" status="danger">
|
||||
<template #icon><IconDelete /></template>
|
||||
<template #icon><icon-delete /></template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
|
@ -160,18 +160,18 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import {
|
||||
IconDelete,
|
||||
IconDownload,
|
||||
IconEye,
|
||||
IconFile,
|
||||
IconRefresh,
|
||||
IconSearch,
|
||||
} from '@arco-design/web-vue/es/icon'
|
||||
import FilePreview from '@/components/FilePreview/index.vue'
|
||||
import { deleteAttachment, getAttachBusinessTypes, getAttachmentList } from '@/apis/attach-info'
|
||||
import {
|
||||
IconSearch,
|
||||
IconRefresh,
|
||||
IconDownload,
|
||||
IconDelete,
|
||||
IconEye,
|
||||
IconFile
|
||||
} from '@arco-design/web-vue/es/icon'
|
||||
import { getAttachBusinessTypes, getAttachmentList, deleteAttachment } from '@/apis/attach-info'
|
||||
import type { AttachInfoData, BusinessType } from '@/apis/attach-info/type'
|
||||
|
||||
defineOptions({ name: 'AttachmentManagement' })
|
||||
|
@ -222,7 +222,7 @@ const fetchAttachmentList = async () => {
|
|||
Message.warning('请先选择业务类型')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getAttachmentList(filterForm.businessType)
|
||||
|
@ -293,7 +293,7 @@ const deleteFile = async (file: AttachInfoData) => {
|
|||
if (res) {
|
||||
Message.success(`已删除: ${file.fileName}`)
|
||||
// 从列表中移除
|
||||
tableData.value = tableData.value.filter((item) => item.id !== file.id)
|
||||
tableData.value = tableData.value.filter(item => item.id !== file.id)
|
||||
pagination.total = tableData.value.length
|
||||
} else {
|
||||
Message.error('删除文件失败')
|
||||
|
@ -308,7 +308,7 @@ const handleBatchDelete = () => {
|
|||
if (selectedRowKeys.value.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除选中的 ${selectedRowKeys.value.length} 个文件吗?`,
|
||||
|
@ -316,7 +316,7 @@ const handleBatchDelete = () => {
|
|||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try {
|
||||
const promises = selectedRowKeys.value.map((id) => deleteAttachment(id))
|
||||
const promises = selectedRowKeys.value.map(id => deleteAttachment(id))
|
||||
await Promise.all(promises)
|
||||
Message.success('批量删除成功')
|
||||
fetchAttachmentList()
|
||||
|
@ -325,7 +325,7 @@ const handleBatchDelete = () => {
|
|||
console.error('批量删除失败:', error)
|
||||
Message.error('批量删除失败')
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -334,13 +334,13 @@ const getFileTypeText = (type: string) => {
|
|||
image: '图片',
|
||||
document: '文档',
|
||||
video: '视频',
|
||||
other: '其他',
|
||||
other: '其他'
|
||||
}
|
||||
return typeMap[type] || '未知'
|
||||
}
|
||||
|
||||
const getBusinessTypeName = (code: string) => {
|
||||
const businessType = businessTypes.value.find((item) => item.code === code)
|
||||
const businessType = businessTypes.value.find(item => item.code === code)
|
||||
return businessType ? businessType.name : code
|
||||
}
|
||||
|
||||
|
@ -349,12 +349,12 @@ const formatFileSize = (size: number) => {
|
|||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
let index = 0
|
||||
let tempSize = size
|
||||
|
||||
|
||||
while (tempSize >= 1024 && index < units.length - 1) {
|
||||
tempSize /= 1024
|
||||
index++
|
||||
}
|
||||
|
||||
|
||||
return `${tempSize.toFixed(2)} ${units[index]}`
|
||||
}
|
||||
|
||||
|
@ -377,4 +377,4 @@ const getFileIcon = (fileType: string) => {
|
|||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="attachment-upload">
|
||||
<a-space direction="vertical" :size="16" style="width: 100%">
|
||||
<a-form ref="formRef" :model="formData">
|
||||
<a-form :model="formData" ref="formRef">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="businessType" label="业务类型" required>
|
||||
|
@ -64,7 +64,7 @@
|
|||
</a-form-item>
|
||||
|
||||
<div class="form-actions">
|
||||
<a-button type="primary" :loading="submitting" @click="handleSubmit">提交</a-button>
|
||||
<a-button type="primary" @click="handleSubmit" :loading="submitting">提交</a-button>
|
||||
<a-button style="margin-left: 10px" @click="resetForm">重置</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
|
@ -73,10 +73,10 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { IconPlus } from '@arco-design/web-vue/es/icon'
|
||||
import { batchAddAttachment, getAttachBusinessTypes } from '@/apis/attach-info'
|
||||
import { getAttachBusinessTypes, batchAddAttachment } from '@/apis/attach-info'
|
||||
import type { BusinessType } from '@/apis/attach-info/type'
|
||||
|
||||
defineOptions({ name: 'AttachmentUpload' })
|
||||
|
@ -98,15 +98,23 @@ const formData = reactive({
|
|||
fileType: '',
|
||||
remark: '',
|
||||
userDefinedPath: '',
|
||||
files: [] as FileItem[],
|
||||
files: [] as FileItem[]
|
||||
})
|
||||
|
||||
// 获取业务类型列表
|
||||
const fetchBusinessTypes = async () => {
|
||||
try {
|
||||
const res = await getAttachBusinessTypes()
|
||||
console.log("res:",res);
|
||||
if (res.data) {
|
||||
businessTypes.value = res.data
|
||||
res.data.forEach(item => {
|
||||
const key = Object.keys(item)[0];
|
||||
const value = item[key];
|
||||
businessTypes.value.push({
|
||||
name: value,
|
||||
code:key
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取业务类型失败:', error)
|
||||
|
@ -120,23 +128,23 @@ onMounted(() => {
|
|||
|
||||
const customRequest = (options: any) => {
|
||||
const { file, onProgress, onSuccess, onError } = options
|
||||
|
||||
|
||||
// 保存文件以供后续上传
|
||||
const fileItem = {
|
||||
file,
|
||||
status: 'ready',
|
||||
uid: options.fileItem.uid,
|
||||
name: options.fileItem.name,
|
||||
name: options.fileItem.name
|
||||
}
|
||||
formData.files.push(fileItem)
|
||||
|
||||
|
||||
// 模拟上传进度
|
||||
onProgress(0)
|
||||
let percent = 0
|
||||
const timer = setInterval(() => {
|
||||
percent += 10
|
||||
onProgress(percent > 100 ? 100 : percent)
|
||||
|
||||
|
||||
if (percent >= 100) {
|
||||
clearInterval(timer)
|
||||
onSuccess()
|
||||
|
@ -151,32 +159,32 @@ const handleChange = (fileList) => {
|
|||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
|
||||
|
||||
if (!formData.businessType) {
|
||||
Message.error('请选择业务类型')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (formData.files.length === 0) {
|
||||
Message.error('请选择要上传的文件')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
submitting.value = true
|
||||
|
||||
|
||||
const formDataToSend = new FormData()
|
||||
formData.files.forEach((item, index) => {
|
||||
formDataToSend.append(`files[${index}]`, item.file)
|
||||
})
|
||||
|
||||
|
||||
const params = {
|
||||
fileType: formData.fileType,
|
||||
remark: formData.remark,
|
||||
userDefinedPath: formData.userDefinedPath,
|
||||
userDefinedPath: formData.userDefinedPath
|
||||
}
|
||||
|
||||
|
||||
const res = await batchAddAttachment(formData.businessType, formDataToSend, params)
|
||||
|
||||
|
||||
if (res) {
|
||||
Message.success('文件上传成功')
|
||||
resetForm()
|
||||
|
@ -185,7 +193,7 @@ const handleSubmit = async () => {
|
|||
}
|
||||
} catch (error: any) {
|
||||
console.error('上传失败:', error)
|
||||
Message.error(`上传失败: ${error.msg || '未知错误'}`)
|
||||
Message.error('上传失败: ' + (error.msg || '未知错误'))
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
|
@ -209,16 +217,16 @@ const resetForm = () => {
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
|
||||
&-box {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
&-text {
|
||||
color: rgb(var(--primary-6));
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
&-tip {
|
||||
margin-top: 10px;
|
||||
color: rgb(var(--gray-6));
|
||||
|
@ -231,4 +239,4 @@ const resetForm = () => {
|
|||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -24,4 +24,4 @@ defineOptions({ name: 'DataStorage' })
|
|||
.data-storage-container {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -0,0 +1,603 @@
|
|||
<template>
|
||||
<GiPageLayout>
|
||||
<div class="data-preprocessing-container">
|
||||
<!-- 上传进度框 -->
|
||||
<div v-if="uploadState.visible" class="upload-progress-fixed">
|
||||
<a-card :title="`上传进度 (${uploadState.percent}%)`" :bordered="false">
|
||||
<a-progress
|
||||
:percent="uploadState.percent"
|
||||
:status="uploadState.status"
|
||||
:stroke-width="16"
|
||||
/>
|
||||
<div class="progress-details">
|
||||
<p><icon-file /> {{ uploadState.currentFile || '准备中...' }}</p>
|
||||
<p><icon-check-circle /> 已完成 {{ uploadState.uploadedCount }}/{{ uploadState.totalCount }}</p>
|
||||
<p><icon-clock-circle /> 状态: {{ getStatusText(uploadState.status) }}</p>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<div class="step-panel">
|
||||
<div class="step-header">
|
||||
<h3>批量上传图片</h3>
|
||||
</div>
|
||||
|
||||
<div class="data-selection">
|
||||
<a-form :model="form" layout="vertical">
|
||||
<!-- 项目选择 -->
|
||||
<a-form-item label="所属项目" required>
|
||||
<a-select
|
||||
v-model="form.projectId"
|
||||
placeholder="请选择项目"
|
||||
allow-search
|
||||
:filter-option="filterProjectOption"
|
||||
>
|
||||
<a-option
|
||||
v-for="project in projectList"
|
||||
:key="project.id"
|
||||
:value="project.id"
|
||||
:label="project.name"
|
||||
/>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 图片来源选择 -->
|
||||
<a-form-item label="图片来源" required>
|
||||
<a-select
|
||||
v-model="form.imageSource"
|
||||
placeholder="请选择图片来源"
|
||||
allow-search
|
||||
:filter-option="filterSourceOption"
|
||||
>
|
||||
<a-option
|
||||
v-for="source in imageSources"
|
||||
:key="source.value"
|
||||
:value="source.value"
|
||||
:label="source.label"
|
||||
/>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 文件夹操作 -->
|
||||
<a-form-item label="文件操作">
|
||||
<div class="folder-actions">
|
||||
<a-upload
|
||||
ref="uploadRef"
|
||||
directory
|
||||
:multiple="true"
|
||||
:show-file-list="false"
|
||||
accept="image/*"
|
||||
:key="uploadKey"
|
||||
@change="handleFolderSelect"
|
||||
>
|
||||
<template #upload-button>
|
||||
<a-button type="outline">
|
||||
<template #icon>
|
||||
<icon-folder />
|
||||
</template>
|
||||
选择文件夹
|
||||
</a-button>
|
||||
</template>
|
||||
</a-upload>
|
||||
<a-button
|
||||
type="outline"
|
||||
status="warning"
|
||||
@click="clearFileList"
|
||||
:disabled="selectedFiles.length === 0"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-delete />
|
||||
</template>
|
||||
清空列表
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
:loading="uploading"
|
||||
:disabled="!canUpload"
|
||||
@click="handleUpload"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-upload />
|
||||
</template>
|
||||
开始上传
|
||||
</a-button>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 文件列表 -->
|
||||
<a-form-item v-if="selectedFiles.length > 0">
|
||||
<div class="file-list-container">
|
||||
<div class="file-list-header">
|
||||
<span>已选择 {{ selectedFiles.length }} 个文件(选中 {{ checkedFiles.length }} 个)</span>
|
||||
<a-checkbox
|
||||
v-model="selectAll"
|
||||
:indeterminate="indeterminate"
|
||||
@change="handleSelectAllChange"
|
||||
>
|
||||
全选
|
||||
</a-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="file-table-wrapper">
|
||||
<a-table
|
||||
:data="selectedFiles"
|
||||
:columns="fileColumns"
|
||||
:row-selection="rowSelection"
|
||||
:pagination="false"
|
||||
row-key="uid"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<!-- 表格列模板 -->
|
||||
<template #thumbnail="{ record }">
|
||||
<div class="thumbnail-cell">
|
||||
<img
|
||||
v-if="isImage(record.type)"
|
||||
:src="record.preview"
|
||||
class="thumbnail-image"
|
||||
alt="预览"
|
||||
/>
|
||||
<div v-else class="file-icon">
|
||||
<icon-file />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #fileType="{ record }">
|
||||
<a-tag :color="getFileTypeColor(record.type)" size="small">
|
||||
{{ record.type }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template #fileSize="{ record }">
|
||||
{{ formatFileSize(record.size) }}
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GiPageLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, nextTick } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import type { UploadItem } from '@arco-design/web-vue/es/upload'
|
||||
import type { SelectOptionData, TableColumnData, TableRowSelection } from '@arco-design/web-vue'
|
||||
import axios from 'axios'
|
||||
|
||||
import {
|
||||
getProjectList,
|
||||
getImageSources
|
||||
} from '@/apis/industrial-image'
|
||||
|
||||
// 类型定义
|
||||
interface FileItem {
|
||||
uid: string
|
||||
name: string
|
||||
type: string
|
||||
size: number
|
||||
file: File
|
||||
preview?: string
|
||||
}
|
||||
|
||||
type UploadStatus = 'waiting' | 'uploading' | 'success' | 'error'
|
||||
|
||||
// 数据状态
|
||||
const projectList = ref([])
|
||||
|
||||
const imageSources = ref([])
|
||||
|
||||
// 获取项目列表
|
||||
const fetchProjectList = async () => {
|
||||
try {
|
||||
const res = await getProjectList({ page: 1, pageSize: 1000 });
|
||||
projectList.value = res.data.map(item => ({
|
||||
name: item.projectName,
|
||||
id: item.projectId
|
||||
}))
|
||||
} catch (error) {
|
||||
Message.error('获取项目列表失败')
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取图片来源
|
||||
const fetchImageSourceList = async () => {
|
||||
try {
|
||||
const res = await getImageSources();
|
||||
res.data.forEach(item => {
|
||||
const key = Object.keys(item)[0];
|
||||
const value = item[key];
|
||||
imageSources.value.push({
|
||||
label: value,
|
||||
value:key
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
Message.error('获取项目列表失败')
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
const form = reactive({
|
||||
projectId: undefined as number | undefined,
|
||||
imageSource: undefined as string | undefined
|
||||
})
|
||||
|
||||
const selectedFiles = ref<FileItem[]>([])
|
||||
const checkedFiles = ref<string[]>([])
|
||||
const uploading = ref(false)
|
||||
const uploadRef = ref()
|
||||
const uploadKey = ref(0)
|
||||
|
||||
const uploadState = reactive({
|
||||
visible: false,
|
||||
percent: 0,
|
||||
status: 'waiting' as UploadStatus,
|
||||
currentFile: '',
|
||||
uploadedCount: 0,
|
||||
totalCount: 0
|
||||
})
|
||||
|
||||
// 计算属性
|
||||
const canUpload = computed(() => {
|
||||
return checkedFiles.value.length > 0
|
||||
&& !!form.projectId
|
||||
&& !!form.imageSource
|
||||
&& !uploading.value
|
||||
})
|
||||
|
||||
const selectAll = ref(false)
|
||||
const indeterminate = computed(() => {
|
||||
return checkedFiles.value.length > 0 &&
|
||||
checkedFiles.value.length < selectedFiles.value.length
|
||||
})
|
||||
|
||||
// 表格列配置
|
||||
const fileColumns: TableColumnData[] = [
|
||||
{
|
||||
title: '选择',
|
||||
dataIndex: 'selection',
|
||||
type: 'selection',
|
||||
width: 60,
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '预览',
|
||||
dataIndex: 'thumbnail',
|
||||
slotName: 'thumbnail',
|
||||
width: 100,
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '文件名',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
slotName: 'fileType',
|
||||
width: 100,
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '大小',
|
||||
dataIndex: 'size',
|
||||
slotName: 'fileSize',
|
||||
width: 100,
|
||||
align: 'center'
|
||||
}
|
||||
]
|
||||
|
||||
// 行选择配置
|
||||
const rowSelection = reactive<TableRowSelection>({
|
||||
type: 'checkbox',
|
||||
showCheckedAll: false,
|
||||
selectedRowKeys: checkedFiles,
|
||||
onChange: (rowKeys: string[]) => {
|
||||
checkedFiles.value = rowKeys
|
||||
selectAll.value = rowKeys.length === selectedFiles.value.length
|
||||
}
|
||||
})
|
||||
|
||||
// 搜索过滤函数
|
||||
const filterProjectOption = (inputValue: string, option: SelectOptionData) => {
|
||||
return option.label.toLowerCase().includes(inputValue.toLowerCase())
|
||||
}
|
||||
|
||||
const filterSourceOption = (inputValue: string, option: SelectOptionData) => {
|
||||
return option.label.toLowerCase().includes(inputValue.toLowerCase())
|
||||
}
|
||||
|
||||
// 文件选择处理
|
||||
const handleFolderSelect = async (fileList: UploadItem[]) => {
|
||||
// 1. 清空现有状态
|
||||
clearFileList()
|
||||
|
||||
// 2. 处理新文件
|
||||
const newFiles: FileItem[] = []
|
||||
for (const item of fileList) {
|
||||
const file = item.file
|
||||
if (!file) continue
|
||||
|
||||
const fileType = file.type.split('/')[0] || 'unknown'
|
||||
const preview = fileType === 'image' ? URL.createObjectURL(file) : undefined
|
||||
|
||||
newFiles.push({
|
||||
uid: item.uid,
|
||||
name: file.name,
|
||||
type: fileType,
|
||||
size: file.size,
|
||||
file: file,
|
||||
preview: preview
|
||||
})
|
||||
}
|
||||
|
||||
// 3. 更新状态
|
||||
selectedFiles.value = newFiles
|
||||
checkedFiles.value = newFiles.map(f => f.uid)
|
||||
selectAll.value = true
|
||||
|
||||
// 4. 重置上传组件(通过改变key强制重新创建组件)
|
||||
uploadKey.value++
|
||||
}
|
||||
|
||||
// 清空文件列表
|
||||
const clearFileList = () => {
|
||||
// 释放预览URL
|
||||
selectedFiles.value.forEach(file => {
|
||||
if (file.preview) URL.revokeObjectURL(file.preview)
|
||||
})
|
||||
|
||||
// 重置状态
|
||||
selectedFiles.value = []
|
||||
checkedFiles.value = []
|
||||
selectAll.value = false
|
||||
}
|
||||
|
||||
// 处理全选变化
|
||||
const handleSelectAllChange = (checked: boolean) => {
|
||||
checkedFiles.value = checked ? selectedFiles.value.map(f => f.uid) : []
|
||||
}
|
||||
|
||||
// 上传处理
|
||||
const handleUpload = async () => {
|
||||
if (!canUpload.value) {
|
||||
Message.error('请完成所有必填项并选择文件')
|
||||
return
|
||||
}
|
||||
|
||||
// 重置上传状态
|
||||
Object.assign(uploadState, {
|
||||
visible: true,
|
||||
percent: 0,
|
||||
status: 'uploading',
|
||||
currentFile: '',
|
||||
uploadedCount: 0,
|
||||
totalCount: checkedFiles.value.length
|
||||
})
|
||||
|
||||
uploading.value = true
|
||||
|
||||
try {
|
||||
const filesToUpload = selectedFiles.value.filter(f => checkedFiles.value.includes(f.uid))
|
||||
|
||||
const formData = new FormData()
|
||||
|
||||
// 添加所有图片文件到FormData
|
||||
filesToUpload.forEach(file => {
|
||||
formData.append('files', file);
|
||||
});
|
||||
|
||||
let url =`http://pms.dtyx.net:9158/image/${form.projectId}/${form.imageSource}/upload-batch`;
|
||||
let res = await axios.post(
|
||||
url,
|
||||
formData,
|
||||
{
|
||||
onUploadProgress: (progressEvent) => {
|
||||
if (progressEvent.total) {
|
||||
uploadedBytes = progressEvent.loaded
|
||||
const elapsedTime = (Date.now() - startTime) / 1000
|
||||
const speed = uploadedBytes / elapsedTime
|
||||
uploadState.speed = `${formatFileSize(speed)}/s`
|
||||
|
||||
const remainingBytes = totalBytes - uploadedBytes
|
||||
uploadState.remainingTime = formatTime(remainingBytes / speed)
|
||||
|
||||
uploadState.percent = Math.round((uploadedBytes / totalBytes) * 100)
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
}
|
||||
)
|
||||
console.log("res:",res);
|
||||
uploadState.status = 'success'
|
||||
Message.success(`成功上传 ${filesToUpload.length} 个文件`)
|
||||
} catch (error) {
|
||||
uploadState.status = 'error'
|
||||
Message.error('上传失败: ' + (error as Error).message)
|
||||
} finally {
|
||||
uploading.value = false
|
||||
// 5秒后自动隐藏进度框
|
||||
setTimeout(() => uploadState.visible = false, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数
|
||||
const getStatusText = (status: UploadStatus) => {
|
||||
const statusMap = {
|
||||
waiting: '等待上传',
|
||||
uploading: '上传中',
|
||||
success: '上传成功',
|
||||
error: '上传失败'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
const getFileTypeColor = (type: string) => {
|
||||
const colors: Record<string, string> = {
|
||||
image: 'arcoblue',
|
||||
video: 'green',
|
||||
audio: 'orange',
|
||||
document: 'purple'
|
||||
}
|
||||
return colors[type.toLowerCase()] || 'gray'
|
||||
}
|
||||
|
||||
const formatFileSize = (bytes: number) => {
|
||||
if (bytes === 0) return '0 B'
|
||||
const k = 1024
|
||||
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
const isImage = (type: string) => type === 'image'
|
||||
|
||||
// 在组件挂载时获取项目列表
|
||||
onMounted(() => {
|
||||
fetchProjectList()
|
||||
fetchImageSourceList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.data-preprocessing-container {
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
min-height: calc(100vh - 40px);
|
||||
}
|
||||
|
||||
.step-panel {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.step-header {
|
||||
margin-bottom: 24px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
padding-bottom: 16px;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
|
||||
.folder-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.file-list-container {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.file-list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.file-table-wrapper {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.file-table {
|
||||
width: 100%;
|
||||
|
||||
:deep(.arco-table-th) {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background-color: var(--color-fill-2);
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail-cell {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
|
||||
.thumbnail-image {
|
||||
max-height: 60px;
|
||||
max-width: 80px;
|
||||
object-fit: contain;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
font-size: 24px;
|
||||
color: var(--color-text-3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 上传进度框样式 */
|
||||
.upload-progress-fixed {
|
||||
position: fixed;
|
||||
bottom: 24px;
|
||||
right: 24px;
|
||||
width: 360px;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||||
border-radius: 8px;
|
||||
animation: fadeIn 0.3s ease;
|
||||
|
||||
:deep(.arco-card-header) {
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
:deep(.arco-progress-text) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-details {
|
||||
margin-top: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--color-text-2);
|
||||
|
||||
p {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 6px 0;
|
||||
|
||||
.arco-icon {
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
</style>
|
|
@ -3,7 +3,7 @@
|
|||
<div class="panel-header">
|
||||
<h3>自动识别设置</h3>
|
||||
<a-button type="text" @click="$emit('close')">
|
||||
<template #icon><IconClose /></template>
|
||||
<template #icon><icon-close /></template>
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
|
@ -30,10 +30,10 @@
|
|||
<div class="section-header">
|
||||
<h4>置信度</h4>
|
||||
</div>
|
||||
<a-slider
|
||||
v-model:model-value="confidence"
|
||||
:min="0"
|
||||
:max="100"
|
||||
<a-slider
|
||||
v-model:model-value="confidence"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="10"
|
||||
show-tooltip
|
||||
:format-tooltip="(value) => `${value}%`"
|
||||
|
@ -50,8 +50,8 @@
|
|||
<span>加载缺陷类型...</span>
|
||||
</div>
|
||||
<a-checkbox-group v-else v-model:model-value="selectedDefectTypes">
|
||||
<div
|
||||
v-for="defectType in defectTypes"
|
||||
<div
|
||||
v-for="defectType in defectTypes"
|
||||
:key="defectType.value"
|
||||
class="defect-item"
|
||||
>
|
||||
|
@ -64,10 +64,10 @@
|
|||
</div>
|
||||
|
||||
<div class="panel-actions">
|
||||
<a-button type="primary" :loading="isRecognizing" block @click="handleStartRecognition">
|
||||
<a-button type="primary" @click="handleStartRecognition" :loading="isRecognizing" block>
|
||||
开始识别
|
||||
</a-button>
|
||||
<a-button block @click="handleResetSettings">
|
||||
<a-button @click="handleResetSettings" block>
|
||||
重置设置
|
||||
</a-button>
|
||||
</div>
|
||||
|
@ -76,14 +76,14 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { IconClose } from '@arco-design/web-vue/es/icon'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { listDefectType } from '@/apis/common/common'
|
||||
import { getModelConfigList } from '@/apis/model-config'
|
||||
import type { DefectTypeOption, DefectTypeResp } from '@/apis/common/type'
|
||||
import type { DefectTypeResp, DefectTypeOption } from '@/apis/common/type'
|
||||
import type { ModelConfigResponse } from '@/apis/model-config/type'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const props = defineProps<{
|
||||
currentImage?: {
|
||||
|
@ -122,16 +122,16 @@ const loadModelList = async () => {
|
|||
try {
|
||||
loadingModels.value = true
|
||||
const response = await getModelConfigList()
|
||||
|
||||
|
||||
if (response && response.rows) {
|
||||
// 处理返回的数据结构
|
||||
if (Array.isArray(response.rows)) {
|
||||
modelList.value = response.rows
|
||||
modelList.value = response.rows;
|
||||
} else {
|
||||
// 检查是否有嵌套数据结构
|
||||
const responseData = response.rows
|
||||
modelList.value = []
|
||||
|
||||
const responseData = response.rows;
|
||||
modelList.value = [];
|
||||
|
||||
// 根据API返回的实际数据结构处理
|
||||
if (responseData && Array.isArray(responseData)) {
|
||||
modelList.value = responseData.map((item: any) => ({
|
||||
|
@ -140,11 +140,11 @@ const loadModelList = async () => {
|
|||
attachId: item.attachId || '',
|
||||
confThreshold: item.confThreshold || 0.5,
|
||||
nmsThreshold: item.nmsThreshold || 0.5,
|
||||
modelPath: item.modelPath || '',
|
||||
}))
|
||||
modelPath: item.modelPath || ''
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果有模型,默认选择第一个
|
||||
if (modelList.value.length > 0) {
|
||||
selectedAlgorithm.value = modelList.value[0].modelId
|
||||
|
@ -164,10 +164,10 @@ const loadDefectTypes = async () => {
|
|||
try {
|
||||
loadingDefectTypes.value = true
|
||||
const response = await listDefectType()
|
||||
|
||||
|
||||
// 转换数据格式 - 处理实际API返回的数据结构
|
||||
const defectTypeOptions: DefectTypeOption[] = []
|
||||
|
||||
|
||||
// 遍历返回的数组,每个元素是一个对象,包含一个键值对
|
||||
response.data.forEach((item: DefectTypeResp) => {
|
||||
// 获取对象的所有键值对
|
||||
|
@ -175,16 +175,16 @@ const loadDefectTypes = async () => {
|
|||
defectTypeOptions.push({
|
||||
value: code,
|
||||
label: name,
|
||||
code,
|
||||
code: code
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
defectTypes.value = defectTypeOptions
|
||||
|
||||
|
||||
// 设置默认选中的缺陷类型(选择前几个)
|
||||
if (defectTypes.value.length > 0) {
|
||||
selectedDefectTypes.value = defectTypes.value.slice(0, 3).map((item) => item.value)
|
||||
selectedDefectTypes.value = defectTypes.value.slice(0, 3).map(item => item.value)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取缺陷类型失败:', error)
|
||||
|
@ -216,7 +216,7 @@ const handleStartRecognition = async () => {
|
|||
const settings = {
|
||||
algorithm: selectedAlgorithm.value,
|
||||
confidence: confidence.value,
|
||||
defectTypes: selectedDefectTypes.value,
|
||||
defectTypes: selectedDefectTypes.value
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -235,7 +235,7 @@ const handleStartRecognition = async () => {
|
|||
const handleResetSettings = () => {
|
||||
selectedAlgorithm.value = modelList.value.length > 0 ? modelList.value[0].modelId : ''
|
||||
confidence.value = 80
|
||||
selectedDefectTypes.value = defectTypes.value.length > 0 ? defectTypes.value.slice(0, 3).map((item) => item.value) : []
|
||||
selectedDefectTypes.value = defectTypes.value.length > 0 ? defectTypes.value.slice(0, 3).map(item => item.value) : []
|
||||
}
|
||||
|
||||
// 设置识别状态
|
||||
|
@ -258,7 +258,7 @@ const goToModelManagement = () => {
|
|||
}
|
||||
|
||||
defineExpose({
|
||||
setRecognizing,
|
||||
setRecognizing
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -275,7 +275,7 @@ defineExpose({
|
|||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
|
@ -290,11 +290,11 @@ defineExpose({
|
|||
flex-direction: column;
|
||||
overflow-y: scroll;
|
||||
height: 600px;
|
||||
|
||||
|
||||
.panel-section {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -351,4 +351,4 @@ defineExpose({
|
|||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -3,10 +3,10 @@
|
|||
<div class="form-header">
|
||||
<h3>缺陷详情</h3>
|
||||
<a-button type="text" @click="$emit('close')">
|
||||
<template #icon><IconClose /></template>
|
||||
<template #icon><icon-close /></template>
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-body">
|
||||
<a-form :model="form" layout="vertical">
|
||||
<a-form-item label="缺陷名称" field="defectName">
|
||||
|
@ -15,12 +15,12 @@
|
|||
|
||||
<!-- 缺陷位置 -->
|
||||
<a-form-item
|
||||
field="defectPosition"
|
||||
label="缺陷位置"
|
||||
field="defectPosition"
|
||||
label="缺陷位置"
|
||||
>
|
||||
<a-input v-model="form.defectPosition" placeholder="请输入缺陷位置" />
|
||||
</a-form-item>
|
||||
|
||||
|
||||
<!-- 缺陷类型 -->
|
||||
<a-form-item
|
||||
field="defectType"
|
||||
|
@ -36,42 +36,42 @@
|
|||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 轴向尺寸 -->
|
||||
<!--轴向尺寸-->
|
||||
<div class="dimension-fields">
|
||||
<a-form-item
|
||||
field="axialDimension"
|
||||
label="轴向尺寸 (mm)"
|
||||
:rules="[{ type: 'number', min: 0, message: '必须大于等于0' }]"
|
||||
field="axialDimension"
|
||||
label="轴向尺寸 (mm)"
|
||||
:rules="[{ type: 'number', min: 0, message: '必须大于等于0' }]"
|
||||
>
|
||||
<a-input-number
|
||||
v-model="form.axialDimension"
|
||||
:min="0"
|
||||
:step="1"
|
||||
placeholder="请输入轴向尺寸"
|
||||
mode="button"
|
||||
size="large"
|
||||
v-model="form.axialDimension"
|
||||
:min="0"
|
||||
:step="1"
|
||||
placeholder="请输入轴向尺寸"
|
||||
mode="button"
|
||||
size="large"
|
||||
>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 弦向尺寸 -->
|
||||
<!--弦向尺寸 -->
|
||||
<a-form-item
|
||||
field="chordDimension"
|
||||
label="弦向尺寸 (mm)"
|
||||
:rules="[{ type: 'number', min: 0, message: '必须大于等于0' }]"
|
||||
field="chordDimension"
|
||||
label="弦向尺寸 (mm)"
|
||||
:rules="[{ type: 'number', min: 0, message: '必须大于等于0' }]"
|
||||
>
|
||||
<a-input-number
|
||||
v-model="form.chordDimension"
|
||||
:min="0"
|
||||
:step="1"
|
||||
placeholder="请输入弦向尺寸"
|
||||
mode="button"
|
||||
size="large"
|
||||
v-model="form.chordDimension"
|
||||
:min="0"
|
||||
:step="1"
|
||||
placeholder="请输入弦向尺寸"
|
||||
mode="button"
|
||||
size="large"
|
||||
>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 缺陷等级 -->
|
||||
<a-form-item
|
||||
field="defectLevel"
|
||||
|
@ -87,46 +87,49 @@
|
|||
<a-option v-for="level in defectLevels" :key="level.code" :value="level.code">{{ level.name }}</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
|
||||
<a-form-item label="描述" field="description">
|
||||
<a-textarea
|
||||
v-model="form.description"
|
||||
<a-textarea
|
||||
v-model="form.description"
|
||||
placeholder="请输入缺陷描述"
|
||||
:rows="3"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
|
||||
<a-form-item label="维修建议" field="repairIdea">
|
||||
<a-textarea
|
||||
v-model="form.repairIdea"
|
||||
<a-textarea
|
||||
v-model="form.repairIdea"
|
||||
placeholder="请输入维修建议"
|
||||
:rows="3"
|
||||
/>
|
||||
</a-form-item>
|
||||
<div class="form-footer">
|
||||
<a-button size="large" @click="$emit('close')">取消</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="large"
|
||||
:loading="submitting"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
<template #icon><IconSave /></template>
|
||||
保存缺陷
|
||||
</a-button>
|
||||
</div>
|
||||
<a-button @click="$emit('close')" size="large">取消</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="handleSubmit"
|
||||
:loading="submitting"
|
||||
>
|
||||
<template #icon><icon-save /></template>
|
||||
保存缺陷
|
||||
</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref, watch } from 'vue'
|
||||
import { ref, reactive, watch, onMounted } from 'vue'
|
||||
import { IconClose, IconSave } from '@arco-design/web-vue/es/icon'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { getDefectLevels } from '@/apis/industrial-image/defect'
|
||||
import type { Annotation } from '@/views/project-operation-platform/data-processing/industrial-image/components/ImageCanvas.vue'
|
||||
import { getDefectLevels, getDefectTypes, type DefectLevelType, type DefectType } from '@/apis/industrial-image/defect'
|
||||
import { listDefectType } from '@/apis/common/common'
|
||||
import type { DefectTypeOption } from '@/apis/common/type'
|
||||
import type { DefectTypeResp, DefectTypeOption } from '@/apis/common/type'
|
||||
|
||||
interface DefectFormData {
|
||||
defectName: string
|
||||
|
@ -164,15 +167,15 @@ const loadingDefectLevels = ref(false)
|
|||
// 表单数据
|
||||
const form = reactive<DefectFormData>({
|
||||
defectName: '',
|
||||
defectType: '',
|
||||
defectType: '',
|
||||
defectTypeLabel: '',
|
||||
defectLevel: '',
|
||||
defectLevel: '',
|
||||
defectLevelLabel: '',
|
||||
defectPosition: '',
|
||||
description: '',
|
||||
repairIdea: '建议进行进一步检查',
|
||||
axialDimension: 0, // 初始为null
|
||||
chordDimension: 0, // 初始为null
|
||||
axialDimension: 0, // 初始为null
|
||||
chordDimension: 0 // 初始为null
|
||||
})
|
||||
|
||||
// 获取缺陷类型列表
|
||||
|
@ -180,10 +183,10 @@ const loadDefectTypes = async () => {
|
|||
try {
|
||||
loadingDefectTypes.value = true
|
||||
const response = await listDefectType()
|
||||
|
||||
|
||||
// 转换数据格式 - 处理实际API返回的数据结构
|
||||
const defectTypeOptions: DefectTypeOption[] = []
|
||||
|
||||
|
||||
// 遍历返回的数组,每个元素是一个对象,包含一个键值对
|
||||
if (Array.isArray(response.data)) {
|
||||
response.data.forEach((item: any) => {
|
||||
|
@ -194,15 +197,15 @@ const loadDefectTypes = async () => {
|
|||
defectTypeOptions.push({
|
||||
value: code,
|
||||
label: name as string,
|
||||
code,
|
||||
code: code
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
defectTypes.value = defectTypeOptions
|
||||
console.log('缺陷类型选项:', defectTypeOptions)
|
||||
|
||||
|
||||
// 如果有选项且没有设置默认值,则设置第一个为默认值
|
||||
if (defectTypeOptions.length > 0 && !form.defectType) {
|
||||
form.defectType = defectTypeOptions[0].code
|
||||
|
@ -217,25 +220,26 @@ const loadDefectTypes = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// 加载缺陷等级
|
||||
const loadDefectLevels = async () => {
|
||||
try {
|
||||
loadingDefectLevels.value = true
|
||||
const response = await getDefectLevels()
|
||||
|
||||
|
||||
// 转换数据格式
|
||||
const defectLevelOptions: DefectTypeOption[] = []
|
||||
|
||||
|
||||
// 检查数据格式并处理
|
||||
if (response.data && response.data.code === 0 && Array.isArray(response.data.data)) {
|
||||
// 处理标准API返回格式
|
||||
response.data.data.forEach((item) => {
|
||||
response.data.data.forEach(item => {
|
||||
defectLevelOptions.push({
|
||||
code: item.code,
|
||||
label: item.name, // 使用name作为label
|
||||
label: item.name, // 使用name作为label
|
||||
value: item.value,
|
||||
name: item.name,
|
||||
sort: item.sort,
|
||||
sort: item.sort
|
||||
})
|
||||
})
|
||||
defectLevels.value = defectLevelOptions
|
||||
|
@ -246,10 +250,10 @@ const loadDefectLevels = async () => {
|
|||
if (entries.length > 0) {
|
||||
const [code, name] = entries[0]
|
||||
defectLevelOptions.push({
|
||||
code,
|
||||
code: code,
|
||||
label: name as string,
|
||||
value: code,
|
||||
name: name as string,
|
||||
name: name as string
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -260,17 +264,17 @@ const loadDefectLevels = async () => {
|
|||
defectLevels.value = [
|
||||
{ code: 'low', label: '轻微', value: 'low', name: '轻微' },
|
||||
{ code: 'medium', label: '中等', value: 'medium', name: '中等' },
|
||||
{ code: 'high', label: '严重', value: 'high', name: '严重' },
|
||||
{ code: 'high', label: '严重', value: 'high', name: '严重' }
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
console.log('缺陷等级选项:', defectLevels.value)
|
||||
|
||||
|
||||
// 如果有选项且没有设置默认值,则设置默认值
|
||||
if (defectLevels.value.length > 0 && !form.defectLevel) {
|
||||
const mediumLevel = defectLevels.value.find((l) =>
|
||||
l.code.toLowerCase().includes('medium')
|
||||
|| (l.name && l.name.includes('中')),
|
||||
const mediumLevel = defectLevels.value.find(l =>
|
||||
l.code.toLowerCase().includes('medium') ||
|
||||
(l.name && l.name.includes('中'))
|
||||
)
|
||||
form.defectLevel = mediumLevel?.code || defectLevels.value[0].code
|
||||
form.defectLevelLabel = mediumLevel?.name || mediumLevel?.label || defectLevels.value[0].label
|
||||
|
@ -281,7 +285,7 @@ const loadDefectLevels = async () => {
|
|||
defectLevels.value = [
|
||||
{ code: 'low', label: '轻微', value: 'low', name: '轻微' },
|
||||
{ code: 'medium', label: '中等', value: 'medium', name: '中等' },
|
||||
{ code: 'high', label: '严重', value: 'high', name: '严重' },
|
||||
{ code: 'high', label: '严重', value: 'high', name: '严重' }
|
||||
]
|
||||
} finally {
|
||||
loadingDefectLevels.value = false
|
||||
|
@ -295,7 +299,7 @@ watch(() => props.annotation, (newAnnotation) => {
|
|||
const isVirtualAnnotation = newAnnotation.id.startsWith('virtual-')
|
||||
// 检查是否是多区域标注
|
||||
const isMultiAnnotation = newAnnotation.metadata?.isMultiAnnotation
|
||||
|
||||
|
||||
if (isVirtualAnnotation) {
|
||||
// 从按钮添加的缺陷,不显示坐标信息
|
||||
form.description = '手动添加的缺陷,请填写详细描述'
|
||||
|
@ -312,7 +316,7 @@ watch(() => props.annotation, (newAnnotation) => {
|
|||
form.description = `标注区域大小: ${Math.round(width)}x${Math.round(height)} 像素`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果有标签信息,设置为缺陷名称
|
||||
if (newAnnotation.label) {
|
||||
form.defectName = newAnnotation.label
|
||||
|
@ -325,45 +329,46 @@ const handleSubmit = async () => {
|
|||
Message.warning('没有标注数据')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 基础验证
|
||||
if (!form.defectName.trim()) {
|
||||
Message.warning('请输入缺陷名称')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!form.defectType) {
|
||||
Message.warning('请选择缺陷类型')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!form.defectLevel) {
|
||||
Message.warning('请选择严重程度')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
submitting.value = true
|
||||
|
||||
|
||||
// 检查是否是多区域标注
|
||||
const isMultiAnnotation = props.annotation.metadata?.isMultiAnnotation
|
||||
const annotationCount = props.annotation.metadata?.allAnnotations?.length || 0
|
||||
|
||||
|
||||
if (isMultiAnnotation && annotationCount > 0) {
|
||||
Message.loading({
|
||||
content: `正在保存包含${annotationCount}个标注区域的缺陷信息...`,
|
||||
duration: 0,
|
||||
duration: 0
|
||||
})
|
||||
} else {
|
||||
Message.loading({
|
||||
content: '正在保存缺陷信息...',
|
||||
duration: 0,
|
||||
duration: 0
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 触发提交事件
|
||||
console.log('form:', form)
|
||||
console.log("form:",form);
|
||||
emit('submit', form, props.annotation)
|
||||
|
||||
} catch (error) {
|
||||
console.error('提交缺陷失败:', error)
|
||||
Message.error('提交失败,请重试')
|
||||
|
@ -374,7 +379,7 @@ const handleSubmit = async () => {
|
|||
|
||||
// 处理缺陷类型变化
|
||||
const handleDefectTypeChange = (value: string) => {
|
||||
const selectedType = defectTypes.value.find((type) => type.code === value)
|
||||
const selectedType = defectTypes.value.find(type => type.code === value)
|
||||
if (selectedType) {
|
||||
form.defectTypeLabel = selectedType.name
|
||||
}
|
||||
|
@ -382,7 +387,7 @@ const handleDefectTypeChange = (value: string) => {
|
|||
|
||||
// 处理缺陷等级变化
|
||||
const handleDefectLevelChange = (value: string) => {
|
||||
const selectedLevel = defectLevels.value.find((level) => level.code === value)
|
||||
const selectedLevel = defectLevels.value.find(level => level.code === value)
|
||||
if (selectedLevel) {
|
||||
form.defectLevelLabel = selectedLevel.name
|
||||
}
|
||||
|
@ -395,13 +400,13 @@ onMounted(() => {
|
|||
if (props.annotation.label) {
|
||||
form.defectName = props.annotation.label
|
||||
}
|
||||
|
||||
|
||||
// 如果标注有position属性,可以设置位置
|
||||
if ('position' in props.annotation && typeof props.annotation.position === 'string') {
|
||||
form.defectPosition = props.annotation.position
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 加载缺陷等级和类型
|
||||
loadDefectLevels()
|
||||
loadDefectTypes()
|
||||
|
@ -424,7 +429,7 @@ onMounted(() => {
|
|||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
|
@ -446,22 +451,22 @@ onMounted(() => {
|
|||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
background: #f8f9fa;
|
||||
|
||||
|
||||
.arco-btn {
|
||||
min-width: 80px;
|
||||
font-weight: 500;
|
||||
|
||||
|
||||
&.arco-btn-primary {
|
||||
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
|
||||
border-color: #1890ff;
|
||||
box-shadow: 0 2px 4px rgba(24, 144, 255, 0.3);
|
||||
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
|
||||
box-shadow: 0 4px 8px rgba(24, 144, 255, 0.4);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 4px rgba(24, 144, 255, 0.3);
|
||||
|
@ -497,4 +502,4 @@ onMounted(() => {
|
|||
font-weight: 500;
|
||||
color: #374151;
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -76,20 +76,20 @@
|
|||
<div class="form-group">
|
||||
<label class="form-label">轴向尺寸</label>
|
||||
<div class="size-input-group">
|
||||
<a-button
|
||||
size="small"
|
||||
<a-button
|
||||
size="small"
|
||||
@click="defectForm.axial = Math.max(0, (defectForm.axial || 0) - 1)"
|
||||
>
|
||||
−
|
||||
</a-button>
|
||||
<a-input-number
|
||||
v-model="defectForm.axial"
|
||||
:min="0"
|
||||
<a-input-number
|
||||
v-model="defectForm.axial"
|
||||
:min="0"
|
||||
size="small"
|
||||
style="width: 70px"
|
||||
/>
|
||||
<a-button
|
||||
size="small"
|
||||
<a-button
|
||||
size="small"
|
||||
@click="defectForm.axial = (defectForm.axial || 0) + 1"
|
||||
>
|
||||
+
|
||||
|
@ -100,20 +100,20 @@
|
|||
<div class="form-group">
|
||||
<label class="form-label">弦向尺寸</label>
|
||||
<div class="size-input-group">
|
||||
<a-button
|
||||
size="small"
|
||||
<a-button
|
||||
size="small"
|
||||
@click="defectForm.chordwise = Math.max(0, (defectForm.chordwise || 0) - 1)"
|
||||
>
|
||||
−
|
||||
</a-button>
|
||||
<a-input-number
|
||||
v-model="defectForm.chordwise"
|
||||
:min="0"
|
||||
<a-input-number
|
||||
v-model="defectForm.chordwise"
|
||||
:min="0"
|
||||
size="small"
|
||||
style="width: 70px"
|
||||
/>
|
||||
<a-button
|
||||
size="small"
|
||||
<a-button
|
||||
size="small"
|
||||
@click="defectForm.chordwise = (defectForm.chordwise || 0) + 1"
|
||||
>
|
||||
+
|
||||
|
@ -128,20 +128,20 @@
|
|||
<div class="form-group">
|
||||
<label class="form-label">面积(mm²)</label>
|
||||
<div class="size-input-group">
|
||||
<a-button
|
||||
size="small"
|
||||
<a-button
|
||||
size="small"
|
||||
@click="defectForm.area = Math.max(0, (defectForm.area || 0) - 1)"
|
||||
>
|
||||
−
|
||||
</a-button>
|
||||
<a-input-number
|
||||
v-model="defectForm.area"
|
||||
:min="0"
|
||||
<a-input-number
|
||||
v-model="defectForm.area"
|
||||
:min="0"
|
||||
size="small"
|
||||
style="width: 70px"
|
||||
/>
|
||||
<a-button
|
||||
size="small"
|
||||
<a-button
|
||||
size="small"
|
||||
@click="defectForm.area = (defectForm.area || 0) + 1"
|
||||
>
|
||||
+
|
||||
|
@ -154,16 +154,16 @@
|
|||
<div class="form-row">
|
||||
<div class="form-group full-width">
|
||||
<label class="form-label">描述</label>
|
||||
<a-textarea
|
||||
v-model="defectForm.description"
|
||||
<a-textarea
|
||||
v-model="defectForm.description"
|
||||
:rows="3"
|
||||
placeholder="叶片前缘纵向裂纹,长度约15mm,宽度约2mm"
|
||||
/>
|
||||
<div class="action-button-container">
|
||||
<a-button
|
||||
size="small"
|
||||
class="standard-library-btn"
|
||||
<a-button
|
||||
size="small"
|
||||
@click="handleSelectFromStandardDescription"
|
||||
class="standard-library-btn"
|
||||
>
|
||||
从标准描述库选择
|
||||
</a-button>
|
||||
|
@ -175,16 +175,16 @@
|
|||
<div class="form-row">
|
||||
<div class="form-group full-width">
|
||||
<label class="form-label">维修建议</label>
|
||||
<a-textarea
|
||||
v-model="defectForm.repairIdea"
|
||||
<a-textarea
|
||||
v-model="defectForm.repairIdea"
|
||||
:rows="3"
|
||||
placeholder="建议进行表面修复处理,防止裂纹扩散"
|
||||
/>
|
||||
<div class="action-button-container">
|
||||
<a-button
|
||||
size="small"
|
||||
class="standard-library-btn"
|
||||
<a-button
|
||||
size="small"
|
||||
@click="handleSelectFromStandardInfo"
|
||||
class="standard-library-btn"
|
||||
>
|
||||
从标准信息库选择
|
||||
</a-button>
|
||||
|
@ -228,8 +228,8 @@
|
|||
<div class="form-row">
|
||||
<div class="form-group full-width">
|
||||
<label class="form-label">技术备注</label>
|
||||
<a-textarea
|
||||
v-model="defectForm.technicalNotes"
|
||||
<a-textarea
|
||||
v-model="defectForm.technicalNotes"
|
||||
:rows="4"
|
||||
placeholder="记录技术细节、处理方案、注意事项等"
|
||||
/>
|
||||
|
@ -239,8 +239,8 @@
|
|||
<div class="form-row">
|
||||
<div class="form-group full-width">
|
||||
<label class="form-label">修复记录</label>
|
||||
<a-textarea
|
||||
v-model="defectForm.repairRecord"
|
||||
<a-textarea
|
||||
v-model="defectForm.repairRecord"
|
||||
:rows="3"
|
||||
placeholder="记录修复过程、使用材料、处理结果等"
|
||||
/>
|
||||
|
@ -257,7 +257,7 @@
|
|||
|
||||
<!-- 无缺陷选中状态 -->
|
||||
<div v-else class="no-defect-selected">
|
||||
<IconFile class="empty-icon" />
|
||||
<icon-file class="empty-icon" />
|
||||
<p>请从左侧选择缺陷进行编辑</p>
|
||||
</div>
|
||||
|
||||
|
@ -272,10 +272,10 @@
|
|||
<div class="standard-library-content">
|
||||
<a-list :data="standardDescriptions" :bordered="false">
|
||||
<template #item="{ item }">
|
||||
<a-list-item
|
||||
:class="{ selected: selectedStandardDescription === item }"
|
||||
class="clickable-item"
|
||||
<a-list-item
|
||||
:class="{ 'selected': selectedStandardDescription === item }"
|
||||
@click="selectedStandardDescription = item"
|
||||
class="clickable-item"
|
||||
>
|
||||
<div class="description-item">
|
||||
<div class="description-title">{{ item.title }}</div>
|
||||
|
@ -298,10 +298,10 @@
|
|||
<div class="standard-library-content">
|
||||
<a-list :data="standardRepairIdeas" :bordered="false">
|
||||
<template #item="{ item }">
|
||||
<a-list-item
|
||||
:class="{ selected: selectedStandardInfo === item }"
|
||||
class="clickable-item"
|
||||
<a-list-item
|
||||
:class="{ 'selected': selectedStandardInfo === item }"
|
||||
@click="selectedStandardInfo = item"
|
||||
class="clickable-item"
|
||||
>
|
||||
<div class="description-item">
|
||||
<div class="description-title">{{ item.title }}</div>
|
||||
|
@ -316,7 +316,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
import { IconFile } from '@arco-design/web-vue/es/icon'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import type { DefectDetectionResult } from '@/apis/industrial-image/defect'
|
||||
|
@ -348,7 +348,7 @@ const defectForm = reactive({
|
|||
inspector: '',
|
||||
recheckStatus: '',
|
||||
technicalNotes: '',
|
||||
repairRecord: '',
|
||||
repairRecord: ''
|
||||
})
|
||||
|
||||
// 标准描述库相关
|
||||
|
@ -357,20 +357,20 @@ const selectedStandardDescription = ref<any>(null)
|
|||
const standardDescriptions = ref([
|
||||
{
|
||||
title: '前缘裂纹模板',
|
||||
content: '叶片前缘纵向裂纹,长度约15mm,宽度约2mm',
|
||||
content: '叶片前缘纵向裂纹,长度约15mm,宽度约2mm'
|
||||
},
|
||||
{
|
||||
title: '表面磨损模板',
|
||||
content: '叶片表面出现明显磨损痕迹,影响空气动力学性能',
|
||||
content: '叶片表面出现明显磨损痕迹,影响空气动力学性能'
|
||||
},
|
||||
{
|
||||
title: '截蚀损伤模板',
|
||||
content: '叶片前缘截蚀损伤,表面粗糙度增加,深度约1-3mm',
|
||||
title: '截蚀损伤模板',
|
||||
content: '叶片前缘截蚀损伤,表面粗糙度增加,深度约1-3mm'
|
||||
},
|
||||
{
|
||||
title: '腐蚀斑点模板',
|
||||
content: '叶片表面出现腐蚀斑点,直径约5-10mm,深度轻微',
|
||||
},
|
||||
content: '叶片表面出现腐蚀斑点,直径约5-10mm,深度轻微'
|
||||
}
|
||||
])
|
||||
|
||||
// 标准信息库相关
|
||||
|
@ -379,24 +379,24 @@ const selectedStandardInfo = ref<any>(null)
|
|||
const standardRepairIdeas = ref([
|
||||
{
|
||||
title: '裂纹修复建议',
|
||||
content: '建议进行表面修复处理,防止裂纹扩散',
|
||||
content: '建议进行表面修复处理,防止裂纹扩散'
|
||||
},
|
||||
{
|
||||
title: '磨损处理建议',
|
||||
content: '定期监测磨损程度,必要时进行表面打磨和重新涂层',
|
||||
content: '定期监测磨损程度,必要时进行表面打磨和重新涂层'
|
||||
},
|
||||
{
|
||||
title: '截蚀修复建议',
|
||||
content: '建议进行前缘修复,使用专用胶泥填补并重新整形',
|
||||
content: '建议进行前缘修复,使用专用胶泥填补并重新整形'
|
||||
},
|
||||
{
|
||||
title: '腐蚀处理建议',
|
||||
content: '清理腐蚀区域,涂抹防腐涂层,定期检查',
|
||||
content: '清理腐蚀区域,涂抹防腐涂层,定期检查'
|
||||
},
|
||||
{
|
||||
title: '严重损伤建议',
|
||||
content: '建议立即停机检修,更换受损部件,避免安全隐患',
|
||||
},
|
||||
content: '建议立即停机检修,更换受损部件,避免安全隐患'
|
||||
}
|
||||
])
|
||||
|
||||
// 监听选中缺陷变化,更新表单数据
|
||||
|
@ -413,7 +413,7 @@ watch(() => props.selectedDefect, (newDefect) => {
|
|||
chordwise: newDefect.chordwise || 0,
|
||||
area: calculateArea(newDefect.axial || 0, newDefect.chordwise || 0),
|
||||
description: newDefect.description || '',
|
||||
repairIdea: newDefect.repairIdea || '',
|
||||
repairIdea: newDefect.repairIdea || ''
|
||||
})
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
@ -437,7 +437,7 @@ const handleSave = () => {
|
|||
|
||||
const updatedDefect = {
|
||||
...props.selectedDefect,
|
||||
...defectForm,
|
||||
...defectForm
|
||||
}
|
||||
|
||||
emit('edit-defect', updatedDefect)
|
||||
|
@ -457,7 +457,7 @@ const handleDelete = () => {
|
|||
onOk: () => {
|
||||
emit('delete-defect', props.selectedDefect!.defectId)
|
||||
Message.success('缺陷已删除')
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -476,7 +476,7 @@ const handleCancel = () => {
|
|||
chordwise: props.selectedDefect.chordwise || 0,
|
||||
area: calculateArea(props.selectedDefect.axial || 0, props.selectedDefect.chordwise || 0),
|
||||
description: props.selectedDefect.description || '',
|
||||
repairIdea: props.selectedDefect.repairIdea || '',
|
||||
repairIdea: props.selectedDefect.repairIdea || ''
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -706,4 +706,4 @@ const handleCancelStandardInfo = () => {
|
|||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -3,21 +3,21 @@
|
|||
<div class="panel-header">
|
||||
<h2>缺陷管理</h2>
|
||||
<div class="header-actions">
|
||||
<a-button
|
||||
type="primary"
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!canAddDefect"
|
||||
@click="handleAddDefect"
|
||||
:disabled="!canAddDefect"
|
||||
>
|
||||
<template #icon><IconPlus /></template>
|
||||
<template #icon><icon-plus /></template>
|
||||
新增缺陷
|
||||
</a-button>
|
||||
<a-button type="text" @click="$emit('close')">
|
||||
<template #icon><IconClose /></template>
|
||||
<template #icon><icon-close /></template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panel-content">
|
||||
<!-- 项目树形结构 -->
|
||||
<div class="tree-section">
|
||||
|
@ -34,11 +34,11 @@
|
|||
<template #title="node">
|
||||
<div class="tree-node">
|
||||
<span class="node-icon">
|
||||
<IconFolder v-if="node.type === 'project'" />
|
||||
<IconSettings v-else-if="node.type === 'turbine'" />
|
||||
<IconTool v-else-if="node.type === 'part'" />
|
||||
<IconBug v-else-if="node.type === 'defect'" />
|
||||
<IconApps v-else />
|
||||
<icon-folder v-if="node.type === 'project'" />
|
||||
<icon-settings v-else-if="node.type === 'turbine'" />
|
||||
<icon-tool v-else-if="node.type === 'part'" />
|
||||
<icon-bug v-else-if="node.type === 'defect'" />
|
||||
<icon-apps v-else />
|
||||
</span>
|
||||
<span class="node-title">{{ node.name }}</span>
|
||||
<span v-if="node.imageCount" class="node-count">({{ node.imageCount }})</span>
|
||||
|
@ -53,9 +53,9 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { IconApps, IconBug, IconClose, IconFolder, IconPlus, IconSettings, IconTool } from '@arco-design/web-vue/es/icon'
|
||||
import { IconClose, IconBug, IconFolder, IconPlus, IconSettings, IconTool, IconApps } from '@arco-design/web-vue/es/icon'
|
||||
import type { PropType } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
|
||||
// 树节点类型
|
||||
export interface TreeNode {
|
||||
|
@ -91,28 +91,28 @@ const props = defineProps({
|
|||
// 缺陷列表
|
||||
defectList: {
|
||||
type: Array as PropType<DefectInfo[]>,
|
||||
default: () => [],
|
||||
default: () => []
|
||||
},
|
||||
// 选中的缺陷
|
||||
selectedDefect: {
|
||||
type: Object as PropType<DefectInfo | null>,
|
||||
default: null,
|
||||
default: null
|
||||
},
|
||||
// 树数据
|
||||
treeData: {
|
||||
type: Array as PropType<TreeNode[]>,
|
||||
default: () => [],
|
||||
default: () => []
|
||||
},
|
||||
// 选中的树节点
|
||||
selectedKeys: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
default: () => []
|
||||
},
|
||||
// 加载状态
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
// 定义事件
|
||||
|
@ -122,7 +122,7 @@ const emit = defineEmits([
|
|||
'load-more',
|
||||
'add-defect',
|
||||
'turbine-select',
|
||||
'close',
|
||||
'close'
|
||||
])
|
||||
|
||||
// 增强的树数据,添加缺陷信息
|
||||
|
@ -130,64 +130,64 @@ const enhancedTreeData = computed(() => {
|
|||
if (!props.treeData || !Array.isArray(props.treeData)) {
|
||||
return []
|
||||
}
|
||||
|
||||
return props.treeData.map((project) => enhanceTreeNode(project))
|
||||
|
||||
return props.treeData.map(project => enhanceTreeNode(project))
|
||||
})
|
||||
|
||||
// 递归增强树节点,为机组添加缺陷信息
|
||||
const enhanceTreeNode = (node: TreeNode): TreeNode => {
|
||||
const enhancedNode = { ...node }
|
||||
|
||||
|
||||
if (node.type === 'turbine') {
|
||||
// 为机组添加缺陷列表作为子节点,与部件平级
|
||||
const defectNodes = getDefectNodesForTurbine(node.id)
|
||||
enhancedNode.children = [
|
||||
...(node.children || []).map((child) => enhanceTreeNode(child)),
|
||||
...defectNodes,
|
||||
...(node.children || []).map(child => enhanceTreeNode(child)),
|
||||
...defectNodes
|
||||
]
|
||||
} else if (node.children) {
|
||||
enhancedNode.children = node.children.map((child) => enhanceTreeNode(child))
|
||||
enhancedNode.children = node.children.map(child => enhanceTreeNode(child))
|
||||
}
|
||||
|
||||
|
||||
return enhancedNode
|
||||
}
|
||||
|
||||
// 获取机组的缺陷节点
|
||||
const getDefectNodesForTurbine = (turbineId: string): TreeNode[] => {
|
||||
// 根据机组ID过滤缺陷列表
|
||||
const turbineDefects = props.defectList.filter((defect) =>
|
||||
defect.turbineId === turbineId || defect.imageId === turbineId,
|
||||
const turbineDefects = props.defectList.filter(defect =>
|
||||
defect.turbineId === turbineId || defect.imageId === turbineId
|
||||
)
|
||||
|
||||
|
||||
// 直接将缺陷作为子节点,与部件平级
|
||||
return turbineDefects.map((defect) => ({
|
||||
return turbineDefects.map(defect => ({
|
||||
id: defect.id,
|
||||
name: defect.defectName || '未命名缺陷',
|
||||
type: 'defect',
|
||||
defectLevel: defect.defectLevel,
|
||||
defectType: defect.defectType,
|
||||
detectionDate: defect.detectionDate,
|
||||
defectData: defect, // 保存完整的缺陷数据
|
||||
defectData: defect // 保存完整的缺陷数据
|
||||
}))
|
||||
}
|
||||
|
||||
// 处理节点选择
|
||||
const handleNodeSelect = (selectedKeys: string[], e: any) => {
|
||||
const selectedNode = e.node
|
||||
|
||||
|
||||
// 如果选择的是机组节点,触发查询缺陷列表
|
||||
if (selectedNode?.type === 'turbine') {
|
||||
emit('turbine-select', selectedNode.id)
|
||||
}
|
||||
|
||||
|
||||
// 如果选择的是缺陷节点,触发缺陷选择事件
|
||||
if (selectedNode?.type === 'defect') {
|
||||
const defect = selectedNode.defectData || props.defectList.find((d) => d.id === selectedNode.id)
|
||||
const defect = selectedNode.defectData || props.defectList.find(d => d.id === selectedNode.id)
|
||||
if (defect) {
|
||||
emit('defect-select', defect)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
emit('node-select', selectedKeys, e)
|
||||
}
|
||||
|
||||
|
@ -216,34 +216,34 @@ const canAddDefect = computed(() => {
|
|||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: #f8f9fa;
|
||||
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.panel-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
.tree-section {
|
||||
flex: 1;
|
||||
background-color: white;
|
||||
|
@ -253,7 +253,7 @@ const canAddDefect = computed(() => {
|
|||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
|
@ -263,7 +263,7 @@ const canAddDefect = computed(() => {
|
|||
border-bottom: 1px solid #f2f3f5;
|
||||
background: white;
|
||||
}
|
||||
|
||||
|
||||
.project-tree {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
|
@ -271,26 +271,26 @@ const canAddDefect = computed(() => {
|
|||
overflow-x: hidden;
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
|
||||
|
||||
// 美化滚动条样式
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
|
||||
|
||||
&:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Firefox 滚动条样式
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #c1c1c1 #f1f1f1;
|
||||
|
@ -317,7 +317,7 @@ const canAddDefect = computed(() => {
|
|||
color: #6b7280;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
.defect-count {
|
||||
font-size: 12px;
|
||||
color: #ff4d4f;
|
||||
|
@ -328,4 +328,4 @@ const canAddDefect = computed(() => {
|
|||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -1,16 +1,16 @@
|
|||
<template>
|
||||
<div class="header-toolbar">
|
||||
<div class="toolbar-buttons">
|
||||
<a-button size="large" :disabled="!currentImageId" @click="handleAutoAnnotate">
|
||||
<template #icon><IconRobot /></template>
|
||||
<a-button size="large" @click="handleAutoAnnotate" :disabled="!currentImageId">
|
||||
<template #icon><icon-robot /></template>
|
||||
自动标注
|
||||
</a-button>
|
||||
<a-button size="large" :disabled="!currentImageId" @click="handleManualAnnotate">
|
||||
<template #icon><IconEdit /></template>
|
||||
<a-button size="large" @click="handleManualAnnotate" :disabled="!currentImageId">
|
||||
<template #icon><icon-edit /></template>
|
||||
手动标注
|
||||
</a-button>
|
||||
<a-button size="large" @click="handleGenerateReport">
|
||||
<template #icon><IconFileImage /></template>
|
||||
<template #icon><icon-file-image /></template>
|
||||
生成检测报告
|
||||
</a-button>
|
||||
</div>
|
||||
|
@ -18,10 +18,11 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
IconEdit,
|
||||
IconFileImage,
|
||||
import {
|
||||
IconPlayArrow,
|
||||
IconRobot,
|
||||
IconEdit,
|
||||
IconFileImage
|
||||
} from '@arco-design/web-vue/es/icon'
|
||||
|
||||
defineProps<{
|
||||
|
@ -39,6 +40,8 @@ const handleStart = () => {
|
|||
emit('start')
|
||||
}
|
||||
|
||||
|
||||
|
||||
const handleAutoAnnotate = () => {
|
||||
emit('autoAnnotate')
|
||||
}
|
||||
|
@ -66,11 +69,11 @@ const handleGenerateReport = () => {
|
|||
.arco-btn {
|
||||
font-weight: 500;
|
||||
border-radius: 6px;
|
||||
|
||||
|
||||
&.arco-btn-primary {
|
||||
background: #3b82f6;
|
||||
border-color: #3b82f6;
|
||||
|
||||
|
||||
&:hover {
|
||||
background: #2563eb;
|
||||
border-color: #2563eb;
|
||||
|
@ -90,4 +93,4 @@ const handleGenerateReport = () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<a-modal
|
||||
:visible="previewModalVisible"
|
||||
title="图像详情"
|
||||
width="80%"
|
||||
:footer="footerButtons"
|
||||
:mask-closable="true"
|
||||
:confirm-loading="loading"
|
||||
@update:visible="emit('update:previewModalVisible', $event)"
|
||||
:visible="previewModalVisible"
|
||||
title="图像详情"
|
||||
width="80%"
|
||||
:footer="footerButtons"
|
||||
:mask-closable="true"
|
||||
@update:visible="emit('update:previewModalVisible', $event)"
|
||||
:confirm-loading="loading"
|
||||
>
|
||||
<div v-if="previewImage" class="modal-image-viewer">
|
||||
<img :src="getImageUrl(previewImage.imagePath)" :alt="editingData.imageName" class="preview-image" />
|
||||
|
@ -62,8 +62,8 @@
|
|||
|
||||
<a-form-item label="图片类型描述" field="imageTypeLabel">
|
||||
<a-textarea
|
||||
v-model="editingData.imageTypeLabel"
|
||||
:auto-size="{ minRows: 2, maxRows: 4 }"
|
||||
v-model="editingData.imageTypeLabel"
|
||||
:auto-size="{ minRows: 2, maxRows: 4 }"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
@ -77,7 +77,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import axios from 'axios'
|
||||
|
||||
|
@ -115,7 +115,7 @@ const editingData = ref<{
|
|||
imageType?: string
|
||||
imageTypeLabel?: string
|
||||
partId?: string
|
||||
imageId?: string
|
||||
imageId?:string
|
||||
}>({
|
||||
imagePath: '',
|
||||
imageName: '',
|
||||
|
@ -127,7 +127,7 @@ const editingData = ref<{
|
|||
imageType: '',
|
||||
imageTypeLabel: '',
|
||||
partId: '',
|
||||
imageId: '',
|
||||
imageId:''
|
||||
})
|
||||
|
||||
// 监听预览图片变化,初始化编辑数据
|
||||
|
@ -140,7 +140,7 @@ watch(() => props.previewImage, (newVal) => {
|
|||
const getImageUrl = (imagePath: string): string => {
|
||||
if (!imagePath) return ''
|
||||
if (imagePath.startsWith('http')) return imagePath
|
||||
const baseUrl = 'http://localhost:8080'
|
||||
const baseUrl = 'http://pms.dtyx.net:9158'
|
||||
return `${baseUrl}${imagePath}`
|
||||
}
|
||||
|
||||
|
@ -177,18 +177,17 @@ const handleSave = async () => {
|
|||
imageId: editingData.value.imageId,
|
||||
imageName: editingData.value.imageName,
|
||||
imageResolution: editingData.value.imageResolution,
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
console.log('requestData:', requestData)
|
||||
console.log("requestData:",requestData);
|
||||
|
||||
// 调用接口更新数据
|
||||
const response = await axios.put(
|
||||
`http://localhost:8080/image/setting-info/${editingData.value.partId}`,
|
||||
requestData,
|
||||
const response = await axios.post(
|
||||
`http://pms.dtyx.net:9158/image/setting-info/${editingData.value.partId}`,
|
||||
requestData
|
||||
)
|
||||
|
||||
if (response.data && response.data.code === 0) {
|
||||
if (response.data && response.data.code === 200 && response.data.success) {
|
||||
Message.success('图片信息保存成功')
|
||||
emit('save', editingData.value)
|
||||
emit('update:previewModalVisible', false)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue