From acf2c84bd163d7b4324950719c1f25e69041518a Mon Sep 17 00:00:00 2001 From: ZHW <1941286652@qq.com> Date: Mon, 15 Dec 2025 19:32:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=B6=E6=B0=B4=E6=9C=BA=E4=B8=8E=E4=BE=9B?= =?UTF-8?q?=E6=B0=B4=E6=9C=BA=E7=9A=84=E9=A1=B5=E9=9D=A2=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../water/controller/AlertController.java | 11 +- .../controller/web/DeviceController.java | 8 +- .../web/DeviceStatusController.java | 4 + .../service/DeviceStatusServiceImpl.java | 27 +- .../resources/web/src/api/deviceStatus.ts | 35 +- src/main/resources/web/src/api/request.ts | 2 +- .../resources/web/src/views/Dashboard.vue | 360 +++++++++++++++--- .../web/src/views/equipment/WaterMaker.vue | 209 +++++----- .../web/src/views/equipment/WaterSupplier.vue | 77 ++-- .../web/src/views/workorder/Timeout.vue | 286 +++++++++----- 10 files changed, 720 insertions(+), 299 deletions(-) diff --git a/src/main/java/com/campus/water/controller/AlertController.java b/src/main/java/com/campus/water/controller/AlertController.java index dd2e766..fae5ee4 100644 --- a/src/main/java/com/campus/water/controller/AlertController.java +++ b/src/main/java/com/campus/water/controller/AlertController.java @@ -20,12 +20,19 @@ import java.util.List; @RequestMapping("/api/alerts") @RequiredArgsConstructor @Tag(name = "告警管理接口") + public class AlertController { private final AlertRepository alertRepository; + @GetMapping("/test") + @PreAuthorize("hasAnyRole('SUPER_ADMIN','AREA_ADMIN', 'REPAIRMAN')") + public ResultVO testAuth() { + return ResultVO.success("权限验证通过"); + } + @GetMapping("/history") - @PreAuthorize("hasAnyRole('ADMIN', 'REPAIRMAN')") + @PreAuthorize("hasAnyRole('SUPER_ADMIN','AREA_ADMIN', 'REPAIRMAN')") @Operation(summary = "分页查询告警历史(支持多条件筛选)") public ResultVO> getAlertHistory( @Parameter(description = "设备ID(可选)") @RequestParam(required = false) String deviceId, @@ -58,7 +65,7 @@ public class AlertController { * 查询未处理告警(紧急优先) */ @GetMapping("/pending") - @PreAuthorize("hasAnyRole('ADMIN', 'REPAIRMAN')") + @PreAuthorize("hasAnyRole('SUPER_ADMIN','AREA_ADMIN', 'REPAIRMAN')") public ResultVO> getPendingAlerts( @Parameter(description = "区域ID(可选)") @RequestParam(required = false) String areaId) { List pendingAlerts = areaId != null diff --git a/src/main/java/com/campus/water/controller/web/DeviceController.java b/src/main/java/com/campus/water/controller/web/DeviceController.java index 05bdcd3..9f98d7c 100644 --- a/src/main/java/com/campus/water/controller/web/DeviceController.java +++ b/src/main/java/com/campus/water/controller/web/DeviceController.java @@ -142,7 +142,7 @@ public class DeviceController { * 按状态查询设备列表(支持区域筛选) * 管理员/运维人员通用接口 */ - @GetMapping("/by-status") + /* @GetMapping("/by-status") @Operation(summary = "按状态查询设备", description = "根据设备状态筛选设备列表,可选区域筛选") public ResponseEntity>> getDevicesByStatus( @RequestParam String status, @@ -155,13 +155,13 @@ public class DeviceController { } catch (Exception e) { return ResponseEntity.ok(ResultVO.error(500, "按状态查询设备失败: " + e.getMessage())); } - } + }*/ /** * 按类型查询设备列表(支持区域筛选) * 管理员/运维人员通用接口 */ - @GetMapping("/by-type") + /* @GetMapping("/by-type") @Operation(summary = "按类型查询设备", description = "根据设备类型筛选设备列表,可选区域筛选") public ResponseEntity>> getDevicesByType( @RequestParam String deviceType, @@ -174,6 +174,6 @@ public class DeviceController { } catch (Exception e) { return ResponseEntity.ok(ResultVO.error(500, "按类型查询设备失败: " + e.getMessage())); } - } + }*/ } \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/web/DeviceStatusController.java b/src/main/java/com/campus/water/controller/web/DeviceStatusController.java index 370e44c..edd736c 100644 --- a/src/main/java/com/campus/water/controller/web/DeviceStatusController.java +++ b/src/main/java/com/campus/water/controller/web/DeviceStatusController.java @@ -22,6 +22,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -104,6 +105,7 @@ public class DeviceStatusController { * 按状态查询设备(仅状态筛选,支持区域) */ @GetMapping("/by-status") + @PreAuthorize("hasAnyRole('SUPER_ADMIN','AREA_ADMIN')") @Operation(summary = "按状态查询设备", description = "根据设备状态筛选设备列表,可选区域筛选") public ResponseEntity>> getDevicesByStatus( @RequestParam String status, @@ -122,6 +124,7 @@ public class DeviceStatusController { * 按类型查询设备(仅类型筛选,支持区域) */ @GetMapping("/by-type") + @PreAuthorize("hasAnyRole('SUPER_ADMIN','AREA_ADMIN')") @Operation(summary = "按类型查询设备", description = "根据设备类型筛选设备列表,可选区域筛选") public ResponseEntity>> getDevicesByType( @RequestParam String deviceType, @@ -137,6 +140,7 @@ public class DeviceStatusController { } @GetMapping("/status-count") + @PreAuthorize("hasAnyRole('SUPER_ADMIN','AREA_ADMIN')") @Operation(summary = "设备状态数量统计", description = "统计各状态设备数量") public ResponseEntity>> getDeviceStatusCount( @RequestParam(required = false) String areaId, diff --git a/src/main/java/com/campus/water/service/DeviceStatusServiceImpl.java b/src/main/java/com/campus/water/service/DeviceStatusServiceImpl.java index f5b9bde..a4aef47 100644 --- a/src/main/java/com/campus/water/service/DeviceStatusServiceImpl.java +++ b/src/main/java/com/campus/water/service/DeviceStatusServiceImpl.java @@ -1,4 +1,3 @@ -// com/campus/water/service/DeviceStatusServiceImpl.java package com.campus.water.service; import com.campus.water.entity.Device; @@ -27,6 +26,7 @@ public class DeviceStatusServiceImpl implements DeviceStatusService { return false; } device.setStatus(request.getStatus()); + device.setRemark(request.getRemark()); deviceRepository.save(device); return true; @@ -81,7 +81,7 @@ public class DeviceStatusServiceImpl implements DeviceStatusService { @Override public List getDevicesByStatusWithArea(String status, String areaId) { try { - Device.DeviceStatus targetStatus = Device.DeviceStatus.valueOf(status.toUpperCase()); + Device.DeviceStatus targetStatus = Device.DeviceStatus.valueOf(status); if (areaId != null && !areaId.isEmpty()) { return deviceRepository.findByStatusAndAreaId(targetStatus, areaId); } else { @@ -101,7 +101,7 @@ public class DeviceStatusServiceImpl implements DeviceStatusService { @Override public List getDevicesByTypeWithArea(String deviceType, String areaId) { try { - Device.DeviceType targetType = Device.DeviceType.valueOf(deviceType.toUpperCase()); + Device.DeviceType targetType = Device.DeviceType.valueOf(deviceType); if (areaId != null && !areaId.isEmpty()) { return deviceRepository.findByDeviceTypeAndAreaId(targetType, areaId); } else { @@ -115,25 +115,26 @@ public class DeviceStatusServiceImpl implements DeviceStatusService { @Override public Map getDeviceStatusCount(String areaId, String deviceType) { - Device.DeviceType targetType = Device.DeviceType.valueOf(deviceType); - return Map.of( - "online", deviceRepository.countByStatusAndAreaIdAndDeviceType(Device.DeviceStatus.online, areaId, targetType), - "offline", deviceRepository.countByStatusAndAreaIdAndDeviceType(Device.DeviceStatus.offline, areaId, targetType), - "fault", deviceRepository.countByStatusAndAreaIdAndDeviceType(Device.DeviceStatus.fault, areaId, targetType) - ); + try { + Device.DeviceType targetType = Device.DeviceType.valueOf(deviceType); + return Map.of( + "online", deviceRepository.countByStatusAndAreaIdAndDeviceType(Device.DeviceStatus.online, areaId, targetType), + "offline", deviceRepository.countByStatusAndAreaIdAndDeviceType(Device.DeviceStatus.offline, areaId, targetType), + "fault", deviceRepository.countByStatusAndAreaIdAndDeviceType(Device.DeviceStatus.fault, areaId, targetType) + ); + } catch (IllegalArgumentException e) { + log.error("设备类型枚举转换失败,类型值:{}", deviceType, e); + throw new RuntimeException("无效的设备类型:" + deviceType); + } } @Override public List getOfflineDevicesExceedThreshold(Integer thresholdMinutes, String areaId) { - // 由于没有last_active_time,此处逻辑需调整: - // 方案1:若设备有最近操作时间,可用作替代; - // 方案2:仅返回状态为offline的设备(不判断时间) return deviceRepository.findByAreaIdAndStatus(areaId, Device.DeviceStatus.offline); } @Override public void autoDetectOfflineDevices(Integer thresholdMinutes) { - // 同理,无last_active_time时,无法通过时间判断,可注释或简化逻辑 log.info("自动检测离线设备(不执行时间判断,仅依赖手动标记)"); } } \ No newline at end of file diff --git a/src/main/resources/web/src/api/deviceStatus.ts b/src/main/resources/web/src/api/deviceStatus.ts index 8c11722..38a9453 100644 --- a/src/main/resources/web/src/api/deviceStatus.ts +++ b/src/main/resources/web/src/api/deviceStatus.ts @@ -3,18 +3,31 @@ import axios from 'axios' export const DeviceStatusApi = { // 获取设备状态列表 - 修改为匹配后端实际接口 - getDevicesByStatus: async (status: string, areaId?: string, deviceType?: string) => { - try { - const params: any = { status } - if (areaId) params.areaId = areaId - if (deviceType) params.deviceType = deviceType + getDevicesByType: async (deviceType: string, areaId?: string) => { + try { + const params: any = { deviceType } + if (areaId) params.areaId = areaId - const response = await axios.get('/api/web/device-status/by-status', { params }) - return response.data - } catch (error) { - throw new Error(`获取设备列表失败: ${error}`) - } - }, + // 注意:这里是调用设备状态管理的接口 + const response = await axios.get('/api/web/device-status/by-type', { params }) + return response.data + } catch (error) { + throw new Error(`获取设备列表失败: ${error}`) + } + }, + + // 按状态查询设备 + getDevicesByStatus: async (status: string, areaId?: string) => { + try { + const params: any = { status } + if (areaId) params.areaId = areaId + + const response = await axios.get('/api/web/device-status/by-status', { params }) + return response.data + } catch (error) { + throw new Error(`获取设备列表失败: ${error}`) + } + }, // 标记设备在线 markDeviceOnline: async (deviceId: string) => { diff --git a/src/main/resources/web/src/api/request.ts b/src/main/resources/web/src/api/request.ts index 8ef72c2..d515364 100644 --- a/src/main/resources/web/src/api/request.ts +++ b/src/main/resources/web/src/api/request.ts @@ -61,7 +61,7 @@ export async function request( let responseText = '' try { responseText = await response.text() - console.log('📥 响应内容:', responseText) + //console.log('📥 响应内容:', responseText) } catch (e) { console.log('📥 无法读取响应文本') } diff --git a/src/main/resources/web/src/views/Dashboard.vue b/src/main/resources/web/src/views/Dashboard.vue index 2fc51b9..0aac9be 100644 --- a/src/main/resources/web/src/views/Dashboard.vue +++ b/src/main/resources/web/src/views/Dashboard.vue @@ -10,91 +10,65 @@
📊
-
156
+
{{ stats.totalDevices }}
设备总数
🟢
-
142
+
{{ stats.onlineDevices }}
在线数量
⚠️
-
8
+
{{ stats.alertDevices }}
告警设备
📋
-
12
+
{{ stats.pendingWorkOrders }}
待处理工单
🔴
-
1
+
{{ stats.offlineDevices }}
离线设备
-
+
⚠️
- 设备 A08制水机 异常,请关注! + 设备 {{ latestAlert.deviceId }} 异常,请关注!
×
- -
- + +
+
-

设备状态分布

-
-
-
-
-
- 75 -
-
-
-
矿化水优良情况
-
所有设备总体情况
+

最新告警

+
+
+
{{ alert.deviceId }}:{{ alert.message }}
+
{{ formatAlertLevel(alert.level) }}
- -
- -
-

最新告警

-
-
-
A08制水机:TDS值超标
-
紧急
+ +
+

工单状态统计

+
+
+
Axhub Charts
+
柱状图
+
+ 通过Group内data和config中继器可更改数据及配置
-
-
-
C11制水机:离线
-
警告
-
-
-
- - -
-

工单状态统计

-
-
-
Axhub Charts
-
柱状图
-
- 通过Group内data和config中继器可更改数据及配置 -
-
- 详情访问:https://axhub.im/charts -
+
+ 详情访问:https://axhub.im/charts
@@ -104,6 +78,171 @@ \ No newline at end of file +.alert-warning { + background: #fff8e6; + border-left: 4px solid #ffc107; + padding: 16px; + display: flex; + align-items: center; + margin-bottom: 24px; + border-radius: 4px; +} + +.alert-icon { + font-size: 20px; + margin-right: 12px; + color: #ff9800; +} + +.alert-content { + flex: 1; + font-size: 14px; + color: #333; +} + +.alert-close { + font-size: 20px; + cursor: pointer; + color: #999; +} + +/* 修改为单列布局 */ +.content-single-column { + display: flex; + flex-direction: column; + gap: 24px; +} + +.content-card { + background: white; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.card-title { + font-size: 18px; + font-weight: 600; + color: #1a1a1a; + margin-bottom: 16px; + text-align: center; /* 标题居中 */ +} + +.alert-list { + display: flex; + flex-direction: column; +} + +.alert-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 0; +} + +.alert-text { + font-size: 14px; + color: #333; +} + +.alert-level { + font-size: 12px; + padding: 4px 8px; + border-radius: 4px; + font-weight: 500; +} + +.alert-level.critical { + background: #ffebee; + color: #c62828; +} + +.alert-level.error { + background: #ffebee; + color: #c62828; +} + +.alert-level.warning { + background: #fff8e1; + color: #ff8f00; +} + +.alert-divider { + height: 1px; + background: #f0f0f0; +} + +.chart-placeholder { + height: 200px; + display: flex; + align-items: center; + justify-content: center; + background: #f8f9fa; + border-radius: 4px; +} + +.placeholder-text { + text-align: center; + color: #666; +} + +@media (max-width: 768px) { + .stats-grid { + grid-template-columns: repeat(2, 1fr); + } +} + diff --git a/src/main/resources/web/src/views/equipment/WaterMaker.vue b/src/main/resources/web/src/views/equipment/WaterMaker.vue index aadfa19..96fe650 100644 --- a/src/main/resources/web/src/views/equipment/WaterMaker.vue +++ b/src/main/resources/web/src/views/equipment/WaterMaker.vue @@ -204,8 +204,9 @@ @@ -850,4 +853,4 @@ onMounted(async () => { min-width: auto; } } - \ No newline at end of file + diff --git a/src/main/resources/web/src/views/equipment/WaterSupplier.vue b/src/main/resources/web/src/views/equipment/WaterSupplier.vue index a7047ae..3162a55 100644 --- a/src/main/resources/web/src/views/equipment/WaterSupplier.vue +++ b/src/main/resources/web/src/views/equipment/WaterSupplier.vue @@ -63,7 +63,7 @@ - + {{ device.id }} 供水机 {{ device.area }} @@ -78,7 +78,7 @@ - + 暂无设备数据 @@ -95,7 +95,7 @@ 上一页 - 第 {{ currentPage }} 页 / 共 {{ totalPages }} 页 + 第 {{ currentPage }} 页 / 共 {{ totalPages }} 页 (共 {{ filteredDevices.length }} 条记录) - - 暂无超时未抢工单 + + + {{ loading ? '正在加载数据...' : '暂无超时未抢工单' }} + @@ -90,18 +92,18 @@
-