A:增加测试文件
This commit is contained in:
1 parent
9751a5f009
commit
cd4ffe2385
2 files changed
+2238
No files matched your search
@@ -0,0 +1,966 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>网络数据分析 - NetVizor</title>
|
||||||
|
|
||||||
|
<!-- Bootstrap 5 CSS -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Font Awesome Icons -->
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Custom CSS -->
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary-color: #2563eb;
|
||||||
|
--secondary-color: #64748b;
|
||||||
|
--success-color: #16a34a;
|
||||||
|
--warning-color: #ea580c;
|
||||||
|
--danger-color: #dc2626;
|
||||||
|
--dark-bg: #1e293b;
|
||||||
|
--light-bg: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--light-bg);
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color), #3b82f6);
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 2px 0 10px rgba(0,0,0,0.1);
|
||||||
|
min-height: calc(100vh - 76px);
|
||||||
|
position: sticky;
|
||||||
|
top: 76px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-pills .nav-link {
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 2px 0;
|
||||||
|
padding: 12px 16px;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-pills .nav-link:hover {
|
||||||
|
background-color: #e2e8f0;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-pills .nav-link.active {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background-color: white;
|
||||||
|
border-bottom: 2px solid #f1f5f9;
|
||||||
|
padding: 20px 24px;
|
||||||
|
border-radius: 12px 12px 0 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
text-align: center;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
color: var(--secondary-color);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-custom {
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
border-bottom: 2px solid #e2e8f0;
|
||||||
|
padding: 16px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td {
|
||||||
|
padding: 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-protocol {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
height: 300px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-selector {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-time {
|
||||||
|
border: 2px solid #e2e8f0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin: 0 4px;
|
||||||
|
background-color: white;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-time.active {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.network-card-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background-color: #f8fafc;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.network-card-item:hover {
|
||||||
|
background-color: #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Navigation -->
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark sticky-top">
|
||||||
|
<div class="container-fluid px-4">
|
||||||
|
<a class="navbar-brand fw-bold" href="#">
|
||||||
|
<i class="fas fa-network-wired me-2"></i>
|
||||||
|
NetVizor 数据分析
|
||||||
|
</a>
|
||||||
|
<div class="navbar-nav ms-auto">
|
||||||
|
<span class="navbar-text me-3">
|
||||||
|
<i class="fas fa-clock me-1"></i>
|
||||||
|
最后更新: {{ lastUpdated }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="app" class="container-fluid px-0">
|
||||||
|
<div class="row g-0">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<div class="col-md-2">
|
||||||
|
<div class="sidebar p-3">
|
||||||
|
<ul class="nav nav-pills flex-column">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" :class="{active: currentTab === 'overview'}" @click="currentTab = 'overview'" href="#overview">
|
||||||
|
<i class="fas fa-tachometer-alt me-2"></i>
|
||||||
|
概览
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" :class="{active: currentTab === 'adapters'}" @click="currentTab = 'adapters'" href="#adapters">
|
||||||
|
<i class="fas fa-ethernet me-2"></i>
|
||||||
|
网卡分析
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" :class="{active: currentTab === 'applications'}" @click="currentTab = 'applications'" href="#applications">
|
||||||
|
<i class="fas fa-desktop me-2"></i>
|
||||||
|
应用分析
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" :class="{active: currentTab === 'connections'}" @click="currentTab = 'connections'" href="#connections">
|
||||||
|
<i class="fas fa-plug me-2"></i>
|
||||||
|
连接分析
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<div class="col-md-10">
|
||||||
|
<div class="p-4">
|
||||||
|
<!-- Time Range Selector -->
|
||||||
|
<div class="time-selector">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<h6 class="mb-0"><i class="fas fa-calendar-alt me-2"></i>时间范围</h6>
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button v-for="range in timeRanges"
|
||||||
|
:key="range.key"
|
||||||
|
:class="['btn-time', {active: selectedTimeRange === range.key}]"
|
||||||
|
@click="selectedTimeRange = range.key">
|
||||||
|
{{ range.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Overview Tab -->
|
||||||
|
<div v-if="currentTab === 'overview'" class="tab-content">
|
||||||
|
<!-- Stats Cards -->
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card">
|
||||||
|
<div class="stat-value text-primary">{{ formatBytes(totalTraffic.download + totalTraffic.upload) }}</div>
|
||||||
|
<div class="stat-label">总流量</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card">
|
||||||
|
<div class="stat-value text-success">{{ formatBytes(totalTraffic.download) }}</div>
|
||||||
|
<div class="stat-label">下载</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card">
|
||||||
|
<div class="stat-value text-warning">{{ formatBytes(totalTraffic.upload) }}</div>
|
||||||
|
<div class="stat-label">上传</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card">
|
||||||
|
<div class="stat-value text-info">{{ activeConnections }}</div>
|
||||||
|
<div class="stat-label">活跃连接</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Traffic Trend Chart -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-chart-line me-2"></i>流量趋势</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas id="trafficChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-chart-pie me-2"></i>协议分布</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas id="protocolChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Top Applications -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-trophy me-2"></i>流量使用排行</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>应用程序</th>
|
||||||
|
<th>下载量</th>
|
||||||
|
<th>上传量</th>
|
||||||
|
<th>总流量</th>
|
||||||
|
<th>连接数</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="app in topApps" :key="app.id">
|
||||||
|
<td>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="app-icon" :style="{backgroundColor: app.color}">
|
||||||
|
{{ app.name.substring(0, 2).toUpperCase() }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="fw-semibold">{{ app.name }}</div>
|
||||||
|
<small class="text-muted">{{ app.version }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>{{ formatBytes(app.download) }}</td>
|
||||||
|
<td>{{ formatBytes(app.upload) }}</td>
|
||||||
|
<td>{{ formatBytes(app.download + app.upload) }}</td>
|
||||||
|
<td><span class="badge bg-primary">{{ app.connections }}</span></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Network Adapters Tab -->
|
||||||
|
<div v-if="currentTab === 'adapters'" class="tab-content">
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6" v-for="adapter in networkAdapters" :key="adapter.id">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h6 class="mb-0">
|
||||||
|
<i class="fas fa-ethernet me-2"></i>
|
||||||
|
{{ adapter.name }}
|
||||||
|
<span class="badge ms-2" :class="adapter.status === 'active' ? 'bg-success' : 'bg-secondary'">
|
||||||
|
{{ adapter.status }}
|
||||||
|
</span>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="network-card-item mb-3">
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<div class="d-flex justify-content-between mb-2">
|
||||||
|
<span>下载速度</span>
|
||||||
|
<span class="fw-bold text-success">{{ formatSpeed(adapter.downloadSpeed) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="progress progress-custom">
|
||||||
|
<div class="progress-bar bg-success" :style="{width: (adapter.downloadSpeed / adapter.maxSpeed * 100) + '%'}"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="network-card-item">
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<div class="d-flex justify-content-between mb-2">
|
||||||
|
<span>上传速度</span>
|
||||||
|
<span class="fw-bold text-warning">{{ formatSpeed(adapter.uploadSpeed) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="progress progress-custom">
|
||||||
|
<div class="progress-bar bg-warning" :style="{width: (adapter.uploadSpeed / adapter.maxSpeed * 100) + '%'}"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<small class="text-muted">
|
||||||
|
<i class="fas fa-info-circle me-1"></i>
|
||||||
|
总流量: {{ formatBytes(adapter.totalTraffic) }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Adapter Traffic Chart -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-chart-area me-2"></i>网卡流量对比</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="chart-container" style="height: 400px;">
|
||||||
|
<canvas id="adapterChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Applications Tab -->
|
||||||
|
<div v-if="currentTab === 'applications'" class="tab-content">
|
||||||
|
<!-- Application Search -->
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input type="text" class="form-control" placeholder="搜索应用程序..." v-model="appSearch">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<select class="form-select" v-model="appSortBy">
|
||||||
|
<option value="traffic">按流量排序</option>
|
||||||
|
<option value="connections">按连接数排序</option>
|
||||||
|
<option value="name">按名称排序</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Application List -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4" v-for="app in filteredApps" :key="app.id">
|
||||||
|
<div class="card" @click="selectedApp = app" style="cursor: pointer;">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex align-items-center mb-3">
|
||||||
|
<div class="app-icon" :style="{backgroundColor: app.color}">
|
||||||
|
{{ app.name.substring(0, 2).toUpperCase() }}
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<h6 class="mb-1">{{ app.name }}</h6>
|
||||||
|
<small class="text-muted">{{ app.version }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row text-center">
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="text-primary fw-bold">{{ formatBytes(app.download) }}</div>
|
||||||
|
<small class="text-muted">下载</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="text-warning fw-bold">{{ formatBytes(app.upload) }}</div>
|
||||||
|
<small class="text-muted">上传</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="text-info fw-bold">{{ app.connections }}</div>
|
||||||
|
<small class="text-muted">连接</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Selected App Details Modal -->
|
||||||
|
<div v-if="selectedApp" class="card mt-4">
|
||||||
|
<div class="card-header d-flex justify-content-between">
|
||||||
|
<h5 class="mb-0">{{ selectedApp.name }} - 详细信息</h5>
|
||||||
|
<button class="btn btn-sm btn-outline-secondary" @click="selectedApp = null">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas :id="'appChart_' + selectedApp.id"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h6>应用信息</h6>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li><strong>版本:</strong> {{ selectedApp.version }}</li>
|
||||||
|
<li><strong>路径:</strong> {{ selectedApp.path }}</li>
|
||||||
|
<li><strong>PID:</strong> {{ selectedApp.pid }}</li>
|
||||||
|
<li><strong>启动时间:</strong> {{ selectedApp.startTime }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Connections Table -->
|
||||||
|
<h6>活跃连接</h6>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>本地地址</th>
|
||||||
|
<th>远程地址</th>
|
||||||
|
<th>协议</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>流量</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="conn in selectedApp.connections" :key="conn.id">
|
||||||
|
<td>{{ conn.localAddress }}:{{ conn.localPort }}</td>
|
||||||
|
<td>{{ conn.remoteAddress }}:{{ conn.remotePort }}</td>
|
||||||
|
<td><span class="badge-protocol" :class="'bg-' + getProtocolColor(conn.protocol)">{{ conn.protocol }}</span></td>
|
||||||
|
<td><span class="badge bg-success">{{ conn.state }}</span></td>
|
||||||
|
<td>
|
||||||
|
<small>
|
||||||
|
↓{{ formatBytes(conn.download) }}
|
||||||
|
↑{{ formatBytes(conn.upload) }}
|
||||||
|
</small>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Connections Tab -->
|
||||||
|
<div v-if="currentTab === 'connections'" class="tab-content">
|
||||||
|
<!-- Connection Stats -->
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card">
|
||||||
|
<div class="stat-value text-primary">{{ totalConnections }}</div>
|
||||||
|
<div class="stat-label">总连接数</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card">
|
||||||
|
<div class="stat-value text-success">{{ establishedConnections }}</div>
|
||||||
|
<div class="stat-label">已建立</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card">
|
||||||
|
<div class="stat-value text-warning">{{ listeningConnections }}</div>
|
||||||
|
<div class="stat-label">监听中</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card">
|
||||||
|
<div class="stat-value text-info">{{ uniqueIps }}</div>
|
||||||
|
<div class="stat-label">唯一IP</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Connections Table -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-list me-2"></i>连接详情</h5>
|
||||||
|
<div class="d-flex">
|
||||||
|
<input type="text" class="form-control form-control-sm me-2" placeholder="筛选连接..." v-model="connectionFilter">
|
||||||
|
<select class="form-select form-select-sm" v-model="protocolFilter">
|
||||||
|
<option value="">所有协议</option>
|
||||||
|
<option value="TCP">TCP</option>
|
||||||
|
<option value="UDP">UDP</option>
|
||||||
|
<option value="HTTP">HTTP</option>
|
||||||
|
<option value="HTTPS">HTTPS</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>应用程序</th>
|
||||||
|
<th>本地地址</th>
|
||||||
|
<th>远程地址</th>
|
||||||
|
<th>协议</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>下载</th>
|
||||||
|
<th>上传</th>
|
||||||
|
<th>持续时间</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="conn in filteredConnections" :key="conn.id">
|
||||||
|
<td>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="app-icon me-2" :style="{backgroundColor: conn.appColor, width: '24px', height: '24px', fontSize: '10px'}">
|
||||||
|
{{ conn.appName.substring(0, 2).toUpperCase() }}
|
||||||
|
</div>
|
||||||
|
{{ conn.appName }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><code>{{ conn.localAddress }}:{{ conn.localPort }}</code></td>
|
||||||
|
<td><code>{{ conn.remoteAddress }}:{{ conn.remotePort }}</code></td>
|
||||||
|
<td><span class="badge-protocol" :class="'bg-' + getProtocolColor(conn.protocol)">{{ conn.protocol }}</span></td>
|
||||||
|
<td><span class="badge bg-success">{{ conn.state }}</span></td>
|
||||||
|
<td>{{ formatBytes(conn.download) }}</td>
|
||||||
|
<td>{{ formatBytes(conn.upload) }}</td>
|
||||||
|
<td>{{ conn.duration }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Scripts -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const { createApp } = Vue;
|
||||||
|
|
||||||
|
createApp({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentTab: 'overview',
|
||||||
|
selectedTimeRange: 'hour',
|
||||||
|
lastUpdated: new Date().toLocaleTimeString(),
|
||||||
|
appSearch: '',
|
||||||
|
appSortBy: 'traffic',
|
||||||
|
selectedApp: null,
|
||||||
|
connectionFilter: '',
|
||||||
|
protocolFilter: '',
|
||||||
|
|
||||||
|
timeRanges: [
|
||||||
|
{ key: 'hour', label: '最近1小时' },
|
||||||
|
{ key: 'day', label: '今天' },
|
||||||
|
{ key: 'week', label: '本周' },
|
||||||
|
{ key: 'month', label: '本月' }
|
||||||
|
],
|
||||||
|
|
||||||
|
// Mock data
|
||||||
|
totalTraffic: {
|
||||||
|
download: 2.5 * 1024 * 1024 * 1024, // 2.5GB
|
||||||
|
upload: 512 * 1024 * 1024 // 512MB
|
||||||
|
},
|
||||||
|
activeConnections: 127,
|
||||||
|
|
||||||
|
networkAdapters: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Ethernet 以太网',
|
||||||
|
status: 'active',
|
||||||
|
downloadSpeed: 125 * 1024 * 1024, // 125MB/s
|
||||||
|
uploadSpeed: 25 * 1024 * 1024, // 25MB/s
|
||||||
|
maxSpeed: 1000 * 1024 * 1024, // 1GB/s
|
||||||
|
totalTraffic: 15.5 * 1024 * 1024 * 1024 // 15.5GB
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'Wi-Fi 无线网络',
|
||||||
|
status: 'active',
|
||||||
|
downloadSpeed: 45 * 1024 * 1024,
|
||||||
|
uploadSpeed: 12 * 1024 * 1024,
|
||||||
|
maxSpeed: 300 * 1024 * 1024,
|
||||||
|
totalTraffic: 8.2 * 1024 * 1024 * 1024
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
topApps: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Google Chrome',
|
||||||
|
version: '118.0.5993.88',
|
||||||
|
download: 1.2 * 1024 * 1024 * 1024,
|
||||||
|
upload: 156 * 1024 * 1024,
|
||||||
|
connections: 23,
|
||||||
|
color: '#4285f4',
|
||||||
|
path: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
||||||
|
pid: 1234,
|
||||||
|
startTime: '09:15:32'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'Microsoft Teams',
|
||||||
|
version: '1.6.00.4472',
|
||||||
|
download: 845 * 1024 * 1024,
|
||||||
|
upload: 234 * 1024 * 1024,
|
||||||
|
connections: 8,
|
||||||
|
color: '#6264a7',
|
||||||
|
path: 'C:\\Users\\User\\AppData\\Local\\Microsoft\\Teams\\current\\Teams.exe',
|
||||||
|
pid: 5678,
|
||||||
|
startTime: '08:45:12'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'Steam',
|
||||||
|
version: '3.0.2.1',
|
||||||
|
download: 567 * 1024 * 1024,
|
||||||
|
upload: 45 * 1024 * 1024,
|
||||||
|
connections: 5,
|
||||||
|
color: '#1b2838',
|
||||||
|
path: 'C:\\Program Files (x86)\\Steam\\steam.exe',
|
||||||
|
pid: 9012,
|
||||||
|
startTime: '10:20:45'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
allConnections: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
filteredApps() {
|
||||||
|
let apps = [...this.topApps];
|
||||||
|
|
||||||
|
// Search filter
|
||||||
|
if (this.appSearch) {
|
||||||
|
apps = apps.filter(app =>
|
||||||
|
app.name.toLowerCase().includes(this.appSearch.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
apps.sort((a, b) => {
|
||||||
|
switch (this.appSortBy) {
|
||||||
|
case 'traffic':
|
||||||
|
return (b.download + b.upload) - (a.download + a.upload);
|
||||||
|
case 'connections':
|
||||||
|
return b.connections - a.connections;
|
||||||
|
case 'name':
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return apps;
|
||||||
|
},
|
||||||
|
|
||||||
|
filteredConnections() {
|
||||||
|
let connections = [...this.allConnections];
|
||||||
|
|
||||||
|
if (this.connectionFilter) {
|
||||||
|
connections = connections.filter(conn =>
|
||||||
|
conn.appName.toLowerCase().includes(this.connectionFilter.toLowerCase()) ||
|
||||||
|
conn.remoteAddress.includes(this.connectionFilter) ||
|
||||||
|
conn.localAddress.includes(this.connectionFilter)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.protocolFilter) {
|
||||||
|
connections = connections.filter(conn =>
|
||||||
|
conn.protocol === this.protocolFilter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return connections;
|
||||||
|
},
|
||||||
|
|
||||||
|
totalConnections() {
|
||||||
|
return this.allConnections.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
establishedConnections() {
|
||||||
|
return this.allConnections.filter(conn => conn.state === 'ESTABLISHED').length;
|
||||||
|
},
|
||||||
|
|
||||||
|
listeningConnections() {
|
||||||
|
return this.allConnections.filter(conn => conn.state === 'LISTENING').length;
|
||||||
|
},
|
||||||
|
|
||||||
|
uniqueIps() {
|
||||||
|
const ips = new Set(this.allConnections.map(conn => conn.remoteAddress));
|
||||||
|
return ips.size;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
formatBytes(bytes) {
|
||||||
|
if (bytes === 0) return '0 B';
|
||||||
|
const k = 1024;
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||||
|
},
|
||||||
|
|
||||||
|
formatSpeed(bytesPerSecond) {
|
||||||
|
return this.formatBytes(bytesPerSecond) + '/s';
|
||||||
|
},
|
||||||
|
|
||||||
|
getProtocolColor(protocol) {
|
||||||
|
const colors = {
|
||||||
|
'TCP': 'primary',
|
||||||
|
'UDP': 'success',
|
||||||
|
'HTTP': 'warning',
|
||||||
|
'HTTPS': 'info'
|
||||||
|
};
|
||||||
|
return colors[protocol] || 'secondary';
|
||||||
|
},
|
||||||
|
|
||||||
|
initCharts() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.initTrafficChart();
|
||||||
|
this.initProtocolChart();
|
||||||
|
this.initAdapterChart();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
initTrafficChart() {
|
||||||
|
const ctx = document.getElementById('trafficChart');
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: Array.from({length: 12}, (_, i) => (11-i) + 'h前'),
|
||||||
|
datasets: [{
|
||||||
|
label: '下载',
|
||||||
|
data: [45, 52, 61, 58, 67, 72, 68, 75, 82, 78, 85, 92],
|
||||||
|
borderColor: 'rgb(34, 197, 94)',
|
||||||
|
backgroundColor: 'rgba(34, 197, 94, 0.1)',
|
||||||
|
tension: 0.4
|
||||||
|
}, {
|
||||||
|
label: '上传',
|
||||||
|
data: [12, 15, 18, 16, 22, 25, 23, 28, 32, 29, 35, 38],
|
||||||
|
borderColor: 'rgb(251, 146, 60)',
|
||||||
|
backgroundColor: 'rgba(251, 146, 60, 0.1)',
|
||||||
|
tension: 0.4
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
ticks: {
|
||||||
|
callback: function(value) {
|
||||||
|
return value + ' MB/s';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
initProtocolChart() {
|
||||||
|
const ctx = document.getElementById('protocolChart');
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
labels: ['TCP', 'UDP', 'HTTP', 'HTTPS'],
|
||||||
|
datasets: [{
|
||||||
|
data: [45, 25, 20, 10],
|
||||||
|
backgroundColor: [
|
||||||
|
'rgb(37, 99, 235)',
|
||||||
|
'rgb(34, 197, 94)',
|
||||||
|
'rgb(251, 146, 60)',
|
||||||
|
'rgb(14, 165, 233)'
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'bottom'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
initAdapterChart() {
|
||||||
|
const ctx = document.getElementById('adapterChart');
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: this.networkAdapters.map(adapter => adapter.name),
|
||||||
|
datasets: [{
|
||||||
|
label: '下载 (MB/s)',
|
||||||
|
data: this.networkAdapters.map(adapter => adapter.downloadSpeed / (1024 * 1024)),
|
||||||
|
backgroundColor: 'rgba(34, 197, 94, 0.8)'
|
||||||
|
}, {
|
||||||
|
label: '上传 (MB/s)',
|
||||||
|
data: this.networkAdapters.map(adapter => adapter.uploadSpeed / (1024 * 1024)),
|
||||||
|
backgroundColor: 'rgba(251, 146, 60, 0.8)'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
generateMockConnections() {
|
||||||
|
const apps = this.topApps;
|
||||||
|
const protocols = ['TCP', 'UDP', 'HTTP', 'HTTPS'];
|
||||||
|
const states = ['ESTABLISHED', 'LISTENING', 'CLOSE_WAIT'];
|
||||||
|
const connections = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
const app = apps[Math.floor(Math.random() * apps.length)];
|
||||||
|
connections.push({
|
||||||
|
id: i + 1,
|
||||||
|
appName: app.name,
|
||||||
|
appColor: app.color,
|
||||||
|
localAddress: '192.168.1.' + (100 + Math.floor(Math.random() * 50)),
|
||||||
|
localPort: 1000 + Math.floor(Math.random() * 64000),
|
||||||
|
remoteAddress: Math.floor(Math.random() * 255) + '.' + Math.floor(Math.random() * 255) + '.' + Math.floor(Math.random() * 255) + '.' + Math.floor(Math.random() * 255),
|
||||||
|
remotePort: 80 + Math.floor(Math.random() * 8000),
|
||||||
|
protocol: protocols[Math.floor(Math.random() * protocols.length)],
|
||||||
|
state: states[Math.floor(Math.random() * states.length)],
|
||||||
|
download: Math.floor(Math.random() * 100 * 1024 * 1024),
|
||||||
|
upload: Math.floor(Math.random() * 50 * 1024 * 1024),
|
||||||
|
duration: Math.floor(Math.random() * 3600) + 's'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.allConnections = connections;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.generateMockConnections();
|
||||||
|
|
||||||
|
// Add connections to apps
|
||||||
|
this.topApps.forEach(app => {
|
||||||
|
app.connections = this.allConnections
|
||||||
|
.filter(conn => conn.appName === app.name)
|
||||||
|
.slice(0, 10)
|
||||||
|
.map(conn => ({
|
||||||
|
id: conn.id,
|
||||||
|
localAddress: conn.localAddress,
|
||||||
|
localPort: conn.localPort,
|
||||||
|
remoteAddress: conn.remoteAddress,
|
||||||
|
remotePort: conn.remotePort,
|
||||||
|
protocol: conn.protocol,
|
||||||
|
state: conn.state,
|
||||||
|
download: conn.download,
|
||||||
|
upload: conn.upload
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.initCharts();
|
||||||
|
|
||||||
|
// Update time every second
|
||||||
|
setInterval(() => {
|
||||||
|
this.lastUpdated = new Date().toLocaleTimeString();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}).mount('#app');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,1272 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>NetVizor - 网络监控</title>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary-bg: #1a1d23;
|
||||||
|
--secondary-bg: #252832;
|
||||||
|
--card-bg: #2d3142;
|
||||||
|
--border-color: #3e4451;
|
||||||
|
--text-primary: #ffffff;
|
||||||
|
--text-secondary: #a0a6b8;
|
||||||
|
--accent-blue: #007bff;
|
||||||
|
--accent-green: #28a745;
|
||||||
|
--accent-red: #dc3545;
|
||||||
|
--accent-orange: #fd7e14;
|
||||||
|
--hover-bg: #3a3f4b;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: var(--primary-bg);
|
||||||
|
color: var(--text-primary);
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background: var(--secondary-bg);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
padding: 0 2rem;
|
||||||
|
height: 64px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--accent-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tab {
|
||||||
|
padding: 12px 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tab.active {
|
||||||
|
background: var(--accent-blue);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tab:hover:not(.active) {
|
||||||
|
background: var(--hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-selector {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-primary);
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-btn {
|
||||||
|
background: var(--accent-green);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-btn:hover {
|
||||||
|
background: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
display: flex;
|
||||||
|
height: calc(100vh - 64px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 300px;
|
||||||
|
background: var(--secondary-bg);
|
||||||
|
border-right: 1px solid var(--border-color);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header {
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-primary);
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-item, .network-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-item:hover, .network-item:hover {
|
||||||
|
background: var(--hover-bg);
|
||||||
|
border-color: var(--accent-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-item.active, .network-item.active {
|
||||||
|
border-color: var(--accent-blue);
|
||||||
|
background: rgba(0, 123, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background: var(--accent-blue);
|
||||||
|
border-radius: 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-name {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-status {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.network-status {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--accent-green);
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
font-size: 2rem;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-change {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-change.positive {
|
||||||
|
color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-change.negative {
|
||||||
|
color: var(--accent-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-canvas {
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
background: var(--secondary-bg);
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table th {
|
||||||
|
background: var(--secondary-bg);
|
||||||
|
padding: 16px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table td {
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table tbody tr:hover {
|
||||||
|
background: var(--hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-active {
|
||||||
|
background: rgba(40, 167, 69, 0.2);
|
||||||
|
color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-inactive {
|
||||||
|
background: rgba(220, 53, 69, 0.2);
|
||||||
|
color: var(--accent-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.protocol-badge {
|
||||||
|
background: rgba(0, 123, 255, 0.2);
|
||||||
|
color: var(--accent-blue);
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-column-layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sidebar {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-column-layout {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tab {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: var(--accent-blue);
|
||||||
|
animation: spin 1s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<div class="logo">
|
||||||
|
<i class="fas fa-network-wired"></i>
|
||||||
|
NetVizor
|
||||||
|
</div>
|
||||||
|
<nav>
|
||||||
|
<ul class="nav-tabs">
|
||||||
|
<li class="nav-tab active" data-tab="overview">概览</li>
|
||||||
|
<li class="nav-tab" data-tab="network">网卡监控</li>
|
||||||
|
<li class="nav-tab" data-tab="applications">应用监控</li>
|
||||||
|
<li class="nav-tab" data-tab="connections">连接管理</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<div class="controls">
|
||||||
|
<select class="time-selector">
|
||||||
|
<option value="5s">实时 (5秒)</option>
|
||||||
|
<option value="1h">1 小时</option>
|
||||||
|
<option value="1d" selected>1 天</option>
|
||||||
|
<option value="1w">1 周</option>
|
||||||
|
<option value="1m">1 月</option>
|
||||||
|
</select>
|
||||||
|
<button class="refresh-btn">
|
||||||
|
<i class="fas fa-sync-alt"></i> 刷新
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-container">
|
||||||
|
<!-- 概览标签页 -->
|
||||||
|
<div class="tab-content active" id="overview">
|
||||||
|
<div class="content">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-blue);">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-blue);">125.3 MB/s</div>
|
||||||
|
<div class="stat-label">下载速度</div>
|
||||||
|
<div class="stat-change positive">
|
||||||
|
<i class="fas fa-arrow-up"></i>
|
||||||
|
+15.2% 比昨天
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-green);">
|
||||||
|
<i class="fas fa-upload"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-green);">58.7 MB/s</div>
|
||||||
|
<div class="stat-label">上传速度</div>
|
||||||
|
<div class="stat-change negative">
|
||||||
|
<i class="fas fa-arrow-down"></i>
|
||||||
|
-3.8% 比昨天
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-orange);">
|
||||||
|
<i class="fas fa-link"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-orange);">342</div>
|
||||||
|
<div class="stat-label">活跃连接</div>
|
||||||
|
<div class="stat-change positive">
|
||||||
|
<i class="fas fa-arrow-up"></i>
|
||||||
|
+28 个新连接
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-red);">
|
||||||
|
<i class="fas fa-desktop"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-red);">23</div>
|
||||||
|
<div class="stat-label">活跃应用</div>
|
||||||
|
<div class="stat-change positive">
|
||||||
|
<i class="fas fa-arrow-up"></i>
|
||||||
|
+2 个应用
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chart-container">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title">网络流量趋势</h3>
|
||||||
|
<div class="chart-legend">
|
||||||
|
<span style="color: var(--accent-blue);">● 下载</span>
|
||||||
|
<span style="color: var(--accent-green); margin-left: 20px;">● 上传</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<canvas id="overviewChart" class="chart-canvas"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="two-column-layout">
|
||||||
|
<div class="table-container">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title">网卡状态</h3>
|
||||||
|
</div>
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>网卡名称</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>下载速度</th>
|
||||||
|
<th>上传速度</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<i class="fas fa-ethernet" style="color: var(--accent-blue);"></i>
|
||||||
|
以太网适配器
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><span class="status-badge status-active">活跃</span></td>
|
||||||
|
<td>89.2 MB/s</td>
|
||||||
|
<td>32.1 MB/s</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<i class="fas fa-wifi" style="color: var(--accent-green);"></i>
|
||||||
|
Wi-Fi 6 适配器
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><span class="status-badge status-active">活跃</span></td>
|
||||||
|
<td>36.1 MB/s</td>
|
||||||
|
<td>26.6 MB/s</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<i class="fas fa-network-wired" style="color: var(--text-secondary);"></i>
|
||||||
|
虚拟适配器 1
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><span class="status-badge status-inactive">未使用</span></td>
|
||||||
|
<td>0 B/s</td>
|
||||||
|
<td>0 B/s</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title">流量排行 TOP 5</h3>
|
||||||
|
</div>
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>应用程序</th>
|
||||||
|
<th>下载量</th>
|
||||||
|
<th>上传量</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div class="app-icon" style="background: #4285f4;">
|
||||||
|
<i class="fab fa-chrome"></i>
|
||||||
|
</div>
|
||||||
|
Google Chrome
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>2.3 GB</td>
|
||||||
|
<td>456 MB</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div class="app-icon" style="background: #7289da;">
|
||||||
|
<i class="fab fa-discord"></i>
|
||||||
|
</div>
|
||||||
|
Discord
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>1.8 GB</td>
|
||||||
|
<td>890 MB</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div class="app-icon" style="background: #ff6b6b;">
|
||||||
|
<i class="fab fa-steam"></i>
|
||||||
|
</div>
|
||||||
|
Steam
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>15.2 GB</td>
|
||||||
|
<td>125 MB</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div class="app-icon" style="background: #0078d4;">
|
||||||
|
<i class="fab fa-microsoft"></i>
|
||||||
|
</div>
|
||||||
|
Microsoft Edge
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>892 MB</td>
|
||||||
|
<td>234 MB</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div class="app-icon" style="background: #00b4d8;">
|
||||||
|
<i class="fas fa-code"></i>
|
||||||
|
</div>
|
||||||
|
Visual Studio Code
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>567 MB</td>
|
||||||
|
<td>189 MB</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 网卡监控标签页 -->
|
||||||
|
<div class="tab-content" id="network">
|
||||||
|
<div class="sidebar">
|
||||||
|
<div class="sidebar-header">
|
||||||
|
<h3 class="sidebar-title">网络适配器</h3>
|
||||||
|
<input type="text" class="search-box" placeholder="搜索网卡...">
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-content">
|
||||||
|
<div class="network-item active">
|
||||||
|
<i class="fas fa-ethernet" style="color: var(--accent-blue); font-size: 1.5rem;"></i>
|
||||||
|
<div class="app-info">
|
||||||
|
<div class="app-name">以太网适配器</div>
|
||||||
|
<div class="app-status">下载: 89.2 MB/s | 上传: 32.1 MB/s</div>
|
||||||
|
</div>
|
||||||
|
<div class="network-status"></div>
|
||||||
|
</div>
|
||||||
|
<div class="network-item">
|
||||||
|
<i class="fas fa-wifi" style="color: var(--accent-green); font-size: 1.5rem;"></i>
|
||||||
|
<div class="app-info">
|
||||||
|
<div class="app-name">Wi-Fi 6 适配器</div>
|
||||||
|
<div class="app-status">下载: 36.1 MB/s | 上传: 26.6 MB/s</div>
|
||||||
|
</div>
|
||||||
|
<div class="network-status"></div>
|
||||||
|
</div>
|
||||||
|
<div class="network-item">
|
||||||
|
<i class="fas fa-network-wired" style="color: var(--text-secondary); font-size: 1.5rem;"></i>
|
||||||
|
<div class="app-info">
|
||||||
|
<div class="app-name">虚拟适配器 1</div>
|
||||||
|
<div class="app-status">未使用</div>
|
||||||
|
</div>
|
||||||
|
<div class="network-status" style="background: var(--accent-red);"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-blue);">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-blue);">89.2 MB/s</div>
|
||||||
|
<div class="stat-label">当前下载速度</div>
|
||||||
|
<div class="stat-change">峰值: 156.8 MB/s</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-green);">
|
||||||
|
<i class="fas fa-upload"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-green);">32.1 MB/s</div>
|
||||||
|
<div class="stat-label">当前上传速度</div>
|
||||||
|
<div class="stat-change">峰值: 89.4 MB/s</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-orange);">
|
||||||
|
<i class="fas fa-exchange-alt"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-orange);">45.2 GB</div>
|
||||||
|
<div class="stat-label">今日总流量</div>
|
||||||
|
<div class="stat-change positive">比昨天多 12.3%</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-red);">
|
||||||
|
<i class="fas fa-tachometer-alt"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-red);">1 Gbps</div>
|
||||||
|
<div class="stat-label">接口速率</div>
|
||||||
|
<div class="stat-change">全双工模式</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chart-container">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title">以太网适配器 - 实时流量监控</h3>
|
||||||
|
</div>
|
||||||
|
<canvas id="networkChart" class="chart-canvas"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title">网卡详细信息</h3>
|
||||||
|
</div>
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>属性</th>
|
||||||
|
<th>值</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>适配器名称</td>
|
||||||
|
<td>Realtek PCIe GbE Family Controller</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>MAC 地址</td>
|
||||||
|
<td>00:1B:44:11:3A:B7</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IP 地址</td>
|
||||||
|
<td>192.168.1.100</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>子网掩码</td>
|
||||||
|
<td>255.255.255.0</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>默认网关</td>
|
||||||
|
<td>192.168.1.1</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>DNS 服务器</td>
|
||||||
|
<td>8.8.8.8, 8.8.4.4</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 应用监控标签页 -->
|
||||||
|
<div class="tab-content" id="applications">
|
||||||
|
<div class="sidebar">
|
||||||
|
<div class="sidebar-header">
|
||||||
|
<h3 class="sidebar-title">应用程序</h3>
|
||||||
|
<input type="text" class="search-box" placeholder="搜索应用...">
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-content">
|
||||||
|
<div class="app-item active">
|
||||||
|
<div class="app-icon" style="background: #4285f4;">
|
||||||
|
<i class="fab fa-chrome"></i>
|
||||||
|
</div>
|
||||||
|
<div class="app-info">
|
||||||
|
<div class="app-name">Google Chrome</div>
|
||||||
|
<div class="app-status">下载: 45.2 MB/s | 上传: 12.1 MB/s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="app-item">
|
||||||
|
<div class="app-icon" style="background: #7289da;">
|
||||||
|
<i class="fab fa-discord"></i>
|
||||||
|
</div>
|
||||||
|
<div class="app-info">
|
||||||
|
<div class="app-name">Discord</div>
|
||||||
|
<div class="app-status">下载: 2.3 MB/s | 上传: 8.7 MB/s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="app-item">
|
||||||
|
<div class="app-icon" style="background: #ff6b6b;">
|
||||||
|
<i class="fab fa-steam"></i>
|
||||||
|
</div>
|
||||||
|
<div class="app-info">
|
||||||
|
<div class="app-name">Steam</div>
|
||||||
|
<div class="app-status">下载: 98.7 MB/s | 上传: 0.2 MB/s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="app-item">
|
||||||
|
<div class="app-icon" style="background: #0078d4;">
|
||||||
|
<i class="fab fa-microsoft"></i>
|
||||||
|
</div>
|
||||||
|
<div class="app-info">
|
||||||
|
<div class="app-name">Microsoft Edge</div>
|
||||||
|
<div class="app-status">下载: 5.6 MB/s | 上传: 1.2 MB/s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-blue);">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-blue);">45.2 MB/s</div>
|
||||||
|
<div class="stat-label">应用下载速度</div>
|
||||||
|
<div class="stat-change positive">活跃连接: 28</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-green);">
|
||||||
|
<i class="fas fa-upload"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-green);">12.1 MB/s</div>
|
||||||
|
<div class="stat-label">应用上传速度</div>
|
||||||
|
<div class="stat-change">发送连接: 15</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-orange);">
|
||||||
|
<i class="fas fa-clock"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-orange);">3h 42m</div>
|
||||||
|
<div class="stat-label">运行时间</div>
|
||||||
|
<div class="stat-change">进程 ID: 4892</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-red);">
|
||||||
|
<i class="fas fa-memory"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-red);">1.2 GB</div>
|
||||||
|
<div class="stat-label">内存使用</div>
|
||||||
|
<div class="stat-change">CPU: 8.5%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chart-container">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title">Google Chrome - 网络活动</h3>
|
||||||
|
</div>
|
||||||
|
<canvas id="appChart" class="chart-canvas"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title">应用程序连接</h3>
|
||||||
|
</div>
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>本地地址</th>
|
||||||
|
<th>远程地址</th>
|
||||||
|
<th>协议</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>下载</th>
|
||||||
|
<th>上传</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>192.168.1.100:52341</td>
|
||||||
|
<td>142.250.191.3:443</td>
|
||||||
|
<td><span class="protocol-badge">HTTPS</span></td>
|
||||||
|
<td><span class="status-badge status-active">已建立</span></td>
|
||||||
|
<td>25.3 MB/s</td>
|
||||||
|
<td>2.1 MB/s</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>192.168.1.100:52342</td>
|
||||||
|
<td>157.240.22.35:443</td>
|
||||||
|
<td><span class="protocol-badge">HTTPS</span></td>
|
||||||
|
<td><span class="status-badge status-active">已建立</span></td>
|
||||||
|
<td>8.7 MB/s</td>
|
||||||
|
<td>1.5 MB/s</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>192.168.1.100:52343</td>
|
||||||
|
<td>104.244.42.193:443</td>
|
||||||
|
<td><span class="protocol-badge">HTTPS</span></td>
|
||||||
|
<td><span class="status-badge status-active">已建立</span></td>
|
||||||
|
<td>11.2 MB/s</td>
|
||||||
|
<td>8.5 MB/s</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 连接管理标签页 -->
|
||||||
|
<div class="tab-content" id="connections">
|
||||||
|
<div class="content">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-blue);">
|
||||||
|
<i class="fas fa-link"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-blue);">342</div>
|
||||||
|
<div class="stat-label">总连接数</div>
|
||||||
|
<div class="stat-change positive">+28 个新连接</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-green);">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-green);">298</div>
|
||||||
|
<div class="stat-label">已建立连接</div>
|
||||||
|
<div class="stat-change positive">87.1% 成功率</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-orange);">
|
||||||
|
<i class="fas fa-clock"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-orange);">28</div>
|
||||||
|
<div class="stat-label">等待连接</div>
|
||||||
|
<div class="stat-change">平均延迟: 45ms</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon" style="color: var(--accent-red);">
|
||||||
|
<i class="fas fa-times-circle"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" style="color: var(--accent-red);">16</div>
|
||||||
|
<div class="stat-label">失败连接</div>
|
||||||
|
<div class="stat-change negative">4.7% 失败率</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title">所有网络连接</h3>
|
||||||
|
<div style="display: flex; gap: 10px;">
|
||||||
|
<input type="text" placeholder="搜索连接..." style="padding: 8px; border-radius: 4px; border: 1px solid var(--border-color); background: var(--card-bg); color: var(--text-primary);">
|
||||||
|
<select style="padding: 8px; border-radius: 4px; border: 1px solid var(--border-color); background: var(--card-bg); color: var(--text-primary);">
|
||||||
|
<option value="">所有协议</option>
|
||||||
|
<option value="tcp">TCP</option>
|
||||||
|
<option value="udp">UDP</option>
|
||||||
|
<option value="http">HTTP</option>
|
||||||
|
<option value="https">HTTPS</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>应用程序</th>
|
||||||
|
<th>本地地址</th>
|
||||||
|
<th>远程地址</th>
|
||||||
|
<th>协议</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>下载速度</th>
|
||||||
|
<th>上传速度</th>
|
||||||
|
<th>持续时间</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div class="app-icon" style="background: #4285f4; width: 24px; height: 24px; font-size: 0.7rem;">
|
||||||
|
<i class="fab fa-chrome"></i>
|
||||||
|
</div>
|
||||||
|
Chrome
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>192.168.1.100:52341</td>
|
||||||
|
<td>142.250.191.3:443</td>
|
||||||
|
<td><span class="protocol-badge">HTTPS</span></td>
|
||||||
|
<td><span class="status-badge status-active">已建立</span></td>
|
||||||
|
<td>25.3 MB/s</td>
|
||||||
|
<td>2.1 MB/s</td>
|
||||||
|
<td>2m 45s</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div class="app-icon" style="background: #7289da; width: 24px; height: 24px; font-size: 0.7rem;">
|
||||||
|
<i class="fab fa-discord"></i>
|
||||||
|
</div>
|
||||||
|
Discord
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>192.168.1.100:52342</td>
|
||||||
|
<td>162.159.130.232:443</td>
|
||||||
|
<td><span class="protocol-badge">WSS</span></td>
|
||||||
|
<td><span class="status-badge status-active">已建立</span></td>
|
||||||
|
<td>2.3 MB/s</td>
|
||||||
|
<td>8.7 MB/s</td>
|
||||||
|
<td>1h 15m</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div class="app-icon" style="background: #ff6b6b; width: 24px; height: 24px; font-size: 0.7rem;">
|
||||||
|
<i class="fab fa-steam"></i>
|
||||||
|
</div>
|
||||||
|
Steam
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>192.168.1.100:52343</td>
|
||||||
|
<td>23.78.102.184:27017</td>
|
||||||
|
<td><span class="protocol-badge">TCP</span></td>
|
||||||
|
<td><span class="status-badge status-active">已建立</span></td>
|
||||||
|
<td>98.7 MB/s</td>
|
||||||
|
<td>0.2 MB/s</td>
|
||||||
|
<td>12m 33s</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div class="app-icon" style="background: #0078d4; width: 24px; height: 24px; font-size: 0.7rem;">
|
||||||
|
<i class="fab fa-microsoft"></i>
|
||||||
|
</div>
|
||||||
|
Edge
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>192.168.1.100:52344</td>
|
||||||
|
<td>13.107.42.14:443</td>
|
||||||
|
<td><span class="protocol-badge">HTTPS</span></td>
|
||||||
|
<td><span class="status-badge status-active">已建立</span></td>
|
||||||
|
<td>5.6 MB/s</td>
|
||||||
|
<td>1.2 MB/s</td>
|
||||||
|
<td>8m 22s</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div class="app-icon" style="background: #00b4d8; width: 24px; height: 24px; font-size: 0.7rem;">
|
||||||
|
<i class="fas fa-code"></i>
|
||||||
|
</div>
|
||||||
|
VS Code
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>192.168.1.100:52345</td>
|
||||||
|
<td>40.76.4.15:443</td>
|
||||||
|
<td><span class="protocol-badge">HTTPS</span></td>
|
||||||
|
<td><span class="status-badge status-inactive">TIME_WAIT</span></td>
|
||||||
|
<td>0 B/s</td>
|
||||||
|
<td>0 B/s</td>
|
||||||
|
<td>-</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 标签页切换功能
|
||||||
|
document.querySelectorAll('.nav-tab').forEach(tab => {
|
||||||
|
tab.addEventListener('click', () => {
|
||||||
|
const targetTab = tab.dataset.tab;
|
||||||
|
|
||||||
|
// 更新导航标签状态
|
||||||
|
document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active'));
|
||||||
|
tab.classList.add('active');
|
||||||
|
|
||||||
|
// 更新内容区域
|
||||||
|
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
|
||||||
|
document.getElementById(targetTab).classList.add('active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 侧边栏项目点击
|
||||||
|
document.querySelectorAll('.app-item, .network-item').forEach(item => {
|
||||||
|
item.addEventListener('click', () => {
|
||||||
|
item.parentElement.querySelectorAll('.app-item, .network-item').forEach(i => i.classList.remove('active'));
|
||||||
|
item.classList.add('active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chart.js 配置
|
||||||
|
Chart.defaults.color = '#a0a6b8';
|
||||||
|
Chart.defaults.borderColor = '#3e4451';
|
||||||
|
|
||||||
|
// 生成随机数据
|
||||||
|
function generateTimeData() {
|
||||||
|
const now = new Date();
|
||||||
|
const data = [];
|
||||||
|
const labels = [];
|
||||||
|
|
||||||
|
for (let i = 23; i >= 0; i--) {
|
||||||
|
const time = new Date(now.getTime() - i * 60 * 60 * 1000);
|
||||||
|
labels.push(time.getHours() + ':00');
|
||||||
|
data.push(Math.random() * 100 + 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { labels, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 概览图表
|
||||||
|
const overviewCtx = document.getElementById('overviewChart').getContext('2d');
|
||||||
|
const overviewData = generateTimeData();
|
||||||
|
new Chart(overviewCtx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: overviewData.labels,
|
||||||
|
datasets: [{
|
||||||
|
label: '下载速度 (MB/s)',
|
||||||
|
data: overviewData.data,
|
||||||
|
borderColor: '#007bff',
|
||||||
|
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
||||||
|
tension: 0.4,
|
||||||
|
fill: true
|
||||||
|
}, {
|
||||||
|
label: '上传速度 (MB/s)',
|
||||||
|
data: overviewData.data.map(v => v * 0.6),
|
||||||
|
borderColor: '#28a745',
|
||||||
|
backgroundColor: 'rgba(40, 167, 69, 0.1)',
|
||||||
|
tension: 0.4,
|
||||||
|
fill: true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
interaction: {
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
color: '#3e4451'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
grid: {
|
||||||
|
color: '#3e4451'
|
||||||
|
},
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 网卡图表
|
||||||
|
const networkCtx = document.getElementById('networkChart').getContext('2d');
|
||||||
|
const networkData = generateTimeData();
|
||||||
|
new Chart(networkCtx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: networkData.labels,
|
||||||
|
datasets: [{
|
||||||
|
label: '下载速度 (MB/s)',
|
||||||
|
data: networkData.data,
|
||||||
|
borderColor: '#007bff',
|
||||||
|
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
||||||
|
tension: 0.4,
|
||||||
|
fill: true
|
||||||
|
}, {
|
||||||
|
label: '上传速度 (MB/s)',
|
||||||
|
data: networkData.data.map(v => v * 0.4),
|
||||||
|
borderColor: '#28a745',
|
||||||
|
backgroundColor: 'rgba(40, 167, 69, 0.1)',
|
||||||
|
tension: 0.4,
|
||||||
|
fill: true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
interaction: {
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
color: '#3e4451'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
grid: {
|
||||||
|
color: '#3e4451'
|
||||||
|
},
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 应用图表
|
||||||
|
const appCtx = document.getElementById('appChart').getContext('2d');
|
||||||
|
const appData = generateTimeData();
|
||||||
|
new Chart(appCtx, {
|
||||||
|
type: 'area',
|
||||||
|
data: {
|
||||||
|
labels: appData.labels,
|
||||||
|
datasets: [{
|
||||||
|
label: '下载速度 (MB/s)',
|
||||||
|
data: appData.data.map(v => v * 0.5),
|
||||||
|
borderColor: '#007bff',
|
||||||
|
backgroundColor: 'rgba(0, 123, 255, 0.2)',
|
||||||
|
tension: 0.4,
|
||||||
|
fill: true
|
||||||
|
}, {
|
||||||
|
label: '上传速度 (MB/s)',
|
||||||
|
data: appData.data.map(v => v * 0.15),
|
||||||
|
borderColor: '#28a745',
|
||||||
|
backgroundColor: 'rgba(40, 167, 69, 0.2)',
|
||||||
|
tension: 0.4,
|
||||||
|
fill: true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
interaction: {
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
color: '#3e4451'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
grid: {
|
||||||
|
color: '#3e4451'
|
||||||
|
},
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新按钮功能
|
||||||
|
document.querySelector('.refresh-btn').addEventListener('click', function() {
|
||||||
|
const btn = this;
|
||||||
|
const originalText = btn.innerHTML;
|
||||||
|
btn.innerHTML = '<div class="loading"></div> 刷新中...';
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
btn.innerHTML = originalText;
|
||||||
|
btn.disabled = false;
|
||||||
|
// 这里可以添加实际的数据刷新逻辑
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 搜索功能
|
||||||
|
document.querySelectorAll('.search-box').forEach(searchBox => {
|
||||||
|
searchBox.addEventListener('input', function() {
|
||||||
|
const searchTerm = this.value.toLowerCase();
|
||||||
|
const items = this.closest('.sidebar').querySelectorAll('.app-item, .network-item');
|
||||||
|
|
||||||
|
items.forEach(item => {
|
||||||
|
const name = item.querySelector('.app-name').textContent.toLowerCase();
|
||||||
|
if (name.includes(searchTerm)) {
|
||||||
|
item.style.display = 'flex';
|
||||||
|
} else {
|
||||||
|
item.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 时间选择器变化事件
|
||||||
|
document.querySelector('.time-selector').addEventListener('change', function() {
|
||||||
|
// 这里可以添加根据时间范围重新加载数据的逻辑
|
||||||
|
console.log('时间范围已更改为:', this.value);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in new issue
Block a user