You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

255 lines
8.1 KiB
Vue

3 months ago
<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>
3 months ago
</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>
3 months ago
<!-- 设备列表与告警记录 -->
<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>
3 months ago
</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", " ");
3 months ago
};
onMounted(() => {
loadStats();
loadDeviceList();
loadAlarmList();
});
3 months ago
</script>
<style lang="scss" scoped>
.home {
padding: 16px;
.mb-4 {
margin-bottom: 16px;
3 months ago
}
// 统计卡片样式
.stat-card {
:deep(.el-card__body) {
padding: 16px 20px;
}
3 months ago
}
.stat-card-inner {
display: flex;
align-items: center;
3 months ago
}
.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;
3 months ago
&.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); }
3 months ago
}
.stat-content {
flex: 1;
min-width: 0;
3 months ago
.stat-title {
font-size: 14px;
color: #909399;
margin-bottom: 4px;
}
3 months ago
.stat-value {
font-size: 28px;
font-weight: 600;
color: #303133;
3 months ago
&.text-success { color: #67c23a; }
&.text-warning { color: #e6a23c; }
&.text-danger { color: #f56c6c; }
3 months ago
}
}
// 卡片头部样式
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
span {
font-size: 16px;
font-weight: 600;
color: #303133;
3 months ago
}
}
// 表格容器
:deep(.el-card__body) {
padding: 16px;
}
3 months ago
}
</style>