Revert "Merge remote-tracking branch 'origin/jgqweb'"

This reverts commit 1648f3df96, reversing
changes made to c76d6beeb0.
This commit is contained in:
wxy 2025-08-04 17:10:11 +08:00
parent d25a34e579
commit 1307eaf651
225 changed files with 11448 additions and 16951 deletions

BIN
.env

Binary file not shown.

View File

@ -4,7 +4,8 @@ VITE_API_PREFIX = '/dev-api'
# 接口地址 # 接口地址
# VITE_API_BASE_URL = 'http://pms.dtyx.net:9158/' # VITE_API_BASE_URL = 'http://pms.dtyx.net:9158/'
VITE_API_BASE_URL = 'http://localhost:8888/' # VITE_API_BASE_URL = 'http://localhost:8888/'
VITE_API_BASE_URL = 'http://10.18.34.213:8888/'
# 接口地址 (WebSocket) # 接口地址 (WebSocket)
VITE_API_WS_URL = 'ws://localhost:8000' VITE_API_WS_URL = 'ws://localhost:8000'

View File

@ -2,6 +2,5 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" /> <mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/Industrial-image-management-system---web" vcs="Git" />
</component> </component>
</project> </project>

View File

@ -1,147 +0,0 @@
# 设备中心模块问题修复和改进总结
## 发现的问题
### 1. 弹窗组件问题
- **缺少查看模式**:原弹窗组件没有区分查看、编辑、新增模式
- **表单验证不完善**:缺少详细的验证规则和错误处理
- **缺少加载状态**提交时没有loading状态管理
- **表单重置逻辑不完善**:取消时没有正确重置表单
- **缺少调试功能**:开发环境下缺少调试工具
### 2. 主页面问题
- **数据转换逻辑复杂**:存在过多的兼容性处理,可能导致数据不一致
- **错误处理不够详细**:错误信息不够具体
- **缺少详情页面跳转**:没有设备详情页面的入口
- **对象引用问题**:直接传递对象引用可能导致数据污染
### 3. 功能缺失
- **缺少设备详情页面**:没有专门的设备详情展示页面
- **缺少维护记录管理**:没有设备维护记录的展示和管理
- **缺少使用记录**:没有设备使用历史的展示
- **缺少文件管理**:没有设备相关文件的管理功能
## 修复和改进内容
### 1. 弹窗组件优化 (`DeviceModal.vue`)
#### 新增功能
- ✅ **查看模式支持**:添加了查看模式,禁用所有输入框
- ✅ **完善的表单验证**:添加了详细的验证规则和长度限制
- ✅ **加载状态管理**添加了提交时的loading状态
- ✅ **表单重置优化**:完善了表单重置逻辑
- ✅ **调试功能**:开发环境下添加了调试按钮
- ✅ **错误处理优化**:更详细的错误信息处理
#### 技术改进
- ✅ **响应式表单数据**使用reactive管理表单数据
- ✅ **计算属性优化**:添加了表单有效性计算
- ✅ **监听器优化**:优化了数据变化监听逻辑
- ✅ **类型安全**完善了TypeScript类型定义
### 2. 主页面优化 (`index.vue`)
#### 功能改进
- ✅ **数据转换优化**:简化了数据转换逻辑,提高一致性
- ✅ **错误处理增强**:添加了更详细的错误信息处理
- ✅ **详情页面跳转**:添加了设备详情页面的跳转功能
- ✅ **对象深拷贝**:使用展开运算符避免对象引用问题
#### 用户体验改进
- ✅ **删除确认优化**:添加了更明确的删除确认提示
- ✅ **操作反馈优化**:改进了操作成功/失败的提示信息
### 3. 新增设备详情页面 (`detail.vue`)
#### 功能特性
- ✅ **基本信息展示**:设备的基本信息展示
- ✅ **状态信息展示**:设备的各种状态信息
- ✅ **维护记录管理**:设备维护记录的展示和管理
- ✅ **使用记录展示**:设备使用历史的展示
- ✅ **位置变更记录**:设备位置变更历史
- ✅ **文件管理**:设备相关文件的上传和管理
#### 技术实现
- ✅ **响应式数据管理**使用ref管理页面数据
- ✅ **路由参数处理**正确处理路由参数获取设备ID
- ✅ **API集成**集成设备详情API
- ✅ **状态文本转换**:统一的状态文本转换函数
### 4. 路由配置优化
#### 新增路由
- ✅ **设备详情路由**:添加了设备详情页面的路由配置
- ✅ **参数传递**支持通过URL参数传递设备ID
## 参考training模块的实现
### 借鉴的设计模式
1. **弹窗组件设计**参考了TrainingPlanModal的弹窗设计模式
2. **表单验证机制**:采用了相同的表单验证和错误处理机制
3. **数据管理方式**:使用了相同的响应式数据管理方式
4. **调试功能**:借鉴了开发环境下的调试工具设计
5. **详情页面设计**参考了TrainingDetail页面的布局和功能设计
### 技术实现对比
| 功能 | Training模块 | 设备管理模块 | 改进状态 |
|------|-------------|-------------|----------|
| 弹窗模式 | 查看/编辑/新增 | 查看/编辑/新增 | ✅ 已实现 |
| 表单验证 | 完善 | 完善 | ✅ 已实现 |
| 加载状态 | 有 | 有 | ✅ 已实现 |
| 调试功能 | 有 | 有 | ✅ 已实现 |
| 详情页面 | 有 | 有 | ✅ 已实现 |
| 错误处理 | 详细 | 详细 | ✅ 已实现 |
## 使用说明
### 1. 设备列表页面
- **搜索功能**:支持按设备名称、类型、状态等条件搜索
- **新增设备**:点击"新增设备"按钮打开新增弹窗
- **查看设备**:点击"查看"按钮以只读模式查看设备信息
- **编辑设备**:点击"编辑"按钮修改设备信息
- **详情页面**:点击"详情"按钮跳转到设备详情页面
- **设备操作**:支持分配、归还、删除等操作
### 2. 设备详情页面
- **基本信息**:展示设备的基本信息
- **状态信息**:展示设备的各种状态
- **维护记录**:查看和管理设备维护记录
- **使用记录**:查看设备使用历史
- **位置变更**:查看设备位置变更历史
- **文件管理**:上传和管理设备相关文件
### 3. 开发调试
- **调试按钮**:开发环境下显示调试按钮
- **表单测试**:可以测试表单数据绑定
- **测试数据**:可以填充测试数据
- **数据清空**:可以清空表单数据
## 后续优化建议
### 1. 功能扩展
- [ ] 添加设备维护记录的增删改功能
- [ ] 实现设备使用记录的实时更新
- [ ] 添加设备状态变更的审批流程
- [ ] 实现设备文件的在线预览功能
### 2. 性能优化
- [ ] 添加数据缓存机制
- [ ] 实现分页加载优化
- [ ] 添加数据预加载功能
### 3. 用户体验
- [ ] 添加操作确认的快捷键支持
- [ ] 实现批量操作功能
- [ ] 添加数据导出功能
- [ ] 实现高级搜索功能
## 总结
通过参考training模块的实现成功修复了设备中心模块的主要问题并添加了缺失的功能。主要改进包括
1. **弹窗组件**:完善了查看、编辑、新增模式,添加了完善的表单验证和错误处理
2. **主页面**:优化了数据转换逻辑,改进了错误处理,添加了详情页面跳转
3. **详情页面**:新增了完整的设备详情展示页面,包含维护记录、使用记录等功能
4. **路由配置**:添加了设备详情页面的路由配置
这些改进大大提升了设备中心模块的功能完整性和用户体验使其与training模块保持了一致的设计标准和实现质量。

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

@ -35,7 +35,7 @@ export default antfu(
rules: { rules: {
'curly': ['off', 'all'], // 对所有控制语句强制使用一致的大括号样式 'curly': ['off', 'all'], // 对所有控制语句强制使用一致的大括号样式
'no-new': 'off', // 不允许在赋值或比较之外使用 new 运算符 'no-new': 'off', // 不允许在赋值或比较之外使用 new 运算符
'no-console': 'off', // 允许使用 console // 'no-console': 'error', // 禁止使用 console
'style/arrow-parens': ['error', 'always'], // 箭头函数参数需要括号 'style/arrow-parens': ['error', 'always'], // 箭头函数参数需要括号
'style/brace-style': ['error', '1tbs', { allowSingleLine: true }], // 对块执行一致的大括号样式 'style/brace-style': ['error', '1tbs', { allowSingleLine: true }], // 对块执行一致的大括号样式
'regexp/no-unused-capturing-group': 'off', 'regexp/no-unused-capturing-group': 'off',

4301
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,9 @@
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"dayjs": "^1.11.4", "dayjs": "^1.11.4",
"echarts": "^5.4.2", "echarts": "^5.4.2",
"html2canvas": "^1.4.1",
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"jspdf": "^3.0.1",
"lint-staged": "^15.2.10", "lint-staged": "^15.2.10",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mitt": "^3.0.0", "mitt": "^3.0.0",

View File

@ -17,14 +17,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAppStore, useUserStore } from '@/stores' import { useAppStore, useUserStore } from '@/stores'
// 1
defineOptions({ name: 'App' }) defineOptions({ name: 'App' })
const userStore = useUserStore() const userStore = useUserStore()
const appStore = useAppStore() const appStore = useAppStore()
appStore.initTheme() appStore.initTheme()
appStore.initSiteConfig() appStore.initSiteConfig()
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.loading-icon { .loading-icon {
animation: arco-loading-circle 1s infinite cubic-bezier(0,0,1,1); animation: arco-loading-circle 1s infinite cubic-bezier(0,0,1,1);

View File

@ -1,12 +1,12 @@
import type { AttachInfoData, BusinessTypeResult } from './type'
import http from '@/utils/http' import http from '@/utils/http'
const { request } = http const { request } = http
import type { AttachInfoData, BusinessTypeResult } from './type'
/** /**
* *
* @param businessType * @param businessType
* @param files * @param files
* @returns
*/ */
export function batchAddAttachment(businessType: string, formData: FormData) { export function batchAddAttachment(businessType: string, formData: FormData) {
return request<AttachInfoData[]>({ return request<AttachInfoData[]>({
@ -14,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'
}, }
}) })
} }
@ -30,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'
}, }
}) })
} }
/** /**
@ -45,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'
}, }
}) })
} }
/** /**
@ -60,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'
}, }
}) })
} }
/** /**
@ -70,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'
}) })
} }
@ -81,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'
}) })
} }
@ -92,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 type { AttendanceRecordReq, AttendanceRecordResp } from './type'
import http from '@/utils/http' import http from '@/utils/http'
import type { AttendanceRecordReq, AttendanceRecordResp } from './type'
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

@ -3,13 +3,13 @@ export interface AttendanceRecordReq {
recordImage?: string recordImage?: string
recordPosition?: string recordPosition?: string
recordPositionLabel?: string recordPositionLabel?: string
} }
/** 新增考勤记录响应体 */ /** 新增考勤记录响应体 */
export interface AttendanceRecordResp { export interface AttendanceRecordResp {
code: number code: number
data: object data: object
msg: string msg: string
status: number status: number
success: boolean success: boolean
} }

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 { type ApiMenuItem, convertMenuData } from '@/utils/menuConverter' import { convertMenuData, type ApiMenuItem } from '@/utils/menuConverter'
export type * from './type' export type * from './type'

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

