<template>
|
<div class="app-container">
|
<el-form :model="filters" :inline="true">
|
<el-form-item label="设备名称">
|
<el-input
|
v-model="filters.name"
|
style="width: 240px"
|
placeholder="请输入设备名称"
|
clearable
|
:prefix-icon="Search"
|
@change="getTableData"
|
/>
|
</el-form-item>
|
<el-form-item label="设备状态">
|
<el-select
|
v-model="filters.status"
|
style="width: 240px"
|
placeholder="请选择设备状态"
|
clearable
|
@change="getTableData"
|
>
|
<el-option label="在线" value="1"></el-option>
|
<el-option label="离线" value="0"></el-option>
|
<el-option label="故障" value="2"></el-option>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="设备类型">
|
<el-select
|
v-model="filters.type"
|
style="width: 240px"
|
placeholder="请选择设备类型"
|
clearable
|
@change="getTableData"
|
>
|
<el-option label="门禁设备" value="0"></el-option>
|
<el-option label="环境监测设备" value="1"></el-option>
|
<el-option label="视频监控设备" value="2"></el-option>
|
<el-option label="智能道闸" value="3"></el-option>
|
</el-select>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="getTableData">搜索</el-button>
|
<el-button @click="resetFilters">重置</el-button>
|
</el-form-item>
|
</el-form>
|
|
<div class="monitor-container">
|
<div class="monitor-stats">
|
<div class="stat-card">
|
<div class="stat-title">在线设备</div>
|
<div class="stat-value online">{{ onlineCount }}</div>
|
</div>
|
<div class="stat-card">
|
<div class="stat-title">离线设备</div>
|
<div class="stat-value offline">{{ offlineCount }}</div>
|
</div>
|
<div class="stat-card">
|
<div class="stat-title">故障设备</div>
|
<div class="stat-value error">{{ errorCount }}</div>
|
</div>
|
<div class="stat-card">
|
<div class="stat-title">设备总数</div>
|
<div class="stat-value total">{{ totalCount }}</div>
|
</div>
|
</div>
|
|
<div class="table_list">
|
<PIMTable
|
rowKey="id"
|
:column="columns"
|
:tableData="dataList"
|
:page="{
|
current: pagination.currentPage,
|
size: pagination.pageSize,
|
total: pagination.total,
|
}"
|
@pagination="changePage"
|
>
|
</PIMTable>
|
</div>
|
</div>
|
|
<!-- 设备详情弹窗 -->
|
<el-dialog
|
v-model="detailVisible"
|
:title="`设备详情 - ${selectedDevice.name}`"
|
width="800px"
|
>
|
<div v-if="selectedDevice" class="device-detail">
|
<div class="detail-section">
|
<h3>基本信息</h3>
|
<el-descriptions :column="2" border>
|
<el-descriptions-item label="设备名称">{{
|
selectedDevice.name
|
}}</el-descriptions-item>
|
<el-descriptions-item label="设备类型">{{
|
typeMap[selectedDevice.type]
|
}}</el-descriptions-item>
|
<el-descriptions-item label="设备状态">{{
|
statusMap[selectedDevice.status]
|
}}</el-descriptions-item>
|
<el-descriptions-item label="设备编号">{{
|
selectedDevice.code
|
}}</el-descriptions-item>
|
<el-descriptions-item label="安装位置">{{
|
selectedDevice.location
|
}}</el-descriptions-item>
|
<el-descriptions-item label="安装时间">{{
|
selectedDevice.installTime
|
}}</el-descriptions-item>
|
<el-descriptions-item label="最后维护时间">{{
|
selectedDevice.maintainTime
|
}}</el-descriptions-item>
|
<el-descriptions-item label="责任人">{{
|
selectedDevice.responsiblePerson
|
}}</el-descriptions-item>
|
</el-descriptions>
|
</div>
|
|
<div class="detail-section">
|
<h3>实时数据</h3>
|
<div class="realtime-data">
|
<div
|
v-for="data in selectedDevice.realtimeData"
|
:key="data.name"
|
class="data-item"
|
>
|
<div class="data-name">{{ data.name }}</div>
|
<div class="data-value">{{ data.value }}{{ data.unit }}</div>
|
<div class="data-status" :class="data.status">
|
{{ data.status === "normal" ? "正常" : "异常" }}
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<div class="detail-section">
|
<h3>控制操作</h3>
|
<div class="control-buttons">
|
<el-button
|
type="primary"
|
@click="controlDevice('restart')"
|
:disabled="selectedDevice.status !== '1'"
|
>重启设备</el-button
|
>
|
<el-button
|
type="warning"
|
@click="controlDevice('reset')"
|
:disabled="selectedDevice.status !== '1'"
|
>重置设备</el-button
|
>
|
<el-button
|
type="danger"
|
@click="controlDevice('shutdown')"
|
:disabled="selectedDevice.status !== '1'"
|
>关闭设备</el-button
|
>
|
<el-button
|
type="success"
|
@click="controlDevice('update')"
|
:disabled="selectedDevice.status !== '1'"
|
>固件更新</el-button
|
>
|
</div>
|
</div>
|
</div>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button @click="detailVisible = false">关闭</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed, onMounted } from "vue";
|
import {
|
Search
|
} from "@element-plus/icons-vue";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
|
// 定义假数据
|
const mockDevices = [
|
{
|
id: 1,
|
name: "东门门禁控制器",
|
code: "DEV-20250001",
|
type: "0",
|
status: "1",
|
location: "东门入口",
|
installTime: "2025-01-15",
|
maintainTime: "2025-12-20",
|
responsiblePerson: "张三",
|
realtimeData: [
|
{ name: "温度", value: 23.5, unit: "℃", status: "normal" },
|
{ name: "湿度", value: 45, unit: "%", status: "normal" },
|
{ name: "运行时长", value: 12560, unit: "h", status: "normal" },
|
{ name: "门禁开关次数", value: 234, unit: "次/天", status: "normal" },
|
],
|
},
|
{
|
id: 2,
|
name: "西门视频监控",
|
code: "DEV-20250002",
|
type: "2",
|
status: "1",
|
location: "西门出口",
|
installTime: "2025-02-20",
|
maintainTime: "2025-11-15",
|
responsiblePerson: "李四",
|
realtimeData: [
|
{ name: "视频清晰度", value: 1080, unit: "P", status: "normal" },
|
{ name: "帧率", value: 25, unit: "fps", status: "normal" },
|
{ name: "存储空间", value: 78, unit: "%", status: "warning" },
|
{ name: "网络延迟", value: 12, unit: "ms", status: "normal" },
|
],
|
},
|
{
|
id: 3,
|
name: "南门环境监测仪",
|
code: "DEV-20250003",
|
type: "1",
|
status: "2",
|
location: "南门广场",
|
installTime: "2025-03-10",
|
maintainTime: "2025-10-05",
|
responsiblePerson: "王五",
|
realtimeData: [
|
{ name: "PM2.5", value: 85, unit: "μg/m³", status: "error" },
|
{ name: "PM10", value: 120, unit: "μg/m³", status: "error" },
|
{ name: "噪声", value: 65, unit: "dB", status: "normal" },
|
{ name: "风速", value: 1.2, unit: "m/s", status: "normal" },
|
],
|
},
|
{
|
id: 4,
|
name: "北门智能道闸",
|
code: "DEV-20250004",
|
type: "3",
|
status: "0",
|
location: "北门入口",
|
installTime: "2025-04-05",
|
maintainTime: "2025-09-20",
|
responsiblePerson: "赵六",
|
realtimeData: [
|
{ name: "开关次数", value: 156, unit: "次/天", status: "normal" },
|
{ name: "运行电压", value: 220, unit: "V", status: "normal" },
|
{ name: "运行电流", value: 5.2, unit: "A", status: "normal" },
|
{ name: "故障代码", value: "E001", unit: "", status: "error" },
|
],
|
},
|
{
|
id: 5,
|
name: "中控室服务器",
|
code: "DEV-20250005",
|
type: "1",
|
status: "1",
|
location: "中控室",
|
installTime: "2025-05-15",
|
maintainTime: "2025-12-01",
|
responsiblePerson: "孙七",
|
realtimeData: [
|
{ name: "CPU使用率", value: 45, unit: "%", status: "normal" },
|
{ name: "内存使用率", value: 68, unit: "%", status: "warning" },
|
{ name: "磁盘使用率", value: 82, unit: "%", status: "warning" },
|
{ name: "网络流量", value: 125, unit: "Mbps", status: "normal" },
|
],
|
},
|
];
|
|
// 响应式数据
|
const filters = reactive({
|
name: "",
|
status: "",
|
type: "",
|
});
|
|
const dataList = ref([]);
|
const pagination = reactive({
|
currentPage: 1,
|
pageSize: 10,
|
total: 0,
|
});
|
|
const detailVisible = ref(false);
|
const selectedDevice = ref({});
|
|
// 状态映射
|
const statusMap = {
|
0: '<el-tag type="warning">离线</el-tag>',
|
1: '<el-tag type="success">在线</el-tag>',
|
2: '<el-tag type="danger">故障</el-tag>',
|
};
|
|
// 类型映射
|
const typeMap = {
|
0: "门禁设备",
|
1: "环境监测设备",
|
2: "视频监控设备",
|
3: "智能道闸",
|
};
|
|
// 设备数量统计
|
const onlineCount = computed(() => {
|
return mockDevices.filter((device) => device.status === "1").length;
|
});
|
|
const offlineCount = computed(() => {
|
return mockDevices.filter((device) => device.status === "0").length;
|
});
|
|
const errorCount = computed(() => {
|
return mockDevices.filter((device) => device.status === "2").length;
|
});
|
|
const totalCount = computed(() => {
|
return mockDevices.length;
|
});
|
|
// 表格列配置
|
const columns = [
|
{
|
label: "设备名称",
|
align: "center",
|
prop: "name",
|
},
|
{
|
label: "设备编号",
|
align: "center",
|
prop: "code",
|
},
|
{
|
label: "设备类型",
|
align: "center",
|
prop: "type",
|
formatter: (row) => {
|
return typeMap[row.type];
|
},
|
},
|
{
|
label: "设备状态",
|
align: "center",
|
prop: "status",
|
formatter: (row) => {
|
return statusMap[row.status];
|
},
|
},
|
{
|
label: "安装位置",
|
align: "center",
|
prop: "location",
|
},
|
{
|
label: "责任人",
|
align: "center",
|
prop: "responsiblePerson",
|
},
|
{
|
label: "最后维护时间",
|
align: "center",
|
prop: "maintainTime",
|
},
|
{
|
dataType: "action",
|
label: "操作",
|
align: "center",
|
fixed: "right",
|
width: 140,
|
operation: [
|
{
|
name: "查看详情",
|
type: "text",
|
clickFun: (row) => {
|
viewDetail(row);
|
},
|
},
|
{
|
name: "实时监控",
|
type: "text",
|
clickFun: (row) => {
|
realtimeMonitor(row);
|
},
|
visible: (row) => {
|
return row.status === "1";
|
},
|
},
|
{
|
name: "重启设备",
|
type: "text",
|
clickFun: (row) => {
|
controlDevice("restart", row);
|
},
|
visible: (row) => {
|
return row.status === "1";
|
},
|
},
|
],
|
},
|
];
|
|
// 过滤后的数据
|
const filteredData = computed(() => {
|
return mockDevices.filter((item) => {
|
const nameMatch = !filters.name || item.name.includes(filters.name);
|
const statusMatch = !filters.status || item.status === filters.status;
|
const typeMatch = !filters.type || item.type === filters.type;
|
return nameMatch && statusMatch && typeMatch;
|
});
|
});
|
|
// 获取表格数据
|
const getTableData = () => {
|
pagination.total = filteredData.value.length;
|
const start = (pagination.currentPage - 1) * pagination.pageSize;
|
const end = start + pagination.pageSize;
|
dataList.value = filteredData.value.slice(start, end);
|
};
|
|
// 重置过滤器
|
const resetFilters = () => {
|
filters.name = "";
|
filters.status = "";
|
filters.type = "";
|
pagination.currentPage = 1;
|
getTableData();
|
};
|
|
// 分页变化
|
const changePage = ({ page, limit }) => {
|
pagination.currentPage = page;
|
pagination.pageSize = limit;
|
getTableData();
|
};
|
|
// 查看设备详情
|
const viewDetail = (row) => {
|
selectedDevice.value = row;
|
detailVisible.value = true;
|
};
|
|
// 实时监控
|
const realtimeMonitor = (row) => {
|
ElMessage.info(`正在跳转到${row.name}的实时监控页面...`);
|
// 这里可以跳转到实时监控页面或打开监控窗口
|
};
|
|
// 设备控制
|
const controlDevice = (action, row = selectedDevice.value) => {
|
const actionMap = {
|
restart: "重启",
|
reset: "重置",
|
shutdown: "关闭",
|
update: "固件更新",
|
};
|
|
ElMessageBox.confirm(
|
`确定要对设备 ${row.name} 执行${actionMap[action]}操作吗?`,
|
"提示",
|
{
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning",
|
}
|
)
|
.then(() => {
|
ElMessage.success(`设备${actionMap[action]}操作已发送`);
|
// 模拟设备响应
|
setTimeout(() => {
|
ElMessage.success(`设备${actionMap[action]}操作执行成功`);
|
}, 1000);
|
})
|
.catch(() => {});
|
};
|
|
// 定时更新设备状态(模拟实时变化)
|
const updateDeviceStatus = () => {
|
// 随机更新一些设备的状态和实时数据
|
mockDevices.forEach((device) => {
|
if (Math.random() > 0.8) {
|
// 80%概率不更新
|
return;
|
}
|
|
// 随机更新状态
|
const statuses = ["0", "1", "2"];
|
device.status = statuses[Math.floor(Math.random() * statuses.length)];
|
|
// 更新实时数据
|
device.realtimeData.forEach((data) => {
|
if (data.name === "温度") {
|
data.value = (20 + Math.random() * 10).toFixed(1);
|
data.status = data.value > 30 ? "error" : "normal";
|
} else if (data.name === "湿度") {
|
data.value = Math.floor(30 + Math.random() * 50);
|
data.status = data.value > 80 ? "error" : "normal";
|
} else if (data.name === "CPU使用率") {
|
data.value = Math.floor(20 + Math.random() * 60);
|
data.status =
|
data.value > 80 ? "error" : data.value > 60 ? "warning" : "normal";
|
}
|
});
|
});
|
|
// 刷新表格数据
|
getTableData();
|
};
|
|
// 初始化数据
|
onMounted(() => {
|
getTableData();
|
// 每5秒更新一次设备状态
|
const interval = setInterval(updateDeviceStatus, 5000);
|
|
// 清理定时器
|
return () => clearInterval(interval);
|
});
|
</script>
|
|
<style lang="scss" scoped>
|
.table_list {
|
margin-top: unset;
|
}
|
|
.monitor-container {
|
.monitor-stats {
|
display: flex;
|
gap: 20px;
|
margin-bottom: 20px;
|
|
.stat-card {
|
flex: 1;
|
background: #fff;
|
padding: 20px;
|
border-radius: 8px;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
text-align: center;
|
|
.stat-title {
|
font-size: 14px;
|
color: #606266;
|
margin-bottom: 10px;
|
}
|
|
.stat-value {
|
font-size: 32px;
|
font-weight: bold;
|
|
&.online {
|
color: #67c23a;
|
}
|
|
&.offline {
|
color: #e6a23c;
|
}
|
|
&.error {
|
color: #f56c6c;
|
}
|
|
&.total {
|
color: #409eff;
|
}
|
}
|
}
|
}
|
}
|
|
.device-detail {
|
.detail-section {
|
margin-bottom: 20px;
|
|
h3 {
|
font-size: 16px;
|
font-weight: bold;
|
margin-bottom: 10px;
|
color: #303133;
|
}
|
}
|
|
.realtime-data {
|
display: grid;
|
grid-template-columns: repeat(2, 1fr);
|
gap: 15px;
|
|
.data-item {
|
background: #f5f7fa;
|
padding: 15px;
|
border-radius: 8px;
|
|
.data-name {
|
font-size: 14px;
|
color: #606266;
|
margin-bottom: 5px;
|
}
|
|
.data-value {
|
font-size: 24px;
|
font-weight: bold;
|
color: #303133;
|
margin-bottom: 5px;
|
}
|
|
.data-status {
|
font-size: 12px;
|
padding: 2px 8px;
|
border-radius: 10px;
|
|
&.normal {
|
background: #f0f9eb;
|
color: #67c23a;
|
}
|
|
&.warning {
|
background: #fdf6ec;
|
color: #e6a23c;
|
}
|
|
&.error {
|
background: #fef0f0;
|
color: #f56c6c;
|
}
|
}
|
}
|
}
|
|
.control-buttons {
|
display: flex;
|
gap: 10px;
|
margin-top: 10px;
|
}
|
}
|
</style>
|