管理员首页更新

This commit is contained in:
2026-04-15 08:40:29 +08:00
parent 5a84f178f8
commit 8b2f021cbe

View File

@@ -38,7 +38,7 @@
<span v-for="item in alarmTrend" :key="item.day" :style="{ height: item.value + '%' }"></span> <span v-for="item in alarmTrend" :key="item.day" :style="{ height: item.value + '%' }"></span>
</div> </div>
<p> 7 天告警趋势</p> <p> 7 天告警趋势</p>
<em>后续接入 ECharts 后可替换为真实折线</em> <em>根据统计接口实时展示</em>
</div> </div>
<div class="donut-card"> <div class="donut-card">
<div class="donut"> <div class="donut">
@@ -76,7 +76,7 @@
<span>在线率</span> <span>在线率</span>
<span>状态</span> <span>状态</span>
</div> </div>
<div v-for="item in pagedProjectList" :key="item.name" class="project-row"> <div v-for="item in pagedProjectList" :key="item.key" class="project-row">
<strong>{{ item.name }}</strong> <strong>{{ item.name }}</strong>
<span>{{ item.warehouseName }}</span> <span>{{ item.warehouseName }}</span>
<span>{{ item.onlineRate }}</span> <span>{{ item.onlineRate }}</span>
@@ -163,55 +163,32 @@ import {
} from '@/api/worn/home' } from '@/api/worn/home'
const overview = reactive({ const overview = reactive({
projectTotal: 12, projectTotal: 0,
warehouseTotal: 86, warehouseTotal: 0,
deviceTotal: 342, deviceTotal: 0,
onlineCount: 328, onlineCount: 0,
offlineCount: 14, offlineCount: 0,
onlineRate: 95.9, onlineRate: 0,
alarmToday: 23, alarmToday: 0,
alarmUnhandled: 5 alarmUnhandled: 0
}) })
const exceptionStats = reactive({ const exceptionStats = reactive({
tempException: 8, total: 0
humidityException: 5,
gasOverCount: 12,
smokeAlarmCount: 3,
waterAlarmCount: 2,
total: 30
}) })
const realtimeAlarms = ref([]) const realtimeAlarms = ref([])
const projectList = ref([ const projectList = ref([])
{ name: '唐山项目', warehouseCount: 12, onlineRate: '98.2%', status: 'normal' },
{ name: '天津项目', warehouseCount: 8, onlineRate: '95.0%', status: 'warning' },
{ name: '北京项目', warehouseCount: 15, onlineRate: '100%', status: 'normal' },
{ name: '上海项目', warehouseCount: 10, onlineRate: '97.5%', status: 'normal' },
{ name: '宁德项目', warehouseCount: 7, onlineRate: '96.4%', status: 'normal' },
{ name: '苏州项目', warehouseCount: 9, onlineRate: '94.8%', status: 'warning' },
{ name: '广州项目', warehouseCount: 11, onlineRate: '99.1%', status: 'normal' },
{ name: '深圳项目', warehouseCount: 6, onlineRate: '93.7%', status: 'warning' }
])
const realtimeEvents = ref([]) const realtimeEvents = ref([])
const recentEventKeys = new Map() const recentEventKeys = new Map()
const projectPage = ref(1) const projectPage = ref(1)
const projectTotal = ref(0)
const DUPLICATE_EVENT_WINDOW = 3000 const DUPLICATE_EVENT_WINDOW = 3000
const PROJECT_PAGE_SIZE = 5 const PROJECT_PAGE_SIZE = 5
const alarmTrend = ref([ const alarmTrend = ref([])
{ day: 'Mon', value: 36 },
{ day: 'Tue', value: 58 },
{ day: 'Wed', value: 44 },
{ day: 'Thu', value: 72 },
{ day: 'Fri', value: 50 },
{ day: 'Sat', value: 64 },
{ day: 'Sun', value: 42 }
])
const overviewCards = computed(() => [ const overviewCards = computed(() => [
{ key: 'project', icon: 'P', label: '项目总数', value: overview.projectTotal, theme: '' }, { key: 'project', icon: 'P', label: '项目总数', value: overview.projectTotal, theme: '' },
@@ -252,7 +229,6 @@ function handleWsMessage(data) {
overview.alarmToday += 1 overview.alarmToday += 1
overview.alarmUnhandled += 1 overview.alarmUnhandled += 1
exceptionStats.total += 1 exceptionStats.total += 1
updateExceptionStats(data)
} }
} }
@@ -313,7 +289,6 @@ function loadAlarmType() {
function loadProjectPage() { function loadProjectPage() {
getHomeProjectList().then((res) => { getHomeProjectList().then((res) => {
const list = Array.isArray(res.data) ? res.data : [] const list = Array.isArray(res.data) ? res.data : []
projectTotal.value = list.length
projectList.value = list.map(normalizeProject).sort(sortProjectWarehouse) projectList.value = list.map(normalizeProject).sort(sortProjectWarehouse)
if (projectPage.value > projectPageTotal.value) { if (projectPage.value > projectPageTotal.value) {
projectPage.value = projectPageTotal.value projectPage.value = projectPageTotal.value
@@ -330,23 +305,27 @@ function changeProjectPage(step) {
projectPage.value = nextPage projectPage.value = nextPage
} }
function normalizeProject(item) { function normalizeProject(item, index) {
const onlineRate = toNumber(item.onlineRate, 0) const onlineRate = toNumber(item.onlineRate, 0)
const name = String(item.parentName || item.projectName || item.name || '未命名项目').trim()
const warehouseName = String(item.warehouseName || item.deptName || '-').trim()
return { return {
name: item.parentName || item.projectName || item.name || '未命名项目', key: `${name}-${warehouseName}-${index}`,
warehouseName: item.warehouseName || item.deptName || '-', name,
warehouseName,
onlineRate: `${onlineRate}%`, onlineRate: `${onlineRate}%`,
status: onlineRate >= 95 ? 'normal' : 'warning' status: onlineRate >= 95 ? 'normal' : 'warning'
} }
} }
function sortProjectWarehouse(a, b) { function sortProjectWarehouse(a, b) {
const projectCompare = String(a.name).localeCompare(String(b.name), 'zh-CN') const projectCompare = String(a.name).localeCompare(String(b.name), 'zh-Hans-CN', { numeric: true, sensitivity: 'base' })
if (projectCompare !== 0) { if (projectCompare !== 0) {
return projectCompare return projectCompare
} }
return String(a.warehouseName).localeCompare(String(b.warehouseName), 'zh-CN') return String(a.warehouseName).localeCompare(String(b.warehouseName), 'zh-Hans-CN', { numeric: true, sensitivity: 'base' })
} }
function toNumber(value, fallback = 0) { function toNumber(value, fallback = 0) {
@@ -604,24 +583,6 @@ function getTypeName(type) {
return typeMap[type] || '数据上报' return typeMap[type] || '数据上报'
} }
function updateExceptionStats(data) {
if (data?.type === 'env') {
if (data.temperature !== undefined || data.humidity !== undefined) {
exceptionStats.tempException += data.temperature !== undefined ? 1 : 0
exceptionStats.humidityException += data.humidity !== undefined ? 1 : 0
}
if (data.nh3 !== undefined || data.h2s !== undefined) {
exceptionStats.gasOverCount += 1
}
}
if (data?.type === 'smoke') {
exceptionStats.smokeAlarmCount += 1
}
if (data?.type === 'water' || data?.eventType === 'water') {
exceptionStats.waterAlarmCount += 1
}
}
function pushRealtimeEvent(event) { function pushRealtimeEvent(event) {
realtimeEvents.value.unshift(event) realtimeEvents.value.unshift(event)
realtimeEvents.value = realtimeEvents.value.slice(0, STREAM_TOTAL_SIZE) realtimeEvents.value = realtimeEvents.value.slice(0, STREAM_TOTAL_SIZE)