@ -1,49 +0,0 @@
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`)
}

View File

@ -1,207 +0,0 @@
/**
*
*/
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

@ -16,6 +16,7 @@ export * as InsuranceTypeAPI from './insurance-type'
export * as HealthRecordAPI from './health-record' export * as HealthRecordAPI from './health-record'
export * as InsuranceFileAPI from './insurance-file' export * as InsuranceFileAPI from './insurance-file'
export * as EmployeeAPI from './employee' export * as EmployeeAPI from './employee'
export * as RegulationAPI from './regulation'
export * from './area/type' export * from './area/type'
export * from './auth/type' export * from './auth/type'

View File

@ -87,7 +87,7 @@ export const detectDefects = (params: DefectDetectionRequest) => {
} }
/** @desc 手动添加缺陷记录 */ /** @desc 手动添加缺陷记录 */
export const addManualDefect = (params: ManualDefectAddRequest, imageId: string) => { export const addManualDefect = (params: ManualDefectAddRequest,imageId:string) => {
return http.post<ManualDefectAddResponse>(`/defect/${imageId}`, params) return http.post<ManualDefectAddResponse>(`/defect/${imageId}`, params)
} }
@ -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 获取缺陷列表 */
@ -111,7 +111,7 @@ export const getDefectList = (params: DefectListParams) => {
msg: string msg: string
status: number status: number
success: boolean success: boolean
}>('/defect/list', params) }>('/defect/list', params )
} }
/** @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,20 +321,21 @@ 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

@ -153,7 +153,7 @@ export interface IndustrialImage {
name: string name: string
/** 图像路径 */ /** 图像路径 */
path: string path: string
/** 图像路径API返回字段 */ /** 图像路径API返回字段*/
imagePath?: string imagePath?: string
/** 缩略图路径 */ /** 缩略图路径 */
thumbnailPath?: string thumbnailPath?: string

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 type { InsuranceInfo, InsuranceListParams, InsuranceListResponse, RenewInsuranceParams } from './type'
import http from '@/utils/http' import http from '@/utils/http'
import type { InsuranceInfo, InsuranceListParams, InsuranceListResponse, RenewInsuranceParams } from './type'
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,7 +1,7 @@
/** 保险信息接口 */ /** 保险信息接口 */
export interface InsuranceInfo { export interface InsuranceInfo {
id?: string id?: string
attachInfoId: string attachInfoId:string
insuranceCompanyId: string insuranceCompanyId: string
insuranceTypeId: string insuranceTypeId: string
userId: string userId: string

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) {
@ -37,4 +37,4 @@ export function deleteRule(id: string) {
// 我的绩效 // 我的绩效
export function getMyEvaluation() { export function getMyEvaluation() {
return http.get('/performance-evaluation/my') return http.get('/performance-evaluation/my')
} }

View File

@ -9,10 +9,10 @@ export interface PerformanceDimension {
createTime?: string createTime?: string
updateBy?: string updateBy?: string
updateTime?: string updateTime?: string
} }
/** 绩效细则 */ /** 绩效细则 */
export interface PerformanceRule { export interface PerformanceRule {
ruleId: string ruleId: string
ruleName: string ruleName: string
description?: string description?: string
@ -25,15 +25,15 @@ export interface PerformanceRule {
createTime?: string createTime?: string
updateBy?: string updateBy?: string
updateTime?: string updateTime?: string
} }
/** 查询参数 */ /** 查询参数 */
export interface DimensionQuery { export interface DimensionQuery {
dimensionName?: string dimensionName?: string
status?: 0 | 1 status?: 0 | 1
} }
export interface RuleQuery { export interface RuleQuery {
dimensionName?: string dimensionName?: string
ruleName?: string ruleName?: string
status?: 0 | 1 status?: 0 | 1
} }

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

@ -23,9 +23,9 @@ export function deleteTaskGroup(id: number) {
return http.del(`${BASE_URL}/group/${id}`) return http.del(`${BASE_URL}/group/${id}`)
} }
/** @desc 查询任务列表 */ /** @desc 查询任务列表(标准导出) */
export function listTask(query: T.TaskPageQuery) { export const listTask = (params: any) => {
return http.get<PageRes<T.TaskResp[]>>(`${BASE_URL}`, query) return http.get('/project-task/list', params)
} }
/** @desc 获取任务详情 */ /** @desc 获取任务详情 */
@ -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 { SalaryCreateRequest, SalaryQuery, SalaryRecord } from '@/views/salary-management/types' import type { SalaryRecord, SalaryQuery, SalaryCreateRequest } 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,3 +1,4 @@
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'
@ -5,13 +6,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;
}) });
} }
/** /**
@ -22,31 +23,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}`);
} }
/** /**
@ -55,6 +56,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,45 @@
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}`)
} }

View File

@ -4,6 +4,7 @@ import http from '@/utils/http'
export type * from './type' export type * from './type'
const BASE_URL = '/system/role' const BASE_URL = '/system/role'
const BASE_URL_NEW = '/role'
/** @desc 查询角色列表(已废弃) */ /** @desc 查询角色列表(已废弃) */
export function listRole(query: T.RoleQuery) { export function listRole(query: T.RoleQuery) {
@ -72,7 +73,7 @@ export function updateRolePermission(id: string, data: any) {
/** @desc 查询角色关联用户 */ /** @desc 查询角色关联用户 */
export function listRoleUser(id: string, query: T.RoleUserPageQuery) { export function listRoleUser(id: string, query: T.RoleUserPageQuery) {
return http.get<PageRes<T.RoleUserResp[]>>(`${BASE_URL}/${id}/user`, query) return http.get<PageRes<T.RoleUserResp[]>>(`${BASE_URL_NEW}/${id}/user`, query)
} }
/** @desc 分配角色给用户 */ /** @desc 分配角色给用户 */
@ -87,5 +88,5 @@ export function unassignFromUsers(userRoleIds: Array<string | number>) {
/** @desc 查询角色关联用户 ID */ /** @desc 查询角色关联用户 ID */
export function listRoleUserId(id: string) { export function listRoleUserId(id: string) {
return http.get(`${BASE_URL}/${id}/user/id`) return http.get(`${BASE_URL_NEW}/${id}/user`)
} }

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

@ -1,44 +0,0 @@
import http from '@/utils/http'
import type * as T from '@/types/training.d'
const BASE_URL = '/training'
/** @desc 分页查询培训计划列表 */
export function pageTrainingPlan(query: T.TrainingPlanPageQuery) {
return http.get<T.TrainingPlanResp[]>(`${BASE_URL}/plan/page`, query)
}
/** @desc 查询培训计划列表 */
export function listTrainingPlan(query?: T.TrainingPlanPageQuery) {
return http.get<T.TrainingPlanResp[]>(`${BASE_URL}/plan/list`, query)
}
/** @desc 查询培训计划详情 */
export function getTrainingPlanDetail(planId: string) {
return http.get<T.TrainingPlanResp>(`${BASE_URL}/plan/detail/${planId}`)
}
/** @desc 新增培训计划 */
export function createTrainingPlan(data: T.TrainingPlanReq) {
return http.post(`${BASE_URL}/plan`, data)
}
/** @desc 更新培训计划 */
export function updateTrainingPlan(planId: string, data: T.TrainingPlanReq) {
return http.put(`${BASE_URL}/plan/${planId}`, data)
}
/** @desc 删除培训计划 */
export function deleteTrainingPlan(planId: string) {
return http.del(`${BASE_URL}/plan/${planId}`)
}
/** @desc 发布培训计划 */
export function publishTrainingPlan(planId: string) {
return http.put(`${BASE_URL}/plan/${planId}/publish`)
}
/** @desc 取消培训计划 */
export function cancelTrainingPlan(planId: string) {
return http.put(`${BASE_URL}/plan/${planId}/cancel`)
}

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 v-if="form.settings.includes('autoAnnotate')" label="标注类型"> <a-form-item label="标注类型" v-if="form.settings.includes('autoAnnotate')">
<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">
<IconUpload size="48" /> <icon-upload size="48" />
</div> </div>
<div class="upload-text"> <div class="upload-text">
<p>点击或拖拽图像文件到此区域</p> <p>点击或拖拽图像文件到此区域</p>
@ -101,12 +101,12 @@
</div> </div>
<!-- 文件列表 --> <!-- 文件列表 -->
<div v-if="fileList.length > 0" class="file-list"> <div class="file-list" v-if="fileList.length > 0">
<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>
<IconDelete /> <icon-delete />
</template> </template>
清空 清空
</a-button> </a-button>
@ -133,7 +133,7 @@
@click="removeFile(index)" @click="removeFile(index)"
> >
<template #icon> <template #icon>
<IconClose /> <icon-close />
</template> </template>
</a-button> </a-button>
</div> </div>
@ -142,7 +142,7 @@
</div> </div>
<!-- 导入进度 --> <!-- 导入进度 -->
<div v-if="importing" class="import-progress"> <div class="import-progress" v-if="importing">
<a-progress <a-progress
:percent="importProgress" :percent="importProgress"
:status="importStatus" :status="importStatus"
@ -152,7 +152,7 @@
</div> </div>
<!-- 导入结果 --> <!-- 导入结果 -->
<div v-if="importResult" class="import-result"> <div class="import-result" v-if="importResult">
<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 v-if="importResult.failed.length > 0" class="result-details"> <div class="result-details" v-if="importResult.failed.length > 0">
<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 { computed, ref, watch } from 'vue' import { ref, computed, watch } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import { import {
IconClose,
IconDelete,
IconUpload, IconUpload,
IconDelete,
IconClose
} 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 {
ImageImportParams,
IndustrialImage,
ProjectTreeNode, ProjectTreeNode,
IndustrialImage,
ImageImportParams
} 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 `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}` return parseFloat((bytes / Math.pow(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,15 +428,16 @@ 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
@ -468,7 +469,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)) }"
:title="`部件ID: ${getPartId(part)}, 选中: ${String(selectedPartId) === String(getPartId(part))}`"
@click="selectPart(part)" @click="selectPart(part)"
:title="`部件ID: ${getPartId(part)}, 选中: ${String(selectedPartId) === String(getPartId(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 v-if="selectedPart" class="part-info"> <div class="part-info" v-if="selectedPart">
<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" :disabled="!hasSelectedImages" @click="handleRemoveImages"> <button class="action-button" @click="handleRemoveImages" :disabled="!hasSelectedImages">
<span class="button-icon">-</span> <span class="button-icon">-</span>
移除图像 移除图像
</button> </button>
<!-- 隐藏的文件输入框 --> <!-- 隐藏的文件输入框 -->
<input <input
ref="fileInput"
type="file" type="file"
ref="fileInput"
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" :checked="allImagesSelected" @change="toggleSelectAll"> <input type="checkbox" @change="toggleSelectAll" :checked="allImagesSelected">
</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 v-model="image.selected" type="checkbox" @click.stop> <input type="checkbox" v-model="image.selected" @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 v-model="imageInfo.startTime" type="text" placeholder="开始时间"> <input type="text" v-model="imageInfo.startTime" placeholder="开始时间">
<span class="range-separator"></span> <span class="range-separator"></span>
<input v-model="imageInfo.endTime" type="text" placeholder="结束时间"> <input type="text" v-model="imageInfo.endTime" 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 v-model="imageInfo.minTemperature" type="number" step="0.1" min="0" max="50"> <input type="number" v-model="imageInfo.minTemperature" step="0.1" min="0" max="50">
<button class="range-btn" @click="imageInfo.minTemperature = Math.min(50, imageInfo.minTemperature + 1)">+</button> <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 v-model="imageInfo.maxTemperature" type="number" step="0.1" min="0" max="50"> <input type="number" v-model="imageInfo.maxTemperature" step="0.1" min="0" max="50">
<button class="range-btn" @click="imageInfo.maxTemperature = Math.min(50, imageInfo.maxTemperature + 1)">+</button> <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 v-model="imageInfo.humidity" type="number" min="0" max="100"> <input type="number" v-model="imageInfo.humidity" min="0" max="100">
<button class="range-btn" @click="imageInfo.humidity = Math.min(100, imageInfo.humidity + 1)">+</button> <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 v-model="imageInfo.captureMethod" type="radio" value="无人机航拍"> <input type="radio" v-model="imageInfo.captureMethod" value="无人机航拍">
<span class="radio-label">无人机航拍</span> <span class="radio-label">无人机航拍</span>
</label> </label>
<label class="radio-option"> <label class="radio-option">
<input v-model="imageInfo.captureMethod" type="radio" value="人工拍摄"> <input type="radio" v-model="imageInfo.captureMethod" value="人工拍摄">
<span class="radio-label">人工拍摄</span> <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 v-model="imageInfo.captureDistance" type="number" min="0"> <input type="number" v-model="imageInfo.captureDistance" min="0">
<button class="range-btn" @click="imageInfo.captureDistance = imageInfo.captureDistance + 1">+</button> <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 v-model="imageInfo.operator" type="text"> <input type="text" v-model="imageInfo.operator">
</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 v-model="imageInfo.cameraModel" type="text"> <input type="text" v-model="imageInfo.cameraModel">
</div> </div>
</div> </div>
</div> </div>
@ -262,24 +262,18 @@
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"
:disabled="!canGoNext"
@click="nextStep" @click="nextStep"
> :disabled="!canGoNext"
下一步 >下一步</button>
</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>
@ -287,7 +281,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, onBeforeUnmount, reactive, ref } from 'vue' import { ref, computed, reactive, onBeforeUnmount } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
// //
@ -358,7 +352,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))
}) })
// //
@ -366,8 +360,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,
@ -377,7 +371,7 @@ const imageInfo = reactive<ImageInfo>({
captureMethod: '无人机航拍', captureMethod: '无人机航拍',
captureDistance: 50, captureDistance: 50,
operator: '', operator: '',
cameraModel: 'ILCE-7RM4', cameraModel: 'ILCE-7RM4'
}) })
// //
@ -440,7 +434,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)
@ -454,7 +448,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
} }
}) })
@ -469,13 +463,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)
} }
// //
@ -486,17 +480,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)
}) })
// //
@ -538,7 +532,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 = {
@ -546,14 +540,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('图像导入成功')
@ -575,7 +569,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)
} }
@ -584,8 +578,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,
@ -593,14 +587,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 v-if="!isCollapsed" class="header-actions"> <div class="header-actions" v-if="!isCollapsed">
<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><IconUpload /></template> <template #icon><icon-upload /></template>
导入图像 导入图像
</a-button> </a-button>
</slot> </slot>
<div v-if="showSearch" class="search-bar"> <div class="search-bar" v-if="showSearch">
<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>
<IconUp /> <icon-up />
</template> </template>
收起 收起
</a-button> </a-button>
</div> </div>
</div> </div>
<div v-show="!isCollapsed" class="image-grid"> <div class="image-grid" v-show="!isCollapsed">
<div v-if="imageList.length === 0" class="empty-data"> <div v-if="imageList.length === 0" class="empty-data">
<IconImage class="empty-icon" /> <icon-image class="empty-icon" />
<p>{{ emptyText }}</p> <p>{{ emptyText }}</p>
</div> </div>
@ -51,8 +51,8 @@
@error="handleImageError" @error="handleImageError"
@load="handleImageLoad" @load="handleImageLoad"
/> />
<div v-if="!image.imagePath" class="image-placeholder"> <div class="image-placeholder" v-if="!image.imagePath">
<IconImage /> <icon-image />
<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)">
<IconEye /> <icon-eye />
</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)">
<IconSettings /> <icon-settings />
</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)">
<IconDelete /> <icon-delete />
</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 v-if="image.partName || image.shootingTime" class="thumbnail-extra"> <div class="thumbnail-extra" v-if="image.partName || image.shootingTime">
<span v-if="image.partName" class="part-name">{{ image.partName }}</span> <span v-if="image.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>
<IconDown /> <icon-down />
</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 {
IconDelete,
IconDown,
IconEye,
IconImage,
IconSettings,
IconUp,
IconUpload, IconUpload,
IconImage,
IconEye,
IconSettings,
IconDelete,
IconUp,
IconDown
} 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://pms.dtyx.net:9158'
}, },
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,10 +1,8 @@
<template> <template>
<div class="turbine-grid-container"> <div class="turbine-grid-container">
<div class="turbine-grid"> <div class="turbine-grid">
<div <div v-for="turbine in turbines" :key="turbine.id" class="turbine-card"
v-for="turbine in turbines" :key="turbine.id" class="turbine-card" :class="getStatusClass(turbine.status)">
: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>
@ -15,18 +13,16 @@
<div class="turbine-info"> <div class="turbine-info">
<div class="turbine-number"> <div class="turbine-number">
<a-input <a-input v-model="turbine.turbineNo" size="small" class="turbine-input" placeholder="请输入机组编号"
v-model="turbine.turbineNo" size="small" class="turbine-input" placeholder="请输入机组编号" @change="handleTurbineNoChange(turbine)" />
@change="handleTurbineNoChange(turbine)"
/>
</div> </div>
</div> </div>
<div class="turbine-actions"> <div class="turbine-actions">
<a-button type="text" size="mini" title="地图选点" @click="openMapModal(turbine)"> <a-button type="text" size="mini" @click="openMapModal(turbine)" title="地图选点">
<template #icon><icon-location /></template> <template #icon><icon-location /></template>
</a-button> </a-button>
<a-button type="text" size="mini" title="编辑" @click="editTurbine(turbine)"> <a-button type="text" size="mini" @click="editTurbine(turbine)" title="编辑">
<template #icon><icon-edit /></template> <template #icon><icon-edit /></template>
</a-button> </a-button>
</div> </div>
@ -44,6 +40,7 @@
</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 {
@ -66,7 +63,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>()
@ -75,7 +72,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:selected-keys="selectedKeys" v-model:selectedKeys="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,6 +19,7 @@ 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,6 +19,7 @@
<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,10 +76,11 @@
<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 { nextTick, onMounted, ref } from 'vue' import { onMounted, ref, nextTick } 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,9 +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

@ -2,7 +2,7 @@ import { createApp } from 'vue'
import ArcoVue, { Card, Drawer, Modal } from '@arco-design/web-vue' import ArcoVue, { Card, Drawer, Modal } from '@arco-design/web-vue'
import '@/styles/arco-ui/index.less' import '@/styles/arco-ui/index.less'
// import '@arco-themes/vue-gi-demo/index.less' // import '@arco-themes/vue-gi-demo/index.less'
import '@arco-design/web-vue/dist/arco.css' // import '@arco-design/web-vue/dist/arco.css'
// 额外引入 Arco Design Icon图标库 // 额外引入 Arco Design Icon图标库
import ArcoVueIcon from '@arco-design/web-vue/es/icon' import ArcoVueIcon from '@arco-design/web-vue/es/icon'

View File

@ -5,12 +5,6 @@ const Layout = () => import('@/layout/index.vue')
/** 系统路由 */ /** 系统路由 */
export const systemRoutes: RouteRecordRaw[] = [ export const systemRoutes: RouteRecordRaw[] = [
{
path: '/test-console',
name: 'TestConsole',
component: () => import('@/test-console.vue'),
meta: { title: 'Console测试', hidden: true },
},
{ {
path: '/login', path: '/login',
name: 'Login', name: 'Login',
@ -32,98 +26,166 @@ export const systemRoutes: RouteRecordRaw[] = [
// } // }
// ], // ],
// }, // },
{
path: '/regulation',
name: 'Regulation',
component: Layout,
redirect: '/regulation/system-regulation',
meta: { title: '制度管理', icon: 'file-text', hidden: false, sort: 1.5 },
children: [
{
path: '/regulation/system-regulation',
name: 'SystemRegulation',
component: () => import('@/views/regulation/repository.vue'),
meta: { title: '制度公告', icon: 'file-text', hidden: false },
},
{
path: '/regulation/process-management',
name: 'ProcessManagement',
component: () => import('@/views/regulation/confirm.vue'),
meta: { title: '制度公示', icon: 'workflow', hidden: false },
},
{
path: '/regulation/proposal',
name: 'RegulationProposal',
component: () => import('@/views/regulation/proposal/index.vue'),
meta: { title: '制度提案', icon: 'edit', hidden: false },
},
{
path: '/regulation/type',
name: 'RegulationType',
component: () => import('@/views/regulation/type/index.vue'),
meta: { title: '制度类型', icon: 'tag', hidden: false },
},
],
},
{ {
path: '/organization', path: '/organization',
name: 'Organization', name: 'Organization',
component: Layout, component: Layout,
redirect: '/organization/hr/member', redirect: '/organization/dept',
meta: { title: '组织架构', icon: 'user-group', hidden: false, sort: 2 }, meta: { title: '组织架构', icon: 'user-group', hidden: false, sort: 2 },
children: [ children: [
{ {
path: '/organization/hr', path: '/organization/user',
name: 'HRManagement', name: 'OrganizationUser',
component: () => import('@/components/ParentView/index.vue'),
redirect: '/organization/hr/member',
meta: { title: '人员管理', icon: 'user', hidden: false },
children: [
{
path: '/organization/hr/member',
name: 'HRMember',
component: () => import('@/views/system/user/index.vue'), component: () => import('@/views/system/user/index.vue'),
meta: { title: '成员', icon: 'user', hidden: false }, meta: { title: '用户管理', icon: 'user', hidden: false, sort: 2.25 },
}, },
{ {
path: '/organization/hr/dept', path: '/organization/dept',
name: 'HRDept', name: 'OrganizationDept',
component: () => import('@/views/system/dept/index.vue'), component: () => import('@/views/system/dept/index.vue'),
meta: { title: '部门', icon: 'dept', hidden: false }, meta: { title: '部门管理', icon: 'mind-mapping', hidden: false, sort: 2.5 },
}, },
{ {
path: '/organization/hr/workload', path: '/organization/post',
name: 'HRWorkload', name: 'OrganizationPost',
component: () => import('@/views/hr/workload/index.vue'), component: () => import('@/views/system/post/index.vue'),
meta: { title: '工作量', icon: 'workload', hidden: false }, meta: { title: '岗位管理', icon: 'nav', hidden: false, sort: 2.75 },
},
{
path: '/organization/hr/attendance',
name: 'HRAttendance',
component: () => import('@/views/hr/attendance/index.vue'),
meta: { title: '考勤', icon: 'attendance', hidden: false },
},
{
path: '/organization/hr/performance',
name: 'HRPerformance',
component: () => import('@/components/ParentView/index.vue'),
meta: { title: '绩效', icon: 'performance', hidden: false },
children: [
{
path: '/organization/hr/performance/dimention',
name: 'Dimention',
component: () => import('@/views/performance/setting/index.vue'),
meta: { title: '绩效维度', icon: 'performance', hidden: false },
},
{
path: '/organization/hr/performance/rule',
name: 'Rule',
component: () => import('@/views/performance/rule.vue'),
meta: { title: '绩效细则', icon: 'performance', hidden: false },
},
{
path: '/organization/hr/performance/my',
name: 'MyPerformance',
component: () => import('@/views/performance/my.vue'),
meta: { title: '我的绩效', icon: 'performance', hidden: false },
},
],
},
{
path: '/organization/hr/salary',
name: 'HRSalary',
component: () => import('@/components/ParentView/index.vue'),
redirect: '/organization/hr/salary/overview',
meta: { title: '工资', icon: 'salary', hidden: false },
children: [
{
path: '/organization/hr/salary/overview',
name: 'HRSalaryOverview',
component: () => import('@/components/ParentView/index.vue'),
meta: { title: '工资概览', icon: 'salary', hidden: false },
children: [
{
path: '/organization/hr/salary/payroll',
name: 'Payroll',
component: () => import('@/views/salary-management/index.vue'),
meta: { title: '工资单', icon: 'salary', hidden: false },
},
],
}, },
// {
// path: '/organization/hr/workload',
// name: 'HRWorkload',
// component: () => import('@/views/hr/workload/index.vue'),
// meta: { title: '任务管理', icon: 'bookmark', hidden: false },
// },
], ],
}, },
// { // {
// path: '/organization',
// name: 'Organization',
// component: Layout,
// redirect: '/organization/hr/member',
// meta: { title: '组织架构', icon: 'user-group', hidden: false, sort: 2 },
// children: [
// {
// path: '/organization/hr',
// name: 'HRManagement',
// component: () => import('@/components/ParentView/index.vue'),
// redirect: '/organization/hr/member',
// meta: { title: '人员管理', icon: 'user', hidden: false },
// children: [
// {
// path: '/organization/hr/member',
// name: 'HRMember',
// component: () => import('@/views/system/user/index.vue'),
// meta: { title: '成员', icon: 'user', hidden: false },
// },
// {
// path: '/organization/hr/dept',
// name: 'HRDept',
// component: () => import('@/views/system/dept/index.vue'),
// meta: { title: '部门', icon: 'dept', hidden: false },
// },
// {
// path: '/organization/hr/workload',
// name: 'HRWorkload',
// component: () => import('@/views/hr/workload/index.vue'),
// meta: { title: '任务管理', icon: 'workload', hidden: false },
// },
// {
// path: '/organization/hr/attendance',
// name: 'HRAttendance',
// component: () => import('@/views/hr/attendance/index.vue'),
// meta: { title: '考勤', icon: 'attendance', hidden: false },
// },
// {
// path: '/organization/hr/performance',
// name: 'HRPerformance',
// component: () => import('@/components/ParentView/index.vue'),
// meta: { title: '绩效', icon: 'performance', hidden: false },
// children: [
// {
// path: '/organization/hr/performance/dimention',
// name: 'Dimention',
// component: () => import('@/views/performance/setting/index.vue'),
// meta: { title: '绩效维度', icon: 'performance', hidden: false },
//
// },
// {
// path: '/organization/hr/performance/rule',
// name: 'Rule',
// component: () => import('@/views/performance/rule.vue'),
// meta: { title: '绩效细则', icon: 'performance', hidden: false },
//
// },
// {
// path: '/organization/hr/performance/my',
// name: 'MyPerformance',
// component: () => import('@/views/performance/my.vue'),
// meta: { title: '我的绩效', icon: 'performance', hidden: false },
//
// },
// ],
// },
// {
// path: '/organization/hr/salary',
// name: 'HRSalary',
// component: () => import('@/components/ParentView/index.vue'),
// redirect: '/organization/hr/salary/overview',
// meta: { title: '工资', icon: 'salary', hidden: false },
// children: [
// {
// path: '/organization/hr/salary/overview',
// name: 'HRSalaryOverview',
// component: () => import('@/components/ParentView/index.vue'),
// meta: { title: '工资概览', icon: 'salary', hidden: false },
// children: [
// {
// path: '/organization/hr/salary/payroll',
// name: 'Payroll',
// component: () => import('@/views/salary-management/index.vue'),
// meta: { title: '工资单', icon: 'salary', hidden: false },
//
// },
// ],
// },
// ],
// },
//
// {
// path: '/organization/hr/salary/insurance', // path: '/organization/hr/salary/insurance',
// name: 'HRInsurance', // name: 'HRInsurance',
// component: () => import('@/components/ParentView/index.vue'), // component: () => import('@/components/ParentView/index.vue'),
@ -159,172 +221,139 @@ export const systemRoutes: RouteRecordRaw[] = [
// name: 'HRPersonalInfo', // name: 'HRPersonalInfo',
// component: () => import('@/views/hr/salary/insurance/personal-info/index.vue'), // component: () => import('@/views/hr/salary/insurance/personal-info/index.vue'),
// meta: { title: '个人信息', icon: 'user', hidden: false }, // meta: { title: '个人信息', icon: 'user', hidden: false },
// }
// ]
// }, // },
{ // ],
path: '/organization/hr/salary/system-insurance/health-management', // },
name: 'HRSystemHealthManagement', //
component: () => import('@/views/hr/salary/system-insurance/health-management/index.vue'), // {
meta: { title: '健康档案管理', icon: 'heart', hidden: false }, // path: '/organization/hr/salary/system-insurance/health-management',
}, // name: 'HRSystemHealthManagement',
{ // component: () => import('@/views/hr/salary/system-insurance/health-management/index.vue'),
path: '/organization/hr/salary/system-insurance', // meta: { title: '健康档案管理', icon: 'heart', hidden: false },
name: 'HRSystemInsurance', // },
component: () => import('@/components/ParentView/index.vue'), // {
redirect: '/organization/hr/salary/system-insurance/overview', // path: '/organization/hr/salary/system-insurance',
meta: { title: '人员保险', icon: 'settings', hidden: false }, // name: 'HRSystemInsurance',
children: [ // component: () => import('@/components/ParentView/index.vue'),
{ // redirect: '/organization/hr/salary/system-insurance/overview',
path: '/organization/hr/salary/system-insurance/overview', // meta: { title: '人员保险', icon: 'settings', hidden: false },
name: 'HRSystemInsuranceOverview', // children: [
component: () => import('@/views/hr/salary/system-insurance/overview/index.vue'), // {
meta: { title: '工作台概览', icon: 'dashboard', hidden: false }, // path: '/organization/hr/salary/system-insurance/overview',
}, // name: 'HRSystemInsuranceOverview',
{ // component: () => import('@/views/hr/salary/system-insurance/overview/index.vue'),
path: '/organization/hr/salary/system-insurance/management', // meta: { title: '工作台概览', icon: 'dashboard', hidden: false },
name: 'HRSystemInsuranceManagement', // },
component: () => import('@/views/hr/salary/system-insurance/management/index.vue'), // {
meta: { title: '保险管理', icon: 'shield', hidden: false }, // path: '/organization/hr/salary/system-insurance/management',
}, // name: 'HRSystemInsuranceManagement',
{ // component: () => import('@/views/hr/salary/system-insurance/management/index.vue'),
path: '/organization/hr/salary/system-insurance/file-management', // meta: { title: '保险管理', icon: 'shield', hidden: false },
name: 'HRSystemFileManagement', // },
component: () => import('@/views/hr/salary/system-insurance/file-management/index.vue'), // {
meta: { title: '保单文件管理', icon: 'file', hidden: false }, // path: '/organization/hr/salary/system-insurance/file-management',
}, // name: 'HRSystemFileManagement',
{ // component: () => import('@/views/hr/salary/system-insurance/file-management/index.vue'),
path: '/organization/hr/salary/system-insurance/company-management', // meta: { title: '保单文件管理', icon: 'file', hidden: false },
name: 'HRSystemCompanyManagement', // },
component: () => import('@/views/hr/salary/system-insurance/company-management/index.vue'), // {
meta: { title: '保险公司管理', icon: 'building', hidden: false }, // path: '/organization/hr/salary/system-insurance/company-management',
}, // name: 'HRSystemCompanyManagement',
{ // component: () => import('@/views/hr/salary/system-insurance/company-management/index.vue'),
path: '/organization/hr/salary/system-insurance/type-management', // meta: { title: '保险公司管理', icon: 'building', hidden: false },
name: 'HRSystemTypeManagement', // },
component: () => import('@/views/hr/salary/system-insurance/type-management/index.vue'), // {
meta: { title: '保险类型管理', icon: 'category', hidden: false }, // path: '/organization/hr/salary/system-insurance/type-management',
}, // name: 'HRSystemTypeManagement',
], // component: () => import('@/views/hr/salary/system-insurance/type-management/index.vue'),
}, // meta: { title: '保险类型管理', icon: 'category', hidden: false },
{ // },
path: '/organization/hr/salary/certification', // ],
name: 'HRCertification', // },
component: () => import('@/views/hr/salary/certification/index.vue'), // {
meta: { title: '人员资质管理', icon: 'idcard', hidden: false }, // path: '/organization/hr/salary/certification',
}, // name: 'HRCertification',
{ // component: () => import('@/views/hr/salary/certification/index.vue'),
path: '/organization/hr/contribution', // meta: { title: '人员资质管理', icon: 'idcard', hidden: false },
name: 'HRContribution', // },
component: () => import('@/views/hr/contribution/index.vue'), // {
meta: { title: '责献积分制度', icon: 'contribution', hidden: false }, // path: '/organization/hr/contribution',
}, // name: 'HRContribution',
], // component: () => import('@/views/hr/contribution/index.vue'),
}, // meta: { title: '责献积分制度', icon: 'contribution', hidden: false },
{ // },
path: '/organization/role', // ],
name: 'OrganizationRole', // },
component: () => import('@/views/system/role/index.vue'), // {
meta: { title: '角色管理', icon: 'role', hidden: false }, // path: '/organization/role',
}, // name: 'OrganizationRole',
], // component: () => import('@/views/system/role/index.vue'),
}, // meta: { title: '角色管理', icon: 'role', hidden: false },
// },
// ],
// },
{ {
path: '/asset-management', path: '/asset-management',
name: 'AssetManagement', name: 'AssetManagement',
component: Layout, component: Layout,
redirect: '/asset-management/device-management/device-center', redirect: '/asset-management/device/inventory',
meta: { title: '资产管理', icon: 'property-safety', hidden: false, sort: 3 }, meta: { title: '资产管理', icon: 'property-safety', hidden: false, sort: 3 },
children: [ children: [
{ {
path: '/asset-management/device-management', path: '/asset-management/intellectual-property1',
name: 'DeviceManagement', name: 'IntellectualProperty1',
component: () => import('@/components/ParentView/index.vue'), component: () => import('@/views/system-resource/information-system/software-management/index.vue'),
redirect: '/asset-management/device-management/device-center', meta: { title: '设备管理', icon: 'copyright', hidden: false },
meta: {
title: '设备管理',
icon: 'device',
hidden: false,
},
children: [ children: [
{ {
path: '/asset-management/device-management/device-center', path: '/asset-management/intellectual-property1',
name: 'DeviceCenter', name: 'IntellectualProperty11',
component: () => import('@/views/system-resource/device-management/index.vue'), component: () => import('@/views/system-resource/information-system/software-management/index.vue'),
meta: { meta: { title: '库存管理', hidden: false },
title: '设备中心',
icon: 'appstore',
hidden: false,
},
}, },
{ {
path: '/asset-management/device-management/device-detail/:id', path: '/asset-management/intellectual-property1',
name: 'DeviceDetail', name: 'IntellectualProperty12',
component: () => import('@/views/system-resource/device-management/detail.vue'), component: () => import('@/views/system-resource/information-system/software-management/index.vue'),
meta: { meta: { title: '设备采购', hidden: false },
title: '设备详情',
icon: 'info-circle',
hidden: true,
},
}, },
{ {
path: '/asset-management/device-management/procurement', path: '/asset-management/intellectual-property1',
name: 'DeviceProcurement', name: 'IntellectualProperty13',
component: () => import('@/views/system-resource/device-management/index.vue'), component: () => import('@/views/system-resource/information-system/software-management/index.vue'),
meta: { meta: { title: '在线管理', hidden: false },
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/device-management/online/drone', path: '/asset-management/intellectual-property11',
name: 'DeviceDrone', name: 'IntellectualProperty14',
component: () => import('@/views/system-resource/device-management/index.vue'), component: () => import('@/views/system-resource/information-system/software-management/index.vue'),
meta: { meta: { title: '无人机', hidden: false },
title: '无人机',
icon: 'drone',
hidden: false,
},
}, },
{ {
path: '/asset-management/device-management/online/nest', path: '/asset-management/intellectual-property12',
name: 'DeviceNest', name: 'IntellectualProperty15',
component: () => import('@/views/system-resource/device-management/index.vue'), component: () => import('@/views/system-resource/information-system/software-management/index.vue'),
meta: { meta: { title: '机巢', hidden: false },
title: '机巢',
icon: 'nest',
hidden: false,
},
}, },
{ {
path: '/asset-management/device-management/online/smart-terminal', path: '/asset-management/intellectual-property13',
name: 'DeviceSmartTerminal', name: 'IntellectualProperty16',
component: () => import('@/views/system-resource/device-management/index.vue'), component: () => import('@/views/system-resource/information-system/software-management/index.vue'),
meta: { meta: { title: '其他智能终端', hidden: false },
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/other-assets', path: '/asset-management/intellectual-property',
name: 'OtherAssets', name: 'IntellectualProperty',
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 },
}, },
@ -529,9 +558,9 @@ export const systemRoutes: RouteRecordRaw[] = [
}, },
}, },
{ {
path: 'project-management/project-template/information-retrieval', path: '/project-management/project-template/information-retrieval',
name: 'InformationRetrieval', name: 'InformationRetrieval',
component: () => import ('@/views/default/error/404.vue'), component: () => import ('@/views/project-management/bidding/information-retrieval/index.vue'),
meta: { meta: {
title: '信息检索(N)', title: '信息检索(N)',
icon: 'trophy', icon: 'trophy',
@ -643,11 +672,11 @@ export const systemRoutes: RouteRecordRaw[] = [
{ {
path: '/project-management/projects/device', path: '/project-management/projects/device',
name: 'ProjectDeviceManagement', name: 'DeviceManagement',
component: () => import('@/views/system-resource/device-management/index.vue'), component: () => import('@/views/system-resource/device-management/index.vue'),
meta: { meta: {
title: '项目设备管理', title: '设备管理',
icon: 'device', icon: 'none',
hidden: false, hidden: false,
}, },
}, },
@ -780,11 +809,11 @@ export const systemRoutes: RouteRecordRaw[] = [
{ {
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/attachment', path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/attachment',
name: 'AttachmentManagement', name: 'AttachmentManagement',
component: () => import('@/views/operation-platform/data-processing/data-storage/index.vue'), component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/data-storage/index.vue'),
meta: { title: '附件管理', icon: 'attachment', hidden: false }, meta: { title: '附件管理', icon: 'attachment', hidden: false },
}, },
{ {
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/model-config', path: '/construction-operation-platform/implementation-workflow/data-processing/model-config',
name: 'ModelConfig', name: 'ModelConfig',
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/model-config/index.vue'), component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/model-config/index.vue'),
meta: { title: '模型配置', icon: 'robot', hidden: false }, meta: { title: '模型配置', icon: 'robot', hidden: false },
@ -795,8 +824,23 @@ export const systemRoutes: RouteRecordRaw[] = [
{ {
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/preprocessed-data', path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/preprocessed-data',
name: 'PreprocessedData', name: 'PreprocessedData',
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/data-preprocessing/index.vue'), component: () => import('@/components/ParentView/index.vue'),
redirect: '/construction-operation-platform/implementation-workflow/data-processing/data-storage',
meta: { title: '数据预处理', icon: 'filter', hidden: false }, meta: { title: '数据预处理', icon: 'filter', hidden: false },
children: [
{
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/preprocessed-data/ImageBatchUpload',
name: 'ImageBatchUpload',
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/data-preprocessing/index.vue'),
meta: { title: '批量上传', icon: 'file', hidden: false },
},
{
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/preprocessed-data/ImageSorting',
name: 'ImageSorting',
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/image-sorting/index.vue'),
meta: { title: '图像分拣', icon: 'attachment', hidden: false },
},
],
}, },
{ {
path: '/construction-operation-platform/implementation-workflow/data-processing/intelligent-inspection', path: '/construction-operation-platform/implementation-workflow/data-processing/intelligent-inspection',
@ -972,6 +1016,30 @@ export const systemRoutes: RouteRecordRaw[] = [
// } // }
], ],
}, },
{
path: '/user/profile',
name: 'UserProfile',
component: Layout,
redirect: '/user/profile',
meta: {
title: '个人中心',
icon: 'user',
hidden: false,
sort: 100,
},
children: [
{
path: '/user/profile',
name: 'UserProfile',
component: () => import('@/views/user/profile/index.vue'),
meta: {
title: '个人中心',
icon: 'user',
hidden: false,
},
},
],
},
{ {
path: '/enterprise-settings', path: '/enterprise-settings',
name: 'EnterpriseSettings', name: 'EnterpriseSettings',
@ -1071,31 +1139,65 @@ export const systemRoutes: RouteRecordRaw[] = [
], ],
}, },
{ {
path: '/training', path: '/system-resource',
name: 'Training', name: 'SystemResource',
component: Layout, component: Layout,
redirect: '/training/plan', redirect: '/system-resource/device-management/warehouse',
meta: { title: '培训管理', icon: 'book', hidden: false, sort: 9 }, meta: { title: '关于平台', icon: 'server', hidden: false, sort: 9 },
children: [ children: [
{ {
path: '/training/plan', path: '/system-resource/device-management/warehouse',
name: 'TrainingPlan', name: 'DeviceWarehouse',
component: () => import('@/views/training/plan/index.vue'), component: () => import('@/views/system-resource/device-management/index.vue'),
meta: { meta: {
title: '培训计划', title: '库存管理',
icon: 'calendar', 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, hidden: false,
}, },
}, },
], ],
}, },
{
path: '/system-resource',
name: 'SystemResource',
component: Layout,
redirect: '/system-resource/information-system/software-management',
meta: { title: '系统资源', icon: 'server', hidden: false, sort: 9 },
children: [
{ {
path: '/system-resource/information-system', path: '/system-resource/information-system',
name: 'InformationSystem', name: 'InformationSystem',
@ -1141,7 +1243,6 @@ export const systemRoutes: RouteRecordRaw[] = [
}, },
], ],
}, },
{ {
path: '/', path: '/',
redirect: '/project-management/project-template/project-aproval', redirect: '/project-management/project-template/project-aproval',
@ -1167,6 +1268,11 @@ export const constantRoutes: RouteRecordRaw[] = [
}, },
], ],
}, },
// {
// path: '/user/profile',
// component: () => import('@/views/user/profile/index.vue'),
// meta: { hidden: true },
// },
{ {
path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*',
component: () => import('@/views/default/error/404.vue'), component: () => import('@/views/default/error/404.vue'),

View File

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

View File

@ -149,34 +149,6 @@ const storeSetup = () => {
isHidden: false, isHidden: false,
sort: 3, sort: 3,
}, },
{
id: 1070,
parentId: 1000,
title: '部门管理',
type: 2,
path: '/system/dept',
name: 'SystemDept',
component: 'system/dept/index',
icon: 'mind-mapping',
isExternal: false,
isCache: false,
isHidden: false,
sort: 4,
},
{
id: 1090,
parentId: 1000,
title: '岗位管理',
type: 2,
path: '/system/post',
name: 'SystemPost',
component: 'system/post/index',
icon: 'settings',
isExternal: false,
isCache: false,
isHidden: false,
sort: 5,
},
], ],
}] }]
// 使用已转换的数据生成路由 // 使用已转换的数据生成路由

View File

@ -5,10 +5,10 @@ import {
type AccountLoginReq, type AccountLoginReq,
AuthTypeConstants, AuthTypeConstants,
type DeptDetail,
type PhoneLoginReq, type PhoneLoginReq,
type RoleDetail,
type UserDetail, type UserDetail,
type DeptDetail,
type RoleDetail,
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,6 +68,8 @@ 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 })
@ -75,6 +77,8 @@ const storeSetup = () => {
token.value = res.data.token token.value = res.data.token
} }
// 退出登录回调 // 退出登录回调
const logoutCallBack = async () => { const logoutCallBack = async () => {
roles.value = [] roles.value = []
@ -124,7 +128,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 @@
@use './var.scss' as *; @import './var.scss';
body { body {
--margin: 14px; // 通用外边距 --margin: 14px; // 通用外边距

View File

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

View File

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

View File

@ -1,41 +0,0 @@
<template>
<div>
<h2>Console.log 测试</h2>
<button @click="testConsole">测试 Console.log</button>
<p>{{ message }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const message = ref('')
const testConsole = () => {
console.log('测试 console.log 是否正常工作')
console.warn('测试 console.warn')
console.error('测试 console.error')
message.value = '请查看浏览器控制台,应该能看到上述日志信息'
}
</script>
<style scoped>
div {
padding: 20px;
text-align: center;
}
button {
padding: 10px 20px;
margin: 10px;
background-color: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #40a9ff;
}
</style>

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;
} }
/** 分页响应数据格式 */ /** 分页响应数据格式 */

View File

@ -70,6 +70,6 @@ declare global {
// for type re-export // for type re-export
declare global { declare global {
// @ts-ignore // @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
import('vue') import('vue')
} }

View File

@ -1,179 +0,0 @@
export interface EquipmentPageQuery {
equipmentName?: string
equipmentType?: string
equipmentStatus?: string
equipmentSn?: string
assetCode?: string
brand?: string
locationStatus?: string
healthStatus?: string
responsiblePerson?: string
useStatus?: string
projectId?: string
userId?: string
equipmentModel?: string
specification?: string
physicalLocation?: string
supplierName?: string
maintenancePerson?: string
inventoryBarcode?: string
assetRemark?: string
// 新增搜索字段
usingDepartment?: string
invoice?: string
barcode?: string
importer?: string
page?: number
pageSize?: number
orderBy?: string
orderDirection?: string
}
export interface EquipmentReq {
equipmentName: string
equipmentModel: string
equipmentType: string
equipmentStatus: string
useStatus: string
equipmentSn: string
assetCode?: string
brand?: string
specification?: string
locationStatus?: string
physicalLocation?: string
responsiblePerson?: string
healthStatus?: string
purchaseTime?: string
inStockTime?: string
activationTime?: string
expectedScrapTime?: string
actualScrapTime?: string
statusChangeTime?: string
purchaseOrder?: string
supplierName?: string
purchasePrice?: number
currentNetValue?: number
depreciationMethod?: string
depreciationYears?: number
salvageValue?: number
warrantyExpireDate?: string
lastMaintenanceDate?: string
nextMaintenanceDate?: string
maintenancePerson?: string
inventoryBarcode?: string
assetRemark?: string
// 新增字段
usingDepartment?: string
borrowingTime?: string
returnTime?: string
outStockTime?: string
totalUsageTime?: string
depreciationRate?: number
depreciationMethodDesc?: string
invoice?: string
invoiceStatus?: string
attachments?: string
photos?: string
barcode?: string
importer?: string
inventoryTimeStatus1?: string
inventoryTimeStatus2?: string
inventoryTimeStatus3?: string
inventoryCheckTimeStatus1?: string
inventoryCheckTimeStatus2?: string
inventoryCheckTimeStatus3?: string
}
export interface EquipmentResp {
equipmentId: string
assetCode?: string
equipmentName: string
equipmentType: string
equipmentTypeLabel?: string
equipmentModel: string
equipmentSn: string
brand?: string
specification?: string
equipmentStatus: string
equipmentStatusLabel?: string
useStatus: string
locationStatus?: string
locationStatusLabel?: string
physicalLocation?: string
responsiblePerson?: string
healthStatus?: string
healthStatusLabel?: string
purchaseTime?: string
inStockTime?: string
activationTime?: string
expectedScrapTime?: string
actualScrapTime?: string
statusChangeTime?: string
purchaseOrder?: string
supplierName?: string
purchasePrice?: number
currentNetValue?: number
depreciationMethod?: string
depreciationYears?: number
salvageValue?: number
warrantyExpireDate?: string
lastMaintenanceDate?: string
nextMaintenanceDate?: string
maintenancePerson?: string
inventoryBarcode?: string
assetRemark?: string
// 新增字段
usingDepartment?: string
borrowingTime?: string
returnTime?: string
outStockTime?: string
totalUsageTime?: string
depreciationRate?: number
depreciationMethodDesc?: string
invoice?: string
invoiceStatus?: string
attachments?: string
photos?: string
barcode?: string
importer?: string
inventoryTimeStatus1?: string
inventoryTimeStatus2?: string
inventoryTimeStatus3?: string
inventoryCheckTimeStatus1?: string
inventoryCheckTimeStatus2?: string
inventoryCheckTimeStatus3?: string
projectId?: string
projectName?: string
userId?: string
name?: string
createTime?: string
updateTime?: string
}
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

@ -1,71 +0,0 @@
export interface TrainingPlanPageQuery {
planName?: string
trainingType?: string
trainingLevel?: string
status?: string
trainer?: string
startTime?: string
endTime?: string
page?: number
pageSize?: number
}
export interface TrainingPlanReq {
planName: string
trainingType: string
trainingLevel: string
trainingContent?: string
trainer?: string
trainingLocation?: string
startTime: string
endTime: string
status?: string
maxParticipants?: number
requirements?: string
remark?: string
}
export interface TrainingPlanResp {
planId: string
planName: string
trainingType: string
trainingLevel: string
trainingContent?: string
trainer?: string
trainingLocation?: string
startTime: string
endTime: string
status: string
maxParticipants?: number
currentParticipants?: number
requirements?: string
remark?: string
createTime: string
createBy: string
materials?: TrainingMaterialResp[]
records?: TrainingRecordResp[]
}
export interface TrainingMaterialResp {
materialId: string
materialName: string
materialType: string
materialPath?: string
materialSize?: number
description?: string
sortOrder?: number
}
export interface TrainingRecordResp {
recordId: string
userId: string
userName: string
deptId?: string
deptName?: string
attendanceStatus: string
signInTime?: string
signOutTime?: string
score?: number
feedback?: string
certificateId?: string
}

View File

@ -30,7 +30,7 @@ const StatusCodeMessage: ICodeMessage = {
} }
const http: AxiosInstance = axios.create({ const http: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL, baseURL: import.meta.env.VITE_API_PREFIX ?? import.meta.env.VITE_API_BASE_URL,
timeout: 30 * 1000, timeout: 30 * 1000,
}) })
@ -145,11 +145,6 @@ const request = async <T = unknown>(config: AxiosRequestConfig): Promise<ApiRes<
responseData.data = responseData.rows responseData.data = responseData.rows
} }
// 兼容后端返回的 status/code 格式
if (responseData.status === 200 || responseData.code === 200 || responseData.code === '200') {
responseData.success = true
}
// 如果返回的code是200但没有设置success字段将success设置为true // 如果返回的code是200但没有设置success字段将success设置为true
if ((responseData.code === 200 || responseData.code === '200') && responseData.success === undefined) { if ((responseData.code === 200 || responseData.code === '200') && responseData.success === undefined) {
responseData.success = true responseData.success = true

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:selected-keys="selectedKeys" v-model:selectedKeys="selectedKeys"
row-key="tableName" row-key="tableName"
:data="dataList" :data="dataList"
:columns="columns" :columns="columns"

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><IconSearch /></template> <template #icon><icon-search /></template>
搜索 搜索
</a-button> </a-button>
<a-button @click="resetSearch"> <a-button @click="resetSearch">
<template #icon><IconRefresh /></template> <template #icon><icon-refresh /></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><IconDownload /></template> <template #icon><icon-download /></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><IconDelete /></template> <template #icon><icon-delete /></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><IconEye /></template> <template #icon><icon-eye /></template>
预览 预览
</a-button> </a-button>
<a-button size="small" @click="downloadFile(record)"> <a-button size="small" @click="downloadFile(record)">
<template #icon><IconDownload /></template> <template #icon><icon-download /></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><IconDelete /></template> <template #icon><icon-delete /></template>
删除 删除
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
@ -160,18 +160,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref } from 'vue' import { ref, reactive, onMounted, computed } from 'vue'
import { Message, Modal } from '@arco-design/web-vue' import { Message, Modal } from '@arco-design/web-vue'
import {
IconDelete,
IconDownload,
IconEye,
IconFile,
IconRefresh,
IconSearch,
} from '@arco-design/web-vue/es/icon'
import FilePreview from '@/components/FilePreview/index.vue' import FilePreview from '@/components/FilePreview/index.vue'
import { deleteAttachment, getAttachBusinessTypes, getAttachmentList } from '@/apis/attach-info' import {
IconSearch,
IconRefresh,
IconDownload,
IconDelete,
IconEye,
IconFile
} from '@arco-design/web-vue/es/icon'
import { getAttachBusinessTypes, getAttachmentList, deleteAttachment } from '@/apis/attach-info'
import type { AttachInfoData, BusinessType } from '@/apis/attach-info/type' 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 ref="formRef" :model="formData"> <a-form :model="formData" ref="formRef">
<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" :loading="submitting" @click="handleSubmit">提交</a-button> <a-button type="primary" @click="handleSubmit" :loading="submitting">提交</a-button>
<a-button style="margin-left: 10px" @click="resetForm">重置</a-button> <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 { onMounted, reactive, ref } from 'vue' import { ref, reactive, onMounted } 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 { batchAddAttachment, getAttachBusinessTypes } from '@/apis/attach-info' import { getAttachBusinessTypes, batchAddAttachment } 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,15 +98,23 @@ const formData = reactive({
fileType: '', fileType: '',
remark: '', remark: '',
userDefinedPath: '', userDefinedPath: '',
files: [] as FileItem[], files: [] as FileItem[]
}) })
// //
const fetchBusinessTypes = async () => { const fetchBusinessTypes = async () => {
try { try {
const res = await getAttachBusinessTypes() const res = await getAttachBusinessTypes()
console.log("res:",res);
if (res.data) { if (res.data) {
businessTypes.value = res.data res.data.forEach(item => {
const key = Object.keys(item)[0];
const value = item[key];
businessTypes.value.push({
name: value,
code:key
});
});
} }
} catch (error) { } catch (error) {
console.error('获取业务类型失败:', error) console.error('获取业务类型失败:', error)
@ -126,7 +134,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 +180,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 +193,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><IconClose /></template> <template #icon><icon-close /></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" :loading="isRecognizing" block @click="handleStartRecognition"> <a-button type="primary" @click="handleStartRecognition" :loading="isRecognizing" block>
开始识别 开始识别
</a-button> </a-button>
<a-button block @click="handleResetSettings"> <a-button @click="handleResetSettings" block>
重置设置 重置设置
</a-button> </a-button>
</div> </div>
@ -76,14 +76,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from 'vue' import { ref, computed, watch, onMounted } 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 { DefectTypeOption, DefectTypeResp } from '@/apis/common/type' import type { DefectTypeResp, DefectTypeOption } 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><IconClose /></template> <template #icon><icon-close /></template>
</a-button> </a-button>
</div> </div>
@ -36,7 +36,7 @@
</a-select> </a-select>
</a-form-item> </a-form-item>
<!-- 轴向尺寸 --> <!--轴向尺寸-->
<div class="dimension-fields"> <div class="dimension-fields">
<a-form-item <a-form-item
field="axialDimension" field="axialDimension"
@ -54,7 +54,7 @@
</a-input-number> </a-input-number>
</a-form-item> </a-form-item>
<!-- 弦向尺寸 --> <!--弦向尺寸 -->
<a-form-item <a-form-item
field="chordDimension" field="chordDimension"
label="弦向尺寸 (mm)" label="弦向尺寸 (mm)"
@ -104,29 +104,32 @@
/> />
</a-form-item> </a-form-item>
<div class="form-footer"> <div class="form-footer">
<a-button size="large" @click="$emit('close')">取消</a-button> <a-button @click="$emit('close')" size="large">取消</a-button>
<a-button <a-button
type="primary" type="primary"
size="large" size="large"
:loading="submitting"
@click="handleSubmit" @click="handleSubmit"
:loading="submitting"
> >
<template #icon><IconSave /></template> <template #icon><icon-save /></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 { onMounted, reactive, ref, watch } from 'vue' import { ref, reactive, watch, onMounted } 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 { getDefectLevels } from '@/apis/industrial-image/defect' import type { Annotation } from '@/views/project-operation-platform/data-processing/industrial-image/components/ImageCanvas.vue'
import { getDefectLevels, getDefectTypes, type DefectLevelType, type DefectType } from '@/apis/industrial-image/defect'
import { listDefectType } from '@/apis/common/common' import { listDefectType } from '@/apis/common/common'
import type { DefectTypeOption } from '@/apis/common/type' import type { DefectTypeResp, DefectTypeOption } from '@/apis/common/type'
interface DefectFormData { interface DefectFormData {
defectName: string defectName: string
@ -172,7 +175,7 @@ const form = reactive<DefectFormData>({
description: '', description: '',
repairIdea: '建议进行进一步检查', repairIdea: '建议进行进一步检查',
axialDimension: 0, // null axialDimension: 0, // null
chordDimension: 0, // null chordDimension: 0 // null
}) })
// //
@ -194,7 +197,7 @@ const loadDefectTypes = async () => {
defectTypeOptions.push({ defectTypeOptions.push({
value: code, value: code,
label: name as string, label: name as string,
code, code: code
}) })
} }
}) })
@ -217,6 +220,7 @@ const loadDefectTypes = async () => {
} }
} }
// //
const loadDefectLevels = async () => { const loadDefectLevels = async () => {
try { try {
@ -229,13 +233,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
@ -246,10 +250,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
}) })
} }
}) })
@ -260,7 +264,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: '严重' }
] ]
} }
@ -268,9 +272,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
@ -281,7 +285,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
@ -352,18 +356,19 @@ 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('提交失败,请重试')
@ -374,7 +379,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
} }
@ -382,7 +387,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"
class="standard-library-btn"
@click="handleSelectFromStandardDescription" @click="handleSelectFromStandardDescription"
class="standard-library-btn"
> >
从标准描述库选择 从标准描述库选择
</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"
class="standard-library-btn"
@click="handleSelectFromStandardInfo" @click="handleSelectFromStandardInfo"
class="standard-library-btn"
> >
从标准信息库选择 从标准信息库选择
</a-button> </a-button>
@ -257,7 +257,7 @@
<!-- 无缺陷选中状态 --> <!-- 无缺陷选中状态 -->
<div v-else class="no-defect-selected"> <div v-else class="no-defect-selected">
<IconFile class="empty-icon" /> <icon-file 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 }"
class="clickable-item"
@click="selectedStandardDescription = item" @click="selectedStandardDescription = item"
class="clickable-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 }"
class="clickable-item"
@click="selectedStandardInfo = item" @click="selectedStandardInfo = item"
class="clickable-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 { reactive, ref, watch } from 'vue' import { ref, watch, reactive } 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"
:disabled="!canAddDefect"
@click="handleAddDefect" @click="handleAddDefect"
:disabled="!canAddDefect"
> >
<template #icon><IconPlus /></template> <template #icon><icon-plus /></template>
新增缺陷 新增缺陷
</a-button> </a-button>
<a-button type="text" @click="$emit('close')"> <a-button type="text" @click="$emit('close')">
<template #icon><IconClose /></template> <template #icon><icon-close /></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">
<IconFolder v-if="node.type === 'project'" /> <icon-folder v-if="node.type === 'project'" />
<IconSettings v-else-if="node.type === 'turbine'" /> <icon-settings v-else-if="node.type === 'turbine'" />
<IconTool v-else-if="node.type === 'part'" /> <icon-tool v-else-if="node.type === 'part'" />
<IconBug v-else-if="node.type === 'defect'" /> <icon-bug v-else-if="node.type === 'defect'" />
<IconApps v-else /> <icon-apps 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 { IconApps, IconBug, IconClose, IconFolder, IconPlus, IconSettings, IconTool } from '@arco-design/web-vue/es/icon' import { IconClose, IconBug, IconFolder, IconPlus, IconSettings, IconTool, IconApps } from '@arco-design/web-vue/es/icon'
import type { PropType } from 'vue' import type { PropType } from 'vue'
import { computed } from 'vue' import { ref, computed, watch } 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" :disabled="!currentImageId" @click="handleAutoAnnotate"> <a-button size="large" @click="handleAutoAnnotate" :disabled="!currentImageId">
<template #icon><IconRobot /></template> <template #icon><icon-robot /></template>
自动标注 自动标注
</a-button> </a-button>
<a-button size="large" :disabled="!currentImageId" @click="handleManualAnnotate"> <a-button size="large" @click="handleManualAnnotate" :disabled="!currentImageId">
<template #icon><IconEdit /></template> <template #icon><icon-edit /></template>
手动标注 手动标注
</a-button> </a-button>
<a-button size="large" @click="handleGenerateReport"> <a-button size="large" @click="handleGenerateReport">
<template #icon><IconFileImage /></template> <template #icon><icon-file-image /></template>
生成检测报告 生成检测报告
</a-button> </a-button>
</div> </div>
@ -19,9 +19,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { import {
IconEdit, IconPlayArrow,
IconFileImage,
IconRobot, IconRobot,
IconEdit,
IconFileImage
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
defineProps<{ defineProps<{
@ -39,6 +40,8 @@ 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"
:confirm-loading="loading"
@update:visible="emit('update:previewModalVisible', $event)" @update:visible="emit('update:previewModalVisible', $event)"
:confirm-loading="loading"
> >
<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 } from 'vue' import { ref, watch, computed } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import axios from 'axios' import axios from 'axios'
@ -115,7 +115,7 @@ const editingData = ref<{
imageType?: string imageType?: string
imageTypeLabel?: string imageTypeLabel?: string
partId?: string partId?: string
imageId?: string imageId?:string
}>({ }>({
imagePath: '', imagePath: '',
imageName: '', imageName: '',
@ -127,7 +127,7 @@ const editingData = ref<{
imageType: '', imageType: '',
imageTypeLabel: '', imageTypeLabel: '',
partId: '', partId: '',
imageId: '', imageId:''
}) })
// //
@ -140,7 +140,7 @@ watch(() => props.previewImage, (newVal) => {
const getImageUrl = (imagePath: string): string => { const getImageUrl = (imagePath: string): string => {
if (!imagePath) return '' if (!imagePath) return ''
if (imagePath.startsWith('http')) return imagePath if (imagePath.startsWith('http')) return imagePath
const baseUrl = 'http://localhost:8080' const baseUrl = 'http://pms.dtyx.net:9158'
return `${baseUrl}${imagePath}` return `${baseUrl}${imagePath}`
} }
@ -177,18 +177,17 @@ 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.post(
`http://localhost:8080/image/setting-info/${editingData.value.partId}`, `http://pms.dtyx.net:9158/image/setting-info/${editingData.value.partId}`,
requestData, requestData
) )
if (response.data && response.data.code === 200 && response.data.success) {
if (response.data && response.data.code === 0) {
Message.success('图片信息保存成功') Message.success('图片信息保存成功')
emit('save', editingData.value) emit('save', editingData.value)
emit('update:previewModalVisible', false) emit('update:previewModalVisible', false)

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" :disabled="scale <= MIN_SCALE" @click="zoomOut"> <a-button size="small" @click="zoomOut" :disabled="scale <= MIN_SCALE">
<template #icon> <template #icon>
<IconMinus /> <icon-minus />
</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" :disabled="scale >= MAX_SCALE" @click="zoomIn"> <a-button size="small" @click="zoomIn" :disabled="scale >= MAX_SCALE">
<template #icon> <template #icon>
<IconPlus /> <icon-plus />
</template> </template>
</a-button> </a-button>
<a-button size="small" @click="resetZoom"> <a-button size="small" @click="resetZoom">
<template #icon> <template #icon>
<IconRefresh /> <icon-refresh />
</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">
<IconImage class="empty-icon" /> <icon-image 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 { nextTick, ref, watch } from 'vue' import { ref, watch, nextTick } 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
} }
} }
@ -226,7 +226,7 @@ const handleMouseLeave = () => {
const getImageUrl = (imagePath: string): string => { const getImageUrl = (imagePath: string): string => {
if (!imagePath) return '' if (!imagePath) return ''
if (imagePath.startsWith('http')) return imagePath if (imagePath.startsWith('http')) return imagePath
const baseUrl = 'http://localhost:8080' const baseUrl = 'http://pms.dtyx.net:9158'
return `${baseUrl}${imagePath}` return `${baseUrl}${imagePath}`
} }
</script> </script>

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">
<IconFolder v-if="node.type === 'project'" /> <icon-folder v-if="node.type === 'project'" />
<IconSettings v-else-if="node.type === 'turbine'" /> <icon-settings v-else-if="node.type === 'turbine'" />
<IconTool v-else-if="node.type === 'part'" /> <icon-tool v-else-if="node.type === 'part'" />
<IconApps v-else /> <icon-apps 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><IconSave /></template> <template #icon><icon-save /></template>
保存结果 保存结果
</a-button> </a-button>
<a-button type="text" @click="handleExportResults"> <a-button type="text" @click="handleExportResults">
<template #icon><IconExport /></template> <template #icon><icon-export /></template>
导出 导出
</a-button> </a-button>
</div> </div>
@ -73,10 +73,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { ref, computed } 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,18 +1,35 @@
import { computed, ref } from 'vue' import { ref, reactive, computed } 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 type { type DefectDetectionRequest, type DefectDetectionResult, DefectInfo, type ManualDefectAddRequest, addDefect, addManualDefect, deleteDefect, detectDefects, getDefectList, updateDefect } from '@/apis/industrial-image/defect' import {
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() {
// 项目树数据 // 项目树数据
@ -56,14 +73,13 @@ 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, y: selectedDefect.value.boundingBox.y + selectedDefect.value.boundingBox.height }, { x: selectedDefect.value.boundingBox.x + selectedDefect.value.boundingBox.width,
] 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
}] }]
}) })
@ -97,7 +113,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,
@ -107,7 +123,7 @@ export function useIndustrialImage() {
expanded: false, expanded: false,
status: project.status, status: project.status,
createTime: project.createTime, createTime: project.createTime,
rawData: project, rawData: project
})) }))
// 默认选中第一个项目 // 默认选中第一个项目
@ -137,7 +153,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,
@ -148,16 +164,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,
@ -168,7 +184,7 @@ export function useIndustrialImage() {
expanded: false, expanded: false,
status: part.partType, status: part.partType,
createTime: part.createTime, createTime: part.createTime,
rawData: part, rawData: part
})) }))
} }
@ -206,7 +222,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 || []
@ -272,6 +288,8 @@ 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
@ -298,7 +316,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)
@ -331,7 +349,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
} }
@ -354,7 +372,7 @@ export function useIndustrialImage() {
console.error('删除图像失败:', error) console.error('删除图像失败:', error)
Message.error('删除图像失败') Message.error('删除图像失败')
} }
}, }
}) })
} }
@ -433,7 +451,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
} }
// 显示报告生成对话框 // 显示报告生成对话框
@ -471,7 +489,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接口上传图像
@ -479,7 +497,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)
@ -542,13 +560,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
})) }))
} }
} }
@ -611,7 +629,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
@ -705,21 +723,19 @@ 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)
@ -808,7 +824,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)
@ -835,7 +851,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)
} }
@ -847,6 +863,7 @@ export function useIndustrialImage() {
} }
// 不再需要类型映射因为RecognitionResults组件已经处理了自定义类型 // 不再需要类型映射因为RecognitionResults组件已经处理了自定义类型
} catch (error) { } catch (error) {
console.error('识别失败:', error) console.error('识别失败:', error)
Message.error('识别失败') Message.error('识别失败')
@ -907,15 +924,15 @@ 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' // 标记为自动识别来源
} }
// 调用手动添加缺陷接口 // 调用手动添加缺陷接口
const response = await addManualDefect(defectData, selectedImage.value.imageId) const response = await addManualDefect(defectData,selectedImage.value.imageId)
if (response.data.success) { if (response.data.success) {
successCount++ successCount++
@ -950,6 +967,7 @@ 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('保存识别结果失败')
@ -1052,6 +1070,6 @@ export function useIndustrialImage() {
// 报告生成相关方法 // 报告生成相关方法
handleReportGenerated, handleReportGenerated,
init, init
} }
} }

