实现设备中心模块的页面搭建和分页查询以及手动新增弹窗页面

This commit is contained in:
Mr.j 2025-07-31 17:32:21 +08:00
parent 7fe24ac2cf
commit 1d16db937c
201 changed files with 11323 additions and 7416 deletions

View File

@ -0,0 +1,147 @@
# 设备中心模块问题修复和改进总结
## 发现的问题
### 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模块保持了一致的设计标准和实现质量。

View File

@ -8,7 +8,7 @@ export default function appInfo(): Plugin {
apply: 'serve', apply: 'serve',
async buildStart() { async buildStart() {
const { bold, green, cyan, bgGreen, underline } = picocolors const { bold, green, cyan, bgGreen, underline } = picocolors
// eslint-disable-next-line no-console
console.log( console.log(
boxen( 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('持续迭代优化的前后端分离中后台管理系统框架。')}`, `${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('持续迭代优化的前后端分离中后台管理系统框架。')}`,

View File

@ -1,6 +1,7 @@
import http from '@/utils/http'
const { request } = http
import type { AttachInfoData, BusinessTypeResult } from './type' import type { AttachInfoData, BusinessTypeResult } from './type'
import http from '@/utils/http'
const { request } = http
/** /**
* *
@ -13,8 +14,8 @@ export function batchAddAttachment(businessType: string, formData: FormData) {
method: 'post', method: 'post',
data: formData, data: formData,
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }
@ -29,8 +30,8 @@ export function addAttachment(formData: FormData) {
method: 'post', method: 'post',
data: formData, data: formData,
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }
/** /**
@ -44,8 +45,8 @@ export function addAttachmentByDefectMarkPic(formData: FormData) {
method: 'post', method: 'post',
data: formData, data: formData,
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }
/** /**
@ -59,8 +60,8 @@ export function addAttachInsurance(formData: FormData) {
method: 'post', method: 'post',
data: formData, data: formData,
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }
/** /**
@ -69,7 +70,7 @@ export function addAttachInsurance(formData: FormData) {
export function getAttachBusinessTypes() { export function getAttachBusinessTypes() {
return request<BusinessTypeResult>({ return request<BusinessTypeResult>({
url: '/common/list/attach-business_type', url: '/common/list/attach-business_type',
method: 'get' method: 'get',
}) })
} }
@ -80,7 +81,7 @@ export function getAttachBusinessTypes() {
export function getAttachmentList(businessType: string) { export function getAttachmentList(businessType: string) {
return request<AttachInfoData[]>({ return request<AttachInfoData[]>({
url: `/attach-info/list/${businessType}`, url: `/attach-info/list/${businessType}`,
method: 'get' method: 'get',
}) })
} }
@ -91,6 +92,6 @@ export function getAttachmentList(businessType: string) {
export function deleteAttachment(id: string | number) { export function deleteAttachment(id: string | number) {
return request<boolean>({ return request<boolean>({
url: `/attach-info/${id}`, url: `/attach-info/${id}`,
method: 'delete' method: 'delete',
}) })
} }

View File

@ -1,5 +1,5 @@
import http from '@/utils/http'
import type { AttendanceRecordReq, AttendanceRecordResp } from './type' import type { AttendanceRecordReq, AttendanceRecordResp } from './type'
import http from '@/utils/http'
const BASE_URL = '/attendance-record' const BASE_URL = '/attendance-record'
@ -7,7 +7,7 @@ const BASE_URL = '/attendance-record'
export function addAttendanceRecord(data: AttendanceRecordReq) { export function addAttendanceRecord(data: AttendanceRecordReq) {
return http.post<AttendanceRecordResp>(BASE_URL, data, { return http.post<AttendanceRecordResp>(BASE_URL, data, {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json',
} },
}) })
} }

View File

@ -1,6 +1,6 @@
import type * as T from './type' import type * as T from './type'
import http from '@/utils/http' import http from '@/utils/http'
import { convertMenuData, type ApiMenuItem } from '@/utils/menuConverter' import { type ApiMenuItem, convertMenuData } from '@/utils/menuConverter'
export type * from './type' export type * from './type'

View File

@ -1,64 +1,64 @@
/** 用户信息响应类型 */ /** 用户信息响应类型 */
export interface UserInfoResponse { export interface UserInfoResponse {
code: number; code: number
status: number; status: number
success: boolean; success: boolean
msg: string; msg: string
data: { data: {
user: UserDetail; user: UserDetail
dept: DeptDetail; dept: DeptDetail
roles: RoleDetail[]; roles: RoleDetail[]
posts: any[]; posts: any[]
}; }
} }
/** 用户详细信息 */ /** 用户详细信息 */
export interface UserDetail { export interface UserDetail {
userId: string; userId: string
account: string; account: string
name: string; name: string
status: number; status: number
userCode: string; userCode: string
userStatus: string; userStatus: string
userType: string; userType: string
mobile: string; mobile: string
createTime: string; createTime: string
avatar?: string; avatar?: string
} }
/** 部门详细信息 */ /** 部门详细信息 */
export interface DeptDetail { export interface DeptDetail {
deptId: string; deptId: string
deptName: string; deptName: string
parentId: string; parentId: string
orderNum: number; orderNum: number
leaderId: string; leaderId: string
status: string; status: string
} }
/** 角色详细信息 */ /** 角色详细信息 */
export interface RoleDetail { export interface RoleDetail {
roleId: string; roleId: string
roleName: string; roleName: string
roleCode: string | null; roleCode: string | null
roleKey: string; roleKey: string
} }
/** 用户类型 - 兼容旧版本 */ /** 用户类型 - 兼容旧版本 */
export interface UserInfo { export interface UserInfo {
id: string; id: string
username: string; username: string
nickname: string; nickname: string
gender: 0 | 1 | 2; gender: 0 | 1 | 2
email: string; email: string
phone: string; phone: string
avatar: string; avatar: string
pwdResetTime: string; pwdResetTime: string
pwdExpired: boolean; pwdExpired: boolean
registrationDate: string; registrationDate: string
deptName: string; deptName: string
roles: string[]; roles: string[]
permissions: string[]; permissions: string[]
} }
/** 路由类型 */ /** 路由类型 */

View File

@ -66,9 +66,9 @@ export interface DefectTypeResp {
/** 缺陷类型选项类型 - 用于前端组件 */ /** 缺陷类型选项类型 - 用于前端组件 */
export interface DefectTypeOption { export interface DefectTypeOption {
code: string; code: string
label: string; label: string
value: string; value: string
name?: string; // 兼容性字段 name?: string // 兼容性字段
sort?: number; // 兼容性字段 sort?: number // 兼容性字段
} }

View File

@ -1,5 +1,5 @@
import type { CertificationInfo, CertificationListParams, CertificationListResponse, CertificationPageResponse, CertificationReq, SimpleUserInfo } from './type'
import http from '@/utils/http' import http from '@/utils/http'
import type { CertificationInfo, CertificationListParams, CertificationListResponse, SimpleUserInfo,CertificationPageResponse, CertificationReq } from './type'
const { request } = http const { request } = http
@ -11,7 +11,7 @@ export function createCertification(data: CertificationReq) {
return request({ return request({
url: '/certification', url: '/certification',
method: 'post', method: 'post',
data data,
}) })
} }
@ -20,7 +20,7 @@ export function getCertificationList(params: CertificationListParams) {
return request<CertificationListResponse>({ return request<CertificationListResponse>({
url: '/certification/list', url: '/certification/list',
method: 'get', method: 'get',
params params,
}) })
} }
@ -28,7 +28,7 @@ export function getCertificationList(params: CertificationListParams) {
export function getCertificationDetail(certificationId: string) { export function getCertificationDetail(certificationId: string) {
return request<CertificationInfo>({ return request<CertificationInfo>({
url: `/certification/detail/${certificationId}`, url: `/certification/detail/${certificationId}`,
method: 'get' method: 'get',
}) })
} }
@ -37,7 +37,7 @@ export function updateCertification(certificationId: string, data: Certification
return request({ return request({
url: `/certification/${certificationId}`, url: `/certification/${certificationId}`,
method: 'put', method: 'put',
data data,
}) })
} }
@ -45,7 +45,7 @@ export function updateCertification(certificationId: string, data: Certification
export function deleteCertification(certificationId: string) { export function deleteCertification(certificationId: string) {
return request({ return request({
url: `/certification/${certificationId}`, url: `/certification/${certificationId}`,
method: 'delete' method: 'delete',
}) })
} }
@ -54,7 +54,7 @@ export function batchDeleteCertification(ids: string[]) {
return request({ return request({
url: '/certification/batch', url: '/certification/batch',
method: 'delete', method: 'delete',
data: { ids } data: { ids },
}) })
} }
@ -64,7 +64,7 @@ export function exportCertification(params: CertificationListParams) {
url: '/certification/export', url: '/certification/export',
method: 'get', method: 'get',
params, params,
responseType: 'blob' responseType: 'blob',
}) })
} }
@ -72,7 +72,7 @@ export function exportCertification(params: CertificationListParams) {
export function getUserList() { export function getUserList() {
return request<SimpleUserInfo[]>({ return request<SimpleUserInfo[]>({
url: '/user/list', url: '/user/list',
method: 'get' method: 'get',
}) })
} }
// 查询人员资质信息分页列表(新接口) // 查询人员资质信息分页列表(新接口)
@ -80,6 +80,6 @@ export function getCertificationPage(params: CertificationListParams) {
return request<CertificationPageResponse>({ return request<CertificationPageResponse>({
url: '/certification/page', url: '/certification/page',
method: 'get', method: 'get',
params params,
}) })
} }

View File

@ -0,0 +1,49 @@
import http from '@/utils/http'
import type * as T from '@/types/equipment.d'
const BASE_URL = '/equipment'
/** @desc 分页查询设备列表 */
export function pageEquipment(query: T.EquipmentPageQuery) {
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)
}
/** @desc 查询设备详情 */
export function getEquipmentDetail(equipmentId: string) {
return http.get<T.EquipmentResp>(`${BASE_URL}/detail/${equipmentId}`)
}
/** @desc 新增设备 */
export function createEquipment(data: T.EquipmentReq) {
return http.post(`${BASE_URL}`, data)
}
/** @desc 更新设备 */
export function updateEquipment(equipmentId: string, data: T.EquipmentReq) {
return http.put(`${BASE_URL}/${equipmentId}`, data)
}
/** @desc 删除设备 */
export function deleteEquipment(equipmentId: string) {
return http.del(`${BASE_URL}/${equipmentId}`)
}
/** @desc 设备状态变更 */
export function changeEquipmentStatus(equipmentId: string, status: string) {
return http.put(`${BASE_URL}/${equipmentId}/status`, { status })
}
/** @desc 设备分配 */
export function assignEquipment(equipmentId: string, userId: string) {
return http.put(`${BASE_URL}/${equipmentId}/assign`, { userId })
}
/** @desc 设备归还 */
export function returnEquipment(equipmentId: string) {
return http.put(`${BASE_URL}/${equipmentId}/return`)
}

207
src/apis/equipment/type.ts Normal file
View File

@ -0,0 +1,207 @@
/**
*
*/
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
}
/**
*
*/
export interface PageResult<T> {
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
}
/**
*
*/
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
}

View File

@ -40,7 +40,7 @@ export function createHealthRecord(data: HealthRecord) {
return request({ return request({
url: '/health-record', url: '/health-record',
method: 'post', method: 'post',
data data,
}) })
} }
@ -49,7 +49,7 @@ export function getHealthRecordList(params: HealthRecordListParams) {
return request<HealthRecordListResponse>({ return request<HealthRecordListResponse>({
url: '/health-record/list', url: '/health-record/list',
method: 'get', method: 'get',
params params,
}) })
} }
@ -57,7 +57,7 @@ export function getHealthRecordList(params: HealthRecordListParams) {
export function getHealthRecordDetail(id: string) { export function getHealthRecordDetail(id: string) {
return request<HealthRecord>({ return request<HealthRecord>({
url: `/health-record/detail/${id}`, url: `/health-record/detail/${id}`,
method: 'get' method: 'get',
}) })
} }
@ -66,7 +66,7 @@ export function updateHealthRecord(id: string, data: HealthRecord) {
return request({ return request({
url: `/health-record/${id}`, url: `/health-record/${id}`,
method: 'put', method: 'put',
data data,
}) })
} }
@ -74,7 +74,7 @@ export function updateHealthRecord(id: string, data: HealthRecord) {
export function deleteHealthRecord(id: string) { export function deleteHealthRecord(id: string) {
return request({ return request({
url: `/health-record/${id}`, url: `/health-record/${id}`,
method: 'delete' method: 'delete',
}) })
} }
@ -89,8 +89,8 @@ export function uploadHealthReport(file: File, recordId: string) {
method: 'post', method: 'post',
data: formData, data: formData,
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }
@ -99,7 +99,7 @@ export function downloadHealthReport(fileId: string) {
return request({ return request({
url: `/health-record/download-report/${fileId}`, url: `/health-record/download-report/${fileId}`,
method: 'get', method: 'get',
responseType: 'blob' responseType: 'blob',
}) })
} }
@ -107,7 +107,7 @@ export function downloadHealthReport(fileId: string) {
export function getEmployeeHealthHistory(employeeId: string) { export function getEmployeeHealthHistory(employeeId: string) {
return request<HealthRecord[]>({ return request<HealthRecord[]>({
url: `/health-record/employee/${employeeId}`, url: `/health-record/employee/${employeeId}`,
method: 'get' method: 'get',
}) })
} }
@ -117,7 +117,7 @@ export function exportHealthRecords(params: HealthRecordListParams) {
url: '/health-record/export', url: '/health-record/export',
method: 'get', method: 'get',
params, params,
responseType: 'blob' responseType: 'blob',
}) })
} }
@ -131,6 +131,6 @@ export function scheduleHealthCheck(data: {
return request({ return request({
url: '/health-record/schedule', url: '/health-record/schedule',
method: 'post', method: 'post',
data data,
}) })
} }

View File

@ -95,12 +95,12 @@ export const addManualDefect = (params: ManualDefectAddRequest,imageId:string) =
// 缺陷列表查询参数接口 // 缺陷列表查询参数接口
export interface DefectListParams { export interface DefectListParams {
defectId?: string; defectId?: string
defectLevel?: string; defectLevel?: string
defectType?: string; defectType?: string
keyword?: string; keyword?: string
turbineId?: string; turbineId?: string
imageId?: string; // 添加imageId参数用于按图像筛选缺陷 imageId?: string // 添加imageId参数用于按图像筛选缺陷
} }
/** @desc 获取缺陷列表 */ /** @desc 获取缺陷列表 */
@ -159,8 +159,8 @@ export const uploadAnnotatedImage = (imageBlob: Blob, fileName: string) => {
success: boolean success: boolean
}>('/attach-info/defect_mark_pic', formData, { }>('/attach-info/defect_mark_pic', formData, {
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }
@ -177,63 +177,63 @@ export interface AttachInfoData {
// 缺陷信息接口 // 缺陷信息接口
export interface DefectInfo { export interface DefectInfo {
id: string; id: string
defectId?: string; defectId?: string
defectName?: string; defectName?: string
defectLevel?: string; defectLevel?: string
defectType?: string; defectType?: string
defectPosition?: string; defectPosition?: string
detectionDate?: string; detectionDate?: string
description?: string; description?: string
repairStatus?: string; repairStatus?: string
repairIdea?: string; repairIdea?: string
labelInfo?: string; labelInfo?: string
markInfo?: { markInfo?: {
bbox?: number[]; bbox?: number[]
clsId?: number; clsId?: number
confidence?: number; confidence?: number
label?: string; label?: string
[key: string]: any; [key: string]: any
}; }
[key: string]: any; [key: string]: any
} }
// 缺陷等级类型 // 缺陷等级类型
export interface DefectLevelType { export interface DefectLevelType {
code: string; code: string
name: string; name: string
value: string; value: string
sort: number; sort: number
description?: string; description?: string
} }
// 缺陷类型 // 缺陷类型
export interface DefectType { export interface DefectType {
code: string; code: string
name: string; name: string
value: string; value: string
sort: number; sort: number
description?: string; description?: string
} }
// 获取缺陷等级列表 // 获取缺陷等级列表
export const getDefectLevels = () => { export const getDefectLevels = () => {
return http.get<{ return http.get<{
code: number; code: number
data: DefectLevelType[]; data: DefectLevelType[]
msg: string; msg: string
status: number; status: number
success: boolean; success: boolean
}>('/common/list/defect-level') }>('/common/list/defect-level')
} }
// 获取缺陷类型列表 // 获取缺陷类型列表
export const getDefectTypes = () => { export const getDefectTypes = () => {
return http.get<{ return http.get<{
code: number; code: number
data: DefectType[]; data: DefectType[]
msg: string; msg: string
status: number; status: number
success: boolean; success: boolean
}>('/common/list/defect-type') }>('/common/list/defect-type')
} }

View File

@ -123,12 +123,12 @@ export const uploadSingleImage = (imageSource: string, file: File, params?: {
if (params?.partId) queryParams.append('partId', params.partId) if (params?.partId) queryParams.append('partId', params.partId)
if (params?.uploadUser) queryParams.append('uploadUser', params.uploadUser) 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, { return http.post(url, formData, {
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }
@ -143,7 +143,7 @@ export const batchUploadImages = (imageSource: string, files: File[], params?: {
const formData = new FormData() const formData = new FormData()
// 添加文件 // 添加文件
files.forEach(file => { files.forEach((file) => {
formData.append('files', file) formData.append('files', file)
}) })
@ -155,12 +155,12 @@ export const batchUploadImages = (imageSource: string, files: File[], params?: {
if (params?.partId) queryParams.append('partId', params.partId) if (params?.partId) queryParams.append('partId', params.partId)
if (params?.uploadUser) queryParams.append('uploadUser', params.uploadUser) 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, { return http.post(url, formData, {
headers: { 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, { return http.post('/defect/detect', params, {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json',
} },
}) })
} }
@ -183,12 +183,12 @@ export const uploadImageToPartV2 = (
imageSource: string, imageSource: string,
partId: string, partId: string,
files: File[], files: File[],
params: Partial<T.ImageUploadParams> params: Partial<T.ImageUploadParams>,
) => { ) => {
const formData = new FormData() const formData = new FormData()
// 添加文件 // 添加文件
files.forEach(file => { files.forEach((file) => {
formData.append('files', file) formData.append('files', file)
}) })
@ -226,8 +226,8 @@ export const uploadImageToPartV2 = (
return http.post(`/image/${imageSource}/upload/${partId}`, formData, { return http.post(`/image/${imageSource}/upload/${partId}`, formData, {
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }
@ -238,8 +238,8 @@ export const importImages = (files: FileList | File[], params: T.ImageImportPara
// 使用批量上传接口 // 使用批量上传接口
return batchUploadImages(params.imageSource || 'default', fileArray, { return batchUploadImages(params.imageSource || 'default', fileArray, {
partId: params.componentId, partId: params.componentId,
uploadUser: params.uploadUser uploadUser: params.uploadUser,
}).then(response => { }).then((response) => {
// 如果需要自动标注 // 如果需要自动标注
if (params.autoAnnotate && params.annotationTypes && params.annotationTypes.length > 0) { if (params.autoAnnotate && params.annotationTypes && params.annotationTypes.length > 0) {
// 这里可以添加自动标注逻辑 // 这里可以添加自动标注逻辑
@ -255,7 +255,7 @@ export const autoAnnotateImage = (params: T.AutoAnnotationParams) => {
confThreshold: params.confidenceThreshold || 0.5, confThreshold: params.confidenceThreshold || 0.5,
defectTypeList: params.annotationTypes, defectTypeList: params.annotationTypes,
imageId: params.imageId, 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 上传图像(保留旧接口兼容性) */ /** @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, { return uploadSingleImage('default', file, {
partId: params.componentId, partId: params.componentId,
uploadUser: 'current-user' uploadUser: 'current-user',
}) })
} }
@ -321,21 +321,20 @@ export function reprocessImage(params: T.ImageProcessParams) {
export const batchProcessImages = (imageIds: string[], processType: string) => { export const batchProcessImages = (imageIds: string[], processType: string) => {
return http.post<T.ImageProcessResult[]>(`/industrial-image/batch-process`, { return http.post<T.ImageProcessResult[]>(`/industrial-image/batch-process`, {
imageIds, imageIds,
processType processType,
}) })
} }
/** @desc 导出处理结果 */ /** @desc 导出处理结果 */
export function exportProcessResults(query: T.ImageQuery) { export function exportProcessResults(query: T.ImageQuery) {
return http.get(`/industrial-image/export/results`, query, { return http.get(`/industrial-image/export/results`, query, {
responseType: 'blob' responseType: 'blob',
}) })
} }
/** @desc 生成检测报告 */ /** @desc 生成检测报告 */
export function generateReport(projectId: string) { export function generateReport(projectId: string) {
return http.post(`/industrial-image/report/generate`, { projectId }, { return http.post(`/industrial-image/report/generate`, { projectId }, {
responseType: 'blob' responseType: 'blob',
}) })
} }

View File

@ -34,7 +34,7 @@ export function createInsuranceCompany(data: InsuranceCompany) {
return request({ return request({
url: '/insurance-company', url: '/insurance-company',
method: 'post', method: 'post',
data data,
}) })
} }
@ -43,7 +43,7 @@ export function getInsuranceCompanyList(params: InsuranceCompanyListParams) {
return request<InsuranceCompanyListResponse>({ return request<InsuranceCompanyListResponse>({
url: '/insurance-company/list', url: '/insurance-company/list',
method: 'get', method: 'get',
params params,
}) })
} }
@ -51,7 +51,7 @@ export function getInsuranceCompanyList(params: InsuranceCompanyListParams) {
export function getInsuranceCompanyDetail(id: string) { export function getInsuranceCompanyDetail(id: string) {
return request<InsuranceCompany>({ return request<InsuranceCompany>({
url: `/insurance-company/detail/${id}`, url: `/insurance-company/detail/${id}`,
method: 'get' method: 'get',
}) })
} }
@ -60,7 +60,7 @@ export function updateInsuranceCompany(id: string, data: InsuranceCompany) {
return request({ return request({
url: `/insurance-company/${id}`, url: `/insurance-company/${id}`,
method: 'put', method: 'put',
data data,
}) })
} }
@ -68,7 +68,7 @@ export function updateInsuranceCompany(id: string, data: InsuranceCompany) {
export function deleteInsuranceCompany(id: string) { export function deleteInsuranceCompany(id: string) {
return request({ return request({
url: `/insurance-company/${id}`, url: `/insurance-company/${id}`,
method: 'delete' method: 'delete',
}) })
} }
@ -76,7 +76,7 @@ export function deleteInsuranceCompany(id: string) {
export function terminateCooperation(id: string) { export function terminateCooperation(id: string) {
return request({ return request({
url: `/insurance-company/terminate/${id}`, url: `/insurance-company/terminate/${id}`,
method: 'post' method: 'post',
}) })
} }
@ -84,7 +84,7 @@ export function terminateCooperation(id: string) {
export function resumeCooperation(id: string) { export function resumeCooperation(id: string) {
return request({ return request({
url: `/insurance-company/resume/${id}`, url: `/insurance-company/resume/${id}`,
method: 'post' method: 'post',
}) })
} }

View File

@ -54,8 +54,8 @@ export function uploadInsuranceFile(data: UploadInsuranceFileParams) {
method: 'post', method: 'post',
data: formData, data: formData,
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }
@ -64,7 +64,7 @@ export function getInsuranceFileList(params: InsuranceFileListParams) {
return request<InsuranceFileListResponse>({ return request<InsuranceFileListResponse>({
url: '/insurance-file/list', url: '/insurance-file/list',
method: 'get', method: 'get',
params params,
}) })
} }
@ -72,7 +72,7 @@ export function getInsuranceFileList(params: InsuranceFileListParams) {
export function getInsuranceFileDetail(id: string) { export function getInsuranceFileDetail(id: string) {
return request<InsuranceFile>({ return request<InsuranceFile>({
url: `/insurance-file/detail/${id}`, url: `/insurance-file/detail/${id}`,
method: 'get' method: 'get',
}) })
} }
@ -81,7 +81,7 @@ export function updateInsuranceFile(id: string, data: Partial<InsuranceFile>) {
return request({ return request({
url: `/insurance-file/${id}`, url: `/insurance-file/${id}`,
method: 'put', method: 'put',
data data,
}) })
} }
@ -89,7 +89,7 @@ export function updateInsuranceFile(id: string, data: Partial<InsuranceFile>) {
export function deleteInsuranceFile(id: string) { export function deleteInsuranceFile(id: string) {
return request({ return request({
url: `/insurance-file/${id}`, url: `/insurance-file/${id}`,
method: 'delete' method: 'delete',
}) })
} }
@ -98,7 +98,7 @@ export function batchDeleteInsuranceFiles(ids: string[]) {
return request({ return request({
url: '/insurance-file/batch', url: '/insurance-file/batch',
method: 'delete', method: 'delete',
data: { ids } data: { ids },
}) })
} }
@ -107,7 +107,7 @@ export function downloadInsuranceFile(id: string) {
return request({ return request({
url: `/insurance-file/download/${id}`, url: `/insurance-file/download/${id}`,
method: 'get', method: 'get',
responseType: 'blob' responseType: 'blob',
}) })
} }
@ -116,7 +116,7 @@ export function previewInsuranceFile(id: string) {
return request({ return request({
url: `/insurance-file/preview/${id}`, url: `/insurance-file/preview/${id}`,
method: 'get', method: 'get',
responseType: 'blob' responseType: 'blob',
}) })
} }
@ -124,7 +124,7 @@ export function previewInsuranceFile(id: string) {
export function getEmployeeFiles(employeeId: string) { export function getEmployeeFiles(employeeId: string) {
return request<InsuranceFile[]>({ return request<InsuranceFile[]>({
url: `/insurance-file/employee/${employeeId}`, url: `/insurance-file/employee/${employeeId}`,
method: 'get' method: 'get',
}) })
} }
@ -136,7 +136,7 @@ export function getInsuranceFileStatistics() {
totalSize: number totalSize: number
}[]>({ }[]>({
url: '/insurance-file/statistics', url: '/insurance-file/statistics',
method: 'get' method: 'get',
}) })
} }
@ -162,7 +162,7 @@ export function batchUploadFiles(data: {
method: 'post', method: 'post',
data: formData, data: formData,
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }

View File

@ -28,7 +28,7 @@ export function createInsuranceType(data: InsuranceType) {
return request({ return request({
url: '/insurance-type', url: '/insurance-type',
method: 'post', method: 'post',
data data,
}) })
} }
@ -37,7 +37,7 @@ export function getInsuranceTypeList(params?: InsuranceTypeListParams) {
return request<InsuranceTypeListResponse>({ return request<InsuranceTypeListResponse>({
url: '/insurance-type/list', url: '/insurance-type/list',
method: 'get', method: 'get',
params params,
}) })
} }
@ -45,7 +45,7 @@ export function getInsuranceTypeList(params?: InsuranceTypeListParams) {
export function getInsuranceTypeDetail(insuranceTypeId: string) { export function getInsuranceTypeDetail(insuranceTypeId: string) {
return request<InsuranceType>({ return request<InsuranceType>({
url: `/insurance-type/detail/${insuranceTypeId}`, url: `/insurance-type/detail/${insuranceTypeId}`,
method: 'get' method: 'get',
}) })
} }
@ -54,7 +54,7 @@ export function updateInsuranceType(id: string, data: InsuranceType) {
return request({ return request({
url: `/insurance-type/${id}`, url: `/insurance-type/${id}`,
method: 'put', method: 'put',
data data,
}) })
} }
@ -62,7 +62,7 @@ export function updateInsuranceType(id: string, data: InsuranceType) {
export function deleteInsuranceType(id: string) { export function deleteInsuranceType(id: string) {
return request({ return request({
url: `/insurance-type/${id}`, url: `/insurance-type/${id}`,
method: 'delete' method: 'delete',
}) })
} }
@ -71,6 +71,6 @@ export function batchDeleteInsuranceType(ids: string[]) {
return request({ return request({
url: '/insurance-type/batch', url: '/insurance-type/batch',
method: 'delete', method: 'delete',
data: { ids } data: { ids },
}) })
} }

View File

@ -1,5 +1,5 @@
import http from '@/utils/http'
import type { InsuranceInfo, InsuranceListParams, InsuranceListResponse, RenewInsuranceParams } from './type' import type { InsuranceInfo, InsuranceListParams, InsuranceListResponse, RenewInsuranceParams } from './type'
import http from '@/utils/http'
const { request } = http const { request } = http
@ -11,7 +11,7 @@ export function createInsurance(data: InsuranceInfo) {
return request({ return request({
url: '/insurance-info', url: '/insurance-info',
method: 'post', method: 'post',
data data,
}) })
} }
@ -20,7 +20,7 @@ export function getInsuranceList(params: InsuranceListParams) {
return request<InsuranceListResponse>({ return request<InsuranceListResponse>({
url: '/insurance-info/list', url: '/insurance-info/list',
method: 'get', method: 'get',
params params,
}) })
} }
@ -28,7 +28,7 @@ export function getInsuranceList(params: InsuranceListParams) {
export function getInsuranceDetail(id: string) { export function getInsuranceDetail(id: string) {
return request<InsuranceInfo>({ return request<InsuranceInfo>({
url: `/insurance-info/detail/${id}`, url: `/insurance-info/detail/${id}`,
method: 'get' method: 'get',
}) })
} }
@ -37,7 +37,7 @@ export function updateInsurance(id: string, data: InsuranceInfo) {
return request({ return request({
url: `/insurance-info/${id}`, url: `/insurance-info/${id}`,
method: 'put', method: 'put',
data data,
}) })
} }
@ -45,7 +45,7 @@ export function updateInsurance(id: string, data: InsuranceInfo) {
export function deleteInsurance(id: string) { export function deleteInsurance(id: string) {
return request({ return request({
url: `/insurance-info/${id}`, url: `/insurance-info/${id}`,
method: 'delete' method: 'delete',
}) })
} }
@ -54,7 +54,7 @@ export function renewInsurance(id: string, data: RenewInsuranceParams) {
return request({ return request({
url: `/insurance-info/renew/${id}`, url: `/insurance-info/renew/${id}`,
method: 'post', method: 'post',
data data,
}) })
} }
@ -63,7 +63,7 @@ export function batchDeleteInsurance(ids: string[]) {
return request({ return request({
url: '/insurance-info/batch', url: '/insurance-info/batch',
method: 'delete', method: 'delete',
data: { ids } data: { ids },
}) })
} }
@ -73,6 +73,6 @@ export function exportInsurance(params: InsuranceListParams) {
url: '/insurance-info/export', url: '/insurance-info/export',
method: 'get', method: 'get',
params, params,
responseType: 'blob' responseType: 'blob',
}) })
} }

View File

@ -1,5 +1,5 @@
import type { ModelConfigDetailResponse, ModelConfigListResponse, ModelConfigRequest, ModelConfigResponse } from './type'
import http from '@/utils/http' import http from '@/utils/http'
import type { ModelConfigRequest, ModelConfigResponse, ModelConfigListResponse, ModelConfigDetailResponse } from './type'
const { request } = http const { request } = http
@ -11,7 +11,7 @@ export function createModelConfig(data: ModelConfigRequest) {
return request<ModelConfigResponse>({ return request<ModelConfigResponse>({
url: '/model-config', url: '/model-config',
method: 'post', method: 'post',
data data,
}) })
} }
@ -23,7 +23,7 @@ export function updateModelConfig(data: ModelConfigRequest) {
return request<ModelConfigResponse>({ return request<ModelConfigResponse>({
url: '/model-config', url: '/model-config',
method: 'put', method: 'put',
data data,
}) })
} }
@ -44,7 +44,7 @@ export function getModelConfigList(params?: {
return request<ModelConfigListResponse>({ return request<ModelConfigListResponse>({
url: '/model-config/list', url: '/model-config/list',
method: 'get', method: 'get',
params params,
}) })
} }
@ -55,7 +55,7 @@ export function getModelConfigList(params?: {
export function getModelConfigDetail(modelId: string) { export function getModelConfigDetail(modelId: string) {
return request<ModelConfigDetailResponse>({ return request<ModelConfigDetailResponse>({
url: `/model-config/${modelId}`, url: `/model-config/${modelId}`,
method: 'get' method: 'get',
}) })
} }
@ -66,6 +66,6 @@ export function getModelConfigDetail(modelId: string) {
export function deleteModelConfig(modelId: string) { export function deleteModelConfig(modelId: string) {
return request<any>({ return request<any>({
url: `/model-config/${modelId}`, url: `/model-config/${modelId}`,
method: 'delete' method: 'delete',
}) })
} }

View File

@ -1,5 +1,5 @@
import type { DimensionQuery, PerformanceDimension, PerformanceRule, RuleQuery } from './type'
import http from '@/utils/http' import http from '@/utils/http'
import type { PerformanceDimension, PerformanceRule, DimensionQuery, RuleQuery } from './type'
/** 维度相关 */ /** 维度相关 */
export function getDimensionList(params?: DimensionQuery) { export function getDimensionList(params?: DimensionQuery) {

View File

@ -89,17 +89,17 @@ export function auditBudget(id: string, data: BudgetAuditReq) {
/** @desc 获取预算类型选项 */ /** @desc 获取预算类型选项 */
export function getBudgetTypes() { 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 上传预算附件 */ /** @desc 上传预算附件 */
export function uploadBudgetAttachment(file: File) { export function uploadBudgetAttachment(file: File) {
const formData = new FormData() const formData = new FormData()
formData.append('file', file) 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: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }
@ -111,6 +111,6 @@ export function deleteBudgetAttachment(id: string) {
/** @desc 导出预算记录 */ /** @desc 导出预算记录 */
export function exportBudgetRecord(query: BudgetQuery) { export function exportBudgetRecord(query: BudgetQuery) {
return http.get(`${BASE_URL}/export`, query, { return http.get(`${BASE_URL}/export`, query, {
responseType: 'blob' responseType: 'blob',
}) })
} }

View File

@ -41,8 +41,8 @@ export function importProject(file: File) {
formData.append('file', file) formData.append('file', file)
return http.post(`${BASE_URL}/import`, formData, { return http.post(`${BASE_URL}/import`, formData, {
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }

View File

@ -65,7 +65,7 @@ export function importTask(file: File, projectId: number) {
formData.append('projectId', projectId.toString()) formData.append('projectId', projectId.toString())
return http.post(`${BASE_URL}/import`, formData, { return http.post(`${BASE_URL}/import`, formData, {
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data',
} },
}) })
} }

View File

@ -1,4 +1,4 @@
import type { SalaryRecord, SalaryQuery, SalaryCreateRequest } from '@/views/salary-management/types' import type { SalaryCreateRequest, SalaryQuery, SalaryRecord } from '@/views/salary-management/types'
import http from '@/utils/http' import http from '@/utils/http'
const BASE_URL = '/salary' 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) return http.put<boolean>(`${BASE_URL}/${id}/approve`, data)
} }

View File

@ -1,4 +1,3 @@
import type * as T from './type'
import http from '@/utils/http' import http from '@/utils/http'
import { convertMenuData } from '@/utils/menuConverter' import { convertMenuData } from '@/utils/menuConverter'
@ -6,13 +5,13 @@ import { convertMenuData } from '@/utils/menuConverter'
* API获取菜单树形数据 * API获取菜单树形数据
*/ */
export function getMenuTreeForRole(query?: { terminalType?: string }) { 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 } // 假设响应格式为 { data: [...菜单数据], success: true, msg: "", code: 200 }
const data = res.data || []; const data = res.data || []
// 转换菜单数据为角色管理组件需要的格式 // 转换菜单数据为角色管理组件需要的格式
const convertedData = convertMenuData(data); const convertedData = convertMenuData(data)
return convertedData; return convertedData
}); })
} }
/** /**
@ -23,31 +22,31 @@ export function getMenuTreeForRole(query?: { terminalType?: string }) {
*/ */
export function transformMenusWithPermissions(menus: any[], selectedMenuIds: 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[]) => { 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) { if (item.children && item.children.length > 0) {
item.children = processMenus(item.children); item.children = processMenus(item.children)
} }
return item; return item
}); })
}; }
return processMenus(result); return processMenus(result)
} }
/** /**
* ID列表 * ID列表
*/ */
export function getRoleMenuIds(roleId: string) { export function getRoleMenuIds(roleId: string) {
return http.get<string[]>(`/role/get-menus/${roleId}`); return http.get<string[]>(`/role/get-menus/${roleId}`)
} }
/** /**
@ -56,6 +55,6 @@ export function getRoleMenuIds(roleId: string) {
export function assignRoleMenus(roleId: string, menuIds: string[]) { export function assignRoleMenus(roleId: string, menuIds: string[]) {
return http.post('/role/bind-menu', { return http.post('/role/bind-menu', {
roleId, roleId,
menuIds menuIds,
}); })
} }

View File

@ -1,46 +1,46 @@
import type * as T from './type'; import type * as T from './type'
import http from '@/utils/http'; import http from '@/utils/http'
const BASE_URL = '/post'; const BASE_URL = '/post'
/** /**
* *
*/ */
export function addPost(data: T.PostAddReq) { 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) { 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) { 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) { 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) { 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) { export function deletePost(postId: string) {
return http.del<any>(`${BASE_URL}/${postId}`); return http.del<any>(`${BASE_URL}/${postId}`)
} }

View File

@ -564,113 +564,113 @@ export interface MessagePageQuery extends MessageQuery, PageQuery {
/** 新增菜单请求参数 */ /** 新增菜单请求参数 */
export interface MenuAddReq { export interface MenuAddReq {
menuName: string; menuName: string
menuType: string; menuType: string
orderNum: number; orderNum: number
parentId: string; parentId: string
perms: string; perms: string
terminalType: string; terminalType: string
url: string; url: string
visible: string; visible: string
} }
/** 新菜单树查询参数 */ /** 新菜单树查询参数 */
export interface MenuTreeQuery { export interface MenuTreeQuery {
menuName?: string; menuName?: string
terminalType?: string; terminalType?: string
} }
/** 新菜单详情响应类型 */ /** 新菜单详情响应类型 */
export interface MenuDetailResp { export interface MenuDetailResp {
menuId: string; menuId: string
menuName: string; menuName: string
menuType: string; menuType: string
orderNum: number; orderNum: number
parentId: string; parentId: string
perms: string; perms: string
url: string; url: string
visible: string; visible: string
} }
/** 菜单更新请求参数 */ /** 菜单更新请求参数 */
export interface MenuUpdateReq { export interface MenuUpdateReq {
menuName: string; menuName: string
menuType: string; menuType: string
orderNum: number; orderNum: number
parentId: string; parentId: string
perms: string; perms: string
terminalType: string; terminalType: string
url: string; url: string
visible: string; visible: string
} }
/** 新角色信息请求实体 */ /** 新角色信息请求实体 */
export interface RoleAddReq { export interface RoleAddReq {
remark: string; remark: string
roleCode: string; roleCode: string
roleKey: string; roleKey: string
roleName: string; roleName: string
status: number; status: number
} }
/** 角色信息更新请求实体 */ /** 角色信息更新请求实体 */
export interface RoleUpdateReq { export interface RoleUpdateReq {
remark: string; remark: string
roleCode: string; roleCode: string
roleKey: string; roleKey: string
roleName: string; roleName: string
status: number; status: number
} }
/** 新角色信息响应实体 */ /** 新角色信息响应实体 */
export interface RoleNewResp { export interface RoleNewResp {
remark: string; remark: string
roleCode: string; roleCode: string
roleId: string; roleId: string
roleKey: string; roleKey: string
roleName: string; roleName: string
status: string; status: string
isSystem?: boolean; isSystem?: boolean
} }
/** 角色菜单绑定请求 */ /** 角色菜单绑定请求 */
export interface RoleBindMenuReq { export interface RoleBindMenuReq {
menuIds: string[]; menuIds: string[]
roleId: string; roleId: string
} }
/** 角色查询参数(新接口) */ /** 角色查询参数(新接口) */
export interface RoleNewQuery { export interface RoleNewQuery {
roleName?: string; roleName?: string
} }
// 岗位相关类型定义 // 岗位相关类型定义
export interface PostVO { export interface PostVO {
postId: string; postId: string
postName: string; postName: string
postSort: number; postSort: number
remark: string; remark: string
status: string | number; status: string | number
createTime?: string; createTime?: string
updateTime?: string; updateTime?: string
} }
export interface PostPageQuery { export interface PostPageQuery {
postName?: string; postName?: string
page?: number; page?: number
size?: number; size?: number
} }
export interface PostAddReq { export interface PostAddReq {
postName: string; postName: string
postSort: number; postSort: number
remark: string; remark: string
status: number; status: number
} }
export interface PostUpdateReq { export interface PostUpdateReq {
postName: string; postName: string
postSort: number; postSort: number
remark: string; remark: string
status: number; status: number
} }

View File

@ -47,7 +47,7 @@ const breadcrumbList = computed(() => {
const arr = obj ? obj.nodes.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false) : [] const arr = obj ? obj.nodes.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false) : []
// home // 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 [home.value, ...arr]
} }

View File

@ -66,7 +66,7 @@
</a-checkbox-group> </a-checkbox-group>
</a-form-item> </a-form-item>
<a-form-item label="标注类型" v-if="form.settings.includes('autoAnnotate')"> <a-form-item v-if="form.settings.includes('autoAnnotate')" label="标注类型">
<a-select <a-select
v-model="form.annotationTypes" v-model="form.annotationTypes"
:options="defectTypeOptions" :options="defectTypeOptions"
@ -89,7 +89,7 @@
<template #upload-button> <template #upload-button>
<div class="upload-area"> <div class="upload-area">
<div class="upload-drag-icon"> <div class="upload-drag-icon">
<icon-upload size="48" /> <IconUpload size="48" />
</div> </div>
<div class="upload-text"> <div class="upload-text">
<p>点击或拖拽图像文件到此区域</p> <p>点击或拖拽图像文件到此区域</p>
@ -101,12 +101,12 @@
</div> </div>
<!-- 文件列表 --> <!-- 文件列表 -->
<div class="file-list" v-if="fileList.length > 0"> <div v-if="fileList.length > 0" class="file-list">
<div class="list-header"> <div class="list-header">
<h4>待导入文件 ({{ fileList.length }})</h4> <h4>待导入文件 ({{ fileList.length }})</h4>
<a-button type="text" @click="clearFiles"> <a-button type="text" @click="clearFiles">
<template #icon> <template #icon>
<icon-delete /> <IconDelete />
</template> </template>
清空 清空
</a-button> </a-button>
@ -133,7 +133,7 @@
@click="removeFile(index)" @click="removeFile(index)"
> >
<template #icon> <template #icon>
<icon-close /> <IconClose />
</template> </template>
</a-button> </a-button>
</div> </div>
@ -142,7 +142,7 @@
</div> </div>
<!-- 导入进度 --> <!-- 导入进度 -->
<div class="import-progress" v-if="importing"> <div v-if="importing" class="import-progress">
<a-progress <a-progress
:percent="importProgress" :percent="importProgress"
:status="importStatus" :status="importStatus"
@ -152,7 +152,7 @@
</div> </div>
<!-- 导入结果 --> <!-- 导入结果 -->
<div class="import-result" v-if="importResult"> <div v-if="importResult" class="import-result">
<a-alert <a-alert
:type="importResult.failed.length > 0 ? 'warning' : 'success'" :type="importResult.failed.length > 0 ? 'warning' : 'success'"
:title="getResultTitle()" :title="getResultTitle()"
@ -160,7 +160,7 @@
show-icon show-icon
/> />
<div class="result-details" v-if="importResult.failed.length > 0"> <div v-if="importResult.failed.length > 0" class="result-details">
<h4>失败文件列表:</h4> <h4>失败文件列表:</h4>
<div class="failed-list"> <div class="failed-list">
<div <div
@ -180,22 +180,22 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch } from 'vue' import { computed, ref, watch } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import { import {
IconUpload, IconClose,
IconDelete, IconDelete,
IconClose IconUpload,
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
import { import {
getImageSources,
getProjectTree, getProjectTree,
importImages, importImages,
getImageSources
} from '@/apis/industrial-image' } from '@/apis/industrial-image'
import type { import type {
ProjectTreeNode, ImageImportParams,
IndustrialImage, IndustrialImage,
ImageImportParams ProjectTreeNode,
} from '@/apis/industrial-image/type' } from '@/apis/industrial-image/type'
interface Props { interface Props {
@ -233,30 +233,30 @@ const form = ref({
latitude: '', latitude: '',
longitude: '', longitude: '',
settings: [] as string[], settings: [] as string[],
annotationTypes: [] as string[] annotationTypes: [] as string[],
}) })
const fileList = ref<FileItem[]>([]) const fileList = ref<FileItem[]>([])
const projectTree = ref<ProjectTreeNode[]>([]) const projectTree = ref<ProjectTreeNode[]>([])
const defectTypes = ref<Array<{ id: string; name: string; description?: string; color?: string }>>([]) const defectTypes = ref<Array<{ id: string, name: string, description?: string, color?: string }>>([])
const imageSources = ref<Array<{ id: string; name: string; code: string }>>([]) const imageSources = ref<Array<{ id: string, name: string, code: string }>>([])
const loadingImageSources = ref(false) const loadingImageSources = ref(false)
// //
const visible = computed({ const visible = computed({
get: () => props.visible, get: () => props.visible,
set: (value) => emit('update:visible', value) set: (value) => emit('update:visible', value),
}) })
const componentOptions = computed(() => { const componentOptions = computed(() => {
const findComponents = (nodes: ProjectTreeNode[]): Array<{ label: string; value: string }> => { const findComponents = (nodes: ProjectTreeNode[]): Array<{ label: string, value: string }> => {
const options: Array<{ label: string; value: string }> = [] const options: Array<{ label: string, value: string }> = []
nodes.forEach(node => { nodes.forEach((node) => {
if (node.type === 'component' || node.type === 'blade' || node.type === 'tower') { if (node.type === 'component' || node.type === 'blade' || node.type === 'tower') {
options.push({ options.push({
label: node.name, label: node.name,
value: node.id value: node.id,
}) })
} }
if (node.children) { if (node.children) {
@ -271,16 +271,16 @@ const componentOptions = computed(() => {
}) })
const defectTypeOptions = computed(() => { const defectTypeOptions = computed(() => {
return defectTypes.value.map(type => ({ return defectTypes.value.map((type) => ({
label: type.name, label: type.name,
value: type.id value: type.id,
})) }))
}) })
const imageSourceOptions = computed(() => { const imageSourceOptions = computed(() => {
return imageSources.value.map(source => ({ return imageSources.value.map((source) => ({
label: source.name, label: source.name,
value: source.code value: source.code,
})) }))
}) })
@ -327,7 +327,7 @@ const onProjectChange = (value: string) => {
const handleFileChange = (fileList: any) => { const handleFileChange = (fileList: any) => {
const files = Array.from(fileList.target?.files || []) as File[] const files = Array.from(fileList.target?.files || []) as File[]
files.forEach(file => { files.forEach((file) => {
if (!file.type.startsWith('image/')) { if (!file.type.startsWith('image/')) {
Message.warning(`文件 ${file.name} 不是图像文件`) Message.warning(`文件 ${file.name} 不是图像文件`)
return return
@ -344,7 +344,7 @@ const handleFileChange = (fileList: any) => {
file, file,
name: file.name, name: file.name,
size: file.size, size: file.size,
preview: e.target?.result as string preview: e.target?.result as string,
} }
fileList.value.push(fileItem) fileList.value.push(fileItem)
} }
@ -365,7 +365,7 @@ const formatFileSize = (bytes: number): string => {
const k = 1024 const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB'] const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k)) const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`
} }
const handleImport = async () => { const handleImport = async () => {
@ -391,7 +391,7 @@ const handleImport = async () => {
importResult.value = null importResult.value = null
try { try {
const files = fileList.value.map(item => item.file) const files = fileList.value.map((item) => item.file)
const params: ImageImportParams = { const params: ImageImportParams = {
imageSource: form.value.imageSource, imageSource: form.value.imageSource,
projectId: form.value.projectId, projectId: form.value.projectId,
@ -401,7 +401,7 @@ const handleImport = async () => {
latitude: form.value.latitude || undefined, latitude: form.value.latitude || undefined,
longitude: form.value.longitude || undefined, longitude: form.value.longitude || undefined,
autoAnnotate: form.value.settings.includes('autoAnnotate'), 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,
} }
// //
@ -428,16 +428,15 @@ const handleImport = async () => {
type: file.type, type: file.type,
projectId: form.value.projectId, projectId: form.value.projectId,
componentId: form.value.componentId, componentId: form.value.componentId,
createTime: new Date().toISOString() createTime: new Date().toISOString(),
})), })),
failed: [] failed: [],
} }
importResult.value = mockResult importResult.value = mockResult
emit('importSuccess', mockResult) emit('importSuccess', mockResult)
Message.success(`成功导入 ${files.length} 个图像文件`) Message.success(`成功导入 ${files.length} 个图像文件`)
} catch (error) { } catch (error) {
console.error('导入失败:', error) console.error('导入失败:', error)
importProgress.value = 100 importProgress.value = 100
@ -469,7 +468,7 @@ const resetForm = () => {
latitude: '', latitude: '',
longitude: '', longitude: '',
settings: [], settings: [],
annotationTypes: [] annotationTypes: [],
} }
fileList.value = [] fileList.value = []
importResult.value = null importResult.value = null

View File

@ -42,8 +42,8 @@
:key="getPartId(part)" :key="getPartId(part)"
class="part-item" class="part-item"
:class="{ selected: String(selectedPartId) === String(getPartId(part)) }" :class="{ selected: String(selectedPartId) === String(getPartId(part)) }"
@click="selectPart(part)"
:title="`部件ID: ${getPartId(part)}, 选中: ${String(selectedPartId) === String(getPartId(part))}`" :title="`部件ID: ${getPartId(part)}, 选中: ${String(selectedPartId) === String(getPartId(part))}`"
@click="selectPart(part)"
> >
<div class="part-icon"> <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"> <svg v-if="part.partType === 'engine'" width="40" height="40" viewBox="0 0 70 70" xmlns="http://www.w3.org/2000/svg">
@ -67,7 +67,7 @@
</div> </div>
</div> </div>
<div class="part-info" v-if="selectedPart"> <div v-if="selectedPart" class="part-info">
<div class="info-line"> <div class="info-line">
<div class="info-label">部件:</div> <div class="info-label">部件:</div>
<div class="info-value">{{ getPartName(selectedPart) }}</div> <div class="info-value">{{ getPartName(selectedPart) }}</div>
@ -90,14 +90,14 @@
<span class="button-icon">+</span> <span class="button-icon">+</span>
添加图像 添加图像
</button> </button>
<button class="action-button" @click="handleRemoveImages" :disabled="!hasSelectedImages"> <button class="action-button" :disabled="!hasSelectedImages" @click="handleRemoveImages">
<span class="button-icon">-</span> <span class="button-icon">-</span>
移除图像 移除图像
</button> </button>
<!-- 隐藏的文件输入框 --> <!-- 隐藏的文件输入框 -->
<input <input
type="file"
ref="fileInput" ref="fileInput"
type="file"
accept="image/*" accept="image/*"
style="display: none;" style="display: none;"
multiple multiple
@ -110,7 +110,7 @@
<thead> <thead>
<tr> <tr>
<th class="checkbox-column"> <th class="checkbox-column">
<input type="checkbox" @change="toggleSelectAll" :checked="allImagesSelected"> <input type="checkbox" :checked="allImagesSelected" @change="toggleSelectAll">
</th> </th>
<th class="preview-column">预览</th> <th class="preview-column">预览</th>
<th>图像名称</th> <th>图像名称</th>
@ -123,7 +123,7 @@
<tbody> <tbody>
<tr v-for="(image, index) in importImages" :key="index" @click="toggleImageSelection(image)"> <tr v-for="(image, index) in importImages" :key="index" @click="toggleImageSelection(image)">
<td> <td>
<input type="checkbox" v-model="image.selected" @click.stop> <input v-model="image.selected" type="checkbox" @click.stop>
</td> </td>
<td class="preview-cell"> <td class="preview-cell">
<img v-if="image.previewUrl" :src="image.previewUrl" class="preview-thumbnail" alt="预览"> <img v-if="image.previewUrl" :src="image.previewUrl" class="preview-thumbnail" alt="预览">
@ -150,9 +150,9 @@
<div class="form-row"> <div class="form-row">
<div class="form-label">拍摄时间范围</div> <div class="form-label">拍摄时间范围</div>
<div class="form-input datetime-range"> <div class="form-input datetime-range">
<input type="text" v-model="imageInfo.startTime" placeholder="开始时间"> <input v-model="imageInfo.startTime" type="text" placeholder="开始时间">
<span class="range-separator"></span> <span class="range-separator"></span>
<input type="text" v-model="imageInfo.endTime" placeholder="结束时间"> <input v-model="imageInfo.endTime" type="text" placeholder="结束时间">
</div> </div>
</div> </div>
@ -173,13 +173,13 @@
<div class="form-input temperature-range"> <div class="form-input temperature-range">
<div class="range-input-group"> <div class="range-input-group">
<button class="range-btn" @click="imageInfo.minTemperature = Math.max(0, imageInfo.minTemperature - 1)">-</button> <button class="range-btn" @click="imageInfo.minTemperature = Math.max(0, imageInfo.minTemperature - 1)">-</button>
<input type="number" v-model="imageInfo.minTemperature" step="0.1" min="0" max="50"> <input v-model="imageInfo.minTemperature" type="number" step="0.1" min="0" max="50">
<button class="range-btn" @click="imageInfo.minTemperature = Math.min(50, imageInfo.minTemperature + 1)">+</button> <button class="range-btn" @click="imageInfo.minTemperature = Math.min(50, imageInfo.minTemperature + 1)">+</button>
</div> </div>
<span class="range-separator"></span> <span class="range-separator"></span>
<div class="range-input-group"> <div class="range-input-group">
<button class="range-btn" @click="imageInfo.maxTemperature = Math.max(0, imageInfo.maxTemperature - 1)">-</button> <button class="range-btn" @click="imageInfo.maxTemperature = Math.max(0, imageInfo.maxTemperature - 1)">-</button>
<input type="number" v-model="imageInfo.maxTemperature" step="0.1" min="0" max="50"> <input v-model="imageInfo.maxTemperature" type="number" step="0.1" min="0" max="50">
<button class="range-btn" @click="imageInfo.maxTemperature = Math.min(50, imageInfo.maxTemperature + 1)">+</button> <button class="range-btn" @click="imageInfo.maxTemperature = Math.min(50, imageInfo.maxTemperature + 1)">+</button>
</div> </div>
</div> </div>
@ -190,7 +190,7 @@
<div class="form-input"> <div class="form-input">
<div class="range-input-group"> <div class="range-input-group">
<button class="range-btn" @click="imageInfo.humidity = Math.max(0, imageInfo.humidity - 1)">-</button> <button class="range-btn" @click="imageInfo.humidity = Math.max(0, imageInfo.humidity - 1)">-</button>
<input type="number" v-model="imageInfo.humidity" min="0" max="100"> <input v-model="imageInfo.humidity" type="number" min="0" max="100">
<button class="range-btn" @click="imageInfo.humidity = Math.min(100, imageInfo.humidity + 1)">+</button> <button class="range-btn" @click="imageInfo.humidity = Math.min(100, imageInfo.humidity + 1)">+</button>
</div> </div>
</div> </div>
@ -217,11 +217,11 @@
<div class="form-label">拍摄方式</div> <div class="form-label">拍摄方式</div>
<div class="form-input capture-method"> <div class="form-input capture-method">
<label class="radio-option"> <label class="radio-option">
<input type="radio" v-model="imageInfo.captureMethod" value="无人机航拍"> <input v-model="imageInfo.captureMethod" type="radio" value="无人机航拍">
<span class="radio-label">无人机航拍</span> <span class="radio-label">无人机航拍</span>
</label> </label>
<label class="radio-option"> <label class="radio-option">
<input type="radio" v-model="imageInfo.captureMethod" value="人工拍摄"> <input v-model="imageInfo.captureMethod" type="radio" value="人工拍摄">
<span class="radio-label">人工拍摄</span> <span class="radio-label">人工拍摄</span>
</label> </label>
</div> </div>
@ -232,7 +232,7 @@
<div class="form-input"> <div class="form-input">
<div class="range-input-group"> <div class="range-input-group">
<button class="range-btn" @click="imageInfo.captureDistance = Math.max(0, imageInfo.captureDistance - 1)">-</button> <button class="range-btn" @click="imageInfo.captureDistance = Math.max(0, imageInfo.captureDistance - 1)">-</button>
<input type="number" v-model="imageInfo.captureDistance" min="0"> <input v-model="imageInfo.captureDistance" type="number" min="0">
<button class="range-btn" @click="imageInfo.captureDistance = imageInfo.captureDistance + 1">+</button> <button class="range-btn" @click="imageInfo.captureDistance = imageInfo.captureDistance + 1">+</button>
</div> </div>
</div> </div>
@ -241,14 +241,14 @@
<div class="form-row"> <div class="form-row">
<div class="form-label">采集员</div> <div class="form-label">采集员</div>
<div class="form-input"> <div class="form-input">
<input type="text" v-model="imageInfo.operator"> <input v-model="imageInfo.operator" type="text">
</div> </div>
</div> </div>
<div class="form-row"> <div class="form-row">
<div class="form-label">相机型号</div> <div class="form-label">相机型号</div>
<div class="form-input"> <div class="form-input">
<input type="text" v-model="imageInfo.cameraModel"> <input v-model="imageInfo.cameraModel" type="text">
</div> </div>
</div> </div>
</div> </div>
@ -262,18 +262,24 @@
v-if="currentStep > 1" v-if="currentStep > 1"
class="dialog-button" class="dialog-button"
@click="currentStep--" @click="currentStep--"
>上一步</button> >
上一步
</button>
<button <button
v-if="currentStep < 3" v-if="currentStep < 3"
class="dialog-button" class="dialog-button"
@click="nextStep"
:disabled="!canGoNext" :disabled="!canGoNext"
>下一步</button> @click="nextStep"
>
下一步
</button>
<button <button
v-if="currentStep === 3" v-if="currentStep === 3"
class="dialog-button primary" class="dialog-button primary"
@click="finishImport" @click="finishImport"
>完成导入</button> >
完成导入
</button>
<button class="dialog-button" @click="closeDialog">取消</button> <button class="dialog-button" @click="closeDialog">取消</button>
</div> </div>
</div> </div>
@ -281,7 +287,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, reactive, onBeforeUnmount } from 'vue' import { computed, onBeforeUnmount, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
// //
@ -352,7 +358,7 @@ const selectedPartId = ref('')
// //
const selectedPart = computed(() => { const selectedPart = computed(() => {
if (!selectedPartId.value) return null 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))
}) })
// //
@ -360,8 +366,8 @@ const importImages = ref<ImportImage[]>([])
// //
const imageInfo = reactive<ImageInfo>({ const imageInfo = reactive<ImageInfo>({
startTime: formatCurrentDate() + ' 00:00', startTime: `${formatCurrentDate()} 00:00`,
endTime: formatCurrentDate() + ' 23:59', endTime: `${formatCurrentDate()} 23:59`,
weather: '晴天', weather: '晴天',
humidity: 50, humidity: 50,
minTemperature: 15, minTemperature: 15,
@ -371,7 +377,7 @@ const imageInfo = reactive<ImageInfo>({
captureMethod: '无人机航拍', captureMethod: '无人机航拍',
captureDistance: 50, captureDistance: 50,
operator: '', operator: '',
cameraModel: 'ILCE-7RM4' cameraModel: 'ILCE-7RM4',
}) })
// //
@ -434,7 +440,7 @@ function handleAddImages() {
function handleFileSelected(event: Event) { function handleFileSelected(event: Event) {
const target = event.target as HTMLInputElement const target = event.target as HTMLInputElement
if (target.files && target.files.length > 0) { 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 // URL
const previewUrl = URL.createObjectURL(file) const previewUrl = URL.createObjectURL(file)
@ -448,7 +454,7 @@ function handleFileSelected(event: Event) {
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
pixelSize: '155.00', pixelSize: '155.00',
selected: false, selected: false,
previewUrl previewUrl,
} }
}) })
@ -463,13 +469,13 @@ function handleFileSelected(event: Event) {
// //
function handleRemoveImages() { function handleRemoveImages() {
// URL // URL
importImages.value.filter(image => image.selected).forEach(image => { importImages.value.filter((image) => image.selected).forEach((image) => {
if (image.previewUrl) { if (image.previewUrl) {
URL.revokeObjectURL(image.previewUrl) URL.revokeObjectURL(image.previewUrl)
} }
}) })
importImages.value = importImages.value.filter(image => !image.selected) importImages.value = importImages.value.filter((image) => !image.selected)
} }
// //
@ -480,17 +486,17 @@ function toggleImageSelection(image: ImportImage) {
// / // /
function toggleSelectAll(event: Event) { function toggleSelectAll(event: Event) {
const checked = (event.target as HTMLInputElement).checked const checked = (event.target as HTMLInputElement).checked
importImages.value.forEach(image => image.selected = checked) importImages.value.forEach((image) => image.selected = checked)
} }
// //
const hasSelectedImages = computed(() => { const hasSelectedImages = computed(() => {
return importImages.value.some(image => image.selected) return importImages.value.some((image) => image.selected)
}) })
// //
const allImagesSelected = computed(() => { 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)
}) })
// //
@ -532,7 +538,7 @@ function finishImport() {
} }
// //
const files = importImages.value.map(image => image.file!).filter(Boolean) const files = importImages.value.map((image) => image.file!).filter(Boolean)
// //
const partData = { const partData = {
@ -540,14 +546,14 @@ function finishImport() {
id: getPartId(selectedPart.value), // id: getPartId(selectedPart.value), //
name: getPartName(selectedPart.value), name: getPartName(selectedPart.value),
partName: getPartName(selectedPart.value), // partName: getPartName(selectedPart.value), //
partType: selectedPart.value.partType partType: selectedPart.value.partType,
} }
// //
emit('import-success', { emit('import-success', {
part: partData, part: partData,
images: files, images: files,
imageInfo: { ...imageInfo } imageInfo: { ...imageInfo },
}) })
Message.success('图像导入成功') Message.success('图像导入成功')
@ -569,7 +575,7 @@ function resetState() {
selectedPartId.value = '' selectedPartId.value = ''
// URL // URL
importImages.value.forEach(image => { importImages.value.forEach((image) => {
if (image.previewUrl) { if (image.previewUrl) {
URL.revokeObjectURL(image.previewUrl) URL.revokeObjectURL(image.previewUrl)
} }
@ -578,8 +584,8 @@ function resetState() {
// //
Object.assign(imageInfo, { Object.assign(imageInfo, {
startTime: formatCurrentDate() + ' 00:00', startTime: `${formatCurrentDate()} 00:00`,
endTime: formatCurrentDate() + ' 23:59', endTime: `${formatCurrentDate()} 23:59`,
weather: '晴天', weather: '晴天',
humidity: 70, humidity: 70,
minTemperature: 20, minTemperature: 20,
@ -587,14 +593,14 @@ function resetState() {
windPower: 0, windPower: 0,
captureMethod: '无人机拍摄', captureMethod: '无人机拍摄',
captureDistance: 15, captureDistance: 15,
operator: '' operator: '',
}) })
} }
// URL // URL
onBeforeUnmount(() => { onBeforeUnmount(() => {
// URL // URL
importImages.value.forEach(image => { importImages.value.forEach((image) => {
if (image.previewUrl) { if (image.previewUrl) {
URL.revokeObjectURL(image.previewUrl) URL.revokeObjectURL(image.previewUrl)
} }

View File

@ -1,13 +1,13 @@
<template> <template>
<div class="industrial-image-list" :class="{ 'collapsed': isCollapsed }"> <div class="industrial-image-list" :class="{ collapsed: isCollapsed }">
<div class="header-actions" v-if="!isCollapsed"> <div v-if="!isCollapsed" class="header-actions">
<slot name="header-left"> <slot name="header-left">
<a-button v-if="showImportButton" type="primary" @click="handleImportImages"> <a-button v-if="showImportButton" type="primary" @click="handleImportImages">
<template #icon><icon-upload /></template> <template #icon><IconUpload /></template>
导入图像 导入图像
</a-button> </a-button>
</slot> </slot>
<div class="search-bar" v-if="showSearch"> <div v-if="showSearch" class="search-bar">
<a-input-search <a-input-search
v-model="searchKeyword" v-model="searchKeyword"
placeholder="输入关键字搜索" placeholder="输入关键字搜索"
@ -23,16 +23,16 @@
@click="toggleCollapse" @click="toggleCollapse"
> >
<template #icon> <template #icon>
<icon-up /> <IconUp />
</template> </template>
收起 收起
</a-button> </a-button>
</div> </div>
</div> </div>
<div class="image-grid" v-show="!isCollapsed"> <div v-show="!isCollapsed" class="image-grid">
<div v-if="imageList.length === 0" class="empty-data"> <div v-if="imageList.length === 0" class="empty-data">
<icon-image class="empty-icon" /> <IconImage class="empty-icon" />
<p>{{ emptyText }}</p> <p>{{ emptyText }}</p>
</div> </div>
@ -51,8 +51,8 @@
@error="handleImageError" @error="handleImageError"
@load="handleImageLoad" @load="handleImageLoad"
/> />
<div class="image-placeholder" v-if="!image.imagePath"> <div v-if="!image.imagePath" class="image-placeholder">
<icon-image /> <IconImage />
<span>暂无图像</span> <span>暂无图像</span>
</div> </div>
<div class="thumbnail-overlay"> <div class="thumbnail-overlay">
@ -62,13 +62,13 @@
</div> </div>
<div class="image-actions"> <div class="image-actions">
<a-button v-if="showPreviewAction" type="text" size="small" @click.stop="handleImagePreview(image)"> <a-button v-if="showPreviewAction" type="text" size="small" @click.stop="handleImagePreview(image)">
<icon-eye /> <IconEye />
</a-button> </a-button>
<a-button v-if="showProcessAction" type="text" size="small" @click.stop="handleImageProcess(image)"> <a-button v-if="showProcessAction" type="text" size="small" @click.stop="handleImageProcess(image)">
<icon-settings /> <IconSettings />
</a-button> </a-button>
<a-button v-if="showDeleteAction" type="text" size="small" status="danger" @click.stop="handleImageDelete(image)"> <a-button v-if="showDeleteAction" type="text" size="small" status="danger" @click.stop="handleImageDelete(image)">
<icon-delete /> <IconDelete />
</a-button> </a-button>
<slot name="item-actions" :image="image"></slot> <slot name="item-actions" :image="image"></slot>
</div> </div>
@ -81,7 +81,7 @@
<span v-if="image.defectCount" class="defect-count">缺陷: {{ image.defectCount }}</span> <span v-if="image.defectCount" class="defect-count">缺陷: {{ image.defectCount }}</span>
<slot name="item-meta" :image="image"></slot> <slot name="item-meta" :image="image"></slot>
</div> </div>
<div class="thumbnail-extra" v-if="image.partName || image.shootingTime"> <div v-if="image.partName || image.shootingTime" class="thumbnail-extra">
<span v-if="image.partName" class="part-name">{{ image.partName }}</span> <span v-if="image.partName" class="part-name">{{ image.partName }}</span>
<span v-if="image.shootingTime" class="capture-time">{{ formatTime(image.shootingTime) }}</span> <span v-if="image.shootingTime" class="capture-time">{{ formatTime(image.shootingTime) }}</span>
</div> </div>
@ -98,7 +98,7 @@
@click="toggleCollapse" @click="toggleCollapse"
> >
<template #icon> <template #icon>
<icon-down /> <IconDown />
</template> </template>
展开图像列表 展开图像列表
</a-button> </a-button>
@ -109,13 +109,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { import {
IconUpload,
IconImage,
IconEye,
IconSettings,
IconDelete, IconDelete,
IconDown,
IconEye,
IconImage,
IconSettings,
IconUp, IconUp,
IconDown IconUpload,
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
export interface IndustrialImage { export interface IndustrialImage {
@ -133,40 +133,40 @@ export interface IndustrialImage {
const props = defineProps({ const props = defineProps({
imageList: { imageList: {
type: Array as () => IndustrialImage[], type: Array as () => IndustrialImage[],
default: () => [] default: () => [],
}, },
selectedImageId: { selectedImageId: {
type: String, type: String,
default: '' default: '',
}, },
baseUrl: { baseUrl: {
type: String, type: String,
default: 'http://localhost:8080' default: 'http://localhost:8080',
}, },
emptyText: { emptyText: {
type: String, type: String,
default: '暂无图像数据' default: '暂无图像数据',
}, },
showImportButton: { showImportButton: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
showSearch: { showSearch: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
showPreviewAction: { showPreviewAction: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
showProcessAction: { showProcessAction: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
showDeleteAction: { showDeleteAction: {
type: Boolean, type: Boolean,
default: true default: true,
} },
}) })
const emit = defineEmits<{ const emit = defineEmits<{
@ -252,7 +252,7 @@ const formatTime = (timeString: string): string => {
month: '2-digit', month: '2-digit',
day: '2-digit', day: '2-digit',
hour: '2-digit', hour: '2-digit',
minute: '2-digit' minute: '2-digit',
}) })
} catch { } catch {
return timeString return timeString

View File

@ -1,8 +1,10 @@
<template> <template>
<div class="turbine-grid-container"> <div class="turbine-grid-container">
<div class="turbine-grid"> <div class="turbine-grid">
<div v-for="turbine in turbines" :key="turbine.id" class="turbine-card" <div
:class="getStatusClass(turbine.status)"> v-for="turbine in turbines" :key="turbine.id" class="turbine-card"
:class="getStatusClass(turbine.status)"
>
<div class="turbine-status-badge" :class="`status-${turbine.status}`"> <div class="turbine-status-badge" :class="`status-${turbine.status}`">
{{ getStatusText(turbine.status) }} {{ getStatusText(turbine.status) }}
</div> </div>
@ -13,16 +15,18 @@
<div class="turbine-info"> <div class="turbine-info">
<div class="turbine-number"> <div class="turbine-number">
<a-input v-model="turbine.turbineNo" size="small" class="turbine-input" placeholder="请输入机组编号" <a-input
@change="handleTurbineNoChange(turbine)" /> v-model="turbine.turbineNo" size="small" class="turbine-input" placeholder="请输入机组编号"
@change="handleTurbineNoChange(turbine)"
/>
</div> </div>
</div> </div>
<div class="turbine-actions"> <div class="turbine-actions">
<a-button type="text" size="mini" @click="openMapModal(turbine)" title="地图选点"> <a-button type="text" size="mini" title="地图选点" @click="openMapModal(turbine)">
<template #icon><icon-location /></template> <template #icon><icon-location /></template>
</a-button> </a-button>
<a-button type="text" size="mini" @click="editTurbine(turbine)" title="编辑"> <a-button type="text" size="mini" title="编辑" @click="editTurbine(turbine)">
<template #icon><icon-edit /></template> <template #icon><icon-edit /></template>
</a-button> </a-button>
</div> </div>
@ -40,7 +44,6 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
interface Turbine { interface Turbine {
@ -63,7 +66,7 @@ interface Emits {
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
showAddButton: false showAddButton: false,
}) })
const emit = defineEmits<Emits>() const emit = defineEmits<Emits>()
@ -72,7 +75,7 @@ const getStatusText = (status: number) => {
const statusMap = { const statusMap = {
0: '待施工', 0: '待施工',
1: '施工中', 1: '施工中',
2: '已完成' 2: '已完成',
} }
return statusMap[status] || '未知状态' return statusMap[status] || '未知状态'
} }

View File

@ -3,7 +3,7 @@
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="24" :md="17"> <a-col :span="24" :md="17">
<GiTable <GiTable
v-model:selectedKeys="selectedKeys" v-model:selected-keys="selectedKeys"
row-key="id" row-key="id"
:data="dataList" :data="dataList"
:columns="listColumns" :columns="listColumns"

View File

@ -16,10 +16,10 @@ export function useDept(options?: { onSuccess?: () => void }) {
const processDeptData = (data: any[]): TreeNodeData[] => { const processDeptData = (data: any[]): TreeNodeData[] => {
if (!data || !data.length) return [] if (!data || !data.length) return []
return data.map(item => ({ return data.map((item) => ({
key: item.deptId, key: item.deptId,
title: item.deptName || '未命名部门', // 将deptName映射为title title: item.deptName || '未命名部门', // 将deptName映射为title
children: item.children ? processDeptData(item.children) : [] children: item.children ? processDeptData(item.children) : [],
})) }))
} }

View File

@ -2,7 +2,7 @@ import { listPost } from '@/apis/system/post'
import type { PostVO } from '@/apis/system/type' import type { PostVO } from '@/apis/system/type'
export function usePost() { export function usePost() {
const postList = ref<{ label: string; value: string }[]>([]) const postList = ref<{ label: string, value: string }[]>([])
const loading = ref(false) const loading = ref(false)
// 获取岗位列表 // 获取岗位列表
@ -24,6 +24,6 @@ export function usePost() {
return { return {
postList, postList,
loading, loading,
getPostList getPostList,
} }
} }

View File

@ -14,10 +14,10 @@ export function useRole(options?: { onSuccess?: () => void }) {
// 将新的角色数据格式转换为表单需要的 LabelValueState 格式 // 将新的角色数据格式转换为表单需要的 LabelValueState 格式
if (res && res.data) { if (res && res.data) {
roleList.value = (res.data || []).map(role => ({ roleList.value = (res.data || []).map((role) => ({
label: role.roleName, label: role.roleName,
value: role.roleId, value: role.roleId,
disabled: role.status !== '1' // 假设状态为1表示启用 disabled: role.status !== '1', // 假设状态为1表示启用
})) }))
} }

View File

@ -19,7 +19,6 @@ import Asider from './components/Asider/index.vue'
import Header from './components/Header/index.vue' import Header from './components/Header/index.vue'
import Main from './components/Main.vue' import Main from './components/Main.vue'
import Tabs from './components/Tabs/index.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 NoticePopup from '@/views/user/message/components/NoticePopup.vue'
import { useAppStore } from '@/stores' import { useAppStore } from '@/stores'
import { useDevice } from '@/hooks' import { useDevice } from '@/hooks'

View File

@ -19,7 +19,6 @@
<script setup lang="ts"> <script setup lang="ts">
import Menu from '../Menu/index.vue' import Menu from '../Menu/index.vue'
import Logo from '../Logo.vue' import Logo from '../Logo.vue'
import WwAds from '../WwAds.vue'
import { useAppStore } from '@/stores' import { useAppStore } from '@/stores'
import { useDevice } from '@/hooks' import { useDevice } from '@/hooks'

View File

@ -76,11 +76,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { Modal } from '@arco-design/web-vue' import { Modal } from '@arco-design/web-vue'
import { useFullscreen } from '@vueuse/core' import { useFullscreen } from '@vueuse/core'
import { onMounted, ref, nextTick } from 'vue' import { nextTick, onMounted, ref } from 'vue'
import Message from './Message.vue' import Message from './Message.vue'
import SettingDrawer from './SettingDrawer.vue' import SettingDrawer from './SettingDrawer.vue'
import Search from './Search.vue' import Search from './Search.vue'
import { getUnreadMessageCount } from '@/apis'
import { useUserStore } from '@/stores' import { useUserStore } from '@/stores'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import { useBreakpoint, useDevice } from '@/hooks' import { useBreakpoint, useDevice } from '@/hooks'

View File

@ -18,11 +18,10 @@ const props = withDefaults(defineProps<Props>(), {
}) })
const appStore = useAppStore() const appStore = useAppStore()
// const title = computed(() => appStore.getTitle()) // const title = computed(() => appStore.getTitle())
const title = "数智平台" const title = '数智平台'
const logo = "/logo.png" const logo = '/logo.png'
// computed(() => appStore.getLogo()) // computed(() => appStore.getLogo())
interface Props { interface Props {
collapsed?: boolean collapsed?: boolean
} }

View File

@ -233,64 +233,98 @@ export const systemRoutes: RouteRecordRaw[] = [
path: '/asset-management', path: '/asset-management',
name: 'AssetManagement', name: 'AssetManagement',
component: Layout, component: Layout,
redirect: '/asset-management/device/inventory', redirect: '/asset-management/device-management/device-center',
meta: { title: '资产管理', icon: 'property-safety', hidden: false, sort: 3 }, meta: { title: '资产管理', icon: 'property-safety', hidden: false, sort: 3 },
children: [ children: [
{ {
path: '/asset-management/intellectual-property1', path: '/asset-management/device-management',
name: 'IntellectualProperty1', name: 'DeviceManagement',
component: () => import('@/views/system-resource/information-system/software-management/index.vue'), component: () => import('@/components/ParentView/index.vue'),
meta: { title: '设备管理', icon: 'copyright', hidden: false }, redirect: '/asset-management/device-management/device-center',
meta: {
title: '设备管理',
icon: 'device',
hidden: false,
},
children: [ children: [
{ {
path: '/asset-management/intellectual-property1', path: '/asset-management/device-management/device-center',
name: 'IntellectualProperty11', name: 'DeviceCenter',
component: () => import('@/views/system-resource/information-system/software-management/index.vue'), component: () => import('@/views/system-resource/device-management/index.vue'),
meta: { title: '库存管理', hidden: false }, meta: {
title: '设备中心',
icon: 'appstore',
hidden: false,
},
}, },
{ {
path: '/asset-management/intellectual-property1', path: '/asset-management/device-management/device-detail/:id',
name: 'IntellectualProperty12', name: 'DeviceDetail',
component: () => import('@/views/system-resource/information-system/software-management/index.vue'), component: () => import('@/views/system-resource/device-management/detail.vue'),
meta: { title: '设备采购', hidden: false }, meta: {
title: '设备详情',
icon: 'info-circle',
hidden: true,
},
}, },
{ {
path: '/asset-management/intellectual-property1', path: '/asset-management/device-management/procurement',
name: 'IntellectualProperty13', name: 'DeviceProcurement',
component: () => import('@/views/system-resource/information-system/software-management/index.vue'), component: () => import('@/views/system-resource/device-management/index.vue'),
meta: { title: '在线管理', hidden: false }, meta: {
title: '设备采购',
icon: 'shopping-cart',
hidden: false,
},
},
{
path: '/asset-management/device-management/online',
name: 'DeviceOnline',
component: () => import('@/components/ParentView/index.vue'),
redirect: '/asset-management/device-management/online/drone',
meta: {
title: '在线管理',
icon: 'cloud',
hidden: false,
},
children: [ children: [
{ {
path: '/asset-management/intellectual-property11', path: '/asset-management/device-management/online/drone',
name: 'IntellectualProperty14', name: 'DeviceDrone',
component: () => import('@/views/system-resource/information-system/software-management/index.vue'), component: () => import('@/views/system-resource/device-management/index.vue'),
meta: { title: '无人机', hidden: false }, meta: {
title: '无人机',
icon: 'drone',
hidden: false,
},
}, },
{ {
path: '/asset-management/intellectual-property12', path: '/asset-management/device-management/online/nest',
name: 'IntellectualProperty15', name: 'DeviceNest',
component: () => import('@/views/system-resource/information-system/software-management/index.vue'), component: () => import('@/views/system-resource/device-management/index.vue'),
meta: { title: '机巢', hidden: false }, meta: {
title: '机巢',
icon: 'nest',
hidden: false,
},
}, },
{ {
path: '/asset-management/intellectual-property13', path: '/asset-management/device-management/online/smart-terminal',
name: 'IntellectualProperty16', name: 'DeviceSmartTerminal',
component: () => import('@/views/system-resource/information-system/software-management/index.vue'), component: () => import('@/views/system-resource/device-management/index.vue'),
meta: { title: '其他智能终端', hidden: false }, meta: {
title: '其他智能终端',
icon: 'terminal',
hidden: false,
}, },
{
path: '/asset-management/intellectual-property14',
name: 'IntellectualProperty17',
component: () => import('@/views/system-resource/information-system/software-management/index.vue'),
meta: { title: '车辆管理', hidden: false },
}, },
], ],
}, },
], ],
}, },
{ {
path: '/asset-management/intellectual-property', path: '/asset-management/other-assets',
name: 'IntellectualProperty', name: 'OtherAssets',
component: () => import('@/views/system-resource/information-system/software-management/index.vue'), component: () => import('@/views/system-resource/information-system/software-management/index.vue'),
meta: { title: '其他资产', icon: 'copyright', hidden: false }, meta: { title: '其他资产', icon: 'copyright', hidden: false },
}, },
@ -609,11 +643,11 @@ export const systemRoutes: RouteRecordRaw[] = [
{ {
path: '/project-management/projects/device', path: '/project-management/projects/device',
name: 'DeviceManagement', name: 'ProjectDeviceManagement',
component: () => import('@/views/system-resource/device-management/index.vue'), component: () => import('@/views/system-resource/device-management/index.vue'),
meta: { meta: {
title: '设备管理', title: '项目设备管理',
icon: 'none', icon: 'device',
hidden: false, hidden: false,
}, },
}, },
@ -1059,62 +1093,9 @@ export const systemRoutes: RouteRecordRaw[] = [
path: '/system-resource', path: '/system-resource',
name: 'SystemResource', name: 'SystemResource',
component: Layout, component: Layout,
redirect: '/system-resource/device-management/warehouse', redirect: '/system-resource/information-system/software-management',
meta: { title: '关于平台', icon: 'server', hidden: false, sort: 9 }, meta: { title: '系统资源', icon: 'server', hidden: false, sort: 9 },
children: [ children: [
{
path: '/system-resource/device-management/warehouse',
name: 'DeviceWarehouse',
component: () => import('@/views/system-resource/device-management/index.vue'),
meta: {
title: '库存管理',
icon: 'warehouse',
hidden: false,
},
},
{
path: '/system-resource/device-management/online',
name: 'DeviceOnline',
component: () => import('@/components/ParentView/index.vue'),
redirect: '/system-resource/device-management/online/drone',
meta: {
title: '在线管理',
icon: 'cloud',
hidden: false,
},
children: [
{
path: '/system-resource/device-management/online/drone',
name: 'DeviceDrone',
component: () => import('@/views/system-resource/device-management/index.vue'),
meta: {
title: '无人机',
icon: 'drone',
hidden: false,
},
},
{
path: '/system-resource/device-management/online/nest',
name: 'DeviceNest',
component: () => import('@/views/system-resource/device-management/index.vue'),
meta: {
title: '机巢',
icon: 'nest',
hidden: false,
},
},
{
path: '/system-resource/device-management/online/smart-terminal',
name: 'DeviceSmartTerminal',
component: () => import('@/views/system-resource/device-management/index.vue'),
meta: {
title: '其他智能终端',
icon: 'terminal',
hidden: false,
},
},
],
},
{ {
path: '/system-resource/information-system', path: '/system-resource/information-system',
name: 'InformationSystem', name: 'InformationSystem',

View File

@ -1,7 +1,7 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { computed, reactive, toRefs, watch, watchEffect } from 'vue' import { computed, reactive, toRefs, watchEffect } from 'vue'
import { generate, getRgbStr } from '@arco-design/color' import { generate, getRgbStr } from '@arco-design/color'
import { type BasicConfig, listSiteOptionDict } from '@/apis' import type { BasicConfig } from '@/apis'
import { getSettings } from '@/config/setting' import { getSettings } from '@/config/setting'
const storeSetup = () => { const storeSetup = () => {

View File

@ -4,10 +4,9 @@ import type { RouteRecordRaw } from 'vue-router'
import { mapTree, toTreeArray } from 'xe-utils' import { mapTree, toTreeArray } from 'xe-utils'
import { cloneDeep, omit } from 'lodash-es' import { cloneDeep, omit } from 'lodash-es'
import { constantRoutes, systemRoutes } from '@/router/route' import { constantRoutes, systemRoutes } from '@/router/route'
import { type RouteItem, getUserRouteWithAdapter } from '@/apis' import type { RouteItem } from '@/apis'
import { transformPathToName } from '@/utils' import { transformPathToName } from '@/utils'
import { asyncRouteModules } from '@/router/asyncModules' import { asyncRouteModules } from '@/router/asyncModules'
import { convertMenuData, type ApiMenuItem } from '@/utils/menuConverter'
const layoutComponentMap = { const layoutComponentMap = {
Layout: () => import('@/layout/index.vue'), Layout: () => import('@/layout/index.vue'),
@ -94,91 +93,91 @@ const storeSetup = () => {
// 获取路由数据并已通过适配器转换 // 获取路由数据并已通过适配器转换
// const { data } = await getUserRouteWithAdapter() // const { data } = await getUserRouteWithAdapter()
const data = [{ const data = [{
"id": 1000, id: 1000,
"parentId": 0, parentId: 0,
"title": "系统管理", title: '系统管理',
"type": 1, type: 1,
"path": "/system", path: '/system',
"name": "System", name: 'System',
"component": "Layout", component: 'Layout',
"redirect": "/system/user", redirect: '/system/user',
"icon": "settings", icon: 'settings',
"isExternal": false, isExternal: false,
"isCache": false, isCache: false,
"isHidden": false, isHidden: false,
"sort": 1, sort: 1,
"children": [ children: [
{ {
"id": 1010, id: 1010,
"parentId": 1000, parentId: 1000,
"title": "用户管理", title: '用户管理',
"type": 2, type: 2,
"path": "/system/user", path: '/system/user',
"name": "SystemUser", name: 'SystemUser',
"component": "system/user/index", component: 'system/user/index',
"icon": "user", icon: 'user',
"isExternal": false, isExternal: false,
"isCache": false, isCache: false,
"isHidden": false, isHidden: false,
"sort": 1 sort: 1,
}, },
{ {
"id": 1030, id: 1030,
"parentId": 1000, parentId: 1000,
"title": "角色管理", title: '角色管理',
"type": 2, type: 2,
"path": "/system/role", path: '/system/role',
"name": "SystemRole", name: 'SystemRole',
"component": "system/role/index", component: 'system/role/index',
"icon": "user-group", icon: 'user-group',
"isExternal": false, isExternal: false,
"isCache": false, isCache: false,
"isHidden": false, isHidden: false,
"sort": 2 sort: 2,
}, },
{ {
"id": 1050, id: 1050,
"parentId": 1000, parentId: 1000,
"title": "菜单管理", title: '菜单管理',
"type": 2, type: 2,
"path": "/system/menu", path: '/system/menu',
"name": "SystemMenu", name: 'SystemMenu',
"component": "system/menu/index", component: 'system/menu/index',
"icon": "menu", icon: 'menu',
"isExternal": false, isExternal: false,
"isCache": false, isCache: false,
"isHidden": false, isHidden: false,
"sort": 3 sort: 3,
}, },
{ {
"id": 1070, id: 1070,
"parentId": 1000, parentId: 1000,
"title": "部门管理", title: '部门管理',
"type": 2, type: 2,
"path": "/system/dept", path: '/system/dept',
"name": "SystemDept", name: 'SystemDept',
"component": "system/dept/index", component: 'system/dept/index',
"icon": "mind-mapping", icon: 'mind-mapping',
"isExternal": false, isExternal: false,
"isCache": false, isCache: false,
"isHidden": false, isHidden: false,
"sort": 4 sort: 4,
}, },
{ {
"id": 1090, id: 1090,
"parentId": 1000, parentId: 1000,
"title": "岗位管理", title: '岗位管理',
"type": 2, type: 2,
"path": "/system/post", path: '/system/post',
"name": "SystemPost", name: 'SystemPost',
"component": "system/post/index", component: 'system/post/index',
"icon": "settings", icon: 'settings',
"isExternal": false, isExternal: false,
"isCache": false, isCache: false,
"isHidden": false, isHidden: false,
"sort": 5 sort: 5,
} },
] ],
}] }]
// 使用已转换的数据生成路由 // 使用已转换的数据生成路由
const asyncRoutes = formatAsyncRoutes(data as unknown as RouteItem[]) const asyncRoutes = formatAsyncRoutes(data as unknown as RouteItem[])

View File

@ -5,10 +5,10 @@ import {
type AccountLoginReq, type AccountLoginReq,
AuthTypeConstants, AuthTypeConstants,
type PhoneLoginReq,
type UserDetail,
type DeptDetail, type DeptDetail,
type PhoneLoginReq,
type RoleDetail, type RoleDetail,
type UserDetail,
type UserInfo, type UserInfo,
accountLogin as accountLoginApi, accountLogin as accountLoginApi,
@ -21,10 +21,10 @@ import { clearToken, getToken, setToken } from '@/utils/auth'
import { resetHasRouteFlag } from '@/router/guard' import { resetHasRouteFlag } from '@/router/guard'
interface NewUserInfoData { interface NewUserInfoData {
user: UserDetail; user: UserDetail
dept: DeptDetail; dept: DeptDetail
roles: RoleDetail[]; roles: RoleDetail[]
posts: any[]; posts: any[]
} }
const storeSetup = () => { const storeSetup = () => {
@ -43,7 +43,7 @@ const storeSetup = () => {
deptName: '', deptName: '',
avatar: '', avatar: '',
roles: [] as string[], roles: [] as string[],
permissions: [] as string[] permissions: [] as string[],
}) })
const nickname = computed(() => userInfo.name) const nickname = computed(() => userInfo.name)
const username = computed(() => userInfo.account) const username = computed(() => userInfo.account)
@ -68,8 +68,6 @@ const storeSetup = () => {
token.value = res.data.tokenValue token.value = res.data.tokenValue
} }
// 手机号登录 // 手机号登录
const phoneLogin = async (req: PhoneLoginReq) => { const phoneLogin = async (req: PhoneLoginReq) => {
const res = await phoneLoginApi({ ...req, clientId: import.meta.env.VITE_CLIENT_ID, authType: AuthTypeConstants.PHONE }) const res = await phoneLoginApi({ ...req, clientId: import.meta.env.VITE_CLIENT_ID, authType: AuthTypeConstants.PHONE })
@ -77,8 +75,6 @@ const storeSetup = () => {
token.value = res.data.token token.value = res.data.token
} }
// 退出登录回调 // 退出登录回调
const logoutCallBack = async () => { const logoutCallBack = async () => {
roles.value = [] roles.value = []
@ -128,7 +124,7 @@ const storeSetup = () => {
// 处理角色信息 // 处理角色信息
if (userRoles && userRoles.length) { 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 roles.value = roleKeys
// 由于新API没有直接提供permissions这里默认给管理员全部权限 // 由于新API没有直接提供permissions这里默认给管理员全部权限

View File

@ -1,4 +1,4 @@
@import './var.scss'; @use './var.scss' as *;
body { body {
--margin: 14px; // 通用外边距 --margin: 14px; // 通用外边距

View File

@ -1,5 +1,5 @@
/* 全局样式 */ /* 全局样式 */
@import './var.scss'; @use './var.scss' as *;
.w-full { .w-full {
width: 100%; width: 100%;

View File

@ -1,17 +1,17 @@
// 基础样式 // 基础样式
@import './base.scss'; @use './base.scss';
// 全局类名样式 // 全局类名样式
@import './global.scss'; @use './global.scss';
// 自定义原生滚动条样式 // 自定义原生滚动条样式
@import './scrollbar-reset.scss'; @use './scrollbar-reset.scss';
// 自定义 nprogress 插件进度条颜色 // 自定义 nprogress 插件进度条颜色
@import './nprogress.scss'; @use './nprogress.scss';
// 富文本的css主题颜色变量 // 富文本的css主题颜色变量
@import './editor.scss'; @use './editor.scss';
// 动画类名 // 动画类名
@import './animated.scss'; @use './animated.scss';

10
src/types/api.d.ts vendored
View File

@ -1,10 +1,10 @@
/** API响应通用类型 */ /** API响应通用类型 */
interface ApiRes<T> { interface ApiRes<T> {
code: number | string; code: number | string
status?: number; status?: number
success: boolean; success: boolean
msg: string; msg: string
data: T; data: T
} }
/** 分页响应数据格式 */ /** 分页响应数据格式 */

127
src/types/equipment.d.ts vendored Normal file
View File

@ -0,0 +1,127 @@
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
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
}
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
projectId?: string
projectName?: string
userId?: string
name?: string
createTime?: string
updateTime?: string
}
export interface EquipmentTypeOption {
label: string
value: string
}
export interface EquipmentStatusOption {
label: string
value: string
color: string
}
export interface LocationStatusOption {
label: string
value: string
color: string
}
export interface HealthStatusOption {
label: string
value: string
color: string
}
export interface DepreciationMethodOption {
label: string
value: string
}

View File

@ -4,31 +4,31 @@
// API返回的菜单项类型 // API返回的菜单项类型
export interface ApiMenuItem { export interface ApiMenuItem {
menuId: string; menuId: string
parentId: string; parentId: string
menuName: string; menuName: string
menuType: string; // 'catalog' | 'route' menuType: string // 'catalog' | 'route'
orderNum: number; orderNum: number
visible: string; visible: string
children?: ApiMenuItem[]; children?: ApiMenuItem[]
[key: string]: any; // 其他可能的字段 [key: string]: any // 其他可能的字段
} }
// 前端需要的菜单项类型 // 前端需要的菜单项类型
export interface FrontendMenuItem { export interface FrontendMenuItem {
id: number | string; id: number | string
parentId: number | string; parentId: number | string
title: string; title: string
type: number; // 1表示目录2表示菜单 type: number // 1表示目录2表示菜单
path: string; path: string
name: string; name: string
component: string; component: string
icon: string; icon: string
isExternal: boolean; isExternal: boolean
isCache: boolean; isCache: boolean
isHidden: boolean; isHidden: boolean
sort: number; sort: number
children?: FrontendMenuItem[]; children?: FrontendMenuItem[]
} }
/** /**
@ -37,14 +37,14 @@ export interface FrontendMenuItem {
const convertMenuType = (menuType: string): number => { const convertMenuType = (menuType: string): number => {
switch (menuType.toLowerCase()) { switch (menuType.toLowerCase()) {
case 'catalog': case 'catalog':
return 1; return 1
case 'route': case 'route':
return 2; return 2
case 'button': case 'button':
return 3; return 3
default: default:
// 默认为菜单类型 // 默认为菜单类型
return 2; return 2
} }
} }
@ -52,7 +52,7 @@ const convertMenuType = (menuType: string): number => {
* : '0' -> false, '1' -> true * : '0' -> false, '1' -> true
*/ */
const convertVisible = (visible: string): boolean => { 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 => { const convertMenuItem = (apiItem: ApiMenuItem): FrontendMenuItem => {
// 根据menuType生成默认的path和component // 根据menuType生成默认的path和component
let path = ''; let path = ''
let component = ''; let component = ''
let name = ''; let name = ''
// 简单的名称生成,去掉空格,保持首字母大写,非首字母小写 // 简单的名称生成,去掉空格,保持首字母大写,非首字母小写
const generateName = (menuName: string): string => { const generateName = (menuName: string): string => {
return menuName.replace(/\s+/g, '') return menuName.replace(/\s+/g, '')
.replace(/^./, (match) => match.toUpperCase()) .replace(/^./, (match) => match.toUpperCase())
.replace(/[\u4e00-\u9fa5]/g, ''); // 移除中文字符 .replace(/[\u4E00-\u9FA5]/g, '') // 移除中文字符
}; }
if (apiItem.menuType.toLowerCase() === 'catalog') { if (apiItem.menuType.toLowerCase() === 'catalog') {
path = `/${apiItem.menuName.toLowerCase().replace(/\s+/g, '-')}`; path = `/${apiItem.menuName.toLowerCase().replace(/\s+/g, '-')}`
component = 'Layout'; component = 'Layout'
name = generateName(apiItem.menuName); name = generateName(apiItem.menuName)
} else { } else {
// 假设route类型菜单都在某个catalog下 // 假设route类型菜单都在某个catalog下
const parentName = apiItem.menuName.toLowerCase().replace(/\s+/g, '-'); const parentName = apiItem.menuName.toLowerCase().replace(/\s+/g, '-')
path = `/system/${parentName}`; path = `/system/${parentName}`
component = `system/${parentName}/index`; component = `system/${parentName}/index`
name = `System${generateName(apiItem.menuName)}`; name = `System${generateName(apiItem.menuName)}`
} }
return { return {
@ -88,21 +88,21 @@ const convertMenuItem = (apiItem: ApiMenuItem): FrontendMenuItem => {
parentId: apiItem.parentId, parentId: apiItem.parentId,
title: apiItem.menuName, title: apiItem.menuName,
type: convertMenuType(apiItem.menuType), type: convertMenuType(apiItem.menuType),
path: path, path,
name: name, name,
component: component, component,
icon: 'settings', // 默认图标 icon: 'settings', // 默认图标
isExternal: false, isExternal: false,
isCache: false, isCache: false,
isHidden: convertVisible(apiItem.visible), isHidden: convertVisible(apiItem.visible),
sort: apiItem.orderNum || 0, sort: apiItem.orderNum || 0,
children: apiItem.children ? apiItem.children.map(child => convertMenuItem(child)) : [] children: apiItem.children ? apiItem.children.map((child) => convertMenuItem(child)) : [],
}; }
} }
/** /**
* API返回的菜单数据为前端需要的格式 * API返回的菜单数据为前端需要的格式
*/ */
export const convertMenuData = (apiMenuData: ApiMenuItem[]): FrontendMenuItem[] => { export const convertMenuData = (apiMenuData: ApiMenuItem[]): FrontendMenuItem[] => {
return apiMenuData.map(item => convertMenuItem(item)); return apiMenuData.map((item) => convertMenuItem(item))
} }

View File

@ -1,7 +1,7 @@
<template> <template>
<GiPageLayout> <GiPageLayout>
<GiTable <GiTable
v-model:selectedKeys="selectedKeys" v-model:selected-keys="selectedKeys"
row-key="tableName" row-key="tableName"
:data="dataList" :data="dataList"
:columns="columns" :columns="columns"

View File

@ -44,11 +44,11 @@
:data="fileList" :data="fileList"
:columns="fileColumns" :columns="fileColumns"
:row-selection="{ type: 'checkbox' }" :row-selection="{ type: 'checkbox' }"
@select="handleFileSelect"
@select-all="handleFileSelectAll"
row-key="id" row-key="id"
:pagination="false" :pagination="false"
:scroll="{ y: 400 }" :scroll="{ y: 400 }"
@select="handleFileSelect"
@select-all="handleFileSelectAll"
> >
<template #fileName="{ record }"> <template #fileName="{ record }">
<div class="file-item"> <div class="file-item">
@ -74,8 +74,8 @@
<div class="step-actions"> <div class="step-actions">
<a-button <a-button
type="primary" type="primary"
@click="nextStep"
:disabled="selectedFiles.length === 0" :disabled="selectedFiles.length === 0"
@click="nextStep"
> >
下一步 下一步
</a-button> </a-button>
@ -156,7 +156,7 @@
</div> </div>
<div class="step-actions"> <div class="step-actions">
<a-button @click="prevStep" :disabled="isProcessing">上一步</a-button> <a-button :disabled="isProcessing" @click="prevStep">上一步</a-button>
<a-button <a-button
v-if="!isProcessing" v-if="!isProcessing"
type="primary" type="primary"
@ -167,8 +167,8 @@
<a-button <a-button
v-else v-else
type="primary" type="primary"
@click="nextStep"
:disabled="processingProgress < 100" :disabled="processingProgress < 100"
@click="nextStep"
> >
下一步 下一步
</a-button> </a-button>
@ -204,7 +204,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface' import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'
@ -219,7 +219,7 @@ const processingProgress = ref(0)
const projectList = ref([ const projectList = ref([
{ id: 1, name: 'A风场2023年检查' }, { id: 1, name: 'A风场2023年检查' },
{ id: 2, name: 'B风场维修项目' }, { id: 2, name: 'B风场维修项目' },
{ id: 3, name: 'C风场建设项目' } { id: 3, name: 'C风场建设项目' },
]) ])
const selectedProject = ref<number>() const selectedProject = ref<number>()
@ -232,15 +232,15 @@ const fileList = ref([
fileName: 'IMG_20231105_1430.jpg', fileName: 'IMG_20231105_1430.jpg',
fileType: 'image', fileType: 'image',
fileSize: '3.2MB', fileSize: '3.2MB',
uploadTime: '2023-11-05 14:32' uploadTime: '2023-11-05 14:32',
}, },
{ {
id: 2, id: 2,
fileName: 'VID_20231106_0915.mp4', fileName: 'VID_20231106_0915.mp4',
fileType: 'video', fileType: 'video',
fileSize: '45.6MB', fileSize: '45.6MB',
uploadTime: '2023-11-06 09:18' uploadTime: '2023-11-06 09:18',
} },
]) ])
// //
@ -250,29 +250,29 @@ const fileColumns: TableColumnData[] = [
dataIndex: 'fileName', dataIndex: 'fileName',
width: 300, width: 300,
ellipsis: true, ellipsis: true,
tooltip: true tooltip: true,
}, },
{ {
title: '类型', title: '类型',
dataIndex: 'fileType', dataIndex: 'fileType',
slotName: 'fileType', slotName: 'fileType',
width: 100, width: 100,
align: 'center' align: 'center',
}, },
{ {
title: '大小', title: '大小',
dataIndex: 'fileSize', dataIndex: 'fileSize',
slotName: 'fileSize', slotName: 'fileSize',
width: 100, width: 100,
align: 'center' align: 'center',
}, },
{ {
title: '上传时间', title: '上传时间',
dataIndex: 'uploadTime', dataIndex: 'uploadTime',
slotName: 'uploadTime', slotName: 'uploadTime',
width: 150, width: 150,
align: 'center' align: 'center',
} },
] ]
// //
@ -281,17 +281,17 @@ const settings = reactive({
denoise: false, denoise: false,
enhance: false, enhance: false,
crop: false, crop: false,
rotate: false rotate: false,
}, },
video: { video: {
keyFrameExtraction: false, keyFrameExtraction: false,
stabilization: false, stabilization: false,
split: false split: false,
}, },
output: { output: {
format: 'JPG', format: 'JPG',
directory: '/output/processed' directory: '/output/processed',
} },
}) })
// //
@ -317,7 +317,7 @@ const handleFileSelect = (selectedRowKeys: number[]) => {
const handleFileSelectAll = (checked: boolean) => { const handleFileSelectAll = (checked: boolean) => {
if (checked) { if (checked) {
selectedFiles.value = fileList.value.map(file => file.id) selectedFiles.value = fileList.value.map((file) => file.id)
} else { } else {
selectedFiles.value = [] selectedFiles.value = []
} }

View File

@ -37,11 +37,11 @@
<a-form-item> <a-form-item>
<a-space> <a-space>
<a-button type="primary" @click="handleSearch"> <a-button type="primary" @click="handleSearch">
<template #icon><icon-search /></template> <template #icon><IconSearch /></template>
搜索 搜索
</a-button> </a-button>
<a-button @click="resetSearch"> <a-button @click="resetSearch">
<template #icon><icon-refresh /></template> <template #icon><IconRefresh /></template>
重置 重置
</a-button> </a-button>
</a-space> </a-space>
@ -55,7 +55,7 @@
<template #extra> <template #extra>
<a-space> <a-space>
<a-button type="primary" size="small" :disabled="selectedRowKeys.length === 0"> <a-button type="primary" size="small" :disabled="selectedRowKeys.length === 0">
<template #icon><icon-download /></template> <template #icon><IconDownload /></template>
批量下载 批量下载
</a-button> </a-button>
<a-button <a-button
@ -65,7 +65,7 @@
:disabled="selectedRowKeys.length === 0" :disabled="selectedRowKeys.length === 0"
@click="handleBatchDelete" @click="handleBatchDelete"
> >
<template #icon><icon-delete /></template> <template #icon><IconDelete /></template>
批量删除 批量删除
</a-button> </a-button>
</a-space> </a-space>
@ -75,14 +75,14 @@
:loading="loading" :loading="loading"
:data="tableData" :data="tableData"
:pagination="pagination" :pagination="pagination"
@page-change="onPageChange"
row-key="id" row-key="id"
:row-selection="{ :row-selection="{
type: 'checkbox', type: 'checkbox',
showCheckedAll: true, showCheckedAll: true,
selectedRowKeys: selectedRowKeys, selectedRowKeys,
onChange: onSelectionChange onChange: onSelectionChange,
}" }"
@page-change="onPageChange"
> >
<template #columns> <template #columns>
<a-table-column title="文件名" data-index="fileName"> <a-table-column title="文件名" data-index="fileName">
@ -116,11 +116,11 @@
<template #cell="{ record }"> <template #cell="{ record }">
<a-space> <a-space>
<a-button size="small" @click="previewFile(record)"> <a-button size="small" @click="previewFile(record)">
<template #icon><icon-eye /></template> <template #icon><IconEye /></template>
预览 预览
</a-button> </a-button>
<a-button size="small" @click="downloadFile(record)"> <a-button size="small" @click="downloadFile(record)">
<template #icon><icon-download /></template> <template #icon><IconDownload /></template>
下载 下载
</a-button> </a-button>
<a-popconfirm <a-popconfirm
@ -128,7 +128,7 @@
@ok="deleteFile(record)" @ok="deleteFile(record)"
> >
<a-button size="small" status="danger"> <a-button size="small" status="danger">
<template #icon><icon-delete /></template> <template #icon><IconDelete /></template>
删除 删除
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
@ -160,18 +160,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message, Modal } from '@arco-design/web-vue' import { Message, Modal } from '@arco-design/web-vue'
import FilePreview from '@/components/FilePreview/index.vue'
import { import {
IconSearch,
IconRefresh,
IconDownload,
IconDelete, IconDelete,
IconDownload,
IconEye, IconEye,
IconFile IconFile,
IconRefresh,
IconSearch,
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
import { getAttachBusinessTypes, getAttachmentList, deleteAttachment } from '@/apis/attach-info' import FilePreview from '@/components/FilePreview/index.vue'
import { deleteAttachment, getAttachBusinessTypes, getAttachmentList } from '@/apis/attach-info'
import type { AttachInfoData, BusinessType } from '@/apis/attach-info/type' import type { AttachInfoData, BusinessType } from '@/apis/attach-info/type'
defineOptions({ name: 'AttachmentManagement' }) defineOptions({ name: 'AttachmentManagement' })
@ -293,7 +293,7 @@ const deleteFile = async (file: AttachInfoData) => {
if (res) { if (res) {
Message.success(`已删除: ${file.fileName}`) 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 pagination.total = tableData.value.length
} else { } else {
Message.error('删除文件失败') Message.error('删除文件失败')
@ -316,7 +316,7 @@ const handleBatchDelete = () => {
cancelText: '取消', cancelText: '取消',
onOk: async () => { onOk: async () => {
try { try {
const promises = selectedRowKeys.value.map(id => deleteAttachment(id)) const promises = selectedRowKeys.value.map((id) => deleteAttachment(id))
await Promise.all(promises) await Promise.all(promises)
Message.success('批量删除成功') Message.success('批量删除成功')
fetchAttachmentList() fetchAttachmentList()
@ -325,7 +325,7 @@ const handleBatchDelete = () => {
console.error('批量删除失败:', error) console.error('批量删除失败:', error)
Message.error('批量删除失败') Message.error('批量删除失败')
} }
} },
}) })
} }
@ -334,13 +334,13 @@ const getFileTypeText = (type: string) => {
image: '图片', image: '图片',
document: '文档', document: '文档',
video: '视频', video: '视频',
other: '其他' other: '其他',
} }
return typeMap[type] || '未知' return typeMap[type] || '未知'
} }
const getBusinessTypeName = (code: string) => { 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 return businessType ? businessType.name : code
} }

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="attachment-upload"> <div class="attachment-upload">
<a-space direction="vertical" :size="16" style="width: 100%"> <a-space direction="vertical" :size="16" style="width: 100%">
<a-form :model="formData" ref="formRef"> <a-form ref="formRef" :model="formData">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="12"> <a-col :span="12">
<a-form-item field="businessType" label="业务类型" required> <a-form-item field="businessType" label="业务类型" required>
@ -64,7 +64,7 @@
</a-form-item> </a-form-item>
<div class="form-actions"> <div class="form-actions">
<a-button type="primary" @click="handleSubmit" :loading="submitting">提交</a-button> <a-button type="primary" :loading="submitting" @click="handleSubmit">提交</a-button>
<a-button style="margin-left: 10px" @click="resetForm">重置</a-button> <a-button style="margin-left: 10px" @click="resetForm">重置</a-button>
</div> </div>
</a-form> </a-form>
@ -73,10 +73,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import { IconPlus } from '@arco-design/web-vue/es/icon' import { IconPlus } from '@arco-design/web-vue/es/icon'
import { getAttachBusinessTypes, batchAddAttachment } from '@/apis/attach-info' import { batchAddAttachment, getAttachBusinessTypes } from '@/apis/attach-info'
import type { BusinessType } from '@/apis/attach-info/type' import type { BusinessType } from '@/apis/attach-info/type'
defineOptions({ name: 'AttachmentUpload' }) defineOptions({ name: 'AttachmentUpload' })
@ -98,7 +98,7 @@ const formData = reactive({
fileType: '', fileType: '',
remark: '', remark: '',
userDefinedPath: '', userDefinedPath: '',
files: [] as FileItem[] files: [] as FileItem[],
}) })
// //
@ -126,7 +126,7 @@ const customRequest = (options: any) => {
file, file,
status: 'ready', status: 'ready',
uid: options.fileItem.uid, uid: options.fileItem.uid,
name: options.fileItem.name name: options.fileItem.name,
} }
formData.files.push(fileItem) formData.files.push(fileItem)
@ -172,7 +172,7 @@ const handleSubmit = async () => {
const params = { const params = {
fileType: formData.fileType, fileType: formData.fileType,
remark: formData.remark, remark: formData.remark,
userDefinedPath: formData.userDefinedPath userDefinedPath: formData.userDefinedPath,
} }
const res = await batchAddAttachment(formData.businessType, formDataToSend, params) const res = await batchAddAttachment(formData.businessType, formDataToSend, params)
@ -185,7 +185,7 @@ const handleSubmit = async () => {
} }
} catch (error: any) { } catch (error: any) {
console.error('上传失败:', error) console.error('上传失败:', error)
Message.error('上传失败: ' + (error.msg || '未知错误')) Message.error(`上传失败: ${error.msg || '未知错误'}`)
} finally { } finally {
submitting.value = false submitting.value = false
} }

View File

@ -3,7 +3,7 @@
<div class="panel-header"> <div class="panel-header">
<h3>自动识别设置</h3> <h3>自动识别设置</h3>
<a-button type="text" @click="$emit('close')"> <a-button type="text" @click="$emit('close')">
<template #icon><icon-close /></template> <template #icon><IconClose /></template>
</a-button> </a-button>
</div> </div>
@ -64,10 +64,10 @@
</div> </div>
<div class="panel-actions"> <div class="panel-actions">
<a-button type="primary" @click="handleStartRecognition" :loading="isRecognizing" block> <a-button type="primary" :loading="isRecognizing" block @click="handleStartRecognition">
开始识别 开始识别
</a-button> </a-button>
<a-button @click="handleResetSettings" block> <a-button block @click="handleResetSettings">
重置设置 重置设置
</a-button> </a-button>
</div> </div>
@ -76,14 +76,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue' import { onMounted, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import { IconClose } from '@arco-design/web-vue/es/icon' import { IconClose } from '@arco-design/web-vue/es/icon'
import { useRouter } from 'vue-router'
import { listDefectType } from '@/apis/common/common' import { listDefectType } from '@/apis/common/common'
import { getModelConfigList } from '@/apis/model-config' import { getModelConfigList } from '@/apis/model-config'
import type { DefectTypeResp, DefectTypeOption } from '@/apis/common/type' import type { DefectTypeOption, DefectTypeResp } from '@/apis/common/type'
import type { ModelConfigResponse } from '@/apis/model-config/type' import type { ModelConfigResponse } from '@/apis/model-config/type'
import { useRouter } from 'vue-router'
const props = defineProps<{ const props = defineProps<{
currentImage?: { currentImage?: {
@ -126,11 +126,11 @@ const loadModelList = async () => {
if (response && response.rows) { if (response && response.rows) {
// //
if (Array.isArray(response.rows)) { if (Array.isArray(response.rows)) {
modelList.value = response.rows; modelList.value = response.rows
} else { } else {
// //
const responseData = response.rows; const responseData = response.rows
modelList.value = []; modelList.value = []
// API // API
if (responseData && Array.isArray(responseData)) { if (responseData && Array.isArray(responseData)) {
@ -140,8 +140,8 @@ const loadModelList = async () => {
attachId: item.attachId || '', attachId: item.attachId || '',
confThreshold: item.confThreshold || 0.5, confThreshold: item.confThreshold || 0.5,
nmsThreshold: item.nmsThreshold || 0.5, nmsThreshold: item.nmsThreshold || 0.5,
modelPath: item.modelPath || '' modelPath: item.modelPath || '',
})); }))
} }
} }
@ -175,7 +175,7 @@ const loadDefectTypes = async () => {
defectTypeOptions.push({ defectTypeOptions.push({
value: code, value: code,
label: name, label: name,
code: code code,
}) })
}) })
}) })
@ -184,7 +184,7 @@ const loadDefectTypes = async () => {
// //
if (defectTypes.value.length > 0) { 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) { } catch (error) {
console.error('获取缺陷类型失败:', error) console.error('获取缺陷类型失败:', error)
@ -216,7 +216,7 @@ const handleStartRecognition = async () => {
const settings = { const settings = {
algorithm: selectedAlgorithm.value, algorithm: selectedAlgorithm.value,
confidence: confidence.value, confidence: confidence.value,
defectTypes: selectedDefectTypes.value defectTypes: selectedDefectTypes.value,
} }
try { try {
@ -235,7 +235,7 @@ const handleStartRecognition = async () => {
const handleResetSettings = () => { const handleResetSettings = () => {
selectedAlgorithm.value = modelList.value.length > 0 ? modelList.value[0].modelId : '' selectedAlgorithm.value = modelList.value.length > 0 ? modelList.value[0].modelId : ''
confidence.value = 80 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({ defineExpose({
setRecognizing setRecognizing,
}) })
</script> </script>

View File

@ -3,7 +3,7 @@
<div class="form-header"> <div class="form-header">
<h3>缺陷详情</h3> <h3>缺陷详情</h3>
<a-button type="text" @click="$emit('close')"> <a-button type="text" @click="$emit('close')">
<template #icon><icon-close /></template> <template #icon><IconClose /></template>
</a-button> </a-button>
</div> </div>
@ -104,32 +104,29 @@
/> />
</a-form-item> </a-form-item>
<div class="form-footer"> <div class="form-footer">
<a-button @click="$emit('close')" size="large">取消</a-button> <a-button size="large" @click="$emit('close')">取消</a-button>
<a-button <a-button
type="primary" type="primary"
size="large" size="large"
@click="handleSubmit"
:loading="submitting" :loading="submitting"
@click="handleSubmit"
> >
<template #icon><icon-save /></template> <template #icon><IconSave /></template>
保存缺陷 保存缺陷
</a-button> </a-button>
</div> </div>
</a-form> </a-form>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, watch, onMounted } from 'vue' import { onMounted, reactive, ref, watch } from 'vue'
import { IconClose, IconSave } from '@arco-design/web-vue/es/icon' import { IconClose, IconSave } from '@arco-design/web-vue/es/icon'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { Annotation } from '@/views/project-operation-platform/data-processing/industrial-image/components/ImageCanvas.vue' import { getDefectLevels } from '@/apis/industrial-image/defect'
import { getDefectLevels, getDefectTypes, type DefectLevelType, type DefectType } from '@/apis/industrial-image/defect'
import { listDefectType } from '@/apis/common/common' import { listDefectType } from '@/apis/common/common'
import type { DefectTypeResp, DefectTypeOption } from '@/apis/common/type' import type { DefectTypeOption } from '@/apis/common/type'
interface DefectFormData { interface DefectFormData {
defectName: string defectName: string
@ -175,7 +172,7 @@ const form = reactive<DefectFormData>({
description: '', description: '',
repairIdea: '建议进行进一步检查', repairIdea: '建议进行进一步检查',
axialDimension: 0, // null axialDimension: 0, // null
chordDimension: 0 // null chordDimension: 0, // null
}) })
// //
@ -197,7 +194,7 @@ const loadDefectTypes = async () => {
defectTypeOptions.push({ defectTypeOptions.push({
value: code, value: code,
label: name as string, label: name as string,
code: code code,
}) })
} }
}) })
@ -220,7 +217,6 @@ const loadDefectTypes = async () => {
} }
} }
// //
const loadDefectLevels = async () => { const loadDefectLevels = async () => {
try { try {
@ -233,13 +229,13 @@ const loadDefectLevels = async () => {
// //
if (response.data && response.data.code === 0 && Array.isArray(response.data.data)) { if (response.data && response.data.code === 0 && Array.isArray(response.data.data)) {
// API // API
response.data.data.forEach(item => { response.data.data.forEach((item) => {
defectLevelOptions.push({ defectLevelOptions.push({
code: item.code, code: item.code,
label: item.name, // 使namelabel label: item.name, // 使namelabel
value: item.value, value: item.value,
name: item.name, name: item.name,
sort: item.sort sort: item.sort,
}) })
}) })
defectLevels.value = defectLevelOptions defectLevels.value = defectLevelOptions
@ -250,10 +246,10 @@ const loadDefectLevels = async () => {
if (entries.length > 0) { if (entries.length > 0) {
const [code, name] = entries[0] const [code, name] = entries[0]
defectLevelOptions.push({ defectLevelOptions.push({
code: code, code,
label: name as string, label: name as string,
value: code, value: code,
name: name as string name: name as string,
}) })
} }
}) })
@ -264,7 +260,7 @@ const loadDefectLevels = async () => {
defectLevels.value = [ defectLevels.value = [
{ code: 'low', label: '轻微', value: 'low', name: '轻微' }, { code: 'low', label: '轻微', value: 'low', name: '轻微' },
{ code: 'medium', label: '中等', value: 'medium', name: '中等' }, { code: 'medium', label: '中等', value: 'medium', name: '中等' },
{ code: 'high', label: '严重', value: 'high', name: '严重' } { code: 'high', label: '严重', value: 'high', name: '严重' },
] ]
} }
@ -272,9 +268,9 @@ const loadDefectLevels = async () => {
// //
if (defectLevels.value.length > 0 && !form.defectLevel) { if (defectLevels.value.length > 0 && !form.defectLevel) {
const mediumLevel = defectLevels.value.find(l => const mediumLevel = defectLevels.value.find((l) =>
l.code.toLowerCase().includes('medium') || l.code.toLowerCase().includes('medium')
(l.name && l.name.includes('中')) || (l.name && l.name.includes('中')),
) )
form.defectLevel = mediumLevel?.code || defectLevels.value[0].code form.defectLevel = mediumLevel?.code || defectLevels.value[0].code
form.defectLevelLabel = mediumLevel?.name || mediumLevel?.label || defectLevels.value[0].label form.defectLevelLabel = mediumLevel?.name || mediumLevel?.label || defectLevels.value[0].label
@ -285,7 +281,7 @@ const loadDefectLevels = async () => {
defectLevels.value = [ defectLevels.value = [
{ code: 'low', label: '轻微', value: 'low', name: '轻微' }, { code: 'low', label: '轻微', value: 'low', name: '轻微' },
{ code: 'medium', label: '中等', value: 'medium', name: '中等' }, { code: 'medium', label: '中等', value: 'medium', name: '中等' },
{ code: 'high', label: '严重', value: 'high', name: '严重' } { code: 'high', label: '严重', value: 'high', name: '严重' },
] ]
} finally { } finally {
loadingDefectLevels.value = false loadingDefectLevels.value = false
@ -356,19 +352,18 @@ const handleSubmit = async () => {
if (isMultiAnnotation && annotationCount > 0) { if (isMultiAnnotation && annotationCount > 0) {
Message.loading({ Message.loading({
content: `正在保存包含${annotationCount}个标注区域的缺陷信息...`, content: `正在保存包含${annotationCount}个标注区域的缺陷信息...`,
duration: 0 duration: 0,
}) })
} else { } else {
Message.loading({ Message.loading({
content: '正在保存缺陷信息...', content: '正在保存缺陷信息...',
duration: 0 duration: 0,
}) })
} }
// //
console.log("form:",form); console.log('form:', form)
emit('submit', form, props.annotation) emit('submit', form, props.annotation)
} catch (error) { } catch (error) {
console.error('提交缺陷失败:', error) console.error('提交缺陷失败:', error)
Message.error('提交失败,请重试') Message.error('提交失败,请重试')
@ -379,7 +374,7 @@ const handleSubmit = async () => {
// //
const handleDefectTypeChange = (value: string) => { const handleDefectTypeChange = (value: string) => {
const selectedType = defectTypes.value.find(type => type.code === value) const selectedType = defectTypes.value.find((type) => type.code === value)
if (selectedType) { if (selectedType) {
form.defectTypeLabel = selectedType.name form.defectTypeLabel = selectedType.name
} }
@ -387,7 +382,7 @@ const handleDefectTypeChange = (value: string) => {
// //
const handleDefectLevelChange = (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) { if (selectedLevel) {
form.defectLevelLabel = selectedLevel.name form.defectLevelLabel = selectedLevel.name
} }

View File

@ -162,8 +162,8 @@
<div class="action-button-container"> <div class="action-button-container">
<a-button <a-button
size="small" size="small"
@click="handleSelectFromStandardDescription"
class="standard-library-btn" class="standard-library-btn"
@click="handleSelectFromStandardDescription"
> >
从标准描述库选择 从标准描述库选择
</a-button> </a-button>
@ -183,8 +183,8 @@
<div class="action-button-container"> <div class="action-button-container">
<a-button <a-button
size="small" size="small"
@click="handleSelectFromStandardInfo"
class="standard-library-btn" class="standard-library-btn"
@click="handleSelectFromStandardInfo"
> >
从标准信息库选择 从标准信息库选择
</a-button> </a-button>
@ -257,7 +257,7 @@
<!-- 无缺陷选中状态 --> <!-- 无缺陷选中状态 -->
<div v-else class="no-defect-selected"> <div v-else class="no-defect-selected">
<icon-file class="empty-icon" /> <IconFile class="empty-icon" />
<p>请从左侧选择缺陷进行编辑</p> <p>请从左侧选择缺陷进行编辑</p>
</div> </div>
@ -273,9 +273,9 @@
<a-list :data="standardDescriptions" :bordered="false"> <a-list :data="standardDescriptions" :bordered="false">
<template #item="{ item }"> <template #item="{ item }">
<a-list-item <a-list-item
:class="{ 'selected': selectedStandardDescription === item }" :class="{ selected: selectedStandardDescription === item }"
@click="selectedStandardDescription = item"
class="clickable-item" class="clickable-item"
@click="selectedStandardDescription = item"
> >
<div class="description-item"> <div class="description-item">
<div class="description-title">{{ item.title }}</div> <div class="description-title">{{ item.title }}</div>
@ -299,9 +299,9 @@
<a-list :data="standardRepairIdeas" :bordered="false"> <a-list :data="standardRepairIdeas" :bordered="false">
<template #item="{ item }"> <template #item="{ item }">
<a-list-item <a-list-item
:class="{ 'selected': selectedStandardInfo === item }" :class="{ selected: selectedStandardInfo === item }"
@click="selectedStandardInfo = item"
class="clickable-item" class="clickable-item"
@click="selectedStandardInfo = item"
> >
<div class="description-item"> <div class="description-item">
<div class="description-title">{{ item.title }}</div> <div class="description-title">{{ item.title }}</div>
@ -316,7 +316,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch, reactive } from 'vue' import { reactive, ref, watch } from 'vue'
import { IconFile } from '@arco-design/web-vue/es/icon' import { IconFile } from '@arco-design/web-vue/es/icon'
import { Message, Modal } from '@arco-design/web-vue' import { Message, Modal } from '@arco-design/web-vue'
import type { DefectDetectionResult } from '@/apis/industrial-image/defect' import type { DefectDetectionResult } from '@/apis/industrial-image/defect'
@ -348,7 +348,7 @@ const defectForm = reactive({
inspector: '', inspector: '',
recheckStatus: '', recheckStatus: '',
technicalNotes: '', technicalNotes: '',
repairRecord: '' repairRecord: '',
}) })
// //
@ -357,20 +357,20 @@ const selectedStandardDescription = ref<any>(null)
const standardDescriptions = ref([ const standardDescriptions = ref([
{ {
title: '前缘裂纹模板', title: '前缘裂纹模板',
content: '叶片前缘纵向裂纹长度约15mm宽度约2mm' content: '叶片前缘纵向裂纹长度约15mm宽度约2mm',
}, },
{ {
title: '表面磨损模板', title: '表面磨损模板',
content: '叶片表面出现明显磨损痕迹,影响空气动力学性能' content: '叶片表面出现明显磨损痕迹,影响空气动力学性能',
}, },
{ {
title: '截蚀损伤模板', title: '截蚀损伤模板',
content: '叶片前缘截蚀损伤表面粗糙度增加深度约1-3mm' content: '叶片前缘截蚀损伤表面粗糙度增加深度约1-3mm',
}, },
{ {
title: '腐蚀斑点模板', title: '腐蚀斑点模板',
content: '叶片表面出现腐蚀斑点直径约5-10mm深度轻微' content: '叶片表面出现腐蚀斑点直径约5-10mm深度轻微',
} },
]) ])
// //
@ -379,24 +379,24 @@ const selectedStandardInfo = ref<any>(null)
const standardRepairIdeas = ref([ const standardRepairIdeas = ref([
{ {
title: '裂纹修复建议', title: '裂纹修复建议',
content: '建议进行表面修复处理,防止裂纹扩散' content: '建议进行表面修复处理,防止裂纹扩散',
}, },
{ {
title: '磨损处理建议', title: '磨损处理建议',
content: '定期监测磨损程度,必要时进行表面打磨和重新涂层' content: '定期监测磨损程度,必要时进行表面打磨和重新涂层',
}, },
{ {
title: '截蚀修复建议', title: '截蚀修复建议',
content: '建议进行前缘修复,使用专用胶泥填补并重新整形' content: '建议进行前缘修复,使用专用胶泥填补并重新整形',
}, },
{ {
title: '腐蚀处理建议', title: '腐蚀处理建议',
content: '清理腐蚀区域,涂抹防腐涂层,定期检查' content: '清理腐蚀区域,涂抹防腐涂层,定期检查',
}, },
{ {
title: '严重损伤建议', title: '严重损伤建议',
content: '建议立即停机检修,更换受损部件,避免安全隐患' content: '建议立即停机检修,更换受损部件,避免安全隐患',
} },
]) ])
// //
@ -413,7 +413,7 @@ watch(() => props.selectedDefect, (newDefect) => {
chordwise: newDefect.chordwise || 0, chordwise: newDefect.chordwise || 0,
area: calculateArea(newDefect.axial || 0, newDefect.chordwise || 0), area: calculateArea(newDefect.axial || 0, newDefect.chordwise || 0),
description: newDefect.description || '', description: newDefect.description || '',
repairIdea: newDefect.repairIdea || '' repairIdea: newDefect.repairIdea || '',
}) })
} }
}, { immediate: true }) }, { immediate: true })
@ -437,7 +437,7 @@ const handleSave = () => {
const updatedDefect = { const updatedDefect = {
...props.selectedDefect, ...props.selectedDefect,
...defectForm ...defectForm,
} }
emit('edit-defect', updatedDefect) emit('edit-defect', updatedDefect)
@ -457,7 +457,7 @@ const handleDelete = () => {
onOk: () => { onOk: () => {
emit('delete-defect', props.selectedDefect!.defectId) emit('delete-defect', props.selectedDefect!.defectId)
Message.success('缺陷已删除') Message.success('缺陷已删除')
} },
}) })
} }
@ -476,7 +476,7 @@ const handleCancel = () => {
chordwise: props.selectedDefect.chordwise || 0, chordwise: props.selectedDefect.chordwise || 0,
area: calculateArea(props.selectedDefect.axial || 0, props.selectedDefect.chordwise || 0), area: calculateArea(props.selectedDefect.axial || 0, props.selectedDefect.chordwise || 0),
description: props.selectedDefect.description || '', description: props.selectedDefect.description || '',
repairIdea: props.selectedDefect.repairIdea || '' repairIdea: props.selectedDefect.repairIdea || '',
}) })
} }
} }

View File

@ -6,14 +6,14 @@
<a-button <a-button
type="primary" type="primary"
size="small" size="small"
@click="handleAddDefect"
:disabled="!canAddDefect" :disabled="!canAddDefect"
@click="handleAddDefect"
> >
<template #icon><icon-plus /></template> <template #icon><IconPlus /></template>
新增缺陷 新增缺陷
</a-button> </a-button>
<a-button type="text" @click="$emit('close')"> <a-button type="text" @click="$emit('close')">
<template #icon><icon-close /></template> <template #icon><IconClose /></template>
</a-button> </a-button>
</div> </div>
</div> </div>
@ -34,11 +34,11 @@
<template #title="node"> <template #title="node">
<div class="tree-node"> <div class="tree-node">
<span class="node-icon"> <span class="node-icon">
<icon-folder v-if="node.type === 'project'" /> <IconFolder v-if="node.type === 'project'" />
<icon-settings v-else-if="node.type === 'turbine'" /> <IconSettings v-else-if="node.type === 'turbine'" />
<icon-tool v-else-if="node.type === 'part'" /> <IconTool v-else-if="node.type === 'part'" />
<icon-bug v-else-if="node.type === 'defect'" /> <IconBug v-else-if="node.type === 'defect'" />
<icon-apps v-else /> <IconApps v-else />
</span> </span>
<span class="node-title">{{ node.name }}</span> <span class="node-title">{{ node.name }}</span>
<span v-if="node.imageCount" class="node-count">({{ node.imageCount }})</span> <span v-if="node.imageCount" class="node-count">({{ node.imageCount }})</span>
@ -53,9 +53,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { IconClose, IconBug, IconFolder, IconPlus, IconSettings, IconTool, IconApps } from '@arco-design/web-vue/es/icon' import { IconApps, IconBug, IconClose, IconFolder, IconPlus, IconSettings, IconTool } from '@arco-design/web-vue/es/icon'
import type { PropType } from 'vue' import type { PropType } from 'vue'
import { ref, computed, watch } from 'vue' import { computed } from 'vue'
// //
export interface TreeNode { export interface TreeNode {
@ -91,28 +91,28 @@ const props = defineProps({
// //
defectList: { defectList: {
type: Array as PropType<DefectInfo[]>, type: Array as PropType<DefectInfo[]>,
default: () => [] default: () => [],
}, },
// //
selectedDefect: { selectedDefect: {
type: Object as PropType<DefectInfo | null>, type: Object as PropType<DefectInfo | null>,
default: null default: null,
}, },
// //
treeData: { treeData: {
type: Array as PropType<TreeNode[]>, type: Array as PropType<TreeNode[]>,
default: () => [] default: () => [],
}, },
// //
selectedKeys: { selectedKeys: {
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
default: () => [] default: () => [],
}, },
// //
loading: { loading: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}) })
// //
@ -122,7 +122,7 @@ const emit = defineEmits([
'load-more', 'load-more',
'add-defect', 'add-defect',
'turbine-select', 'turbine-select',
'close' 'close',
]) ])
// //
@ -131,7 +131,7 @@ const enhancedTreeData = computed(() => {
return [] return []
} }
return props.treeData.map(project => enhanceTreeNode(project)) return props.treeData.map((project) => enhanceTreeNode(project))
}) })
// //
@ -142,11 +142,11 @@ const enhanceTreeNode = (node: TreeNode): TreeNode => {
// //
const defectNodes = getDefectNodesForTurbine(node.id) const defectNodes = getDefectNodesForTurbine(node.id)
enhancedNode.children = [ enhancedNode.children = [
...(node.children || []).map(child => enhanceTreeNode(child)), ...(node.children || []).map((child) => enhanceTreeNode(child)),
...defectNodes ...defectNodes,
] ]
} else if (node.children) { } else if (node.children) {
enhancedNode.children = node.children.map(child => enhanceTreeNode(child)) enhancedNode.children = node.children.map((child) => enhanceTreeNode(child))
} }
return enhancedNode return enhancedNode
@ -155,19 +155,19 @@ const enhanceTreeNode = (node: TreeNode): TreeNode => {
// //
const getDefectNodesForTurbine = (turbineId: string): TreeNode[] => { const getDefectNodesForTurbine = (turbineId: string): TreeNode[] => {
// ID // ID
const turbineDefects = props.defectList.filter(defect => const turbineDefects = props.defectList.filter((defect) =>
defect.turbineId === turbineId || defect.imageId === turbineId defect.turbineId === turbineId || defect.imageId === turbineId,
) )
// //
return turbineDefects.map(defect => ({ return turbineDefects.map((defect) => ({
id: defect.id, id: defect.id,
name: defect.defectName || '未命名缺陷', name: defect.defectName || '未命名缺陷',
type: 'defect', type: 'defect',
defectLevel: defect.defectLevel, defectLevel: defect.defectLevel,
defectType: defect.defectType, defectType: defect.defectType,
detectionDate: defect.detectionDate, detectionDate: defect.detectionDate,
defectData: defect // defectData: defect, //
})) }))
} }
@ -182,7 +182,7 @@ const handleNodeSelect = (selectedKeys: string[], e: any) => {
// //
if (selectedNode?.type === 'defect') { 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) { if (defect) {
emit('defect-select', defect) emit('defect-select', defect)
} }

View File

@ -1,16 +1,16 @@
<template> <template>
<div class="header-toolbar"> <div class="header-toolbar">
<div class="toolbar-buttons"> <div class="toolbar-buttons">
<a-button size="large" @click="handleAutoAnnotate" :disabled="!currentImageId"> <a-button size="large" :disabled="!currentImageId" @click="handleAutoAnnotate">
<template #icon><icon-robot /></template> <template #icon><IconRobot /></template>
自动标注 自动标注
</a-button> </a-button>
<a-button size="large" @click="handleManualAnnotate" :disabled="!currentImageId"> <a-button size="large" :disabled="!currentImageId" @click="handleManualAnnotate">
<template #icon><icon-edit /></template> <template #icon><IconEdit /></template>
手动标注 手动标注
</a-button> </a-button>
<a-button size="large" @click="handleGenerateReport"> <a-button size="large" @click="handleGenerateReport">
<template #icon><icon-file-image /></template> <template #icon><IconFileImage /></template>
生成检测报告 生成检测报告
</a-button> </a-button>
</div> </div>
@ -19,10 +19,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { import {
IconPlayArrow,
IconRobot,
IconEdit, IconEdit,
IconFileImage IconFileImage,
IconRobot,
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
defineProps<{ defineProps<{
@ -40,8 +39,6 @@ const handleStart = () => {
emit('start') emit('start')
} }
const handleAutoAnnotate = () => { const handleAutoAnnotate = () => {
emit('autoAnnotate') emit('autoAnnotate')
} }

View File

@ -5,8 +5,8 @@
width="80%" width="80%"
:footer="footerButtons" :footer="footerButtons"
:mask-closable="true" :mask-closable="true"
@update:visible="emit('update:previewModalVisible', $event)"
:confirm-loading="loading" :confirm-loading="loading"
@update:visible="emit('update:previewModalVisible', $event)"
> >
<div v-if="previewImage" class="modal-image-viewer"> <div v-if="previewImage" class="modal-image-viewer">
<img :src="getImageUrl(previewImage.imagePath)" :alt="editingData.imageName" class="preview-image" /> <img :src="getImageUrl(previewImage.imagePath)" :alt="editingData.imageName" class="preview-image" />
@ -77,7 +77,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch, computed } from 'vue' import { ref, watch } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import axios from 'axios' import axios from 'axios'
@ -127,7 +127,7 @@ const editingData = ref<{
imageType: '', imageType: '',
imageTypeLabel: '', imageTypeLabel: '',
partId: '', partId: '',
imageId:'' imageId: '',
}) })
// //
@ -177,15 +177,15 @@ const handleSave = async () => {
imageId: editingData.value.imageId, imageId: editingData.value.imageId,
imageName: editingData.value.imageName, imageName: editingData.value.imageName,
imageResolution: editingData.value.imageResolution, imageResolution: editingData.value.imageResolution,
} },
], ],
} }
console.log("requestData:",requestData); console.log('requestData:', requestData)
// //
const response = await axios.put( const response = await axios.put(
`http://localhost:8080/image/setting-info/${editingData.value.partId}`, `http://localhost:8080/image/setting-info/${editingData.value.partId}`,
requestData requestData,
) )
if (response.data && response.data.code === 0) { if (response.data && response.data.code === 0) {

View File

@ -4,20 +4,20 @@
<!-- 缩放控制工具栏 --> <!-- 缩放控制工具栏 -->
<div v-if="selectedImage" class="zoom-controls"> <div v-if="selectedImage" class="zoom-controls">
<a-space> <a-space>
<a-button size="small" @click="zoomOut" :disabled="scale <= MIN_SCALE"> <a-button size="small" :disabled="scale <= MIN_SCALE" @click="zoomOut">
<template #icon> <template #icon>
<icon-minus /> <IconMinus />
</template> </template>
</a-button> </a-button>
<span class="zoom-text">{{ Math.round(scale * 100) }}%</span> <span class="zoom-text">{{ Math.round(scale * 100) }}%</span>
<a-button size="small" @click="zoomIn" :disabled="scale >= MAX_SCALE"> <a-button size="small" :disabled="scale >= MAX_SCALE" @click="zoomIn">
<template #icon> <template #icon>
<icon-plus /> <IconPlus />
</template> </template>
</a-button> </a-button>
<a-button size="small" @click="resetZoom"> <a-button size="small" @click="resetZoom">
<template #icon> <template #icon>
<icon-refresh /> <IconRefresh />
</template> </template>
重置 重置
</a-button> </a-button>
@ -32,7 +32,7 @@
<div <div
v-if="selectedImage" v-if="selectedImage"
class="image-viewer" class="image-viewer"
:class="{ 'dragging': isDragging }" :class="{ dragging: isDragging }"
@wheel="handleWheel" @wheel="handleWheel"
@mousedown="handleMouseDown" @mousedown="handleMouseDown"
@mousemove="handleMouseMove" @mousemove="handleMouseMove"
@ -43,7 +43,7 @@
class="image-container" class="image-container"
:style="{ :style="{
transform: `scale(${scale}) translate(${translateX}px, ${translateY}px)`, transform: `scale(${scale}) translate(${translateX}px, ${translateY}px)`,
transition: isDragging ? 'none' : 'transform 0.2s ease-out' transition: isDragging ? 'none' : 'transform 0.2s ease-out',
}" }"
> >
<img <img
@ -55,7 +55,7 @@
</div> </div>
</div> </div>
<div v-else class="empty-preview"> <div v-else class="empty-preview">
<icon-image class="empty-icon" /> <IconImage class="empty-icon" />
<p>请从下方缩略图中选择图像</p> <p>请从下方缩略图中选择图像</p>
</div> </div>
</div> </div>
@ -64,7 +64,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch, nextTick } from 'vue' import { nextTick, ref, watch } from 'vue'
import { IconImage, IconMinus, IconPlus, IconRefresh } from '@arco-design/web-vue/es/icon' import { IconImage, IconMinus, IconPlus, IconRefresh } from '@arco-design/web-vue/es/icon'
const props = defineProps<{ const props = defineProps<{
@ -153,7 +153,7 @@ const getBoundaryLimits = () => {
maxX, maxX,
maxY, maxY,
minX: -maxX, minX: -maxX,
minY: -maxY minY: -maxY,
} }
} }

View File

@ -15,10 +15,10 @@
<template #title="node"> <template #title="node">
<div class="tree-node"> <div class="tree-node">
<span class="node-icon"> <span class="node-icon">
<icon-folder v-if="node.type === 'project'" /> <IconFolder v-if="node.type === 'project'" />
<icon-settings v-else-if="node.type === 'turbine'" /> <IconSettings v-else-if="node.type === 'turbine'" />
<icon-tool v-else-if="node.type === 'part'" /> <IconTool v-else-if="node.type === 'part'" />
<icon-apps v-else /> <IconApps v-else />
</span> </span>
<span class="node-title">{{ node.name }}</span> <span class="node-title">{{ node.name }}</span>
<span v-if="node.imageCount" class="node-count">({{ node.imageCount }})</span> <span v-if="node.imageCount" class="node-count">({{ node.imageCount }})</span>
@ -31,10 +31,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { import {
IconApps,
IconFolder, IconFolder,
IconSettings, IconSettings,
IconTool, IconTool,
IconApps
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
import type { ProjectTreeNode } from '@/apis/industrial-image' import type { ProjectTreeNode } from '@/apis/industrial-image'

View File

@ -4,11 +4,11 @@
<h3>识别结果</h3> <h3>识别结果</h3>
<div class="header-actions"> <div class="header-actions">
<a-button type="text" @click="handleSaveResults"> <a-button type="text" @click="handleSaveResults">
<template #icon><icon-save /></template> <template #icon><IconSave /></template>
保存结果 保存结果
</a-button> </a-button>
<a-button type="text" @click="handleExportResults"> <a-button type="text" @click="handleExportResults">
<template #icon><icon-export /></template> <template #icon><IconExport /></template>
导出 导出
</a-button> </a-button>
</div> </div>
@ -73,10 +73,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { computed, ref } from 'vue'
import { import {
IconExport,
IconSave, IconSave,
IconExport
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
@ -105,7 +105,7 @@ const statistics = computed(() => {
wear: 0, wear: 0,
wearConfidence: 0, wearConfidence: 0,
deformation: 0, deformation: 0,
deformationConfidence: 0 deformationConfidence: 0,
} }
if (props.results.length === 0) return stats if (props.results.length === 0) return stats
@ -137,28 +137,28 @@ const statistics = computed(() => {
}, {} as Record<string, DefectDetectionResult[]>) }, {} as Record<string, DefectDetectionResult[]>)
// //
const crackResults = categorizedResults['crack'] || [] const crackResults = categorizedResults.crack || []
stats.crack = crackResults.length stats.crack = crackResults.length
stats.crackConfidence = crackResults.length > 0 stats.crackConfidence = crackResults.length > 0
? Math.round(crackResults.reduce((sum, r) => sum + ((r.markInfo?.confidence || 0) * 100), 0) / crackResults.length) ? Math.round(crackResults.reduce((sum, r) => sum + ((r.markInfo?.confidence || 0) * 100), 0) / crackResults.length)
: 0 : 0
// //
const corrosionResults = categorizedResults['corrosion'] || [] const corrosionResults = categorizedResults.corrosion || []
stats.corrosion = corrosionResults.length stats.corrosion = corrosionResults.length
stats.corrosionConfidence = corrosionResults.length > 0 stats.corrosionConfidence = corrosionResults.length > 0
? Math.round(corrosionResults.reduce((sum, r) => sum + ((r.markInfo?.confidence || 0) * 100), 0) / corrosionResults.length) ? Math.round(corrosionResults.reduce((sum, r) => sum + ((r.markInfo?.confidence || 0) * 100), 0) / corrosionResults.length)
: 0 : 0
// //
const wearResults = categorizedResults['wear'] || [] const wearResults = categorizedResults.wear || []
stats.wear = wearResults.length stats.wear = wearResults.length
stats.wearConfidence = wearResults.length > 0 stats.wearConfidence = wearResults.length > 0
? Math.round(wearResults.reduce((sum, r) => sum + ((r.markInfo?.confidence || 0) * 100), 0) / wearResults.length) ? Math.round(wearResults.reduce((sum, r) => sum + ((r.markInfo?.confidence || 0) * 100), 0) / wearResults.length)
: 0 : 0
// //
const deformationResults = categorizedResults['deformation'] || [] const deformationResults = categorizedResults.deformation || []
stats.deformation = deformationResults.length stats.deformation = deformationResults.length
stats.deformationConfidence = deformationResults.length > 0 stats.deformationConfidence = deformationResults.length > 0
? Math.round(deformationResults.reduce((sum, r) => sum + ((r.markInfo?.confidence || 0) * 100), 0) / deformationResults.length) ? Math.round(deformationResults.reduce((sum, r) => sum + ((r.markInfo?.confidence || 0) * 100), 0) / deformationResults.length)
@ -227,7 +227,7 @@ const getRecommendation = (type: string, confidence: number): string => {
deformation: '检查结构完整性', deformation: '检查结构完整性',
scratch: '轻微处理即可', scratch: '轻微处理即可',
hole: '立即修补', hole: '立即修补',
dirt: '清洁处理' dirt: '清洁处理',
} }
return recommendations[type] || '建议进一步检查' return recommendations[type] || '建议进一步检查'
} }

View File

@ -1,35 +1,18 @@
import { ref, reactive, computed } from 'vue' import { computed, ref } from 'vue'
import { Message, Modal } from '@arco-design/web-vue' import { Message, Modal } from '@arco-design/web-vue'
import { import {
type PartInfo,
type ProjectInfo,
type ProjectTreeNode,
type TurbineInfo,
deleteImage,
getImageList,
getPartList,
getProjectList, getProjectList,
getTurbineList, getTurbineList,
getPartList,
getImageList,
deleteImage,
autoAnnotateImage,
generateReport,
batchUploadImages,
uploadImageToPartV2, uploadImageToPartV2,
type ProjectTreeNode,
type IndustrialImage,
type ProjectInfo,
type TurbineInfo,
type PartInfo
} from '@/apis/industrial-image' } from '@/apis/industrial-image'
import { import type { type DefectDetectionRequest, type DefectDetectionResult, DefectInfo, type ManualDefectAddRequest, addDefect, addManualDefect, deleteDefect, detectDefects, getDefectList, updateDefect } from '@/apis/industrial-image/defect'
detectDefects,
getDefectList,
addDefect,
updateDefect,
deleteDefect,
addManualDefect,
type DefectDetectionRequest,
type DefectDetectionResult,
type ManualDefectAddRequest
} from '@/apis/industrial-image/defect'
import type { TreeNodeData, ImageInfo } from '@/apis/industrial-image/type'
import type { DefectInfo } from '@/apis/industrial-image/defect'
import type { Annotation } from '@/views/project-operation-platform/data-processing/industrial-image/components/ImageCanvas.vue'
export function useIndustrialImage() { export function useIndustrialImage() {
// 项目树数据 // 项目树数据
@ -73,13 +56,14 @@ export function useIndustrialImage() {
return [{ return [{
id: selectedDefect.value.id, id: selectedDefect.value.id,
type: 'rectangle' as const, type: 'rectangle' as const,
points: selectedDefect.value.boundingBox ? [ points: selectedDefect.value.boundingBox
? [
{ x: selectedDefect.value.boundingBox.x, y: selectedDefect.value.boundingBox.y }, { x: selectedDefect.value.boundingBox.x, y: selectedDefect.value.boundingBox.y },
{ x: selectedDefect.value.boundingBox.x + selectedDefect.value.boundingBox.width, { x: selectedDefect.value.boundingBox.x + selectedDefect.value.boundingBox.width, y: selectedDefect.value.boundingBox.y + selectedDefect.value.boundingBox.height },
y: selectedDefect.value.boundingBox.y + selectedDefect.value.boundingBox.height } ]
] : [], : [],
color: selectedDefect.value.severity === 'high' ? '#ff4d4f' : '#faad14', color: selectedDefect.value.severity === 'high' ? '#ff4d4f' : '#faad14',
label: selectedDefect.value.defectType label: selectedDefect.value.defectType,
}] }]
}) })
@ -113,7 +97,7 @@ export function useIndustrialImage() {
projects = [] projects = []
} }
projectTreeData.value = projects.map(project => ({ projectTreeData.value = projects.map((project) => ({
id: project.projectId, id: project.projectId,
name: project.projectName, name: project.projectName,
type: 'project' as const, type: 'project' as const,
@ -123,7 +107,7 @@ export function useIndustrialImage() {
expanded: false, expanded: false,
status: project.status, status: project.status,
createTime: project.createTime, createTime: project.createTime,
rawData: project rawData: project,
})) }))
// 默认选中第一个项目 // 默认选中第一个项目
@ -153,7 +137,7 @@ export function useIndustrialImage() {
const response = await getTurbineList({ projectId: node.id }) const response = await getTurbineList({ projectId: node.id })
const turbines = response.data || [] const turbines = response.data || []
node.children = turbines.map(turbine => ({ node.children = turbines.map((turbine) => ({
id: turbine.turbineId || turbine.projectId, id: turbine.turbineId || turbine.projectId,
name: turbine.turbineName || turbine.turbineDesc || `机组${turbine.turbineId || turbine.projectId}`, name: turbine.turbineName || turbine.turbineDesc || `机组${turbine.turbineId || turbine.projectId}`,
type: 'turbine' as const, type: 'turbine' as const,
@ -164,16 +148,16 @@ export function useIndustrialImage() {
expanded: false, expanded: false,
status: turbine.status, status: turbine.status,
createTime: turbine.createTime, createTime: turbine.createTime,
rawData: turbine rawData: turbine,
})) }))
} else if (node.type === 'turbine') { } else if (node.type === 'turbine') {
const turbineData = node.rawData as TurbineInfo const turbineData = node.rawData as TurbineInfo
const response = await getPartList({ const response = await getPartList({
turbineId: turbineData?.turbineId || node.id turbineId: turbineData?.turbineId || node.id,
}) })
const parts = response.data || [] const parts = response.data || []
node.children = parts.map(part => ({ node.children = parts.map((part) => ({
id: part.partId, id: part.partId,
name: part.partName || part.partType || `部件${part.partId}`, name: part.partName || part.partType || `部件${part.partId}`,
type: 'part' as const, type: 'part' as const,
@ -184,7 +168,7 @@ export function useIndustrialImage() {
expanded: false, expanded: false,
status: part.partType, status: part.partType,
createTime: part.createTime, createTime: part.createTime,
rawData: part rawData: part,
})) }))
} }
@ -222,7 +206,7 @@ export function useIndustrialImage() {
try { try {
const turbineData = turbineNode.rawData as TurbineInfo const turbineData = turbineNode.rawData as TurbineInfo
const response = await getPartList({ const response = await getPartList({
turbineId: turbineData?.turbineId || turbineNode.id turbineId: turbineData?.turbineId || turbineNode.id,
}) })
const parts = response.data || [] const parts = response.data || []
@ -288,8 +272,6 @@ export function useIndustrialImage() {
} }
} }
// 查找选中的节点 // 查找选中的节点
const findSelectedNode = (nodes: ProjectTreeNode[], nodeId: string): ProjectTreeNode | null => { const findSelectedNode = (nodes: ProjectTreeNode[], nodeId: string): ProjectTreeNode | null => {
if (!nodes || !Array.isArray(nodes) || !nodeId) return null if (!nodes || !Array.isArray(nodes) || !nodeId) return null
@ -316,7 +298,7 @@ export function useIndustrialImage() {
if (!node) continue if (!node) continue
if (node.children && node.children.length > 0) { if (node.children && node.children.length > 0) {
const childFound = node.children.find(child => child && child.id === childId) const childFound = node.children.find((child) => child && child.id === childId)
if (childFound) return node if (childFound) return node
const found = findParentNode(node.children, childId) const found = findParentNode(node.children, childId)
@ -349,7 +331,7 @@ export function useIndustrialImage() {
} }
const handleImagePreview = (image: any) => { const handleImagePreview = (image: any) => {
console.log("image:",image); console.log('image:', image)
previewImage.value = image previewImage.value = image
previewModalVisible.value = true previewModalVisible.value = true
} }
@ -372,7 +354,7 @@ export function useIndustrialImage() {
console.error('删除图像失败:', error) console.error('删除图像失败:', error)
Message.error('删除图像失败') Message.error('删除图像失败')
} }
} },
}) })
} }
@ -451,7 +433,7 @@ export function useIndustrialImage() {
reportUnitData.value = { reportUnitData.value = {
id: selectedNode.id, id: selectedNode.id,
name: selectedNode.name, name: selectedNode.name,
type: selectedNode.type type: selectedNode.type,
} }
// 显示报告生成对话框 // 显示报告生成对话框
@ -489,7 +471,7 @@ export function useIndustrialImage() {
temperatureMax: data.imageInfo.maxTemperature, temperatureMax: data.imageInfo.maxTemperature,
temperatureMin: data.imageInfo.minTemperature, temperatureMin: data.imageInfo.minTemperature,
weather: data.imageInfo.weather, weather: data.imageInfo.weather,
windLevel: data.imageInfo.windPower windLevel: data.imageInfo.windPower,
} }
// 使用新的API接口上传图像 // 使用新的API接口上传图像
@ -497,7 +479,7 @@ export function useIndustrialImage() {
'default', // 图像源 'default', // 图像源
data.part.partId, // 部件ID data.part.partId, // 部件ID
data.images, // 文件列表 data.images, // 文件列表
uploadParams uploadParams,
) )
console.log('批量上传响应:', response) console.log('批量上传响应:', response)
@ -560,13 +542,13 @@ export function useIndustrialImage() {
// 分页结构 // 分页结构
defects = resultData.list.map((item: any) => ({ defects = resultData.list.map((item: any) => ({
id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`, id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
...item ...item,
})) }))
} else if (Array.isArray(resultData)) { } else if (Array.isArray(resultData)) {
// 直接数组结构 // 直接数组结构
defects = resultData.map((item: any) => ({ defects = resultData.map((item: any) => ({
id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`, id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
...item ...item,
})) }))
} }
} }
@ -629,7 +611,7 @@ export function useIndustrialImage() {
repairIdea: defectForm.repairIdea, repairIdea: defectForm.repairIdea,
imageId: selectedImage.value?.imageId || '', imageId: selectedImage.value?.imageId || '',
detectionDate: new Date().toISOString(), detectionDate: new Date().toISOString(),
source: 'manual' source: 'manual',
} }
// 根据节点类型设置相关ID // 根据节点类型设置相关ID
@ -723,19 +705,21 @@ export function useIndustrialImage() {
detectionDate: new Date().toISOString(), detectionDate: new Date().toISOString(),
labelInfo: JSON.stringify(annotation), labelInfo: JSON.stringify(annotation),
markInfo: { markInfo: {
bbox: annotation.type === 'rectangle' ? [ bbox: annotation.type === 'rectangle'
? [
Math.min(annotation.points[0].x, annotation.points[1].x), Math.min(annotation.points[0].x, annotation.points[1].x),
Math.min(annotation.points[0].y, annotation.points[1].y), Math.min(annotation.points[0].y, annotation.points[1].y),
Math.abs(annotation.points[1].x - annotation.points[0].x), Math.abs(annotation.points[1].x - annotation.points[0].x),
Math.abs(annotation.points[1].y - annotation.points[0].y) Math.abs(annotation.points[1].y - annotation.points[0].y),
] : [], ]
: [],
clsId: 1, clsId: 1,
confidence: 1.0, confidence: 1.0,
label: annotation.defectType || '手动标注' label: annotation.defectType || '手动标注',
}, },
repairIdea: '', repairIdea: '',
repairStatus: 'PENDING', repairStatus: 'PENDING',
source: 'MANUAL' source: 'MANUAL',
} }
console.log('发送给后端的缺陷数据:', defectData) console.log('发送给后端的缺陷数据:', defectData)
@ -824,7 +808,7 @@ export function useIndustrialImage() {
confThreshold: settings.confidence / 100, // 转换为0-1的置信度 confThreshold: settings.confidence / 100, // 转换为0-1的置信度
defectTypeList: settings.defectTypes, defectTypeList: settings.defectTypes,
imageId: selectedImage.value.imageId, imageId: selectedImage.value.imageId,
modelId: settings.algorithm // 使用算法作为模型ID modelId: settings.algorithm, // 使用算法作为模型ID
} }
const response = await detectDefects(detectParams) const response = await detectDefects(detectParams)
@ -851,7 +835,7 @@ export function useIndustrialImage() {
// 更新当前选中图像的路径为识别后返回的attachPath // 更新当前选中图像的路径为识别后返回的attachPath
selectedImage.value = { selectedImage.value = {
...selectedImage.value, ...selectedImage.value,
imagePath: firstResult.attachPath imagePath: firstResult.attachPath,
} }
console.log('图片路径已更新为识别后的路径:', firstResult.attachPath) console.log('图片路径已更新为识别后的路径:', firstResult.attachPath)
} }
@ -863,7 +847,6 @@ export function useIndustrialImage() {
} }
// 不再需要类型映射因为RecognitionResults组件已经处理了自定义类型 // 不再需要类型映射因为RecognitionResults组件已经处理了自定义类型
} catch (error) { } catch (error) {
console.error('识别失败:', error) console.error('识别失败:', error)
Message.error('识别失败') Message.error('识别失败')
@ -924,11 +907,11 @@ export function useIndustrialImage() {
bbox: [0, 0, 0, 0], bbox: [0, 0, 0, 0],
clsId: 0, clsId: 0,
confidence: 0.8, confidence: 0.8,
label: result.defectType || '自动识别' label: result.defectType || '自动识别',
}, },
repairIdea: result.repairIdea || '建议进一步检查确认', repairIdea: result.repairIdea || '建议进一步检查确认',
repairStatus: result.repairStatus || 'pending', repairStatus: result.repairStatus || 'pending',
source: 'auto' // 标记为自动识别来源 source: 'auto', // 标记为自动识别来源
} }
// 调用手动添加缺陷接口 // 调用手动添加缺陷接口
@ -967,7 +950,6 @@ export function useIndustrialImage() {
if (successCount > 0) { if (successCount > 0) {
loadDefectList() loadDefectList()
} }
} catch (error) { } catch (error) {
console.error('保存识别结果失败:', error) console.error('保存识别结果失败:', error)
Message.error('保存识别结果失败') Message.error('保存识别结果失败')
@ -1070,6 +1052,6 @@ export function useIndustrialImage() {
// 报告生成相关方法 // 报告生成相关方法
handleReportGenerated, handleReportGenerated,
init init,
} }
} }

View File

@ -12,11 +12,13 @@
<!-- 主体内容区域 --> <!-- 主体内容区域 -->
<div class="main-content"> <div class="main-content">
<!-- 上部区域 --> <!-- 上部区域 -->
<div class="top-section" :class="{ <div
class="top-section" :class="{
'auto-recognition-layout': isAutoRecognitionMode, 'auto-recognition-layout': isAutoRecognitionMode,
'manual-annotation-layout': isManualAnnotationMode, 'manual-annotation-layout': isManualAnnotationMode,
'top-section-expanded': isImageListCollapsed || isAutoRecognitionMode || isManualAnnotationMode 'top-section-expanded': isImageListCollapsed || isAutoRecognitionMode || isManualAnnotationMode,
}"> }"
>
<!-- 自动识别模式三列布局 --> <!-- 自动识别模式三列布局 -->
<template v-if="isAutoRecognitionMode"> <template v-if="isAutoRecognitionMode">
<!-- 左侧自动识别设置面板 --> <!-- 左侧自动识别设置面板 -->
@ -85,7 +87,7 @@
@submit="handleDefectFormSubmit" @submit="handleDefectFormSubmit"
/> />
<div v-else class="no-annotation-prompt"> <div v-else class="no-annotation-prompt">
<icon-bug class="prompt-icon" /> <IconBug class="prompt-icon" />
<p>请在图像上绘制矩形标注缺陷</p> <p>请在图像上绘制矩形标注缺陷</p>
</div> </div>
</div> </div>
@ -129,7 +131,7 @@
</div> </div>
<!-- 下部工业图像列表区域 - 仅在非自动识别和非手动标注模式下显示 --> <!-- 下部工业图像列表区域 - 仅在非自动识别和非手动标注模式下显示 -->
<div v-if="!isAutoRecognitionMode && !isManualAnnotationMode" class="bottom-section" :class="{ 'collapsed': isImageListCollapsed }"> <div v-if="!isAutoRecognitionMode && !isManualAnnotationMode" class="bottom-section" :class="{ collapsed: isImageListCollapsed }">
<IndustrialImageList <IndustrialImageList
:image-list="imageList" :image-list="imageList"
:selected-image-id="selectedImageId" :selected-image-id="selectedImageId"
@ -157,14 +159,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, computed } from 'vue' import { computed, onMounted, ref } from 'vue'
import { IconBug } from '@arco-design/web-vue/es/icon' import { IconBug } from '@arco-design/web-vue/es/icon'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import HeaderToolbar from './components/HeaderToolbar.vue' import HeaderToolbar from './components/HeaderToolbar.vue'
import ProjectTree from './components/ProjectTree.vue' import ProjectTree from './components/ProjectTree.vue'
import ImagePreview from './components/ImagePreview.vue' import ImagePreview from './components/ImagePreview.vue'
import ImageCanvas from '@/views/project-operation-platform/data-processing/industrial-image/components/ImageCanvas.vue'
import IndustrialImageList from '@/components/IndustrialImageList/index.vue'
import ImageModals from './components/ImageModals.vue' import ImageModals from './components/ImageModals.vue'
import RecognitionResults from './components/RecognitionResults.vue' import RecognitionResults from './components/RecognitionResults.vue'
import DefectListPanel from './components/DefectListPanel.vue' import DefectListPanel from './components/DefectListPanel.vue'
@ -172,9 +172,10 @@ import DefectDetailsForm from './components/DefectDetailsForm.vue'
import AutoRecognitionSettings from './components/AutoRecognitionSettings.vue' import AutoRecognitionSettings from './components/AutoRecognitionSettings.vue'
import { useIndustrialImage } from './hooks/useIndustrialImage' import { useIndustrialImage } from './hooks/useIndustrialImage'
import type { TreeNode } from './components/DefectListPanel.vue' import type { TreeNode } from './components/DefectListPanel.vue'
import IndustrialImageList from '@/components/IndustrialImageList/index.vue'
import ImageCanvas from '@/views/project-operation-platform/data-processing/industrial-image/components/ImageCanvas.vue'
import type { Annotation } from '@/views/project-operation-platform/data-processing/industrial-image/components/ImageCanvas.vue' import type { Annotation } from '@/views/project-operation-platform/data-processing/industrial-image/components/ImageCanvas.vue'
import type { ManualDefectAddRequest, DefectInfo, AttachInfoData } from '@/apis/industrial-image/defect' import type { DefectInfo, type DefectLevelType, type DefectType, ManualDefectAddRequest, addManualDefect, getDefectLevels, getDefectList, getDefectTypes, uploadAnnotatedImage } from '@/apis/industrial-image/defect'
import { addManualDefect, getDefectList, uploadAnnotatedImage, getDefectTypes, getDefectLevels, type DefectType, type DefectLevelType } from '@/apis/industrial-image/defect'
defineOptions({ name: 'IndustrialImageProcessing' }) defineOptions({ name: 'IndustrialImageProcessing' })
@ -314,7 +315,7 @@ const {
// //
handleReportGenerated, handleReportGenerated,
init init,
} = useIndustrialImage() } = useIndustrialImage()
// //
@ -341,16 +342,16 @@ const treeData = computed(() => {
key: 'test-part-1', key: 'test-part-1',
title: '测试部件1', title: '测试部件1',
type: 'part', type: 'part',
defectCount: 0 defectCount: 0,
} },
] ],
} },
] ],
} },
] as TreeNode[] ] as TreeNode[]
} }
const result = projectTreeData.value.map(project => { const result = projectTreeData.value.map((project) => {
if (!project) { if (!project) {
console.warn('发现空的project对象') console.warn('发现空的project对象')
return null return null
@ -361,7 +362,7 @@ const treeData = computed(() => {
title: project.name || '未命名项目', title: project.name || '未命名项目',
type: 'project' as const, type: 'project' as const,
defectCount: 0, defectCount: 0,
children: project.children?.map(turbine => { children: project.children?.map((turbine) => {
if (!turbine) { if (!turbine) {
console.warn('发现空的turbine对象') console.warn('发现空的turbine对象')
return null return null
@ -372,7 +373,7 @@ const treeData = computed(() => {
title: turbine.name || '未命名机组', title: turbine.name || '未命名机组',
type: 'turbine' as const, type: 'turbine' as const,
defectCount: 0, defectCount: 0,
children: turbine.children?.map(part => { children: turbine.children?.map((part) => {
if (!part) { if (!part) {
console.warn('发现空的part对象') console.warn('发现空的part对象')
return null return null
@ -382,18 +383,18 @@ const treeData = computed(() => {
key: part.id || `part-${Math.random().toString(36).substr(2, 9)}`, key: part.id || `part-${Math.random().toString(36).substr(2, 9)}`,
title: part.name || '未命名部件', title: part.name || '未命名部件',
type: 'part' as const, type: 'part' as const,
defectCount: 0 defectCount: 0,
} }
}).filter(Boolean) || [] }).filter(Boolean) || [],
} }
return turbineNode return turbineNode
}).filter(Boolean) || [] }).filter(Boolean) || [],
} }
console.log('创建的project节点:', projectNode) console.log('创建的project节点:', projectNode)
return projectNode return projectNode
}).filter(item => item !== null) as TreeNode[] }).filter((item) => item !== null) as TreeNode[]
console.log('最终的treeData:', result) console.log('最终的treeData:', result)
return result return result
@ -437,14 +438,13 @@ const handleAnnotationFinish = async (annotations: Annotation[], imageBlob: Blob
attachId: attachInfo, attachId: attachInfo,
// attachPath: attachInfo.attachPath, // attachPath: attachInfo.attachPath,
// attachInfo: attachInfo, // attachInfo: attachInfo,
allAnnotations: annotations allAnnotations: annotations,
} },
} }
// //
currentAnnotation.value = combinedAnnotation currentAnnotation.value = combinedAnnotation
Message.success(`成功绘制${annotations.length}个区域,请填写缺陷详情`) Message.success(`成功绘制${annotations.length}个区域,请填写缺陷详情`)
} catch (error) { } catch (error) {
console.error('处理标注完成失败:', error) console.error('处理标注完成失败:', error)
Message.error('处理标注失败') Message.error('处理标注失败')
@ -476,10 +476,10 @@ const handleAddDefectFromPanel = () => {
type: 'rectangle', type: 'rectangle',
points: [ points: [
{ x: 100, y: 100 }, { x: 100, y: 100 },
{ x: 200, y: 200 } { x: 200, y: 200 },
], ],
color: '#ff4d4f', color: '#ff4d4f',
label: '手动添加的缺陷' label: '手动添加的缺陷',
} }
// //
@ -508,15 +508,15 @@ const handleTurbineSelect = async (turbineId: string) => {
// //
defects = resultData.list.map((item: any) => ({ defects = resultData.list.map((item: any) => ({
id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`, id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
turbineId: turbineId, turbineId,
...item ...item,
})) }))
} else if (Array.isArray(resultData)) { } else if (Array.isArray(resultData)) {
// //
defects = resultData.map((item: any) => ({ defects = resultData.map((item: any) => ({
id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`, id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
turbineId: turbineId, turbineId,
...item ...item,
})) }))
} }
@ -565,13 +565,13 @@ const loadDefectList = async () => {
// //
defects = resultData.list.map((item: any) => ({ defects = resultData.list.map((item: any) => ({
id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`, id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
...item ...item,
})) }))
} else if (Array.isArray(resultData)) { } else if (Array.isArray(resultData)) {
// //
defects = resultData.map((item: any) => ({ defects = resultData.map((item: any) => ({
id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`, id: item.id || item.defectId || `defect-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
...item ...item,
})) }))
} }
@ -613,7 +613,7 @@ const handleDefectFormSubmit = async (formData: any, annotation: Annotation) =>
// API // API
const defectData = { const defectData = {
attachId: attachId, attachId,
attachPath: '', attachPath: '',
axial: formData.axialDimension, axial: formData.axialDimension,
chordwise: formData.chordDimension, chordwise: formData.chordDimension,
@ -627,36 +627,38 @@ const handleDefectFormSubmit = async (formData: any, annotation: Annotation) =>
defectTypeLabel: formData.defectTypeLabel || getDefectTypeLabel(formData.defectType), defectTypeLabel: formData.defectTypeLabel || getDefectTypeLabel(formData.defectType),
description: formData.description, description: formData.description,
detectionDate: new Date().toISOString().split('T')[0], detectionDate: new Date().toISOString().split('T')[0],
imageId: imageId, imageId,
labelInfo: '', labelInfo: '',
// labelInfo: isMultiAnnotation ? // labelInfo: isMultiAnnotation ?
// JSON.stringify(annotation.metadata?.allAnnotations || []) : // JSON.stringify(annotation.metadata?.allAnnotations || []) :
// JSON.stringify(annotation), // JSON.stringify(annotation),
markInfo: { markInfo: {
bbox: isMultiAnnotation ? bbox: isMultiAnnotation
// bbox // bbox
(annotation.metadata?.allAnnotations || []).map(ann => [ ? (annotation.metadata?.allAnnotations || []).map((ann) => [
Math.min(ann.points[0].x, ann.points[1].x), Math.min(ann.points[0].x, ann.points[1].x),
Math.min(ann.points[0].y, ann.points[1].y), Math.min(ann.points[0].y, ann.points[1].y),
Math.abs(ann.points[1].x - ann.points[0].x), Math.abs(ann.points[1].x - ann.points[0].x),
Math.abs(ann.points[1].y - ann.points[0].y) Math.abs(ann.points[1].y - ann.points[0].y),
]) : ])
// //
annotation.type === 'rectangle' ? [ : annotation.type === 'rectangle'
? [
Math.min(annotation.points[0].x, annotation.points[1].x), Math.min(annotation.points[0].x, annotation.points[1].x),
Math.min(annotation.points[0].y, annotation.points[1].y), Math.min(annotation.points[0].y, annotation.points[1].y),
Math.abs(annotation.points[1].x - annotation.points[0].x), Math.abs(annotation.points[1].x - annotation.points[0].x),
Math.abs(annotation.points[1].y - annotation.points[0].y) Math.abs(annotation.points[1].y - annotation.points[0].y),
] : [], ]
: [],
clsId: 1, clsId: 1,
confidence: 1.0, confidence: 1.0,
label: formData.defectType label: formData.defectType,
}, },
repairIdea: formData.repairIdea, repairIdea: formData.repairIdea,
repairStatus: 'PENDING', repairStatus: 'PENDING',
repairStatusLabel: '待处理', repairStatusLabel: '待处理',
source: 'MANUAL', source: 'MANUAL',
sourceLabel: '手动标注' sourceLabel: '手动标注',
} }
console.log('发送给后端的缺陷数据:', defectData) console.log('发送给后端的缺陷数据:', defectData)
@ -676,12 +678,11 @@ const handleDefectFormSubmit = async (formData: any, annotation: Annotation) =>
// //
const annotationCount = annotation.metadata?.allAnnotations?.length || 1 const annotationCount = annotation.metadata?.allAnnotations?.length || 1
Message.success({ Message.success({
content: isMultiAnnotation ? content: isMultiAnnotation
`成功保存包含${annotationCount}个标注区域的缺陷信息!` : ? `成功保存包含${annotationCount}个标注区域的缺陷信息!`
'缺陷信息保存成功!', : '缺陷信息保存成功!',
duration: 3000 duration: 3000,
}) })
} catch (error) { } catch (error) {
console.error('添加缺陷失败:', error) console.error('添加缺陷失败:', error)
Message.clear() Message.clear()
@ -693,9 +694,9 @@ const handleDefectFormSubmit = async (formData: any, annotation: Annotation) =>
const calculateBoundingBox = (annotations: Annotation[]): number[] => { const calculateBoundingBox = (annotations: Annotation[]): number[] => {
if (!annotations || annotations.length === 0) return [0, 0, 0, 0] if (!annotations || annotations.length === 0) return [0, 0, 0, 0]
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity
annotations.forEach(annotation => { annotations.forEach((annotation) => {
if (annotation.points && annotation.points.length >= 2) { if (annotation.points && annotation.points.length >= 2) {
const [p1, p2] = annotation.points const [p1, p2] = annotation.points
minX = Math.min(minX, p1.x, p2.x) minX = Math.min(minX, p1.x, p2.x)
@ -710,13 +711,13 @@ const calculateBoundingBox = (annotations: Annotation[]): number[] => {
// //
const getDefectLevelLabel = (levelCode: string): string => { const getDefectLevelLabel = (levelCode: string): string => {
const level = defectLevels.value.find(l => l.code === levelCode) const level = defectLevels.value.find((l) => l.code === levelCode)
return level ? level.name : levelCode return level ? level.name : levelCode
} }
// //
const getDefectTypeLabel = (typeCode: string): string => { const getDefectTypeLabel = (typeCode: string): string => {
const type = defectTypes.value.find(t => t.code === typeCode) const type = defectTypes.value.find((t) => t.code === typeCode)
return type ? type.name : typeCode return type ? type.name : typeCode
} }

View File

@ -6,11 +6,11 @@
<div class="operation-bar"> <div class="operation-bar">
<a-space> <a-space>
<a-button type="primary" @click="handleAdd"> <a-button type="primary" @click="handleAdd">
<template #icon><icon-plus /></template> <template #icon><IconPlus /></template>
新建配置 新建配置
</a-button> </a-button>
<a-button @click="refreshList"> <a-button @click="refreshList">
<template #icon><icon-refresh /></template> <template #icon><IconRefresh /></template>
刷新 刷新
</a-button> </a-button>
</a-space> </a-space>
@ -31,9 +31,9 @@
:data="tableData" :data="tableData"
:loading="loading" :loading="loading"
:pagination="pagination" :pagination="pagination"
row-key="modelId"
@page-change="onPageChange" @page-change="onPageChange"
@page-size-change="onPageSizeChange" @page-size-change="onPageSizeChange"
row-key="modelId"
> >
<template #columns> <template #columns>
<!-- <a-table-column title="模型ID" data-index="modelId" /> --> <!-- <a-table-column title="模型ID" data-index="modelId" /> -->
@ -53,11 +53,11 @@
<template #cell="{ record }"> <template #cell="{ record }">
<a-space> <a-space>
<a-button type="text" size="small" @click="handleEdit(record)"> <a-button type="text" size="small" @click="handleEdit(record)">
<template #icon><icon-edit /></template> <template #icon><IconEdit /></template>
编辑 编辑
</a-button> </a-button>
<a-button type="text" size="small" @click="handleView(record)"> <a-button type="text" size="small" @click="handleView(record)">
<template #icon><icon-eye /></template> <template #icon><IconEye /></template>
查看 查看
</a-button> </a-button>
<a-popconfirm <a-popconfirm
@ -65,7 +65,7 @@
@ok="handleDelete(record)" @ok="handleDelete(record)"
> >
<a-button type="text" status="danger" size="small"> <a-button type="text" status="danger" size="small">
<template #icon><icon-delete /></template> <template #icon><IconDelete /></template>
删除 删除
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
@ -81,9 +81,9 @@
<a-modal <a-modal
v-model:visible="formVisible" v-model:visible="formVisible"
:title="isEdit ? '编辑模型配置' : '新增模型配置'" :title="isEdit ? '编辑模型配置' : '新增模型配置'"
unmount-on-close
@cancel="closeForm" @cancel="closeForm"
@before-ok="handleSubmit" @before-ok="handleSubmit"
unmount-on-close
> >
<a-form <a-form
ref="formRef" ref="formRef"
@ -125,7 +125,7 @@
:custom-request="uploadModelFile" :custom-request="uploadModelFile"
> >
<a-button type="primary"> <a-button type="primary">
<template #icon><icon-upload /></template> <template #icon><IconUpload /></template>
上传模型文件 上传模型文件
</a-button> </a-button>
</a-upload> </a-upload>
@ -134,15 +134,15 @@
status="danger" status="danger"
@click="formData.attachId = ''" @click="formData.attachId = ''"
> >
<template #icon><icon-delete /></template> <template #icon><IconDelete /></template>
清除 清除
</a-button> </a-button>
</a-space> </a-space>
<div class="upload-tip" v-if="uploadingFile"> <div v-if="uploadingFile" class="upload-tip">
<a-spin /> 上传中...{{ uploadProgress }}% <a-spin /> 上传中...{{ uploadProgress }}%
</div> </div>
<div class="upload-tip success" v-if="uploadedFileName && !uploadingFile"> <div v-if="uploadedFileName && !uploadingFile" class="upload-tip success">
<icon-check-circle /> 已上传: {{ uploadedFileName }} <IconCheckCircle /> 已上传: {{ uploadedFileName }}
</div> </div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
@ -156,8 +156,8 @@
if (value < 0 || value > 1) { if (value < 0 || value > 1) {
cb('置信度阈值必须在0-1之间'); cb('置信度阈值必须在0-1之间');
} }
} },
} },
]" ]"
> >
<a-input-number <a-input-number
@ -181,8 +181,8 @@
if (value < 0 || value > 1) { if (value < 0 || value > 1) {
cb('NMS阈值必须在0-1之间'); cb('NMS阈值必须在0-1之间');
} }
} },
} },
]" ]"
> >
<a-input-number <a-input-number
@ -202,9 +202,9 @@
<a-modal <a-modal
v-model:visible="detailVisible" v-model:visible="detailVisible"
title="模型配置详情" title="模型配置详情"
@cancel="closeDetail"
:footer="false" :footer="false"
unmount-on-close unmount-on-close
@cancel="closeDetail"
> >
<a-descriptions <a-descriptions
:data="detailData" :data="detailData"
@ -217,33 +217,33 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'; import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue'
import { import {
IconPlus, IconCheckCircle,
IconRefresh, IconDelete,
IconEdit, IconEdit,
IconEye, IconEye,
IconDelete, IconPlus,
IconRefresh,
IconUpload, IconUpload,
IconCheckCircle } from '@arco-design/web-vue/es/icon'
} from '@arco-design/web-vue/es/icon';
import { import {
getModelConfigList,
getModelConfigDetail,
createModelConfig, createModelConfig,
deleteModelConfig,
getModelConfigDetail,
getModelConfigList,
updateModelConfig, updateModelConfig,
deleteModelConfig } from '@/apis/model-config'
} from '@/apis/model-config'; import { addAttachment } from '@/apis/attach-info'
import { addAttachment } from '@/apis/attach-info'; import type { ModelConfigRequest, ModelConfigResponse } from '@/apis/model-config/type'
import type { ModelConfigRequest, ModelConfigResponse } from '@/apis/model-config/type';
defineOptions({ name: 'ModelConfig' }); defineOptions({ name: 'ModelConfig' })
// //
const tableData = ref<any[]>([]); const tableData = ref<any[]>([])
const loading = ref(false); const loading = ref(false)
const searchKeyword = ref(''); const searchKeyword = ref('')
// //
const pagination = reactive({ const pagination = reactive({
@ -252,117 +252,117 @@ const pagination = reactive({
total: 0, total: 0,
showTotal: true, showTotal: true,
showJumper: true, showJumper: true,
}); })
// //
const formRef = ref(); const formRef = ref()
const formVisible = ref(false); const formVisible = ref(false)
const isEdit = ref(false); const isEdit = ref(false)
const formData = reactive<ModelConfigRequest>({ const formData = reactive<ModelConfigRequest>({
attachId: '', attachId: '',
confThreshold: 0, confThreshold: 0,
modelId: '', modelId: '',
modelName: '', modelName: '',
nmsThreshold: 0, nmsThreshold: 0,
}); })
// //
const detailVisible = ref(false); const detailVisible = ref(false)
const detailData = ref<{ label: string; value: any }[]>([]); const detailData = ref<{ label: string, value: any }[]>([])
// //
const uploadingFile = ref(false); const uploadingFile = ref(false)
const uploadProgress = ref(0); const uploadProgress = ref(0)
const uploadedFileName = ref(''); const uploadedFileName = ref('')
// //
const fetchModelConfigList = async () => { const fetchModelConfigList = async () => {
loading.value = true; loading.value = true
try { try {
const res = await getModelConfigList({ const res = await getModelConfigList({
keyword: searchKeyword.value, keyword: searchKeyword.value,
page: pagination.current, page: pagination.current,
pageSize: pagination.pageSize, pageSize: pagination.pageSize,
}); })
if (res.data) { if (res.data) {
tableData.value = Array.isArray(res.data) ? res.data : []; tableData.value = Array.isArray(res.data) ? res.data : []
pagination.total = Array.isArray(res.data) ? res.data.length : 0; pagination.total = Array.isArray(res.data) ? res.data.length : 0
} else { } else {
tableData.value = []; tableData.value = []
pagination.total = 0; pagination.total = 0
} }
} catch (error) { } catch (error) {
console.error('获取模型配置列表失败:', error); console.error('获取模型配置列表失败:', error)
Message.error('获取模型配置列表失败'); Message.error('获取模型配置列表失败')
} finally { } finally {
loading.value = false; loading.value = false
}
} }
};
// //
onMounted(() => { onMounted(() => {
fetchModelConfigList(); fetchModelConfigList()
}); })
// //
const refreshList = () => { const refreshList = () => {
fetchModelConfigList(); fetchModelConfigList()
}; }
// //
const handleSearch = () => { const handleSearch = () => {
pagination.current = 1; pagination.current = 1
fetchModelConfigList(); fetchModelConfigList()
}; }
// //
const onPageChange = (page: number) => { const onPageChange = (page: number) => {
pagination.current = page; pagination.current = page
fetchModelConfigList(); fetchModelConfigList()
}; }
const onPageSizeChange = (pageSize: number) => { const onPageSizeChange = (pageSize: number) => {
pagination.pageSize = pageSize; pagination.pageSize = pageSize
fetchModelConfigList(); fetchModelConfigList()
}; }
// //
const handleAdd = () => { const handleAdd = () => {
isEdit.value = false; isEdit.value = false
resetForm(); resetForm()
formVisible.value = true; formVisible.value = true
}; }
// //
const handleEdit = async (record: ModelConfigResponse) => { const handleEdit = async (record: ModelConfigResponse) => {
isEdit.value = true; isEdit.value = true
resetForm(); resetForm()
try { try {
const res = await getModelConfigDetail(record.modelId); const res = await getModelConfigDetail(record.modelId)
if (res.data) { if (res.data) {
const modelData = res.data.data; const modelData = res.data.data
formData.modelId = modelData.modelId; formData.modelId = modelData.modelId
formData.modelName = modelData.modelName; formData.modelName = modelData.modelName
formData.attachId = modelData.attachId; formData.attachId = modelData.attachId
formData.confThreshold = modelData.confThreshold; formData.confThreshold = modelData.confThreshold
formData.nmsThreshold = modelData.nmsThreshold; formData.nmsThreshold = modelData.nmsThreshold
formVisible.value = true; formVisible.value = true
} }
} catch (error) { } catch (error) {
console.error('获取详情失败:', error); console.error('获取详情失败:', error)
Message.error('获取详情失败'); Message.error('获取详情失败')
}
} }
};
// //
const handleView = async (record: ModelConfigResponse) => { const handleView = async (record: ModelConfigResponse) => {
try { try {
const res = await getModelConfigDetail(record.modelId); const res = await getModelConfigDetail(record.modelId)
if (res.data) { if (res.data) {
const modelData = res.data.data; const modelData = res.data.data
detailData.value = [ detailData.value = [
// { label: 'ID', value: modelData.modelId }, // { label: 'ID', value: modelData.modelId },
{ label: '模型名称', value: modelData.modelName }, { label: '模型名称', value: modelData.modelName },
@ -370,34 +370,34 @@ const handleView = async (record: ModelConfigResponse) => {
{ label: '模型附件ID', value: modelData.attachId || '-' }, { label: '模型附件ID', value: modelData.attachId || '-' },
{ label: '置信度阈值', value: modelData.confThreshold ? modelData.confThreshold.toFixed(2) : '-' }, { label: '置信度阈值', value: modelData.confThreshold ? modelData.confThreshold.toFixed(2) : '-' },
{ label: 'NMS阈值', value: modelData.nmsThreshold ? modelData.nmsThreshold.toFixed(2) : '-' }, { label: 'NMS阈值', value: modelData.nmsThreshold ? modelData.nmsThreshold.toFixed(2) : '-' },
]; ]
detailVisible.value = true; detailVisible.value = true
} }
} catch (error) { } catch (error) {
console.error('获取详情失败:', error); console.error('获取详情失败:', error)
Message.error('获取详情失败'); Message.error('获取详情失败')
}
} }
};
// //
const handleDelete = async (record: ModelConfigResponse) => { const handleDelete = async (record: ModelConfigResponse) => {
try { try {
await deleteModelConfig(record.modelId); await deleteModelConfig(record.modelId)
Message.success('删除成功'); Message.success('删除成功')
fetchModelConfigList(); fetchModelConfigList()
} catch (error) { } catch (error) {
console.error('删除失败:', error); console.error('删除失败:', error)
Message.error('删除失败'); Message.error('删除失败')
}
} }
};
// //
const handleSubmit = async () => { const handleSubmit = async () => {
if (!formRef.value) return false; if (!formRef.value) return false
try { try {
await formRef.value.validate(); await formRef.value.validate()
const submitData = { const submitData = {
modelId: formData.modelId, modelId: formData.modelId,
@ -405,76 +405,76 @@ const handleSubmit = async () => {
attachId: formData.attachId, attachId: formData.attachId,
confThreshold: formData.confThreshold, confThreshold: formData.confThreshold,
nmsThreshold: formData.nmsThreshold, nmsThreshold: formData.nmsThreshold,
}; }
if (isEdit.value) { if (isEdit.value) {
await updateModelConfig(submitData); await updateModelConfig(submitData)
Message.success('更新成功'); Message.success('更新成功')
} else { } else {
await createModelConfig(submitData); await createModelConfig(submitData)
Message.success('创建成功'); Message.success('创建成功')
} }
closeForm(); closeForm()
fetchModelConfigList(); fetchModelConfigList()
return true; return true
} catch (error) { } catch (error) {
console.error('提交失败:', error); console.error('提交失败:', error)
Message.error('提交失败: ' + (error as any)?.msg || '未知错误'); Message.error(`提交失败: ${(error as any)?.msg}` || '未知错误')
return false; return false
}
} }
};
// //
const resetForm = () => { const resetForm = () => {
// formData.modelId = ''; // formData.modelId = '';
formData.modelName = ''; formData.modelName = ''
formData.attachId = ''; formData.attachId = ''
formData.confThreshold = 0; formData.confThreshold = 0
formData.nmsThreshold = 0; formData.nmsThreshold = 0
}; }
// //
const closeForm = () => { const closeForm = () => {
formVisible.value = false; formVisible.value = false
resetForm(); resetForm()
}; }
// //
const closeDetail = () => { const closeDetail = () => {
detailVisible.value = false; detailVisible.value = false
detailData.value = []; detailData.value = []
}; }
// //
const uploadModelFile = async (options: any) => { const uploadModelFile = async (options: any) => {
const {onProgress, onError, onSuccess, fileItem, name} = options; const { onProgress, onError, onSuccess, fileItem, name } = options
try { try {
// FormData // FormData
const uploadFormData = new FormData(); const uploadFormData = new FormData()
uploadFormData.append(name || 'file', fileItem.file); uploadFormData.append(name || 'file', fileItem.file)
// API /attach-info/{businessType} // API /attach-info/{businessType}
const res = await addAttachment(uploadFormData); const res = await addAttachment(uploadFormData)
if (res && res.data) { if (res && res.data) {
// ID // ID
const attachId = res.data; const attachId = res.data
formData.attachId = attachId; formData.attachId = attachId
uploadedFileName.value = fileItem.file.name; uploadedFileName.value = fileItem.file.name
Message.success('模型文件上传成功'); Message.success('模型文件上传成功')
onSuccess(res); onSuccess(res)
} else { } else {
Message.error('模型文件上传失败'); Message.error('模型文件上传失败')
onError(new Error('上传失败')); onError(new Error('上传失败'))
} }
} catch (error) { } catch (error) {
console.error('上传失败:', error); console.error('上传失败:', error)
Message.error('上传失败: ' + (error as any)?.msg || '未知错误'); Message.error(`上传失败: ${(error as any)?.msg}` || '未知错误')
onError(error); onError(error)
} finally { } finally {
uploadingFile.value = false; uploadingFile.value = false
}
} }
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -2,7 +2,7 @@
<div class="app-container"> <div class="app-container">
<a-card class="general-card" title="应用使用数据" :bordered="false"> <a-card class="general-card" title="应用使用数据" :bordered="false">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="6" v-for="(stat, index) in appStatistics" :key="index"> <a-col v-for="(stat, index) in appStatistics" :key="index" :span="6">
<a-card class="stat-card"> <a-card class="stat-card">
<a-statistic <a-statistic
:title="stat.title" :title="stat.title"
@ -46,9 +46,9 @@
<template v-if="column.dataIndex === 'trend'"> <template v-if="column.dataIndex === 'trend'">
<span :style="{ color: getTrendColor(record.trend) }"> <span :style="{ color: getTrendColor(record.trend) }">
{{ record.trend >= 0 ? '+' : '' }}{{ record.trend }}% {{ record.trend >= 0 ? '+' : '' }}{{ record.trend }}%
<icon-up v-if="record.trend > 0" style="color: #52c41a" /> <IconUp v-if="record.trend > 0" style="color: #52c41a" />
<icon-down v-if="record.trend < 0" style="color: #ff4d4f" /> <IconDown v-if="record.trend < 0" style="color: #ff4d4f" />
<icon-minus v-if="record.trend === 0" style="color: #1890ff" /> <IconMinus v-if="record.trend === 0" style="color: #1890ff" />
</span> </span>
</template> </template>
</template> </template>
@ -63,9 +63,9 @@
<template v-if="column.dataIndex === 'trend'"> <template v-if="column.dataIndex === 'trend'">
<span :style="{ color: getTrendColor(record.trend) }"> <span :style="{ color: getTrendColor(record.trend) }">
{{ record.trend >= 0 ? '+' : '' }}{{ record.trend }}% {{ record.trend >= 0 ? '+' : '' }}{{ record.trend }}%
<icon-up v-if="record.trend > 0" style="color: #52c41a" /> <IconUp v-if="record.trend > 0" style="color: #52c41a" />
<icon-down v-if="record.trend < 0" style="color: #ff4d4f" /> <IconDown v-if="record.trend < 0" style="color: #ff4d4f" />
<icon-minus v-if="record.trend === 0" style="color: #1890ff" /> <IconMinus v-if="record.trend === 0" style="color: #1890ff" />
</span> </span>
</template> </template>
</template> </template>
@ -80,9 +80,9 @@
<template v-if="column.dataIndex === 'trend'"> <template v-if="column.dataIndex === 'trend'">
<span :style="{ color: getTrendColor(record.trend) }"> <span :style="{ color: getTrendColor(record.trend) }">
{{ record.trend >= 0 ? '+' : '' }}{{ record.trend }}% {{ record.trend >= 0 ? '+' : '' }}{{ record.trend }}%
<icon-up v-if="record.trend > 0" style="color: #52c41a" /> <IconUp v-if="record.trend > 0" style="color: #52c41a" />
<icon-down v-if="record.trend < 0" style="color: #ff4d4f" /> <IconDown v-if="record.trend < 0" style="color: #ff4d4f" />
<icon-minus v-if="record.trend === 0" style="color: #1890ff" /> <IconMinus v-if="record.trend === 0" style="color: #1890ff" />
</span> </span>
</template> </template>
</template> </template>
@ -105,20 +105,17 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, reactive, nextTick } from 'vue' import { nextTick, onMounted, reactive, ref } from 'vue'
import { Statistic } from '@arco-design/web-vue'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { import {
IconApps, IconApps,
IconMobile,
IconUser,
IconClockCircle, IconClockCircle,
IconUp,
IconDown, IconDown,
IconMinus IconMinus,
IconUp,
IconUser,
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
const appVisitChart = ref(null) const appVisitChart = ref(null)
const appTimeChart = ref(null) const appTimeChart = ref(null)
const deviceDistributionChart = ref(null) const deviceDistributionChart = ref(null)
@ -128,25 +125,25 @@
{ {
title: '应用总数', title: '应用总数',
value: 12, value: 12,
icon: IconApps icon: IconApps,
}, },
{ {
title: '活跃应用', title: '活跃应用',
value: 8, value: 8,
icon: IconApps icon: IconApps,
}, },
{ {
title: '日均访问量', title: '日均访问量',
value: 1258, value: 1258,
icon: IconUser icon: IconUser,
}, },
{ {
title: '日均使用时长', title: '日均使用时长',
value: 3.5, value: 3.5,
precision: 1, precision: 1,
suffix: '小时', suffix: '小时',
icon: IconClockCircle icon: IconClockCircle,
} },
]) ])
// 使 // 使
@ -184,7 +181,7 @@
dataIndex: 'trend', dataIndex: 'trend',
key: 'trend', key: 'trend',
sorter: (a, b) => a.trend - b.trend, sorter: (a, b) => a.trend - b.trend,
} },
] ]
// Web // Web
@ -196,7 +193,7 @@
userCount: 320, userCount: 320,
usageRate: 95, usageRate: 95,
averageTime: '2.5小时', averageTime: '2.5小时',
trend: 8.5 trend: 8.5,
}, },
{ {
key: '2', key: '2',
@ -205,7 +202,7 @@
userCount: 285, userCount: 285,
usageRate: 90, usageRate: 90,
averageTime: '3小时', averageTime: '3小时',
trend: 5.2 trend: 5.2,
}, },
{ {
key: '3', key: '3',
@ -214,7 +211,7 @@
userCount: 210, userCount: 210,
usageRate: 85, usageRate: 85,
averageTime: '2小时', averageTime: '2小时',
trend: 3.8 trend: 3.8,
}, },
{ {
key: '4', key: '4',
@ -223,7 +220,7 @@
userCount: 180, userCount: 180,
usageRate: 75, usageRate: 75,
averageTime: '1.5小时', averageTime: '1.5小时',
trend: -2.1 trend: -2.1,
}, },
{ {
key: '5', key: '5',
@ -232,8 +229,8 @@
userCount: 150, userCount: 150,
usageRate: 65, usageRate: 65,
averageTime: '1小时', averageTime: '1小时',
trend: 0 trend: 0,
} },
] ]
// //
@ -245,7 +242,7 @@
userCount: 350, userCount: 350,
usageRate: 98, usageRate: 98,
averageTime: '3.5小时', averageTime: '3.5小时',
trend: 12.5 trend: 12.5,
}, },
{ {
key: '2', key: '2',
@ -254,7 +251,7 @@
userCount: 320, userCount: 320,
usageRate: 92, usageRate: 92,
averageTime: '4小时', averageTime: '4小时',
trend: 9.8 trend: 9.8,
}, },
{ {
key: '3', key: '3',
@ -263,7 +260,7 @@
userCount: 260, userCount: 260,
usageRate: 88, usageRate: 88,
averageTime: '3小时', averageTime: '3小时',
trend: 7.5 trend: 7.5,
}, },
{ {
key: '4', key: '4',
@ -272,8 +269,8 @@
userCount: 180, userCount: 180,
usageRate: 72, usageRate: 72,
averageTime: '2小时', averageTime: '2小时',
trend: -1.5 trend: -1.5,
} },
] ]
// //
@ -285,7 +282,7 @@
userCount: 420, userCount: 420,
usageRate: 96, usageRate: 96,
averageTime: '1.5小时', averageTime: '1.5小时',
trend: 15.8 trend: 15.8,
}, },
{ {
key: '2', key: '2',
@ -294,7 +291,7 @@
userCount: 380, userCount: 380,
usageRate: 90, usageRate: 90,
averageTime: '1小时', averageTime: '1小时',
trend: 10.5 trend: 10.5,
}, },
{ {
key: '3', key: '3',
@ -303,8 +300,8 @@
userCount: 345, userCount: 345,
usageRate: 85, usageRate: 85,
averageTime: '0.8小时', averageTime: '0.8小时',
trend: 8.2 trend: 8.2,
} },
] ]
// //
@ -328,42 +325,42 @@
const visitChart = echarts.init(appVisitChart.value) const visitChart = echarts.init(appVisitChart.value)
visitChart.setOption({ visitChart.setOption({
tooltip: { tooltip: {
trigger: 'axis' trigger: 'axis',
}, },
legend: { legend: {
data: ['Web端', '移动端', '小程序'] data: ['Web端', '移动端', '小程序'],
}, },
grid: { grid: {
left: '3%', left: '3%',
right: '4%', right: '4%',
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true,
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
}, },
yAxis: { yAxis: {
type: 'value' type: 'value',
}, },
series: [ series: [
{ {
name: 'Web端', name: 'Web端',
type: 'line', type: 'line',
data: [2500, 2800, 3200, 3100, 2950, 1800, 1200] data: [2500, 2800, 3200, 3100, 2950, 1800, 1200],
}, },
{ {
name: '移动端', name: '移动端',
type: 'line', type: 'line',
data: [3200, 3500, 3800, 3600, 3400, 2800, 2500] data: [3200, 3500, 3800, 3600, 3400, 2800, 2500],
}, },
{ {
name: '小程序', name: '小程序',
type: 'line', type: 'line',
data: [4500, 4800, 5200, 4900, 4700, 3900, 3500] data: [4500, 4800, 5200, 4900, 4700, 3900, 3500],
} },
] ],
}) })
} }
@ -372,11 +369,11 @@
const timeChart = echarts.init(appTimeChart.value) const timeChart = echarts.init(appTimeChart.value)
timeChart.setOption({ timeChart.setOption({
tooltip: { tooltip: {
trigger: 'item' trigger: 'item',
}, },
legend: { legend: {
orient: 'vertical', orient: 'vertical',
left: 'left' left: 'left',
}, },
series: [ series: [
{ {
@ -386,17 +383,17 @@
data: [ data: [
{ value: 35, name: 'Web端' }, { value: 35, name: 'Web端' },
{ value: 45, name: '移动端' }, { value: 45, name: '移动端' },
{ value: 20, name: '小程序' } { value: 20, name: '小程序' },
], ],
emphasis: { emphasis: {
itemStyle: { itemStyle: {
shadowBlur: 10, shadowBlur: 10,
shadowOffsetX: 0, shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)' shadowColor: 'rgba(0, 0, 0, 0.5)',
} },
} },
} },
] ],
}) })
} }
@ -407,22 +404,22 @@
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'shadow' type: 'shadow',
} },
}, },
legend: {}, legend: {},
grid: { grid: {
left: '3%', left: '3%',
right: '4%', right: '4%',
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true,
}, },
xAxis: { xAxis: {
type: 'value' type: 'value',
}, },
yAxis: { yAxis: {
type: 'category', type: 'category',
data: ['Windows PC', 'Mac', 'iOS', 'Android', '微信'] data: ['Windows PC', 'Mac', 'iOS', 'Android', '微信'],
}, },
series: [ series: [
{ {
@ -430,26 +427,26 @@
type: 'bar', type: 'bar',
stack: 'total', stack: 'total',
label: { label: {
show: true show: true,
}, },
emphasis: { emphasis: {
focus: 'series' focus: 'series',
}, },
data: [5200, 3800, 6500, 8200, 9500] data: [5200, 3800, 6500, 8200, 9500],
}, },
{ {
name: '用户数', name: '用户数',
type: 'bar', type: 'bar',
stack: 'total', stack: 'total',
label: { label: {
show: true show: true,
}, },
emphasis: { emphasis: {
focus: 'series' focus: 'series',
}, },
data: [280, 220, 320, 380, 420] data: [280, 220, 320, 380, 420],
} },
] ],
}) })
} }

View File

@ -48,7 +48,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, nextTick } from 'vue' import { nextTick, onMounted, ref } from 'vue'
import * as echarts from 'echarts' import * as echarts from 'echarts'
// @ant-design/icons-vue // @ant-design/icons-vue
// import { ArrowUpOutlined, ArrowDownOutlined, MinusOutlined } from '@ant-design/icons-vue' // import { ArrowUpOutlined, ArrowDownOutlined, MinusOutlined } from '@ant-design/icons-vue'
@ -92,7 +92,7 @@
dataIndex: 'trend', dataIndex: 'trend',
key: 'trend', key: 'trend',
sorter: (a, b) => a.trend - b.trend, sorter: (a, b) => a.trend - b.trend,
} },
] ]
const functionData = [ const functionData = [
@ -103,7 +103,7 @@
usageCount: 1245, usageCount: 1245,
usageRate: 92, usageRate: 92,
averageTime: '15分钟', averageTime: '15分钟',
trend: 5.2 trend: 5.2,
}, },
{ {
key: '2', key: '2',
@ -112,7 +112,7 @@
usageCount: 865, usageCount: 865,
usageRate: 78, usageRate: 78,
averageTime: '12分钟', averageTime: '12分钟',
trend: 3.8 trend: 3.8,
}, },
{ {
key: '3', key: '3',
@ -121,7 +121,7 @@
usageCount: 1056, usageCount: 1056,
usageRate: 85, usageRate: 85,
averageTime: '18分钟', averageTime: '18分钟',
trend: 7.5 trend: 7.5,
}, },
{ {
key: '4', key: '4',
@ -130,7 +130,7 @@
usageCount: 932, usageCount: 932,
usageRate: 80, usageRate: 80,
averageTime: '14分钟', averageTime: '14分钟',
trend: -2.1 trend: -2.1,
}, },
{ {
key: '5', key: '5',
@ -139,7 +139,7 @@
usageCount: 1120, usageCount: 1120,
usageRate: 88, usageRate: 88,
averageTime: '20分钟', averageTime: '20分钟',
trend: 4.3 trend: 4.3,
}, },
{ {
key: '6', key: '6',
@ -148,7 +148,7 @@
usageCount: 986, usageCount: 986,
usageRate: 82, usageRate: 82,
averageTime: '16分钟', averageTime: '16分钟',
trend: 0 trend: 0,
}, },
{ {
key: '7', key: '7',
@ -157,7 +157,7 @@
usageCount: 1320, usageCount: 1320,
usageRate: 95, usageRate: 95,
averageTime: '25分钟', averageTime: '25分钟',
trend: 8.7 trend: 8.7,
}, },
{ {
key: '8', key: '8',
@ -166,7 +166,7 @@
usageCount: 1150, usageCount: 1150,
usageRate: 90, usageRate: 90,
averageTime: '22分钟', averageTime: '22分钟',
trend: 6.2 trend: 6.2,
}, },
{ {
key: '9', key: '9',
@ -175,7 +175,7 @@
usageCount: 1280, usageCount: 1280,
usageRate: 93, usageRate: 93,
averageTime: '30分钟', averageTime: '30分钟',
trend: 9.5 trend: 9.5,
}, },
{ {
key: '10', key: '10',
@ -184,7 +184,7 @@
usageCount: 1180, usageCount: 1180,
usageRate: 91, usageRate: 91,
averageTime: '28分钟', averageTime: '28分钟',
trend: 5.8 trend: 5.8,
}, },
{ {
key: '11', key: '11',
@ -193,7 +193,7 @@
usageCount: 1420, usageCount: 1420,
usageRate: 98, usageRate: 98,
averageTime: '35分钟', averageTime: '35分钟',
trend: 12.3 trend: 12.3,
}, },
{ {
key: '12', key: '12',
@ -202,8 +202,8 @@
usageCount: 720, usageCount: 720,
usageRate: 65, usageRate: 65,
averageTime: '10分钟', averageTime: '10分钟',
trend: -1.5 trend: -1.5,
} },
] ]
// //
@ -229,37 +229,37 @@
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'shadow' type: 'shadow',
} },
}, },
legend: { legend: {
data: ['使用次数', '使用人数'] data: ['使用次数', '使用人数'],
}, },
grid: { grid: {
left: '3%', left: '3%',
right: '4%', right: '4%',
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true,
}, },
xAxis: { xAxis: {
type: 'value' type: 'value',
}, },
yAxis: { yAxis: {
type: 'category', type: 'category',
data: ['组织架构', '资产管理', '产品与服务', '项目管理', '施工操作台', '聊天平台', '企业设置', '系统资源管理'] data: ['组织架构', '资产管理', '产品与服务', '项目管理', '施工操作台', '聊天平台', '企业设置', '系统资源管理'],
}, },
series: [ series: [
{ {
name: '使用次数', name: '使用次数',
type: 'bar', type: 'bar',
data: [2110, 1988, 2106, 2470, 2460, 1420, 720, 650] data: [2110, 1988, 2106, 2470, 2460, 1420, 720, 650],
}, },
{ {
name: '使用人数', name: '使用人数',
type: 'bar', type: 'bar',
data: [320, 302, 315, 335, 340, 356, 120, 85] data: [320, 302, 315, 335, 340, 356, 120, 85],
} },
] ],
}) })
} }
@ -268,11 +268,11 @@
const departmentChart = echarts.init(departmentUsageChart.value) const departmentChart = echarts.init(departmentUsageChart.value)
departmentChart.setOption({ departmentChart.setOption({
tooltip: { tooltip: {
trigger: 'item' trigger: 'item',
}, },
legend: { legend: {
orient: 'vertical', orient: 'vertical',
left: 'left' left: 'left',
}, },
series: [ series: [
{ {
@ -284,17 +284,17 @@
{ value: 25, name: '市场部' }, { value: 25, name: '市场部' },
{ value: 20, name: '销售部' }, { value: 20, name: '销售部' },
{ value: 10, name: '人事部' }, { value: 10, name: '人事部' },
{ value: 10, name: '财务部' } { value: 10, name: '财务部' },
], ],
emphasis: { emphasis: {
itemStyle: { itemStyle: {
shadowBlur: 10, shadowBlur: 10,
shadowOffsetX: 0, shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)' shadowColor: 'rgba(0, 0, 0, 0.5)',
} },
} },
} },
] ],
}) })
} }
@ -303,11 +303,11 @@
const timeChart = echarts.init(usageTimeChart.value) const timeChart = echarts.init(usageTimeChart.value)
timeChart.setOption({ timeChart.setOption({
tooltip: { tooltip: {
trigger: 'item' trigger: 'item',
}, },
legend: { legend: {
orient: 'vertical', orient: 'vertical',
left: 'left' left: 'left',
}, },
series: [ series: [
{ {
@ -318,21 +318,21 @@
itemStyle: { itemStyle: {
borderRadius: 10, borderRadius: 10,
borderColor: '#fff', borderColor: '#fff',
borderWidth: 2 borderWidth: 2,
}, },
label: { label: {
show: false, show: false,
position: 'center' position: 'center',
}, },
emphasis: { emphasis: {
label: { label: {
show: true, show: true,
fontSize: '16', fontSize: '16',
fontWeight: 'bold' fontWeight: 'bold',
} },
}, },
labelLine: { labelLine: {
show: false show: false,
}, },
data: [ data: [
{ value: 20, name: '组织架构' }, { value: 20, name: '组织架构' },
@ -342,10 +342,10 @@
{ value: 20, name: '施工操作台' }, { value: 20, name: '施工操作台' },
{ value: 5, name: '聊天平台' }, { value: 5, name: '聊天平台' },
{ value: 3, name: '企业设置' }, { value: 3, name: '企业设置' },
{ value: 2, name: '系统资源管理' } { value: 2, name: '系统资源管理' },
] ],
} },
] ],
}) })
} }

View File

@ -76,7 +76,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, nextTick } from 'vue' import { nextTick, onMounted, ref } from 'vue'
import * as echarts from 'echarts' import * as echarts from 'echarts'
const departmentActivityChart = ref(null) const departmentActivityChart = ref(null)
@ -116,7 +116,7 @@
title: '操作次数', title: '操作次数',
dataIndex: 'operationCount', dataIndex: 'operationCount',
key: 'operationCount', key: 'operationCount',
} },
] ]
const rankData = [ const rankData = [
@ -127,7 +127,7 @@
department: '技术部', department: '技术部',
activityScore: 98, activityScore: 98,
loginCount: 45, loginCount: 45,
operationCount: 532 operationCount: 532,
}, },
{ {
key: '2', key: '2',
@ -136,7 +136,7 @@
department: '市场部', department: '市场部',
activityScore: 95, activityScore: 95,
loginCount: 42, loginCount: 42,
operationCount: 498 operationCount: 498,
}, },
{ {
key: '3', key: '3',
@ -145,7 +145,7 @@
department: '销售部', department: '销售部',
activityScore: 92, activityScore: 92,
loginCount: 40, loginCount: 40,
operationCount: 475 operationCount: 475,
}, },
{ {
key: '4', key: '4',
@ -154,7 +154,7 @@
department: '人事部', department: '人事部',
activityScore: 88, activityScore: 88,
loginCount: 38, loginCount: 38,
operationCount: 450 operationCount: 450,
}, },
{ {
key: '5', key: '5',
@ -163,7 +163,7 @@
department: '财务部', department: '财务部',
activityScore: 85, activityScore: 85,
loginCount: 36, loginCount: 36,
operationCount: 420 operationCount: 420,
}, },
{ {
key: '6', key: '6',
@ -172,7 +172,7 @@
department: '技术部', department: '技术部',
activityScore: 82, activityScore: 82,
loginCount: 34, loginCount: 34,
operationCount: 405 operationCount: 405,
}, },
{ {
key: '7', key: '7',
@ -181,7 +181,7 @@
department: '市场部', department: '市场部',
activityScore: 79, activityScore: 79,
loginCount: 32, loginCount: 32,
operationCount: 380 operationCount: 380,
}, },
{ {
key: '8', key: '8',
@ -190,7 +190,7 @@
department: '销售部', department: '销售部',
activityScore: 76, activityScore: 76,
loginCount: 30, loginCount: 30,
operationCount: 365 operationCount: 365,
}, },
{ {
key: '9', key: '9',
@ -199,7 +199,7 @@
department: '人事部', department: '人事部',
activityScore: 73, activityScore: 73,
loginCount: 28, loginCount: 28,
operationCount: 350 operationCount: 350,
}, },
{ {
key: '10', key: '10',
@ -208,8 +208,8 @@
department: '财务部', department: '财务部',
activityScore: 70, activityScore: 70,
loginCount: 26, loginCount: 26,
operationCount: 335 operationCount: 335,
} },
] ]
// //
@ -243,7 +243,7 @@
title: '缺勤次数', title: '缺勤次数',
dataIndex: 'absentCount', dataIndex: 'absentCount',
key: 'absentCount', key: 'absentCount',
} },
] ]
const attendanceData = [ const attendanceData = [
@ -254,7 +254,7 @@
attendanceRate: 98, attendanceRate: 98,
lateCount: 3, lateCount: 3,
earlyLeaveCount: 1, earlyLeaveCount: 1,
absentCount: 0 absentCount: 0,
}, },
{ {
key: '2', key: '2',
@ -263,7 +263,7 @@
attendanceRate: 96, attendanceRate: 96,
lateCount: 5, lateCount: 5,
earlyLeaveCount: 2, earlyLeaveCount: 2,
absentCount: 1 absentCount: 1,
}, },
{ {
key: '3', key: '3',
@ -272,7 +272,7 @@
attendanceRate: 95, attendanceRate: 95,
lateCount: 6, lateCount: 6,
earlyLeaveCount: 3, earlyLeaveCount: 3,
absentCount: 1 absentCount: 1,
}, },
{ {
key: '4', key: '4',
@ -281,7 +281,7 @@
attendanceRate: 97, attendanceRate: 97,
lateCount: 2, lateCount: 2,
earlyLeaveCount: 1, earlyLeaveCount: 1,
absentCount: 0 absentCount: 0,
}, },
{ {
key: '5', key: '5',
@ -290,8 +290,8 @@
attendanceRate: 99, attendanceRate: 99,
lateCount: 1, lateCount: 1,
earlyLeaveCount: 0, earlyLeaveCount: 0,
absentCount: 0 absentCount: 0,
} },
] ]
// //
@ -329,40 +329,40 @@
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'shadow' type: 'shadow',
} },
}, },
legend: {}, legend: {},
grid: { grid: {
left: '3%', left: '3%',
right: '4%', right: '4%',
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true,
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: ['技术部', '市场部', '销售部', '人事部', '财务部'] data: ['技术部', '市场部', '销售部', '人事部', '财务部'],
}, },
yAxis: { yAxis: {
type: 'value' type: 'value',
}, },
series: [ series: [
{ {
name: '活跃度', name: '活跃度',
type: 'bar', type: 'bar',
data: [92, 85, 88, 79, 82] data: [92, 85, 88, 79, 82],
}, },
{ {
name: '登录次数', name: '登录次数',
type: 'bar', type: 'bar',
data: [320, 280, 310, 240, 260] data: [320, 280, 310, 240, 260],
}, },
{ {
name: '操作次数', name: '操作次数',
type: 'bar', type: 'bar',
data: [2800, 2100, 2400, 1800, 2000] data: [2800, 2100, 2400, 1800, 2000],
} },
] ],
}) })
} }
@ -371,22 +371,22 @@
const dailyActiveChart = echarts.init(dailyActiveUsersChart.value) const dailyActiveChart = echarts.init(dailyActiveUsersChart.value)
dailyActiveChart.setOption({ dailyActiveChart.setOption({
tooltip: { tooltip: {
trigger: 'axis' trigger: 'axis',
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
}, },
yAxis: { yAxis: {
type: 'value' type: 'value',
}, },
series: [ series: [
{ {
data: [120, 132, 145, 135, 128, 68, 42], data: [120, 132, 145, 135, 128, 68, 42],
type: 'line', type: 'line',
areaStyle: {} areaStyle: {},
} },
] ],
}) })
} }
@ -395,25 +395,25 @@
const onlineChart = echarts.init(onlineTimeChart.value) const onlineChart = echarts.init(onlineTimeChart.value)
onlineChart.setOption({ onlineChart.setOption({
tooltip: { tooltip: {
trigger: 'axis' trigger: 'axis',
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: ['技术部', '市场部', '销售部', '人事部', '财务部'] data: ['技术部', '市场部', '销售部', '人事部', '财务部'],
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
axisLabel: { axisLabel: {
formatter: '{value} 小时' formatter: '{value} 小时',
} },
}, },
series: [ series: [
{ {
name: '平均在线时长', name: '平均在线时长',
type: 'bar', type: 'bar',
data: [7.5, 6.8, 7.2, 6.5, 6.9] data: [7.5, 6.8, 7.2, 6.5, 6.9],
} },
] ],
}) })
} }
@ -422,45 +422,45 @@
const attendanceChart = echarts.init(attendanceTrendChart.value) const attendanceChart = echarts.init(attendanceTrendChart.value)
attendanceChart.setOption({ attendanceChart.setOption({
tooltip: { tooltip: {
trigger: 'axis' trigger: 'axis',
}, },
legend: { legend: {
data: ['出勤率', '迟到率', '早退率'] data: ['出勤率', '迟到率', '早退率'],
}, },
grid: { grid: {
left: '3%', left: '3%',
right: '4%', right: '4%',
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true,
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
data: ['1月', '2月', '3月', '4月', '5月', '6月'] data: ['1月', '2月', '3月', '4月', '5月', '6月'],
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
axisLabel: { axisLabel: {
formatter: '{value}%' formatter: '{value}%',
} },
}, },
series: [ series: [
{ {
name: '出勤率', name: '出勤率',
type: 'line', type: 'line',
data: [96.2, 97.1, 96.8, 97.5, 98.2, 97.8] data: [96.2, 97.1, 96.8, 97.5, 98.2, 97.8],
}, },
{ {
name: '迟到率', name: '迟到率',
type: 'line', type: 'line',
data: [2.8, 2.2, 2.5, 1.8, 1.2, 1.5] data: [2.8, 2.2, 2.5, 1.8, 1.2, 1.5],
}, },
{ {
name: '早退率', name: '早退率',
type: 'line', type: 'line',
data: [1.0, 0.7, 0.7, 0.7, 0.6, 0.7] data: [1.0, 0.7, 0.7, 0.7, 0.6, 0.7],
} },
] ],
}) })
} }

View File

@ -11,7 +11,7 @@
style="margin-right: 50px" style="margin-right: 50px"
> >
<template #prefix> <template #prefix>
<icon-file /> <IconFile />
</template> </template>
</a-statistic> </a-statistic>
</a-card> </a-card>
@ -24,7 +24,7 @@
:precision="0" :precision="0"
> >
<template #prefix> <template #prefix>
<icon-user-group /> <IconUserGroup />
</template> </template>
</a-statistic> </a-statistic>
</a-card> </a-card>
@ -37,7 +37,7 @@
:precision="0" :precision="0"
> >
<template #prefix> <template #prefix>
<icon-computer /> <IconComputer />
</template> </template>
</a-statistic> </a-statistic>
</a-card> </a-card>
@ -50,7 +50,7 @@
:precision="0" :precision="0"
> >
<template #prefix> <template #prefix>
<icon-check-circle /> <IconCheckCircle />
</template> </template>
</a-statistic> </a-statistic>
</a-card> </a-card>
@ -86,9 +86,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, reactive, nextTick } from 'vue' import { nextTick, onMounted, reactive, ref } from 'vue'
import { Statistic } from '@arco-design/web-vue' import { IconCheckCircle, IconComputer, IconFile, IconUserGroup } from '@arco-design/web-vue/es/icon'
import { IconFile, IconUserGroup, IconComputer, IconCheckCircle } from '@arco-design/web-vue/es/icon'
import * as echarts from 'echarts' import * as echarts from 'echarts'
const projectProgressChart = ref(null) const projectProgressChart = ref(null)
@ -99,7 +98,7 @@
projectCount: 128, projectCount: 128,
memberCount: 356, memberCount: 356,
deviceCount: 243, deviceCount: 243,
completedProjectCount: 15 completedProjectCount: 15,
}) })
onMounted(() => { onMounted(() => {
@ -110,11 +109,11 @@
const projectChart = echarts.init(projectProgressChart.value) const projectChart = echarts.init(projectProgressChart.value)
projectChart.setOption({ projectChart.setOption({
tooltip: { tooltip: {
trigger: 'item' trigger: 'item',
}, },
legend: { legend: {
orient: 'vertical', orient: 'vertical',
left: 'left' left: 'left',
}, },
series: [ series: [
{ {
@ -125,17 +124,17 @@
{ value: 48, name: '进行中' }, { value: 48, name: '进行中' },
{ value: 65, name: '已完成' }, { value: 65, name: '已完成' },
{ value: 12, name: '已暂停' }, { value: 12, name: '已暂停' },
{ value: 3, name: '已取消' } { value: 3, name: '已取消' },
], ],
emphasis: { emphasis: {
itemStyle: { itemStyle: {
shadowBlur: 10, shadowBlur: 10,
shadowOffsetX: 0, shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)' shadowColor: 'rgba(0, 0, 0, 0.5)',
} },
} },
} },
] ],
}) })
} }
@ -146,22 +145,22 @@
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'shadow' type: 'shadow',
} },
}, },
legend: {}, legend: {},
grid: { grid: {
left: '3%', left: '3%',
right: '4%', right: '4%',
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true,
}, },
xAxis: { xAxis: {
type: 'value' type: 'value',
}, },
yAxis: { yAxis: {
type: 'category', type: 'category',
data: ['服务器', '存储空间', '带宽', '设备使用率', '人力资源'] data: ['服务器', '存储空间', '带宽', '设备使用率', '人力资源'],
}, },
series: [ series: [
{ {
@ -169,26 +168,26 @@
type: 'bar', type: 'bar',
stack: 'total', stack: 'total',
label: { label: {
show: true show: true,
}, },
emphasis: { emphasis: {
focus: 'series' focus: 'series',
}, },
data: [65, 72, 58, 80, 75] data: [65, 72, 58, 80, 75],
}, },
{ {
name: '剩余', name: '剩余',
type: 'bar', type: 'bar',
stack: 'total', stack: 'total',
label: { label: {
show: true show: true,
}, },
emphasis: { emphasis: {
focus: 'series' focus: 'series',
}, },
data: [35, 28, 42, 20, 25] data: [35, 28, 42, 20, 25],
} },
] ],
}) })
} }
@ -197,42 +196,42 @@
const businessChart = echarts.init(businessTrendChart.value) const businessChart = echarts.init(businessTrendChart.value)
businessChart.setOption({ businessChart.setOption({
tooltip: { tooltip: {
trigger: 'axis' trigger: 'axis',
}, },
legend: { legend: {
data: ['项目数量', '营业收入', '新增客户'] data: ['项目数量', '营业收入', '新增客户'],
}, },
grid: { grid: {
left: '3%', left: '3%',
right: '4%', right: '4%',
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true,
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
data: ['1月', '2月', '3月', '4月', '5月', '6月'] data: ['1月', '2月', '3月', '4月', '5月', '6月'],
}, },
yAxis: { yAxis: {
type: 'value' type: 'value',
}, },
series: [ series: [
{ {
name: '项目数量', name: '项目数量',
type: 'line', type: 'line',
data: [10, 12, 15, 18, 22, 24] data: [10, 12, 15, 18, 22, 24],
}, },
{ {
name: '营业收入', name: '营业收入',
type: 'line', type: 'line',
data: [120, 132, 145, 160, 178, 190] data: [120, 132, 145, 160, 178, 190],
}, },
{ {
name: '新增客户', name: '新增客户',
type: 'line', type: 'line',
data: [5, 7, 8, 10, 12, 15] data: [5, 7, 8, 10, 12, 15],
} },
] ],
}) })
} }

View File

@ -84,8 +84,8 @@
<a-link @click="editRecord(record)">编辑</a-link> <a-link @click="editRecord(record)">编辑</a-link>
<a-link @click="resetPassword(record)">重置密码</a-link> <a-link @click="resetPassword(record)">重置密码</a-link>
<a-link <a-link
@click="toggleStatus(record)"
:class="record.status === 'active' ? 'text-red-500' : 'text-green-500'" :class="record.status === 'active' ? 'text-red-500' : 'text-green-500'"
@click="toggleStatus(record)"
> >
{{ record.status === 'active' ? '禁用' : '启用' }} {{ record.status === 'active' ? '禁用' : '启用' }}
</a-link> </a-link>
@ -96,18 +96,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
// //
let searchForm = reactive({ const searchForm = reactive({
username: '', username: '',
adminType: '', adminType: '',
status: '', status: '',
createTime: '', createTime: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
// //
@ -117,8 +117,8 @@ const queryFormColumns = [
label: '用户名', label: '用户名',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入用户名' placeholder: '请输入用户名',
} },
}, },
{ {
field: 'adminType', field: 'adminType',
@ -130,9 +130,9 @@ const queryFormColumns = [
{ label: '超级管理员', value: '超级管理员' }, { label: '超级管理员', value: '超级管理员' },
{ label: '系统管理员', value: '系统管理员' }, { label: '系统管理员', value: '系统管理员' },
{ label: '业务管理员', value: '业务管理员' }, { label: '业务管理员', value: '业务管理员' },
{ label: '财务管理员', value: '财务管理员' } { label: '财务管理员', value: '财务管理员' },
] ],
} },
}, },
{ {
field: 'status', field: 'status',
@ -143,10 +143,10 @@ const queryFormColumns = [
options: [ options: [
{ label: '正常', value: 'active' }, { label: '正常', value: 'active' },
{ label: '禁用', value: 'disabled' }, { label: '禁用', value: 'disabled' },
{ label: '锁定', value: 'locked' } { label: '锁定', value: 'locked' },
] ],
} },
} },
] ]
// //
@ -163,7 +163,7 @@ const tableColumns: TableColumnData[] = [
{ title: '在线状态', dataIndex: 'onlineStatus', slotName: 'onlineStatus', width: 100 }, { title: '在线状态', dataIndex: 'onlineStatus', slotName: 'onlineStatus', width: 100 },
{ title: '状态', dataIndex: 'status', slotName: 'status', width: 80 }, { title: '状态', dataIndex: 'status', slotName: 'status', width: 80 },
{ title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true }, { title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true },
{ title: '操作', slotName: 'action', width: 250, fixed: 'right' } { title: '操作', slotName: 'action', width: 250, fixed: 'right' },
] ]
// //
@ -182,7 +182,7 @@ const dataList = ref([
loginCount: 1256, loginCount: 1256,
isOnline: true, isOnline: true,
status: 'active', status: 'active',
remark: '系统超级管理员,拥有所有权限' remark: '系统超级管理员,拥有所有权限',
}, },
{ {
id: 2, id: 2,
@ -197,7 +197,7 @@ const dataList = ref([
loginCount: 892, loginCount: 892,
isOnline: false, isOnline: false,
status: 'active', status: 'active',
remark: '负责项目相关业务管理' remark: '负责项目相关业务管理',
}, },
{ {
id: 3, id: 3,
@ -212,7 +212,7 @@ const dataList = ref([
loginCount: 654, loginCount: 654,
isOnline: true, isOnline: true,
status: 'active', status: 'active',
remark: '负责财务相关业务管理' remark: '负责财务相关业务管理',
}, },
{ {
id: 4, id: 4,
@ -227,8 +227,8 @@ const dataList = ref([
loginCount: 432, loginCount: 432,
isOnline: false, isOnline: false,
status: 'disabled', status: 'disabled',
remark: '技术支持人员,目前停用中' remark: '技术支持人员,目前停用中',
} },
]) ])
const pagination = reactive({ const pagination = reactive({
@ -236,16 +236,16 @@ const pagination = reactive({
pageSize: 10, pageSize: 10,
total: 4, total: 4,
showTotal: true, showTotal: true,
showPageSize: true showPageSize: true,
}) })
// //
const getAdminTypeColor = (type: string) => { const getAdminTypeColor = (type: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'超级管理员': 'red', 超级管理员: 'red',
'系统管理员': 'blue', 系统管理员: 'blue',
'业务管理员': 'green', 业务管理员: 'green',
'财务管理员': 'orange' 财务管理员: 'orange',
} }
return colorMap[type] || 'gray' return colorMap[type] || 'gray'
} }
@ -253,9 +253,9 @@ const getAdminTypeColor = (type: string) => {
// //
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'active': 'green', active: 'green',
'disabled': 'red', disabled: 'red',
'locked': 'orange' locked: 'orange',
} }
return colorMap[status] || 'gray' return colorMap[status] || 'gray'
} }
@ -263,9 +263,9 @@ const getStatusColor = (status: string) => {
// //
const getStatusText = (status: string) => { const getStatusText = (status: string) => {
const textMap: Record<string, string> = { const textMap: Record<string, string> = {
'active': '正常', active: '正常',
'disabled': '禁用', disabled: '禁用',
'locked': '锁定' locked: '锁定',
} }
return textMap[status] || status return textMap[status] || status
} }
@ -285,7 +285,7 @@ const reset = () => {
status: '', status: '',
createTime: '', createTime: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
pagination.current = 1 pagination.current = 1
search() search()

View File

@ -93,7 +93,7 @@
<template #action="{ record }"> <template #action="{ record }">
<a-space> <a-space>
<a-link @click="editBankAccount(record)">编辑</a-link> <a-link @click="editBankAccount(record)">编辑</a-link>
<a-link @click="setDefaultAccount(record)" v-if="!record.isDefault">设为默认</a-link> <a-link v-if="!record.isDefault" @click="setDefaultAccount(record)">设为默认</a-link>
<a-link @click="deleteBankAccount(record)">删除</a-link> <a-link @click="deleteBankAccount(record)">删除</a-link>
</a-space> </a-space>
</template> </template>
@ -122,7 +122,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from 'vue' import { reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
@ -139,7 +139,7 @@ const companyInfo = reactive({
phone: '010-12345678', phone: '010-12345678',
email: 'info@windtech.com', email: 'info@windtech.com',
website: 'https://www.windtech.com', website: 'https://www.windtech.com',
employeeCount: '158人' employeeCount: '158人',
}) })
// //
@ -150,7 +150,7 @@ const certificates = ref([
description: '有效期2023-12-31', description: '有效期2023-12-31',
image: '/api/placeholder/300/200', image: '/api/placeholder/300/200',
issueDate: '2021-01-01', issueDate: '2021-01-01',
expiryDate: '2023-12-31' expiryDate: '2023-12-31',
}, },
{ {
id: 2, id: 2,
@ -158,7 +158,7 @@ const certificates = ref([
description: '有效期2025-06-30', description: '有效期2025-06-30',
image: '/api/placeholder/300/200', image: '/api/placeholder/300/200',
issueDate: '2022-07-01', issueDate: '2022-07-01',
expiryDate: '2025-06-30' expiryDate: '2025-06-30',
}, },
{ {
id: 3, id: 3,
@ -166,8 +166,8 @@ const certificates = ref([
description: '有效期2024-12-31', description: '有效期2024-12-31',
image: '/api/placeholder/300/200', image: '/api/placeholder/300/200',
issueDate: '2021-01-01', issueDate: '2021-01-01',
expiryDate: '2024-12-31' expiryDate: '2024-12-31',
} },
]) ])
// //
@ -178,7 +178,7 @@ const bankAccounts = ref([
accountNumber: '1234567890123456789', accountNumber: '1234567890123456789',
accountName: '风电智能检测技术有限公司', accountName: '风电智能检测技术有限公司',
accountType: '基本户', accountType: '基本户',
isDefault: true isDefault: true,
}, },
{ {
id: 2, id: 2,
@ -186,8 +186,8 @@ const bankAccounts = ref([
accountNumber: '9876543210987654321', accountNumber: '9876543210987654321',
accountName: '风电智能检测技术有限公司', accountName: '风电智能检测技术有限公司',
accountType: '一般户', accountType: '一般户',
isDefault: false isDefault: false,
} },
]) ])
// //
@ -197,7 +197,7 @@ const bankColumns: TableColumnData[] = [
{ title: '账户名称', dataIndex: 'accountName', width: 250 }, { title: '账户名称', dataIndex: 'accountName', width: 250 },
{ title: '账户类型', dataIndex: 'accountType', slotName: 'accountType', width: 120 }, { title: '账户类型', dataIndex: 'accountType', slotName: 'accountType', width: 120 },
{ title: '默认账户', dataIndex: 'isDefault', slotName: 'isDefault', width: 100 }, { title: '默认账户', dataIndex: 'isDefault', slotName: 'isDefault', width: 100 },
{ title: '操作', slotName: 'action', width: 200 } { title: '操作', slotName: 'action', width: 200 },
] ]
// //
@ -205,15 +205,15 @@ const statistics = reactive({
annualRevenue: 12580, annualRevenue: 12580,
totalProjects: 156, totalProjects: 156,
ongoingProjects: 23, ongoingProjects: 23,
clientCount: 68 clientCount: 68,
}) })
// //
const getBankTypeColor = (type: string) => { const getBankTypeColor = (type: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'基本户': 'blue', 基本户: 'blue',
'一般户': 'green', 一般户: 'green',
'专用户': 'orange' 专用户: 'orange',
} }
return colorMap[type] || 'gray' return colorMap[type] || 'gray'
} }

View File

@ -67,20 +67,20 @@
<a-space> <a-space>
<a-link @click="viewTaskDetail(record)">详情</a-link> <a-link @click="viewTaskDetail(record)">详情</a-link>
<a-link <a-link
@click="startTask(record)"
v-if="record.status === 'pending'" v-if="record.status === 'pending'"
@click="startTask(record)"
> >
开始 开始
</a-link> </a-link>
<a-link <a-link
@click="pauseTask(record)"
v-if="record.status === 'running'" v-if="record.status === 'running'"
@click="pauseTask(record)"
> >
暂停 暂停
</a-link> </a-link>
<a-link <a-link
@click="resumeTask(record)"
v-if="record.status === 'paused'" v-if="record.status === 'paused'"
@click="resumeTask(record)"
> >
继续 继续
</a-link> </a-link>
@ -138,7 +138,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from 'vue' import { reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
@ -147,7 +147,7 @@ const migrationStats = reactive({
migratedData: 1256.8, migratedData: 1256.8,
pendingData: 324.2, pendingData: 324.2,
totalTasks: 28, totalTasks: 28,
successRate: 95.2 successRate: 95.2,
}) })
// //
@ -163,7 +163,7 @@ const migrationTasks = ref([
createTime: '2024-03-01 10:00:00', createTime: '2024-03-01 10:00:00',
startTime: '2024-03-01 10:30:00', startTime: '2024-03-01 10:30:00',
endTime: '2024-03-01 12:45:00', endTime: '2024-03-01 12:45:00',
operator: '张管理员' operator: '张管理员',
}, },
{ {
id: 2, id: 2,
@ -176,7 +176,7 @@ const migrationTasks = ref([
createTime: '2024-03-10 09:00:00', createTime: '2024-03-10 09:00:00',
startTime: '2024-03-10 09:30:00', startTime: '2024-03-10 09:30:00',
endTime: '', endTime: '',
operator: '李管理员' operator: '李管理员',
}, },
{ {
id: 3, id: 3,
@ -189,8 +189,8 @@ const migrationTasks = ref([
createTime: '2024-03-15 14:00:00', createTime: '2024-03-15 14:00:00',
startTime: '', startTime: '',
endTime: '', endTime: '',
operator: '王管理员' operator: '王管理员',
} },
]) ])
// //
@ -204,7 +204,7 @@ const dataSources = ref([
database: 'old_crm', database: 'old_crm',
username: 'admin', username: 'admin',
isConnected: true, isConnected: true,
createTime: '2024-02-15 10:00:00' createTime: '2024-02-15 10:00:00',
}, },
{ {
id: 2, id: 2,
@ -215,7 +215,7 @@ const dataSources = ref([
database: 'old_project', database: 'old_project',
username: 'admin', username: 'admin',
isConnected: true, isConnected: true,
createTime: '2024-02-20 11:30:00' createTime: '2024-02-20 11:30:00',
}, },
{ {
id: 3, id: 3,
@ -226,8 +226,8 @@ const dataSources = ref([
database: '/data/files', database: '/data/files',
username: 'fileuser', username: 'fileuser',
isConnected: false, isConnected: false,
createTime: '2024-03-01 09:15:00' createTime: '2024-03-01 09:15:00',
} },
]) ])
// //
@ -240,7 +240,7 @@ const taskColumns: TableColumnData[] = [
{ title: '状态', dataIndex: 'status', slotName: 'status', width: 100 }, { title: '状态', dataIndex: 'status', slotName: 'status', width: 100 },
{ title: '创建时间', dataIndex: 'createTime', width: 160 }, { title: '创建时间', dataIndex: 'createTime', width: 160 },
{ title: '操作人员', dataIndex: 'operator', width: 100 }, { title: '操作人员', dataIndex: 'operator', width: 100 },
{ title: '操作', slotName: 'action', width: 200 } { title: '操作', slotName: 'action', width: 200 },
] ]
// //
@ -253,7 +253,7 @@ const sourceColumns: TableColumnData[] = [
{ title: '用户名', dataIndex: 'username', width: 120 }, { title: '用户名', dataIndex: 'username', width: 120 },
{ title: '连接状态', dataIndex: 'connectionStatus', slotName: 'connectionStatus', width: 120 }, { title: '连接状态', dataIndex: 'connectionStatus', slotName: 'connectionStatus', width: 120 },
{ title: '创建时间', dataIndex: 'createTime', width: 160 }, { title: '创建时间', dataIndex: 'createTime', width: 160 },
{ title: '操作', slotName: 'sourceAction', width: 200 } { title: '操作', slotName: 'sourceAction', width: 200 },
] ]
// //
@ -261,7 +261,7 @@ const taskPagination = reactive({
current: 1, current: 1,
pageSize: 10, pageSize: 10,
total: 3, total: 3,
showTotal: true showTotal: true,
}) })
const tasksLoading = ref(false) const tasksLoading = ref(false)
@ -269,11 +269,11 @@ const tasksLoading = ref(false)
// //
const getTaskStatusColor = (status: string) => { const getTaskStatusColor = (status: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'pending': 'gray', pending: 'gray',
'running': 'blue', running: 'blue',
'paused': 'orange', paused: 'orange',
'completed': 'green', completed: 'green',
'failed': 'red' failed: 'red',
} }
return colorMap[status] || 'gray' return colorMap[status] || 'gray'
} }
@ -281,11 +281,11 @@ const getTaskStatusColor = (status: string) => {
// //
const getTaskStatusText = (status: string) => { const getTaskStatusText = (status: string) => {
const textMap: Record<string, string> = { const textMap: Record<string, string> = {
'pending': '待开始', pending: '待开始',
'running': '运行中', running: '运行中',
'paused': '已暂停', paused: '已暂停',
'completed': '已完成', completed: '已完成',
'failed': '失败' failed: '失败',
} }
return textMap[status] || status return textMap[status] || status
} }
@ -301,11 +301,11 @@ const getProgressColor = (progress: number) => {
// //
const getSourceTypeColor = (type: string) => { const getSourceTypeColor = (type: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'MySQL': 'blue', MySQL: 'blue',
'PostgreSQL': 'green', PostgreSQL: 'green',
'Oracle': 'orange', Oracle: 'orange',
'FTP': 'purple', FTP: 'purple',
'SFTP': 'cyan' SFTP: 'cyan',
} }
return colorMap[type] || 'gray' return colorMap[type] || 'gray'
} }

View File

@ -85,8 +85,8 @@
<a-link @click="viewVersionDetail(record)">详情</a-link> <a-link @click="viewVersionDetail(record)">详情</a-link>
<a-link @click="downloadVersion(record)">下载</a-link> <a-link @click="downloadVersion(record)">下载</a-link>
<a-link <a-link
@click="rollbackVersion(record)"
v-if="record.status === 'installed' && record.id !== currentVersionId" v-if="record.status === 'installed' && record.id !== currentVersionId"
@click="rollbackVersion(record)"
> >
回滚 回滚
</a-link> </a-link>
@ -150,7 +150,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from 'vue' import { reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
@ -162,7 +162,7 @@ const latestVersion = ref({
version: 'v2.4.0', version: 'v2.4.0',
description: '新增项目管理模块优化系统性能修复已知bug', description: '新增项目管理模块优化系统性能修复已知bug',
releaseDate: '2024-03-20', releaseDate: '2024-03-20',
size: '156.8 MB' size: '156.8 MB',
}) })
// //
@ -175,7 +175,7 @@ const versionHistory = ref([
installDate: '2023-12-20', installDate: '2023-12-20',
status: 'archived', status: 'archived',
description: '初始版本发布,包含基础功能模块', description: '初始版本发布,包含基础功能模块',
size: '128.5 MB' size: '128.5 MB',
}, },
{ {
id: 2, id: 2,
@ -185,7 +185,7 @@ const versionHistory = ref([
installDate: '2024-01-20', installDate: '2024-01-20',
status: 'archived', status: 'archived',
description: '新增用户管理和权限控制功能', description: '新增用户管理和权限控制功能',
size: '142.3 MB' size: '142.3 MB',
}, },
{ {
id: 3, id: 3,
@ -195,7 +195,7 @@ const versionHistory = ref([
installDate: '2024-02-20', installDate: '2024-02-20',
status: 'current', status: 'current',
description: '修复安全漏洞,优化界面交互', description: '修复安全漏洞,优化界面交互',
size: '145.7 MB' size: '145.7 MB',
}, },
{ {
id: 4, id: 4,
@ -205,8 +205,8 @@ const versionHistory = ref([
installDate: '', installDate: '',
status: 'available', status: 'available',
description: '新增项目管理模块,优化系统性能', description: '新增项目管理模块,优化系统性能',
size: '156.8 MB' size: '156.8 MB',
} },
]) ])
// //
@ -218,7 +218,7 @@ const versionColumns: TableColumnData[] = [
{ title: '状态', dataIndex: 'status', slotName: 'status', width: 100 }, { title: '状态', dataIndex: 'status', slotName: 'status', width: 100 },
{ title: '文件大小', dataIndex: 'size', width: 100 }, { title: '文件大小', dataIndex: 'size', width: 100 },
{ title: '描述', dataIndex: 'description', width: 300, ellipsis: true, tooltip: true }, { title: '描述', dataIndex: 'description', width: 300, ellipsis: true, tooltip: true },
{ title: '操作', slotName: 'action', width: 200 } { title: '操作', slotName: 'action', width: 200 },
] ]
// //
@ -226,16 +226,16 @@ const upgradeConfig = reactive({
autoCheck: true, autoCheck: true,
checkFrequency: 'weekly', checkFrequency: 'weekly',
notificationMethods: ['email', 'system'], notificationMethods: ['email', 'system'],
maintenanceWindow: ['02:00', '06:00'] maintenanceWindow: ['02:00', '06:00'],
}) })
// //
const getVersionStatusColor = (status: string) => { const getVersionStatusColor = (status: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'current': 'green', current: 'green',
'available': 'blue', available: 'blue',
'archived': 'gray', archived: 'gray',
'deprecated': 'red' deprecated: 'red',
} }
return colorMap[status] || 'gray' return colorMap[status] || 'gray'
} }
@ -243,10 +243,10 @@ const getVersionStatusColor = (status: string) => {
// //
const getVersionStatusText = (status: string) => { const getVersionStatusText = (status: string) => {
const textMap: Record<string, string> = { const textMap: Record<string, string> = {
'current': '当前版本', current: '当前版本',
'available': '可升级', available: '可升级',
'archived': '已归档', archived: '已归档',
'deprecated': '已弃用' deprecated: '已弃用',
} }
return textMap[status] || status return textMap[status] || status
} }
@ -254,10 +254,10 @@ const getVersionStatusText = (status: string) => {
// //
const getVersionTypeColor = (type: string) => { const getVersionTypeColor = (type: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'主要版本': 'red', 主要版本: 'red',
'功能版本': 'blue', 功能版本: 'blue',
'补丁版本': 'green', 补丁版本: 'green',
'热修复': 'orange' 热修复: 'orange',
} }
return colorMap[type] || 'gray' return colorMap[type] || 'gray'
} }
@ -303,7 +303,7 @@ const resetUpgradeConfig = () => {
autoCheck: true, autoCheck: true,
checkFrequency: 'weekly', checkFrequency: 'weekly',
notificationMethods: ['email', 'system'], notificationMethods: ['email', 'system'],
maintenanceWindow: ['02:00', '06:00'] maintenanceWindow: ['02:00', '06:00'],
}) })
Message.success('升级配置已重置') Message.success('升级配置已重置')
} }

View File

@ -49,18 +49,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
// //
let searchForm = reactive({ const searchForm = reactive({
userName: '', userName: '',
deptName: '', deptName: '',
status: '', status: '',
attendanceDate: '', attendanceDate: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
// //
@ -70,16 +70,16 @@ const queryFormColumns = [
label: '员工姓名', label: '员工姓名',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入员工姓名' placeholder: '请输入员工姓名',
} },
}, },
{ {
field: 'deptName', field: 'deptName',
label: '部门', label: '部门',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入部门名称' placeholder: '请输入部门名称',
} },
}, },
{ {
field: 'status', field: 'status',
@ -92,10 +92,10 @@ const queryFormColumns = [
{ label: '迟到', value: 'late' }, { label: '迟到', value: 'late' },
{ label: '早退', value: 'early' }, { label: '早退', value: 'early' },
{ label: '缺勤', value: 'absent' }, { label: '缺勤', value: 'absent' },
{ label: '请假', value: 'leave' } { label: '请假', value: 'leave' },
] ],
} },
} },
] ]
// //
@ -109,7 +109,7 @@ const tableColumns: TableColumnData[] = [
{ title: '工作时长', dataIndex: 'workHours', width: 100 }, { title: '工作时长', dataIndex: 'workHours', width: 100 },
{ title: '考勤状态', dataIndex: 'status', slotName: 'status', width: 100 }, { title: '考勤状态', dataIndex: 'status', slotName: 'status', width: 100 },
{ title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true }, { title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true },
{ title: '操作', slotName: 'action', width: 120, fixed: 'right' } { title: '操作', slotName: 'action', width: 120, fixed: 'right' },
] ]
// //
@ -125,7 +125,7 @@ const dataList = ref([
endTime: '18:00', endTime: '18:00',
workHours: '8.0小时', workHours: '8.0小时',
status: 'normal', status: 'normal',
remark: '' remark: '',
}, },
{ {
id: 2, id: 2,
@ -137,7 +137,7 @@ const dataList = ref([
endTime: '18:00', endTime: '18:00',
workHours: '7.75小时', workHours: '7.75小时',
status: 'late', status: 'late',
remark: '迟到15分钟' remark: '迟到15分钟',
}, },
{ {
id: 3, id: 3,
@ -149,8 +149,8 @@ const dataList = ref([
endTime: '17:30', endTime: '17:30',
workHours: '7.5小时', workHours: '7.5小时',
status: 'early', status: 'early',
remark: '早退30分钟' remark: '早退30分钟',
} },
]) ])
const pagination = reactive({ const pagination = reactive({
@ -158,17 +158,17 @@ const pagination = reactive({
pageSize: 10, pageSize: 10,
total: 3, total: 3,
showTotal: true, showTotal: true,
showPageSize: true showPageSize: true,
}) })
// //
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'normal': 'green', normal: 'green',
'late': 'orange', late: 'orange',
'early': 'blue', early: 'blue',
'absent': 'red', absent: 'red',
'leave': 'gray' leave: 'gray',
} }
return colorMap[status] || 'gray' return colorMap[status] || 'gray'
} }
@ -176,11 +176,11 @@ const getStatusColor = (status: string) => {
// //
const getStatusText = (status: string) => { const getStatusText = (status: string) => {
const textMap: Record<string, string> = { const textMap: Record<string, string> = {
'normal': '正常', normal: '正常',
'late': '迟到', late: '迟到',
'early': '早退', early: '早退',
'absent': '缺勤', absent: '缺勤',
'leave': '请假' leave: '请假',
} }
return textMap[status] || status return textMap[status] || status
} }
@ -201,7 +201,7 @@ const reset = () => {
status: '', status: '',
attendanceDate: '', attendanceDate: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
pagination.current = 1 pagination.current = 1
search() search()

View File

@ -99,17 +99,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
// //
let searchForm = reactive({ const searchForm = reactive({
userName: '', userName: '',
deptName: '', deptName: '',
pointLevel: '', pointLevel: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
// //
@ -119,16 +119,16 @@ const queryFormColumns = [
label: '员工姓名', label: '员工姓名',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入员工姓名' placeholder: '请输入员工姓名',
} },
}, },
{ {
field: 'deptName', field: 'deptName',
label: '部门', label: '部门',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入部门名称' placeholder: '请输入部门名称',
} },
}, },
{ {
field: 'pointLevel', field: 'pointLevel',
@ -141,10 +141,10 @@ const queryFormColumns = [
{ label: '熟练', value: '熟练' }, { label: '熟练', value: '熟练' },
{ label: '专家', value: '专家' }, { label: '专家', value: '专家' },
{ label: '大师', value: '大师' }, { label: '大师', value: '大师' },
{ label: '传奇', value: '传奇' } { label: '传奇', value: '传奇' },
] ],
} },
} },
] ]
// //
@ -160,7 +160,7 @@ const tableColumns: TableColumnData[] = [
{ title: '累计获得', dataIndex: 'totalGain', width: 100 }, { title: '累计获得', dataIndex: 'totalGain', width: 100 },
{ title: '累计扣除', dataIndex: 'totalDeduct', width: 100 }, { title: '累计扣除', dataIndex: 'totalDeduct', width: 100 },
{ title: '最后更新', dataIndex: 'lastUpdate', width: 160 }, { title: '最后更新', dataIndex: 'lastUpdate', width: 160 },
{ title: '操作', slotName: 'action', width: 150, fixed: 'right' } { title: '操作', slotName: 'action', width: 150, fixed: 'right' },
] ]
// //
@ -178,7 +178,7 @@ const dataList = ref([
monthDeduct: 2, monthDeduct: 2,
totalGain: 320, totalGain: 320,
totalDeduct: 35, totalDeduct: 35,
lastUpdate: '2024-01-15 16:30:00' lastUpdate: '2024-01-15 16:30:00',
}, },
{ {
id: 2, id: 2,
@ -192,7 +192,7 @@ const dataList = ref([
monthDeduct: 0, monthDeduct: 0,
totalGain: 156, totalGain: 156,
totalDeduct: 0, totalDeduct: 0,
lastUpdate: '2024-01-14 14:20:00' lastUpdate: '2024-01-14 14:20:00',
}, },
{ {
id: 3, id: 3,
@ -206,7 +206,7 @@ const dataList = ref([
monthDeduct: 5, monthDeduct: 5,
totalGain: 95, totalGain: 95,
totalDeduct: 17, totalDeduct: 17,
lastUpdate: '2024-01-13 10:15:00' lastUpdate: '2024-01-13 10:15:00',
}, },
{ {
id: 4, id: 4,
@ -220,8 +220,8 @@ const dataList = ref([
monthDeduct: 0, monthDeduct: 0,
totalGain: 350, totalGain: 350,
totalDeduct: 0, totalDeduct: 0,
lastUpdate: '2024-01-15 18:00:00' lastUpdate: '2024-01-15 18:00:00',
} },
]) ])
const pagination = reactive({ const pagination = reactive({
@ -229,17 +229,17 @@ const pagination = reactive({
pageSize: 10, pageSize: 10,
total: 4, total: 4,
showTotal: true, showTotal: true,
showPageSize: true showPageSize: true,
}) })
// //
const getLevelColor = (level: string) => { const getLevelColor = (level: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'新手': 'red', 新手: 'red',
'熟练': 'orange', 熟练: 'orange',
'专家': 'blue', 专家: 'blue',
'大师': 'green', 大师: 'green',
'传奇': 'purple' 传奇: 'purple',
} }
return colorMap[level] || 'gray' return colorMap[level] || 'gray'
} }
@ -259,7 +259,7 @@ const reset = () => {
deptName: '', deptName: '',
pointLevel: '', pointLevel: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
pagination.current = 1 pagination.current = 1
search() search()

View File

@ -56,18 +56,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
// //
let searchForm = reactive({ const searchForm = reactive({
userName: '', userName: '',
deptName: '', deptName: '',
performanceLevel: '', performanceLevel: '',
assessmentPeriod: '', assessmentPeriod: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
// //
@ -77,16 +77,16 @@ const queryFormColumns = [
label: '员工姓名', label: '员工姓名',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入员工姓名' placeholder: '请输入员工姓名',
} },
}, },
{ {
field: 'deptName', field: 'deptName',
label: '部门', label: '部门',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入部门名称' placeholder: '请输入部门名称',
} },
}, },
{ {
field: 'performanceLevel', field: 'performanceLevel',
@ -98,10 +98,10 @@ const queryFormColumns = [
{ label: '优秀', value: '优秀' }, { label: '优秀', value: '优秀' },
{ label: '良好', value: '良好' }, { label: '良好', value: '良好' },
{ label: '一般', value: '一般' }, { label: '一般', value: '一般' },
{ label: '待改进', value: '待改进' } { label: '待改进', value: '待改进' },
] ],
} },
} },
] ]
// //
@ -118,7 +118,7 @@ const tableColumns: TableColumnData[] = [
{ title: '创新能力', dataIndex: 'innovation', width: 100 }, { title: '创新能力', dataIndex: 'innovation', width: 100 },
{ title: '评估人', dataIndex: 'assessor', width: 120 }, { title: '评估人', dataIndex: 'assessor', width: 120 },
{ title: '评估时间', dataIndex: 'assessmentTime', width: 160 }, { title: '评估时间', dataIndex: 'assessmentTime', width: 160 },
{ title: '操作', slotName: 'action', width: 120, fixed: 'right' } { title: '操作', slotName: 'action', width: 120, fixed: 'right' },
] ]
// //
@ -137,7 +137,7 @@ const dataList = ref([
teamwork: '90分', teamwork: '90分',
innovation: '88分', innovation: '88分',
assessor: '李经理', assessor: '李经理',
assessmentTime: '2024-04-01 14:30:00' assessmentTime: '2024-04-01 14:30:00',
}, },
{ {
id: 2, id: 2,
@ -152,7 +152,7 @@ const dataList = ref([
teamwork: '85分', teamwork: '85分',
innovation: '82分', innovation: '82分',
assessor: '李经理', assessor: '李经理',
assessmentTime: '2024-04-01 15:00:00' assessmentTime: '2024-04-01 15:00:00',
}, },
{ {
id: 3, id: 3,
@ -167,8 +167,8 @@ const dataList = ref([
teamwork: '80分', teamwork: '80分',
innovation: '76分', innovation: '76分',
assessor: '张经理', assessor: '张经理',
assessmentTime: '2024-04-02 10:00:00' assessmentTime: '2024-04-02 10:00:00',
} },
]) ])
const pagination = reactive({ const pagination = reactive({
@ -176,16 +176,16 @@ const pagination = reactive({
pageSize: 10, pageSize: 10,
total: 3, total: 3,
showTotal: true, showTotal: true,
showPageSize: true showPageSize: true,
}) })
// //
const getLevelColor = (level: string) => { const getLevelColor = (level: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'优秀': 'green', 优秀: 'green',
'良好': 'blue', 良好: 'blue',
'一般': 'orange', 一般: 'orange',
'待改进': 'red' 待改进: 'red',
} }
return colorMap[level] || 'gray' return colorMap[level] || 'gray'
} }
@ -214,7 +214,7 @@ const reset = () => {
performanceLevel: '', performanceLevel: '',
assessmentPeriod: '', assessmentPeriod: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
pagination.current = 1 pagination.current = 1
search() search()

View File

@ -5,7 +5,7 @@
<h2 class="page-title">人员资质管理</h2> <h2 class="page-title">人员资质管理</h2>
<a-button type="primary" @click="showAddModal"> <a-button type="primary" @click="showAddModal">
<template #icon> <template #icon>
<icon-plus /> <IconPlus />
</template> </template>
新增资质 新增资质
</a-button> </a-button>
@ -30,13 +30,13 @@
<a-space> <a-space>
<a-button type="primary" @click="handleSearch"> <a-button type="primary" @click="handleSearch">
<template #icon> <template #icon>
<icon-search /> <IconSearch />
</template> </template>
搜索 搜索
</a-button> </a-button>
<a-button @click="handleReset"> <a-button @click="handleReset">
<template #icon> <template #icon>
<icon-refresh /> <IconRefresh />
</template> </template>
重置 重置
</a-button> </a-button>
@ -47,15 +47,19 @@
<!-- 人员资质表格 --> <!-- 人员资质表格 -->
<a-card class="table-card" :bordered="false"> <a-card class="table-card" :bordered="false">
<a-table :columns="columns" :data="certificationList" :pagination="paginationConfig" :loading="loading" <a-table
row-key="certificationId" @page-change="handlePageChange"> :columns="columns" :data="certificationList" :pagination="paginationConfig" :loading="loading"
row-key="certificationId" @page-change="handlePageChange"
>
<template #userName="{ record }"> <template #userName="{ record }">
<span>{{ getUserName(record.userId) }}</span> <span>{{ getUserName(record.userId) }}</span>
</template> </template>
<template #certificationImage="{ record }"> <template #certificationImage="{ record }">
<a-image v-if="record.certificationImage" :src="record.certificationImage" width="60" height="40" <a-image
fit="cover" show-loader preview /> v-if="record.certificationImage" :src="record.certificationImage" width="60" height="40"
fit="cover" show-loader preview
/>
<span v-else>-</span> <span v-else>-</span>
</template> </template>
@ -77,8 +81,10 @@
</a-card> </a-card>
<!-- 新增/编辑资质信息模态框 --> <!-- 新增/编辑资质信息模态框 -->
<a-modal v-model:visible="modalVisible" :title="isEdit ? '编辑资质信息' : '新增资质信息'" width="700px" @ok="handleSubmit" <a-modal
@cancel="handleCancel"> v-model:visible="modalVisible" :title="isEdit ? '编辑资质信息' : '新增资质信息'" width="700px" @ok="handleSubmit"
@cancel="handleCancel"
>
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical"> <a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="12"> <a-col :span="12">
@ -96,8 +102,10 @@
<a-form-item label="证书类型" field="certificationType"> <a-form-item label="证书类型" field="certificationType">
<a-select v-model="formData.certificationType" placeholder="请选择证书类型"> <a-select v-model="formData.certificationType" placeholder="请选择证书类型">
<a-option v-for="item in CERTIFICATION_TYPE_OPTIONS" :key="item.value" :value="item.value" <a-option
:label="item.label"> v-for="item in CERTIFICATION_TYPE_OPTIONS" :key="item.value" :value="item.value"
:label="item.label"
>
{{ item.label }} {{ item.label }}
</a-option> </a-option>
</a-select> </a-select>
@ -105,8 +113,10 @@
<a-col :span="12"> <a-col :span="12">
<a-form-item label="持证人" field="userId"> <a-form-item label="持证人" field="userId">
<a-select v-model="formData.userId" placeholder="请选择持证人" :loading="loadingUsers" allow-search <a-select
:filter-option="filterUserOption" @="console.log('d', formData)"> v-model="formData.userId" placeholder="请选择持证人" :loading="loadingUsers" allow-search
:filter-option="filterUserOption" @="console.log('d', formData)"
>
<a-option v-for="user in userList" :key="user.userId" :value="user.userId" :label="user.name"> <a-option v-for="user in userList" :key="user.userId" :value="user.userId" :label="user.name">
{{ user.name }}({{ user.account }}) {{ user.name }}({{ user.account }})
</a-option> </a-option>
@ -129,14 +139,18 @@
</a-row> </a-row>
<a-form-item label="证书图片" field="certificationImage"> <a-form-item label="证书图片" field="certificationImage">
<a-upload :custom-request="handleUpload" :show-file-list="false" accept="image/*" <a-upload
:before-upload="beforeUpload"> :custom-request="handleUpload" :show-file-list="false" accept="image/*"
:before-upload="beforeUpload"
>
<template #upload-button> <template #upload-button>
<div class="upload-wrapper"> <div class="upload-wrapper">
<a-image v-if="formData.certificationImage" :src="formData.certificationImage" width="200" <a-image
height="120" fit="cover" show-loader preview /> v-if="formData.certificationImage" :src="formData.certificationImage" width="200"
height="120" fit="cover" show-loader preview
/>
<div v-else class="upload-placeholder"> <div v-else class="upload-placeholder">
<icon-plus /> <IconPlus />
<div>点击上传证书图片</div> <div>点击上传证书图片</div>
</div> </div>
</div> </div>
@ -150,9 +164,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message, Modal } from '@arco-design/web-vue' import { Message, Modal } from '@arco-design/web-vue'
import { IconPlus, IconSearch, IconRefresh } from '@arco-design/web-vue/es/icon' import { IconPlus, IconRefresh, IconSearch } from '@arco-design/web-vue/es/icon'
import * as CertificationAPI from '@/apis/employee' import * as CertificationAPI from '@/apis/employee'
import type { CertificationInfo, CertificationListParams, SimpleUserInfo } from '@/apis/employee' import type { CertificationInfo, CertificationListParams, SimpleUserInfo } from '@/apis/employee'
import { uploadFile } from '@/apis/common/common' import { uploadFile } from '@/apis/common/common'
@ -174,7 +188,7 @@ const userList = ref<SimpleUserInfo[]>([])
const searchForm = reactive<CertificationListParams>({ const searchForm = reactive<CertificationListParams>({
certificationName: '', certificationName: '',
certificationType: '', certificationType: '',
userName: '' userName: '',
}) })
// //
@ -183,7 +197,7 @@ const paginationConfig = reactive({
pageSize: 10, pageSize: 10,
total: 0, total: 0,
showTotal: true, showTotal: true,
showJumper: true showJumper: true,
}) })
// //
@ -201,23 +215,23 @@ const formData = reactive<CertificationInfo>({
// //
const formRules = { const formRules = {
certificationCode: [ certificationCode: [
{ required: true, message: '请输入证书编号' } { required: true, message: '请输入证书编号' },
], ],
certificationName: [ certificationName: [
{ required: true, message: '请输入证书名称' } { required: true, message: '请输入证书名称' },
], ],
certificationType: [ certificationType: [
{ required: true, message: '请输入证书类型' } { required: true, message: '请输入证书类型' },
], ],
userId: [ userId: [
{ required: true, message: '请选择持证人' } { required: true, message: '请选择持证人' },
], ],
validityDateBegin: [ validityDateBegin: [
{ required: true, message: '请选择有效期开始日期' } { required: true, message: '请选择有效期开始日期' },
], ],
validityDateEnd: [ validityDateEnd: [
{ required: true, message: '请选择有效期结束日期' } { required: true, message: '请选择有效期结束日期' },
] ],
} }
const CERTIFICATION_TYPE_OPTIONS = [ const CERTIFICATION_TYPE_OPTIONS = [
{ label: '高处作业', value: 'height-operation' }, { label: '高处作业', value: 'height-operation' },
@ -226,69 +240,69 @@ const CERTIFICATION_TYPE_OPTIONS = [
{ label: '海上交通安全', value: 'maritime-traffic-safety' }, { label: '海上交通安全', value: 'maritime-traffic-safety' },
{ label: '驾驶证', value: 'driving-license' }, { label: '驾驶证', value: 'driving-license' },
{ label: '防雷检测', value: 'lightning-protection-detection' }, { label: '防雷检测', value: 'lightning-protection-detection' },
{ label: '无人机驾驶', value: 'drone-driving' } { label: '无人机驾驶', value: 'drone-driving' },
] ]
// //
const columns = [ const columns = [
{ {
title: '证书编号', title: '证书编号',
dataIndex: 'certificationCode', dataIndex: 'certificationCode',
width: 150 width: 150,
}, },
{ {
title: '证书名称', title: '证书名称',
dataIndex: 'certificationName', dataIndex: 'certificationName',
width: 200 width: 200,
}, },
{ {
title: '证书类型', title: '证书类型',
dataIndex: 'certificationType', dataIndex: 'certificationType',
width: 150, width: 150,
render: ({ record }) => { render: ({ record }) => {
const type = CERTIFICATION_TYPE_OPTIONS.find(item => item.value === record.certificationType) const type = CERTIFICATION_TYPE_OPTIONS.find((item) => item.value === record.certificationType)
return type ? type.label : '-' return type ? type.label : '-'
} },
}, },
{ {
title: '持证人', title: '持证人',
dataIndex: 'userName', dataIndex: 'userName',
slotName: 'userName', slotName: 'userName',
width: 120 width: 120,
}, },
{ {
title: '证书图片', title: '证书图片',
dataIndex: 'certificationImage', dataIndex: 'certificationImage',
slotName: 'certificationImage', slotName: 'certificationImage',
width: 100 width: 100,
}, },
{ {
title: '有效期', title: '有效期',
dataIndex: 'validityPeriod', dataIndex: 'validityPeriod',
slotName: 'validityPeriod', slotName: 'validityPeriod',
width: 200 width: 200,
}, },
{ {
title: '操作', title: '操作',
slotName: 'actions', slotName: 'actions',
width: 150, width: 150,
fixed: 'right' fixed: 'right',
} },
] ]
// //
const getUserName = (userId: string) => { const getUserName = (userId: string) => {
const user = userList.value.find(u => u.userId === userId) const user = userList.value.find((u) => u.userId === userId)
return user ? user.name : '-' return user ? user.name : '-'
} }
// //
const filterUserOption = (inputValue: string, option: any) => { const filterUserOption = (inputValue: string, option: any) => {
const user = userList.value.find(u => u.userId === option.value) const user = userList.value.find((u) => u.userId === option.value)
if (!user) return false if (!user) return false
const searchText = inputValue.toLowerCase() const searchText = inputValue.toLowerCase()
return user.name.toLowerCase().includes(searchText) || return user.name.toLowerCase().includes(searchText)
user.account.toLowerCase().includes(searchText) || user.account.toLowerCase().includes(searchText)
} }
// //
@ -300,7 +314,7 @@ const getUserList = async () => {
console.log('用户列表响应:', response) console.log('用户列表响应:', response)
if (response.data) { if (response.data) {
userList.value = response.data.map(item => ({ userList.value = response.data.map((item) => ({
userId: item.userId, userId: item.userId,
name: item.name, name: item.name,
account: item.account, account: item.account,
@ -387,7 +401,7 @@ const handleReset = () => {
Object.assign(searchForm, { Object.assign(searchForm, {
certificationName: '', certificationName: '',
certificationType: '', certificationType: '',
userName: '' userName: '',
}) })
paginationConfig.current = 1 paginationConfig.current = 1
getCertificationList() getCertificationList()
@ -429,7 +443,7 @@ const deleteRecord = (record: CertificationInfo) => {
console.error('删除失败:', error) console.error('删除失败:', error)
Message.error('删除失败') Message.error('删除失败')
} }
} },
}) })
} }
@ -471,7 +485,7 @@ const resetForm = () => {
certificationType: '', certificationType: '',
userId: '', userId: '',
validityDateBegin: '', validityDateBegin: '',
validityDateEnd: '' validityDateEnd: '',
}) })
formRef.value?.resetFields() formRef.value?.resetFields()
} }
@ -480,7 +494,7 @@ const resetForm = () => {
const init = async () => { const init = async () => {
await Promise.all([ await Promise.all([
getUserList(), getUserList(),
getCertificationList() getCertificationList(),
]) ])
} }

View File

@ -58,7 +58,7 @@
<a-space> <a-space>
<a-link @click="viewDetail(record)">详情</a-link> <a-link @click="viewDetail(record)">详情</a-link>
<a-link @click="editRecord(record)">编辑</a-link> <a-link @click="editRecord(record)">编辑</a-link>
<a-link @click="confirmSalary(record)" v-if="record.status === 'draft'">确认</a-link> <a-link v-if="record.status === 'draft'" @click="confirmSalary(record)">确认</a-link>
</a-space> </a-space>
</template> </template>
</GiTable> </GiTable>
@ -66,18 +66,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
// //
let searchForm = reactive({ const searchForm = reactive({
userName: '', userName: '',
deptName: '', deptName: '',
salaryMonth: '', salaryMonth: '',
status: '', status: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
// //
@ -87,16 +87,16 @@ const queryFormColumns = [
label: '员工姓名', label: '员工姓名',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入员工姓名' placeholder: '请输入员工姓名',
} },
}, },
{ {
field: 'deptName', field: 'deptName',
label: '部门', label: '部门',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入部门名称' placeholder: '请输入部门名称',
} },
}, },
{ {
field: 'status', field: 'status',
@ -107,10 +107,10 @@ const queryFormColumns = [
options: [ options: [
{ label: '草稿', value: 'draft' }, { label: '草稿', value: 'draft' },
{ label: '已确认', value: 'confirmed' }, { label: '已确认', value: 'confirmed' },
{ label: '已发放', value: 'paid' } { label: '已发放', value: 'paid' },
] ],
} },
} },
] ]
// //
@ -132,7 +132,7 @@ const tableColumns: TableColumnData[] = [
{ title: '其他扣除', dataIndex: 'otherDeduction', width: 100 }, { title: '其他扣除', dataIndex: 'otherDeduction', width: 100 },
{ title: '实发工资', dataIndex: 'netSalary', slotName: 'netSalary', width: 120 }, { title: '实发工资', dataIndex: 'netSalary', slotName: 'netSalary', width: 120 },
{ title: '状态', dataIndex: 'status', slotName: 'status', width: 100 }, { title: '状态', dataIndex: 'status', slotName: 'status', width: 100 },
{ title: '操作', slotName: 'action', width: 150, fixed: 'right' } { title: '操作', slotName: 'action', width: 150, fixed: 'right' },
] ]
// //
@ -156,7 +156,7 @@ const dataList = ref([
housingFund: 960, housingFund: 960,
otherDeduction: 0, otherDeduction: 0,
netSalary: 14895, netSalary: 14895,
status: 'confirmed' status: 'confirmed',
}, },
{ {
id: 2, id: 2,
@ -176,7 +176,7 @@ const dataList = ref([
housingFund: 880, housingFund: 880,
otherDeduction: 0, otherDeduction: 0,
netSalary: 13245, netSalary: 13245,
status: 'paid' status: 'paid',
}, },
{ {
id: 3, id: 3,
@ -196,8 +196,8 @@ const dataList = ref([
housingFund: 640, housingFund: 640,
otherDeduction: 0, otherDeduction: 0,
netSalary: 9215, netSalary: 9215,
status: 'draft' status: 'draft',
} },
]) ])
const pagination = reactive({ const pagination = reactive({
@ -205,15 +205,15 @@ const pagination = reactive({
pageSize: 10, pageSize: 10,
total: 3, total: 3,
showTotal: true, showTotal: true,
showPageSize: true showPageSize: true,
}) })
// //
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'draft': 'orange', draft: 'orange',
'confirmed': 'blue', confirmed: 'blue',
'paid': 'green' paid: 'green',
} }
return colorMap[status] || 'gray' return colorMap[status] || 'gray'
} }
@ -221,9 +221,9 @@ const getStatusColor = (status: string) => {
// //
const getStatusText = (status: string) => { const getStatusText = (status: string) => {
const textMap: Record<string, string> = { const textMap: Record<string, string> = {
'draft': '草稿', draft: '草稿',
'confirmed': '已确认', confirmed: '已确认',
'paid': '已发放' paid: '已发放',
} }
return textMap[status] || status return textMap[status] || status
} }
@ -244,7 +244,7 @@ const reset = () => {
salaryMonth: '', salaryMonth: '',
status: '', status: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
pagination.current = 1 pagination.current = 1
search() search()

View File

@ -48,7 +48,7 @@
title="保险详情" title="保险详情"
width="800px" width="800px"
> >
<div class="detail-content" v-if="selectedRecord"> <div v-if="selectedRecord" class="detail-content">
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">保险公司</span> <span class="detail-label">保险公司</span>
<span class="detail-value">{{ selectedRecord.company }}</span> <span class="detail-value">{{ selectedRecord.company }}</span>
@ -106,7 +106,7 @@ const insuranceColumns = [
{ title: '生效日期', dataIndex: 'startDate', width: 120 }, { title: '生效日期', dataIndex: 'startDate', width: 120 },
{ title: '到期日期', dataIndex: 'endDate', width: 120 }, { title: '到期日期', dataIndex: 'endDate', width: 120 },
{ title: '状态', dataIndex: 'status', slotName: 'status', width: 100 }, { title: '状态', dataIndex: 'status', slotName: 'status', width: 100 },
{ title: '操作', slotName: 'action', width: 120 } { title: '操作', slotName: 'action', width: 120 },
] ]
// //
@ -122,7 +122,7 @@ const insuranceData = ref([
amount: '50万元', amount: '50万元',
coverage: '住院医疗、门诊医疗、重大疾病', coverage: '住院医疗、门诊医疗、重大疾病',
beneficiary: '法定继承人', beneficiary: '法定继承人',
remark: '企业统一购买,覆盖基本医疗保险' remark: '企业统一购买,覆盖基本医疗保险',
}, },
{ {
id: 2, id: 2,
@ -135,7 +135,7 @@ const insuranceData = ref([
amount: '100万元', amount: '100万元',
coverage: '意外伤害、意外医疗、交通意外', coverage: '意外伤害、意外医疗、交通意外',
beneficiary: '法定继承人', beneficiary: '法定继承人',
remark: '个人购买,工作期间意外保障' remark: '个人购买,工作期间意外保障',
}, },
{ {
id: 3, id: 3,
@ -148,8 +148,8 @@ const insuranceData = ref([
amount: '200万元', amount: '200万元',
coverage: '养老金、年金给付', coverage: '养老金、年金给付',
beneficiary: '法定继承人', beneficiary: '法定继承人',
remark: '企业年金计划,长期养老保障' remark: '企业年金计划,长期养老保障',
} },
]) ])
// //

View File

@ -93,7 +93,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from 'vue' import { reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
// //
@ -102,7 +102,7 @@ const insuranceStats = reactive({
lastCheckup: '2023-10-15', lastCheckup: '2023-10-15',
expiringCount: 1, expiringCount: 1,
healthScore: 86, healthScore: 86,
healthTrend: '2%' healthTrend: '2%',
}) })
// //
@ -114,7 +114,7 @@ const reminderColumns = [
{ title: '事项', dataIndex: 'item', width: 200 }, { title: '事项', dataIndex: 'item', width: 200 },
{ title: '日期', dataIndex: 'date', width: 150 }, { title: '日期', dataIndex: 'date', width: 150 },
{ title: '状态', dataIndex: 'status', slotName: 'status', width: 120 }, { title: '状态', dataIndex: 'status', slotName: 'status', width: 120 },
{ title: '操作', slotName: 'action', width: 150 } { title: '操作', slotName: 'action', width: 150 },
] ]
// //
@ -124,22 +124,22 @@ const reminderData = ref([
item: '医疗保险续保', item: '医疗保险续保',
date: '2023-12-15', date: '2023-12-15',
status: 'pending', status: 'pending',
actionText: '详情' actionText: '详情',
}, },
{ {
id: 2, id: 2,
item: '年度体检', item: '年度体检',
date: '2024-04-15', date: '2024-04-15',
status: 'completed', status: 'completed',
actionText: '详情' actionText: '详情',
}, },
{ {
id: 3, id: 3,
item: '健康问卷', item: '健康问卷',
date: '2023-12-01', date: '2023-12-01',
status: 'completed', status: 'completed',
actionText: '填写' actionText: '填写',
} },
]) ])
// //

View File

@ -5,7 +5,7 @@
<div class="page-header"> <div class="page-header">
<h2 class="page-title">个人信息</h2> <h2 class="page-title">个人信息</h2>
<a-button type="primary" @click="handleEdit"> <a-button type="primary" @click="handleEdit">
<template #icon><icon-edit /></template> <template #icon><IconEdit /></template>
编辑信息 编辑信息
</a-button> </a-button>
</div> </div>
@ -190,7 +190,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from 'vue' import { reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import { IconEdit } from '@arco-design/web-vue/es/icon' import { IconEdit } from '@arco-design/web-vue/es/icon'
@ -212,7 +212,7 @@ const personalInfo = reactive({
position: '高级工程师', position: '高级工程师',
joinDate: '2020-03-01', joinDate: '2020-03-01',
workYears: '3年8个月', workYears: '3年8个月',
supervisor: '王五' supervisor: '王五',
}) })
// //
@ -225,7 +225,7 @@ const editForm = reactive({
email: '', email: '',
address: '', address: '',
emergencyContact: '', emergencyContact: '',
emergencyPhone: '' emergencyPhone: '',
}) })
// //
@ -243,7 +243,7 @@ const handleEdit = () => {
email: personalInfo.email, email: personalInfo.email,
address: personalInfo.address, address: personalInfo.address,
emergencyContact: personalInfo.emergencyContact, emergencyContact: personalInfo.emergencyContact,
emergencyPhone: personalInfo.emergencyPhone emergencyPhone: personalInfo.emergencyPhone,
}) })
editModalVisible.value = true editModalVisible.value = true
} }

View File

@ -15,8 +15,8 @@
> >
<div class="file-info"> <div class="file-info">
<div class="file-icon"> <div class="file-icon">
<icon-file-pdf v-if="file.type === 'pdf'" /> <IconFilePdf v-if="file.type === 'pdf'" />
<icon-file v-else /> <IconFile v-else />
</div> </div>
<div class="file-details"> <div class="file-details">
<div class="file-name">{{ file.name }}</div> <div class="file-name">{{ file.name }}</div>
@ -49,7 +49,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import { IconFilePdf, IconFile } from '@arco-design/web-vue/es/icon' import { IconFile, IconFilePdf } from '@arco-design/web-vue/es/icon'
// //
const fileList = ref([ const fileList = ref([
@ -59,7 +59,7 @@ const fileList = ref([
type: 'pdf', type: 'pdf',
uploadDate: '2023-01-05', uploadDate: '2023-01-05',
size: '2.5MB', size: '2.5MB',
url: '/files/insurance/medical-insurance-policy.pdf' url: '/files/insurance/medical-insurance-policy.pdf',
}, },
{ {
id: 2, id: 2,
@ -67,7 +67,7 @@ const fileList = ref([
type: 'pdf', type: 'pdf',
uploadDate: '2023-01-05', uploadDate: '2023-01-05',
size: '1.8MB', size: '1.8MB',
url: '/files/insurance/accident-insurance-policy.pdf' url: '/files/insurance/accident-insurance-policy.pdf',
}, },
{ {
id: 3, id: 3,
@ -75,7 +75,7 @@ const fileList = ref([
type: 'pdf', type: 'pdf',
uploadDate: '2022-01-05', uploadDate: '2022-01-05',
size: '3.2MB', size: '3.2MB',
url: '/files/insurance/pension-insurance-policy.pdf' url: '/files/insurance/pension-insurance-policy.pdf',
}, },
{ {
id: 4, id: 4,
@ -83,8 +83,8 @@ const fileList = ref([
type: 'docx', type: 'docx',
uploadDate: '2022-01-10', uploadDate: '2022-01-10',
size: '1.5MB', size: '1.5MB',
url: '/files/insurance/insurance-terms.docx' url: '/files/insurance/insurance-terms.docx',
} },
]) ])
// //

View File

@ -4,7 +4,7 @@
<h2>保险公司管理</h2> <h2>保险公司管理</h2>
<a-button type="primary" @click="showAddCompanyModal"> <a-button type="primary" @click="showAddCompanyModal">
<template #icon> <template #icon>
<icon-plus /> <IconPlus />
</template> </template>
添加保险公司 添加保险公司
</a-button> </a-button>
@ -43,13 +43,13 @@
<a-space> <a-space>
<a-button type="primary" @click="handleSearch"> <a-button type="primary" @click="handleSearch">
<template #icon> <template #icon>
<icon-search /> <IconSearch />
</template> </template>
查询 查询
</a-button> </a-button>
<a-button @click="handleReset"> <a-button @click="handleReset">
<template #icon> <template #icon>
<icon-refresh /> <IconRefresh />
</template> </template>
重置 重置
</a-button> </a-button>
@ -83,20 +83,20 @@
编辑 编辑
</a-button> </a-button>
<a-button <a-button
v-if="record.status === '0'"
type="primary" type="primary"
status="warning" status="warning"
size="small" size="small"
@click="terminateCooperation(record)" @click="terminateCooperation(record)"
v-if="record.status === '0'"
> >
终止合作 终止合作
</a-button> </a-button>
<a-button <a-button
v-if="record.status === '1'"
type="primary" type="primary"
status="success" status="success"
size="small" size="small"
@click="resumeCooperation(record)" @click="resumeCooperation(record)"
v-if="record.status === '1'"
> >
恢复合作 恢复合作
</a-button> </a-button>
@ -118,9 +118,9 @@
v-model:visible="companyModalVisible" v-model:visible="companyModalVisible"
:title="isEdit ? '编辑保险公司' : '添加保险公司'" :title="isEdit ? '编辑保险公司' : '添加保险公司'"
width="500px" width="500px"
:confirm-loading="submitLoading"
@ok="handleCompanySubmit" @ok="handleCompanySubmit"
@cancel="handleCompanyCancel" @cancel="handleCompanyCancel"
:confirm-loading="submitLoading"
> >
<a-form <a-form
ref="companyFormRef" ref="companyFormRef"
@ -157,9 +157,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message, Modal } from '@arco-design/web-vue' import { Message, Modal } from '@arco-design/web-vue'
import { IconPlus, IconSearch, IconRefresh } from '@arco-design/web-vue/es/icon' import { IconPlus, IconRefresh, IconSearch } from '@arco-design/web-vue/es/icon'
import * as InsuranceCompanyAPI from '@/apis/insurance-company' import * as InsuranceCompanyAPI from '@/apis/insurance-company'
// //
@ -231,7 +231,7 @@ const paginationConfig = reactive({
total: 0, total: 0,
showTotal: true, showTotal: true,
showPageSize: true, showPageSize: true,
pageSizeOptions: ['10', '20', '50', '100'] pageSizeOptions: ['10', '20', '50', '100'],
}) })
// //
@ -243,7 +243,7 @@ const companyForm = reactive({
status: '0', status: '0',
email: '', email: '',
address: '', address: '',
startDate: '' startDate: '',
}) })
// //
@ -258,13 +258,13 @@ const companyRules = {
{ required: true, message: '请输入联系电话' }, { required: true, message: '请输入联系电话' },
{ {
pattern: /^1[3-9]\d{9}$/, pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号码' message: '请输入正确的手机号码',
}, },
], ],
email: [ email: [
{ {
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: '请输入正确的邮箱地址' message: '请输入正确的邮箱地址',
}, },
], ],
} }
@ -359,7 +359,7 @@ const terminateCooperation = (record: Company) => {
console.error('终止合作失败:', error) console.error('终止合作失败:', error)
Message.error('终止合作失败') Message.error('终止合作失败')
} }
} },
}) })
} }
@ -381,7 +381,7 @@ const resumeCooperation = (record: Company) => {
console.error('恢复合作失败:', error) console.error('恢复合作失败:', error)
Message.error('恢复合作失败') Message.error('恢复合作失败')
} }
} },
}) })
} }
@ -403,7 +403,7 @@ const deleteCompany = (record: Company) => {
console.error('删除失败:', error) console.error('删除失败:', error)
Message.error('删除失败') Message.error('删除失败')
} }
} },
}) })
} }
@ -461,7 +461,7 @@ const resetForm = () => {
status: '0', status: '0',
email: '', email: '',
address: '', address: '',
startDate: '' startDate: '',
}) })
companyFormRef.value?.resetFields() companyFormRef.value?.resetFields()
} }

View File

@ -5,7 +5,7 @@
<div class="page-header"> <div class="page-header">
<h2 class="page-title">保单文件管理</h2> <h2 class="page-title">保单文件管理</h2>
<a-button type="primary" @click="handleUploadFile"> <a-button type="primary" @click="handleUploadFile">
<template #icon><icon-upload /></template> <template #icon><IconUpload /></template>
上传文件 上传文件
</a-button> </a-button>
</div> </div>
@ -24,7 +24,7 @@
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<a-button type="primary" @click="handleSearch"> <a-button type="primary" @click="handleSearch">
<template #icon><icon-search /></template> <template #icon><IconSearch /></template>
搜索 搜索
</a-button> </a-button>
</a-form-item> </a-form-item>
@ -130,7 +130,7 @@
> >
<template #upload-button> <template #upload-button>
<a-button> <a-button>
<template #icon><icon-upload /></template> <template #icon><IconUpload /></template>
选择文件 选择文件
</a-button> </a-button>
</template> </template>
@ -154,7 +154,7 @@
title="文件详情" title="文件详情"
width="600px" width="600px"
> >
<div class="detail-content" v-if="selectedRecord"> <div v-if="selectedRecord" class="detail-content">
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">员工姓名</span> <span class="detail-label">员工姓名</span>
<span class="detail-value">{{ selectedRecord.employeeName }}</span> <span class="detail-value">{{ selectedRecord.employeeName }}</span>
@ -175,7 +175,7 @@
<span class="detail-label">上传日期</span> <span class="detail-label">上传日期</span>
<span class="detail-value">{{ selectedRecord.uploadDate }}</span> <span class="detail-value">{{ selectedRecord.uploadDate }}</span>
</div> </div>
<div class="detail-item" v-if="selectedRecord.remarks"> <div v-if="selectedRecord.remarks" class="detail-item">
<span class="detail-label">备注</span> <span class="detail-label">备注</span>
<span class="detail-value">{{ selectedRecord.remarks }}</span> <span class="detail-value">{{ selectedRecord.remarks }}</span>
</div> </div>
@ -186,18 +186,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import { import {
IconSearch,
IconUpload, IconUpload,
IconSearch
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
import { listAllUser } from '@/apis/system/user' import { listAllUser } from '@/apis/system/user'
import type { UserResp } from '@/apis/system/type' import type { UserResp } from '@/apis/system/type'
// //
const searchForm = reactive({ const searchForm = reactive({
keyword: '' keyword: '',
}) })
// //
@ -211,7 +211,7 @@ const columns = [
{ title: '文件名', dataIndex: 'fileName', width: 200 }, { title: '文件名', dataIndex: 'fileName', width: 200 },
{ title: '文件类型', dataIndex: 'fileType', slotName: 'fileType', width: 120 }, { title: '文件类型', dataIndex: 'fileType', slotName: 'fileType', width: 120 },
{ title: '上传日期', dataIndex: 'uploadDate', width: 140 }, { title: '上传日期', dataIndex: 'uploadDate', width: 140 },
{ title: '操作', slotName: 'action', width: 180 } { title: '操作', slotName: 'action', width: 180 },
] ]
// //
@ -223,7 +223,7 @@ const tableData = ref([
fileName: '医疗保险合同.pdf', fileName: '医疗保险合同.pdf',
fileType: 'PDF文档', fileType: 'PDF文档',
uploadDate: '2023-01-05', uploadDate: '2023-01-05',
remarks: '医疗保险相关合同文件' remarks: '医疗保险相关合同文件',
}, },
{ {
id: 2, id: 2,
@ -232,7 +232,7 @@ const tableData = ref([
fileName: '意外险合同.pdf', fileName: '意外险合同.pdf',
fileType: 'PDF文档', fileType: 'PDF文档',
uploadDate: '2023-01-05', uploadDate: '2023-01-05',
remarks: '意外险保单合同' remarks: '意外险保单合同',
}, },
{ {
id: 3, id: 3,
@ -241,8 +241,8 @@ const tableData = ref([
fileName: '养老保险合同.pdf', fileName: '养老保险合同.pdf',
fileType: 'PDF文档', fileType: 'PDF文档',
uploadDate: '2022-01-05', uploadDate: '2022-01-05',
remarks: '养老保险合同文档' remarks: '养老保险合同文档',
} },
]) ])
// //
@ -252,7 +252,7 @@ const pagination = reactive({
total: 3, total: 3,
showTotal: true, showTotal: true,
showJumper: true, showJumper: true,
showPageSize: true showPageSize: true,
}) })
// //
@ -269,7 +269,7 @@ const uploadForm = reactive({
fileName: '', fileName: '',
fileType: '', fileType: '',
remarks: '', remarks: '',
description: '' description: '',
}) })
// //
@ -303,7 +303,7 @@ const handleUploadFile = () => {
employeeId: '', employeeId: '',
fileName: '', fileName: '',
fileType: '', fileType: '',
remarks: '' remarks: '',
}) })
} }

View File

@ -5,7 +5,7 @@
<div class="page-header"> <div class="page-header">
<h2 class="page-title">健康档案管理</h2> <h2 class="page-title">健康档案管理</h2>
<a-button type="primary" @click="handleAddRecord"> <a-button type="primary" @click="handleAddRecord">
<template #icon><icon-plus /></template> <template #icon><IconPlus /></template>
添加记录 添加记录
</a-button> </a-button>
</div> </div>
@ -24,7 +24,7 @@
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<a-button type="primary" @click="handleSearch"> <a-button type="primary" @click="handleSearch">
<template #icon><icon-search /></template> <template #icon><IconSearch /></template>
搜索 搜索
</a-button> </a-button>
</a-form-item> </a-form-item>
@ -162,10 +162,10 @@
<a-form-item label="体检报告"> <a-form-item label="体检报告">
<a-upload <a-upload
:file-list="formData.reportFiles" :file-list="formData.reportFiles"
@change="handleFileChange"
action="#" action="#"
:before-upload="beforeUpload" :before-upload="beforeUpload"
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png" accept=".pdf,.doc,.docx,.jpg,.jpeg,.png"
@change="handleFileChange"
> >
<template #upload-button> <template #upload-button>
<a-button type="outline">选择文件</a-button> <a-button type="outline">选择文件</a-button>
@ -183,7 +183,7 @@
title="健康档案详情" title="健康档案详情"
width="800px" width="800px"
> >
<div class="detail-content" v-if="selectedRecord"> <div v-if="selectedRecord" class="detail-content">
<div class="detail-section"> <div class="detail-section">
<h4 class="section-title">基本信息</h4> <h4 class="section-title">基本信息</h4>
<div class="detail-grid"> <div class="detail-grid">
@ -217,7 +217,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="detail-section" v-if="selectedRecord.remarks"> <div v-if="selectedRecord.remarks" class="detail-section">
<h4 class="section-title">体检备注</h4> <h4 class="section-title">体检备注</h4>
<p class="remarks-text">{{ selectedRecord.remarks }}</p> <p class="remarks-text">{{ selectedRecord.remarks }}</p>
</div> </div>
@ -228,18 +228,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import { import {
IconPlus, IconPlus,
IconSearch IconSearch,
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
import { listAllUser } from '@/apis/system/user' import { listAllUser } from '@/apis/system/user'
import type { UserResp } from '@/apis/system/type' import type { UserResp } from '@/apis/system/type'
// //
const searchForm = reactive({ const searchForm = reactive({
keyword: '' keyword: '',
}) })
// //
@ -254,7 +254,7 @@ const columns = [
{ title: '体检医院', dataIndex: 'hospital', width: 160 }, { title: '体检医院', dataIndex: 'hospital', width: 160 },
{ title: '体检结果', dataIndex: 'result', slotName: 'result', width: 120 }, { title: '体检结果', dataIndex: 'result', slotName: 'result', width: 120 },
{ title: '下次体检日期', dataIndex: 'nextCheckupDate', width: 140 }, { title: '下次体检日期', dataIndex: 'nextCheckupDate', width: 140 },
{ title: '操作', slotName: 'action', width: 120 } { title: '操作', slotName: 'action', width: 120 },
] ]
// //
@ -267,7 +267,7 @@ const tableData = ref([
hospital: '北京协和医院', hospital: '北京协和医院',
result: '正常', result: '正常',
nextCheckupDate: '2024-10-15', nextCheckupDate: '2024-10-15',
remarks: '身体状况良好,各项指标正常' remarks: '身体状况良好,各项指标正常',
}, },
{ {
id: 2, id: 2,
@ -277,7 +277,7 @@ const tableData = ref([
hospital: '上海瑞金医院', hospital: '上海瑞金医院',
result: '轻微异常', result: '轻微异常',
nextCheckupDate: '2024-03-20', nextCheckupDate: '2024-03-20',
remarks: '血压稍高,建议控制饮食,加强运动' remarks: '血压稍高,建议控制饮食,加强运动',
}, },
{ {
id: 3, id: 3,
@ -287,8 +287,8 @@ const tableData = ref([
hospital: '广州中山医院', hospital: '广州中山医院',
result: '异常', result: '异常',
nextCheckupDate: '2023-11-10', nextCheckupDate: '2023-11-10',
remarks: '肝功能异常,需要进一步检查和治疗' remarks: '肝功能异常,需要进一步检查和治疗',
} },
]) ])
// //
@ -298,7 +298,7 @@ const pagination = reactive({
total: 3, total: 3,
showTotal: true, showTotal: true,
showJumper: true, showJumper: true,
showPageSize: true showPageSize: true,
}) })
// //
@ -322,7 +322,7 @@ const formData = reactive({
checkType: '', checkType: '',
summary: '', summary: '',
suggestions: '', suggestions: '',
reportFiles: [] as any[] reportFiles: [] as any[],
}) })
// //
@ -364,7 +364,7 @@ const handleAddRecord = () => {
checkType: '', checkType: '',
summary: '', summary: '',
suggestions: '', suggestions: '',
reportFiles: [] reportFiles: [],
}) })
} }

View File

@ -5,7 +5,7 @@
<h2 class="page-title">保险信息管理</h2> <h2 class="page-title">保险信息管理</h2>
<a-button type="primary" @click="showAddModal"> <a-button type="primary" @click="showAddModal">
<template #icon> <template #icon>
<icon-plus /> <IconPlus />
</template> </template>
新增保险 新增保险
</a-button> </a-button>
@ -62,13 +62,13 @@
<a-space> <a-space>
<a-button type="primary" @click="handleSearch"> <a-button type="primary" @click="handleSearch">
<template #icon> <template #icon>
<icon-search /> <IconSearch />
</template> </template>
搜索 搜索
</a-button> </a-button>
<a-button @click="handleReset"> <a-button @click="handleReset">
<template #icon> <template #icon>
<icon-refresh /> <IconRefresh />
</template> </template>
重置 重置
</a-button> </a-button>
@ -171,7 +171,6 @@
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="12"> <a-col :span="12">
<a-form-item label="选择人员" field="userId"> <a-form-item label="选择人员" field="userId">
<a-select <a-select
v-model="formData.userId" v-model="formData.userId"
placeholder="请选择员工" placeholder="请选择员工"
@ -190,7 +189,6 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-form-item label="保险单号" field="insuranceBillCode"> <a-form-item label="保险单号" field="insuranceBillCode">
<a-input v-model="formData.insuranceBillCode" placeholder="请输入保险单号" /> <a-input v-model="formData.insuranceBillCode" placeholder="请输入保险单号" />
@ -273,9 +271,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message, Modal } from '@arco-design/web-vue' import { Message, Modal } from '@arco-design/web-vue'
import { IconPlus, IconSearch, IconRefresh } from '@arco-design/web-vue/es/icon' import { IconPlus, IconRefresh, IconSearch } from '@arco-design/web-vue/es/icon'
import * as InsuranceAPI from '@/apis/insurance' import * as InsuranceAPI from '@/apis/insurance'
import { listAllUser } from '@/apis/system/user' import { listAllUser } from '@/apis/system/user'
import type { InsuranceInfo, InsuranceListParams } from '@/apis/insurance' import type { InsuranceInfo, InsuranceListParams } from '@/apis/insurance'
@ -300,7 +298,7 @@ const typeList = ref<any[]>([])
const searchForm = reactive<InsuranceListParams>({ const searchForm = reactive<InsuranceListParams>({
insuranceCompanyId: '', insuranceCompanyId: '',
insuranceTypeId: '', insuranceTypeId: '',
userId: '' userId: '',
}) })
// //
@ -309,7 +307,7 @@ const paginationConfig = reactive({
pageSize: 10, pageSize: 10,
total: 0, total: 0,
showTotal: true, showTotal: true,
showJumper: true showJumper: true,
}) })
// //
@ -325,35 +323,35 @@ const formData = reactive<InsuranceInfo>({
insuranceAmount: 0, insuranceAmount: 0,
insurancePremium: 0, insurancePremium: 0,
beneficiary: '', beneficiary: '',
remark: '' remark: '',
}) })
// //
const formRules = { const formRules = {
insuranceCompanyId: [ insuranceCompanyId: [
{ required: true, message: '请选择保险公司' } { required: true, message: '请选择保险公司' },
], ],
insuranceTypeId: [ insuranceTypeId: [
{ required: true, message: '请选择保险类型' } { required: true, message: '请选择保险类型' },
], ],
userId: [ userId: [
{ required: true, message: '请输入用户ID' } { required: true, message: '请输入用户ID' },
], ],
insuranceBillCode: [ insuranceBillCode: [
{ required: true, message: '请输入保险单号' } { required: true, message: '请输入保险单号' },
], ],
effectiveDate: [ effectiveDate: [
{ required: true, message: '请选择生效日期' } { required: true, message: '请选择生效日期' },
], ],
expireDate: [ expireDate: [
{ required: true, message: '请选择到期日期' } { required: true, message: '请选择到期日期' },
], ],
insuranceAmount: [ insuranceAmount: [
{ required: true, message: '请输入保险金额' } { required: true, message: '请输入保险金额' },
], ],
insurancePremium: [ insurancePremium: [
{ required: true, message: '请输入保险费' } { required: true, message: '请输入保险费' },
] ],
} }
// //
@ -361,62 +359,62 @@ const columns = [
{ {
title: '用户名称', title: '用户名称',
dataIndex: 'name', dataIndex: 'name',
width: 100 width: 100,
}, },
{ {
title: '保险公司', title: '保险公司',
dataIndex: 'insuranceCompanyName', dataIndex: 'insuranceCompanyName',
slotName: 'insuranceCompanyName', slotName: 'insuranceCompanyName',
width: 150 width: 150,
}, },
{ {
title: '保险类型', title: '保险类型',
dataIndex: 'insuranceTypeName', dataIndex: 'insuranceTypeName',
slotName: 'insuranceTypeName', slotName: 'insuranceTypeName',
width: 120 width: 120,
}, },
{ {
title: '保险单号', title: '保险单号',
dataIndex: 'insuranceBillCode', dataIndex: 'insuranceBillCode',
width: 150 width: 150,
}, },
{ {
title: '生效日期', title: '生效日期',
dataIndex: 'effectiveDate', dataIndex: 'effectiveDate',
width: 120 width: 120,
}, },
{ {
title: '到期日期', title: '到期日期',
dataIndex: 'expireDate', dataIndex: 'expireDate',
width: 120 width: 120,
}, },
{ {
title: '保险金额', title: '保险金额',
dataIndex: 'insuranceAmount', dataIndex: 'insuranceAmount',
width: 120 width: 120,
}, },
{ {
title: '状态', title: '状态',
dataIndex: 'status', dataIndex: 'status',
slotName: 'status', slotName: 'status',
width: 80 width: 80,
}, },
{ {
title: '操作', title: '操作',
slotName: 'actions', slotName: 'actions',
width: 150, width: 150,
fixed: 'right' fixed: 'right',
} },
] ]
// //
const getCompanyName = (id: string) => { const getCompanyName = (id: string) => {
const company = companyList.value.find(c => c.id === id) const company = companyList.value.find((c) => c.id === id)
return company ? company.insuranceCompanyName : '-' return company ? company.insuranceCompanyName : '-'
} }
// //
const getTypeName = (id: string) => { const getTypeName = (id: string) => {
const type = typeList.value.find(t => t.id === id) const type = typeList.value.find((t) => t.id === id)
return type ? type.insuranceTypeName : '-' return type ? type.insuranceTypeName : '-'
} }
@ -465,7 +463,7 @@ const getInsuranceList = async () => {
const params: InsuranceListParams = { const params: InsuranceListParams = {
...searchForm, ...searchForm,
current: paginationConfig.current, current: paginationConfig.current,
size: paginationConfig.pageSize size: paginationConfig.pageSize,
} }
const response = await InsuranceAPI.getInsuranceList(params) const response = await InsuranceAPI.getInsuranceList(params)
@ -522,7 +520,7 @@ const handleReset = () => {
Object.assign(searchForm, { Object.assign(searchForm, {
insuranceCompanyId: '', insuranceCompanyId: '',
insuranceTypeId: '', insuranceTypeId: '',
userId: '' userId: '',
}) })
paginationConfig.current = 1 paginationConfig.current = 1
getInsuranceList() getInsuranceList()
@ -564,7 +562,7 @@ const deleteRecord = (record: InsuranceInfo) => {
console.error('删除失败:', error) console.error('删除失败:', error)
Message.error('删除失败') Message.error('删除失败')
} }
} },
}) })
} }
@ -610,7 +608,7 @@ const resetForm = () => {
insuranceAmount: 0, insuranceAmount: 0,
insurancePremium: 0, insurancePremium: 0,
beneficiary: '', beneficiary: '',
remark: '' remark: '',
}) })
formRef.value?.resetFields() formRef.value?.resetFields()
} }
@ -621,7 +619,7 @@ const init = async () => {
getCompanyList(), getCompanyList(),
getTypeList(), getTypeList(),
fetchEmployeeOptions(), fetchEmployeeOptions(),
getInsuranceList() getInsuranceList(),
]) ])
} }

View File

@ -57,7 +57,7 @@
</div> </div>
<div class="filter-right"> <div class="filter-right">
<a-button type="outline" @click="handleExportExcel"> <a-button type="outline" @click="handleExportExcel">
<template #icon><icon-download /></template> <template #icon><IconDownload /></template>
导出Excel 导出Excel
</a-button> </a-button>
</div> </div>
@ -76,7 +76,7 @@
<a-card class="chart-card" :bordered="false"> <a-card class="chart-card" :bordered="false">
<template #title> <template #title>
<div class="chart-title"> <div class="chart-title">
<icon-bar-chart /> <IconBarChart />
保险类型分布图表 保险类型分布图表
</div> </div>
</template> </template>
@ -105,15 +105,21 @@
<!-- 简单的饼图可视化 --> <!-- 简单的饼图可视化 -->
<svg width="300" height="300" viewBox="0 0 300 300"> <svg width="300" height="300" viewBox="0 0 300 300">
<circle cx="150" cy="150" r="80" fill="#1890ff" stroke="#fff" stroke-width="2" /> <circle cx="150" cy="150" r="80" fill="#1890ff" stroke="#fff" stroke-width="2" />
<circle cx="150" cy="150" r="80" fill="#52c41a" stroke="#fff" stroke-width="2" <circle
cx="150" cy="150" r="80" fill="#52c41a" stroke="#fff" stroke-width="2"
stroke-dasharray="125.6 502.4" stroke-dashoffset="0" stroke-dasharray="125.6 502.4" stroke-dashoffset="0"
transform="rotate(90 150 150)" /> transform="rotate(90 150 150)"
<circle cx="150" cy="150" r="80" fill="#faad14" stroke="#fff" stroke-width="2" />
<circle
cx="150" cy="150" r="80" fill="#faad14" stroke="#fff" stroke-width="2"
stroke-dasharray="100.5 527.5" stroke-dashoffset="-125.6" stroke-dasharray="100.5 527.5" stroke-dashoffset="-125.6"
transform="rotate(90 150 150)" /> transform="rotate(90 150 150)"
<circle cx="150" cy="150" r="80" fill="#f5222d" stroke="#fff" stroke-width="2" />
<circle
cx="150" cy="150" r="80" fill="#f5222d" stroke="#fff" stroke-width="2"
stroke-dasharray="62.8 565.2" stroke-dashoffset="-226.1" stroke-dasharray="62.8 565.2" stroke-dashoffset="-226.1"
transform="rotate(90 150 150)" /> transform="rotate(90 150 150)"
/>
</svg> </svg>
</div> </div>
</div> </div>
@ -124,16 +130,16 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from 'vue' import { reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import { IconDownload, IconBarChart } from '@arco-design/web-vue/es/icon' import { IconBarChart, IconDownload } from '@arco-design/web-vue/es/icon'
// //
const stats = reactive({ const stats = reactive({
totalEmployees: 156, totalEmployees: 156,
validPolicies: 142, validPolicies: 142,
expiringPolicies: 23, expiringPolicies: 23,
annualPremium: '¥ 1,256,800' annualPremium: '¥ 1,256,800',
}) })
// //

View File

@ -4,7 +4,7 @@
<h2>保险类型管理</h2> <h2>保险类型管理</h2>
<a-button type="primary" @click="showAddTypeModal"> <a-button type="primary" @click="showAddTypeModal">
<template #icon> <template #icon>
<icon-plus /> <IconPlus />
</template> </template>
添加保险类型 添加保险类型
</a-button> </a-button>
@ -79,7 +79,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message, Modal } from '@arco-design/web-vue' import { Message, Modal } from '@arco-design/web-vue'
import { IconPlus } from '@arco-design/web-vue/es/icon' import { IconPlus } from '@arco-design/web-vue/es/icon'
import * as InsuranceTypeAPI from '@/apis/insurance-type' import * as InsuranceTypeAPI from '@/apis/insurance-type'
@ -128,7 +128,7 @@ const typeForm = reactive({
id: '', id: '',
insuranceTypeName: '', insuranceTypeName: '',
description: '', description: '',
coverage: '' coverage: '',
}) })
// //
@ -236,7 +236,7 @@ const resetForm = () => {
id: '', id: '',
insuranceTypeName: '', insuranceTypeName: '',
description: '', description: '',
coverage: '' coverage: '',
}) })
typeFormRef.value?.resetFields() typeFormRef.value?.resetFields()
} }

View File

@ -47,18 +47,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
// //
let searchForm = reactive({ const searchForm = reactive({
userName: '', userName: '',
projectName: '', projectName: '',
startDate: '', startDate: '',
endDate: '', endDate: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
// //
@ -68,17 +68,17 @@ const queryFormColumns = [
label: '员工姓名', label: '员工姓名',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入员工姓名' placeholder: '请输入员工姓名',
} },
}, },
{ {
field: 'projectName', field: 'projectName',
label: '项目名称', label: '项目名称',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入项目名称' placeholder: '请输入项目名称',
} },
} },
] ]
// //
@ -90,7 +90,7 @@ const tableColumns: TableColumnData[] = [
{ title: '工作量(小时)', dataIndex: 'workload', slotName: 'workload', width: 120 }, { title: '工作量(小时)', dataIndex: 'workload', slotName: 'workload', width: 120 },
{ title: '工作日期', dataIndex: 'workDate', width: 120 }, { title: '工作日期', dataIndex: 'workDate', width: 120 },
{ title: '创建时间', dataIndex: 'createTime', width: 160 }, { title: '创建时间', dataIndex: 'createTime', width: 160 },
{ title: '操作', slotName: 'action', width: 120, fixed: 'right' } { title: '操作', slotName: 'action', width: 120, fixed: 'right' },
] ]
// //
@ -104,7 +104,7 @@ const dataList = ref([
workContent: '前端开发', workContent: '前端开发',
workload: 8, workload: 8,
workDate: '2024-01-15', workDate: '2024-01-15',
createTime: '2024-01-15 10:30:00' createTime: '2024-01-15 10:30:00',
}, },
{ {
id: 2, id: 2,
@ -114,8 +114,8 @@ const dataList = ref([
workContent: '后端接口开发', workContent: '后端接口开发',
workload: 6, workload: 6,
workDate: '2024-01-15', workDate: '2024-01-15',
createTime: '2024-01-15 11:20:00' createTime: '2024-01-15 11:20:00',
} },
]) ])
const pagination = reactive({ const pagination = reactive({
@ -123,7 +123,7 @@ const pagination = reactive({
pageSize: 10, pageSize: 10,
total: 2, total: 2,
showTotal: true, showTotal: true,
showPageSize: true showPageSize: true,
}) })
// //
@ -142,7 +142,7 @@ const reset = () => {
startDate: '', startDate: '',
endDate: '', endDate: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
pagination.current = 1 pagination.current = 1
search() search()

View File

@ -1,6 +1,8 @@
<template> <template>
<a-form ref="formRef" :model="form" :rules="rules" :label-col-style="{ display: 'none' }" <a-form
:wrapper-col-style="{ flex: 1 }" size="large" @submit="handleLogin"> ref="formRef" :model="form" :rules="rules" :label-col-style="{ display: 'none' }"
:wrapper-col-style="{ flex: 1 }" size="large" @submit="handleLogin"
>
<a-form-item field="account" hide-label> <a-form-item field="account" hide-label>
<a-input v-model="form.account" placeholder="请输入用户名" allow-clear /> <a-input v-model="form.account" placeholder="请输入用户名" allow-clear />
</a-form-item> </a-form-item>
@ -113,7 +115,6 @@ const handleLogin = async () => {
loading.value = false loading.value = false
} }
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -14,7 +14,7 @@
</a-col> </a-col>
<a-col :xs="24" :sm="12" :md="11"> <a-col :xs="24" :sm="12" :md="11">
<div class="login-right"> <div class="login-right">
<a-tabs v-model:activeKey="activeTab" class="login-right__form"> <a-tabs v-model:active-key="activeTab" class="login-right__form">
<a-tab-pane key="1" title="账号登录"> <a-tab-pane key="1" title="账号登录">
<component :is="AccountLogin" v-if="activeTab === '1'" /> <component :is="AccountLogin" v-if="activeTab === '1'" />
</a-tab-pane> </a-tab-pane>
@ -45,7 +45,7 @@
<a-row align="stretch" class="login-box"> <a-row align="stretch" class="login-box">
<a-col :xs="24" :sm="12" :md="11"> <a-col :xs="24" :sm="12" :md="11">
<div class="login-right"> <div class="login-right">
<a-tabs v-model:activeKey="activeTab" class="login-right__form"> <a-tabs v-model:active-key="activeTab" class="login-right__form">
<a-tab-pane key="1" title="账号登录"> <a-tab-pane key="1" title="账号登录">
<component :is="AccountLogin" v-if="activeTab === '1'" /> <component :is="AccountLogin" v-if="activeTab === '1'" />
</a-tab-pane> </a-tab-pane>
@ -173,7 +173,6 @@ const activeTab = ref('1')
} }
} }
} }
.theme-btn { .theme-btn {
@ -326,7 +325,6 @@ const activeTab = ref('1')
} }
} }
} }
.theme-btn { .theme-btn {

View File

@ -17,13 +17,13 @@
:show-file-list="false" :show-file-list="false"
multiple multiple
:accept="getAcceptType()" :accept="getAcceptType()"
@change="handleFileChange"
drag drag
class="upload-dragger" class="upload-dragger"
@change="handleFileChange"
> >
<div class="upload-content"> <div class="upload-content">
<div class="upload-icon"> <div class="upload-icon">
<icon-upload :size="48" /> <IconUpload :size="48" />
</div> </div>
<div class="upload-text"> <div class="upload-text">
<p class="primary-text">将文件拖拽到此处<span class="link-text">点击上传</span></p> <p class="primary-text">将文件拖拽到此处<span class="link-text">点击上传</span></p>
@ -37,9 +37,9 @@
<div class="action-buttons"> <div class="action-buttons">
<a-button <a-button
type="primary" type="primary"
@click="startUpload"
:loading="uploading" :loading="uploading"
:disabled="uploadQueue.length === 0" :disabled="uploadQueue.length === 0"
@click="startUpload"
> >
开始上传 开始上传
</a-button> </a-button>
@ -138,7 +138,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed } from 'vue' import { ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
import { IconUpload } from '@arco-design/web-vue/es/icon' import { IconUpload } from '@arco-design/web-vue/es/icon'
@ -163,7 +163,7 @@ const uploadedFiles = ref([
size: 3355443, // 3.2MB size: 3355443, // 3.2MB
uploadTime: '2023-11-05 14:32', uploadTime: '2023-11-05 14:32',
status: '成功', status: '成功',
url: '/api/files/IMG_20231105_1430.jpg' url: '/api/files/IMG_20231105_1430.jpg',
}, },
{ {
id: 2, id: 2,
@ -172,7 +172,7 @@ const uploadedFiles = ref([
size: 47185920, // 45.6MB size: 47185920, // 45.6MB
uploadTime: '2023-11-06 09:18', uploadTime: '2023-11-06 09:18',
status: '成功', status: '成功',
url: '/api/files/VID_20231106_0915.mp4' url: '/api/files/VID_20231106_0915.mp4',
}, },
{ {
id: 3, id: 3,
@ -181,8 +181,8 @@ const uploadedFiles = ref([
size: 2936013, // 2.8MB size: 2936013, // 2.8MB
uploadTime: '2023-11-07 16:48', uploadTime: '2023-11-07 16:48',
status: '成功', status: '成功',
url: '/api/files/IMG_20231107_1645.jpg' url: '/api/files/IMG_20231107_1645.jpg',
} },
]) ])
// //
@ -192,17 +192,17 @@ const fileColumns: TableColumnData[] = [
{ title: '大小', dataIndex: 'size', slotName: 'size', width: 100 }, { title: '大小', dataIndex: 'size', slotName: 'size', width: 100 },
{ title: '上传时间', dataIndex: 'uploadTime', width: 150 }, { title: '上传时间', dataIndex: 'uploadTime', width: 150 },
{ title: '状态', dataIndex: 'status', slotName: 'status', width: 100 }, { title: '状态', dataIndex: 'status', slotName: 'status', width: 100 },
{ title: '操作', slotName: 'action', width: 150, fixed: 'right' } { title: '操作', slotName: 'action', width: 150, fixed: 'right' },
] ]
// //
const getAcceptType = () => { const getAcceptType = () => {
const typeMap: Record<string, string> = { const typeMap: Record<string, string> = {
'image': 'image/*', image: 'image/*',
'video': 'video/*', video: 'video/*',
'audio': 'audio/*', audio: 'audio/*',
'document': '.pdf,.doc,.docx,.txt,.xls,.xlsx,.ppt,.pptx', document: '.pdf,.doc,.docx,.txt,.xls,.xlsx,.ppt,.pptx',
'other': '*' other: '*',
} }
return typeMap[activeTab.value] || '*' return typeMap[activeTab.value] || '*'
} }
@ -210,11 +210,11 @@ const getAcceptType = () => {
// //
const getFileTypeColor = (type: string) => { const getFileTypeColor = (type: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'image': 'blue', image: 'blue',
'video': 'green', video: 'green',
'audio': 'orange', audio: 'orange',
'document': 'purple', document: 'purple',
'other': 'gray' other: 'gray',
} }
return colorMap[type] || 'gray' return colorMap[type] || 'gray'
} }
@ -222,10 +222,10 @@ const getFileTypeColor = (type: string) => {
// //
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'成功': 'green', 成功: 'green',
'失败': 'red', 失败: 'red',
'上传中': 'blue', 上传中: 'blue',
'等待': 'orange' 等待: 'orange',
} }
return colorMap[status] || 'gray' return colorMap[status] || 'gray'
} }
@ -236,7 +236,7 @@ const formatFileSize = (bytes: number) => {
const k = 1024 const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB'] const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k)) const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + sizes[i] return Number.parseFloat((bytes / k ** i).toFixed(2)) + sizes[i]
} }
// //
@ -255,7 +255,7 @@ const customUpload = (option: any) => {
name: file.name, name: file.name,
size: file.size, size: file.size,
type: getFileType(file.name), type: getFileType(file.name),
status: '等待' status: '等待',
}) })
Message.success(`文件 ${file.name} 已添加到上传队列`) Message.success(`文件 ${file.name} 已添加到上传队列`)
@ -298,7 +298,7 @@ const startUpload = async () => {
queueItem.status = '上传中' queueItem.status = '上传中'
// //
await new Promise(resolve => setTimeout(resolve, 1000)) await new Promise((resolve) => setTimeout(resolve, 1000))
// //
uploadedFiles.value.push({ uploadedFiles.value.push({
@ -308,7 +308,7 @@ const startUpload = async () => {
size: queueItem.size, size: queueItem.size,
uploadTime: new Date().toLocaleString(), uploadTime: new Date().toLocaleString(),
status: '成功', status: '成功',
url: `/api/files/${queueItem.name}` url: `/api/files/${queueItem.name}`,
}) })
} }
@ -340,7 +340,7 @@ const previewFile = (file: any) => {
// //
const deleteFile = (file: any) => { const deleteFile = (file: any) => {
const index = uploadedFiles.value.findIndex(f => f.id === file.id) const index = uploadedFiles.value.findIndex((f) => f.id === file.id)
if (index > -1) { if (index > -1) {
uploadedFiles.value.splice(index, 1) uploadedFiles.value.splice(index, 1)
Message.success('文件已删除') Message.success('文件已删除')

View File

@ -28,11 +28,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from 'vue' import { ref } from 'vue'
import { Message } from '@arco-design/web-vue'
import RuleDrawer from './RuleDrawer.vue' import RuleDrawer from './RuleDrawer.vue'
import { deleteRule, getRuleList } from '@/apis/performance' import { deleteRule, getRuleList } from '@/apis/performance'
import type { RuleResp } from '@/apis/performance/type' import type { RuleResp } from '@/apis/performance/type'
import { Message } from '@arco-design/web-vue'
const visible = ref(false) const visible = ref(false)
const rules = ref<RuleResp[]>([]) const rules = ref<RuleResp[]>([])

View File

@ -36,10 +36,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { onMounted, ref } from 'vue'
import { getDimensionList, deleteDimension } from '@/apis/performance'
import DimensionDrawer from './components/DimensionDrawer.vue' import DimensionDrawer from './components/DimensionDrawer.vue'
import RuleList from './components/RuleList.vue' import RuleList from './components/RuleList.vue'
import { deleteDimension, getDimensionList } from '@/apis/performance'
import type { DimensionResp } from '@/apis/performance/type' import type { DimensionResp } from '@/apis/performance/type'
const dimensions = ref<DimensionResp[]>([]) const dimensions = ref<DimensionResp[]>([])

View File

@ -21,7 +21,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import { getMyEvaluation } from '@/apis/performance-setting' import { getMyEvaluation } from '@/apis/performance-setting'
import type { PerformanceRule } from '@/apis/performance-setting/type'
const data = ref<any[]>([]) const data = ref<any[]>([])
const visible = ref(false) const visible = ref(false)

View File

@ -26,7 +26,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { addRule, updateRule } from '@/apis/performance-setting' import { addRule, updateRule } from '@/apis/performance-setting'
import type { PerformanceRule, PerformanceDimension } from '@/apis/performance-setting/type' import type { PerformanceDimension, PerformanceRule } from '@/apis/performance-setting/type'
const props = defineProps<{ dimension?: PerformanceDimension }>() const props = defineProps<{ dimension?: PerformanceDimension }>()
const emit = defineEmits(['success']) const emit = defineEmits(['success'])
@ -50,7 +50,7 @@ watch(
form.value.dimensionName = val.dimensionName form.value.dimensionName = val.dimensionName
} }
}, },
{ immediate: true } { immediate: true },
) )
const open = (record?: PerformanceRule) => { const open = (record?: PerformanceRule) => {

View File

@ -14,15 +14,15 @@
</a-space> </a-space>
</template> </template>
</a-table> </a-table>
<RuleDrawer ref="ruleDrawerRef" @success="loadRules" :dimension="dimension" /> <RuleDrawer ref="ruleDrawerRef" :dimension="dimension" @success="loadRules" />
</a-drawer> </a-drawer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { getRuleList, deleteRule } from '@/apis/performance-setting'
import RuleDrawer from './RuleDrawer.vue' import RuleDrawer from './RuleDrawer.vue'
import type { PerformanceRule, PerformanceDimension } from '@/apis/performance-setting/type' import { deleteRule, getRuleList } from '@/apis/performance-setting'
import type { PerformanceDimension, PerformanceRule } from '@/apis/performance-setting/type'
const visible = ref(false) const visible = ref(false)
const dimension = ref<PerformanceDimension>() const dimension = ref<PerformanceDimension>()

View File

@ -62,18 +62,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
// //
let searchForm = reactive({ const searchForm = reactive({
droneName: '', droneName: '',
model: '', model: '',
status: '', status: '',
owner: '', owner: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
// //
@ -83,16 +83,16 @@ const queryFormColumns = [
label: '无人机名称', label: '无人机名称',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入无人机名称' placeholder: '请输入无人机名称',
} },
}, },
{ {
field: 'model', field: 'model',
label: '型号', label: '型号',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入型号' placeholder: '请输入型号',
} },
}, },
{ {
field: 'status', field: 'status',
@ -104,10 +104,10 @@ const queryFormColumns = [
{ label: '可用', value: 'available' }, { label: '可用', value: 'available' },
{ label: '使用中', value: 'in_use' }, { label: '使用中', value: 'in_use' },
{ label: '维护中', value: 'maintenance' }, { label: '维护中', value: 'maintenance' },
{ label: '故障', value: 'fault' } { label: '故障', value: 'fault' },
] ],
} },
} },
] ]
// //
@ -124,7 +124,7 @@ const tableColumns: TableColumnData[] = [
{ title: '负责人', dataIndex: 'owner', width: 100 }, { title: '负责人', dataIndex: 'owner', width: 100 },
{ title: '存放位置', dataIndex: 'location', width: 150, ellipsis: true, tooltip: true }, { title: '存放位置', dataIndex: 'location', width: 150, ellipsis: true, tooltip: true },
{ title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true }, { title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true },
{ title: '操作', slotName: 'action', width: 200, fixed: 'right' } { title: '操作', slotName: 'action', width: 200, fixed: 'right' },
] ]
// //
@ -143,7 +143,7 @@ const dataList = ref([
status: 'available', status: 'available',
owner: '李飞行员', owner: '李飞行员',
location: '设备仓库A区', location: '设备仓库A区',
remark: '配备高清相机和热成像设备' remark: '配备高清相机和热成像设备',
}, },
{ {
id: 2, id: 2,
@ -158,7 +158,7 @@ const dataList = ref([
status: 'in_use', status: 'in_use',
owner: '王飞行员', owner: '王飞行员',
location: '现场作业点', location: '现场作业点',
remark: '正在执行塔筒巡检任务' remark: '正在执行塔筒巡检任务',
}, },
{ {
id: 3, id: 3,
@ -173,8 +173,8 @@ const dataList = ref([
status: 'maintenance', status: 'maintenance',
owner: '张工程师', owner: '张工程师',
location: '维修车间', location: '维修车间',
remark: '螺旋桨需要更换' remark: '螺旋桨需要更换',
} },
]) ])
const pagination = reactive({ const pagination = reactive({
@ -182,16 +182,16 @@ const pagination = reactive({
pageSize: 10, pageSize: 10,
total: 3, total: 3,
showTotal: true, showTotal: true,
showPageSize: true showPageSize: true,
}) })
// //
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'available': 'green', available: 'green',
'in_use': 'blue', in_use: 'blue',
'maintenance': 'orange', maintenance: 'orange',
'fault': 'red' fault: 'red',
} }
return colorMap[status] || 'gray' return colorMap[status] || 'gray'
} }
@ -199,10 +199,10 @@ const getStatusColor = (status: string) => {
// //
const getStatusText = (status: string) => { const getStatusText = (status: string) => {
const textMap: Record<string, string> = { const textMap: Record<string, string> = {
'available': '可用', available: '可用',
'in_use': '使用中', in_use: '使用中',
'maintenance': '维护中', maintenance: '维护中',
'fault': '故障' fault: '故障',
} }
return textMap[status] || status return textMap[status] || status
} }
@ -222,7 +222,7 @@ const reset = () => {
status: '', status: '',
owner: '', owner: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
pagination.current = 1 pagination.current = 1
search() search()

View File

@ -50,17 +50,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
// //
let searchForm = reactive({ const searchForm = reactive({
deviceName: '', deviceName: '',
location: '', location: '',
status: '', status: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
// //
@ -70,16 +70,16 @@ const queryFormColumns = [
label: '设备名称', label: '设备名称',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入设备名称' placeholder: '请输入设备名称',
} },
}, },
{ {
field: 'location', field: 'location',
label: '安装位置', label: '安装位置',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入安装位置' placeholder: '请输入安装位置',
} },
}, },
{ {
field: 'status', field: 'status',
@ -91,10 +91,10 @@ const queryFormColumns = [
{ label: '正常运行', value: 'normal' }, { label: '正常运行', value: 'normal' },
{ label: '故障', value: 'fault' }, { label: '故障', value: 'fault' },
{ label: '维护中', value: 'maintenance' }, { label: '维护中', value: 'maintenance' },
{ label: '离线', value: 'offline' } { label: '离线', value: 'offline' },
] ],
} },
} },
] ]
// //
@ -109,7 +109,7 @@ const tableColumns: TableColumnData[] = [
{ title: '负责人', dataIndex: 'manager', width: 100 }, { title: '负责人', dataIndex: 'manager', width: 100 },
{ title: '联系电话', dataIndex: 'phone', width: 120 }, { title: '联系电话', dataIndex: 'phone', width: 120 },
{ title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true }, { title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true },
{ title: '操作', slotName: 'action', width: 180, fixed: 'right' } { title: '操作', slotName: 'action', width: 180, fixed: 'right' },
] ]
// //
@ -126,7 +126,7 @@ const dataList = ref([
lastCheckTime: '2024-01-15 10:30:00', lastCheckTime: '2024-01-15 10:30:00',
manager: '张工程师', manager: '张工程师',
phone: '13800138001', phone: '13800138001',
remark: '运行正常,数据传输稳定' remark: '运行正常,数据传输稳定',
}, },
{ {
id: 2, id: 2,
@ -139,7 +139,7 @@ const dataList = ref([
lastCheckTime: '2024-01-14 15:20:00', lastCheckTime: '2024-01-14 15:20:00',
manager: '李工程师', manager: '李工程师',
phone: '13800138002', phone: '13800138002',
remark: '传感器故障,需要更换' remark: '传感器故障,需要更换',
}, },
{ {
id: 3, id: 3,
@ -152,8 +152,8 @@ const dataList = ref([
lastCheckTime: '2024-01-13 09:15:00', lastCheckTime: '2024-01-13 09:15:00',
manager: '王工程师', manager: '王工程师',
phone: '13800138003', phone: '13800138003',
remark: '定期维护中' remark: '定期维护中',
} },
]) ])
const pagination = reactive({ const pagination = reactive({
@ -161,16 +161,16 @@ const pagination = reactive({
pageSize: 10, pageSize: 10,
total: 3, total: 3,
showTotal: true, showTotal: true,
showPageSize: true showPageSize: true,
}) })
// //
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'normal': 'green', normal: 'green',
'fault': 'red', fault: 'red',
'maintenance': 'orange', maintenance: 'orange',
'offline': 'gray' offline: 'gray',
} }
return colorMap[status] || 'gray' return colorMap[status] || 'gray'
} }
@ -178,10 +178,10 @@ const getStatusColor = (status: string) => {
// //
const getStatusText = (status: string) => { const getStatusText = (status: string) => {
const textMap: Record<string, string> = { const textMap: Record<string, string> = {
'normal': '正常运行', normal: '正常运行',
'fault': '故障', fault: '故障',
'maintenance': '维护中', maintenance: '维护中',
'offline': '离线' offline: '离线',
} }
return textMap[status] || status return textMap[status] || status
} }
@ -200,7 +200,7 @@ const reset = () => {
location: '', location: '',
status: '', status: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
pagination.current = 1 pagination.current = 1
search() search()

View File

@ -53,8 +53,8 @@
<a-space> <a-space>
<a-link @click="viewDetail(record)">查看</a-link> <a-link @click="viewDetail(record)">查看</a-link>
<a-link @click="downloadNotice(record)">下载</a-link> <a-link @click="downloadNotice(record)">下载</a-link>
<a-link @click="sendNotice(record)" v-if="record.sendStatus === 'not_sent'">发送</a-link> <a-link v-if="record.sendStatus === 'not_sent'" @click="sendNotice(record)">发送</a-link>
<a-link @click="signContract(record)" v-if="record.sendStatus === 'sent'">签署合同</a-link> <a-link v-if="record.sendStatus === 'sent'" @click="signContract(record)">签署合同</a-link>
</a-space> </a-space>
</template> </template>
</GiTable> </GiTable>
@ -62,18 +62,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
// //
let searchForm = reactive({ const searchForm = reactive({
projectName: '', projectName: '',
noticeCode: '', noticeCode: '',
sendStatus: '', sendStatus: '',
noticeDate: '', noticeDate: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
// //
@ -83,16 +83,16 @@ const queryFormColumns = [
label: '项目名称', label: '项目名称',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入项目名称' placeholder: '请输入项目名称',
} },
}, },
{ {
field: 'noticeCode', field: 'noticeCode',
label: '通知书编号', label: '通知书编号',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入通知书编号' placeholder: '请输入通知书编号',
} },
}, },
{ {
field: 'sendStatus', field: 'sendStatus',
@ -104,10 +104,10 @@ const queryFormColumns = [
{ label: '未发送', value: 'not_sent' }, { label: '未发送', value: 'not_sent' },
{ label: '已发送', value: 'sent' }, { label: '已发送', value: 'sent' },
{ label: '已确认', value: 'confirmed' }, { label: '已确认', value: 'confirmed' },
{ label: '已签约', value: 'contracted' } { label: '已签约', value: 'contracted' },
] ],
} },
} },
] ]
// //
@ -126,7 +126,7 @@ const tableColumns: TableColumnData[] = [
{ title: '联系人', dataIndex: 'contactPerson', width: 100 }, { title: '联系人', dataIndex: 'contactPerson', width: 100 },
{ title: '联系电话', dataIndex: 'contactPhone', width: 120 }, { title: '联系电话', dataIndex: 'contactPhone', width: 120 },
{ title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true }, { title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true },
{ title: '操作', slotName: 'action', width: 200, fixed: 'right' } { title: '操作', slotName: 'action', width: 200, fixed: 'right' },
] ]
// //
@ -147,7 +147,7 @@ const dataList = ref([
confirmTime: '2024-02-17 10:15:00', confirmTime: '2024-02-17 10:15:00',
contactPerson: '李经理', contactPerson: '李经理',
contactPhone: '13800138001', contactPhone: '13800138001',
remark: '已成功签署合同' remark: '已成功签署合同',
}, },
{ {
id: 2, id: 2,
@ -164,7 +164,7 @@ const dataList = ref([
confirmTime: '2024-02-22 11:30:00', confirmTime: '2024-02-22 11:30:00',
contactPerson: '王经理', contactPerson: '王经理',
contactPhone: '13800138002', contactPhone: '13800138002',
remark: '等待签署正式合同' remark: '等待签署正式合同',
}, },
{ {
id: 3, id: 3,
@ -181,8 +181,8 @@ const dataList = ref([
confirmTime: '', confirmTime: '',
contactPerson: '张经理', contactPerson: '张经理',
contactPhone: '13800138003', contactPhone: '13800138003',
remark: '已发送,等待对方确认' remark: '已发送,等待对方确认',
} },
]) ])
const pagination = reactive({ const pagination = reactive({
@ -190,16 +190,16 @@ const pagination = reactive({
pageSize: 10, pageSize: 10,
total: 3, total: 3,
showTotal: true, showTotal: true,
showPageSize: true showPageSize: true,
}) })
// //
const getSendStatusColor = (status: string) => { const getSendStatusColor = (status: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
'not_sent': 'gray', not_sent: 'gray',
'sent': 'blue', sent: 'blue',
'confirmed': 'orange', confirmed: 'orange',
'contracted': 'green' contracted: 'green',
} }
return colorMap[status] || 'gray' return colorMap[status] || 'gray'
} }
@ -207,10 +207,10 @@ const getSendStatusColor = (status: string) => {
// //
const getSendStatusText = (status: string) => { const getSendStatusText = (status: string) => {
const textMap: Record<string, string> = { const textMap: Record<string, string> = {
'not_sent': '未发送', not_sent: '未发送',
'sent': '已发送', sent: '已发送',
'confirmed': '已确认', confirmed: '已确认',
'contracted': '已签约' contracted: '已签约',
} }
return textMap[status] || status return textMap[status] || status
} }
@ -230,7 +230,7 @@ const reset = () => {
sendStatus: '', sendStatus: '',
noticeDate: '', noticeDate: '',
page: 1, page: 1,
size: 10 size: 10,
}) })
pagination.current = 1 pagination.current = 1
search() search()

Some files were not shown because too many files have changed in this diff Show More