<!DOCTYPE html>
|
<html lang="zh-CN">
|
<head>
|
<meta charset="UTF-8">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<title>机器人数据监控</title>
|
<!-- 引入 layui-vue 样式 -->
|
<link href="https://cdn.jsdelivr.net/npm/layui-vue@2.1.0/dist/index.css" rel="stylesheet">
|
<!-- 引入 Vue 3 -->
|
<script src="https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.min.js"></script>
|
<!-- 引入 layui-vue -->
|
<script src="https://cdn.jsdelivr.net/npm/layui-vue@2.1.0/dist/index.js"></script>
|
<!-- 引入 Chart.js -->
|
<script src="/static/js/chart.umd.min.js"></script>
|
<!-- 引入 Font Awesome -->
|
<link href="/static/css/font-awesome.min.css" rel="stylesheet">
|
<style>
|
body {
|
font-family: 'Inter', sans-serif;
|
background-color: #f5f7fa;
|
}
|
|
.layui-card {
|
margin-bottom: 16px;
|
}
|
|
.layui-card-header {
|
font-weight: 600;
|
}
|
|
.status-online {
|
color: #10b981;
|
}
|
|
.status-offline {
|
color: #ef4444;
|
}
|
|
.chart-container {
|
height: 300px;
|
}
|
|
.realtime-container {
|
height: 160px;
|
overflow-y: auto;
|
}
|
</style>
|
</head>
|
<body>
|
<div id="app">
|
<!-- 顶部导航栏 -->
|
<lay-header height="60px" bg-color="#fff" shadow>
|
<template #left>
|
<div class="flex items-center space-x-2">
|
<i class="fa fa-android text-2xl" style="color: #3b82f6"></i>
|
<h1 class="text-xl font-bold" style="color: #1e293b">机器人数据监控</h1>
|
</div>
|
</template>
|
<template #right>
|
<div class="flex items-center space-x-4">
|
<lay-input placeholder="搜索机器人..." prefix-icon="search" style="width: 200px"></lay-input>
|
<lay-button type="primary" @click="refreshData">
|
<i class="fa fa-refresh mr-1"></i> 刷新
|
</lay-button>
|
</div>
|
</template>
|
</lay-header>
|
|
<!-- 主内容区 -->
|
<lay-container style="padding: 20px">
|
<!-- 状态概览 -->
|
<lay-row :gutter="16">
|
<lay-col :span="6">
|
<lay-card shadow>
|
<div class="flex items-center justify-between">
|
<div>
|
<p style="color: #64748b; font-size: 14px">总机器人数</p>
|
<h3 style="font-size: 24px; font-weight: bold; color: #1e293b">24</h3>
|
</div>
|
<div style="width: 48px; height: 48px; border-radius: 50%; background-color: #dbeafe; display: flex; align-items: center; justify-content: center">
|
<i class="fa fa-microchip text-xl" style="color: #3b82f6"></i>
|
</div>
|
</div>
|
</lay-card>
|
</lay-col>
|
<lay-col :span="6">
|
<lay-card shadow>
|
<div class="flex items-center justify-between">
|
<div>
|
<p style="color: #64748b; font-size: 14px">在线机器人</p>
|
<h3 style="font-size: 24px; font-weight: bold; color: #10b981">18</h3>
|
</div>
|
<div style="width: 48px; height: 48px; border-radius: 50%; background-color: #d1fae5; display: flex; align-items: center; justify-content: center">
|
<i class="fa fa-check-circle text-xl" style="color: #10b981"></i>
|
</div>
|
</div>
|
</lay-card>
|
</lay-col>
|
<lay-col :span="6">
|
<lay-card shadow>
|
<div class="flex items-center justify-between">
|
<div>
|
<p style="color: #64748b; font-size: 14px">离线机器人</p>
|
<h3 style="font-size: 24px; font-weight: bold; color: #ef4444">6</h3>
|
</div>
|
<div style="width: 48px; height: 48px; border-radius: 50%; background-color: #fee2e2; display: flex; align-items: center; justify-content: center">
|
<i class="fa fa-exclamation-circle text-xl" style="color: #ef4444"></i>
|
</div>
|
</div>
|
</lay-card>
|
</lay-col>
|
<lay-col :span="6">
|
<lay-card shadow>
|
<div class="flex items-center justify-between">
|
<div>
|
<p style="color: #64748b; font-size: 14px">今日数据量</p>
|
<h3 style="font-size: 24px; font-weight: bold; color: #f59e0b">1.2k</h3>
|
</div>
|
<div style="width: 48px; height: 48px; border-radius: 50%; background-color: #fef3c7; display: flex; align-items: center; justify-content: center">
|
<i class="fa fa-database text-xl" style="color: #f59e0b"></i>
|
</div>
|
</div>
|
</lay-card>
|
</lay-col>
|
</lay-row>
|
|
<!-- 数据图表 -->
|
<lay-row :gutter="16" style="margin-top: 16px">
|
<lay-col :span="12">
|
<lay-card shadow>
|
<template #header>
|
<div class="flex justify-between items-center">
|
<h2 style="font-size: 16px; font-weight: 600; color: #1e293b">上行数据趋势</h2>
|
<div class="flex space-x-2">
|
<lay-button size="sm" type="primary">小时</lay-button>
|
<lay-button size="sm">天</lay-button>
|
<lay-button size="sm">周</lay-button>
|
</div>
|
</div>
|
</template>
|
<div class="chart-container">
|
<canvas ref="upDataChart"></canvas>
|
</div>
|
</lay-card>
|
</lay-col>
|
<lay-col :span="12">
|
<lay-card shadow>
|
<template #header>
|
<div class="flex justify-between items-center">
|
<h2 style="font-size: 16px; font-weight: 600; color: #1e293b">下行数据趋势</h2>
|
<div class="flex space-x-2">
|
<lay-button size="sm" type="primary">小时</lay-button>
|
<lay-button size="sm">天</lay-button>
|
<lay-button size="sm">周</lay-button>
|
</div>
|
</div>
|
</template>
|
<div class="chart-container">
|
<canvas ref="downDataChart"></canvas>
|
</div>
|
</lay-card>
|
</lay-col>
|
</lay-row>
|
|
<!-- 设备数据表格 -->
|
<lay-card shadow style="margin-top: 16px">
|
<template #header>
|
<div class="flex justify-between items-center">
|
<h2 style="font-size: 16px; font-weight: 600; color: #1e293b">机器人数据列表</h2>
|
<div class="flex space-x-2">
|
<lay-button size="sm">
|
<i class="fa fa-filter mr-1"></i> 筛选
|
</lay-button>
|
<lay-button size="sm">
|
<i class="fa fa-download mr-1"></i> 导出
|
</lay-button>
|
</div>
|
</div>
|
</template>
|
<lay-table :data="devices" :height="400">
|
<lay-table-column prop="id" label="设备ID" width="120"></lay-table-column>
|
<lay-table-column prop="name" label="设备名称" width="150"></lay-table-column>
|
<lay-table-column prop="status" label="状态" width="100">
|
<template #default="{ row }">
|
<lay-badge v-if="row.status === 'online'" type="success">在线</lay-badge>
|
<lay-badge v-else type="danger">离线</lay-badge>
|
</template>
|
</lay-table-column>
|
<lay-table-column prop="upData" label="上行数据" width="120"></lay-table-column>
|
<lay-table-column prop="downData" label="下行数据" width="120"></lay-table-column>
|
<lay-table-column prop="lastComm" label="最后通信" width="150"></lay-table-column>
|
<lay-table-column label="操作" width="150">
|
<template #default="{ row }">
|
<lay-button size="sm" type="primary" style="margin-right: 8px">
|
<i class="fa fa-eye"></i>
|
</lay-button>
|
<lay-button size="sm" type="warning" style="margin-right: 8px">
|
<i class="fa fa-edit"></i>
|
</lay-button>
|
<lay-button size="sm" type="danger">
|
<i class="fa fa-trash"></i>
|
</lay-button>
|
</template>
|
</lay-table-column>
|
</lay-table>
|
<div class="flex justify-between items-center mt-4">
|
<p style="color: #64748b; font-size: 14px">显示 1-10 条,共 24 条</p>
|
<lay-pagination
|
v-model:current="currentPage"
|
v-model:limit="pageSize"
|
:total="total"
|
:limits="[10, 20, 50, 100]"
|
layout="prev, pager, next, jumper, sizes, total"
|
></lay-pagination>
|
</div>
|
</lay-card>
|
|
<!-- 实时数据更新 -->
|
<lay-card shadow style="margin-top: 16px">
|
<template #header>
|
<h2 style="font-size: 16px; font-weight: 600; color: #1e293b">实时数据更新</h2>
|
</template>
|
<div class="realtime-container p-2 border border-gray-200 rounded-lg">
|
<div v-for="(item, index) in realtimeData" :key="index" class="py-1 border-b border-gray-100">
|
<span style="color: #64748b; font-size: 12px">{{ item.timestamp }}</span>
|
<span style="color: #1e293b; margin-left: 10px">{{ item.message }}</span>
|
</div>
|
</div>
|
</lay-card>
|
</lay-container>
|
|
<!-- 页脚 -->
|
<lay-footer height="60px" bg-color="#fff" shadow>
|
<div class="text-center" style="color: #64748b; font-size: 14px">
|
© 2026 机器人数据监控系统 | 版本 1.0.0
|
</div>
|
</lay-footer>
|
</div>
|
|
<script>
|
const {createApp, ref, onMounted} = Vue;
|
const app = createApp({
|
components: {
|
LayHeader: layui.LayHeader,
|
LayContainer: layui.LayContainer,
|
LayRow: layui.LayRow,
|
LayCol: layui.LayCol,
|
LayCard: layui.LayCard,
|
LayInput: layui.LayInput,
|
LayButton: layui.LayButton,
|
LayTable: layui.LayTable,
|
LayTableColumn: layui.LayTableColumn,
|
LayBadge: layui.LayBadge,
|
LayPagination: layui.LayPagination,
|
LayFooter: layui.LayFooter
|
},
|
setup() {
|
// 模拟机器人数据
|
const devices = ref([
|
{
|
id: 'ROB-001',
|
name: '配送机器人1号',
|
status: 'online',
|
upData: '2.4KB',
|
downData: '0.8KB',
|
lastComm: '2分钟前'
|
},
|
{
|
id: 'ROB-002',
|
name: '配送机器人2号',
|
status: 'online',
|
upData: '1.8KB',
|
downData: '0.5KB',
|
lastComm: '5分钟前'
|
},
|
{
|
id: 'ROB-003',
|
name: '巡检机器人1号',
|
status: 'offline',
|
upData: '0KB',
|
downData: '0KB',
|
lastComm: '2小时前'
|
},
|
{
|
id: 'ROB-004',
|
name: '配送机器人3号',
|
status: 'online',
|
upData: '3.2KB',
|
downData: '1.2KB',
|
lastComm: '1分钟前'
|
},
|
{
|
id: 'ROB-005',
|
name: '巡检机器人2号',
|
status: 'online',
|
upData: '1.5KB',
|
downData: '0.6KB',
|
lastComm: '3分钟前'
|
},
|
{
|
id: 'ROB-006',
|
name: '配送机器人4号',
|
status: 'offline',
|
upData: '0KB',
|
downData: '0KB',
|
lastComm: '5小时前'
|
},
|
{
|
id: 'ROB-007',
|
name: '巡检机器人3号',
|
status: 'online',
|
upData: '2.1KB',
|
downData: '0.9KB',
|
lastComm: '4分钟前'
|
},
|
{
|
id: 'ROB-008',
|
name: '配送机器人5号',
|
status: 'online',
|
upData: '2.8KB',
|
downData: '1.1KB',
|
lastComm: '2分钟前'
|
},
|
{
|
id: 'ROB-009',
|
name: '巡检机器人4号',
|
status: 'offline',
|
upData: '0KB',
|
downData: '0KB',
|
lastComm: '1天前'
|
},
|
{
|
id: 'ROB-010',
|
name: '配送机器人6号',
|
status: 'online',
|
upData: '1.9KB',
|
downData: '0.7KB',
|
lastComm: '6分钟前'
|
}
|
]);
|
|
// 分页数据
|
const currentPage = ref(1);
|
const pageSize = ref(10);
|
const total = ref(24);
|
|
// 实时数据
|
const realtimeData = ref([]);
|
|
// 图表引用
|
const upDataChart = ref(null);
|
const downDataChart = ref(null);
|
|
// 刷新数据
|
const refreshData = () => {
|
console.log('刷新数据');
|
// 这里可以添加实际的刷新逻辑
|
};
|
|
// 初始化图表
|
const initCharts = () => {
|
// 上行数据图表
|
const upCtx = upDataChart.value.getContext('2d');
|
new Chart(upCtx, {
|
type: 'line',
|
data: {
|
labels: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00'],
|
datasets: [{
|
label: '上行数据 (KB)',
|
data: [12, 19, 15, 25, 22, 30, 28, 35],
|
borderColor: '#3b82f6',
|
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
tension: 0.4,
|
fill: true
|
}]
|
},
|
options: {
|
responsive: true,
|
maintainAspectRatio: false,
|
plugins: {
|
legend: {
|
display: false
|
}
|
},
|
scales: {
|
y: {
|
beginAtZero: true
|
}
|
}
|
}
|
});
|
|
// 下行数据图表
|
const downCtx = downDataChart.value.getContext('2d');
|
new Chart(downCtx, {
|
type: 'line',
|
data: {
|
labels: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00'],
|
datasets: [{
|
label: '下行数据 (KB)',
|
data: [5, 8, 6, 12, 10, 15, 13, 18],
|
borderColor: '#10b981',
|
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
tension: 0.4,
|
fill: true
|
}]
|
},
|
options: {
|
responsive: true,
|
maintainAspectRatio: false,
|
plugins: {
|
legend: {
|
display: false
|
}
|
},
|
scales: {
|
y: {
|
beginAtZero: true
|
}
|
}
|
}
|
});
|
};
|
|
// 模拟实时数据更新
|
const simulateRealtimeData = () => {
|
const messages = [
|
'ROB-001 配送机器人1号: 运行中,位置: A1区',
|
'ROB-002 配送机器人2号: 待机中,位置: B2区',
|
'ROB-004 配送机器人3号: 充电中,电量: 85%',
|
'ROB-005 巡检机器人2号: 巡检中,已完成3/5任务',
|
'ROB-007 巡检机器人3号: 待机中,位置: C3区',
|
'ROB-008 配送机器人5号: 运行中,位置: D4区'
|
];
|
|
setInterval(() => {
|
const message = messages[Math.floor(Math.random() * messages.length)];
|
const timestamp = new Date().toLocaleTimeString();
|
realtimeData.value.unshift({timestamp, message});
|
// 限制显示条数
|
if (realtimeData.value.length > 20) {
|
realtimeData.value.pop();
|
}
|
}, 2000);
|
};
|
|
// 页面加载完成后初始化
|
onMounted(() => {
|
initCharts();
|
simulateRealtimeData();
|
});
|
|
return {
|
devices,
|
currentPage,
|
pageSize,
|
total,
|
realtimeData,
|
upDataChart,
|
downDataChart,
|
refreshData
|
};
|
}
|
});
|
app.mount('#app');
|
</script>
|
</body>
|
</html>
|