1167 lines
38 KiB
HTML
1167 lines
38 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>无人机智能分析监控平台</title>
|
||
<!-- 引入ECharts -->
|
||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
||
<!-- 引入ECharts主题 -->
|
||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/extension/dataTool.min.js"></script>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
font-family: 'Microsoft YaHei', 'Segoe UI', Arial, sans-serif;
|
||
}
|
||
|
||
:root {
|
||
--primary-color: #1890ff;
|
||
--primary-light: #40a9ff;
|
||
--primary-lighter: #69c0ff;
|
||
--primary-lightest: #91d5ff;
|
||
--bg-color: #f0f8ff;
|
||
--text-dark: #1d3557;
|
||
--text-medium: #457b9d;
|
||
--success-color: #52c41a;
|
||
--warning-color: #faad14;
|
||
--error-color: #ff4d4f;
|
||
--card-bg: #ffffff;
|
||
--shadow-color: rgba(24, 144, 255, 0.1);
|
||
--border-color: #e6f7ff;
|
||
}
|
||
|
||
body {
|
||
background-color: var(--bg-color);
|
||
color: var(--text-dark);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* 顶部导航栏 */
|
||
.header {
|
||
background: linear-gradient(135deg, var(--primary-color), var(--primary-light));
|
||
color: white;
|
||
padding: 0 30px;
|
||
height: 70px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.logo {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.logo-icon {
|
||
font-size: 32px;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.system-status {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.status-indicator {
|
||
width: 12px;
|
||
height: 12px;
|
||
border-radius: 50%;
|
||
background-color: var(--success-color);
|
||
margin-right: 8px;
|
||
box-shadow: 0 0 8px var(--success-color);
|
||
}
|
||
|
||
/* 主体布局 */
|
||
.main-container {
|
||
display: flex;
|
||
margin-top: 70px;
|
||
min-height: calc(100vh - 70px);
|
||
}
|
||
|
||
/* 侧边导航栏 */
|
||
.sidebar {
|
||
width: 220px;
|
||
background-color: var(--card-bg);
|
||
border-right: 1px solid var(--border-color);
|
||
padding: 20px 0;
|
||
box-shadow: 2px 0 8px var(--shadow-color);
|
||
position: fixed;
|
||
top: 70px;
|
||
bottom: 0;
|
||
left: 0;
|
||
z-index: 999;
|
||
}
|
||
|
||
.nav-item {
|
||
padding: 16px 30px;
|
||
display: flex;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
color: var(--text-medium);
|
||
border-left: 4px solid transparent;
|
||
}
|
||
|
||
.nav-item:hover {
|
||
background-color: var(--primary-lightest);
|
||
color: var(--primary-color);
|
||
}
|
||
|
||
.nav-item.active {
|
||
background-color: #e6f7ff;
|
||
color: var(--primary-color);
|
||
border-left-color: var(--primary-color);
|
||
font-weight: bold;
|
||
}
|
||
|
||
.nav-icon {
|
||
margin-right: 12px;
|
||
font-size: 20px;
|
||
}
|
||
|
||
/* 主内容区 */
|
||
.content {
|
||
flex: 1;
|
||
padding: 25px 30px 30px 250px;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* 统计卡片 */
|
||
.stats-container {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 20px;
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.stat-card {
|
||
background: linear-gradient(135deg, var(--primary-color), var(--primary-light));
|
||
color: white;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
box-shadow: 0 6px 16px var(--shadow-color);
|
||
display: flex;
|
||
align-items: center;
|
||
transition: transform 0.3s, box-shadow 0.3s;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.stat-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 12px 24px rgba(24, 144, 255, 0.3);
|
||
}
|
||
|
||
.stat-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
width: 80px;
|
||
height: 80px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 50%;
|
||
transform: translate(20px, -20px);
|
||
}
|
||
|
||
.stat-icon {
|
||
font-size: 36px;
|
||
margin-right: 20px;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.stat-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.stat-title {
|
||
font-size: 14px;
|
||
opacity: 0.9;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 28px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.stat-change {
|
||
font-size: 12px;
|
||
opacity: 0.9;
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 5px;
|
||
}
|
||
|
||
/* 图表卡片 */
|
||
.chart-container {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 20px;
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.chart-card {
|
||
background-color: var(--card-bg);
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
box-shadow: 0 4px 12px var(--shadow-color);
|
||
transition: transform 0.3s;
|
||
}
|
||
|
||
.chart-card:hover {
|
||
transform: translateY(-3px);
|
||
}
|
||
|
||
.chart-card.full-width {
|
||
grid-column: span 2;
|
||
}
|
||
|
||
.chart-title {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
margin-bottom: 20px;
|
||
color: var(--text-dark);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.chart-title-icon {
|
||
color: var(--primary-color);
|
||
font-size: 20px;
|
||
}
|
||
|
||
.chart-wrapper {
|
||
height: 300px;
|
||
width: 100%;
|
||
}
|
||
|
||
/* 任务列表 */
|
||
.tasks-container {
|
||
background-color: var(--card-bg);
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
box-shadow: 0 4px 12px var(--shadow-color);
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.table-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.task-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
}
|
||
|
||
.task-table th {
|
||
text-align: left;
|
||
padding: 12px 15px;
|
||
border-bottom: 2px solid var(--border-color);
|
||
color: var(--text-medium);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.task-table td {
|
||
padding: 15px;
|
||
border-bottom: 1px solid var(--border-color);
|
||
}
|
||
|
||
.task-table tr:hover {
|
||
background-color: #f9fdff;
|
||
}
|
||
|
||
/* 状态标签 */
|
||
.status-badge {
|
||
padding: 4px 12px;
|
||
border-radius: 20px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.status-running {
|
||
background-color: rgba(82, 196, 26, 0.1);
|
||
color: var(--success-color);
|
||
}
|
||
|
||
.status-paused {
|
||
background-color: rgba(250, 173, 20, 0.1);
|
||
color: var(--warning-color);
|
||
}
|
||
|
||
.status-error {
|
||
background-color: rgba(255, 77, 79, 0.1);
|
||
color: var(--error-color);
|
||
}
|
||
|
||
.status-stopped {
|
||
background-color: rgba(0, 0, 0, 0.05);
|
||
color: #666;
|
||
}
|
||
|
||
/* 操作按钮 */
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
|
||
.btn {
|
||
padding: 6px 12px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.btn-primary {
|
||
background-color: var(--primary-color);
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background-color: var(--primary-light);
|
||
}
|
||
|
||
.btn-warning {
|
||
background-color: var(--warning-color);
|
||
color: white;
|
||
}
|
||
|
||
.btn-warning:hover {
|
||
background-color: #ffc53d;
|
||
}
|
||
|
||
.btn-danger {
|
||
background-color: var(--error-color);
|
||
color: white;
|
||
}
|
||
|
||
.btn-danger:hover {
|
||
background-color: #ff7875;
|
||
}
|
||
|
||
/* 资源监控卡片 */
|
||
.resource-cards {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 20px;
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.resource-card {
|
||
background-color: var(--card-bg);
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
box-shadow: 0 4px 12px var(--shadow-color);
|
||
transition: transform 0.3s;
|
||
}
|
||
|
||
.resource-card:hover {
|
||
transform: translateY(-3px);
|
||
}
|
||
|
||
.resource-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
margin-bottom: 15px;
|
||
color: var(--text-dark);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.resource-value {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.progress-bar {
|
||
height: 8px;
|
||
background-color: #f0f0f0;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
margin-top: 15px;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
border-radius: 4px;
|
||
transition: width 0.5s;
|
||
}
|
||
|
||
.progress-cpu {
|
||
background: linear-gradient(90deg, #52c41a, #a0d911);
|
||
}
|
||
|
||
.progress-memory {
|
||
background: linear-gradient(90deg, #1890ff, #40a9ff);
|
||
}
|
||
|
||
.progress-gpu {
|
||
background: linear-gradient(90deg, #722ed1, #eb2f96);
|
||
}
|
||
|
||
.progress-network {
|
||
background: linear-gradient(90deg, #fa8c16, #faad14);
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 1200px) {
|
||
.stats-container, .resource-cards {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
|
||
.chart-container {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.chart-card.full-width {
|
||
grid-column: span 1;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.sidebar {
|
||
width: 60px;
|
||
padding: 20px 0;
|
||
}
|
||
|
||
.nav-text {
|
||
display: none;
|
||
}
|
||
|
||
.nav-icon {
|
||
margin-right: 0;
|
||
font-size: 24px;
|
||
}
|
||
|
||
.content {
|
||
padding-left: 80px;
|
||
}
|
||
|
||
.stats-container, .resource-cards {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.chart-card {
|
||
padding: 15px;
|
||
}
|
||
|
||
.chart-wrapper {
|
||
height: 250px;
|
||
}
|
||
}
|
||
|
||
/* 动画效果 */
|
||
@keyframes pulse {
|
||
0% { opacity: 0.8; }
|
||
50% { opacity: 1; }
|
||
100% { opacity: 0.8; }
|
||
}
|
||
|
||
.pulse {
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
/* 滚动条美化 */
|
||
::-webkit-scrollbar {
|
||
width: 8px;
|
||
height: 8px;
|
||
}
|
||
|
||
::-webkit-scrollbar-track {
|
||
background: #f1f1f1;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb {
|
||
background: var(--primary-light);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb:hover {
|
||
background: var(--primary-color);
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- 顶部导航栏 -->
|
||
<div class="header">
|
||
<div class="logo">
|
||
<span class="logo-icon">✈️</span>
|
||
<span>无人机智能分析监控平台</span>
|
||
</div>
|
||
<div class="system-status">
|
||
<span class="status-indicator pulse"></span>
|
||
<span>系统运行正常 | 最后更新: <span id="last-update">刚刚</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="main-container">
|
||
<!-- 侧边导航栏 -->
|
||
<div class="sidebar">
|
||
<div class="nav-item active">
|
||
<span class="nav-icon">📊</span>
|
||
<span class="nav-text">监控仪表盘</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<span class="nav-icon">🚀</span>
|
||
<span class="nav-text">任务管理</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<span class="nav-icon">🧠</span>
|
||
<span class="nav-text">算法管理</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<span class="nav-icon">📈</span>
|
||
<span class="nav-text">性能分析</span>
|
||
</div>
|
||
<div class="nav-item">
|
||
<span class="nav-icon">⚙️</span>
|
||
<span class="nav-text">系统设置</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 主内容区 -->
|
||
<div class="content">
|
||
<!-- 统计卡片 -->
|
||
<div class="stats-container">
|
||
<div class="stat-card">
|
||
<span class="stat-icon">🚀</span>
|
||
<div class="stat-info">
|
||
<div class="stat-title">运行任务</div>
|
||
<div class="stat-value" id="running-tasks">12</div>
|
||
<div class="stat-change">↑ 较昨日增加2个</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<span class="stat-icon">🧠</span>
|
||
<div class="stat-info">
|
||
<div class="stat-title">算法实例</div>
|
||
<div class="stat-value" id="algorithm-instances">8</div>
|
||
<div class="stat-change">→ 与昨日持平</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<span class="stat-icon">✈️</span>
|
||
<div class="stat-info">
|
||
<div class="stat-title">在线无人机</div>
|
||
<div class="stat-value" id="online-drones">5</div>
|
||
<div class="stat-change">↑ 1架刚刚上线</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<span class="stat-icon">📊</span>
|
||
<div class="stat-info">
|
||
<div class="stat-title">系统资源占用</div>
|
||
<div class="stat-value" id="system-resource">76%</div>
|
||
<div class="stat-change">↓ 较昨日降低4%</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 图表区域 -->
|
||
<div class="chart-container">
|
||
<div class="chart-card">
|
||
<div class="chart-title">
|
||
<span>任务性能监控</span>
|
||
<span class="chart-title-icon">📈</span>
|
||
</div>
|
||
<div class="chart-wrapper" id="performance-chart"></div>
|
||
</div>
|
||
<div class="chart-card">
|
||
<div class="chart-title">
|
||
<span>资源使用情况</span>
|
||
<span class="chart-title-icon">💻</span>
|
||
</div>
|
||
<div class="chart-wrapper" id="resource-chart"></div>
|
||
</div>
|
||
<div class="chart-card full-width">
|
||
<div class="chart-title">
|
||
<span>任务状态分布</span>
|
||
<span class="chart-title-icon">📊</span>
|
||
</div>
|
||
<div class="chart-wrapper" id="status-chart"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 资源监控卡片 -->
|
||
<div class="resource-cards">
|
||
<div class="resource-card">
|
||
<div class="resource-title">
|
||
<span>CPU使用率</span>
|
||
<span>🔧</span>
|
||
</div>
|
||
<div class="resource-value" id="cpu-usage">68%</div>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill progress-cpu" id="cpu-progress" style="width: 68%"></div>
|
||
</div>
|
||
</div>
|
||
<div class="resource-card">
|
||
<div class="resource-title">
|
||
<span>内存使用率</span>
|
||
<span>💾</span>
|
||
</div>
|
||
<div class="resource-value" id="memory-usage">76%</div>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill progress-memory" id="memory-progress" style="width: 76%"></div>
|
||
</div>
|
||
</div>
|
||
<div class="resource-card">
|
||
<div class="resource-title">
|
||
<span>GPU使用率</span>
|
||
<span>🎮</span>
|
||
</div>
|
||
<div class="resource-value" id="gpu-usage">82%</div>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill progress-gpu" id="gpu-progress" style="width: 82%"></div>
|
||
</div>
|
||
</div>
|
||
<div class="resource-card">
|
||
<div class="resource-title">
|
||
<span>网络带宽</span>
|
||
<span>🌐</span>
|
||
</div>
|
||
<div class="resource-value" id="network-usage">45%</div>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill progress-network" id="network-progress" style="width: 45%"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务列表 -->
|
||
<div class="tasks-container">
|
||
<div class="table-header">
|
||
<h3>当前运行任务</h3>
|
||
<div>
|
||
<button class="btn btn-primary" onclick="refreshTasks()">刷新</button>
|
||
<button class="btn btn-primary" onclick="createTask()">新建任务</button>
|
||
</div>
|
||
</div>
|
||
<table class="task-table">
|
||
<thead>
|
||
<tr>
|
||
<th>任务ID</th>
|
||
<th>任务名称</th>
|
||
<th>无人机编号</th>
|
||
<th>算法实例</th>
|
||
<th>状态</th>
|
||
<th>FPS</th>
|
||
<th>延迟(ms)</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="task-table-body">
|
||
<!-- 任务数据将通过JavaScript动态生成 -->
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 模拟任务数据
|
||
const tasks = [
|
||
{ id: 'TASK-001', name: '城市安防巡逻', drone: 'Drone-01', algorithm: 'YOLOv8-安防模型', status: 'running', fps: 24, latency: 120 },
|
||
{ id: 'TASK-002', name: '农田监测', drone: 'Drone-02', algorithm: 'YOLOv5-农业模型', status: 'running', fps: 20, latency: 150 },
|
||
{ id: 'TASK-003', name: '电力线路巡检', drone: 'Drone-03', algorithm: 'YOLOv7-电力模型', status: 'paused', fps: 0, latency: 0 },
|
||
{ id: 'TASK-004', name: '交通流量监控', drone: 'Drone-04', algorithm: 'YOLOv8-交通模型', status: 'running', fps: 30, latency: 90 },
|
||
{ id: 'TASK-005', name: '森林防火监测', drone: 'Drone-05', algorithm: 'YOLOv6-火灾模型', status: 'error', fps: 5, latency: 500 },
|
||
{ id: 'TASK-006', name: '建筑工地监控', drone: 'Drone-06', algorithm: 'YOLOv5-工地模型', status: 'stopped', fps: 0, latency: 0 },
|
||
{ id: 'TASK-007', name: '水域污染检测', drone: 'Drone-01', algorithm: 'YOLOv7-环保模型', status: 'running', fps: 18, latency: 180 },
|
||
{ id: 'TASK-008', name: '应急响应侦察', drone: 'Drone-03', algorithm: 'YOLOv8-应急模型', status: 'running', fps: 25, latency: 110 }
|
||
];
|
||
|
||
// 状态映射
|
||
const statusMap = {
|
||
'running': { text: '运行中', class: 'status-running' },
|
||
'paused': { text: '已暂停', class: 'status-paused' },
|
||
'error': { text: '错误', class: 'status-error' },
|
||
'stopped': { text: '已停止', class: 'status-stopped' }
|
||
};
|
||
|
||
// 初始化函数
|
||
function init() {
|
||
updateLastUpdateTime();
|
||
renderTaskTable();
|
||
initPerformanceChart();
|
||
initResourceChart();
|
||
initStatusChart();
|
||
startDataRefresh();
|
||
}
|
||
|
||
// 更新最后更新时间
|
||
function updateLastUpdateTime() {
|
||
const now = new Date();
|
||
const timeString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
|
||
document.getElementById('last-update').textContent = timeString;
|
||
}
|
||
|
||
// 渲染任务表格
|
||
function renderTaskTable() {
|
||
const tableBody = document.getElementById('task-table-body');
|
||
tableBody.innerHTML = '';
|
||
|
||
tasks.forEach(task => {
|
||
const statusInfo = statusMap[task.status];
|
||
const row = document.createElement('tr');
|
||
|
||
row.innerHTML = `
|
||
<td>${task.id}</td>
|
||
<td>${task.name}</td>
|
||
<td>${task.drone}</td>
|
||
<td>${task.algorithm}</td>
|
||
<td><span class="status-badge ${statusInfo.class}">${statusInfo.text}</span></td>
|
||
<td>${task.fps}</td>
|
||
<td>${task.latency}</td>
|
||
<td>
|
||
<div class="action-buttons">
|
||
${task.status === 'running' ? '<button class="btn btn-warning" onclick="pauseTask(\'' + task.id + '\')">暂停</button>' : ''}
|
||
${task.status === 'paused' ? '<button class="btn btn-primary" onclick="resumeTask(\'' + task.id + '\')">恢复</button>' : ''}
|
||
${task.status !== 'stopped' ? '<button class="btn btn-danger" onclick="stopTask(\'' + task.id + '\')">停止</button>' : ''}
|
||
<button class="btn btn-primary" onclick="restartTask(\'' + task.id + '\')">重启</button>
|
||
</div>
|
||
</td>
|
||
`;
|
||
|
||
tableBody.appendChild(row);
|
||
});
|
||
}
|
||
|
||
// 初始化性能监控图表
|
||
function initPerformanceChart() {
|
||
const chartDom = document.getElementById('performance-chart');
|
||
const chart = echarts.init(chartDom);
|
||
|
||
// 模拟时间数据
|
||
const timeData = [];
|
||
const now = new Date();
|
||
for (let i = 9; i >= 0; i--) {
|
||
const time = new Date(now.getTime() - i * 60000);
|
||
timeData.push(`${time.getHours().toString().padStart(2, '0')}:${time.getMinutes().toString().padStart(2, '0')}`);
|
||
}
|
||
|
||
// 模拟FPS和延迟数据
|
||
const fpsData = [];
|
||
const latencyData = [];
|
||
for (let i = 0; i < 10; i++) {
|
||
fpsData.push(Math.floor(Math.random() * 10) + 20); // 20-30 FPS
|
||
latencyData.push(Math.floor(Math.random() * 100) + 80); // 80-180ms
|
||
}
|
||
|
||
const option = {
|
||
color: ['#1890ff', '#52c41a'],
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'cross'
|
||
}
|
||
},
|
||
legend: {
|
||
data: ['FPS', '延迟(ms)'],
|
||
textStyle: {
|
||
color: '#1d3557'
|
||
}
|
||
},
|
||
grid: {
|
||
left: '3%',
|
||
right: '4%',
|
||
bottom: '3%',
|
||
top: '15%',
|
||
containLabel: true
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
boundaryGap: false,
|
||
data: timeData,
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: '#d9d9d9'
|
||
}
|
||
}
|
||
},
|
||
yAxis: [
|
||
{
|
||
type: 'value',
|
||
name: 'FPS',
|
||
min: 0,
|
||
max: 35,
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: '#1890ff'
|
||
}
|
||
}
|
||
},
|
||
{
|
||
type: 'value',
|
||
name: '延迟(ms)',
|
||
min: 0,
|
||
max: 300,
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: '#52c41a'
|
||
}
|
||
}
|
||
}
|
||
],
|
||
series: [
|
||
{
|
||
name: 'FPS',
|
||
type: 'line',
|
||
smooth: true,
|
||
lineStyle: {
|
||
width: 3
|
||
},
|
||
symbol: 'circle',
|
||
symbolSize: 8,
|
||
data: fpsData,
|
||
areaStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: 'rgba(24, 144, 255, 0.3)' },
|
||
{ offset: 1, color: 'rgba(24, 144, 255, 0.05)' }
|
||
])
|
||
}
|
||
},
|
||
{
|
||
name: '延迟(ms)',
|
||
type: 'line',
|
||
smooth: true,
|
||
yAxisIndex: 1,
|
||
lineStyle: {
|
||
width: 3
|
||
},
|
||
symbol: 'circle',
|
||
symbolSize: 8,
|
||
data: latencyData,
|
||
areaStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: 'rgba(82, 196, 26, 0.3)' },
|
||
{ offset: 1, color: 'rgba(82, 196, 26, 0.05)' }
|
||
])
|
||
}
|
||
}
|
||
]
|
||
};
|
||
|
||
chart.setOption(option);
|
||
|
||
// 响应式调整
|
||
window.addEventListener('resize', function() {
|
||
chart.resize();
|
||
});
|
||
}
|
||
|
||
// 初始化资源监控图表
|
||
function initResourceChart() {
|
||
const chartDom = document.getElementById('resource-chart');
|
||
const chart = echarts.init(chartDom);
|
||
|
||
const option = {
|
||
color: ['#1890ff', '#52c41a', '#722ed1', '#fa8c16'],
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow'
|
||
}
|
||
},
|
||
legend: {
|
||
data: ['CPU', '内存', 'GPU', '网络'],
|
||
textStyle: {
|
||
color: '#1d3557'
|
||
}
|
||
},
|
||
grid: {
|
||
left: '3%',
|
||
right: '4%',
|
||
bottom: '3%',
|
||
top: '15%',
|
||
containLabel: true
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
data: ['10:00', '10:05', '10:10', '10:15', '10:20', '10:25', '10:30'],
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: '#d9d9d9'
|
||
}
|
||
}
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
name: '使用率(%)',
|
||
min: 0,
|
||
max: 100,
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: '#d9d9d9'
|
||
}
|
||
}
|
||
},
|
||
series: [
|
||
{
|
||
name: 'CPU',
|
||
type: 'bar',
|
||
barWidth: '20%',
|
||
data: [65, 68, 70, 72, 68, 65, 68]
|
||
},
|
||
{
|
||
name: '内存',
|
||
type: 'bar',
|
||
barWidth: '20%',
|
||
data: [70, 72, 75, 78, 76, 74, 76]
|
||
},
|
||
{
|
||
name: 'GPU',
|
||
type: 'bar',
|
||
barWidth: '20%',
|
||
data: [80, 82, 85, 83, 82, 80, 82]
|
||
},
|
||
{
|
||
name: '网络',
|
||
type: 'bar',
|
||
barWidth: '20%',
|
||
data: [40, 42, 45, 48, 45, 42, 45]
|
||
}
|
||
]
|
||
};
|
||
|
||
chart.setOption(option);
|
||
|
||
// 响应式调整
|
||
window.addEventListener('resize', function() {
|
||
chart.resize();
|
||
});
|
||
}
|
||
|
||
// 初始化任务状态分布图表
|
||
function initStatusChart() {
|
||
const chartDom = document.getElementById('status-chart');
|
||
const chart = echarts.init(chartDom);
|
||
|
||
// 统计任务状态
|
||
const statusCount = {
|
||
'运行中': 0,
|
||
'已暂停': 0,
|
||
'错误': 0,
|
||
'已停止': 0
|
||
};
|
||
|
||
tasks.forEach(task => {
|
||
statusCount[statusMap[task.status].text]++;
|
||
});
|
||
|
||
const option = {
|
||
color: ['#52c41a', '#faad14', '#ff4d4f', '#8c8c8c'],
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||
},
|
||
legend: {
|
||
orient: 'vertical',
|
||
right: 10,
|
||
top: 'center',
|
||
textStyle: {
|
||
color: '#1d3557'
|
||
},
|
||
data: ['运行中', '已暂停', '错误', '已停止']
|
||
},
|
||
series: [
|
||
{
|
||
name: '任务状态',
|
||
type: 'pie',
|
||
radius: ['40%', '70%'],
|
||
center: ['35%', '50%'],
|
||
avoidLabelOverlap: false,
|
||
itemStyle: {
|
||
borderRadius: 10,
|
||
borderColor: '#fff',
|
||
borderWidth: 2
|
||
},
|
||
label: {
|
||
show: false,
|
||
position: 'center'
|
||
},
|
||
emphasis: {
|
||
label: {
|
||
show: true,
|
||
fontSize: '18',
|
||
fontWeight: 'bold'
|
||
}
|
||
},
|
||
labelLine: {
|
||
show: false
|
||
},
|
||
data: [
|
||
{ value: statusCount['运行中'], name: '运行中' },
|
||
{ value: statusCount['已暂停'], name: '已暂停' },
|
||
{ value: statusCount['错误'], name: '错误' },
|
||
{ value: statusCount['已停止'], name: '已停止' }
|
||
]
|
||
}
|
||
]
|
||
};
|
||
|
||
chart.setOption(option);
|
||
|
||
// 响应式调整
|
||
window.addEventListener('resize', function() {
|
||
chart.resize();
|
||
});
|
||
}
|
||
|
||
// 开始数据刷新
|
||
function startDataRefresh() {
|
||
// 每5秒更新一次数据
|
||
setInterval(() => {
|
||
updateLastUpdateTime();
|
||
updateResourceValues();
|
||
updateTaskMetrics();
|
||
}, 5000);
|
||
}
|
||
|
||
// 更新资源值
|
||
function updateResourceValues() {
|
||
// 模拟资源数据变化
|
||
const cpu = Math.floor(Math.random() * 20) + 60; // 60-80%
|
||
const memory = Math.floor(Math.random() * 15) + 70; // 70-85%
|
||
const gpu = Math.floor(Math.random() * 20) + 75; // 75-95%
|
||
const network = Math.floor(Math.random() * 30) + 40; // 40-70%
|
||
|
||
document.getElementById('cpu-usage').textContent = cpu + '%';
|
||
document.getElementById('cpu-progress').style.width = cpu + '%';
|
||
|
||
document.getElementById('memory-usage').textContent = memory + '%';
|
||
document.getElementById('memory-progress').style.width = memory + '%';
|
||
|
||
document.getElementById('gpu-usage').textContent = gpu + '%';
|
||
document.getElementById('gpu-progress').style.width = gpu + '%';
|
||
|
||
document.getElementById('network-usage').textContent = network + '%';
|
||
document.getElementById('network-progress').style.width = network + '%';
|
||
|
||
// 更新系统资源占用
|
||
const systemResource = Math.round((cpu + memory + gpu) / 3);
|
||
document.getElementById('system-resource').textContent = systemResource + '%';
|
||
}
|
||
|
||
// 更新任务指标
|
||
function updateTaskMetrics() {
|
||
// 模拟任务数据变化
|
||
tasks.forEach(task => {
|
||
if (task.status === 'running') {
|
||
task.fps = Math.floor(Math.random() * 5) + 20; // 20-25 FPS
|
||
task.latency = Math.floor(Math.random() * 50) + 100; // 100-150ms
|
||
}
|
||
});
|
||
|
||
// 随机改变一个任务状态(模拟真实环境)
|
||
if (Math.random() > 0.7) {
|
||
const randomIndex = Math.floor(Math.random() * tasks.length);
|
||
const statuses = ['running', 'paused', 'error', 'stopped'];
|
||
const currentStatus = tasks[randomIndex].status;
|
||
let newStatus;
|
||
|
||
do {
|
||
newStatus = statuses[Math.floor(Math.random() * statuses.length)];
|
||
} while (newStatus === currentStatus);
|
||
|
||
tasks[randomIndex].status = newStatus;
|
||
|
||
// 如果状态变为运行中,设置FPS和延迟
|
||
if (newStatus === 'running') {
|
||
tasks[randomIndex].fps = Math.floor(Math.random() * 10) + 20;
|
||
tasks[randomIndex].latency = Math.floor(Math.random() * 100) + 80;
|
||
} else {
|
||
tasks[randomIndex].fps = 0;
|
||
tasks[randomIndex].latency = 0;
|
||
}
|
||
|
||
renderTaskTable();
|
||
initStatusChart(); // 重新渲染状态图表
|
||
}
|
||
}
|
||
|
||
// 任务操作函数
|
||
function pauseTask(taskId) {
|
||
const task = tasks.find(t => t.id === taskId);
|
||
if (task) {
|
||
task.status = 'paused';
|
||
task.fps = 0;
|
||
task.latency = 0;
|
||
renderTaskTable();
|
||
initStatusChart();
|
||
alert(`任务 ${taskId} 已暂停`);
|
||
}
|
||
}
|
||
|
||
function resumeTask(taskId) {
|
||
const task = tasks.find(t => t.id === taskId);
|
||
if (task) {
|
||
task.status = 'running';
|
||
task.fps = Math.floor(Math.random() * 10) + 20;
|
||
task.latency = Math.floor(Math.random() * 100) + 80;
|
||
renderTaskTable();
|
||
initStatusChart();
|
||
alert(`任务 ${taskId} 已恢复`);
|
||
}
|
||
}
|
||
|
||
function stopTask(taskId) {
|
||
const task = tasks.find(t => t.id === taskId);
|
||
if (task) {
|
||
task.status = 'stopped';
|
||
task.fps = 0;
|
||
task.latency = 0;
|
||
renderTaskTable();
|
||
initStatusChart();
|
||
alert(`任务 ${taskId} 已停止`);
|
||
}
|
||
}
|
||
|
||
function restartTask(taskId) {
|
||
const task = tasks.find(t => t.id === taskId);
|
||
if (task) {
|
||
task.status = 'running';
|
||
task.fps = Math.floor(Math.random() * 10) + 20;
|
||
task.latency = Math.floor(Math.random() * 100) + 80;
|
||
renderTaskTable();
|
||
initStatusChart();
|
||
alert(`任务 ${taskId} 正在重启...`);
|
||
}
|
||
}
|
||
|
||
function refreshTasks() {
|
||
// 模拟刷新任务数据
|
||
updateTaskMetrics();
|
||
alert('任务列表已刷新');
|
||
}
|
||
|
||
function createTask() {
|
||
alert('打开新建任务对话框...');
|
||
// 这里实际开发中会打开一个模态框来创建新任务
|
||
}
|
||
|
||
// 页面加载完成后初始化
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
</script>
|
||
</body>
|
||
</html> |