-
⚠️ 设备已离线 (无法连接)
-
⚠️ 数据中断 {{ row.diffDays.toFixed(1) }} 天
-
⚠️ 数据滞后 {{ row.diffDays.toFixed(1) }} 天
+
+
+
+ ⚠️ 设备已离线 (无法连接)
+
+
+
+ ⚠️ 严重滞后 {{ Math.floor(row.diffDays) }} 天
+
+
+
+ ⚠️ 数据滞后 {{ Math.floor(row.diffDays) }} 天
+
+
+
+ ⚠️ 非今日数据 (滞后 < 24h)
+
+
+
+ ✅ 数据最新 ({{ row.diffHours < 1 ? '刚刚' : `${Math.floor(row.diffHours)}小时前` }})
+
+
+
+ 🛠️ 维护期间忽略数据告警
@@ -213,7 +241,6 @@ const logDialog = reactive({
form: { content: '' }
})
-// 计算统计信息 (注意:错误和警告只统计未隐藏的设备)
const summary = computed(() => {
const activeDevices = rawData.value.filter(r => !r.is_hidden)
const errors = activeDevices.filter(r => r.statusType === 'error').length
@@ -229,15 +256,6 @@ const summary = computed(() => {
}
})
-const getDaysDiff = (dateStr, nowObj) => {
- if (!dateStr || dateStr === 'N/A') return 999
- let cleanDateStr = dateStr.toString().replace(/_/g, '-')
- const d = new Date(cleanDateStr)
- if (isNaN(d.getTime())) return 999
- const diffTime = Math.abs(nowObj - d)
- return diffTime / (1000 * 60 * 60 * 24)
-}
-
const fetchData = async () => {
loading.value = true
try {
@@ -246,15 +264,52 @@ const fetchData = async () => {
const now = new Date()
let processedData = backendList.map(item => {
- const diff = getDaysDiff(item.latest_time, now)
-
- // 默认是否隐藏 (如果后端没传,默认为 false)
const isHidden = item.is_hidden === true || item.is_hidden === 1
+ let diffDays = 0
+ let diffHours = 0
+ let isToday = false
+ let validTime = false
+
+ // 计算真实时间
+ if (item.latest_time && item.latest_time !== 'N/A') {
+ const cleanDateStr = item.latest_time.toString().replace(/_/g, '-')
+ const d = new Date(cleanDateStr)
+ if (!isNaN(d.getTime())) {
+ validTime = true
+ const diffTime = now - d
+ const safeDiff = diffTime > 0 ? diffTime : 0
+
+ diffHours = safeDiff / (1000 * 60 * 60)
+ diffDays = safeDiff / (1000 * 60 * 60 * 24)
+ isToday = d.toDateString() === now.toDateString()
+ }
+ }
+
+ // --- 核心排序逻辑修正 ---
+ // 目标顺序:维修 > 离线 > 无数据 > 滞后时间长 > 滞后时间短
+ // 我们用 sortHours 来控制这个顺序,数值越大越靠前
+ let sortHours = diffHours;
+
+ // 1. 维修中:给一个最大的安全整数,保证在所有排序中都是第一
+ if (item.is_maintaining) {
+ sortHours = Number.MAX_SAFE_INTEGER; // 9007199254740991,绝对第一
+ }
+ // 2. 离线:给一个次大的数 (10亿)
+ else if (item.status === 'offline' || item.status === '已离线') {
+ sortHours = 1000000000;
+ }
+ // 3. 无数据但没报离线:给一个第三大的数 (5亿)
+ else if (!validTime) {
+ sortHours = 500000000;
+ }
+ // 4. 其他情况 sortHours 就是真实的 diffHours
+
let statusColor = '#67C23A'
let statusLabel = '正常在线'
let statusType = 'normal'
- let sortWeight = 4
+ let sortWeight = 6
+ let statusLabelColor = '#fff'
if (item.is_maintaining) {
statusColor = '#409EFF'
@@ -262,47 +317,57 @@ const fetchData = async () => {
statusType = 'maintenance'
sortWeight = 1
}
- else if (item.status === 'offline' || item.status === '已离线') {
+ else if ((item.status === 'offline' || item.status === '已离线') || (!validTime || diffDays > 7)) {
+ if (item.status === 'offline' || item.status === '已离线') {
+ statusLabel = '🔴 设备离线'
+ } else {
+ statusLabel = '严重滞后'
+ }
statusColor = '#F56C6C'
- statusLabel = '🔴 设备离线'
statusType = 'error'
sortWeight = 2
}
- else if (!item.latest_time || diff > 7) {
- statusColor = '#F56C6C'
- statusLabel = '💾 数据中断'
- statusType = 'error'
- sortWeight = 2
- }
- else if (diff > 2) {
+ else if (diffHours > 24) {
statusColor = '#E6A23C'
- statusLabel = '⚠️ 数据滞后'
+ statusLabel = '数据滞后'
statusType = 'warning'
sortWeight = 3
}
+ else if (!isToday) {
+ statusColor = '#FAC858'
+ statusLabel = '昨日数据'
+ statusType = 'slight-warning'
+ statusLabelColor = '#333'
+ sortWeight = 4
+ }
else {
statusColor = '#67C23A'
statusLabel = '🟢 运行正常'
statusType = 'normal'
- sortWeight = 4
+ sortWeight = 5
}
return {
...item,
- is_hidden: isHidden, // 绑定隐藏状态
- diffDays: diff,
+ is_hidden: isHidden,
+ diffDays,
+ diffHours,
+ sortHours, // 排序专用字段
+ isToday,
statusColor,
statusLabel,
statusType,
sortWeight,
+ statusLabelColor,
isEditingSite: false,
tempSite: ''
}
})
+ // 默认排序逻辑(即便没有 el-table 排序,数组本身也应该是这个顺序)
processedData.sort((a, b) => {
- if (a.sortWeight !== b.sortWeight) return a.sortWeight - b.sortWeight
- return (a.name || '').localeCompare(b.name || '')
+ // 数值越大越靠前 (Maintenance > Offline > NoData > Old > New)
+ return b.sortHours - a.sortHours
})
rawData.value = processedData
@@ -329,24 +394,16 @@ const runManualMonitor = async () => {
}
}
-// --- 过滤逻辑 (核心修改) ---
const filteredData = computed(() => {
return rawData.value.filter(item => {
- // 1. 隐藏状态筛选
if (filters.status === 'hidden') {
- // 只有在选中“回收站”时,才显示已隐藏的设备
if (!item.is_hidden) return false
} else {
- // 在其他模式(全部、异常)下,必须隐藏已隐藏的设备
if (item.is_hidden) return false
-
- // 处理异常筛选
if (filters.status === 'abnormal') {
- if (item.statusType !== 'error' && item.statusType !== 'warning') return false
+ if (item.statusType !== 'error' && item.statusType !== 'warning' && item.statusType !== 'slight-warning') return false
}
}
-
- // 2. 关键词搜索
const keyMatch = !filters.keyword || (item.name && item.name.toLowerCase().includes(filters.keyword.toLowerCase()))
return keyMatch
})
@@ -395,20 +452,14 @@ const handleMaintenanceBeforeChange = (row) => {
})
}
-// --- 新增:隐藏/恢复设备 ---
const toggleHidden = async (row, targetState) => {
try {
await axios.post(`${API_BASE}/api/toggle_hidden`, {
name: row.name,
is_hidden: targetState
})
-
- // 更新本地状态,这样不需要刷新页面也能立即消失/出现
row.is_hidden = targetState
-
ElMessage.success(targetState ? '设备已隐藏(移至回收站)' : '设备已恢复显示')
-
- // 自动刷新数据以确保排序和统计正确
fetchData()
} catch (e) {
console.error(e)
@@ -443,10 +494,11 @@ const submitLog = async () => {
const formatDisplayName = (name) => name ? name.toUpperCase().replace(/_/g, ' ') : ''
const tableRowClassName = ({ row }) => {
- if (row.is_hidden) return 'hidden-row' // 灰色行
+ if (row.is_hidden) return 'hidden-row'
+ if (row.statusType === 'maintenance') return 'maintenance-row'
if (row.statusType === 'error') return 'error-row'
if (row.statusType === 'warning') return 'warning-row'
- if (row.statusType === 'maintenance') return 'maintenance-row'
+ if (row.statusType === 'slight-warning') return 'slight-warning-row'
return ''
}
@@ -522,7 +574,6 @@ onBeforeUnmount(() => window.removeEventListener('resize', () => windowWidth.val
color: #909399;
}
-/* 回收站文字颜色 */
.search-input {
width: 250px;
}
@@ -538,15 +589,20 @@ onBeforeUnmount(() => window.removeEventListener('resize', () => windowWidth.val
color: #999;
}
-/* 删除线样式 */
-
-.warning-text {
- color: #F56C6C;
- font-size: 12px;
+.status-text {
+ font-size: 13px;
margin-top: 4px;
font-weight: bold;
+ display: flex;
+ align-items: center;
}
+.maintenance-text { color: #409EFF; }
+.error-text { color: #F56C6C; }
+.warning-text { color: #E6A23C; }
+.slight-warning-text { color: #dcb041; }
+.success-text { color: #67C23A; }
+
.display-cell {
cursor: pointer;
padding: 5px;
@@ -602,14 +658,13 @@ onBeforeUnmount(() => window.removeEventListener('resize', () => windowWidth.val
align-items: flex-start;
gap: 10px;
}
-
.filter-section {
flex-direction: column;
align-items: stretch;
}
-
.search-input {
width: 100%;
+ min-height: 300px;
}
}