View File

@ -12,13 +12,11 @@
<!-- 主体内容区域 --> <!-- 主体内容区域 -->
<div class="main-content"> <div class="main-content">
<!-- 上部区域 --> <!-- 上部区域 -->
<div <div class="top-section" :class="{
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">
<!-- 左侧自动识别设置面板 --> <!-- 左侧自动识别设置面板 -->
@ -87,7 +85,7 @@
@submit="handleDefectFormSubmit" @submit="handleDefectFormSubmit"
/> />
<div v-else class="no-annotation-prompt"> <div v-else class="no-annotation-prompt">
<IconBug class="prompt-icon" /> <icon-bug class="prompt-icon" />
<p>请在图像上绘制矩形标注缺陷</p> <p>请在图像上绘制矩形标注缺陷</p>
</div> </div>
</div> </div>
@ -131,7 +129,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"
@ -159,12 +157,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref } from 'vue' import { onMounted, ref, computed } 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,10 +172,9 @@ 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 { DefectInfo, type DefectLevelType, type DefectType, ManualDefectAddRequest, addManualDefect, getDefectLevels, getDefectList, getDefectTypes, uploadAnnotatedImage } from '@/apis/industrial-image/defect' import type { ManualDefectAddRequest, DefectInfo, AttachInfoData } 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' })
@ -315,7 +314,7 @@ const {
// //
handleReportGenerated, handleReportGenerated,
init, init
} = useIndustrialImage() } = useIndustrialImage()
// //
@ -342,16 +341,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
@ -362,7 +361,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
@ -373,7 +372,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
@ -383,18 +382,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
@ -418,7 +417,7 @@ const handleAnnotationFinish = async (annotations: Annotation[], imageBlob: Blob
const fileName = `annotated_${selectedImage.value?.imageName || 'image'}_${Date.now()}.png` const fileName = `annotated_${selectedImage.value?.imageName || 'image'}_${Date.now()}.png`
const uploadResponse = await uploadAnnotatedImage(imageBlob, fileName) const uploadResponse = await uploadAnnotatedImage(imageBlob, fileName)
if (!uploadResponse.data) { if ( !uploadResponse.data) {
Message.error('上传标注图片失败') Message.error('上传标注图片失败')
return return
} }
@ -438,13 +437,14 @@ 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,38 +627,36 @@ 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)
@ -678,11 +676,12 @@ 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()
@ -694,9 +693,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; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity let minX = Infinity, minY = Infinity, maxX = -Infinity, 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)
@ -711,13 +710,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><IconPlus /></template> <template #icon><icon-plus /></template>
新建配置 新建配置
</a-button> </a-button>
<a-button @click="refreshList"> <a-button @click="refreshList">
<template #icon><IconRefresh /></template> <template #icon><icon-refresh /></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><IconEdit /></template> <template #icon><icon-edit /></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><IconEye /></template> <template #icon><icon-eye /></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><IconDelete /></template> <template #icon><icon-delete /></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><IconUpload /></template> <template #icon><icon-upload /></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><IconDelete /></template> <template #icon><icon-delete /></template>
清除 清除
</a-button> </a-button>
</a-space> </a-space>
<div v-if="uploadingFile" class="upload-tip"> <div class="upload-tip" v-if="uploadingFile">
<a-spin /> 上传中...{{ uploadProgress }}% <a-spin /> 上传中...{{ uploadProgress }}%
</div> </div>
<div v-if="uploadedFileName && !uploadingFile" class="upload-tip success"> <div class="upload-tip success" v-if="uploadedFileName && !uploadingFile">
<IconCheckCircle /> 已上传: {{ uploadedFileName }} <icon-check-circle /> 已上传: {{ 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 { onMounted, reactive, ref } from 'vue' import { ref, reactive, onMounted } from 'vue';
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue';
import { import {
IconCheckCircle,
IconDelete,
IconEdit,
IconEye,
IconPlus, IconPlus,
IconRefresh, IconRefresh,
IconEdit,
IconEye,
IconDelete,
IconUpload, IconUpload,
} from '@arco-design/web-vue/es/icon' IconCheckCircle
} from '@arco-design/web-vue/es/icon';
import { import {
createModelConfig,
deleteModelConfig,
getModelConfigDetail,
getModelConfigList, getModelConfigList,
getModelConfigDetail,
createModelConfig,
updateModelConfig, updateModelConfig,
} from '@/apis/model-config' deleteModelConfig
import { addAttachment } from '@/apis/attach-info' } from '@/apis/model-config';
import type { ModelConfigRequest, ModelConfigResponse } from '@/apis/model-config/type' import { addAttachment } from '@/apis/attach-info';
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 v-for="(stat, index) in appStatistics" :key="index" :span="6"> <a-col :span="6" v-for="(stat, index) in appStatistics" :key="index">
<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 }}%
<IconUp v-if="record.trend > 0" style="color: #52c41a" /> <icon-up v-if="record.trend > 0" style="color: #52c41a" />
<IconDown v-if="record.trend < 0" style="color: #ff4d4f" /> <icon-down v-if="record.trend < 0" style="color: #ff4d4f" />
<IconMinus v-if="record.trend === 0" style="color: #1890ff" /> <icon-minus 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 }}%
<IconUp v-if="record.trend > 0" style="color: #52c41a" /> <icon-up v-if="record.trend > 0" style="color: #52c41a" />
<IconDown v-if="record.trend < 0" style="color: #ff4d4f" /> <icon-down v-if="record.trend < 0" style="color: #ff4d4f" />
<IconMinus v-if="record.trend === 0" style="color: #1890ff" /> <icon-minus 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 }}%
<IconUp v-if="record.trend > 0" style="color: #52c41a" /> <icon-up v-if="record.trend > 0" style="color: #52c41a" />
<IconDown v-if="record.trend < 0" style="color: #ff4d4f" /> <icon-down v-if="record.trend < 0" style="color: #ff4d4f" />
<IconMinus v-if="record.trend === 0" style="color: #1890ff" /> <icon-minus v-if="record.trend === 0" style="color: #1890ff" />
</span> </span>
</template> </template>
</template> </template>
@ -102,52 +102,55 @@
</a-row> </a-row>
</a-card> </a-card>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, reactive, ref } from 'vue' import { ref, onMounted, reactive, nextTick } from 'vue'
import * as echarts from 'echarts' import { Statistic } from '@arco-design/web-vue'
import { import * as echarts from 'echarts'
import {
IconApps, IconApps,
IconClockCircle, IconMobile,
IconDown,
IconMinus,
IconUp,
IconUser, IconUser,
} from '@arco-design/web-vue/es/icon' IconClockCircle,
IconUp,
IconDown,
IconMinus
} from '@arco-design/web-vue/es/icon'
const appVisitChart = ref(null)
const appTimeChart = ref(null)
const deviceDistributionChart = ref(null)
// const appVisitChart = ref(null)
const appStatistics = reactive([ const appTimeChart = ref(null)
const deviceDistributionChart = ref(null)
//
const appStatistics = reactive([
{ {
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
}, }
]) ])
// 使 // 使
const appColumns = [ const appColumns = [
{ {
title: '应用名称', title: '应用名称',
dataIndex: 'appName', dataIndex: 'appName',
@ -181,11 +184,11 @@ const appColumns = [
dataIndex: 'trend', dataIndex: 'trend',
key: 'trend', key: 'trend',
sorter: (a, b) => a.trend - b.trend, sorter: (a, b) => a.trend - b.trend,
}, }
] ]
// Web // Web
const webAppData = [ const webAppData = [
{ {
key: '1', key: '1',
appName: '企业管理后台', appName: '企业管理后台',
@ -193,7 +196,7 @@ const webAppData = [
userCount: 320, userCount: 320,
usageRate: 95, usageRate: 95,
averageTime: '2.5小时', averageTime: '2.5小时',
trend: 8.5, trend: 8.5
}, },
{ {
key: '2', key: '2',
@ -202,7 +205,7 @@ const webAppData = [
userCount: 285, userCount: 285,
usageRate: 90, usageRate: 90,
averageTime: '3小时', averageTime: '3小时',
trend: 5.2, trend: 5.2
}, },
{ {
key: '3', key: '3',
@ -211,7 +214,7 @@ const webAppData = [
userCount: 210, userCount: 210,
usageRate: 85, usageRate: 85,
averageTime: '2小时', averageTime: '2小时',
trend: 3.8, trend: 3.8
}, },
{ {
key: '4', key: '4',
@ -220,7 +223,7 @@ const webAppData = [
userCount: 180, userCount: 180,
usageRate: 75, usageRate: 75,
averageTime: '1.5小时', averageTime: '1.5小时',
trend: -2.1, trend: -2.1
}, },
{ {
key: '5', key: '5',
@ -229,12 +232,12 @@ const webAppData = [
userCount: 150, userCount: 150,
usageRate: 65, usageRate: 65,
averageTime: '1小时', averageTime: '1小时',
trend: 0, trend: 0
}, }
] ]
// //
const mobileAppData = [ const mobileAppData = [
{ {
key: '1', key: '1',
appName: '移动办公APP', appName: '移动办公APP',
@ -242,7 +245,7 @@ const mobileAppData = [
userCount: 350, userCount: 350,
usageRate: 98, usageRate: 98,
averageTime: '3.5小时', averageTime: '3.5小时',
trend: 12.5, trend: 12.5
}, },
{ {
key: '2', key: '2',
@ -251,7 +254,7 @@ const mobileAppData = [
userCount: 320, userCount: 320,
usageRate: 92, usageRate: 92,
averageTime: '4小时', averageTime: '4小时',
trend: 9.8, trend: 9.8
}, },
{ {
key: '3', key: '3',
@ -260,7 +263,7 @@ const mobileAppData = [
userCount: 260, userCount: 260,
usageRate: 88, usageRate: 88,
averageTime: '3小时', averageTime: '3小时',
trend: 7.5, trend: 7.5
}, },
{ {
key: '4', key: '4',
@ -269,12 +272,12 @@ const mobileAppData = [
userCount: 180, userCount: 180,
usageRate: 72, usageRate: 72,
averageTime: '2小时', averageTime: '2小时',
trend: -1.5, trend: -1.5
}, }
] ]
// //
const miniAppData = [ const miniAppData = [
{ {
key: '1', key: '1',
appName: '企业服务小程序', appName: '企业服务小程序',
@ -282,7 +285,7 @@ const miniAppData = [
userCount: 420, userCount: 420,
usageRate: 96, usageRate: 96,
averageTime: '1.5小时', averageTime: '1.5小时',
trend: 15.8, trend: 15.8
}, },
{ {
key: '2', key: '2',
@ -291,7 +294,7 @@ const miniAppData = [
userCount: 380, userCount: 380,
usageRate: 90, usageRate: 90,
averageTime: '1小时', averageTime: '1小时',
trend: 10.5, trend: 10.5
}, },
{ {
key: '3', key: '3',
@ -300,24 +303,24 @@ const miniAppData = [
userCount: 345, userCount: 345,
usageRate: 85, usageRate: 85,
averageTime: '0.8小时', averageTime: '0.8小时',
trend: 8.2, trend: 8.2
}, }
] ]
// //
const getUsageRateColor = (rate) => { const getUsageRateColor = (rate) => {
if (rate >= 90) return '#52c41a' if (rate >= 90) return '#52c41a'
if (rate >= 70) return '#1890ff' if (rate >= 70) return '#1890ff'
return '#faad14' return '#faad14'
} }
const getTrendColor = (trend) => { const getTrendColor = (trend) => {
if (trend > 0) return '#52c41a' if (trend > 0) return '#52c41a'
if (trend < 0) return '#ff4d4f' if (trend < 0) return '#ff4d4f'
return '#1890ff' return '#1890ff'
} }
onMounted(() => { onMounted(() => {
// 使 nextTick DOM // 使 nextTick DOM
nextTick(() => { nextTick(() => {
// 访 // 访
@ -325,42 +328,42 @@ onMounted(() => {
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]
}, }
], ]
}) })
} }
@ -369,11 +372,11 @@ onMounted(() => {
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: [
{ {
@ -383,17 +386,17 @@ onMounted(() => {
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)'
}, }
}, }
}, }
], ]
}) })
} }
@ -404,22 +407,22 @@ onMounted(() => {
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: [
{ {
@ -427,26 +430,26 @@ onMounted(() => {
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]
}, }
], ]
}) })
} }
@ -466,8 +469,8 @@ onMounted(() => {
} }
}) })
}) })
}) })
</script> </script>
<style scoped> <style scoped>
.general-card { .general-card {

View File

@ -45,20 +45,20 @@
</a-card> </a-card>
</a-card> </a-card>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, ref } from 'vue' import { ref, onMounted, nextTick } 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'
const moduleUsageChart = ref(null) const moduleUsageChart = ref(null)
const departmentUsageChart = ref(null) const departmentUsageChart = ref(null)
const usageTimeChart = ref(null) const usageTimeChart = ref(null)
// 使 // 使
const functionColumns = [ const functionColumns = [
{ {
title: '功能模块', title: '功能模块',
dataIndex: 'module', dataIndex: 'module',
@ -92,10 +92,10 @@ const functionColumns = [
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 = [
{ {
key: '1', key: '1',
module: '组织架构', module: '组织架构',
@ -103,7 +103,7 @@ const functionData = [
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 @@ const functionData = [
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 @@ const functionData = [
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 @@ const functionData = [
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 @@ const functionData = [
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 @@ const functionData = [
usageCount: 986, usageCount: 986,
usageRate: 82, usageRate: 82,
averageTime: '16分钟', averageTime: '16分钟',
trend: 0, trend: 0
}, },
{ {
key: '7', key: '7',
@ -157,7 +157,7 @@ const functionData = [
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 @@ const functionData = [
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 @@ const functionData = [
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 @@ const functionData = [
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 @@ const functionData = [
usageCount: 1420, usageCount: 1420,
usageRate: 98, usageRate: 98,
averageTime: '35分钟', averageTime: '35分钟',
trend: 12.3, trend: 12.3
}, },
{ {
key: '12', key: '12',
@ -202,24 +202,24 @@ const functionData = [
usageCount: 720, usageCount: 720,
usageRate: 65, usageRate: 65,
averageTime: '10分钟', averageTime: '10分钟',
trend: -1.5, trend: -1.5
}, }
] ]
// //
const getUsageRateColor = (rate) => { const getUsageRateColor = (rate) => {
if (rate >= 90) return '#52c41a' if (rate >= 90) return '#52c41a'
if (rate >= 70) return '#1890ff' if (rate >= 70) return '#1890ff'
return '#faad14' return '#faad14'
} }
const getTrendColor = (trend) => { const getTrendColor = (trend) => {
if (trend > 0) return '#52c41a' if (trend > 0) return '#52c41a'
if (trend < 0) return '#ff4d4f' if (trend < 0) return '#ff4d4f'
return '#1890ff' return '#1890ff'
} }
onMounted(() => { onMounted(() => {
// 使 nextTick DOM // 使 nextTick DOM
nextTick(() => { nextTick(() => {
// 使 // 使
@ -229,37 +229,37 @@ onMounted(() => {
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 @@ onMounted(() => {
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 @@ onMounted(() => {
{ 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 @@ onMounted(() => {
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 @@ onMounted(() => {
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 @@ onMounted(() => {
{ value: 20, name: '施工操作台' }, { value: 20, name: '施工操作台' },
{ value: 5, name: '聊天平台' }, { value: 5, name: '聊天平台' },
{ value: 3, name: '企业设置' }, { value: 3, name: '企业设置' },
{ value: 2, name: '系统资源管理' }, { value: 2, name: '系统资源管理' }
], ]
}, }
], ]
}) })
} }
@ -365,8 +365,8 @@ onMounted(() => {
} }
}) })
}) })
}) })
</script> </script>
<style scoped> <style scoped>
.general-card { .general-card {

View File

@ -73,19 +73,19 @@
</a-tabs> </a-tabs>
</a-card> </a-card>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, ref } from 'vue' import { ref, onMounted, nextTick } from 'vue'
import * as echarts from 'echarts' import * as echarts from 'echarts'
const departmentActivityChart = ref(null) const departmentActivityChart = ref(null)
const dailyActiveUsersChart = ref(null) const dailyActiveUsersChart = ref(null)
const onlineTimeChart = ref(null) const onlineTimeChart = ref(null)
const attendanceTrendChart = ref(null) const attendanceTrendChart = ref(null)
// //
const rankColumns = [ const rankColumns = [
{ {
title: '排名', title: '排名',
dataIndex: 'rank', dataIndex: 'rank',
@ -116,10 +116,10 @@ const rankColumns = [
title: '操作次数', title: '操作次数',
dataIndex: 'operationCount', dataIndex: 'operationCount',
key: 'operationCount', key: 'operationCount',
}, }
] ]
const rankData = [ const rankData = [
{ {
key: '1', key: '1',
rank: 1, rank: 1,
@ -127,7 +127,7 @@ const rankData = [
department: '技术部', department: '技术部',
activityScore: 98, activityScore: 98,
loginCount: 45, loginCount: 45,
operationCount: 532, operationCount: 532
}, },
{ {
key: '2', key: '2',
@ -136,7 +136,7 @@ const rankData = [
department: '市场部', department: '市场部',
activityScore: 95, activityScore: 95,
loginCount: 42, loginCount: 42,
operationCount: 498, operationCount: 498
}, },
{ {
key: '3', key: '3',
@ -145,7 +145,7 @@ const rankData = [
department: '销售部', department: '销售部',
activityScore: 92, activityScore: 92,
loginCount: 40, loginCount: 40,
operationCount: 475, operationCount: 475
}, },
{ {
key: '4', key: '4',
@ -154,7 +154,7 @@ const rankData = [
department: '人事部', department: '人事部',
activityScore: 88, activityScore: 88,
loginCount: 38, loginCount: 38,
operationCount: 450, operationCount: 450
}, },
{ {
key: '5', key: '5',
@ -163,7 +163,7 @@ const rankData = [
department: '财务部', department: '财务部',
activityScore: 85, activityScore: 85,
loginCount: 36, loginCount: 36,
operationCount: 420, operationCount: 420
}, },
{ {
key: '6', key: '6',
@ -172,7 +172,7 @@ const rankData = [
department: '技术部', department: '技术部',
activityScore: 82, activityScore: 82,
loginCount: 34, loginCount: 34,
operationCount: 405, operationCount: 405
}, },
{ {
key: '7', key: '7',
@ -181,7 +181,7 @@ const rankData = [
department: '市场部', department: '市场部',
activityScore: 79, activityScore: 79,
loginCount: 32, loginCount: 32,
operationCount: 380, operationCount: 380
}, },
{ {
key: '8', key: '8',
@ -190,7 +190,7 @@ const rankData = [
department: '销售部', department: '销售部',
activityScore: 76, activityScore: 76,
loginCount: 30, loginCount: 30,
operationCount: 365, operationCount: 365
}, },
{ {
key: '9', key: '9',
@ -199,7 +199,7 @@ const rankData = [
department: '人事部', department: '人事部',
activityScore: 73, activityScore: 73,
loginCount: 28, loginCount: 28,
operationCount: 350, operationCount: 350
}, },
{ {
key: '10', key: '10',
@ -208,12 +208,12 @@ const rankData = [
department: '财务部', department: '财务部',
activityScore: 70, activityScore: 70,
loginCount: 26, loginCount: 26,
operationCount: 335, operationCount: 335
}, }
] ]
// //
const attendanceColumns = [ const attendanceColumns = [
{ {
title: '部门', title: '部门',
dataIndex: 'department', dataIndex: 'department',
@ -243,10 +243,10 @@ const attendanceColumns = [
title: '缺勤次数', title: '缺勤次数',
dataIndex: 'absentCount', dataIndex: 'absentCount',
key: 'absentCount', key: 'absentCount',
}, }
] ]
const attendanceData = [ const attendanceData = [
{ {
key: '1', key: '1',
department: '技术部', department: '技术部',
@ -254,7 +254,7 @@ const attendanceData = [
attendanceRate: 98, attendanceRate: 98,
lateCount: 3, lateCount: 3,
earlyLeaveCount: 1, earlyLeaveCount: 1,
absentCount: 0, absentCount: 0
}, },
{ {
key: '2', key: '2',
@ -263,7 +263,7 @@ const attendanceData = [
attendanceRate: 96, attendanceRate: 96,
lateCount: 5, lateCount: 5,
earlyLeaveCount: 2, earlyLeaveCount: 2,
absentCount: 1, absentCount: 1
}, },
{ {
key: '3', key: '3',
@ -272,7 +272,7 @@ const attendanceData = [
attendanceRate: 95, attendanceRate: 95,
lateCount: 6, lateCount: 6,
earlyLeaveCount: 3, earlyLeaveCount: 3,
absentCount: 1, absentCount: 1
}, },
{ {
key: '4', key: '4',
@ -281,7 +281,7 @@ const attendanceData = [
attendanceRate: 97, attendanceRate: 97,
lateCount: 2, lateCount: 2,
earlyLeaveCount: 1, earlyLeaveCount: 1,
absentCount: 0, absentCount: 0
}, },
{ {
key: '5', key: '5',
@ -290,36 +290,36 @@ const attendanceData = [
attendanceRate: 99, attendanceRate: 99,
lateCount: 1, lateCount: 1,
earlyLeaveCount: 0, earlyLeaveCount: 0,
absentCount: 0, absentCount: 0
}, }
] ]
// //
const getRankColor = (rank) => { const getRankColor = (rank) => {
if (rank <= 3) return '#f50' if (rank <= 3) return '#f50'
if (rank <= 10) return '#2db7f5' if (rank <= 10) return '#2db7f5'
return '#87d068' return '#87d068'
} }
const getScoreColor = (score) => { const getScoreColor = (score) => {
if (score >= 90) return '#52c41a' if (score >= 90) return '#52c41a'
if (score >= 70) return '#1890ff' if (score >= 70) return '#1890ff'
return '#faad14' return '#faad14'
} }
const getAttendanceColor = (rate) => { const getAttendanceColor = (rate) => {
if (rate >= 95) return '#52c41a' if (rate >= 95) return '#52c41a'
if (rate >= 90) return '#1890ff' if (rate >= 90) return '#1890ff'
return '#faad14' return '#faad14'
} }
const getLateCountColor = (count) => { const getLateCountColor = (count) => {
if (count <= 2) return 'green' if (count <= 2) return 'green'
if (count <= 5) return 'orange' if (count <= 5) return 'orange'
return 'red' return 'red'
} }
onMounted(() => { onMounted(() => {
// 使 nextTick DOM // 使 nextTick DOM
nextTick(() => { nextTick(() => {
// //
@ -329,40 +329,40 @@ onMounted(() => {
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 @@ onMounted(() => {
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 @@ onMounted(() => {
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 @@ onMounted(() => {
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]
}, }
], ]
}) })
} }
@ -484,8 +484,8 @@ onMounted(() => {
} }
}) })
}) })
}) })
</script> </script>
<style scoped> <style scoped>
.general-card { .general-card {

View File

@ -11,7 +11,7 @@
style="margin-right: 50px" style="margin-right: 50px"
> >
<template #prefix> <template #prefix>
<IconFile /> <icon-file />
</template> </template>
</a-statistic> </a-statistic>
</a-card> </a-card>
@ -24,7 +24,7 @@
:precision="0" :precision="0"
> >
<template #prefix> <template #prefix>
<IconUserGroup /> <icon-user-group />
</template> </template>
</a-statistic> </a-statistic>
</a-card> </a-card>
@ -37,7 +37,7 @@
:precision="0" :precision="0"
> >
<template #prefix> <template #prefix>
<IconComputer /> <icon-computer />
</template> </template>
</a-statistic> </a-statistic>
</a-card> </a-card>
@ -50,7 +50,7 @@
:precision="0" :precision="0"
> >
<template #prefix> <template #prefix>
<IconCheckCircle /> <icon-check-circle />
</template> </template>
</a-statistic> </a-statistic>
</a-card> </a-card>
@ -83,25 +83,26 @@
</a-row> </a-row>
</a-card> </a-card>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, reactive, ref } from 'vue' import { ref, onMounted, reactive, nextTick } from 'vue'
import { IconCheckCircle, IconComputer, IconFile, IconUserGroup } from '@arco-design/web-vue/es/icon' import { Statistic } from '@arco-design/web-vue'
import * as echarts from 'echarts' import { IconFile, IconUserGroup, IconComputer, IconCheckCircle } from '@arco-design/web-vue/es/icon'
import * as echarts from 'echarts'
const projectProgressChart = ref(null) const projectProgressChart = ref(null)
const resourceUsageChart = ref(null) const resourceUsageChart = ref(null)
const businessTrendChart = ref(null) const businessTrendChart = ref(null)
const statistics = reactive({ const statistics = reactive({
projectCount: 128, projectCount: 128,
memberCount: 356, memberCount: 356,
deviceCount: 243, deviceCount: 243,
completedProjectCount: 15, completedProjectCount: 15
}) })
onMounted(() => { onMounted(() => {
// 使 nextTick DOM // 使 nextTick DOM
nextTick(() => { nextTick(() => {
// //
@ -109,11 +110,11 @@ onMounted(() => {
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: [
{ {
@ -124,17 +125,17 @@ onMounted(() => {
{ 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)'
}, }
}, }
}, }
], ]
}) })
} }
@ -145,22 +146,22 @@ onMounted(() => {
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: [
{ {
@ -168,26 +169,26 @@ onMounted(() => {
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]
}, }
], ]
}) })
} }
@ -196,42 +197,42 @@ onMounted(() => {
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]
}, }
], ]
}) })
} }
@ -251,8 +252,8 @@ onMounted(() => {
} }
}) })
}) })
}) })
</script> </script>
<style scoped> <style scoped>
.general-card { .general-card {

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
:class="record.status === 'active' ? 'text-red-500' : 'text-green-500'"
@click="toggleStatus(record)" @click="toggleStatus(record)"
:class="record.status === 'active' ? 'text-red-500' : 'text-green-500'"
> >
{{ 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 { onMounted, reactive, ref } from 'vue' import { ref, reactive, onMounted } 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'
// //
const searchForm = reactive({ let 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 v-if="!record.isDefault" @click="setDefaultAccount(record)">设为默认</a-link> <a-link @click="setDefaultAccount(record)" v-if="!record.isDefault">设为默认</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 { reactive, ref } from 'vue' import { ref, reactive } 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
v-if="record.status === 'pending'"
@click="startTask(record)" @click="startTask(record)"
v-if="record.status === 'pending'"
> >
开始 开始
</a-link> </a-link>
<a-link <a-link
v-if="record.status === 'running'"
@click="pauseTask(record)" @click="pauseTask(record)"
v-if="record.status === 'running'"
> >
暂停 暂停
</a-link> </a-link>
<a-link <a-link
v-if="record.status === 'paused'"
@click="resumeTask(record)" @click="resumeTask(record)"
v-if="record.status === 'paused'"
> >
继续 继续
</a-link> </a-link>
@ -138,7 +138,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from 'vue' import { ref, reactive } 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
v-if="record.status === 'installed' && record.id !== currentVersionId"
@click="rollbackVersion(record)" @click="rollbackVersion(record)"
v-if="record.status === 'installed' && record.id !== currentVersionId"
> >
回滚 回滚
</a-link> </a-link>
@ -150,7 +150,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from 'vue' import { ref, reactive } 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

@ -1,3 +1,4 @@
<!-- 考勤统计 -->
<template> <template>
<GiPageLayout> <GiPageLayout>
<GiTable <GiTable
@ -49,18 +50,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref } from 'vue' import { ref, reactive, onMounted } 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'
// //
const searchForm = reactive({ let searchForm = reactive({
userName: '', userName: '',
deptName: '', deptName: '',
status: '', status: '',
attendanceDate: '', attendanceDate: '',
page: 1, page: 1,
size: 10, size: 10
}) })
// //
@ -70,16 +71,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 +93,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 +110,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 +126,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 +138,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 +150,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 +159,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 +177,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 +202,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 { onMounted, reactive, ref } from 'vue' import { ref, reactive, onMounted } 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'
// //
const searchForm = reactive({ let 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 { onMounted, reactive, ref } from 'vue' import { ref, reactive, onMounted } 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'
// //
const searchForm = reactive({ let 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>
<IconPlus /> <icon-plus />
</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>
<IconSearch /> <icon-search />
</template> </template>
搜索 搜索
</a-button> </a-button>
<a-button @click="handleReset"> <a-button @click="handleReset">
<template #icon> <template #icon>
<IconRefresh /> <icon-refresh />
</template> </template>
重置 重置
</a-button> </a-button>
@ -47,19 +47,15 @@
<!-- 人员资质表格 --> <!-- 人员资质表格 -->
<a-card class="table-card" :bordered="false"> <a-card class="table-card" :bordered="false">
<a-table <a-table :columns="columns" :data="certificationList" :pagination="paginationConfig" :loading="loading"
:columns="columns" :data="certificationList" :pagination="paginationConfig" :loading="loading" row-key="certificationId" @page-change="handlePageChange">
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 <a-image v-if="record.certificationImage" :src="record.certificationImage" width="60" height="40"
v-if="record.certificationImage" :src="record.certificationImage" width="60" height="40" fit="cover" show-loader preview />
fit="cover" show-loader preview
/>
<span v-else>-</span> <span v-else>-</span>
</template> </template>
@ -81,10 +77,8 @@
</a-card> </a-card>
<!-- 新增/编辑资质信息模态框 --> <!-- 新增/编辑资质信息模态框 -->
<a-modal <a-modal v-model:visible="modalVisible" :title="isEdit ? '编辑资质信息' : '新增资质信息'" width="700px" @ok="handleSubmit"
v-model:visible="modalVisible" :title="isEdit ? '编辑资质信息' : '新增资质信息'" width="700px" @ok="handleSubmit" @cancel="handleCancel">
@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">
@ -102,10 +96,8 @@
<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 <a-option v-for="item in CERTIFICATION_TYPE_OPTIONS" :key="item.value" :value="item.value"
v-for="item in CERTIFICATION_TYPE_OPTIONS" :key="item.value" :value="item.value" :label="item.label">
:label="item.label"
>
{{ item.label }} {{ item.label }}
</a-option> </a-option>
</a-select> </a-select>
@ -113,10 +105,8 @@
<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" placeholder="请选择持证人" :loading="loadingUsers" allow-search
v-model="formData.userId" placeholder="请选择持证人" :loading="loadingUsers" allow-search :filter-option="filterUserOption" @="console.log('d', formData)">
: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>
@ -139,18 +129,14 @@
</a-row> </a-row>
<a-form-item label="证书图片" field="certificationImage"> <a-form-item label="证书图片" field="certificationImage">
<a-upload <a-upload :custom-request="handleUpload" :show-file-list="false" accept="image/*"
:custom-request="handleUpload" :show-file-list="false" accept="image/*" :before-upload="beforeUpload">
:before-upload="beforeUpload"
>
<template #upload-button> <template #upload-button>
<div class="upload-wrapper"> <div class="upload-wrapper">
<a-image <a-image v-if="formData.certificationImage" :src="formData.certificationImage" width="200"
v-if="formData.certificationImage" :src="formData.certificationImage" width="200" height="120" fit="cover" show-loader preview />
height="120" fit="cover" show-loader preview
/>
<div v-else class="upload-placeholder"> <div v-else class="upload-placeholder">
<IconPlus /> <icon-plus />
<div>点击上传证书图片</div> <div>点击上传证书图片</div>
</div> </div>
</div> </div>
@ -164,9 +150,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref } from 'vue' import { ref, reactive, onMounted, computed } from 'vue'
import { Message, Modal } from '@arco-design/web-vue' import { Message, Modal } from '@arco-design/web-vue'
import { IconPlus, IconRefresh, IconSearch } from '@arco-design/web-vue/es/icon' import { IconPlus, IconSearch, IconRefresh } 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'
@ -188,7 +174,7 @@ const userList = ref<SimpleUserInfo[]>([])
const searchForm = reactive<CertificationListParams>({ const searchForm = reactive<CertificationListParams>({
certificationName: '', certificationName: '',
certificationType: '', certificationType: '',
userName: '', userName: ''
}) })
// //
@ -197,7 +183,7 @@ const paginationConfig = reactive({
pageSize: 10, pageSize: 10,
total: 0, total: 0,
showTotal: true, showTotal: true,
showJumper: true, showJumper: true
}) })
// //
@ -215,23 +201,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' },
@ -240,69 +226,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)
} }
// //
@ -314,7 +300,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,
@ -401,7 +387,7 @@ const handleReset = () => {
Object.assign(searchForm, { Object.assign(searchForm, {
certificationName: '', certificationName: '',
certificationType: '', certificationType: '',
userName: '', userName: ''
}) })
paginationConfig.current = 1 paginationConfig.current = 1
getCertificationList() getCertificationList()
@ -443,7 +429,7 @@ const deleteRecord = (record: CertificationInfo) => {
console.error('删除失败:', error) console.error('删除失败:', error)
Message.error('删除失败') Message.error('删除失败')
} }
}, }
}) })
} }
@ -485,7 +471,7 @@ const resetForm = () => {
certificationType: '', certificationType: '',
userId: '', userId: '',
validityDateBegin: '', validityDateBegin: '',
validityDateEnd: '', validityDateEnd: ''
}) })
formRef.value?.resetFields() formRef.value?.resetFields()
} }
@ -494,7 +480,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 v-if="record.status === 'draft'" @click="confirmSalary(record)">确认</a-link> <a-link @click="confirmSalary(record)" v-if="record.status === 'draft'">确认</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 { onMounted, reactive, ref } from 'vue' import { ref, reactive, onMounted } 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'
// //
const searchForm = reactive({ let 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 v-if="selectedRecord" class="detail-content"> <div class="detail-content" v-if="selectedRecord">
<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 { reactive, ref } from 'vue' import { ref, reactive } 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><IconEdit /></template> <template #icon><icon-edit /></template>
编辑信息 编辑信息
</a-button> </a-button>
</div> </div>
@ -190,7 +190,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from 'vue' import { ref, reactive } 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">
<IconFilePdf v-if="file.type === 'pdf'" /> <icon-file-pdf v-if="file.type === 'pdf'" />
<IconFile v-else /> <icon-file 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 { IconFile, IconFilePdf } from '@arco-design/web-vue/es/icon' import { IconFilePdf, IconFile } 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'
}, }
]) ])
// //

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