A:增加测试文件

This commit is contained in:
jimmy committed 2025-08-08 13:39:13 +08:00
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>