refactor(frontend): Dashboard 消费后端时效判定,删除本地重复计算

- 删除 renderList 里自算的 diffDays / diffHours / isToday / validTime,改为
  消费后端 to_dict() 的 stale_level / stale_days。这几个变量此前只在
  Dashboard.vue 内部使用(已 grep 确认),移除不影响其他组件。
- 状态分级映射:severe -> 严重滞后、lagging -> 滞后、yesterday -> 昨日数据。
  模板契约(statusColor / statusLabel / statusType / statusLabelColor /
  statusReason / sortWeight)保持不变,未改动任何模板代码。
- sortWeight 改为 stale_days * 24,与原 diffHours 同为小时量纲,排序结果与
  原实现一致(同为「当天」的设备不再按小时细分,顺序退化为稳定序)。
- 离线原因文案与后端 offset 统一:拿不到可解析的数据时间就说「从未同步」,
  不再另造「暂无数据」的说法。
- 时间解析保留下来仅用于显示格式化,不再参与滞后判定。

构建产物 web_dist 已重新生成并验证含新字段,但按 .gitignore 不入库。
This commit is contained in:
DXC
2026-09-15 17:10:57 +08:00
parent 783c633a15
commit 8521aee7f9

View File

@ -341,8 +341,13 @@ const fetchData = async () => {
const isOrphanIoT = (item.source === 'iot_card')
const isWhitelist = !!item.is_whitelist
// === 1. 智能时间解析与格式化 (增强版) ===
let diffDays = 0, diffHours = 0, isToday = false, validTime = false
// === 1. 时间显示格式化 ===
// 滞后判定已统一由后端提供(stale_level / stale_days / offset),
// 这里只负责把 latest_time 渲染成统一格式,不再本地计算天数。
// 判定与渲染分家的原因见 services/time_utils.py 顶部说明。
const staleLevel = item.stale_level || 'unknown'
const staleDays = (typeof item.stale_days === 'number') ? item.stale_days : null
const validTime = staleLevel !== 'unknown'
let timeStr = item.latest_time
// 默认显示原始值,稍后如果解析成功则覆盖它
@ -377,16 +382,8 @@ const fetchData = async () => {
d = new Date(cleanStr);
}
// C. 如果解析成功,强制重新生成统一的显示字符串
// C. 解析成功则强制重新生成统一的显示格式 YYYY-MM-DD HH:mm:ss
if (d && !isNaN(d.getTime())) {
validTime = true
isToday = d.toDateString() === now.toDateString()
const diff = now - d
diffHours = (diff > 0 ? diff : 0) / (1000 * 3600)
diffDays = diffHours / 24
// 🌟 核心修改点:生成标准显示格式 YYYY-MM-DD HH:mm:ss 🌟
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const dd = String(d.getDate()).padStart(2, '0')
@ -429,29 +426,33 @@ const fetchData = async () => {
}
}
// 4. 状态判定
// 4. 状态判定(分级由后端给出,这里只做映射,不再本地判天数)
let statusColor = '#67C23A', statusLabel = '正常', statusType = 'normal', statusLabelColor = '#fff'
let statusReason = ''
let sortWeight = diffHours
// 排序权重:问题设备排前面。staleDays * 24 与原 diffHours 同为小时量纲,
// 排序结果与原实现一致(同为“当天”的设备不再按小时细分,顺序退化为稳定序)
let sortWeight = (staleDays === null) ? 80000000 : staleDays * 24
if (item.is_maintaining) {
statusColor = '#409EFF'; statusLabel = '维修中'; statusType = 'maintenance';
sortWeight = Number.MAX_SAFE_INTEGER;
} else if (!validTime || item.status === 'offline') {
statusLabel = '离线'; statusColor = '#F56C6C'; statusType = 'error';
statusReason = validTime ? '设备离线' : '暂无数据';
// 文案与后端 offset 统一:拿不到可解析的数据时间就说“从未同步”,
// 不再另造一套“暂无数据”的说法
statusReason = (item.status === 'offline') ? '设备离线' : (item.offset || '从未同步');
sortWeight = 80000000;
} else if (diffDays > 7) {
} else if (staleLevel === 'severe') {
statusLabel = '严重滞后'; statusColor = '#F56C6C'; statusType = 'error';
statusReason = `滞后 ${Math.floor(diffDays)} 天`;
} else if (diffHours > 24) {
statusReason = item.offset || `滞后 ${staleDays} 天`;
} else if (staleLevel === 'lagging') {
statusLabel = '滞后'; statusColor = '#E6A23C'; statusType = 'warning';
statusReason = `滞后 ${Math.floor(diffDays)} 天`;
statusReason = item.offset || `滞后 ${staleDays} 天`;
} else if (expireWarning) {
statusLabel = '即将过期'; statusColor = '#E6A23C'; statusType = 'warning';
statusReason = `即将过期`;
sortWeight = 400;
} else if (!isToday) {
} else if (staleLevel === 'yesterday') {
statusLabel = '昨日数据'; statusColor = '#FAC858'; statusType = 'slight-warning'; statusLabelColor = '#333';
statusReason = '非今日数据';
} else {
@ -465,7 +466,7 @@ const fetchData = async () => {
isOrphanIoT,
isBound,
isWhitelist,
diffDays, diffHours, sortWeight, isToday,
sortWeight, staleLevel, staleDays,
statusColor, statusLabel, statusType, statusLabelColor, statusReason,
isEditingSite: false, tempSite: '',
data_quality: item.data_quality || 'ok',