|
|
|
|
<template>
|
|
|
|
|
<div class="app-container home">
|
|
|
|
|
<!-- 统计卡片 -->
|
|
|
|
|
<el-row :gutter="16" class="mb-4">
|
|
|
|
|
<el-col :xs="12" :sm="12" :md="6">
|
|
|
|
|
<el-card shadow="hover" class="stat-card">
|
|
|
|
|
<div class="stat-card-inner">
|
|
|
|
|
<div class="stat-icon bg-primary"><el-icon :size="28"><Monitor /></el-icon></div>
|
|
|
|
|
<div class="stat-content">
|
|
|
|
|
<div class="stat-title">设备总数</div>
|
|
|
|
|
<div class="stat-value">{{ stats.totalDevices || 0 }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="12" :sm="12" :md="6">
|
|
|
|
|
<el-card shadow="hover" class="stat-card">
|
|
|
|
|
<div class="stat-card-inner">
|
|
|
|
|
<div class="stat-icon bg-success"><el-icon :size="28"><CircleCheck /></el-icon></div>
|
|
|
|
|
<div class="stat-content">
|
|
|
|
|
<div class="stat-title">在线设备</div>
|
|
|
|
|
<div class="stat-value text-success">{{ stats.onlineDevices || 0 }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="12" :sm="12" :md="6">
|
|
|
|
|
<el-card shadow="hover" class="stat-card">
|
|
|
|
|
<div class="stat-card-inner">
|
|
|
|
|
<div class="stat-icon bg-warning"><el-icon :size="28"><CircleClose /></el-icon></div>
|
|
|
|
|
<div class="stat-content">
|
|
|
|
|
<div class="stat-title">离线设备</div>
|
|
|
|
|
<div class="stat-value text-warning">{{ stats.offlineDevices || 0 }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="12" :sm="12" :md="6">
|
|
|
|
|
<el-card shadow="hover" class="stat-card">
|
|
|
|
|
<div class="stat-card-inner">
|
|
|
|
|
<div class="stat-icon bg-danger"><el-icon :size="28"><Warning /></el-icon></div>
|
|
|
|
|
<div class="stat-content">
|
|
|
|
|
<div class="stat-title">告警设备</div>
|
|
|
|
|
<div class="stat-value text-danger">{{ stats.alarmDevices || 0 }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
|
|
|
|
|
<!-- 设备列表与告警记录 -->
|
|
|
|
|
<el-row :gutter="16">
|
|
|
|
|
<el-col :xs="24" :md="14">
|
|
|
|
|
<el-card shadow="hover">
|
|
|
|
|
<template #header>
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<span>设备列表</span>
|
|
|
|
|
<el-button type="primary" link @click="$router.push('/base/rfidDevice')">查看更多</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<el-table :data="deviceList" border stripe max-height="400" empty-text="暂无数据">
|
|
|
|
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
|
|
|
|
<el-table-column prop="deviceCode" label="设备编号" min-width="120" />
|
|
|
|
|
<el-table-column prop="deviceName" label="设备名称" min-width="120" />
|
|
|
|
|
<el-table-column prop="locationAlias" label="所在位置" min-width="100">
|
|
|
|
|
<template #default="{ row }">{{ row.locationAlias || '-' }}</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="onlineStatus" label="在线状态" width="100" align="center">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-tag :type="row.onlineStatus === '1' ? 'success' : 'info'" size="small">
|
|
|
|
|
{{ row.onlineStatus === '1' ? '在线' : '离线' }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="alarmStatus" label="告警状态" width="100" align="center">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-tag :type="row.alarmStatus === '1' ? 'danger' : 'success'" size="small">
|
|
|
|
|
{{ row.alarmStatus === '1' ? '告警' : '正常' }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="24" :md="10">
|
|
|
|
|
<el-card shadow="hover">
|
|
|
|
|
<template #header>
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<span>告警记录</span>
|
|
|
|
|
<el-button type="primary" link @click="$router.push('/base/rfidReadRecord')">查看更多</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<el-table :data="alarmList" border stripe max-height="400" empty-text="暂无数据">
|
|
|
|
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
|
|
|
|
<el-table-column prop="deviceName" label="设备" min-width="100">
|
|
|
|
|
<template #default="{ row }">{{ row.deviceName || row.deviceCode || '-' }}</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="recordTime" label="时间" min-width="140">
|
|
|
|
|
<template #default="{ row }">{{ formatRecordTime(row.recordTime) }}</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="alarmLevel" label="级别" width="80" align="center">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-tag :type="row.alarmLevel === '2' ? 'danger' : 'warning'" size="small">
|
|
|
|
|
{{ row.alarmLevel === '2' ? '严重' : (row.alarmLevel === '1' ? '一般' : '-') }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="alarmAction" label="行为" min-width="80">
|
|
|
|
|
<template #default="{ row }">{{ row.alarmAction || '-' }}</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup name="Index" lang="ts">
|
|
|
|
|
import { getDashboardStats } from "@/api/rfid/dashboard";
|
|
|
|
|
import { getRfidDeviceList } from "@/api/rfid/rfidDevice";
|
|
|
|
|
import { getRfidReadRecordList } from "@/api/rfid/rfidReadRecord";
|
|
|
|
|
import type { RfidDeviceVO } from "@/api/rfid/rfidDevice/types";
|
|
|
|
|
import type { RfidReadRecordVO } from "@/api/rfid/rfidReadRecord/types";
|
|
|
|
|
|
|
|
|
|
const stats = ref({
|
|
|
|
|
totalDevices: 0,
|
|
|
|
|
onlineDevices: 0,
|
|
|
|
|
offlineDevices: 0,
|
|
|
|
|
alarmDevices: 0
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const deviceList = ref<RfidDeviceVO[]>([]);
|
|
|
|
|
const alarmList = ref<RfidReadRecordVO[]>([]);
|
|
|
|
|
|
|
|
|
|
const loadStats = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const res = await getDashboardStats();
|
|
|
|
|
if (res.data) {
|
|
|
|
|
stats.value = res.data;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("获取统计数据失败", e);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const loadDeviceList = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const res: any = await getRfidDeviceList({ isMarked: "1" } as any);
|
|
|
|
|
deviceList.value = res.data || [];
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("获取设备列表失败", e);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const loadAlarmList = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const res: any = await getRfidReadRecordList({ alarmFlag: "1" } as any);
|
|
|
|
|
alarmList.value = res.data || [];
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("获取告警记录失败", e);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const formatRecordTime = (val: any) => {
|
|
|
|
|
if (!val) {
|
|
|
|
|
return "-";
|
|
|
|
|
}
|
|
|
|
|
return String(val).replace("T", " ");
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
loadStats();
|
|
|
|
|
loadDeviceList();
|
|
|
|
|
loadAlarmList();
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.home {
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
|
|
|
|
.mb-4 {
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 统计卡片样式
|
|
|
|
|
.stat-card {
|
|
|
|
|
:deep(.el-card__body) {
|
|
|
|
|
padding: 16px 20px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-card-inner {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-icon {
|
|
|
|
|
width: 56px;
|
|
|
|
|
height: 56px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
color: #fff;
|
|
|
|
|
margin-right: 16px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
|
|
|
|
&.bg-primary { background: linear-gradient(135deg, #409eff, #53a8ff); }
|
|
|
|
|
&.bg-success { background: linear-gradient(135deg, #67c23a, #85ce61); }
|
|
|
|
|
&.bg-warning { background: linear-gradient(135deg, #e6a23c, #ebb563); }
|
|
|
|
|
&.bg-danger { background: linear-gradient(135deg, #f56c6c, #f78989); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-content {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
|
|
|
|
.stat-title {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
color: #909399;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-value {
|
|
|
|
|
font-size: 28px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
|
|
|
|
&.text-success { color: #67c23a; }
|
|
|
|
|
&.text-warning { color: #e6a23c; }
|
|
|
|
|
&.text-danger { color: #f56c6c; }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 卡片头部样式
|
|
|
|
|
.card-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
|
|
span {
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 表格容器
|
|
|
|
|
:deep(.el-card__body) {
|
|
|
|
|
padding: 16px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|