A:动画及测试文件

This commit is contained in:
jimmy committed 2025-07-07 10:57:53 +08:00
1 parent f6d1409295
commit 7716850e16
17 files changed
+11653

No files matched your search

@@ -0,0 +1,331 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动态折线图 - 跳动小球轨迹</title>
<style>
body {
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #1e3c72, #2a5298);
font-family: 'Arial', sans-serif;
color: white;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.1);
border-radius: 20px;
padding: 30px;
backdrop-filter: blur(10px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
h1 {
text-align: center;
margin-bottom: 30px;
font-size: 2.5em;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
#chartContainer {
position: relative;
width: 100%;
height: 500px;
background: rgba(0, 0, 0, 0.2);
border-radius: 15px;
overflow: hidden;
border: 2px solid rgba(255, 255, 255, 0.2);
}
#chart {
width: 100%;
height: 100%;
}
.ball {
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #ff6b6b, #ff4757);
box-shadow: 0 0 20px rgba(255, 107, 107, 0.8), 0 0 40px rgba(255, 107, 107, 0.4);
z-index: 10;
transform: translate(-50%, -50%);
animation: bounce 0.6s ease-in-out infinite alternate;
}
@keyframes bounce {
0% { transform: translate(-50%, -50%) scale(1); }
100% { transform: translate(-50%, -50%) scale(1.2); }
}
.value-display {
position: absolute;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 8px 12px;
border-radius: 8px;
font-size: 14px;
font-weight: bold;
z-index: 11;
transform: translate(-50%, -150%);
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.8; }
50% { opacity: 1; }
}
.controls {
margin-top: 20px;
text-align: center;
}
button {
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
border: none;
color: white;
padding: 12px 24px;
margin: 0 10px;
border-radius: 25px;
cursor: pointer;
font-size: 16px;
font-weight: bold;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}
button:active {
transform: translateY(0);
}
.info {
margin-top: 20px;
text-align: center;
font-size: 18px;
color: rgba(255, 255, 255, 0.8);
}
</style>
</head>
<body>
<div class="container">
<h1>🎯 动态数据轨迹图</h1>
<div id="chartContainer">
<svg id="chart" viewBox="0 0 1000 400">
<!-- 网格线 -->
<defs>
<pattern id="grid" width="50" height="40" patternUnits="userSpaceOnUse">
<path d="M 50 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="1"/>
</pattern>
<linearGradient id="lineGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4ecdc4;stop-opacity:0.3" />
<stop offset="100%" style="stop-color:#ff6b6b;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
<!-- 主要轨迹线 -->
<path id="mainPath" fill="none" stroke="url(#lineGradient)" stroke-width="3" opacity="0.8"/>
<!-- 辅助点 -->
<g id="points"></g>
</svg>
<!-- 跳动的小球 -->
<div class="ball" id="ball"></div>
<div class="value-display" id="valueDisplay">0</div>
</div>
<div class="controls">
<button onclick="startAnimation()">🚀 开始动画</button>
<button onclick="pauseAnimation()">⏸️ 暂停</button>
<button onclick="resetAnimation()">🔄 重置</button>
<button onclick="changeSpeed()">⚡ 切换速度</button>
</div>
<div class="info">
<p>💡 小球会沿着数据轨迹移动,实时显示当前数值</p>
</div>
</div>
<script>
let animationRunning = false;
let animationSpeed = 50; // 毫秒
let currentIndex = 0;
let dataPoints = [];
let pathData = '';
let animationId;
// 生成模拟数据
function generateData() {
dataPoints = [];
const baseValue = 200;
let currentValue = baseValue;
for (let i = 0; i < 200; i++) {
// 生成随机波动数据
const change = (Math.random() - 0.5) * 40;
currentValue += change;
// 限制范围
currentValue = Math.max(50, Math.min(350, currentValue));
dataPoints.push({
x: i * 5, // x轴间距
y: 400 - currentValue, // y轴翻转
value: Math.round(currentValue)
});
}
}
// 更新SVG路径
function updatePath() {
if (currentIndex < 2) return;
pathData = `M ${dataPoints[0].x} ${dataPoints[0].y}`;
for (let i = 1; i <= currentIndex; i++) {
pathData += ` L ${dataPoints[i].x} ${dataPoints[i].y}`;
}
document.getElementById('mainPath').setAttribute('d', pathData);
// 添加轨迹点
const pointsGroup = document.getElementById('points');
if (currentIndex < dataPoints.length) {
const point = dataPoints[currentIndex];
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('cx', point.x);
circle.setAttribute('cy', point.y);
circle.setAttribute('r', '2');
circle.setAttribute('fill', 'rgba(255, 255, 255, 0.6)');
pointsGroup.appendChild(circle);
}
}
// 更新小球位置
function updateBall() {
if (currentIndex >= dataPoints.length) {
pauseAnimation();
return;
}
const point = dataPoints[currentIndex];
const ball = document.getElementById('ball');
const valueDisplay = document.getElementById('valueDisplay');
// 计算在容器中的实际位置
const container = document.getElementById('chartContainer');
const rect = container.getBoundingClientRect();
const svgWidth = 1000;
const svgHeight = 400;
const actualX = (point.x / svgWidth) * rect.width;
const actualY = (point.y / svgHeight) * rect.height;
ball.style.left = actualX + 'px';
ball.style.top = actualY + 'px';
valueDisplay.style.left = actualX + 'px';
valueDisplay.style.top = actualY + 'px';
valueDisplay.textContent = point.value;
// 自动滚动视图保持小球在视线内
const scrollThreshold = rect.width * 0.8;
if (actualX > scrollThreshold) {
const svg = document.getElementById('chart');
const newViewBox = `${point.x - 800} 0 1000 400`;
svg.setAttribute('viewBox', newViewBox);
}
}
// 动画循环
function animate() {
if (!animationRunning) return;
updatePath();
updateBall();
currentIndex++;
if (currentIndex < dataPoints.length) {
animationId = setTimeout(animate, animationSpeed);
} else {
pauseAnimation();
}
}
// 开始动画
function startAnimation() {
if (dataPoints.length === 0) {
generateData();
}
animationRunning = true;
animate();
}
// 暂停动画
function pauseAnimation() {
animationRunning = false;
if (animationId) {
clearTimeout(animationId);
}
}
// 重置动画
function resetAnimation() {
pauseAnimation();
currentIndex = 0;
document.getElementById('mainPath').setAttribute('d', '');
document.getElementById('points').innerHTML = '';
document.getElementById('chart').setAttribute('viewBox', '0 0 1000 400');
const ball = document.getElementById('ball');
const valueDisplay = document.getElementById('valueDisplay');
ball.style.left = '0px';
ball.style.top = '200px';
valueDisplay.style.left = '0px';
valueDisplay.style.top = '200px';
valueDisplay.textContent = '0';
generateData();
}
// 切换速度
function changeSpeed() {
const speeds = [100, 50, 25, 10];
const currentSpeedIndex = speeds.indexOf(animationSpeed);
const nextSpeedIndex = (currentSpeedIndex + 1) % speeds.length;
animationSpeed = speeds[nextSpeedIndex];
const speedNames = ['慢速', '正常', '快速', '极速'];
alert(`切换到 ${speedNames[nextSpeedIndex]} 模式`);
}
// 初始化
window.addEventListener('load', function() {
generateData();
// 处理窗口大小变化
window.addEventListener('resize', function() {
if (currentIndex > 0) {
updateBall();
}
});
});
</script>
</body>
</html>
@@ -0,0 +1,372 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动态折线图 - 跳动小球轨迹</title>
<style>
body {
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-family: 'Arial', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.container {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
max-width: 1000px;
width: 100%;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
font-size: 2.5em;
background: linear-gradient(45deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.chart-container {
position: relative;
width: 100%;
height: 400px;
background: #f8f9fa;
border-radius: 15px;
overflow: hidden;
box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.1);
}
canvas {
width: 100%;
height: 100%;
display: block;
}
.controls {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 20px;
flex-wrap: wrap;
}
button {
padding: 12px 24px;
border: none;
border-radius: 25px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.start-btn {
background: linear-gradient(45deg, #4CAF50, #45a049);
color: white;
}
.start-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(76, 175, 80, 0.3);
}
.stop-btn {
background: linear-gradient(45deg, #f44336, #da190b);
color: white;
}
.stop-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(244, 67, 54, 0.3);
}
.reset-btn {
background: linear-gradient(45deg, #2196F3, #0b7dda);
color: white;
}
.reset-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(33, 150, 243, 0.3);
}
.info {
margin-top: 20px;
padding: 15px;
background: rgba(103, 126, 234, 0.1);
border-radius: 10px;
text-align: center;
color: #333;
}
.current-value {
font-size: 1.2em;
font-weight: bold;
color: #667eea;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>🎯 动态折线图</h1>
<div class="chart-container">
<canvas id="chartCanvas"></canvas>
</div>
<div class="controls">
<button class="start-btn" onclick="startAnimation()">开始动画</button>
<button class="stop-btn" onclick="stopAnimation()">停止动画</button>
<button class="reset-btn" onclick="resetChart()">重置图表</button>
</div>
<div class="info">
<div>小球位置固定在画布右侧,数据值实时更新</div>
<div class="current-value" id="currentValue">当前值: 0</div>
</div>
</div>
<script>
const canvas = document.getElementById('chartCanvas');
const ctx = canvas.getContext('2d');
const currentValueEl = document.getElementById('currentValue');
// 设置canvas实际尺寸
function resizeCanvas() {
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * window.devicePixelRatio;
canvas.height = rect.height * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
}
// 初始化
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// 图表配置
const config = {
ballRadius: 8,
ballPosition: 0.85, // 小球在画布右侧85%的位置
trailWidth: 3,
gridColor: '#e0e0e0',
ballColor: '#ff4757',
trailColor: '#667eea',
ballGlowColor: 'rgba(255, 71, 87, 0.3)',
animationSpeed: 50, // ms
dataRange: { min: 10, max: 90 }, // 数据范围百分比
scrollSpeed: 2 // 画布滚动速度
};
// 数据存储
let dataPoints = [];
let animationId = null;
let isRunning = false;
let time = 0;
let currentValue = 50;
// 生成模拟数据
function generateNextValue() {
// 使用正弦波加随机波动生成更自然的数据
const baseValue = 50 + Math.sin(time * 0.02) * 20;
const randomFluctuation = (Math.random() - 0.5) * 10;
currentValue = Math.max(config.dataRange.min,
Math.min(config.dataRange.max, baseValue + randomFluctuation));
time++;
return currentValue;
}
// 绘制网格
function drawGrid() {
const width = canvas.width / window.devicePixelRatio;
const height = canvas.height / window.devicePixelRatio;
ctx.strokeStyle = config.gridColor;
ctx.lineWidth = 1;
ctx.setLineDash([5, 5]);
// 水平网格线
for (let i = 0; i <= 10; i++) {
const y = (height / 10) * i;
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
}
// 垂直网格线
for (let i = 0; i <= 20; i++) {
const x = (width / 20) * i;
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
ctx.stroke();
}
ctx.setLineDash([]);
}
// 绘制轨迹线
function drawTrail() {
if (dataPoints.length < 2) return;
const width = canvas.width / window.devicePixelRatio;
const height = canvas.height / window.devicePixelRatio;
ctx.strokeStyle = config.trailColor;
ctx.lineWidth = config.trailWidth;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// 添加阴影效果
ctx.shadowColor = 'rgba(102, 126, 234, 0.3)';
ctx.shadowBlur = 8;
ctx.shadowOffsetY = 2;
ctx.beginPath();
for (let i = 0; i < dataPoints.length; i++) {
const point = dataPoints[i];
const x = width * config.ballPosition - (dataPoints.length - 1 - i) * config.scrollSpeed;
const y = height - (point / 100) * height;
if (x < 0) continue; // 不绘制超出左边界的点
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
// 重置阴影
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetY = 0;
}
// 绘制跳动的小球
function drawBall() {
const width = canvas.width / window.devicePixelRatio;
const height = canvas.height / window.devicePixelRatio;
const ballX = width * config.ballPosition;
const ballY = height - (currentValue / 100) * height;
// 小球跳动效果
const bounce = Math.sin(time * 0.3) * 2;
const finalBallY = ballY + bounce;
// 绘制小球光晕
const gradient = ctx.createRadialGradient(ballX, finalBallY, 0, ballX, finalBallY, config.ballRadius * 3);
gradient.addColorStop(0, config.ballGlowColor);
gradient.addColorStop(1, 'transparent');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(ballX, finalBallY, config.ballRadius * 3, 0, Math.PI * 2);
ctx.fill();
// 绘制小球主体
ctx.fillStyle = config.ballColor;
ctx.beginPath();
ctx.arc(ballX, finalBallY, config.ballRadius, 0, Math.PI * 2);
ctx.fill();
// 小球高光
ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
ctx.beginPath();
ctx.arc(ballX - 2, finalBallY - 2, config.ballRadius * 0.4, 0, Math.PI * 2);
ctx.fill();
}
// 主渲染函数
function render() {
const width = canvas.width / window.devicePixelRatio;
const height = canvas.height / window.devicePixelRatio;
// 清空画布
ctx.clearRect(0, 0, width, height);
// 绘制背景渐变
const bgGradient = ctx.createLinearGradient(0, 0, 0, height);
bgGradient.addColorStop(0, '#f8f9fa');
bgGradient.addColorStop(1, '#e9ecef');
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, width, height);
// 绘制网格
drawGrid();
// 绘制轨迹
drawTrail();
// 绘制小球
drawBall();
// 更新当前值显示
currentValueEl.textContent = `当前值: ${currentValue.toFixed(1)}`;
}
// 动画循环
function animate() {
if (!isRunning) return;
// 生成新数据点
const newValue = generateNextValue();
dataPoints.push(newValue);
// 限制数据点数量,移除画布外的点
const maxPoints = Math.ceil(canvas.width / window.devicePixelRatio / config.scrollSpeed) + 10;
if (dataPoints.length > maxPoints) {
dataPoints.shift();
}
render();
animationId = setTimeout(animate, config.animationSpeed);
}
// 控制函数
function startAnimation() {
if (isRunning) return;
isRunning = true;
animate();
}
function stopAnimation() {
isRunning = false;
if (animationId) {
clearTimeout(animationId);
animationId = null;
}
}
function resetChart() {
stopAnimation();
dataPoints = [];
time = 0;
currentValue = 50;
render();
}
// 初始渲染
render();
// 自动开始演示
setTimeout(startAnimation, 1000);
</script>
</body>
</html>
@@ -0,0 +1,626 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网速记录仪</title>
<style>
body {
margin: 0;
padding: 20px;
background: #0a0a0a;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.container {
width: 90%;
max-width: 1200px;
margin: 20px auto;
}
.title {
text-align: center;
margin-bottom: 30px;
font-size: 24px;
color: #00ff88;
text-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
}
.controls {
background: rgba(0, 255, 136, 0.05);
border: 1px solid rgba(0, 255, 136, 0.2);
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
display: flex;
gap: 30px;
align-items: center;
flex-wrap: wrap;
}
.control-group {
display: flex;
align-items: center;
gap: 10px;
}
.control-label {
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
min-width: 80px;
}
.slider {
width: 150px;
height: 6px;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
outline: none;
-webkit-appearance: none;
appearance: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
background: #00ff88;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
}
.slider::-moz-range-thumb {
width: 16px;
height: 16px;
background: #00ff88;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
}
.slider-value {
font-size: 14px;
color: #00ff88;
min-width: 40px;
text-align: right;
}
.button {
background: rgba(0, 255, 136, 0.2);
border: 1px solid rgba(0, 255, 136, 0.5);
color: #00ff88;
padding: 8px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
}
.button:hover {
background: rgba(0, 255, 136, 0.3);
box-shadow: 0 0 15px rgba(0, 255, 136, 0.5);
}
.button:active {
transform: scale(0.95);
}
.recorder-container {
position: relative;
width: 100%;
height: 400px;
background: linear-gradient(180deg,
rgba(0, 255, 136, 0.03) 0%,
rgba(0, 255, 136, 0.01) 50%,
rgba(0, 255, 136, 0.03) 100%);
border: 1px solid rgba(0, 255, 136, 0.2);
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 30px rgba(0, 255, 136, 0.1);
}
.grid {
position: absolute;
width: 100%;
height: 100%;
background-image:
repeating-linear-gradient(0deg,
rgba(0, 255, 136, 0.05) 0px,
transparent 1px,
transparent 39px,
rgba(0, 255, 136, 0.05) 40px),
repeating-linear-gradient(90deg,
rgba(0, 255, 136, 0.05) 0px,
transparent 1px,
transparent 39px,
rgba(0, 255, 136, 0.05) 40px);
}
.canvas-container {
position: relative;
width: 100%;
height: 100%;
}
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.indicator {
position: absolute;
right: 30px;
width: 20px;
height: 20px;
border-radius: 50%;
box-shadow: inset 0 0 10px rgba(255, 255, 255, 0.5);
z-index: 10;
transition: top 0.1s ease-out;
}
.indicator.download {
background: #00ff88;
box-shadow:
0 0 20px #00ff88,
0 0 40px #00ff88,
inset 0 0 10px rgba(255, 255, 255, 0.5);
animation: pulse-green 1.5s ease-in-out infinite;
}
.indicator.upload {
background: #00d4ff;
box-shadow:
0 0 20px #00d4ff,
0 0 40px #00d4ff,
inset 0 0 10px rgba(255, 255, 255, 0.5);
animation: pulse-blue 1.5s ease-in-out infinite;
right: 60px;
}
@keyframes pulse-green {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.2);
opacity: 0.8;
}
}
@keyframes pulse-blue {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.2);
opacity: 0.8;
}
}
.stats {
margin-top: 30px;
display: flex;
justify-content: space-around;
gap: 20px;
}
.stat-box {
background: rgba(0, 255, 136, 0.1);
border: 1px solid rgba(0, 255, 136, 0.3);
border-radius: 8px;
padding: 20px;
text-align: center;
flex: 1;
}
.stat-box.upload {
background: rgba(0, 212, 255, 0.1);
border-color: rgba(0, 212, 255, 0.3);
}
.stat-label {
font-size: 14px;
color: rgba(255, 255, 255, 0.7);
margin-bottom: 5px;
}
.stat-value {
font-size: 28px;
font-weight: bold;
text-shadow: 0 0 10px currentColor;
}
.stat-value.download {
color: #00ff88;
}
.stat-value.upload {
color: #00d4ff;
}
.unit {
font-size: 16px;
color: rgba(255, 255, 255, 0.7);
margin-left: 5px;
}
.legend {
display: flex;
gap: 30px;
justify-content: center;
margin-top: 10px;
}
.legend-item {
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
}
.legend-dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
.legend-dot.download {
background: #00ff88;
box-shadow: 0 0 10px #00ff88;
}
.legend-dot.upload {
background: #00d4ff;
box-shadow: 0 0 10px #00d4ff;
}
</style>
</head>
<body>
<div class="container">
<h1 class="title">网络速度监测仪</h1>
<div class="controls">
<div class="control-group">
<label class="control-label">动画速度:</label>
<input type="range" class="slider" id="animSpeed" min="0.5" max="3" step="0.1" value="1">
<span class="slider-value" id="animSpeedValue">1.0</span>
</div>
<div class="control-group">
<label class="control-label">波动频率:</label>
<input type="range" class="slider" id="fluctuation" min="0.1" max="2" step="0.1" value="1">
<span class="slider-value" id="fluctuationValue">1.0</span>
</div>
<div class="control-group">
<button class="button" id="pauseBtn">暂停</button>
<button class="button" id="clearBtn">清除轨迹</button>
</div>
</div>
<div class="recorder-container">
<div class="grid"></div>
<div class="canvas-container">
<canvas id="speedCanvas"></canvas>
<div class="indicator download" id="downloadIndicator"></div>
<div class="indicator upload" id="uploadIndicator"></div>
</div>
</div>
<div class="legend">
<div class="legend-item">
<div class="legend-dot download"></div>
<span>下载速度</span>
</div>
<div class="legend-item">
<div class="legend-dot upload"></div>
<span>上传速度</span>
</div>
</div>
<div class="stats">
<div class="stat-box">
<div class="stat-label">当前下载速度</div>
<div class="stat-value download">
<span id="currentDownload">0</span>
<span class="unit">Mbps</span>
</div>
</div>
<div class="stat-box upload">
<div class="stat-label">当前上传速度</div>
<div class="stat-value upload">
<span id="currentUpload">0</span>
<span class="unit">Mbps</span>
</div>
</div>
<div class="stat-box">
<div class="stat-label">平均下载速度</div>
<div class="stat-value download">
<span id="avgDownload">0</span>
<span class="unit">Mbps</span>
</div>
</div>
<div class="stat-box upload">
<div class="stat-label">平均上传速度</div>
<div class="stat-value upload">
<span id="avgUpload">0</span>
<span class="unit">Mbps</span>
</div>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('speedCanvas');
const ctx = canvas.getContext('2d');
const downloadIndicator = document.getElementById('downloadIndicator');
const uploadIndicator = document.getElementById('uploadIndicator');
// 控制元素
const animSpeedSlider = document.getElementById('animSpeed');
const animSpeedValue = document.getElementById('animSpeedValue');
const fluctuationSlider = document.getElementById('fluctuation');
const fluctuationValue = document.getElementById('fluctuationValue');
const pauseBtn = document.getElementById('pauseBtn');
const clearBtn = document.getElementById('clearBtn');
// 设置画布大小
function resizeCanvas() {
const container = canvas.parentElement;
canvas.width = container.offsetWidth;
canvas.height = container.offsetHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// 数据存储
const maxDataPoints = 200;
const downloadData = [];
const uploadData = [];
let maxSpeed = 100;
// 当前速度和目标速度
let currentDownload = 50;
let targetDownload = 50;
let currentUpload = 30;
let targetUpload = 30;
// 动画参数
let animationTime = 0;
let dataUpdateCounter = 0;
let speedUpdateCounter = 0;
let animationSpeed = 1;
let fluctuationRate = 1;
let isPaused = false;
let animationId = null;
// 更新控制值
animSpeedSlider.addEventListener('input', (e) => {
animationSpeed = parseFloat(e.target.value);
animSpeedValue.textContent = animationSpeed.toFixed(1);
});
fluctuationSlider.addEventListener('input', (e) => {
fluctuationRate = parseFloat(e.target.value);
fluctuationValue.textContent = fluctuationRate.toFixed(1);
});
// 暂停/继续按钮
pauseBtn.addEventListener('click', () => {
isPaused = !isPaused;
pauseBtn.textContent = isPaused ? '继续' : '暂停';
if (!isPaused && !animationId) {
animate();
}
});
// 清除轨迹按钮
clearBtn.addEventListener('click', () => {
downloadData.length = 0;
uploadData.length = 0;
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
// 生成随机速度数据
function generateSpeed() {
// 下载速度波动(通常较高)
const downloadChange = (Math.random() - 0.5) * 20 * fluctuationRate;
targetDownload = Math.max(20, Math.min(95, targetDownload + downloadChange));
// 上传速度波动(通常较低)
const uploadChange = (Math.random() - 0.5) * 15 * fluctuationRate;
targetUpload = Math.max(10, Math.min(60, targetUpload + uploadChange));
// 偶尔出现下载峰值
if (Math.random() < 0.05 * fluctuationRate) {
targetDownload = Math.random() * 20 + 75;
}
// 偶尔出现上传峰值
if (Math.random() < 0.05 * fluctuationRate) {
targetUpload = Math.random() * 15 + 45;
}
// 偶尔同时降低
if (Math.random() < 0.03 * fluctuationRate) {
targetDownload = Math.random() * 20 + 20;
targetUpload = Math.random() * 10 + 10;
}
}
// 平滑插值
function lerp(start, end, t) {
return start + (end - start) * t;
}
// 更新统计数据
function updateStats() {
document.getElementById('currentDownload').textContent = Math.round(currentDownload);
document.getElementById('currentUpload').textContent = Math.round(currentUpload);
if (downloadData.length > 0) {
const avgDown = downloadData.reduce((a, b) => a + b, 0) / downloadData.length;
document.getElementById('avgDownload').textContent = Math.round(avgDown);
}
if (uploadData.length > 0) {
const avgUp = uploadData.reduce((a, b) => a + b, 0) / uploadData.length;
document.getElementById('avgUpload').textContent = Math.round(avgUp);
}
}
// 绘制单条曲线
function drawCurve(data, color, shadowColor, indicatorOffset) {
if (data.length < 2) return;
// 设置渐变
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop(0, `${color}22`);
gradient.addColorStop(0.5, `${color}99`);
gradient.addColorStop(1, color);
ctx.strokeStyle = gradient;
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// 添加光晕效果
ctx.shadowColor = shadowColor;
ctx.shadowBlur = 10;
ctx.beginPath();
// 计算每个数据点的X位置
const pointSpacing = 2 * animationSpeed;
const endX = canvas.width - 30 - indicatorOffset; // 终点位置与指示器对齐
const startX = endX - (data.length - 1) * pointSpacing;
// 绘制平滑曲线
for (let i = 0; i < data.length; i++) {
const x = startX + i * pointSpacing;
const y = canvas.height - (data[i] / maxSpeed) * (canvas.height - 40) - 20;
if (i === 0) {
ctx.moveTo(x, y);
} else {
// 使用贝塞尔曲线创建平滑效果
const prevX = startX + (i - 1) * pointSpacing;
const prevY = canvas.height - (data[i - 1] / maxSpeed) * (canvas.height - 40) - 20;
const midX = (prevX + x) / 2;
const midY = (prevY + y) / 2;
ctx.quadraticCurveTo(prevX, prevY, midX, midY);
}
}
// 连接到最后一个点
if (data.length > 0) {
const lastX = endX;
const lastY = canvas.height - (data[data.length - 1] / maxSpeed) * (canvas.height - 40) - 20;
ctx.lineTo(lastX, lastY);
}
ctx.stroke();
// 绘制发光的尾迹
ctx.strokeStyle = `${color}55`;
ctx.lineWidth = 8;
ctx.shadowBlur = 20;
ctx.stroke();
}
// 绘制所有曲线
function drawCurves() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 先绘制上传曲线(在后面)
ctx.save();
drawCurve(uploadData, '#00d4ff', '#00d4ff', 30); // 偏移30像素,对应upload indicator位置
ctx.restore();
// 再绘制下载曲线(在前面)
ctx.save();
drawCurve(downloadData, '#00ff88', '#00ff88', 0); // 偏移0像素,对应download indicator位置
ctx.restore();
}
// 更新指示器位置
function updateIndicators() {
const downloadY = canvas.height - (currentDownload / maxSpeed) * (canvas.height - 40) - 20;
downloadIndicator.style.top = (downloadY - 10) + 'px';
const uploadY = canvas.height - (currentUpload / maxSpeed) * (canvas.height - 40) - 20;
uploadIndicator.style.top = (uploadY - 10) + 'px';
}
// 动画循环
function animate() {
if (isPaused) {
animationId = null;
return;
}
animationTime++;
// 平滑过渡到目标速度 - 使用固定的插值速度
currentDownload = lerp(currentDownload, targetDownload, 0.1);
currentUpload = lerp(currentUpload, targetUpload, 0.1);
// 数据更新计数器 - 基于动画速度
dataUpdateCounter += animationSpeed;
// 当计数器达到10时,添加新数据点
if (dataUpdateCounter >= 10) {
dataUpdateCounter = 0;
downloadData.push(currentDownload);
uploadData.push(currentUpload);
// 限制数据点数量
if (downloadData.length > maxDataPoints) {
downloadData.shift();
}
if (uploadData.length > maxDataPoints) {
uploadData.shift();
}
}
// 速度变化计数器 - 基于波动频率
speedUpdateCounter += fluctuationRate;
// 根据波动频率调整生成新目标的频率
if (speedUpdateCounter >= 60) {
speedUpdateCounter = 0;
generateSpeed();
}
// 更新显示
drawCurves();
updateIndicators();
updateStats();
animationId = requestAnimationFrame(animate);
}
// 初始化数据
for (let i = 0; i < 30; i++) {
downloadData.push(50 + Math.random() * 20 - 10);
uploadData.push(30 + Math.random() * 15 - 7.5);
}
// 开始动画
animate();
</script>
</body>
</html>
@@ -0,0 +1,345 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网速记录仪</title>
<style>
body {
margin: 0;
padding: 20px;
background: #0a0a0a;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.container {
width: 90%;
max-width: 1000px;
margin: 20px auto;
}
.title {
text-align: center;
margin-bottom: 30px;
font-size: 24px;
color: #00ff88;
text-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
}
.recorder-container {
position: relative;
width: 100%;
height: 400px;
background: linear-gradient(180deg,
rgba(0, 255, 136, 0.03) 0%,
rgba(0, 255, 136, 0.01) 50%,
rgba(0, 255, 136, 0.03) 100%);
border: 1px solid rgba(0, 255, 136, 0.2);
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 30px rgba(0, 255, 136, 0.1);
}
.grid {
position: absolute;
width: 100%;
height: 100%;
background-image:
repeating-linear-gradient(0deg,
rgba(0, 255, 136, 0.05) 0px,
transparent 1px,
transparent 39px,
rgba(0, 255, 136, 0.05) 40px),
repeating-linear-gradient(90deg,
rgba(0, 255, 136, 0.05) 0px,
transparent 1px,
transparent 39px,
rgba(0, 255, 136, 0.05) 40px);
}
.canvas-container {
position: relative;
width: 100%;
height: 100%;
}
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.indicator {
position: absolute;
right: 30px;
width: 20px;
height: 20px;
background: #00ff88;
border-radius: 50%;
box-shadow:
0 0 20px #00ff88,
0 0 40px #00ff88,
inset 0 0 10px rgba(255, 255, 255, 0.5);
animation: pulse 1.5s ease-in-out infinite;
z-index: 10;
transition: top 0.1s ease-out;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.2);
opacity: 0.8;
}
}
.stats {
margin-top: 30px;
display: flex;
justify-content: space-around;
gap: 20px;
}
.stat-box {
background: rgba(0, 255, 136, 0.1);
border: 1px solid rgba(0, 255, 136, 0.3);
border-radius: 8px;
padding: 20px;
text-align: center;
flex: 1;
}
.stat-label {
font-size: 14px;
color: rgba(255, 255, 255, 0.7);
margin-bottom: 5px;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #00ff88;
text-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
}
.unit {
font-size: 16px;
color: rgba(255, 255, 255, 0.7);
margin-left: 5px;
}
</style>
</head>
<body>
<div class="container">
<h1 class="title">网络速度监测仪</h1>
<div class="recorder-container">
<div class="grid"></div>
<div class="canvas-container">
<canvas id="speedCanvas"></canvas>
<div class="indicator" id="indicator"></div>
</div>
</div>
<div class="stats">
<div class="stat-box">
<div class="stat-label">当前速度</div>
<div class="stat-value">
<span id="currentSpeed">0</span>
<span class="unit">Mbps</span>
</div>
</div>
<div class="stat-box">
<div class="stat-label">平均速度</div>
<div class="stat-value">
<span id="avgSpeed">0</span>
<span class="unit">Mbps</span>
</div>
</div>
<div class="stat-box">
<div class="stat-label">峰值速度</div>
<div class="stat-value">
<span id="maxSpeed">0</span>
<span class="unit">Mbps</span>
</div>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('speedCanvas');
const ctx = canvas.getContext('2d');
const indicator = document.getElementById('indicator');
// 设置画布大小
function resizeCanvas() {
const container = canvas.parentElement;
canvas.width = container.offsetWidth;
canvas.height = container.offsetHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// 数据存储
const maxDataPoints = 200;
const speedData = [];
let maxSpeed = 100; // 最大速度范围
let currentSpeed = 50;
let targetSpeed = 50;
// 动画参数
let animationTime = 0;
const scrollSpeed = 2; // 每帧移动的像素数
// 生成随机网速数据
function generateSpeed() {
// 模拟网速的自然波动
const change = (Math.random() - 0.5) * 20;
targetSpeed = Math.max(5, Math.min(95, targetSpeed + change));
// 偶尔出现速度峰值
if (Math.random() < 0.05) {
targetSpeed = Math.random() * 30 + 70;
}
// 偶尔出现速度低谷
if (Math.random() < 0.05) {
targetSpeed = Math.random() * 20 + 10;
}
}
// 平滑插值
function lerp(start, end, t) {
return start + (end - start) * t;
}
// 更新统计数据
function updateStats() {
document.getElementById('currentSpeed').textContent = Math.round(currentSpeed);
if (speedData.length > 0) {
const avg = speedData.reduce((a, b) => a + b, 0) / speedData.length;
document.getElementById('avgSpeed').textContent = Math.round(avg);
const max = Math.max(...speedData);
document.getElementById('maxSpeed').textContent = Math.round(max);
}
}
// 绘制曲线
function drawCurve() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (speedData.length < 2) return;
// 设置渐变
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop(0, 'rgba(0, 255, 136, 0.1)');
gradient.addColorStop(0.5, 'rgba(0, 255, 136, 0.6)');
gradient.addColorStop(1, 'rgba(0, 255, 136, 1)');
ctx.strokeStyle = gradient;
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// 添加光晕效果
ctx.shadowColor = '#00ff88';
ctx.shadowBlur = 10;
ctx.beginPath();
// 计算每个数据点的X位置
const pointSpacing = scrollSpeed;
const startX = canvas.width - 50 - (speedData.length - 1) * pointSpacing;
// 绘制平滑曲线
for (let i = 0; i < speedData.length; i++) {
const x = startX + i * pointSpacing;
const y = canvas.height - (speedData[i] / maxSpeed) * (canvas.height - 40) - 20;
if (i === 0) {
ctx.moveTo(x, y);
} else {
// 使用贝塞尔曲线创建平滑效果
const prevX = startX + (i - 1) * pointSpacing;
const prevY = canvas.height - (speedData[i - 1] / maxSpeed) * (canvas.height - 40) - 20;
const midX = (prevX + x) / 2;
const midY = (prevY + y) / 2;
ctx.quadraticCurveTo(prevX, prevY, midX, midY);
}
}
// 连接到最后一个点
if (speedData.length > 0) {
const lastX = canvas.width - 50;
const lastY = canvas.height - (speedData[speedData.length - 1] / maxSpeed) * (canvas.height - 40) - 20;
ctx.lineTo(lastX, lastY);
}
ctx.stroke();
// 绘制发光的尾迹
ctx.strokeStyle = 'rgba(0, 255, 136, 0.3)';
ctx.lineWidth = 8;
ctx.shadowBlur = 20;
ctx.stroke();
}
// 更新指示器位置
function updateIndicator() {
const y = canvas.height - (currentSpeed / maxSpeed) * (canvas.height - 40) - 20;
indicator.style.top = (y - 10) + 'px';
}
// 动画循环
function animate() {
animationTime++;
// 平滑过渡到目标速度
currentSpeed = lerp(currentSpeed, targetSpeed, 0.1);
// 每10帧添加一个新数据点
if (animationTime % 10 === 0) {
speedData.push(currentSpeed);
// 限制数据点数量
if (speedData.length > maxDataPoints) {
speedData.shift();
}
}
// 每60帧生成新的目标速度
if (animationTime % 60 === 0) {
generateSpeed();
}
// 更新显示
drawCurve();
updateIndicator();
updateStats();
requestAnimationFrame(animate);
}
// 初始化数据
for (let i = 0; i < 50; i++) {
speedData.push(50 + Math.random() * 20 - 10);
}
// 开始动画
animate();
</script>
</body>
</html>
@@ -0,0 +1,677 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网速监测仪 - 上传下载双线版</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #0a0e1b;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
}
.container {
width: 90%;
max-width: 1200px;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.header h1 {
font-size: 2.5em;
background: linear-gradient(135deg, #00ff88 0%, #00a8ff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 10px;
}
.monitor-container {
position: relative;
width: 100%;
height: 500px;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 15px;
overflow: hidden;
backdrop-filter: blur(10px);
}
.canvas-wrapper {
position: relative;
width: 100%;
height: 100%;
}
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#gridCanvas {
z-index: 1;
}
#chartCanvas {
z-index: 2;
}
.ball {
position: absolute;
width: 16px;
height: 16px;
border-radius: 50%;
transform: translate(-50%, -50%);
z-index: 10;
transition: none; /* 移除过渡效果,使用实时位置 */
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% {
transform: translate(-50%, -50%) scale(1);
}
50% {
transform: translate(-50%, -50%) scale(1.2);
}
}
.ball-download {
background: #00ff88;
box-shadow:
0 0 20px #00ff88,
0 0 40px #00ff88,
inset 0 0 8px rgba(255, 255, 255, 0.6);
}
.ball-upload {
background: #00a8ff;
box-shadow:
0 0 20px #00a8ff,
0 0 40px #00a8ff,
inset 0 0 8px rgba(255, 255, 255, 0.6);
}
.ball::before {
content: '';
position: absolute;
top: 20%;
left: 20%;
width: 40%;
height: 40%;
background: rgba(255, 255, 255, 0.8);
border-radius: 50%;
filter: blur(2px);
}
.stats-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-top: 30px;
}
.stat-group {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 20px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
}
.stat-group h3 {
grid-column: 1 / -1;
margin-bottom: 10px;
font-size: 1.2em;
display: flex;
align-items: center;
gap: 10px;
}
.stat-group.download h3 {
color: #00ff88;
}
.stat-group.upload h3 {
color: #00a8ff;
}
.stat-box {
text-align: center;
}
.stat-label {
font-size: 0.8em;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 5px;
}
.stat-value {
font-size: 1.5em;
font-weight: bold;
}
.download .stat-value {
color: #00ff88;
text-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
}
.upload .stat-value {
color: #00a8ff;
text-shadow: 0 0 10px rgba(0, 168, 255, 0.5);
}
.controls {
margin-top: 30px;
display: flex;
justify-content: center;
gap: 15px;
flex-wrap: wrap;
}
.control-group {
display: flex;
align-items: center;
gap: 10px;
background: rgba(255, 255, 255, 0.05);
padding: 10px 20px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.control-group label {
font-size: 0.9em;
color: rgba(255, 255, 255, 0.8);
}
.control-group input[type="range"] {
width: 120px;
height: 4px;
background: rgba(255, 255, 255, 0.2);
outline: none;
-webkit-appearance: none;
border-radius: 2px;
}
.control-group input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
background: #fff;
border-radius: 50%;
cursor: pointer;
}
.control-value {
min-width: 40px;
text-align: right;
font-weight: bold;
color: #00ff88;
}
.legend {
position: absolute;
top: 20px;
right: 20px;
display: flex;
flex-direction: column;
gap: 10px;
background: rgba(0, 0, 0, 0.5);
padding: 15px;
border-radius: 8px;
backdrop-filter: blur(10px);
}
.legend-item {
display: flex;
align-items: center;
gap: 10px;
}
.legend-color {
width: 20px;
height: 3px;
border-radius: 2px;
}
.legend-color.download {
background: #00ff88;
box-shadow: 0 0 10px #00ff88;
}
.legend-color.upload {
background: #00a8ff;
box-shadow: 0 0 10px #00a8ff;
}
button {
background: linear-gradient(135deg, #00ff88 0%, #00a8ff 100%);
color: #0a0e1b;
border: none;
padding: 12px 24px;
border-radius: 8px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(0, 255, 136, 0.3);
}
button:active {
transform: translateY(0);
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>网络速度监测仪</h1>
<p style="color: rgba(255, 255, 255, 0.6);">实时监测上传下载速度</p>
</div>
<div class="monitor-container">
<div class="canvas-wrapper">
<canvas id="gridCanvas"></canvas>
<canvas id="chartCanvas"></canvas>
<div class="ball ball-download" id="ballDownload"></div>
<div class="ball ball-upload" id="ballUpload"></div>
</div>
<div class="legend">
<div class="legend-item">
<div class="legend-color download"></div>
<span>下载速度</span>
</div>
<div class="legend-item">
<div class="legend-color upload"></div>
<span>上传速度</span>
</div>
</div>
</div>
<div class="stats-container">
<div class="stat-group download">
<h3>📥 下载速度</h3>
<div class="stat-box">
<div class="stat-label">当前</div>
<div class="stat-value"><span id="downloadCurrent">0</span> Mbps</div>
</div>
<div class="stat-box">
<div class="stat-label">平均</div>
<div class="stat-value"><span id="downloadAvg">0</span> Mbps</div>
</div>
<div class="stat-box">
<div class="stat-label">峰值</div>
<div class="stat-value"><span id="downloadMax">0</span> Mbps</div>
</div>
</div>
<div class="stat-group upload">
<h3>📤 上传速度</h3>
<div class="stat-box">
<div class="stat-label">当前</div>
<div class="stat-value"><span id="uploadCurrent">0</span> Mbps</div>
</div>
<div class="stat-box">
<div class="stat-label">平均</div>
<div class="stat-value"><span id="uploadAvg">0</span> Mbps</div>
</div>
<div class="stat-box">
<div class="stat-label">峰值</div>
<div class="stat-value"><span id="uploadMax">0</span> Mbps</div>
</div>
</div>
</div>
<div class="controls">
<div class="control-group">
<label>动画速度:</label>
<input type="range" id="speedControl" min="10" max="100" value="50">
<span class="control-value" id="speedValue">50</span>
</div>
<div class="control-group">
<label>数据平滑度:</label>
<input type="range" id="smoothControl" min="1" max="20" value="10">
<span class="control-value" id="smoothValue">10</span>
</div>
<button onclick="toggleAnimation()">暂停/继续</button>
<button onclick="resetData()">重置数据</button>
</div>
</div>
<script>
// 获取画布和上下文
const gridCanvas = document.getElementById('gridCanvas');
const chartCanvas = document.getElementById('chartCanvas');
const gridCtx = gridCanvas.getContext('2d');
const chartCtx = chartCanvas.getContext('2d');
// 获取小球元素
const ballDownload = document.getElementById('ballDownload');
const ballUpload = document.getElementById('ballUpload');
// 配置参数 - 必须在使用前定义
const config = {
maxDataPoints: 300,
animationSpeed: 50,
smoothness: 10,
maxSpeed: 100,
pixelsPerFrame: 2,
ballFixedX: 0.9, // 小球固定在90%的位置
colors: {
download: '#00ff88',
upload: '#00a8ff',
grid: 'rgba(255, 255, 255, 0.05)'
}
};
// 数据存储
const data = {
download: [],
upload: []
};
// 当前值和目标值
const current = {
download: 50,
upload: 30
};
const target = {
download: 50,
upload: 30
};
// 动画控制
let animationRunning = true;
let frameCount = 0;
// 设置画布大小
function resizeCanvas() {
const container = gridCanvas.parentElement;
const rect = container.getBoundingClientRect();
gridCanvas.width = rect.width;
gridCanvas.height = rect.height;
chartCanvas.width = rect.width;
chartCanvas.height = rect.height;
drawGrid(); // 重绘网格
}
// 绘制网格
function drawGrid() {
gridCtx.clearRect(0, 0, gridCanvas.width, gridCanvas.height);
gridCtx.strokeStyle = config.colors.grid;
gridCtx.lineWidth = 1;
// 水平线
const hLines = 10;
for (let i = 0; i <= hLines; i++) {
const y = (gridCanvas.height / hLines) * i;
gridCtx.beginPath();
gridCtx.moveTo(0, y);
gridCtx.lineTo(gridCanvas.width, y);
gridCtx.stroke();
}
// 垂直线
const vLines = 20;
for (let i = 0; i <= vLines; i++) {
const x = (gridCanvas.width / vLines) * i;
gridCtx.beginPath();
gridCtx.moveTo(x, 0);
gridCtx.lineTo(x, gridCanvas.height);
gridCtx.stroke();
}
}
// 生成随机速度
function generateRandomSpeed(current, type) {
const baseChange = (Math.random() - 0.5) * 15;
let newSpeed = current + baseChange;
// 添加一些特征
if (type === 'download') {
// 下载速度通常更高且更稳定
if (Math.random() < 0.02) newSpeed = Math.random() * 30 + 60;
if (Math.random() < 0.01) newSpeed = Math.random() * 10 + 10; // 偶尔低速
} else {
// 上传速度通常较低
if (Math.random() < 0.02) newSpeed = Math.random() * 20 + 40;
if (Math.random() < 0.01) newSpeed = Math.random() * 10 + 5; // 偶尔低速
}
return Math.max(5, Math.min(95, newSpeed));
}
// 平滑插值
function lerp(start, end, t) {
return start + (end - start) * t;
}
// 绘制曲线
function drawCurve(ctx, data, color, yOffset = 0) {
if (data.length < 2) return null;
ctx.save(); // 保存上下文状态
// 创建渐变色
const gradient = ctx.createLinearGradient(0, 0, chartCanvas.width, 0);
gradient.addColorStop(0, color + '33'); // 33是20%的透明度
gradient.addColorStop(0.5, color + '88'); // 88是53%的透明度
gradient.addColorStop(1, color);
ctx.strokeStyle = gradient;
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.shadowColor = color;
ctx.shadowBlur = 15;
ctx.beginPath();
const spacing = config.pixelsPerFrame;
const fixedX = chartCanvas.width * config.ballFixedX;
// 计算起始X位置,使最后一个点在固定位置
const startX = fixedX - (data.length - 1) * spacing;
let lastPoint = null;
// 绘制平滑曲线
for (let i = 0; i < data.length; i++) {
const x = startX + i * spacing;
const y = chartCanvas.height - (data[i] / config.maxSpeed) * (chartCanvas.height - 60) - 30 + yOffset;
if (x < -50) continue; // 跳过画布外的点
if (i === 0 || (i > 0 && startX + (i - 1) * spacing < -50)) {
ctx.moveTo(x, y);
} else {
// 使用贝塞尔曲线创建平滑效果
const prevX = startX + (i - 1) * spacing;
const prevY = chartCanvas.height - (data[i - 1] / config.maxSpeed) * (chartCanvas.height - 60) - 30 + yOffset;
const cpX = (prevX + x) / 2;
const cpY = (prevY + y) / 2;
ctx.quadraticCurveTo(prevX, prevY, cpX, cpY);
}
if (i === data.length - 1) {
lastPoint = { x, y };
ctx.lineTo(x, y); // 确保最后一个点被绘制
}
}
ctx.stroke();
// 绘制发光效果
ctx.strokeStyle = color + '33'; // 20%透明度
ctx.lineWidth = 8;
ctx.shadowBlur = 25;
ctx.stroke();
ctx.restore(); // 恢复上下文状态
return lastPoint;
}
// 更新小球位置
function updateBallPosition(ball, position) {
if (position && ball) {
ball.style.left = position.x + 'px';
ball.style.top = position.y + 'px';
ball.style.display = 'block'; // 确保小球可见
}
}
// 更新统计数据
function updateStats(type, data) {
if (data.length === 0) return;
const currentValue = Math.round(data[data.length - 1]);
const avgValue = Math.round(data.reduce((a, b) => a + b, 0) / data.length);
const maxValue = Math.round(Math.max(...data));
document.getElementById(`${type}Current`).textContent = currentValue;
document.getElementById(`${type}Avg`).textContent = avgValue;
document.getElementById(`${type}Max`).textContent = maxValue;
}
// 主动画循环
function animate() {
if (animationRunning) {
frameCount++;
// 平滑过渡到目标值
const smoothFactor = 1 / config.smoothness;
current.download = lerp(current.download, target.download, smoothFactor);
current.upload = lerp(current.upload, target.upload, smoothFactor);
// 每几帧添加一个数据点
if (frameCount % 2 === 0) {
data.download.push(current.download);
data.upload.push(current.upload);
// 限制数据点数量
if (data.download.length > config.maxDataPoints) {
data.download.shift();
}
if (data.upload.length > config.maxDataPoints) {
data.upload.shift();
}
}
// 定期生成新目标
if (frameCount % config.animationSpeed === 0) {
target.download = generateRandomSpeed(target.download, 'download');
target.upload = generateRandomSpeed(target.upload, 'upload');
}
// 清空画布
chartCtx.clearRect(0, 0, chartCanvas.width, chartCanvas.height);
// 绘制曲线并获取末端位置
const downloadPos = drawCurve(chartCtx, data.download, config.colors.download, 0);
const uploadPos = drawCurve(chartCtx, data.upload, config.colors.upload, 20);
// 更新小球位置(确保与曲线末端对齐)
updateBallPosition(ballDownload, downloadPos);
updateBallPosition(ballUpload, uploadPos);
// 更新统计
updateStats('download', data.download);
updateStats('upload', data.upload);
}
requestAnimationFrame(animate);
}
// 控制函数
window.toggleAnimation = function() {
animationRunning = !animationRunning;
}
window.resetData = function() {
data.download = [];
data.upload = [];
current.download = 50;
current.upload = 30;
target.download = 50;
target.upload = 30;
frameCount = 0;
// 添加一些初始数据
for (let i = 0; i < 50; i++) {
data.download.push(50 + Math.random() * 20 - 10);
data.upload.push(30 + Math.random() * 15 - 7.5);
}
}
// 控件事件
document.getElementById('speedControl').addEventListener('input', (e) => {
config.animationSpeed = parseInt(e.target.value);
document.getElementById('speedValue').textContent = e.target.value;
});
document.getElementById('smoothControl').addEventListener('input', (e) => {
config.smoothness = parseInt(e.target.value);
document.getElementById('smoothValue').textContent = e.target.value;
});
// 初始化
console.log('开始初始化...');
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// 测试canvas是否正常工作
if (chartCtx) {
chartCtx.fillStyle = 'rgba(255, 255, 255, 0.1)';
chartCtx.fillRect(0, 0, 100, 100);
console.log('Canvas 测试成功');
}
// 添加一些初始数据
for (let i = 0; i < 50; i++) {
data.download.push(50 + Math.random() * 20 - 10);
data.upload.push(30 + Math.random() * 15 - 7.5);
}
// 设置初始值为最后一个数据点
current.download = data.download[data.download.length - 1];
current.upload = data.upload[data.upload.length - 1];
target.download = current.download;
target.upload = current.upload;
drawGrid();
console.log('初始化完成,开始动画...');
console.log('数据点数量:', data.download.length, data.upload.length);
animate();
</script>
</body>
</html>
@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NetGuardian - Modern Network Monitor</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; }
.card { @apply rounded-2xl shadow-md bg-white dark:bg-gray-800 p-4; }
</style>
</head>
<body class="bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-white">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<aside class="w-72 bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 p-4 flex flex-col">
<h2 class="text-xl font-semibold mb-4">NetGuardian</h2>
<nav class="space-y-2">
<button class="w-full text-left hover:bg-gray-200 dark:hover:bg-gray-700 p-2 rounded">总览 Dashboard</button>
<button class="w-full text-left hover:bg-gray-200 dark:hover:bg-gray-700 p-2 rounded">连接详情 Connections</button>
<button class="w-full text-left hover:bg-gray-200 dark:hover:bg-gray-700 p-2 rounded">进程监控 Processes</button>
<button class="w-full text-left hover:bg-gray-200 dark:hover:bg-gray-700 p-2 rounded">流量统计 Traffic</button>
<button class="w-full text-left hover:bg-gray-200 dark:hover:bg-gray-700 p-2 rounded">规则管理 Rules</button>
<button class="w-full text-left hover:bg-gray-200 dark:hover:bg-gray-700 p-2 rounded">设置 Settings</button>
</nav>
</aside>
<!-- Main Content -->
<main class="flex-1 overflow-y-auto p-6 space-y-6">
<!-- Summary Cards -->
<section class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
<div class="card">
<h3 class="text-sm font-medium">当前连接</h3>
<p class="text-2xl font-bold">128</p>
</div>
<div class="card">
<h3 class="text-sm font-medium">上传总速率</h3>
<p class="text-2xl font-bold">2.3 MB/s</p>
</div>
<div class="card">
<h3 class="text-sm font-medium">下载总速率</h3>
<p class="text-2xl font-bold">5.1 MB/s</p>
</div>
<div class="card">
<h3 class="text-sm font-medium">阻止请求</h3>
<p class="text-2xl font-bold">27</p>
</div>
</section>
<!-- Real-time Chart -->
<section class="card">
<h3 class="text-lg font-semibold mb-2">实时流量图</h3>
<div id="traffic-chart" style="height: 300px;"></div>
</section>
<!-- Connection Table -->
<section class="card">
<h3 class="text-lg font-semibold mb-2">当前连接详情</h3>
<div class="overflow-auto">
<table class="min-w-full text-sm">
<thead>
<tr class="bg-gray-200 dark:bg-gray-700">
<th class="text-left p-2">进程</th>
<th class="text-left p-2">本地端口</th>
<th class="text-left p-2">远程地址</th>
<th class="text-left p-2">协议</th>
<th class="text-left p-2">上传速率</th>
<th class="text-left p-2">下载速率</th>
</tr>
</thead>
<tbody>
<tr>
<td class="p-2">chrome.exe</td>
<td class="p-2">49152</td>
<td class="p-2">93.184.216.34 (example.com)</td>
<td class="p-2">TCP</td>
<td class="p-2">123 KB/s</td>
<td class="p-2">987 KB/s</td>
</tr>
<!-- 更多行通过 JS 动态生成 -->
</tbody>
</table>
</div>
</section>
</main>
</div>
<script>
const chart = echarts.init(document.getElementById('traffic-chart'));
const option = {
tooltip: { trigger: 'axis' },
xAxis: { type: 'category', data: Array.from({length: 20}, (_, i) => `${i}s`) },
yAxis: { type: 'value', name: 'KB/s' },
series: [
{ name: '上传', type: 'line', data: Array.from({length: 20}, () => Math.random() * 100) },
{ name: '下载', type: 'line', data: Array.from({length: 20}, () => Math.random() * 200) }
]
};
chart.setOption(option);
</script>
</body>
</html>
@@ -0,0 +1,732 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NetGuard - 现代化网络管理工具</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #0a0e1a;
--bg-secondary: #151923;
--bg-card: #1a1f2e;
--bg-hover: #252b3b;
--text-primary: #ffffff;
--text-secondary: #8892b0;
--text-dim: #64748b;
--accent-primary: #00d4ff;
--accent-secondary: #7c3aed;
--accent-success: #10b981;
--accent-danger: #ef4444;
--accent-warning: #f59e0b;
--border-color: #2a3142;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
overflow: hidden;
height: 100vh;
}
/* 主布局 */
.app-container {
display: flex;
height: 100vh;
}
/* 侧边栏 */
.sidebar {
width: 240px;
background: var(--bg-secondary);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
transition: width 0.3s ease;
}
.sidebar.collapsed {
width: 60px;
}
.logo {
padding: 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
gap: 12px;
}
.logo-icon {
width: 32px;
height: 32px;
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.nav-menu {
flex: 1;
padding: 20px 0;
}
.nav-item {
padding: 12px 20px;
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
}
.nav-item:hover {
background: var(--bg-hover);
}
.nav-item.active {
background: var(--bg-hover);
color: var(--accent-primary);
}
.nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--accent-primary);
}
.nav-icon {
width: 20px;
height: 20px;
opacity: 0.8;
}
/* 主内容区 */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 顶部栏 */
.top-bar {
height: 60px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
}
.search-box {
display: flex;
align-items: center;
background: var(--bg-card);
border-radius: 8px;
padding: 8px 16px;
width: 300px;
border: 1px solid var(--border-color);
transition: all 0.3s ease;
}
.search-box:focus-within {
border-color: var(--accent-primary);
box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.1);
}
.search-input {
background: none;
border: none;
color: var(--text-primary);
outline: none;
width: 100%;
margin-left: 8px;
}
.top-actions {
display: flex;
gap: 12px;
align-items: center;
}
.action-btn {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 8px 16px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
.action-btn:hover {
border-color: var(--accent-primary);
color: var(--accent-primary);
}
.action-btn.primary {
background: var(--accent-primary);
color: var(--bg-primary);
border: none;
}
/* 内容区域 */
.content-area {
flex: 1;
padding: 24px;
overflow-y: auto;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto 1fr;
gap: 20px;
}
/* 统计卡片 */
.stats-container {
grid-column: 1 / -1;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.stat-card {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 20px;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
opacity: 0;
transition: opacity 0.3s ease;
}
.stat-card:hover::before {
opacity: 1;
}
.stat-value {
font-size: 32px;
font-weight: 600;
margin: 8px 0;
}
.stat-label {
color: var(--text-secondary);
font-size: 14px;
}
.stat-trend {
position: absolute;
top: 20px;
right: 20px;
font-size: 12px;
}
.trend-up {
color: var(--accent-success);
}
.trend-down {
color: var(--accent-danger);
}
/* 实时监控区 */
.monitor-section {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 20px;
height: 300px;
display: flex;
flex-direction: column;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.section-title {
font-size: 18px;
font-weight: 500;
}
.chart-container {
flex: 1;
position: relative;
background: var(--bg-secondary);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-dim);
}
/* 连接列表 */
.connections-section {
grid-column: 1 / -1;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
}
.connection-filters {
display: flex;
gap: 12px;
margin-bottom: 16px;
}
.filter-chip {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 20px;
padding: 6px 16px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
}
.filter-chip.active {
background: var(--accent-primary);
color: var(--bg-primary);
border-color: var(--accent-primary);
}
.connections-table {
overflow-x: auto;
}
.table {
width: 100%;
border-collapse: collapse;
}
.table th {
text-align: left;
padding: 12px;
border-bottom: 1px solid var(--border-color);
color: var(--text-secondary);
font-weight: 500;
font-size: 14px;
}
.table td {
padding: 12px;
border-bottom: 1px solid rgba(42, 49, 66, 0.5);
}
.table tr:hover {
background: var(--bg-hover);
}
.process-info {
display: flex;
align-items: center;
gap: 12px;
}
.process-icon {
width: 32px;
height: 32px;
background: var(--bg-secondary);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.process-name {
font-weight: 500;
}
.process-path {
font-size: 12px;
color: var(--text-dim);
}
.speed-badge {
background: var(--bg-secondary);
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
margin-right: 8px;
}
.status-active {
background: var(--accent-success);
box-shadow: 0 0 8px var(--accent-success);
}
.status-blocked {
background: var(--accent-danger);
}
/* 悬浮详情面板 */
.detail-panel {
position: fixed;
right: -400px;
top: 0;
bottom: 0;
width: 400px;
background: var(--bg-secondary);
border-left: 1px solid var(--border-color);
transition: right 0.3s ease;
z-index: 1000;
display: flex;
flex-direction: column;
}
.detail-panel.open {
right: 0;
box-shadow: -10px 0 30px rgba(0, 0, 0, 0.5);
}
.panel-header {
padding: 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.close-btn {
width: 32px;
height: 32px;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
}
.close-btn:hover {
border-color: var(--accent-danger);
color: var(--accent-danger);
}
/* 动画效果 */
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.live-indicator {
width: 8px;
height: 8px;
background: var(--accent-success);
border-radius: 50%;
animation: pulse 2s infinite;
}
/* 响应式调整 */
@media (max-width: 1200px) {
.stats-container {
grid-template-columns: repeat(2, 1fr);
}
.content-area {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="app-container">
<!-- 侧边栏 -->
<aside class="sidebar">
<div class="logo">
<div class="logo-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M12 2L2 7V12C2 16.5 4.5 20.5 8 21.5C8.5 20.5 9 19.5 9.5 18.5C7.5 17.5 6 15.5 6 13V9L12 6L18 9V13C18 13.5 17.9 14 17.8 14.5C18.8 14.5 19.8 14.8 20.5 15.3C20.8 14.2 21 13.1 21 12V7L12 2Z" fill="white"/>
<circle cx="18" cy="18" r="4" fill="#00d4ff"/>
</svg>
</div>
<span class="logo-text">NetGuard</span>
</div>
<nav class="nav-menu">
<div class="nav-item active">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path d="M3 12v3c0 1.657 3.134 3 7 3s7-1.343 7-3v-3c0 1.657-3.134 3-7 3s-7-1.343-7-3z"/>
<path d="M3 7v3c0 1.657 3.134 3 7 3s7-1.343 7-3V7c0 1.657-3.134 3-7 3S3 8.657 3 7z"/>
<path d="M17 5c0 1.657-3.134 3-7 3S3 6.657 3 5s3.134-3 7-3 7 1.343 7 3z"/>
</svg>
<span>实时监控</span>
</div>
<div class="nav-item">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"/>
<path fill-rule="evenodd" d="M4 5a2 2 0 012-2 1 1 0 000 2H6a2 2 0 00-2 2v6a2 2 0 002 2h2a1 1 0 100-2H6V7h8v6h-2a1 1 0 100 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a1 1 0 100-2h2a2 2 0 012 2z"/>
</svg>
<span>防火墙规则</span>
</div>
<div class="nav-item">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 4.804A7.968 7.968 0 005.5 4c-1.255 0-2.443.29-3.5.804v10A7.969 7.969 0 015.5 14c1.669 0 3.218.51 4.5 1.385A7.962 7.962 0 0114.5 14c1.255 0 2.443.29 3.5.804v-10A7.968 7.968 0 0014.5 4c-1.255 0-2.443.29-3.5.804V12a1 1 0 11-2 0V4.804z"/>
</svg>
<span>应用管理</span>
</div>
<div class="nav-item">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/>
</svg>
<span>流量分析</span>
</div>
<div class="nav-item">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"/>
</svg>
<span>设置</span>
</div>
</nav>
</aside>
<!-- 主内容区 -->
<main class="main-content">
<!-- 顶部栏 -->
<header class="top-bar">
<div class="search-box">
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="9" cy="9" r="7"/>
<path d="M14 14l4 4"/>
</svg>
<input type="text" class="search-input" placeholder="搜索进程、IP地址或域名...">
</div>
<div class="top-actions">
<div class="live-indicator"></div>
<span style="color: var(--text-secondary); font-size: 14px; margin-right: 16px;">实时监控中</span>
<button class="action-btn">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"/>
</svg>
阻止所有
</button>
<button class="action-btn primary">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z"/>
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z"/>
</svg>
学习模式
</button>
</div>
</header>
<!-- 内容区域 -->
<div class="content-area">
<!-- 统计卡片 -->
<div class="stats-container">
<div class="stat-card">
<div class="stat-label">活跃连接</div>
<div class="stat-value">247</div>
<div class="stat-trend trend-up">↑ 12%</div>
</div>
<div class="stat-card">
<div class="stat-label">总下载</div>
<div class="stat-value">3.2GB</div>
<div class="stat-trend trend-up">↑ 5.3MB/s</div>
</div>
<div class="stat-card">
<div class="stat-label">总上传</div>
<div class="stat-value">1.1GB</div>
<div class="stat-trend trend-up">↑ 2.1MB/s</div>
</div>
<div class="stat-card">
<div class="stat-label">已阻止</div>
<div class="stat-value">89</div>
<div class="stat-trend" style="color: var(--accent-warning);">15 今日</div>
</div>
</div>
<!-- 实时监控图表 -->
<div class="monitor-section">
<div class="section-header">
<h3 class="section-title">网络流量实时监控</h3>
<select style="background: var(--bg-secondary); border: 1px solid var(--border-color); padding: 4px 8px; border-radius: 4px; color: var(--text-primary);">
<option>最近5分钟</option>
<option>最近1小时</option>
<option>最近24小时</option>
</select>
</div>
<div class="chart-container">
<!-- 这里会放置 Echarts 实时流量图 -->
<span>实时流量图表区域</span>
</div>
</div>
<div class="monitor-section">
<div class="section-header">
<h3 class="section-title">应用流量排行</h3>
<button class="action-btn" style="padding: 4px 12px; font-size: 12px;">查看全部</button>
</div>
<div class="chart-container">
<!-- 这里会放置应用流量饼图 -->
<span>应用流量分布图</span>
</div>
</div>
<!-- 连接列表 -->
<div class="connections-section">
<div class="section-header">
<h3 class="section-title">活跃连接</h3>
<div class="connection-filters">
<div class="filter-chip active">全部</div>
<div class="filter-chip">TCP</div>
<div class="filter-chip">UDP</div>
<div class="filter-chip">已阻止</div>
</div>
</div>
<div class="connections-table">
<table class="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 class="process-info">
<div class="process-icon">
<svg width="20" height="20" fill="#00d4ff" viewBox="0 0 24 24">
<path d="M12,3C7.58,3 4,4.79 4,7C4,9.21 7.58,11 12,11C16.42,11 20,9.21 20,7C20,4.79 16.42,3 12,3M4,9V12C4,14.21 7.58,16 12,16C16.42,16 20,14.21 20,12V9C20,11.21 16.42,13 12,13C7.58,13 4,11.21 4,9M4,14V17C4,19.21 7.58,21 12,21C16.42,21 20,19.21 20,17V14C20,16.21 16.42,18 12,18C7.58,18 4,16.21 4,14Z"/>
</svg>
</div>
<div>
<div class="process-name">Chrome.exe</div>
<div class="process-path">C:\Program Files\Google\Chrome</div>
</div>
</div>
</td>
<td>TCP</td>
<td>192.168.1.100:54321</td>
<td>
<div>142.250.185.78:443</div>
<div class="process-path">youtube.com</div>
</td>
<td><span class="speed-badge">2.3 MB/s</span></td>
<td>156 MB</td>
<td><span class="status-dot status-active"></span>活跃</td>
<td>
<button class="action-btn" style="padding: 4px 12px; font-size: 12px;">详情</button>
</td>
</tr>
<tr>
<td>
<div class="process-info">
<div class="process-icon" style="background: #ef4444;">
<svg width="20" height="20" fill="white" viewBox="0 0 24 24">
<path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4M12,6A6,6 0 0,0 6,12A6,6 0 0,0 12,18A6,6 0 0,0 18,12A6,6 0 0,0 12,6M12,8A4,4 0 0,1 16,12A4,4 0 0,1 12,16A4,4 0 0,1 8,12A4,4 0 0,1 12,8M12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12A2,2 0 0,0 12,10Z"/>
</svg>
</div>
<div>
<div class="process-name">Unknown.exe</div>
<div class="process-path">C:\Users\AppData\Temp</div>
</div>
</div>
</td>
<td>TCP</td>
<td>192.168.1.100:55789</td>
<td>
<div>185.45.67.89:8080</div>
<div class="process-path" style="color: var(--accent-danger);">未知</div>
</td>
<td><span class="speed-badge">0 KB/s</span></td>
<td>12 KB</td>
<td><span class="status-dot status-blocked"></span>已阻止</td>
<td>
<button class="action-btn" style="padding: 4px 12px; font-size: 12px;">详情</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</main>
<!-- 详情面板 -->
<div class="detail-panel" id="detailPanel">
<div class="panel-header">
<h3>连接详情</h3>
<button class="close-btn" onclick="document.getElementById('detailPanel').classList.remove('open')">
<svg width="20" height="20" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"/>
</svg>
</button>
</div>
<!-- 详情内容 -->
</div>
</div>
<script>
// 模拟点击详情按钮打开面板
document.querySelectorAll('.action-btn').forEach(btn => {
if (btn.textContent === '详情') {
btn.addEventListener('click', () => {
document.getElementById('detailPanel').classList.add('open');
});
}
});
</script>
</body>
</html>
@@ -0,0 +1,1063 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NetGuard - 现代网络管理工具</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #0a0a0a;
--bg-secondary: #141414;
--bg-tertiary: #1a1a1a;
--bg-hover: #242424;
--text-primary: #ffffff;
--text-secondary: #a0a0a0;
--text-tertiary: #666666;
--accent: #3b82f6;
--accent-hover: #2563eb;
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
--border: #2a2a2a;
--shadow: rgba(0, 0, 0, 0.5);
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
overflow: hidden;
user-select: none;
}
/* 主布局 */
.app-container {
display: flex;
height: 100vh;
width: 100vw;
}
/* 侧边栏 */
.sidebar {
width: 240px;
background: var(--bg-secondary);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
transition: width 0.3s ease;
}
.sidebar.collapsed {
width: 60px;
}
.logo {
padding: 20px;
font-size: 24px;
font-weight: 700;
color: var(--accent);
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
gap: 12px;
}
.logo-icon {
width: 32px;
height: 32px;
background: linear-gradient(135deg, var(--accent), #8b5cf6);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.nav-menu {
flex: 1;
padding: 12px 0;
}
.nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s ease;
position: relative;
}
.nav-item:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.nav-item.active {
color: var(--accent);
background: rgba(59, 130, 246, 0.1);
}
.nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--accent);
}
.nav-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
}
/* 主内容区 */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 顶部栏 */
.topbar {
height: 60px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
}
.search-container {
position: relative;
width: 400px;
}
.search-input {
width: 100%;
padding: 8px 16px 8px 40px;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 8px;
color: var(--text-primary);
font-size: 14px;
transition: all 0.2s ease;
}
.search-input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: var(--text-tertiary);
}
.topbar-actions {
display: flex;
gap: 12px;
align-items: center;
}
.icon-button {
width: 36px;
height: 36px;
border-radius: 8px;
background: var(--bg-tertiary);
border: 1px solid var(--border);
color: var(--text-secondary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.icon-button:hover {
background: var(--bg-hover);
color: var(--text-primary);
border-color: var(--accent);
}
/* 内容区域 */
.content-area {
flex: 1;
padding: 24px;
overflow-y: auto;
background: var(--bg-primary);
}
/* 统计卡片 */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin-bottom: 32px;
}
.stat-card {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px var(--shadow);
border-color: var(--accent);
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--accent), #8b5cf6);
opacity: 0;
transition: opacity 0.3s ease;
}
.stat-card:hover::before {
opacity: 1;
}
.stat-header {
display: flex;
justify-content: space-between;
align-items: start;
margin-bottom: 16px;
}
.stat-title {
color: var(--text-secondary);
font-size: 14px;
font-weight: 500;
}
.stat-icon {
width: 40px;
height: 40px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
}
.stat-value {
font-size: 32px;
font-weight: 700;
margin-bottom: 8px;
background: linear-gradient(135deg, var(--text-primary), var(--text-secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.stat-change {
font-size: 14px;
display: flex;
align-items: center;
gap: 4px;
}
.stat-change.positive {
color: var(--success);
}
.stat-change.negative {
color: var(--danger);
}
/* 实时连接表格 */
.connections-section {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 12px;
overflow: hidden;
}
.section-header {
padding: 20px 24px;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.section-title {
font-size: 18px;
font-weight: 600;
}
.filter-tabs {
display: flex;
gap: 4px;
background: var(--bg-tertiary);
padding: 4px;
border-radius: 8px;
}
.filter-tab {
padding: 6px 16px;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
color: var(--text-secondary);
}
.filter-tab:hover {
color: var(--text-primary);
}
.filter-tab.active {
background: var(--accent);
color: white;
}
.connections-table {
width: 100%;
border-collapse: collapse;
}
.table-header {
background: var(--bg-tertiary);
position: sticky;
top: 0;
z-index: 10;
}
.table-header th {
padding: 12px 16px;
text-align: left;
font-weight: 500;
color: var(--text-secondary);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.table-row {
border-bottom: 1px solid var(--border);
transition: all 0.2s ease;
cursor: pointer;
}
.table-row:hover {
background: var(--bg-hover);
}
.table-row td {
padding: 16px;
font-size: 14px;
}
.process-info {
display: flex;
align-items: center;
gap: 12px;
}
.process-icon {
width: 32px;
height: 32px;
border-radius: 8px;
background: var(--bg-tertiary);
display: flex;
align-items: center;
justify-content: center;
}
.process-name {
font-weight: 500;
}
.process-path {
font-size: 12px;
color: var(--text-tertiary);
}
.connection-status {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
border-radius: 16px;
font-size: 12px;
font-weight: 500;
}
.status-active {
background: rgba(16, 185, 129, 0.1);
color: var(--success);
}
.status-blocked {
background: rgba(239, 68, 68, 0.1);
color: var(--danger);
}
.bandwidth-meter {
display: flex;
align-items: center;
gap: 8px;
}
.bandwidth-bar {
width: 80px;
height: 4px;
background: var(--bg-tertiary);
border-radius: 2px;
overflow: hidden;
}
.bandwidth-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent), #8b5cf6);
transition: width 0.3s ease;
}
/* 浮动操作面板 */
.floating-panel {
position: fixed;
bottom: 24px;
right: 24px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 12px;
padding: 16px;
box-shadow: 0 8px 32px var(--shadow);
display: flex;
gap: 12px;
align-items: center;
transform: translateY(100px);
opacity: 0;
transition: all 0.3s ease;
}
.floating-panel.visible {
transform: translateY(0);
opacity: 1;
}
.action-button {
padding: 8px 16px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
border: none;
}
.action-button.primary {
background: var(--accent);
color: white;
}
.action-button.primary:hover {
background: var(--accent-hover);
}
.action-button.secondary {
background: var(--bg-tertiary);
color: var(--text-primary);
border: 1px solid var(--border);
}
.action-button.secondary:hover {
background: var(--bg-hover);
}
/* 响应式设计 */
@media (max-width: 1200px) {
.sidebar {
width: 60px;
}
.logo span,
.nav-item span {
display: none;
}
.search-container {
width: 300px;
}
}
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: 1fr;
}
.topbar {
padding: 0 16px;
}
.content-area {
padding: 16px;
}
}
/* 动画 */
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.live-indicator {
width: 8px;
height: 8px;
background: var(--success);
border-radius: 50%;
animation: pulse 2s infinite;
}
/* 自定义滚动条 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-tertiary);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-tertiary);
}
</style>
</head>
<body>
<div class="app-container">
<!-- 侧边栏 -->
<aside class="sidebar">
<div class="logo">
<div class="logo-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7v10c0 5.5 3.8 10 10 10s10-4.5 10-10V7l-10-5z"/>
</svg>
</div>
<span>NetGuard</span>
</div>
<nav class="nav-menu">
<div class="nav-item active">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7" rx="1"/>
<rect x="14" y="3" width="7" height="7" rx="1"/>
<rect x="3" y="14" width="7" height="7" rx="1"/>
<rect x="14" y="14" width="7" height="7" rx="1"/>
</svg>
<span>实时监控</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
</svg>
<span>流量分析</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7v10c0 5.5 3.8 10 10 10s10-4.5 10-10V7l-10-5z"/>
<path d="M12 22V12"/>
<path d="M12 12L2 7"/>
<path d="M12 12l10-5"/>
</svg>
<span>防火墙规则</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"/>
<path d="M12 1v6m0 6v6m11-7h-6m-6 0H1m20.5-5.5L17 11l-4.5-4.5M7.5 7.5L3 3m18.5 18.5L17 17l-4.5 4.5m-5 0L3 21"/>
</svg>
<span>网络拓扑</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
<polyline points="3.27 6.96 12 12.01 20.73 6.96"/>
<line x1="12" y1="22.01" x2="12" y2="12.01"/>
</svg>
<span>进程管理</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
<path d="M12 8v4m0 4h.01"/>
</svg>
<span>威胁检测</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="11" width="18" height="10" rx="2" ry="2"/>
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
</svg>
<span>日志审计</span>
</div>
</nav>
<div class="nav-item" style="border-top: 1px solid var(--border); margin-top: auto;">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"/>
<path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"/>
</svg>
<span>设置</span>
</div>
</aside>
<!-- 主内容区 -->
<main class="main-content">
<!-- 顶部栏 -->
<header class="topbar">
<div class="search-container">
<svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<path d="m21 21-4.35-4.35"/>
</svg>
<input type="text" class="search-input" placeholder="搜索进程、IP地址或域名...">
</div>
<div class="topbar-actions">
<button class="icon-button">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
</svg>
</button>
<button class="icon-button">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
</svg>
</button>
<button class="icon-button">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
<circle cx="12" cy="7" r="4"/>
</svg>
</button>
</div>
</header>
<!-- 内容区域 -->
<div class="content-area">
<!-- 统计卡片 -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-header">
<div>
<div class="stat-title">活跃连接</div>
<div class="stat-value">247</div>
<div class="stat-change positive">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/>
<polyline points="17 6 23 6 23 12"/>
</svg>
<span>+12.5%</span>
</div>
</div>
<div class="stat-icon" style="background: rgba(59, 130, 246, 0.1); color: var(--accent);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
<path d="M1.42 9a16 16 0 0 1 21.16 0"/>
<path d="M8.53 16.11a6 6 0 0 1 6.95 0"/>
<line x1="12" y1="20" x2="12.01" y2="20"/>
</svg>
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<div>
<div class="stat-title">总流量</div>
<div class="stat-value">12.4 GB</div>
<div class="stat-change positive">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/>
<polyline points="17 6 23 6 23 12"/>
</svg>
<span>+8.2%</span>
</div>
</div>
<div class="stat-icon" style="background: rgba(139, 92, 246, 0.1); color: #8b5cf6;">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<div>
<div class="stat-title">阻止威胁</div>
<div class="stat-value">89</div>
<div class="stat-change negative">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="23 18 13.5 8.5 8.5 13.5 1 6"/>
<polyline points="17 18 23 18 23 12"/>
</svg>
<span>-5.3%</span>
</div>
</div>
<div class="stat-icon" style="background: rgba(239, 68, 68, 0.1); color: var(--danger);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12.01" y2="16"/>
</svg>
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<div>
<div class="stat-title">活跃进程</div>
<div class="stat-value">42</div>
<div class="stat-change positive">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/>
<polyline points="17 6 23 6 23 12"/>
</svg>
<span>+3.1%</span>
</div>
</div>
<div class="stat-icon" style="background: rgba(16, 185, 129, 0.1); color: var(--success);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="4" y="4" width="16" height="16" rx="2" ry="2"/>
<rect x="9" y="9" width="6" height="6"/>
<line x1="9" y1="1" x2="9" y2="4"/>
<line x1="15" y1="1" x2="15" y2="4"/>
<line x1="9" y1="20" x2="9" y2="23"/>
<line x1="15" y1="20" x2="15" y2="23"/>
<line x1="20" y1="9" x2="23" y2="9"/>
<line x1="20" y1="14" x2="23" y2="14"/>
<line x1="1" y1="9" x2="4" y2="9"/>
<line x1="1" y1="14" x2="4" y2="14"/>
</svg>
</div>
</div>
</div>
</div>
<!-- 实时连接表格 -->
<div class="connections-section">
<div class="section-header">
<h2 class="section-title">
实时连接
<span class="live-indicator" style="display: inline-block; margin-left: 8px;"></span>
</h2>
<div class="filter-tabs">
<div class="filter-tab active">全部</div>
<div class="filter-tab">活跃</div>
<div class="filter-tab">阻止</div>
<div class="filter-tab">监控</div>
</div>
</div>
<table class="connections-table">
<thead class="table-header">
<tr>
<th>进程</th>
<th>远程地址</th>
<th>端口</th>
<th>协议</th>
<th>状态</th>
<th>带宽</th>
<th>总流量</th>
<th>持续时间</th>
</tr>
</thead>
<tbody>
<tr class="table-row">
<td>
<div class="process-info">
<div class="process-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</svg>
</div>
<div>
<div class="process-name">Chrome.exe</div>
<div class="process-path">C:\Program Files\Google\Chrome</div>
</div>
</div>
</td>
<td>142.250.185.46 (google.com)</td>
<td>443</td>
<td>HTTPS</td>
<td><span class="connection-status status-active">活跃</span></td>
<td>
<div class="bandwidth-meter">
<div class="bandwidth-bar">
<div class="bandwidth-fill" style="width: 65%"></div>
</div>
<span>2.4 MB/s</span>
</div>
</td>
<td>156 MB</td>
<td>12m 34s</td>
</tr>
<tr class="table-row">
<td>
<div class="process-info">
<div class="process-icon" style="background: rgba(139, 92, 246, 0.1); color: #8b5cf6;">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M9 11H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2zm2-7h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11z"/>
</svg>
</div>
<div>
<div class="process-name">Discord.exe</div>
<div class="process-path">C:\Users\AppData\Local\Discord</div>
</div>
</div>
</td>
<td>162.159.135.234 (discord.com)</td>
<td>443</td>
<td>WSS</td>
<td><span class="connection-status status-active">活跃</span></td>
<td>
<div class="bandwidth-meter">
<div class="bandwidth-bar">
<div class="bandwidth-fill" style="width: 20%"></div>
</div>
<span>128 KB/s</span>
</div>
</td>
<td>42 MB</td>
<td>1h 23m</td>
</tr>
<tr class="table-row">
<td>
<div class="process-info">
<div class="process-icon" style="background: rgba(239, 68, 68, 0.1); color: var(--danger);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>
</svg>
</div>
<div>
<div class="process-name">Unknown.exe</div>
<div class="process-path">C:\Temp\suspicious</div>
</div>
</div>
</td>
<td>185.220.101.34 (未知)</td>
<td>8080</td>
<td>TCP</td>
<td><span class="connection-status status-blocked">已阻止</span></td>
<td>
<div class="bandwidth-meter">
<div class="bandwidth-bar">
<div class="bandwidth-fill" style="width: 0%"></div>
</div>
<span>0 KB/s</span>
</div>
</td>
<td>0 B</td>
<td>-</td>
</tr>
<tr class="table-row">
<td>
<div class="process-info">
<div class="process-icon" style="background: rgba(16, 185, 129, 0.1); color: var(--success);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"/>
</svg>
</div>
<div>
<div class="process-name">OneDrive.exe</div>
<div class="process-path">C:\Program Files\Microsoft\OneDrive</div>
</div>
</div>
</td>
<td>52.109.12.18 (microsoft.com)</td>
<td>443</td>
<td>HTTPS</td>
<td><span class="connection-status status-active">活跃</span></td>
<td>
<div class="bandwidth-meter">
<div class="bandwidth-bar">
<div class="bandwidth-fill" style="width: 45%"></div>
</div>
<span>1.2 MB/s</span>
</div>
</td>
<td>89 MB</td>
<td>5m 12s</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 浮动操作面板 -->
<div class="floating-panel visible">
<span style="color: var(--text-secondary); font-size: 14px;">已选择 2 个连接</span>
<button class="action-button secondary">添加规则</button>
<button class="action-button primary">阻止连接</button>
</div>
</main>
</div>
<script>
// 模拟实时数据更新
document.addEventListener('DOMContentLoaded', function() {
// 统计数据动画
const statValues = document.querySelectorAll('.stat-value');
statValues.forEach(stat => {
const finalValue = stat.textContent;
const isNumeric = /^\d+$/.test(finalValue);
if (isNumeric) {
let currentValue = 0;
const targetValue = parseInt(finalValue);
const increment = targetValue / 50;
const counter = setInterval(() => {
currentValue += increment;
if (currentValue >= targetValue) {
currentValue = targetValue;
clearInterval(counter);
}
stat.textContent = Math.floor(currentValue);
}, 20);
}
});
// 带宽动画
const bandwidthBars = document.querySelectorAll('.bandwidth-fill');
setInterval(() => {
bandwidthBars.forEach(bar => {
if (bar.parentElement.parentElement.parentElement.parentElement.querySelector('.status-active')) {
const currentWidth = parseInt(bar.style.width);
const variation = (Math.random() - 0.5) * 20;
const newWidth = Math.max(0, Math.min(100, currentWidth + variation));
bar.style.width = newWidth + '%';
}
});
}, 1000);
// 筛选标签切换
const filterTabs = document.querySelectorAll('.filter-tab');
filterTabs.forEach(tab => {
tab.addEventListener('click', function() {
filterTabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
});
});
// 表格行选择
const tableRows = document.querySelectorAll('.table-row');
let selectedCount = 0;
const floatingPanel = document.querySelector('.floating-panel');
tableRows.forEach(row => {
row.addEventListener('click', function() {
this.classList.toggle('selected');
selectedCount = document.querySelectorAll('.table-row.selected').length;
if (selectedCount > 0) {
floatingPanel.classList.add('visible');
floatingPanel.querySelector('span').textContent = `已选择 ${selectedCount} 个连接`;
} else {
floatingPanel.classList.remove('visible');
}
});
});
// 侧边栏折叠
let sidebarCollapsed = false;
document.addEventListener('keydown', function(e) {
if (e.ctrlKey && e.key === 'b') {
e.preventDefault();
const sidebar = document.querySelector('.sidebar');
sidebar.classList.toggle('collapsed');
}
});
// 搜索功能
const searchInput = document.querySelector('.search-input');
searchInput.addEventListener('input', function(e) {
const searchTerm = e.target.value.toLowerCase();
tableRows.forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(searchTerm) ? '' : 'none';
});
});
// 模拟新连接通知
setInterval(() => {
const statValue = document.querySelector('.stat-value');
const currentValue = parseInt(statValue.textContent);
if (Math.random() > 0.7) {
statValue.textContent = currentValue + 1;
// 添加脉冲效果
statValue.style.animation = 'pulse 0.5s';
setTimeout(() => {
statValue.style.animation = '';
}, 500);
}
}, 5000);
});
// 添加选中行的样式
const style = document.createElement('style');
style.textContent = `
.table-row.selected {
background: rgba(59, 130, 246, 0.1);
border-left: 3px solid var(--accent);
}
.table-row.selected td:first-child {
padding-left: 13px;
}
`;
document.head.appendChild(style);
</script>
</body>
</html>
@@ -0,0 +1,1284 @@
</svg>
</div>
<div class="overview-value">156</div>
<div class="mini-chart">
<div class="chart-line" style="background: linear-gradient(to top, rgba(255, 51, 102, 0.2), transparent); border-color: var(--danger); clip-path: polygon(0 100%, 10% 95%, 20% 98%, 30% 90%, 40% 92%, 50% 85%, 60% 88%, 70% 82%, 80% 85%, 90% 80%, 100% 75%, 100% 100%);"></div>
</div>
</div>
</div>
<!-- 实时流量图表 -->
<div class="chart-container">
<div class="chart-header">
<h3 class="chart-title">网络流量实时监控</h3>
<div class="chart-controls">
<button class="chart-btn active">1分钟</button>
<button class="chart-btn">5分钟</button>
<button class="chart-btn">30分钟</button>
</div>
</div>
<div style="position: relative; height: 220px; background: var(--bg-tertiary); border-radius: 8px; overflow: hidden;">
<!-- 模拟的实时图表 -->
<svg width="100%" height="100%" style="position: absolute;">
<defs>
<linearGradient id="downloadGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgba(0,212,255,0.4);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgba(0,212,255,0);stop-opacity:1" />
</linearGradient>
<linearGradient id="uploadGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgba(0,255,136,0.4);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgba(0,255,136,0);stop-opacity:1" />
</linearGradient>
</defs>
<path d="M 0 150 Q 50 140, 100 145 T 200 135 T 300 140 T 400 130 T 500 135 T 600 125 T 700 130 T 800 120 L 800 220 L 0 220 Z" fill="url(#downloadGradient)" />
<path d="M 0 150 Q 50 140, 100 145 T 200 135 T 300 140 T 400 130 T 500 135 T 600 125 T 700 130 T 800 120" fill="none" stroke="#00d4ff" stroke-width="2" />
<path d="M 0 180 Q 50 175, 100 178 T 200 172 T 300 175 T 400 170 T 500 172 T 600 168 T 700 170 T 800 165 L 800 220 L 0 220 Z" fill="url(#uploadGradient)" />
<path d="M 0 180 Q 50 175, 100 178 T 200 172 T 300 175 T 400 170 T 500 172 T 600 168 T 700 170 T 800 165" fill="none" stroke="#00ff88" stroke-width="2" />
</svg>
<div style="position: absolute; bottom: 10px; right: 20px; display: flex; gap: 20px; font-size: 12px;">
<span style="display: flex; align-items: center; gap: 8px;">
<span style="width: 12px; height: 2px; background: #00d4ff;"></span>
下载
</span>
<span style="display: flex; align-items: center; gap: 8px;">
<span style="width: 12px; height: 2px; background: #00ff88;"></span>
上传
</span>
</div>
</div>
</div>
<!-- 搜索栏 -->
<div class="search-bar">
<svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
<input type="text" class="search-input" placeholder="搜索进程、IP地址或域名...">
</div>
<!-- 实时连接列表 -->
<div class="connections-grid">
<div class="connection-card">
<div class="process-info">
<div class="process-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="M8 12h8"></path>
<path d="M12 8v8"></path>
</svg>
</div>
<div class="process-details">
<h4>Chrome.exe <span class="tag system">系统进程</span></h4>
<p>C:\Program Files\Google\Chrome\Application</p>
</div>
</div>
<div class="connection-details">
<div class="connection-path">
<span>本地:51234</span>
<span class="connection-arrow">→</span>
<span class="ip-address">142.250.185.46:443</span>
</div>
<div class="domain-name">googleapis.com</div>
</div>
<div class="traffic-info">
<div class="speed-value">↓ 458 KB/s</div>
<div class="total-traffic">总计: 234 MB</div>
</div>
<div class="action-buttons">
<button class="action-btn" title="查看详情">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</button>
<button class="action-btn block" title="阻止连接">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="m4.93 4.93 14.14 14.14"></path>
</svg>
</button>
</div>
</div>
<div class="connection-card">
<div class="process-info">
<div class="process-icon" style="background: var(--accent-dim);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"></path>
<line x1="4" y1="22" x2="4" y2="15"></line>
</svg>
</div>
<div class="process-details">
<h4>MicrosoftEdge.exe <span class="tag trusted">已信任</span></h4>
<p>C:\Program Files (x86)\Microsoft\Edge\Application</p>
</div>
</div>
<div class="connection-details">
<div class="connection-path">
<span>本地:52810</span>
<span class="connection-arrow">→</span>
<span class="ip-address">13.107.42.14:443</span>
</div>
<div class="domain-name">outlook.live.com</div>
</div>
<div class="traffic-info">
<div class="speed-value">↓ 24 KB/s</div>
<div class="total-traffic">总计: 12.4 MB</div>
</div>
<div class="action-buttons">
<button class="action-btn" title="查看详情">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</button>
<button class="action-btn block" title="阻止连接">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="m4.93 4.93 14.14 14.14"></path>
</svg>
</button>
</div>
</div>
<div class="connection-card">
<div class="process-info">
<div class="process-icon" style="background: rgba(255, 170, 0, 0.2); color: var(--warning);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
<line x1="12" y1="9" x2="12" y2="13"></line>
<line x1="12" y1="17" x2="12.01" y2="17"></line>
</svg>
</div>
<div class="process-details">
<h4>Unknown.exe <span class="tag warning">未知进程</span></h4>
<p>C:\Users\Admin\AppData\Local\Temp</p>
</div>
</div>
<div class="connection-details">
<div class="connection-path">
<span>本地:49823</span>
<span class="connection-arrow">→</span>
<span class="ip-address">185.234.218.50:8080</span>
</div>
<div class="domain-name">未解析</div>
</div>
<div class="traffic-info">
<div class="speed-value" style="color: var(--warning);">↑ 1.2 MB/s</div>
<div class="total-traffic">总计: 458 MB</div>
</div>
<div class="action-buttons">
<button class="action-btn" title="查看详情">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</button>
<button class="action-btn block" title="阻止连接" style="background: var(--danger); color: white;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="m4.93 4.93 14.14 14.14"></path>
</svg>
</button>
</div>
</div>
</div>
</div>
<!-- 进程管理视图(隐藏) -->
<div id="process-view" class="view-content" style="display: none;">
<div class="process-list">
<div class="list-header">
<span>进程名称</span>
<span>连接数</span>
<span>下载速度</span>
<span>上传速度</span>
<span>操作</span>
</div>
<div class="list-item">
<div class="process-info">
<div class="process-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="M8 12h8"></path>
<path d="M12 8v8"></path>
</svg>
</div>
<div>
<h4>Chrome.exe</h4>
<p style="font-size: 12px; color: var(--text-secondary);">PID: 12456</p>
</div>
</div>
<span>23</span>
<span style="color: var(--success);">458 KB/s</span>
<span>24 KB/s</span>
<div class="action-buttons">
<button class="action-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
<line x1="16" y1="13" x2="8" y2="13"></line>
<line x1="16" y1="17" x2="8" y2="17"></line>
<polyline points="10 9 9 9 8 9"></polyline>
</svg>
</button>
<button class="action-btn block">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="m4.93 4.93 14.14 14.14"></path>
</svg>
</button>
</div>
</div>
</div>
</div>
<!-- 网络拓扑视图(隐藏) -->
<div id="topology-view" class="view-content" style="display: none;">
<div class="topology-container">
<div class="topology-node node-central">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect>
<path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path>
</svg>
<span style="font-size: 12px; margin-top: 4px;">本机</span>
</div>
<div class="topology-node" style="left: 20%; top: 20%;">
<span style="font-size: 14px; font-weight: 500;">Google</span>
<span style="font-size: 11px; color: var(--text-secondary);">8 连接</span>
</div>
<div class="topology-node" style="left: 70%; top: 25%;">
<span style="font-size: 14px; font-weight: 500;">Microsoft</span>
<span style="font-size: 11px; color: var(--text-secondary);">5 连接</span>
</div>
<div class="topology-node" style="left: 15%; top: 70%;">
<span style="font-size: 14px; font-weight: 500;">AWS</span>
<span style="font-size: 11px; color: var(--text-secondary);">3 连接</span>
</div>
<div class="topology-node" style="left: 75%; top: 75%;">
<span style="font-size: 14px; font-weight: 500;">Cloudflare</span>
<span style="font-size: 11px; color: var(--text-secondary);">12 连接</span>
</div>
</div>
</div>
<!-- 设置视图(隐藏) -->
<div id="settings-view" class="view-content" style="display: none;">
<div class="settings-panel">
<div class="settings-section">
<h3 class="settings-title">常规设置</h3>
<div class="setting-item">
<div class="setting-label">
<h4>开机自启动</h4>
<p>Windows 启动时自动运行 NetGuard</p>
</div>
<div class="toggle-switch active">
<div class="toggle-slider"></div>
</div>
</div>
<div class="setting-item">
<div class="setting-label">
<h4>最小化到托盘</h4>
<p>关闭窗口时最小化到系统托盘</p>
</div>
<div class="toggle-switch">
<div class="toggle-slider"></div>
</div>
</div>
<div class="setting-item">
<div class="setting-label">
<h4>自动更新</h4>
<p>自动下载并安装最新版本</p>
</div>
<div class="toggle-switch active">
<div class="toggle-slider"></div>
</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-title">监控设置</h3>
<div class="setting-item">
<div class="setting-label">
<h4>异常行为检测</h4>
<p>检测并警告可疑的网络活动</p>
</div>
<div class="toggle-switch active">
<div class="toggle-slider"></div>
</div>
</div>
<div class="setting-item">
<div class="setting-label">
<h4>DNS 解析监控</h4>
<p>记录并显示域名解析信息</p>
</div>
<div class="toggle-switch active">
<div class="toggle-slider"></div>
</div>
</div>
<div class="setting-item">
<div class="setting-label">
<h4>流量统计</h4>
<p>保存历史流量数据用于分析</p>
</div>
<div class="toggle-switch active">
<div class="toggle-slider"></div>
</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-title">界面设置</h3>
<div class="setting-item">
<div class="setting-label">
<h4>暗色主题</h4>
<p>使用暗色界面主题</p>
</div>
<div class="toggle-switch active">
<div class="toggle-slider"></div>
</div>
</div>
<div class="setting-item">
<div class="setting-label">
<h4>动画效果</h4>
<p>启用界面过渡动画</p>
</div>
<div class="toggle-switch active">
<div class="toggle-slider"></div>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- 通知示例 -->
<div class="notification" style="display: none;">
<div class="notification-header">
<div class="notification-icon warning">!</div>
<h4 style="font-size: 14px;">异常行为检测</h4>
</div>
<p style="font-size: 13px; color: var(--text-secondary);">Unknown.exe 在过去1分钟内连接了 47 个不同的IP地址</p>
<div style="display: flex; gap: 8px; margin-top: 12px;">
<button class="action-btn" style="flex: 1; height: 32px;">查看详情</button>
<button class="action-btn block" style="flex: 1; height: 32px;">立即阻止</button>
</div>
</div>
</div>
<script>
// 导航切换
document.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', function() {
// 移除所有活跃状态
document.querySelectorAll('.nav-item').forEach(nav => nav.classList.remove('active'));
this.classList.add('active');
// 隐藏所有视图
document.querySelectorAll('.view-content').forEach(view => view.style.display = 'none');
// 显示对应视图
const views = ['realtime-view', 'process-view', 'settings-view', 'process-view', 'topology-view', 'settings-view'];
const index = Array.from(document.querySelectorAll('.nav-item')).indexOf(this);
const targetView = document.getElementById(views[index]);
if (targetView) {
targetView.style.display = 'block';
targetView.classList.add('fade-in');
}
});
});
// 开关切换
document.querySelectorAll('.toggle-switch').forEach(toggle => {
toggle.addEventListener('click', function() {
this.classList.toggle('active');
});
});
// 模拟实时数据更新
function updateRealtimeData() {
// 更新速度显示
const downloadSpeed = document.querySelector('.status-item:nth-child(2) .status-value');
const uploadSpeed = document.querySelector('.status-item:nth-child(3) .status-value');
if (downloadSpeed) {
const newDownload = (Math.random() * 5).toFixed(1);
downloadSpeed.textContent = `↓ ${newDownload} MB/s`;
}
if (uploadSpeed) {
const newUpload = (Math.random() * 500).toFixed(0);
uploadSpeed.textContent = `↑ ${newUpload} KB/s`;
}
// 更新连接数
const connections = document.querySelector('.status-item:nth-child(4) .status-value');
if (connections) {
connections.textContent = Math.floor(Math.random() * 20 + 40);
}
}
// 每秒更新数据
setInterval(updateRealtimeData, 1000);
// 模拟通知
setTimeout(() => {
const notification = document.querySelector('.notification');
if (notification) {
notification.style.display = 'block';
setTimeout(() => {
notification.style.display = 'none';
}, 5000);
}
}, 3000);
// 搜索功能
const searchInput = document.querySelector('.search-input');
if (searchInput) {
searchInput.addEventListener('input', function(e) {
const searchTerm = e.target.value.toLowerCase();
document.querySelectorAll('.connection-card').forEach(card => {
const text = card.textContent.toLowerCase();
card.style.display = text.includes(searchTerm) ? 'grid' : 'none';
});
});
}
// 添加连接卡片hover效果的动态阴影
document.querySelectorAll('.connection-card').forEach(card => {
card.addEventListener('mousemove', function(e) {
const rect = this.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
this.style.background = `radial-gradient(circle at ${x}px ${y}px, rgba(0, 212, 255, 0.05), var(--bg-secondary))`;
});
card.addEventListener('mouseleave', function() {
this.style.background = 'var(--bg-secondary)';
});
});
</script>
</body>
</html><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NetGuard - 现代网络监控工具</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #0a0a0a;
--bg-secondary: #141414;
--bg-tertiary: #1a1a1a;
--bg-hover: #252525;
--text-primary: #ffffff;
--text-secondary: #a0a0a0;
--text-tertiary: #707070;
--accent: #00d4ff;
--accent-dim: #0099cc;
--success: #00ff88;
--warning: #ffaa00;
--danger: #ff3366;
--border: rgba(255, 255, 255, 0.1);
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
overflow: hidden;
user-select: none;
}
/* 主布局 */
.app-container {
display: flex;
height: 100vh;
position: relative;
}
/* 顶部状态栏 */
.top-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 48px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
z-index: 1000;
backdrop-filter: blur(10px);
background: rgba(20, 20, 20, 0.9);
}
.status-indicators {
display: flex;
gap: 30px;
align-items: center;
}
.status-item {
display: flex;
align-items: center;
gap: 8px;
}
.status-icon {
width: 12px;
height: 12px;
border-radius: 50%;
animation: pulse 2s infinite;
}
.status-icon.active {
background: var(--success);
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.status-value {
font-size: 14px;
font-weight: 500;
}
.status-label {
font-size: 12px;
color: var(--text-secondary);
margin-left: 4px;
}
/* 侧边栏 */
.sidebar {
width: 200px;
background: var(--bg-secondary);
border-right: 1px solid var(--border);
padding-top: 48px;
transition: width 0.3s ease;
flex-shrink: 0;
}
.sidebar.collapsed {
width: 60px;
}
.nav-item {
display: flex;
align-items: center;
padding: 12px 20px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s ease;
position: relative;
overflow: hidden;
}
.nav-item:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.nav-item.active {
background: var(--bg-tertiary);
color: var(--accent);
}
.nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--accent);
}
.nav-icon {
width: 20px;
height: 20px;
margin-right: 12px;
flex-shrink: 0;
}
.sidebar.collapsed .nav-text {
display: none;
}
/* 主内容区 */
.main-content {
flex: 1;
padding: 68px 20px 20px;
overflow-y: auto;
background: var(--bg-primary);
}
/* 实时连接视图 */
.connections-grid {
display: grid;
gap: 15px;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.connection-card {
background: var(--bg-secondary);
border-radius: 12px;
padding: 16px;
border: 1px solid var(--border);
transition: all 0.2s ease;
display: grid;
grid-template-columns: auto 1fr auto auto;
gap: 16px;
align-items: center;
}
.connection-card:hover {
border-color: var(--accent-dim);
transform: translateX(2px);
box-shadow: 0 4px 20px rgba(0, 212, 255, 0.1);
}
.process-info {
display: flex;
align-items: center;
gap: 12px;
}
.process-icon {
width: 36px;
height: 36px;
background: var(--bg-tertiary);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
}
.process-details h4 {
font-size: 14px;
font-weight: 500;
margin-bottom: 4px;
}
.process-details p {
font-size: 12px;
color: var(--text-secondary);
}
.connection-details {
display: flex;
flex-direction: column;
gap: 4px;
}
.connection-path {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
}
.connection-arrow {
color: var(--text-tertiary);
}
.ip-address {
color: var(--accent);
font-family: 'Consolas', monospace;
}
.domain-name {
color: var(--text-secondary);
font-size: 12px;
}
.traffic-info {
text-align: right;
}
.speed-value {
font-size: 16px;
font-weight: 500;
color: var(--success);
}
.total-traffic {
font-size: 12px;
color: var(--text-secondary);
margin-top: 4px;
}
.action-buttons {
display: flex;
gap: 8px;
}
.action-btn {
width: 32px;
height: 32px;
border-radius: 8px;
border: 1px solid var(--border);
background: transparent;
color: var(--text-secondary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.action-btn:hover {
background: var(--bg-hover);
color: var(--text-primary);
border-color: var(--accent-dim);
}
.action-btn.block {
color: var(--danger);
}
/* 流量概览卡片 */
.overview-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.overview-card {
background: var(--bg-secondary);
border-radius: 16px;
padding: 20px;
border: 1px solid var(--border);
}
.overview-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.overview-title {
font-size: 14px;
color: var(--text-secondary);
}
.overview-value {
font-size: 28px;
font-weight: 600;
margin-bottom: 8px;
}
.mini-chart {
height: 60px;
background: var(--bg-tertiary);
border-radius: 8px;
position: relative;
overflow: hidden;
}
.chart-line {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 100%;
background: linear-gradient(to top, rgba(0, 212, 255, 0.2), transparent);
border-top: 2px solid var(--accent);
}
/* 实时图表区域 */
.chart-container {
background: var(--bg-secondary);
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
border: 1px solid var(--border);
height: 300px;
position: relative;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.chart-title {
font-size: 16px;
font-weight: 500;
}
.chart-controls {
display: flex;
gap: 8px;
}
.chart-btn {
padding: 6px 12px;
border-radius: 6px;
border: 1px solid var(--border);
background: transparent;
color: var(--text-secondary);
font-size: 12px;
cursor: pointer;
transition: all 0.2s ease;
}
.chart-btn.active {
background: var(--accent);
color: var(--bg-primary);
border-color: var(--accent);
}
/* 进程列表 */
.process-list {
background: var(--bg-secondary);
border-radius: 16px;
overflow: hidden;
border: 1px solid var(--border);
}
.list-header {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr 100px;
padding: 12px 20px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border);
font-size: 12px;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.list-item {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr 100px;
padding: 16px 20px;
border-bottom: 1px solid var(--border);
transition: background 0.2s ease;
align-items: center;
}
.list-item:hover {
background: var(--bg-hover);
}
/* 拓扑图容器 */
.topology-container {
background: var(--bg-secondary);
border-radius: 16px;
padding: 20px;
height: 500px;
position: relative;
overflow: hidden;
border: 1px solid var(--border);
}
.topology-node {
position: absolute;
width: 120px;
height: 60px;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
}
.topology-node:hover {
transform: scale(1.05);
border-color: var(--accent);
box-shadow: 0 4px 20px rgba(0, 212, 255, 0.3);
}
.node-central {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: var(--accent-dim);
border-color: var(--accent);
}
/* 设置面板 */
.settings-panel {
display: grid;
gap: 20px;
}
.settings-section {
background: var(--bg-secondary);
border-radius: 16px;
padding: 24px;
border: 1px solid var(--border);
}
.settings-title {
font-size: 18px;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid var(--border);
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
}
.setting-label {
display: flex;
flex-direction: column;
gap: 4px;
}
.setting-label h4 {
font-size: 14px;
font-weight: 500;
}
.setting-label p {
font-size: 12px;
color: var(--text-secondary);
}
/* 开关组件 */
.toggle-switch {
position: relative;
width: 48px;
height: 24px;
background: var(--bg-tertiary);
border-radius: 12px;
cursor: pointer;
transition: background 0.3s ease;
}
.toggle-switch.active {
background: var(--accent);
}
.toggle-slider {
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
background: var(--text-primary);
border-radius: 50%;
transition: transform 0.3s ease;
}
.toggle-switch.active .toggle-slider {
transform: translateX(24px);
}
/* 搜索栏 */
.search-bar {
position: relative;
margin-bottom: 20px;
}
.search-input {
width: 100%;
padding: 12px 20px 12px 48px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 12px;
color: var(--text-primary);
font-size: 14px;
transition: all 0.2s ease;
}
.search-input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.1);
}
.search-icon {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
color: var(--text-secondary);
}
/* 响应式设计 */
@media (max-width: 1200px) {
.overview-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.sidebar {
position: fixed;
left: -200px;
height: 100vh;
z-index: 999;
}
.sidebar.open {
left: 0;
}
.main-content {
padding-left: 20px;
}
.overview-grid {
grid-template-columns: 1fr;
}
.connection-card {
grid-template-columns: 1fr;
gap: 12px;
}
}
/* 动画效果 */
.fade-in {
animation: fadeIn 0.3s ease;
}
.slide-in {
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { transform: translateX(-10px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
/* 提示标签 */
.tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 500;
margin-left: 8px;
}
.tag.trusted {
background: rgba(0, 255, 136, 0.2);
color: var(--success);
}
.tag.system {
background: rgba(0, 212, 255, 0.2);
color: var(--accent);
}
.tag.warning {
background: rgba(255, 170, 0, 0.2);
color: var(--warning);
}
/* 通知提示 */
.notification {
position: fixed;
top: 68px;
right: 20px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 12px;
padding: 16px 20px;
min-width: 300px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
animation: slideInRight 0.3s ease;
}
@keyframes slideInRight {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
.notification-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}
.notification-icon {
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.notification-icon.warning {
background: var(--warning);
color: var(--bg-primary);
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-secondary);
}
::-webkit-scrollbar-thumb {
background: var(--bg-hover);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-tertiary);
}
</style>
</head>
<body>
<div class="app-container">
<!-- 顶部状态栏 -->
<div class="top-bar">
<div class="status-indicators">
<div class="status-item">
<div class="status-icon active"></div>
<span class="status-value">防火墙已启用</span>
</div>
<div class="status-item">
<span class="status-value">↓ 2.4 MB/s</span>
<span class="status-label">下载</span>
</div>
<div class="status-item">
<span class="status-value">↑ 156 KB/s</span>
<span class="status-label">上传</span>
</div>
<div class="status-item">
<span class="status-value">47</span>
<span class="status-label">活跃连接</span>
</div>
</div>
<div class="top-actions">
<button class="action-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
</button>
<button class="action-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path>
</svg>
</button>
</div>
</div>
<!-- 侧边栏 -->
<aside class="sidebar">
<nav>
<div class="nav-item active">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"></path>
</svg>
<span class="nav-text">实时监控</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 12h18m-9-9v18"></path>
</svg>
<span class="nav-text">进程管理</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
</svg>
<span class="nav-text">防火墙规则</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
<span class="nav-text">流量分析</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
</svg>
<span class="nav-text">网络拓扑</span>
</div>
<div class="nav-item">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
<span class="nav-text">设置</span>
</div>
</nav>
</aside>
<!-- 主内容区 -->
<main class="main-content">
<!-- 实时监控视图 -->
<div id="realtime-view" class="view-content">
<!-- 概览卡片 -->
<div class="overview-grid">
<div class="overview-card">
<div class="overview-header">
<span class="overview-title">今日总流量</span>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color: var(--accent)">
<path d="M3 3v18h18"></path>
<path d="m19 9-5 5-4-4-3 3"></path>
</svg>
</div>
<div class="overview-value">12.4 GB</div>
<div class="mini-chart">
<div class="chart-line" style="clip-path: polygon(0 100%, 10% 80%, 20% 85%, 30% 70%, 40% 60%, 50% 65%, 60% 50%, 70% 40%, 80% 45%, 90% 30%, 100% 20%, 100% 100%);"></div>
</div>
</div>
<div class="overview-card">
<div class="overview-header">
<span class="overview-title">活跃进程</span>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color: var(--success)">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<line x1="9" y1="9" x2="15" y2="15"></line>
<line x1="15" y1="9" x2="9" y2="15"></line>
</svg>
</div>
<div class="overview-value">23</div>
<div class="mini-chart">
<div class="chart-line" style="background: linear-gradient(to top, rgba(0, 255, 136, 0.2), transparent); border-color: var(--success); clip-path: polygon(0 100%, 10% 90%, 20% 85%, 30% 88%, 40% 85%, 50% 82%, 60% 80%, 70% 78%, 80% 75%, 90% 70%, 100% 65%, 100% 100%);"></div>
</div>
</div>
<div class="overview-card">
<div class="overview-header">
<span class="overview-title">阻止的连接</span>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color: var(--danger)">
<circle cx="12" cy="12" r="10"></circle>
<line x1="4.93" y1="4.93" x2="19.07" y2="19.07"></line>
@@ -0,0 +1,806 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Network Monitor - 界面设计概念</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #0a0a0a;
--bg-secondary: #141414;
--bg-tertiary: #1a1a1a;
--bg-hover: #252525;
--text-primary: #ffffff;
--text-secondary: #a0a0a0;
--text-tertiary: #666666;
--accent: #0ea5e9;
--accent-hover: #38bdf8;
--success: #22c55e;
--warning: #f59e0b;
--danger: #ef4444;
--border: #2a2a2a;
--glass: rgba(255, 255, 255, 0.03);
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
font-size: 13px;
overflow: hidden;
}
/* 主容器 */
.app-container {
display: flex;
height: 100vh;
background: var(--bg-primary);
}
/* 侧边栏 */
.sidebar {
width: 56px;
background: var(--bg-secondary);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
transition: width 0.2s ease;
z-index: 100;
}
.sidebar.expanded {
width: 200px;
}
.sidebar-toggle {
height: 48px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-bottom: 1px solid var(--border);
}
.nav-item {
position: relative;
height: 48px;
display: flex;
align-items: center;
padding: 0 16px;
cursor: pointer;
transition: all 0.2s ease;
color: var(--text-secondary);
}
.nav-item:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.nav-item.active {
background: var(--glass);
color: var(--accent);
border-left: 2px solid var(--accent);
}
.nav-icon {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.nav-label {
margin-left: 12px;
white-space: nowrap;
opacity: 0;
transition: opacity 0.2s ease;
}
.sidebar.expanded .nav-label {
opacity: 1;
}
/* 主内容区 */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 顶部栏 */
.topbar {
height: 48px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
padding: 0 16px;
gap: 16px;
}
.search-box {
flex: 1;
max-width: 400px;
position: relative;
}
.search-input {
width: 100%;
height: 32px;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 6px;
padding: 0 36px 0 12px;
color: var(--text-primary);
font-size: 13px;
transition: all 0.2s ease;
}
.search-input:focus {
outline: none;
border-color: var(--accent);
background: var(--bg-hover);
}
.search-icon {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
color: var(--text-tertiary);
}
.status-indicators {
display: flex;
gap: 24px;
margin-left: auto;
}
.status-item {
display: flex;
align-items: center;
gap: 8px;
}
.status-label {
color: var(--text-secondary);
font-size: 12px;
}
.status-value {
font-weight: 600;
color: var(--text-primary);
}
/* 内容区域 */
.content-area {
flex: 1;
display: flex;
overflow: hidden;
}
/* 实时监控视图 */
.monitor-view {
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
overflow-y: auto;
}
/* 统计卡片 */
.stats-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
}
.stat-card {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 8px;
padding: 16px;
display: flex;
flex-direction: column;
gap: 8px;
transition: all 0.2s ease;
}
.stat-card:hover {
border-color: var(--accent);
transform: translateY(-2px);
}
.stat-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.stat-title {
color: var(--text-secondary);
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.stat-value {
font-size: 24px;
font-weight: 700;
color: var(--text-primary);
}
.stat-trend {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
}
.trend-up {
color: var(--success);
}
.trend-down {
color: var(--danger);
}
/* 连接列表 */
.connections-section {
flex: 1;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 8px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.section-header {
padding: 16px;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
}
.section-title {
font-size: 14px;
font-weight: 600;
}
.filter-buttons {
display: flex;
gap: 8px;
}
.filter-btn {
padding: 4px 12px;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text-secondary);
font-size: 12px;
cursor: pointer;
transition: all 0.2s ease;
}
.filter-btn:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.filter-btn.active {
background: var(--accent);
color: white;
border-color: var(--accent);
}
/* 连接表格 */
.connections-table {
flex: 1;
overflow-y: auto;
}
.table-header {
display: grid;
grid-template-columns: 200px 150px 120px 150px 100px 100px 80px;
padding: 12px 16px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border);
font-size: 12px;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
position: sticky;
top: 0;
z-index: 10;
}
.table-row {
display: grid;
grid-template-columns: 200px 150px 120px 150px 100px 100px 80px;
padding: 12px 16px;
border-bottom: 1px solid var(--border);
transition: all 0.2s ease;
cursor: pointer;
}
.table-row:hover {
background: var(--bg-hover);
}
.process-info {
display: flex;
align-items: center;
gap: 8px;
}
.process-icon {
width: 24px;
height: 24px;
background: var(--accent);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
font-weight: bold;
}
.process-name {
font-weight: 500;
}
.connection-status {
display: flex;
align-items: center;
gap: 6px;
}
.status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
}
.status-active {
background: var(--success);
box-shadow: 0 0 8px var(--success);
}
.speed-indicator {
display: flex;
flex-direction: column;
gap: 2px;
}
.speed-up {
color: var(--success);
font-size: 11px;
}
.speed-down {
color: var(--accent);
font-size: 11px;
}
/* 详情面板 */
.detail-panel {
width: 320px;
background: var(--bg-secondary);
border-left: 1px solid var(--border);
display: flex;
flex-direction: column;
transition: transform 0.3s ease;
}
.detail-panel.collapsed {
transform: translateX(100%);
}
.detail-header {
padding: 16px;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
}
.detail-content {
flex: 1;
overflow-y: auto;
padding: 16px;
}
.detail-section {
margin-bottom: 24px;
}
.detail-section-title {
font-size: 12px;
text-transform: uppercase;
color: var(--text-secondary);
margin-bottom: 12px;
letter-spacing: 0.5px;
}
.detail-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid var(--border);
}
.detail-label {
color: var(--text-secondary);
font-size: 12px;
}
.detail-value {
color: var(--text-primary);
font-size: 12px;
font-weight: 500;
}
/* 图表容器 */
.chart-container {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 8px;
padding: 16px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-tertiary);
}
/* 浮动操作按钮 */
.fab {
position: fixed;
bottom: 24px;
right: 24px;
width: 48px;
height: 48px;
background: var(--accent);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.4);
transition: all 0.2s ease;
}
.fab:hover {
background: var(--accent-hover);
transform: scale(1.1);
}
/* 响应式调整 */
@media (max-width: 1200px) {
.table-header,
.table-row {
grid-template-columns: 180px 120px 100px 120px 80px 80px 60px;
}
.detail-panel {
width: 280px;
}
}
@media (max-width: 768px) {
.sidebar {
position: absolute;
height: 100%;
transform: translateX(-100%);
}
.sidebar.expanded {
transform: translateX(0);
}
.stats-cards {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="app-container">
<!-- 侧边栏 -->
<div class="sidebar" id="sidebar">
<div class="sidebar-toggle" onclick="toggleSidebar()">
<span class="nav-icon">☰</span>
</div>
<div class="nav-item active">
<span class="nav-icon">📊</span>
<span class="nav-label">实时监控</span>
</div>
<div class="nav-item">
<span class="nav-icon">🛡️</span>
<span class="nav-label">防火墙规则</span>
</div>
<div class="nav-item">
<span class="nav-icon">📈</span>
<span class="nav-label">流量分析</span>
</div>
<div class="nav-item">
<span class="nav-icon">🌍</span>
<span class="nav-label">连接地图</span>
</div>
<div class="nav-item">
<span class="nav-icon">📝</span>
<span class="nav-label">日志审计</span>
</div>
<div class="nav-item">
<span class="nav-icon">⚙️</span>
<span class="nav-label">设置</span>
</div>
</div>
<!-- 主内容区 -->
<div class="main-content">
<!-- 顶部栏 -->
<div class="topbar">
<div class="search-box">
<input type="text" class="search-input" placeholder="搜索进程、IP、域名...">
<span class="search-icon">🔍</span>
</div>
<div class="status-indicators">
<div class="status-item">
<span class="status-label">活动连接</span>
<span class="status-value">127</span>
</div>
<div class="status-item">
<span class="status-label">总速率</span>
<span class="status-value">12.4 MB/s</span>
</div>
<div class="status-item">
<span class="status-label">阻止</span>
<span class="status-value" style="color: var(--danger)">23</span>
</div>
</div>
</div>
<!-- 内容区域 -->
<div class="content-area">
<!-- 监控视图 -->
<div class="monitor-view">
<!-- 统计卡片 -->
<div class="stats-cards">
<div class="stat-card">
<div class="stat-header">
<span class="stat-title">上传速率</span>
<span class="stat-icon">↑</span>
</div>
<div class="stat-value">3.2 MB/s</div>
<div class="stat-trend trend-up">
<span>↑ 12%</span>
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<span class="stat-title">下载速率</span>
<span class="stat-icon">↓</span>
</div>
<div class="stat-value">9.2 MB/s</div>
<div class="stat-trend trend-down">
<span>↓ 5%</span>
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<span class="stat-title">活跃进程</span>
<span class="stat-icon">💻</span>
</div>
<div class="stat-value">48</div>
<div class="stat-trend">
<span style="color: var(--text-secondary)">→ 0%</span>
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<span class="stat-title">威胁检测</span>
<span class="stat-icon">⚠️</span>
</div>
<div class="stat-value">2</div>
<div class="stat-trend trend-up" style="color: var(--warning)">
<span>新增警告</span>
</div>
</div>
</div>
<!-- 实时速率图表 -->
<div class="chart-container">
<span>实时网络速率图表 (ECharts)</span>
</div>
<!-- 连接列表 -->
<div class="connections-section">
<div class="section-header">
<h3 class="section-title">活动连接</h3>
<div class="filter-buttons">
<button class="filter-btn active">全部</button>
<button class="filter-btn">TCP</button>
<button class="filter-btn">UDP</button>
<button class="filter-btn">已阻止</button>
</div>
</div>
<div class="connections-table">
<div class="table-header">
<div>进程</div>
<div>远程地址</div>
<div>域名</div>
<div>本地端口</div>
<div>协议</div>
<div>速率</div>
<div>状态</div>
</div>
<div class="table-row" onclick="showDetail()">
<div class="process-info">
<div class="process-icon">Ch</div>
<span class="process-name">Chrome.exe</span>
</div>
<div>142.251.42.142</div>
<div>youtube.com</div>
<div>58432:443</div>
<div>TCP</div>
<div class="speed-indicator">
<span class="speed-up">↑ 124 KB/s</span>
<span class="speed-down">↓ 2.1 MB/s</span>
</div>
<div class="connection-status">
<span class="status-dot status-active"></span>
<span>活动</span>
</div>
</div>
<div class="table-row">
<div class="process-info">
<div class="process-icon" style="background: var(--success)">St</div>
<span class="process-name">Steam.exe</span>
</div>
<div>185.25.182.77</div>
<div>steamcdn.com</div>
<div>62145:443</div>
<div>TCP</div>
<div class="speed-indicator">
<span class="speed-up">↑ 12 KB/s</span>
<span class="speed-down">↓ 6.8 MB/s</span>
</div>
<div class="connection-status">
<span class="status-dot status-active"></span>
<span>活动</span>
</div>
</div>
<div class="table-row">
<div class="process-info">
<div class="process-icon" style="background: var(--warning)">Un</div>
<span class="process-name">Unknown.exe</span>
</div>
<div>45.33.32.156</div>
<div>-</div>
<div>51234:80</div>
<div>TCP</div>
<div class="speed-indicator">
<span class="speed-up">↑ 524 KB/s</span>
<span class="speed-down">↓ 0 KB/s</span>
</div>
<div class="connection-status">
<span class="status-dot" style="background: var(--warning)"></span>
<span style="color: var(--warning)">可疑</span>
</div>
</div>
</div>
</div>
</div>
<!-- 详情面板 -->
<div class="detail-panel collapsed" id="detailPanel">
<div class="detail-header">
<h3>连接详情</h3>
<span style="cursor: pointer" onclick="hideDetail()">✕</span>
</div>
<div class="detail-content">
<div class="detail-section">
<h4 class="detail-section-title">进程信息</h4>
<div class="detail-item">
<span class="detail-label">进程名称</span>
<span class="detail-value">Chrome.exe</span>
</div>
<div class="detail-item">
<span class="detail-label">PID</span>
<span class="detail-value">12456</span>
</div>
<div class="detail-item">
<span class="detail-label">路径</span>
<span class="detail-value">C:\Program Files\...</span>
</div>
<div class="detail-item">
<span class="detail-label">启动时间</span>
<span class="detail-value">2025-01-15 09:23</span>
</div>
</div>
<div class="detail-section">
<h4 class="detail-section-title">连接信息</h4>
<div class="detail-item">
<span class="detail-label">远程IP</span>
<span class="detail-value">142.251.42.142</span>
</div>
<div class="detail-item">
<span class="detail-label">地理位置</span>
<span class="detail-value">美国 加利福尼亚</span>
</div>
<div class="detail-item">
<span class="detail-label">建立时间</span>
<span class="detail-value">10分钟前</span>
</div>
<div class="detail-item">
<span class="detail-label">总流量</span>
<span class="detail-value">125.4 MB</span>
</div>
</div>
<div class="detail-section">
<h4 class="detail-section-title">流量统计</h4>
<div class="chart-container" style="height: 150px;">
<span>连接流量图表</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 浮动操作按钮 -->
<div class="fab">
<span>+</span>
</div>
</div>
<script>
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
sidebar.classList.toggle('expanded');
}
function showDetail() {
const panel = document.getElementById('detailPanel');
panel.classList.remove('collapsed');
}
function hideDetail() {
const panel = document.getElementById('detailPanel');
panel.classList.add('collapsed');
}
// 模拟实时数据更新
setInterval(() => {
const values = document.querySelectorAll('.stat-value');
values[0].textContent = (Math.random() * 5).toFixed(1) + ' MB/s';
values[1].textContent = (Math.random() * 10 + 5).toFixed(1) + ' MB/s';
}, 2000);
</script>
</body>
</html>
@@ -0,0 +1,861 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NetGuard Pro - 网络管理工具</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #0f172a;
--bg-secondary: #1e293b;
--bg-tertiary: #334155;
--text-primary: #f8fafc;
--text-secondary: #cbd5e1;
--text-muted: #64748b;
--accent-primary: #3b82f6;
--accent-secondary: #06b6d4;
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
--border: #475569;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
--blur: blur(8px);
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
overflow: hidden;
height: 100vh;
}
.app-container {
display: flex;
height: 100vh;
width: 100vw;
}
/* 顶部标题栏 */
.title-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 40px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
z-index: 1000;
-webkit-app-region: drag;
}
.app-logo {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: 14px;
}
.logo-icon {
width: 20px;
height: 20px;
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
}
.window-controls {
display: flex;
gap: 8px;
-webkit-app-region: no-drag;
}
.control-btn {
width: 12px;
height: 12px;
border-radius: 50%;
cursor: pointer;
transition: opacity 0.2s;
}
.control-btn:hover { opacity: 0.8; }
.control-btn.close { background: #ef4444; }
.control-btn.minimize { background: #f59e0b; }
.control-btn.maximize { background: #10b981; }
/* 侧边栏 */
.sidebar {
width: 240px;
background: var(--bg-secondary);
border-right: 1px solid var(--border);
padding-top: 40px;
display: flex;
flex-direction: column;
transition: width 0.3s ease;
}
.sidebar.collapsed {
width: 60px;
}
.sidebar-header {
padding: 20px 16px;
border-bottom: 1px solid var(--border);
}
.status-overview {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 16px;
}
.status-card {
background: var(--bg-tertiary);
border-radius: 8px;
padding: 12px;
text-align: center;
}
.status-value {
font-size: 18px;
font-weight: 600;
color: var(--accent-primary);
}
.status-label {
font-size: 11px;
color: var(--text-muted);
margin-top: 4px;
}
.nav-menu {
flex: 1;
padding: 16px 0;
}
.nav-item {
display: flex;
align-items: center;
padding: 12px 16px;
margin: 2px 8px;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
color: var(--text-secondary);
}
.nav-item:hover {
background: var(--bg-tertiary);
color: var(--text-primary);
}
.nav-item.active {
background: var(--accent-primary);
color: white;
}
.nav-icon {
width: 20px;
height: 20px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
}
/* 主内容区 */
.main-content {
flex: 1;
padding-top: 40px;
background: var(--bg-primary);
overflow: hidden;
display: flex;
flex-direction: column;
}
.content-header {
padding: 20px 24px;
border-bottom: 1px solid var(--border);
background: var(--bg-secondary);
}
.page-title {
font-size: 24px;
font-weight: 600;
margin-bottom: 8px;
}
.page-description {
color: var(--text-muted);
font-size: 14px;
}
.content-body {
flex: 1;
padding: 24px;
overflow-y: auto;
}
/* 卡片组件 */
.card {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
box-shadow: var(--shadow);
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.card-title {
font-size: 16px;
font-weight: 600;
}
.card-actions {
display: flex;
gap: 8px;
}
.btn {
padding: 6px 12px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
font-weight: 500;
transition: all 0.2s;
}
.btn-primary {
background: var(--accent-primary);
color: white;
}
.btn-secondary {
background: var(--bg-tertiary);
color: var(--text-secondary);
}
.btn:hover {
transform: translateY(-1px);
box-shadow: var(--shadow);
}
/* 实时连接表格 */
.connections-grid {
display: grid;
gap: 12px;
}
.connection-item {
background: var(--bg-tertiary);
border-radius: 8px;
padding: 16px;
display: grid;
grid-template-columns: auto 1fr auto auto auto;
align-items: center;
gap: 16px;
transition: all 0.2s;
}
.connection-item:hover {
background: var(--bg-primary);
transform: translateY(-1px);
}
.connection-status {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--success);
}
.connection-info {
display: flex;
flex-direction: column;
gap: 4px;
}
.connection-process {
font-weight: 500;
font-size: 14px;
}
.connection-target {
color: var(--text-muted);
font-size: 12px;
}
.connection-protocol {
background: var(--accent-primary);
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 10px;
font-weight: 500;
}
.connection-speed {
text-align: right;
font-size: 12px;
}
.speed-up { color: var(--success); }
.speed-down { color: var(--accent-secondary); }
/* 图表区域 */
.chart-container {
height: 200px;
background: var(--bg-tertiary);
border-radius: 8px;
padding: 16px;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
}
/* 进程列表 */
.process-grid {
display: grid;
gap: 8px;
}
.process-item {
background: var(--bg-tertiary);
border-radius: 8px;
padding: 12px 16px;
display: grid;
grid-template-columns: 32px 1fr auto auto;
align-items: center;
gap: 12px;
}
.process-icon {
width: 24px;
height: 24px;
background: var(--accent-primary);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
}
.process-details h4 {
font-size: 14px;
margin-bottom: 2px;
}
.process-details p {
font-size: 11px;
color: var(--text-muted);
}
.process-stats {
text-align: right;
font-size: 12px;
}
.toggle-btn {
width: 40px;
height: 20px;
background: var(--bg-primary);
border-radius: 10px;
position: relative;
cursor: pointer;
transition: background 0.3s;
}
.toggle-btn.active {
background: var(--success);
}
.toggle-btn::after {
content: '';
position: absolute;
width: 16px;
height: 16px;
background: white;
border-radius: 50%;
top: 2px;
left: 2px;
transition: transform 0.3s;
}
.toggle-btn.active::after {
transform: translateX(20px);
}
/* 响应式设计 */
@media (max-width: 1200px) {
.sidebar {
width: 60px;
}
.status-overview {
grid-template-columns: 1fr;
}
.nav-item span {
display: none;
}
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: var(--bg-secondary);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
/* 动画 */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.pulse {
animation: pulse 2s infinite;
}
/* 工具提示 */
.tooltip {
position: relative;
}
.tooltip::after {
content: attr(data-tooltip);
position: absolute;
bottom: -30px;
left: 50%;
transform: translateX(-50%);
background: var(--bg-primary);
color: var(--text-primary);
padding: 4px 8px;
border-radius: 4px;
font-size: 11px;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s;
z-index: 1000;
}
.tooltip:hover::after {
opacity: 1;
}
</style>
</head>
<body>
<div class="app-container">
<!-- 标题栏 -->
<div class="title-bar">
<div class="app-logo">
<div class="logo-icon">🛡️</div>
<span>NetGuard Pro</span>
</div>
<div class="window-controls">
<div class="control-btn minimize"></div>
<div class="control-btn maximize"></div>
<div class="control-btn close"></div>
</div>
</div>
<!-- 侧边栏 -->
<div class="sidebar" id="sidebar">
<div class="sidebar-header">
<div class="status-overview">
<div class="status-card">
<div class="status-value">24</div>
<div class="status-label">活跃连接</div>
</div>
<div class="status-card">
<div class="status-value">1.2MB/s</div>
<div class="status-label">当前流量</div>
</div>
</div>
</div>
<nav class="nav-menu">
<div class="nav-item active" data-page="dashboard">
<div class="nav-icon">📊</div>
<span>实时监控</span>
</div>
<div class="nav-item" data-page="processes">
<div class="nav-icon">⚙️</div>
<span>进程管理</span>
</div>
<div class="nav-item" data-page="firewall">
<div class="nav-icon">🛡️</div>
<span>防火墙</span>
</div>
<div class="nav-item" data-page="analytics">
<div class="nav-icon">📈</div>
<span>流量分析</span>
</div>
<div class="nav-item" data-page="network-map">
<div class="nav-icon">🌐</div>
<span>连接地图</span>
</div>
<div class="nav-item" data-page="settings">
<div class="nav-icon">⚡</div>
<span>系统设置</span>
</div>
</nav>
</div>
<!-- 主内容区 -->
<div class="main-content">
<div class="content-header">
<h1 class="page-title" id="pageTitle">实时网络监控</h1>
<p class="page-description" id="pageDescription">监控当前系统的网络连接状态和流量情况</p>
</div>
<div class="content-body" id="contentBody">
<!-- 实时监控页面 -->
<div id="dashboard-content">
<!-- 活跃连接 -->
<div class="card">
<div class="card-header">
<h3 class="card-title">活跃连接</h3>
<div class="card-actions">
<button class="btn btn-secondary tooltip" data-tooltip="刷新连接列表">🔄</button>
<button class="btn btn-secondary tooltip" data-tooltip="导出连接数据">📥</button>
</div>
</div>
<div class="connections-grid">
<div class="connection-item">
<div class="connection-status pulse"></div>
<div class="connection-info">
<div class="connection-process">Chrome.exe (PID: 12340)</div>
<div class="connection-target">192.168.1.1:443 → www.google.com</div>
</div>
<div class="connection-protocol">HTTPS</div>
<div class="connection-speed">
<div class="speed-up">↑ 245 KB/s</div>
<div class="speed-down">↓ 1.2 MB/s</div>
</div>
<button class="btn btn-secondary">详情</button>
</div>
<div class="connection-item">
<div class="connection-status"></div>
<div class="connection-info">
<div class="connection-process">Steam.exe (PID: 8956)</div>
<div class="connection-target">103.28.54.10:27030 → Steam CDN</div>
</div>
<div class="connection-protocol">TCP</div>
<div class="connection-speed">
<div class="speed-up">↑ 12 KB/s</div>
<div class="speed-down">↓ 856 KB/s</div>
</div>
<button class="btn btn-secondary">详情</button>
</div>
<div class="connection-item">
<div class="connection-status"></div>
<div class="connection-info">
<div class="connection-process">System (PID: 4)</div>
<div class="connection-target">8.8.8.8:53 → DNS</div>
</div>
<div class="connection-protocol">UDP</div>
<div class="connection-speed">
<div class="speed-up">↑ 2 KB/s</div>
<div class="speed-down">↓ 1 KB/s</div>
</div>
<button class="btn btn-secondary">详情</button>
</div>
</div>
</div>
<!-- 实时流量图表 -->
<div class="card">
<div class="card-header">
<h3 class="card-title">实时流量监控</h3>
<div class="card-actions">
<button class="btn btn-secondary">1分钟</button>
<button class="btn btn-primary">5分钟</button>
<button class="btn btn-secondary">15分钟</button>
</div>
</div>
<div class="chart-container">
📈 实时流量图表 (ECharts 集成区域)
</div>
</div>
<!-- 进程网络使用排行 -->
<div class="card">
<div class="card-header">
<h3 class="card-title">网络使用排行</h3>
<div class="card-actions">
<button class="btn btn-secondary">按流量</button>
<button class="btn btn-secondary">按连接数</button>
</div>
</div>
<div class="process-grid">
<div class="process-item">
<div class="process-icon">🌐</div>
<div class="process-details">
<h4>Google Chrome</h4>
<p>C:\Program Files\Google\Chrome\Application\chrome.exe</p>
</div>
<div class="process-stats">
<div>2.3 MB/s</div>
<div style="color: var(--text-muted);">8 连接</div>
</div>
<div class="toggle-btn active"></div>
</div>
<div class="process-item">
<div class="process-icon">🎮</div>
<div class="process-details">
<h4>Steam</h4>
<p>C:\Program Files (x86)\Steam\steam.exe</p>
</div>
<div class="process-stats">
<div>856 KB/s</div>
<div style="color: var(--text-muted);">3 连接</div>
</div>
<div class="toggle-btn active"></div>
</div>
<div class="process-item">
<div class="process-icon">💬</div>
<div class="process-details">
<h4>Microsoft Teams</h4>
<p>C:\Users\User\AppData\Local\Microsoft\Teams\current\Teams.exe</p>
</div>
<div class="process-stats">
<div>234 KB/s</div>
<div style="color: var(--text-muted);">5 连接</div>
</div>
<div class="toggle-btn active"></div>
</div>
</div>
</div>
</div>
<!-- 其他页面内容将通过 JavaScript 动态加载 -->
<div id="processes-content" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="card-title">进程网络权限管理</h3>
<div class="card-actions">
<button class="btn btn-primary">添加规则</button>
<button class="btn btn-secondary">批量操作</button>
</div>
</div>
<p style="color: var(--text-muted); text-align: center; padding: 40px;">进程管理功能区域 - 显示所有联网进程的详细信息和权限控制</p>
</div>
</div>
<div id="firewall-content" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="card-title">防火墙规则管理</h3>
<div class="card-actions">
<button class="btn btn-primary">新建规则</button>
<button class="btn btn-secondary">导入/导出</button>
</div>
</div>
<p style="color: var(--text-muted); text-align: center; padding: 40px;">防火墙管理功能区域 - 统一的规则管理界面</p>
</div>
</div>
<div id="analytics-content" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="card-title">流量分析与统计</h3>
<div class="card-actions">
<button class="btn btn-secondary">日报告</button>
<button class="btn btn-secondary">周报告</button>
<button class="btn btn-secondary">月报告</button>
</div>
</div>
<p style="color: var(--text-muted); text-align: center; padding: 40px;">流量分析功能区域 - 历史数据、趋势分析、可视化图表</p>
</div>
</div>
<div id="network-map-content" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="card-title">网络连接地图</h3>
<div class="card-actions">
<button class="btn btn-secondary">拓扑视图</button>
<button class="btn btn-secondary">地理视图</button>
</div>
</div>
<p style="color: var(--text-muted); text-align: center; padding: 40px;">网络地图功能区域 - 连接可视化、地理位置映射</p>
</div>
</div>
<div id="settings-content" style="display: none;">
<div class="card">
<div class="card-header">
<h3 class="card-title">系统设置</h3>
</div>
<p style="color: var(--text-muted); text-align: center; padding: 40px;">系统设置功能区域 - 配置选项、日志管理、系统偏好</p>
</div>
</div>
</div>
</div>
</div>
<script>
// 页面切换功能
const navItems = document.querySelectorAll('.nav-item');
const pageTitle = document.getElementById('pageTitle');
const pageDescription = document.getElementById('pageDescription');
const contentSections = {
'dashboard': {
title: '实时网络监控',
description: '监控当前系统的网络连接状态和流量情况',
element: document.getElementById('dashboard-content')
},
'processes': {
title: '进程网络管理',
description: '管理应用程序的网络访问权限和行为监控',
element: document.getElementById('processes-content')
},
'firewall': {
title: '防火墙控制中心',
description: '统一管理和配置Windows防火墙规则',
element: document.getElementById('firewall-content')
},
'analytics': {
title: '流量分析报告',
description: '深入分析网络使用模式和历史趋势',
element: document.getElementById('analytics-content')
},
'network-map': {
title: '网络连接地图',
description: '可视化网络连接和地理位置分布',
element: document.getElementById('network-map-content')
},
'settings': {
title: '系统设置',
description: '配置应用程序行为和系统偏好',
element: document.getElementById('settings-content')
}
};
navItems.forEach(item => {
item.addEventListener('click', () => {
// 移除所有活跃状态
navItems.forEach(nav => nav.classList.remove('active'));
// 添加当前活跃状态
item.classList.add('active');
// 获取页面标识
const page = item.getAttribute('data-page');
const pageInfo = contentSections[page];
if (pageInfo) {
// 更新页面标题和描述
pageTitle.textContent = pageInfo.title;
pageDescription.textContent = pageInfo.description;
// 隐藏所有内容区域
Object.values(contentSections).forEach(section => {
section.element.style.display = 'none';
});
// 显示当前页面内容
pageInfo.element.style.display = 'block';
}
});
});
// 切换按钮功能
document.querySelectorAll('.toggle-btn').forEach(btn => {
btn.addEventListener('click', () => {
btn.classList.toggle('active');
});
});
// 模拟实时数据更新
function updateRealTimeData() {
// 更新状态卡片数据
const connectionCount = document.querySelector('.status-value');
const speedValue = document.querySelectorAll('.status-value')[1];
if (connectionCount && speedValue) {
const connections = Math.floor(Math.random() * 10) + 20;
const speed = (Math.random() * 2 + 0.5).toFixed(1);
connectionCount.textContent = connections;
speedValue.textContent = `${speed}MB/s`;
}
// 更新连接状态指示器
document.querySelectorAll('.connection-status').forEach(status => {
if (Math.random() > 0.8) {
status.classList.toggle('pulse');
}
});
}
// 每3秒更新一次数据
setInterval(updateRealTimeData, 3000);
// 窗口控制按钮功能
document.querySelector('.control-btn.minimize').addEventListener('click', () => {
console.log('窗口最小化');
});
document.querySelector('.control-btn.maximize').addEventListener('click', () => {
console.log('窗口最大化/还原');
});
document.querySelector('.control-btn.close').addEventListener('click', () => {
console.log('关闭窗口');
});
// 响应式侧边栏
function handleResize() {
const sidebar = document.getElementById('sidebar');
if (window.innerWidth < 1200) {
sidebar.classList.add('collapsed');
} else {
sidebar.classList.remove('collapsed');
}
}
window.addEventListener('resize', handleResize);
handleResize(); // 初始检查
</script>
</body>
</html>
@@ -0,0 +1,746 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NetGuard Pro - 网络监控工具</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.3/echarts.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
background: #0a0a0a;
color: #e5e5e5;
overflow: hidden;
height: 100vh;
}
.app-container {
display: flex;
height: 100vh;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
}
/* 侧边栏 */
.sidebar {
width: 280px;
background: rgba(20, 20, 20, 0.95);
backdrop-filter: blur(20px);
border-right: 1px solid #333;
display: flex;
flex-direction: column;
transition: width 0.3s ease;
}
.sidebar.collapsed {
width: 60px;
}
.sidebar-header {
padding: 20px;
border-bottom: 1px solid #333;
display: flex;
align-items: center;
gap: 12px;
}
.logo {
width: 32px;
height: 32px;
background: linear-gradient(45deg, #00d4ff, #0099cc);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
}
.app-title {
font-size: 18px;
font-weight: 600;
color: #fff;
}
.nav-menu {
flex: 1;
padding: 20px 0;
}
.nav-item {
display: flex;
align-items: center;
padding: 12px 20px;
color: #ccc;
text-decoration: none;
transition: all 0.2s ease;
border-left: 3px solid transparent;
gap: 12px;
}
.nav-item:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
}
.nav-item.active {
background: rgba(0, 212, 255, 0.1);
color: #00d4ff;
border-left-color: #00d4ff;
}
.nav-icon {
width: 20px;
height: 20px;
}
/* 主内容区 */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 顶部工具栏 */
.toolbar {
height: 60px;
background: rgba(20, 20, 20, 0.95);
backdrop-filter: blur(20px);
border-bottom: 1px solid #333;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
}
.search-box {
flex: 1;
max-width: 400px;
position: relative;
}
.search-input {
width: 100%;
background: rgba(255, 255, 255, 0.1);
border: 1px solid #444;
border-radius: 8px;
padding: 8px 16px 8px 40px;
color: #fff;
font-size: 14px;
}
.search-input::placeholder {
color: #888;
}
.search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: #888;
}
.toolbar-actions {
display: flex;
gap: 12px;
align-items: center;
}
.status-indicator {
display: flex;
align-items: center;
gap: 8px;
background: rgba(0, 212, 255, 0.1);
padding: 6px 12px;
border-radius: 6px;
font-size: 12px;
}
.status-dot {
width: 8px;
height: 8px;
background: #00ff88;
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* 内容区域 */
.content-area {
flex: 1;
padding: 20px;
overflow: auto;
display: grid;
grid-template-columns: 2fr 1fr;
gap: 20px;
}
/* 卡片样式 */
.card {
background: rgba(30, 30, 30, 0.8);
backdrop-filter: blur(20px);
border: 1px solid #333;
border-radius: 12px;
padding: 20px;
transition: all 0.2s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 32px rgba(0, 212, 255, 0.1);
}
.card-header {
display: flex;
justify-content: between;
align-items: center;
margin-bottom: 16px;
}
.card-title {
font-size: 16px;
font-weight: 600;
color: #fff;
}
/* 实时连接表格 */
.connections-table {
width: 100%;
border-collapse: collapse;
}
.connections-table th,
.connections-table td {
padding: 8px 12px;
text-align: left;
border-bottom: 1px solid #333;
}
.connections-table th {
background: rgba(255, 255, 255, 0.05);
font-weight: 600;
font-size: 12px;
color: #ccc;
text-transform: uppercase;
}
.connections-table td {
font-size: 13px;
}
.process-name {
color: #00d4ff;
font-weight: 500;
}
.connection-status {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 500;
}
.status-established {
background: rgba(0, 255, 136, 0.2);
color: #00ff88;
}
.status-listening {
background: rgba(255, 193, 7, 0.2);
color: #ffc107;
}
/* 统计卡片 */
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 20px;
}
.stat-card {
background: rgba(40, 40, 40, 0.6);
padding: 16px;
border-radius: 8px;
border: 1px solid #444;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #00d4ff;
margin-bottom: 4px;
}
.stat-label {
font-size: 12px;
color: #888;
text-transform: uppercase;
}
/* 图表容器 */
.chart-container {
height: 200px;
margin-top: 16px;
}
/* 进程列表 */
.process-item {
display: flex;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #333;
gap: 12px;
}
.process-icon {
width: 24px;
height: 24px;
background: #00d4ff;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
font-weight: bold;
}
.process-info {
flex: 1;
}
.process-name-text {
font-size: 13px;
font-weight: 500;
color: #fff;
}
.process-path {
font-size: 11px;
color: #888;
}
.process-stats {
text-align: right;
font-size: 11px;
}
.bandwidth {
color: #00ff88;
}
/* 底部状态栏 */
.status-bar {
height: 30px;
background: rgba(20, 20, 20, 0.95);
border-top: 1px solid #333;
display: flex;
align-items: center;
padding: 0 20px;
font-size: 12px;
color: #888;
gap: 20px;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.content-area {
grid-template-columns: 1fr;
}
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #444;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #555;
}
</style>
</head>
<body>
<div class="app-container">
<!-- 侧边栏 -->
<div class="sidebar">
<div class="sidebar-header">
<div class="logo">N</div>
<div class="app-title">NetGuard Pro</div>
</div>
<nav class="nav-menu">
<a href="#" class="nav-item active">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1v-6zM14 9a1 1 0 00-1 1v6a1 1 0 001 1h2a1 1 0 001-1v-6a1 1 0 00-1-1h-2z"/>
</svg>
仪表板
</a>
<a href="#" class="nav-item">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M3 3a1 1 0 000 2v8a2 2 0 002 2h2.586l-1.293 1.293a1 1 0 101.414 1.414L10 15.414l2.293 2.293a1 1 0 001.414-1.414L12.414 15H15a2 2 0 002-2V5a1 1 0 100-2H3zm11.707 4.707a1 1 0 00-1.414-1.414L10 9.586 8.707 8.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"/>
</svg>
实时连接
</a>
<a href="#" class="nav-item">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
进程监控
</a>
<a href="#" class="nav-item">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M2.166 4.999A11.954 11.954 0 0010 1.944 11.954 11.954 0 0017.834 5c.11.65.166 1.32.166 2.001 0 5.225-3.34 9.67-8 11.317C5.34 16.67 2 12.225 2 7c0-.682.057-1.35.166-2.001zm11.541 3.708a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"/>
</svg>
防火墙规则
</a>
<a href="#" class="nav-item">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/>
</svg>
流量分析
</a>
<a href="#" class="nav-item">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"/>
</svg>
设置
</a>
</nav>
</div>
<!-- 主内容区 -->
<div class="main-content">
<!-- 顶部工具栏 -->
<div class="toolbar">
<div class="search-box">
<svg class="search-icon" width="16" height="16" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"/>
</svg>
<input type="text" class="search-input" placeholder="搜索进程、IP 地址或域名...">
</div>
<div class="toolbar-actions">
<div class="status-indicator">
<div class="status-dot"></div>
监控中
</div>
</div>
</div>
<!-- 内容区域 -->
<div class="content-area">
<!-- 左侧主区域 -->
<div>
<!-- 统计卡片 -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value" id="activeConnections">247</div>
<div class="stat-label">活跃连接</div>
</div>
<div class="stat-card">
<div class="stat-value" id="totalProcesses">42</div>
<div class="stat-label">联网进程</div>
</div>
<div class="stat-card">
<div class="stat-value" id="uploadSpeed">1.2 MB/s</div>
<div class="stat-label">上传速度</div>
</div>
<div class="stat-card">
<div class="stat-value" id="downloadSpeed">5.8 MB/s</div>
<div class="stat-label">下载速度</div>
</div>
</div>
<!-- 实时连接表格 -->
<div class="card">
<div class="card-header">
<h3 class="card-title">实时连接</h3>
</div>
<div style="overflow-x: auto;">
<table class="connections-table">
<thead>
<tr>
<th>进程</th>
<th>本地地址</th>
<th>远程地址</th>
<th>状态</th>
<th>带宽</th>
</tr>
</thead>
<tbody id="connectionsTableBody">
<tr>
<td><span class="process-name">chrome.exe</span></td>
<td>192.168.1.100:52341</td>
<td>142.250.191.14:443 (google.com)</td>
<td><span class="connection-status status-established">已连接</span></td>
<td class="bandwidth">↓ 850 KB/s</td>
</tr>
<tr>
<td><span class="process-name">steam.exe</span></td>
<td>192.168.1.100:27036</td>
<td>208.78.164.9:27017 (steam)</td>
<td><span class="connection-status status-established">已连接</span></td>
<td class="bandwidth">↓ 45 KB/s</td>
</tr>
<tr>
<td><span class="process-name">discord.exe</span></td>
<td>192.168.1.100:0</td>
<td>162.159.130.233:443</td>
<td><span class="connection-status status-listening">监听</span></td>
<td class="bandwidth">↓ 12 KB/s</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 流量图表 -->
<div class="card" style="margin-top: 20px;">
<div class="card-header">
<h3 class="card-title">网络流量趋势</h3>
</div>
<div class="chart-container" id="trafficChart"></div>
</div>
</div>
<!-- 右侧边栏 -->
<div>
<!-- 活跃进程 -->
<div class="card">
<div class="card-header">
<h3 class="card-title">活跃进程</h3>
</div>
<div id="processesListContainer">
<div class="process-item">
<div class="process-icon">C</div>
<div class="process-info">
<div class="process-name-text">chrome.exe</div>
<div class="process-path">C:\Program Files\Google\Chrome\Application\</div>
</div>
<div class="process-stats">
<div class="bandwidth">↓ 850 KB/s</div>
<div style="color: #888;">23 连接</div>
</div>
</div>
<div class="process-item">
<div class="process-icon">S</div>
<div class="process-info">
<div class="process-name-text">steam.exe</div>
<div class="process-path">C:\Program Files (x86)\Steam\</div>
</div>
<div class="process-stats">
<div class="bandwidth">↓ 45 KB/s</div>
<div style="color: #888;">3 连接</div>
</div>
</div>
<div class="process-item">
<div class="process-icon">D</div>
<div class="process-info">
<div class="process-name-text">discord.exe</div>
<div class="process-path">C:\Users\User\AppData\Local\Discord\</div>
</div>
<div class="process-stats">
<div class="bandwidth">↓ 12 KB/s</div>
<div style="color: #888;">2 连接</div>
</div>
</div>
</div>
</div>
<!-- 地理分布 -->
<div class="card" style="margin-top: 20px;">
<div class="card-header">
<h3 class="card-title">连接地理分布</h3>
</div>
<div class="chart-container" id="geoChart"></div>
</div>
<!-- 防火墙状态 -->
<div class="card" style="margin-top: 20px;">
<div class="card-header">
<h3 class="card-title">防火墙状态</h3>
</div>
<div style="padding: 10px 0;">
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
<span>已阻止连接</span>
<span style="color: #ff6b6b;">142</span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
<span>活跃规则</span>
<span style="color: #00d4ff;">28</span>
</div>
<div style="display: flex; justify-content: space-between;">
<span>白名单进程</span>
<span style="color: #00ff88;">15</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 底部状态栏 -->
<div class="status-bar">
<span>监控状态: 正常</span>
<span>|</span>
<span>ETW 事件: 1,247</span>
<span>|</span>
<span>内存使用: 45.2 MB</span>
<span>|</span>
<span>版本: 1.0.0</span>
</div>
<script>
// 初始化图表
function initCharts() {
// 流量趋势图
const trafficChart = echarts.init(document.getElementById('trafficChart'), 'dark');
const trafficOption = {
backgroundColor: 'transparent',
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '10%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['00:00', '02:00', '04:00', '06:00', '08:00', '10:00', '12:00'],
axisLine: { lineStyle: { color: '#333' } },
axisLabel: { color: '#888' }
},
yAxis: {
type: 'value',
axisLine: { lineStyle: { color: '#333' } },
axisLabel: { color: '#888' },
splitLine: { lineStyle: { color: '#333' } }
},
series: [
{
name: '下载',
type: 'line',
smooth: true,
lineStyle: { color: '#00d4ff', width: 2 },
areaStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(0, 212, 255, 0.3)' },
{ offset: 1, color: 'rgba(0, 212, 255, 0.0)' }
]
}
},
data: [1.2, 2.1, 3.5, 2.8, 4.2, 5.8, 3.9]
},
{
name: '上传',
type: 'line',
smooth: true,
lineStyle: { color: '#00ff88', width: 2 },
areaStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(0, 255, 136, 0.3)' },
{ offset: 1, color: 'rgba(0, 255, 136, 0.0)' }
]
}
},
data: [0.5, 0.8, 1.2, 0.9, 1.5, 1.8, 1.2]
}
]
};
trafficChart.setOption(trafficOption);
// 地理分布图
const geoChart = echarts.init(document.getElementById('geoChart'), 'dark');
const geoOption = {
backgroundColor: 'transparent',
series: [{
type: 'pie',
radius: ['40%', '70%'],
center: ['50%', '50%'],
data: [
{ value: 45, name: '美国', itemStyle: { color: '#00d4ff' } },
{ value: 25, name: '中国', itemStyle: { color: '#00ff88' } },
{ value: 15, name: '日本', itemStyle: { color: '#ffc107' } },
{ value: 10, name: '德国', itemStyle: { color: '#ff6b6b' } },
{ value: 5, name: '其他', itemStyle: { color: '#888' } }
],
label: {
show: true,
color: '#e5e5e5',
fontSize: 12
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
};
geoChart.setOption(geoOption);
}
// 模拟实时数据更新
function simulateRealTimeData() {
const connections = document.getElementById('activeConnections');
const uploadSpeed = document.getElementById('uploadSpeed');
const downloadSpeed = document.getElementById('downloadSpeed');
setInterval(() => {
// 随机更新数据
const newConnections = Math.floor(Math.random() * 50) + 220;
const newUpload = (Math.random() * 2 + 0.5).toFixed(1);
const newDownload = (Math.random() * 4 + 3).toFixed(1);
connections.textContent = newConnections;
uploadSpeed.textContent = newUpload + ' MB/s';
downloadSpeed.textContent = newDownload + ' MB/s';
}, 2000);
}
// 初始化应用
document.addEventListener('DOMContentLoaded', function() {
initCharts();
simulateRealTimeData();
// 响应式调整
window.addEventListener('resize', function() {
setTimeout(() => {
echarts.getInstanceByDom(document.getElementById('trafficChart'))?.resize();
echarts.getInstanceByDom(document.getElementById('geoChart'))?.resize();
}, 200);
});
});
</script>
</body>
</html>
@@ -0,0 +1,911 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NetGuard - 网络监控工具</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary-color: #0078d4;
--primary-hover: #106ebe;
--secondary-color: #605e5c;
--success-color: #107c10;
--warning-color: #ffb900;
--danger-color: #d13438;
--bg-primary: #f3f2f1;
--bg-secondary: #ffffff;
--bg-tertiary: #faf9f8;
--text-primary: #323130;
--text-secondary: #605e5c;
--text-muted: #a19f9d;
--border-color: #e1dfdd;
--hover-bg: #f3f2f1;
--selected-bg: #deecf9;
}
[data-theme="dark"] {
--bg-primary: #1e1e1e;
--bg-secondary: #2d2d30;
--bg-tertiary: #252526;
--text-primary: #cccccc;
--text-secondary: #9d9d9d;
--text-muted: #6d6d6d;
--border-color: #3e3e42;
--hover-bg: #2a2d2e;
--selected-bg: #37373d;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
font-size: 13px;
overflow: hidden;
}
.app-container {
display: flex;
height: 100vh;
flex-direction: column;
}
/* 工具栏 */
.toolbar {
height: 40px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
padding: 0 12px;
gap: 8px;
flex-shrink: 0;
}
.toolbar-group {
display: flex;
align-items: center;
gap: 4px;
padding: 0 8px;
border-right: 1px solid var(--border-color);
}
.toolbar-group:last-child {
border-right: none;
margin-left: auto;
}
.toolbar-btn {
height: 28px;
padding: 0 8px;
border: 1px solid transparent;
background: transparent;
border-radius: 2px;
cursor: pointer;
font-size: 12px;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 4px;
}
.toolbar-btn:hover {
background: var(--hover-bg);
border-color: var(--border-color);
}
.toolbar-btn.active {
background: var(--primary-color);
color: white;
}
.status-bar {
height: 24px;
background: var(--bg-tertiary);
border-top: 1px solid var(--border-color);
display: flex;
align-items: center;
padding: 0 12px;
font-size: 11px;
color: var(--text-secondary);
gap: 16px;
flex-shrink: 0;
}
.status-item {
display: flex;
align-items: center;
gap: 4px;
}
.status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-indicator.active {
background: var(--success-color);
}
.status-indicator.warning {
background: var(--warning-color);
}
/* 主工作区 */
.main-workspace {
flex: 1;
display: flex;
overflow: hidden;
}
/* 左侧面板 */
.left-panel {
width: 280px;
background: var(--bg-secondary);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.panel-header {
height: 36px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
padding: 0 12px;
font-weight: 600;
font-size: 12px;
}
.panel-content {
flex: 1;
overflow-y: auto;
}
/* 进程树 */
.process-tree {
padding: 8px;
}
.process-item {
display: flex;
align-items: center;
height: 24px;
padding: 0 8px;
border-radius: 2px;
cursor: pointer;
font-size: 12px;
position: relative;
}
.process-item:hover {
background: var(--hover-bg);
}
.process-item.selected {
background: var(--selected-bg);
}
.process-icon {
width: 16px;
height: 16px;
margin-right: 8px;
background: var(--secondary-color);
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 10px;
font-weight: bold;
}
.process-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.connection-count {
font-size: 10px;
color: var(--text-muted);
background: var(--bg-tertiary);
padding: 1px 4px;
border-radius: 2px;
}
/* 右侧主视图 */
.main-view {
flex: 1;
display: flex;
flex-direction: column;
background: var(--bg-secondary);
}
.view-header {
height: 36px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
padding: 0 12px;
gap: 8px;
}
.view-title {
font-weight: 600;
font-size: 12px;
}
.view-controls {
margin-left: auto;
display: flex;
gap: 4px;
}
.control-input {
height: 24px;
padding: 0 6px;
border: 1px solid var(--border-color);
border-radius: 2px;
font-size: 11px;
background: var(--bg-secondary);
color: var(--text-primary);
}
/* 连接表格 */
.connections-grid {
flex: 1;
overflow: hidden;
position: relative;
}
.grid-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: auto;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
th {
height: 28px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border-color);
border-right: 1px solid var(--border-color);
padding: 0 8px;
text-align: left;
font-weight: 600;
font-size: 11px;
color: var(--text-secondary);
position: sticky;
top: 0;
z-index: 1;
}
th:last-child {
border-right: none;
}
td {
height: 22px;
padding: 0 8px;
border-bottom: 1px solid var(--border-color);
border-right: 1px solid var(--border-color);
vertical-align: middle;
}
td:last-child {
border-right: none;
}
tr:hover {
background: var(--hover-bg);
}
tr.selected {
background: var(--selected-bg);
}
.cell-content {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 状态指示器 */
.status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
display: inline-block;
margin-right: 6px;
}
.status-dot.established {
background: var(--success-color);
}
.status-dot.listening {
background: var(--primary-color);
}
.status-dot.blocked {
background: var(--danger-color);
}
/* 网速指示器 */
.speed-cell {
display: flex;
align-items: center;
gap: 6px;
}
.speed-bar {
width: 40px;
height: 3px;
background: var(--border-color);
border-radius: 1px;
overflow: hidden;
}
.speed-fill {
height: 100%;
background: var(--success-color);
transition: width 0.3s ease;
}
.speed-text {
font-size: 10px;
color: var(--text-muted);
min-width: 45px;
}
/* 底部面板 */
.bottom-panel {
height: 160px;
background: var(--bg-secondary);
border-top: 1px solid var(--border-color);
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.bottom-tabs {
height: 28px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border-color);
display: flex;
}
.bottom-tab {
padding: 0 12px;
height: 28px;
border-right: 1px solid var(--border-color);
background: transparent;
border: none;
cursor: pointer;
font-size: 11px;
color: var(--text-secondary);
display: flex;
align-items: center;
}
.bottom-tab.active {
background: var(--bg-secondary);
color: var(--text-primary);
border-bottom: 2px solid var(--primary-color);
}
.bottom-content {
flex: 1;
padding: 8px;
overflow-y: auto;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 11px;
line-height: 1.4;
}
.log-entry {
margin-bottom: 2px;
display: flex;
gap: 8px;
}
.log-time {
color: var(--text-muted);
min-width: 80px;
}
.log-level {
min-width: 40px;
font-weight: bold;
}
.log-level.info {
color: var(--primary-color);
}
.log-level.warn {
color: var(--warning-color);
}
.log-level.error {
color: var(--danger-color);
}
/* 响应式调整 */
@media (max-width: 1200px) {
.left-panel {
width: 240px;
}
}
@media (max-width: 800px) {
.left-panel {
display: none;
}
.bottom-panel {
height: 120px;
}
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 12px;
height: 12px;
}
::-webkit-scrollbar-track {
background: var(--bg-tertiary);
}
::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--secondary-color);
}
::-webkit-scrollbar-corner {
background: var(--bg-tertiary);
}
</style>
</head>
<body>
<div class="app-container">
<!-- 工具栏 -->
<div class="toolbar">
<div class="toolbar-group">
<button class="toolbar-btn active">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<path d="M12 6v6l4 2"/>
</svg>
实时监控
</button>
<button class="toolbar-btn">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 12l2 2 4-4"/>
<path d="M21 12c-1 0-3-1-3-3s2-3 3-3 3 1 3 3-2 3-3 3"/>
<path d="M3 12c1 0 3-1 3-3s-2-3-3-3-3 1-3 3 2 3 3 3"/>
</svg>
防火墙规则
</button>
<button class="toolbar-btn">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 20V10"/>
<path d="M12 20V4"/>
<path d="M6 20v-6"/>
</svg>
统计分析
</button>
</div>
<div class="toolbar-group">
<button class="toolbar-btn" title="暂停监控">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="6" y="4" width="4" height="16"/>
<rect x="14" y="4" width="4" height="16"/>
</svg>
</button>
<button class="toolbar-btn" title="清空列表">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 6h18"/>
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6m3 0V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/>
</svg>
</button>
<button class="toolbar-btn" title="导出数据">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7,10 12,15 17,10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
</div>
<div class="toolbar-group">
<span style="font-size: 11px; color: var(--text-muted);">更新间隔:</span>
<select class="control-input" style="width: 60px;">
<option>500ms</option>
<option selected>1s</option>
<option>2s</option>
<option>5s</option>
</select>
<button class="toolbar-btn" title="设置">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1 1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
</button>
</div>
</div>
<!-- 主工作区 -->
<div class="main-workspace">
<!-- 左侧进程面板 -->
<div class="left-panel">
<div class="panel-header">
活跃进程 (43)
</div>
<div class="panel-content">
<div class="process-tree">
<div class="process-item selected">
<div class="process-icon">CH</div>
<span class="process-name">chrome.exe</span>
<span class="connection-count">15</span>
</div>
<div class="process-item">
<div class="process-icon">ST</div>
<span class="process-name">steam.exe</span>
<span class="connection-count">7</span>
</div>
<div class="process-item">
<div class="process-icon">SV</div>
<span class="process-name">svchost.exe</span>
<span class="connection-count">23</span>
</div>
<div class="process-item">
<div class="process-icon">VS</div>
<span class="process-name">Code.exe</span>
<span class="connection-count">5</span>
</div>
<div class="process-item">
<div class="process-icon">DC</div>
<span class="process-name">Discord.exe</span>
<span class="connection-count">3</span>
</div>
<div class="process-item">
<div class="process-icon">UP</div>
<span class="process-name">UpdateService.exe</span>
<span class="connection-count">1</span>
</div>
<div class="process-item">
<div class="process-icon">WI</div>
<span class="process-name">winlogon.exe</span>
<span class="connection-count">2</span>
</div>
</div>
</div>
</div>
<!-- 右侧主视图 -->
<div class="main-view">
<div class="view-header">
<span class="view-title">chrome.exe 的网络连接 (15个活跃)</span>
<div class="view-controls">
<input type="text" class="control-input" placeholder="过滤..." style="width: 120px;">
<select class="control-input" style="width: 80px;">
<option>全部</option>
<option>TCP</option>
<option>UDP</option>
</select>
<select class="control-input" style="width: 80px;">
<option>全部</option>
<option>建立</option>
<option>监听</option>
<option>已阻止</option>
</select>
</div>
</div>
<div class="connections-grid">
<div class="grid-container">
<table>
<thead>
<tr>
<th style="width: 60px;">状态</th>
<th style="width: 80px;">协议</th>
<th style="width: 140px;">本地地址</th>
<th style="width: 140px;">远程地址</th>
<th style="width: 200px;">远程主机</th>
<th style="width: 80px;">网速</th>
<th style="width: 80px;">发送</th>
<th style="width: 80px;">接收</th>
<th style="width: 100px;">持续时间</th>
</tr>
</thead>
<tbody>
<tr class="selected">
<td><span class="status-dot established"></span>已建立</td>
<td>TCP</td>
<td class="cell-content">192.168.1.100:54321</td>
<td class="cell-content">74.125.224.72:443</td>
<td class="cell-content">google.com</td>
<td>
<div class="speed-cell">
<div class="speed-bar">
<div class="speed-fill" style="width: 75%"></div>
</div>
<span class="speed-text">1.2 MB/s</span>
</div>
</td>
<td class="cell-content">45.2 MB</td>
<td class="cell-content">156.8 MB</td>
<td class="cell-content">00:02:34</td>
</tr>
<tr>
<td><span class="status-dot established"></span>已建立</td>
<td>TCP</td>
<td class="cell-content">192.168.1.100:54322</td>
<td class="cell-content">172.217.24.14:443</td>
<td class="cell-content">googleapis.com</td>
<td>
<div class="speed-cell">
<div class="speed-bar">
<div class="speed-fill" style="width: 25%"></div>
</div>
<span class="speed-text">256 KB/s</span>
</div>
</td>
<td class="cell-content">2.1 MB</td>
<td class="cell-content">8.3 MB</td>
<td class="cell-content">00:01:12</td>
</tr>
<tr>
<td><span class="status-dot blocked"></span>已阻止</td>
<td>TCP</td>
<td class="cell-content">192.168.1.100:54323</td>
<td class="cell-content">185.199.108.153:443</td>
<td class="cell-content">github.com</td>
<td>
<div class="speed-cell">
<div class="speed-bar">
<div class="speed-fill" style="width: 0%"></div>
</div>
<span class="speed-text">0 B/s</span>
</div>
</td>
<td class="cell-content">0 B</td>
<td class="cell-content">0 B</td>
<td class="cell-content">-</td>
</tr>
<tr>
<td><span class="status-dot established"></span>已建立</td>
<td>TCP</td>
<td class="cell-content">192.168.1.100:54324</td>
<td class="cell-content">140.82.114.25:443</td>
<td class="cell-content">github.com</td>
<td>
<div class="speed-cell">
<div class="speed-bar">
<div class="speed-fill" style="width: 15%"></div>
</div>
<span class="speed-text">84 KB/s</span>
</div>
</td>
<td class="cell-content">456 KB</td>
<td class="cell-content">1.2 MB</td>
<td class="cell-content">00:00:45</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- 底部面板 -->
<div class="bottom-panel">
<div class="bottom-tabs">
<button class="bottom-tab active">连接详情</button>
<button class="bottom-tab">事件日志</button>
<button class="bottom-tab">防火墙规则</button>
<button class="bottom-tab">DNS解析</button>
</div>
<div class="bottom-content">
<div class="log-entry">
<span class="log-time">14:32:15</span>
<span class="log-level info">INFO</span>
<span>chrome.exe (PID: 4852) 建立连接到 google.com:443</span>
</div>
<div class="log-entry">
<span class="log-time">14:32:14</span>
<span class="log-level warn">WARN</span>
<span>检测到 chrome.exe 尝试连接被阻止的地址 185.199.108.153:443</span>
</div>
<div class="log-entry">
<span class="log-time">14:32:12</span>
<span class="log-level info">INFO</span>
<span>DNS解析: google.com -> 74.125.224.72</span>
</div>
<div class="log-entry">
<span class="log-time">14:32:10</span>
<span class="log-level error">ERROR</span>
<span>防火墙规则冲突: 规则 "Block GitHub" 与 "Allow Chrome" 存在冲突</span>
</div>
<div class="log-entry">
<span class="log-time">14:32:08</span>
<span class="log-level info">INFO</span>
<span>steam.exe (PID: 3421) 连接已断开 104.96.230.4:80</span>
</div>
</div>
</div>
<!-- 状态栏 -->
<div class="status-bar">
<div class="status-item">
<span class="status-indicator active"></span>
<span>监控中</span>
</div>
<div class="status-item">
<span>247 个活跃连接</span>
</div>
<div class="status-item">
<span>网速: ↑ 2.1 MB/s ↓ 5.3 MB/s</span>
</div>
<div class="status-item">
<span>今日流量: 1.2 GB</span>
</div>
<div class="status-item">
<span>规则: 156 条激活</span>
</div>
<div class="status-item">
<span class="status-indicator warning"></span>
<span>3 个异常连接</span>
</div>
</div>
</div>
<script>
// 工具软件交互逻辑
document.addEventListener('DOMContentLoaded', function() {
// 进程列表选择
document.querySelectorAll('.process-item').forEach(item => {
item.addEventListener('click', function() {
document.querySelectorAll('.process-item').forEach(i => i.classList.remove('selected'));
this.classList.add('selected');
// 更新主视图标题
const processName = this.querySelector('.process-name').textContent;
const connectionCount = this.querySelector('.connection-count').textContent;
document.querySelector('.view-title').textContent =
`${processName} 的网络连接 (${connectionCount}个活跃)`;
});
});
// 表格行选择
document.querySelectorAll('tbody tr').forEach(row => {
row.addEventListener('click', function() {
document.querySelectorAll('tbody tr').forEach(r => r.classList.remove('selected'));
this.classList.add('selected');
});
});
// 底部标签页切换
document.querySelectorAll('.bottom-tab').forEach(tab => {
tab.addEventListener('click', function() {
document.querySelectorAll('.bottom-tab').forEach(t => t.classList.remove('active'));
this.classList.add('active');
// 这里可以切换不同的内容
const tabText = this.textContent;
console.log('切换到标签页:', tabText);
});
});
// 工具栏按钮功能
document.querySelectorAll('.toolbar-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
if (this.title) {
console.log('执行操作:', this.title);
// 模拟操作反馈
const originalText = this.innerHTML;
this.style.opacity = '0.5';
setTimeout(() => {
this.style.opacity = '1';
}, 200);
}
});
});
// 实时数据更新模拟
setInterval(() => {
// 更新网速条
document.querySelectorAll('.speed-fill').forEach(fill => {
const randomWidth = Math.random() * 100;
fill.style.width = randomWidth + '%';
});
// 更新网速文本
const speeds = ['1.2 MB/s', '856 KB/s', '2.1 MB/s', '124 KB/s', '0 B/s'];
document.querySelectorAll('.speed-text').forEach(text => {
if (!text.textContent.includes('0 B/s')) {
text.textContent = speeds[Math.floor(Math.random() * speeds.length - 1)];
}
});
// 更新状态栏网速
const statusSpeed = document.querySelector('.status-bar .status-item:nth-child(3) span');
if (statusSpeed) {
const up = (Math.random() * 5).toFixed(1);
const down = (Math.random() * 10).toFixed(1);
statusSpeed.textContent = `网速: ↑ ${up} MB/s ↓ ${down} MB/s`;
}
}, 1000);
// 搜索过滤功能
const searchInput = document.querySelector('input[placeholder="过滤..."]');
if (searchInput) {
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
document.querySelectorAll('tbody tr').forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(searchTerm) ? '' : 'none';
});
});
}
// 右键菜单模拟
document.querySelectorAll('tbody tr').forEach(row => {
row.addEventListener('contextmenu', function(e) {
e.preventDefault();
console.log('右键菜单: 阻止连接、查看详情、复制地址等');
});
});
// 键盘快捷键支持
document.addEventListener('keydown', function(e) {
if (e.ctrlKey) {
switch(e.key) {
case 'f':
e.preventDefault();
searchInput?.focus();
break;
case 'r':
e.preventDefault();
console.log('刷新数据');
break;
case 's':
e.preventDefault();
console.log('停止/开始监控');
break;
}
}
});
});
</script>
</body>
</html>
@@ -0,0 +1,1310 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NetGuard - 智能网络监控</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary: #6366f1;
--primary-dark: #4f46e5;
--secondary: #8b5cf6;
--accent: #06b6d4;
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
--bg-primary: #0a0a0b;
--bg-secondary: #111113;
--bg-tertiary: #1a1a1d;
--bg-elevated: #202024;
--bg-card: rgba(26, 26, 29, 0.7);
--text-primary: #f8fafc;
--text-secondary: #cbd5e1;
--text-muted: #64748b;
--text-inverse: #0f172a;
--border-subtle: rgba(148, 163, 184, 0.1);
--border-muted: rgba(148, 163, 184, 0.2);
--glow-primary: rgba(99, 102, 241, 0.5);
--glow-accent: rgba(6, 182, 212, 0.3);
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
background: radial-gradient(ellipse at top, var(--bg-secondary) 0%, var(--bg-primary) 100%);
color: var(--text-primary);
overflow: hidden;
font-size: 14px;
font-weight: 400;
line-height: 1.5;
}
/* 全局网格布局 */
.app-grid {
display: grid;
grid-template-areas:
"titlebar titlebar titlebar"
"sidebar main-view inspector"
"sidebar timeline inspector";
grid-template-columns: 260px 1fr 320px;
grid-template-rows: 40px 1fr 180px;
height: 100vh;
gap: 1px;
background: var(--border-subtle);
}
/* 标题栏 */
.titlebar {
grid-area: titlebar;
background: var(--bg-elevated);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
border-bottom: 1px solid var(--border-subtle);
backdrop-filter: blur(20px);
}
.titlebar-left {
display: flex;
align-items: center;
gap: 20px;
}
.app-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: 15px;
}
.app-icon {
width: 20px;
height: 20px;
background: linear-gradient(135deg, var(--primary), var(--secondary));
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 20px var(--glow-primary);
}
.view-switcher {
display: flex;
background: var(--bg-tertiary);
border-radius: 8px;
padding: 2px;
}
.view-tab {
padding: 6px 12px;
border-radius: 6px;
font-size: 13px;
color: var(--text-muted);
cursor: pointer;
transition: all 0.2s ease;
border: none;
background: transparent;
}
.view-tab.active {
background: var(--primary);
color: white;
box-shadow: 0 2px 8px var(--glow-primary);
}
.titlebar-right {
display: flex;
align-items: center;
gap: 12px;
}
.control-btn {
width: 32px;
height: 24px;
border: none;
background: transparent;
border-radius: 4px;
cursor: pointer;
color: var(--text-muted);
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}
.control-btn:hover {
background: var(--bg-tertiary);
color: var(--text-primary);
}
.status-indicator {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--text-secondary);
}
.status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--success);
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* 侧边栏 */
.sidebar {
grid-area: sidebar;
background: var(--bg-card);
backdrop-filter: blur(20px);
border-right: 1px solid var(--border-subtle);
display: flex;
flex-direction: column;
}
.sidebar-section {
padding: 16px;
border-bottom: 1px solid var(--border-subtle);
}
.section-title {
font-size: 12px;
font-weight: 600;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 12px;
}
.quick-stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 16px;
}
.stat-pill {
background: var(--bg-tertiary);
border: 1px solid var(--border-muted);
border-radius: 8px;
padding: 8px;
text-align: center;
}
.stat-value {
font-size: 18px;
font-weight: 700;
color: var(--primary);
line-height: 1;
}
.stat-label {
font-size: 10px;
color: var(--text-muted);
margin-top: 2px;
}
.process-list {
flex: 1;
overflow-y: auto;
}
.process-group {
margin-bottom: 12px;
}
.group-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 8px;
background: var(--bg-tertiary);
border-radius: 6px;
font-size: 12px;
font-weight: 500;
margin-bottom: 4px;
}
.process-item {
display: flex;
align-items: center;
padding: 8px;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
margin-bottom: 2px;
}
.process-item:hover {
background: var(--bg-tertiary);
}
.process-item.active {
background: linear-gradient(135deg, var(--primary), var(--secondary));
box-shadow: 0 4px 12px var(--glow-primary);
}
.process-icon {
width: 24px;
height: 24px;
border-radius: 6px;
margin-right: 10px;
background: var(--accent);
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
font-weight: 600;
color: white;
}
.process-info {
flex: 1;
min-width: 0;
}
.process-name {
font-size: 13px;
font-weight: 500;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.process-meta {
font-size: 11px;
color: var(--text-muted);
display: flex;
gap: 8px;
}
.activity-indicator {
width: 32px;
display: flex;
justify-content: center;
}
.activity-bar {
width: 2px;
height: 16px;
background: var(--bg-tertiary);
border-radius: 1px;
position: relative;
overflow: hidden;
}
.activity-fill {
position: absolute;
bottom: 0;
width: 100%;
background: linear-gradient(to top, var(--success), var(--accent));
border-radius: 1px;
transition: height 0.3s ease;
}
/* 主视图 */
.main-view {
grid-area: main-view;
background: var(--bg-secondary);
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
}
.main-header {
padding: 16px 20px;
background: var(--bg-card);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border-subtle);
display: flex;
align-items: center;
justify-content: space-between;
}
.main-title {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
}
.main-subtitle {
font-size: 12px;
color: var(--text-muted);
margin-top: 2px;
}
.search-bar {
display: flex;
align-items: center;
background: var(--bg-tertiary);
border: 1px solid var(--border-muted);
border-radius: 8px;
padding: 0 12px;
width: 280px;
height: 36px;
}
.search-input {
flex: 1;
background: transparent;
border: none;
color: var(--text-primary);
font-size: 13px;
outline: none;
}
.search-input::placeholder {
color: var(--text-muted);
}
/* 连接网格 */
.connections-grid {
flex: 1;
position: relative;
overflow: hidden;
}
.grid-header {
position: sticky;
top: 0;
background: var(--bg-elevated);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border-subtle);
display: grid;
grid-template-columns: 140px 100px 180px 180px 1fr 80px 100px;
font-size: 11px;
font-weight: 600;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
z-index: 10;
}
.grid-header > div {
padding: 12px 16px;
border-right: 1px solid var(--border-subtle);
}
.grid-content {
position: absolute;
top: 44px;
left: 0;
right: 0;
bottom: 0;
overflow-y: auto;
}
.connection-row {
display: grid;
grid-template-columns: 140px 100px 180px 180px 1fr 80px 100px;
align-items: center;
min-height: 48px;
border-bottom: 1px solid var(--border-subtle);
transition: all 0.2s ease;
cursor: pointer;
}
.connection-row:hover {
background: var(--bg-card);
backdrop-filter: blur(20px);
}
.connection-row.selected {
background: linear-gradient(90deg, var(--glow-primary), transparent);
border-left: 3px solid var(--primary);
}
.connection-row > div {
padding: 0 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.status-cell {
display: flex;
align-items: center;
gap: 8px;
}
.status-badge {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-badge.established {
background: var(--success);
box-shadow: 0 0 6px var(--success);
}
.status-badge.listening {
background: var(--accent);
box-shadow: 0 0 6px var(--accent);
}
.status-badge.blocked {
background: var(--danger);
box-shadow: 0 0 6px var(--danger);
}
.protocol-badge {
padding: 2px 6px;
background: var(--bg-tertiary);
border: 1px solid var(--border-muted);
border-radius: 4px;
font-size: 10px;
font-weight: 500;
color: var(--text-secondary);
}
.address-cell {
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
font-size: 12px;
color: var(--text-secondary);
}
.domain-cell {
color: var(--accent);
font-weight: 500;
}
.speed-cell {
display: flex;
align-items: center;
gap: 8px;
}
.speed-chart {
width: 40px;
height: 20px;
background: var(--bg-tertiary);
border-radius: 3px;
position: relative;
overflow: hidden;
}
.speed-wave {
position: absolute;
bottom: 0;
width: 100%;
height: 60%;
background: linear-gradient(to top, var(--success), var(--accent));
border-radius: 3px 3px 0 0;
animation: wave 2s ease-in-out infinite;
}
@keyframes wave {
0%, 100% { height: 20%; }
50% { height: 80%; }
}
.speed-text {
font-size: 11px;
color: var(--text-muted);
font-weight: 500;
}
/* 检查器面板 */
.inspector {
grid-area: inspector;
background: var(--bg-card);
backdrop-filter: blur(20px);
border-left: 1px solid var(--border-subtle);
display: flex;
flex-direction: column;
}
.inspector-header {
padding: 16px;
border-bottom: 1px solid var(--border-subtle);
}
.inspector-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 8px;
}
.inspector-content {
flex: 1;
overflow-y: auto;
padding: 16px;
}
.detail-group {
margin-bottom: 20px;
}
.detail-label {
font-size: 11px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.detail-value {
font-size: 13px;
color: var(--text-primary);
margin-bottom: 4px;
}
.detail-value.code {
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
background: var(--bg-tertiary);
padding: 4px 8px;
border-radius: 4px;
font-size: 11px;
}
.geo-info {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
background: var(--bg-tertiary);
border-radius: 6px;
}
.country-flag {
width: 20px;
height: 15px;
background: linear-gradient(45deg, var(--primary), var(--secondary));
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
font-size: 8px;
color: white;
}
/* 时间轴 */
.timeline {
grid-area: timeline;
background: var(--bg-card);
backdrop-filter: blur(20px);
border-top: 1px solid var(--border-subtle);
display: flex;
flex-direction: column;
}
.timeline-header {
padding: 12px 16px;
border-bottom: 1px solid var(--border-subtle);
display: flex;
align-items: center;
justify-content: space-between;
}
.timeline-title {
font-size: 13px;
font-weight: 600;
}
.timeline-controls {
display: flex;
gap: 8px;
}
.timeline-btn {
padding: 4px 8px;
background: var(--bg-tertiary);
border: 1px solid var(--border-muted);
border-radius: 4px;
font-size: 11px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s ease;
}
.timeline-btn:hover {
background: var(--primary);
color: white;
}
.timeline-content {
flex: 1;
padding: 16px;
overflow-y: auto;
}
.event-item {
display: flex;
align-items: flex-start;
gap: 12px;
margin-bottom: 12px;
padding: 8px;
border-radius: 6px;
transition: all 0.2s ease;
}
.event-item:hover {
background: var(--bg-tertiary);
}
.event-time {
font-size: 10px;
color: var(--text-muted);
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
min-width: 60px;
}
.event-icon {
width: 16px;
height: 16px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 8px;
color: white;
margin-top: 2px;
}
.event-icon.connection {
background: var(--success);
}
.event-icon.blocked {
background: var(--danger);
}
.event-icon.dns {
background: var(--accent);
}
.event-content {
flex: 1;
font-size: 12px;
line-height: 1.4;
}
.event-title {
color: var(--text-primary);
margin-bottom: 2px;
}
.event-details {
color: var(--text-muted);
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--border-muted);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
/* 响应式适配 */
@media (max-width: 1400px) {
.app-grid {
grid-template-columns: 240px 1fr 280px;
}
}
@media (max-width: 1200px) {
.app-grid {
grid-template-areas:
"titlebar titlebar"
"sidebar main-view"
"timeline timeline";
grid-template-columns: 240px 1fr;
grid-template-rows: 40px 1fr 160px;
}
.inspector {
display: none;
}
}
</style>
</head>
<body>
<div class="app-grid">
<!-- 标题栏 -->
<div class="titlebar">
<div class="titlebar-left">
<div class="app-title">
<div class="app-icon">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
<path d="M2 17l10 5 10-5"/>
</svg>
</div>
NetGuard
</div>
<div class="view-switcher">
<button class="view-tab active">实时监控</button>
<button class="view-tab">防火墙</button>
<button class="view-tab">分析</button>
</div>
</div>
<div class="titlebar-right">
<div class="status-indicator">
<div class="status-dot"></div>
实时监控中
</div>
<button class="control-btn" title="设置">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1 1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
</button>
</div>
</div>
<!-- 侧边栏 -->
<div class="sidebar">
<div class="sidebar-section">
<div class="section-title">系统概览</div>
<div class="quick-stats">
<div class="stat-pill">
<div class="stat-value">247</div>
<div class="stat-label">活跃连接</div>
</div>
<div class="stat-pill">
<div class="stat-value">43</div>
<div class="stat-label">进程数</div>
</div>
<div class="stat-pill">
<div class="stat-value">5.2</div>
<div class="stat-label">MB/s</div>
</div>
<div class="stat-pill">
<div class="stat-value">156</div>
<div class="stat-label">规则</div>
</div>
</div>
</div>
<div class="process-list">
<div class="process-group">
<div class="group-header">
<span>浏览器应用</span>
<span>23</span>
</div>
<div class="process-item active">
<div class="process-icon" style="background: #4285f4;">CH</div>
<div class="process-info">
<div class="process-name">Chrome</div>
<div class="process-meta">
<span>15 连接</span>
<span>2.1 MB/s</span>
</div>
</div>
<div class="activity-indicator">
<div class="activity-bar">
<div class="activity-fill" style="height: 75%;"></div>
</div>
</div>
</div>
<div class="process-item">
<div class="process-icon" style="background: #ff6b35;">FF</div>
<div class="process-info">
<div class="process-name">Firefox</div>
<div class="process-meta">
<span>8 连接</span>
<span>456 KB/s</span>
</div>
</div>
<div class="activity-indicator">
<div class="activity-bar">
<div class="activity-fill" style="height: 35%;"></div>
</div>
</div>
</div>
</div>
<div class="process-group">
<div class="group-header">
<span>开发工具</span>
<span>12</span>
</div>
<div class="process-item">
<div class="process-icon" style="background: #007acc;">VS</div>
<div class="process-info">
<div class="process-name">Visual Studio Code</div>
<div class="process-meta">
<span>5 连接</span>
<span>128 KB/s</span>
</div>
</div>
<div class="activity-indicator">
<div class="activity-bar">
<div class="activity-fill" style="height: 20%;"></div>
</div>
</div>
</div>
</div>
<div class="process-group">
<div class="group-header">
<span>系统服务</span>
<span>8</span>
</div>
<div class="process-item">
<div class="process-icon" style="background: #00bcf2;">SV</div>
<div class="process-info">
<div class="process-name">svchost.exe</div>
<div class="process-meta">
<span>23 连接</span>
<span>89 KB/s</span>
</div>
</div>
<div class="activity-indicator">
<div class="activity-bar">
<div class="activity-fill" style="height: 45%;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 主视图 -->
<div class="main-view">
<div class="main-header">
<div>
<div class="main-title">Chrome 网络活动</div>
<div class="main-subtitle">15 个活跃连接 • 平均速度 2.1 MB/s</div>
</div>
<div class="search-bar">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<path d="M21 21l-4.35-4.35"/>
</svg>
<input type="text" class="search-input" placeholder="搜索连接、域名或IP地址...">
</div>
</div>
<div class="connections-grid">
<div class="grid-header">
<div>状态</div>
<div>协议</div>
<div>本地地址</div>
<div>远程地址</div>
<div>域名/主机</div>
<div>网速</div>
<div>流量</div>
</div>
<div class="grid-content">
<div class="connection-row selected">
<div class="status-cell">
<div class="status-badge established"></div>
<span>已建立</span>
</div>
<div>
<span class="protocol-badge">TCP</span>
</div>
<div class="address-cell">192.168.1.100:54321</div>
<div class="address-cell">74.125.224.72:443</div>
<div class="domain-cell">google.com</div>
<div class="speed-cell">
<div class="speed-chart">
<div class="speed-wave"></div>
</div>
<span class="speed-text">1.2 MB/s</span>
</div>
<div>156.8 MB</div>
</div>
<div class="connection-row">
<div class="status-cell">
<div class="status-badge established"></div>
<span>已建立</span>
</div>
<div>
<span class="protocol-badge">TCP</span>
</div>
<div class="address-cell">192.168.1.100:54322</div>
<div class="address-cell">172.217.24.14:443</div>
<div class="domain-cell">googleapis.com</div>
<div class="speed-cell">
<div class="speed-chart">
<div class="speed-wave" style="animation-delay: 0.5s;"></div>
</div>
<span class="speed-text">456 KB/s</span>
</div>
<div>8.3 MB</div>
</div>
<div class="connection-row">
<div class="status-cell">
<div class="status-badge blocked"></div>
<span>已阻止</span>
</div>
<div>
<span class="protocol-badge">TCP</span>
</div>
<div class="address-cell">192.168.1.100:54323</div>
<div class="address-cell">185.199.108.153:443</div>
<div class="domain-cell">github.com</div>
<div class="speed-cell">
<div class="speed-chart">
<div class="speed-wave" style="height: 0;"></div>
</div>
<span class="speed-text">0 B/s</span>
</div>
<div>0 B</div>
</div>
<div class="connection-row">
<div class="status-cell">
<div class="status-badge listening"></div>
<span>监听中</span>
</div>
<div>
<span class="protocol-badge">UDP</span>
</div>
<div class="address-cell">0.0.0.0:53</div>
<div class="address-cell">-</div>
<div class="domain-cell">DNS服务</div>
<div class="speed-cell">
<div class="speed-chart">
<div class="speed-wave" style="animation-delay: 1s; height: 30%;"></div>
</div>
<span class="speed-text">12 KB/s</span>
</div>
<div>2.1 MB</div>
</div>
<div class="connection-row">
<div class="status-cell">
<div class="status-badge established"></div>
<span>已建立</span>
</div>
<div>
<span class="protocol-badge">TCP</span>
</div>
<div class="address-cell">192.168.1.100:54324</div>
<div class="address-cell">140.82.114.25:443</div>
<div class="domain-cell">github.com</div>
<div class="speed-cell">
<div class="speed-chart">
<div class="speed-wave" style="animation-delay: 1.5s; height: 40%;"></div>
</div>
<span class="speed-text">84 KB/s</span>
</div>
<div>1.2 MB</div>
</div>
</div>
</div>
</div>
<!-- 检查器面板 -->
<div class="inspector">
<div class="inspector-header">
<div class="inspector-title">连接详情</div>
</div>
<div class="inspector-content">
<div class="detail-group">
<div class="detail-label">连接信息</div>
<div class="detail-value">状态: 已建立 (ESTABLISHED)</div>
<div class="detail-value">协议: TCP</div>
<div class="detail-value">持续时间: 00:02:34</div>
<div class="detail-value">PID: 4852</div>
</div>
<div class="detail-group">
<div class="detail-label">本地端点</div>
<div class="detail-value code">192.168.1.100:54321</div>
</div>
<div class="detail-group">
<div class="detail-label">远程端点</div>
<div class="detail-value code">74.125.224.72:443</div>
<div class="detail-value">google.com</div>
<div class="geo-info">
<div class="country-flag">US</div>
<div>
<div style="font-size: 12px; font-weight: 500;">美国</div>
<div style="font-size: 10px; color: var(--text-muted);">加利福尼亚州,山景城</div>
</div>
</div>
</div>
<div class="detail-group">
<div class="detail-label">流量统计</div>
<div class="detail-value">发送: 45.2 MB</div>
<div class="detail-value">接收: 156.8 MB</div>
<div class="detail-value">当前速度: 1.2 MB/s</div>
<div class="detail-value">峰值速度: 3.8 MB/s</div>
</div>
<div class="detail-group">
<div class="detail-label">进程信息</div>
<div class="detail-value">chrome.exe</div>
<div class="detail-value" style="font-size: 11px; color: var(--text-muted);">
C:\Program Files\Google\Chrome\Application\chrome.exe
</div>
<div class="detail-value">版本: 120.0.6099.129</div>
<div class="detail-value">签名: Google LLC (已验证)</div>
</div>
<div class="detail-group">
<div class="detail-label">安全信息</div>
<div class="detail-value">TLS 1.3</div>
<div class="detail-value">证书: *.google.com</div>
<div class="detail-value" style="color: var(--success);">✓ 证书有效</div>
</div>
</div>
</div>
<!-- 时间轴 -->
<div class="timeline">
<div class="timeline-header">
<div class="timeline-title">实时事件流</div>
<div class="timeline-controls">
<button class="timeline-btn">暂停</button>
<button class="timeline-btn">清空</button>
<button class="timeline-btn">导出</button>
</div>
</div>
<div class="timeline-content">
<div class="event-item">
<div class="event-time">14:32:15</div>
<div class="event-icon connection">→</div>
<div class="event-content">
<div class="event-title">新连接建立</div>
<div class="event-details">chrome.exe → google.com:443 (TLS)</div>
</div>
</div>
<div class="event-item">
<div class="event-time">14:32:14</div>
<div class="event-icon blocked">✕</div>
<div class="event-content">
<div class="event-title">连接被阻止</div>
<div class="event-details">chrome.exe → github.com:443 (规则: Block GitHub)</div>
</div>
</div>
<div class="event-item">
<div class="event-time">14:32:12</div>
<div class="event-icon dns">?</div>
<div class="event-content">
<div class="event-title">DNS 查询</div>
<div class="event-details">google.com → 74.125.224.72 (89ms)</div>
</div>
</div>
<div class="event-item">
<div class="event-time">14:32:10</div>
<div class="event-icon connection">←</div>
<div class="event-content">
<div class="event-title">连接关闭</div>
<div class="event-details">steam.exe ← 104.96.230.4:80 (正常断开)</div>
</div>
</div>
<div class="event-item">
<div class="event-time">14:32:08</div>
<div class="event-icon connection">→</div>
<div class="event-content">
<div class="event-title">高速传输检测</div>
<div class="event-details">chrome.exe → google.com 峰值速度 3.8 MB/s</div>
</div>
</div>
<div class="event-item">
<div class="event-time">14:32:05</div>
<div class="event-icon dns">?</div>
<div class="event-content">
<div class="event-title">DNS 查询</div>
<div class="event-details">googleapis.com → 172.217.24.14 (45ms)</div>
</div>
</div>
</div>
</div>
</div>
<script>
// 现代化交互逻辑
document.addEventListener('DOMContentLoaded', function() {
// 进程选择交互
document.querySelectorAll('.process-item').forEach(item => {
item.addEventListener('click', function() {
document.querySelectorAll('.process-item').forEach(i => i.classList.remove('active'));
this.classList.add('active');
// 更新主视图内容
const processName = this.querySelector('.process-name').textContent;
const connections = this.querySelector('.process-meta').textContent.split(' ')[0];
const speed = this.querySelector('.process-meta').textContent.split('• ')[1] || '0 B/s';
document.querySelector('.main-title').textContent = `${processName} 网络活动`;
document.querySelector('.main-subtitle').textContent = `${connections} 个活跃连接 • 平均速度 ${speed}`;
});
});
// 连接行选择
document.querySelectorAll('.connection-row').forEach(row => {
row.addEventListener('click', function() {
document.querySelectorAll('.connection-row').forEach(r => r.classList.remove('selected'));
this.classList.add('selected');
// 这里可以更新检查器面板的内容
console.log('选中连接,更新详情面板');
});
});
// 视图切换
document.querySelectorAll('.view-tab').forEach(tab => {
tab.addEventListener('click', function() {
document.querySelectorAll('.view-tab').forEach(t => t.classList.remove('active'));
this.classList.add('active');
// 切换视图逻辑
console.log('切换视图:', this.textContent);
});
});
// 搜索功能
const searchInput = document.querySelector('.search-input');
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
document.querySelectorAll('.connection-row').forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(searchTerm) ? 'grid' : 'none';
});
});
// 时间轴控制
document.querySelectorAll('.timeline-btn').forEach(btn => {
btn.addEventListener('click', function() {
const action = this.textContent;
console.log('时间轴操作:', action);
if (action === '暂停') {
this.textContent = '继续';
} else if (action === '继续') {
this.textContent = '暂停';
}
});
});
// 实时数据模拟
setInterval(() => {
// 更新活动指示器
document.querySelectorAll('.activity-fill').forEach(fill => {
const height = Math.random() * 100;
fill.style.height = height + '%';
});
// 随机添加新事件
if (Math.random() > 0.7) {
addNewEvent();
}
// 更新速度文本
document.querySelectorAll('.speed-text').forEach(text => {
if (!text.textContent.includes('0 B/s')) {
const speeds = ['1.2 MB/s', '856 KB/s', '2.1 MB/s', '124 KB/s'];
text.textContent = speeds[Math.floor(Math.random() * speeds.length)];
}
});
}, 2000);
// 添加新事件到时间轴
function addNewEvent() {
const events = [
{ type: 'connection', title: '新连接建立', details: 'chrome.exe → example.com:443' },
{ type: 'dns', title: 'DNS 查询', details: 'example.com → 93.184.216.34' },
{ type: 'blocked', title: '连接被阻止', details: 'app.exe → suspicious.site:80' }
];
const event = events[Math.floor(Math.random() * events.length)];
const now = new Date();
const timeStr = now.toLocaleTimeString('zh-CN', { hour12: false }).slice(0, 8);
const eventElement = document.createElement('div');
eventElement.className = 'event-item';
eventElement.innerHTML = `
<div class="event-time">${timeStr}</div>
<div class="event-icon ${event.type}">${event.type === 'connection' ? '→' : event.type === 'dns' ? '?' : '✕'}</div>
<div class="event-content">
<div class="event-title">${event.title}</div>
<div class="event-details">${event.details}</div>
</div>
`;
const timelineContent = document.querySelector('.timeline-content');
timelineContent.insertBefore(eventElement, timelineContent.firstChild);
// 限制事件数量
const events_list = timelineContent.children;
if (events_list.length > 10) {
timelineContent.removeChild(events_list[events_list.length - 1]);
}
}
// 键盘快捷键
document.addEventListener('keydown', function(e) {
if (e.ctrlKey || e.metaKey) {
switch(e.key) {
case 'f':
e.preventDefault();
searchInput.focus();
break;
case '1':
e.preventDefault();
document.querySelector('.view-tab').click();
break;
case '2':
e.preventDefault();
document.querySelectorAll('.view-tab')[1].click();
break;
case '3':
e.preventDefault();
document.querySelectorAll('.view-tab')[2].click();
break;
}
}
});
});
</script>
</body>
</html>
@@ -0,0 +1,760 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NetGuard - 现代化网络监控工具</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary-color: #2563eb;
--primary-hover: #1d4ed8;
--secondary-color: #64748b;
--success-color: #10b981;
--warning-color: #f59e0b;
--danger-color: #ef4444;
--bg-primary: #0f172a;
--bg-secondary: #1e293b;
--bg-tertiary: #334155;
--text-primary: #f8fafc;
--text-secondary: #cbd5e1;
--text-muted: #64748b;
--border-color: #334155;
--glass-bg: rgba(30, 41, 59, 0.7);
--glass-border: rgba(148, 163, 184, 0.1);
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
color: var(--text-primary);
overflow-x: hidden;
}
.app-container {
display: flex;
height: 100vh;
position: relative;
}
/* 侧边栏 */
.sidebar {
width: 280px;
background: var(--glass-bg);
backdrop-filter: blur(20px);
border-right: 1px solid var(--glass-border);
padding: 24px;
overflow-y: auto;
transition: all 0.3s ease;
}
.logo {
display: flex;
align-items: center;
margin-bottom: 32px;
padding-bottom: 24px;
border-bottom: 1px solid var(--border-color);
}
.logo-icon {
width: 40px;
height: 40px;
background: linear-gradient(45deg, var(--primary-color), #3b82f6);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
box-shadow: 0 4px 20px rgba(37, 99, 235, 0.3);
}
.logo-text {
font-size: 24px;
font-weight: 700;
background: linear-gradient(45deg, var(--primary-color), #3b82f6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.nav-menu {
list-style: none;
}
.nav-item {
margin-bottom: 8px;
}
.nav-link {
display: flex;
align-items: center;
padding: 12px 16px;
border-radius: 12px;
text-decoration: none;
color: var(--text-secondary);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.nav-link::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(37, 99, 235, 0.1), transparent);
transition: left 0.5s ease;
}
.nav-link:hover::before {
left: 100%;
}
.nav-link:hover, .nav-link.active {
background: rgba(37, 99, 235, 0.1);
color: var(--text-primary);
transform: translateX(4px);
}
.nav-link.active {
background: linear-gradient(135deg, rgba(37, 99, 235, 0.2), rgba(59, 130, 246, 0.1));
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.nav-icon {
width: 20px;
height: 20px;
margin-right: 12px;
opacity: 0.7;
}
.nav-link:hover .nav-icon, .nav-link.active .nav-icon {
opacity: 1;
}
/* 主内容区 */
.main-content {
flex: 1;
padding: 24px;
overflow-y: auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 32px;
padding-bottom: 24px;
border-bottom: 1px solid var(--border-color);
}
.page-title {
font-size: 32px;
font-weight: 700;
background: linear-gradient(135deg, var(--text-primary), var(--text-secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.header-actions {
display: flex;
gap: 12px;
}
.btn {
padding: 10px 20px;
border-radius: 10px;
border: none;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
text-decoration: none;
font-size: 14px;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary-color), #3b82f6);
color: white;
box-shadow: 0 4px 20px rgba(37, 99, 235, 0.3);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 25px rgba(37, 99, 235, 0.4);
}
.btn-secondary {
background: var(--glass-bg);
color: var(--text-secondary);
border: 1px solid var(--glass-border);
backdrop-filter: blur(10px);
}
.btn-secondary:hover {
background: var(--bg-tertiary);
color: var(--text-primary);
}
/* 仪表板网格 */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 24px;
margin-bottom: 32px;
}
.card {
background: var(--glass-bg);
backdrop-filter: blur(20px);
border-radius: 16px;
border: 1px solid var(--glass-border);
padding: 24px;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
.stat-card {
text-align: center;
}
.stat-value {
font-size: 36px;
font-weight: 700;
margin-bottom: 8px;
background: linear-gradient(135deg, var(--primary-color), #3b82f6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.stat-label {
color: var(--text-secondary);
font-size: 14px;
font-weight: 500;
}
.stat-change {
margin-top: 8px;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
}
.stat-change.positive {
color: var(--success-color);
}
.stat-change.negative {
color: var(--danger-color);
}
/* 连接表格 */
.connections-table {
background: var(--glass-bg);
backdrop-filter: blur(20px);
border-radius: 16px;
border: 1px solid var(--glass-border);
overflow: hidden;
}
.table-header {
padding: 20px 24px;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.table-title {
font-size: 18px;
font-weight: 600;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 16px 24px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background: rgba(37, 99, 235, 0.05);
font-weight: 600;
color: var(--text-secondary);
font-size: 14px;
}
tr:hover {
background: rgba(37, 99, 235, 0.05);
}
.process-info {
display: flex;
align-items: center;
gap: 12px;
}
.process-icon {
width: 24px;
height: 24px;
background: linear-gradient(135deg, var(--primary-color), #3b82f6);
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
font-weight: 600;
}
.status-badge {
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
}
.status-active {
background: rgba(16, 185, 129, 0.2);
color: var(--success-color);
}
.status-blocked {
background: rgba(239, 68, 68, 0.2);
color: var(--danger-color);
}
.speed-indicator {
display: flex;
align-items: center;
gap: 8px;
}
.speed-bar {
width: 60px;
height: 4px;
background: var(--bg-tertiary);
border-radius: 2px;
overflow: hidden;
}
.speed-fill {
height: 100%;
background: linear-gradient(90deg, var(--success-color), var(--warning-color));
border-radius: 2px;
transition: width 0.3s ease;
}
/* 工具栏 */
.toolbar {
display: flex;
gap: 12px;
margin-bottom: 24px;
padding: 16px;
background: var(--glass-bg);
backdrop-filter: blur(20px);
border-radius: 12px;
border: 1px solid var(--glass-border);
}
.search-box {
flex: 1;
padding: 10px 16px;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
color: var(--text-primary);
font-size: 14px;
}
.search-box::placeholder {
color: var(--text-muted);
}
.filter-select {
padding: 10px 16px;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
color: var(--text-primary);
font-size: 14px;
min-width: 120px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
width: 60px;
padding: 24px 12px;
}
.logo-text {
display: none;
}
.nav-link {
justify-content: center;
padding: 12px;
}
.nav-link span {
display: none;
}
.dashboard-grid {
grid-template-columns: 1fr;
}
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.5s ease-out;
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--bg-tertiary);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--secondary-color);
}
</style>
</head>
<body>
<div class="app-container">
<!-- 侧边栏 -->
<nav class="sidebar">
<div class="logo">
<div class="logo-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
<path d="M2 17l10 5 10-5"/>
<path d="M2 12l10 5 10-5"/>
</svg>
</div>
<span class="logo-text">NetGuard</span>
</div>
<ul class="nav-menu">
<li class="nav-item">
<a href="#" class="nav-link active">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7"/>
<rect x="14" y="3" width="7" height="7"/>
<rect x="14" y="14" width="7" height="7"/>
<rect x="3" y="14" width="7" height="7"/>
</svg>
<span>仪表板</span>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
</svg>
<span>实时连接</span>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>
</svg>
<span>进程监控</span>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
<line x1="8" y1="21" x2="16" y2="21"/>
<line x1="12" y1="17" x2="12" y2="21"/>
</svg>
<span>防火墙规则</span>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 20V10"/>
<path d="M12 20V4"/>
<path d="M6 20v-6"/>
</svg>
<span>统计分析</span>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1 1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
<span>设置</span>
</a>
</li>
</ul>
</nav>
<!-- 主内容区 -->
<main class="main-content">
<div class="header">
<h1 class="page-title">网络监控仪表板</h1>
<div class="header-actions">
<button class="btn btn-secondary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
查看详情
</button>
<button class="btn btn-primary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
<path d="M2 17l10 5 10-5"/>
<path d="M2 12l10 5 10-5"/>
</svg>
添加规则
</button>
</div>
</div>
<!-- 统计卡片 -->
<div class="dashboard-grid fade-in">
<div class="card stat-card">
<div class="stat-value">247</div>
<div class="stat-label">活跃连接</div>
<div class="stat-change positive">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M7 14l5-5 5 5"/>
</svg>
+12%
</div>
</div>
<div class="card stat-card">
<div class="stat-value">1.2 GB</div>
<div class="stat-label">今日流量</div>
<div class="stat-change positive">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M7 14l5-5 5 5"/>
</svg>
+8%
</div>
</div>
<div class="card stat-card">
<div class="stat-value">43</div>
<div class="stat-label">活跃进程</div>
<div class="stat-change negative">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M17 10l-5 5-5-5"/>
</svg>
-3%
</div>
</div>
<div class="card stat-card">
<div class="stat-value">156</div>
<div class="stat-label">防火墙规则</div>
<div class="stat-change positive">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M7 14l5-5 5 5"/>
</svg>
+2
</div>
</div>
</div>
<!-- 工具栏 -->
<div class="toolbar">
<input type="text" class="search-box" placeholder="搜索进程、IP或端口...">
<select class="filter-select">
<option>所有连接</option>
<option>活跃连接</option>
<option>已阻止</option>
</select>
<select class="filter-select">
<option>所有协议</option>
<option>TCP</option>
<option>UDP</option>
</select>
</div>
<!-- 连接表格 -->
<div class="connections-table fade-in">
<div class="table-header">
<h2 class="table-title">实时网络连接</h2>
<span class="stat-change positive">实时更新</span>
</div>
<table>
<thead>
<tr>
<th>进程</th>
<th>本地地址</th>
<th>远程地址</th>
<th>协议</th>
<th>状态</th>
<th>网速</th>
<th>流量</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="process-info">
<div class="process-icon">CH</div>
<div>
<div>Chrome.exe</div>
<div style="font-size: 12px; color: var(--text-muted);">Google Chrome</div>
</div>
</div>
</td>
<td>192.168.1.100:54321</td>
<td>74.125.224.72:443</td>
<td>TCP</td>
<td><span class="status-badge status-active">活跃</span></td>
<td>
<div class="speed-indicator">
<span>1.2 MB/s</span>
<div class="speed-bar">
<div class="speed-fill" style="width: 75%"></div>
</div>
</div>
</td>
<td>245.6 MB</td>
</tr>
<tr>
<td>
<div class="process-info">
<div class="process-icon">ST</div>
<div>
<div>Steam.exe</div>
<div style="font-size: 12px; color: var(--text-muted);">Steam Client</div>
</div>
</div>
</td>
<td>192.168.1.100:27036</td>
<td>104.96.230.4:80</td>
<td>TCP</td>
<td><span class="status-badge status-active">活跃</span></td>
<td>
<div class="speed-indicator">
<span>456 KB/s</span>
<div class="speed-bar">
<div class="speed-fill" style="width: 35%"></div>
</div>
</div>
</td>
<td>89.2 MB</td>
</tr>
<tr>
<td>
<div class="process-info">
<div class="process-icon">SV</div>
<div>
<div>svchost.exe</div>
<div style="font-size: 12px; color: var(--text-muted);">Windows Service</div>
</div>
</div>
</td>
<td>192.168.1.100:49152</td>
<td>20.42.73.24:443</td>
<td>TCP</td>
<td><span class="status-badge status-blocked">已阻止</span></td>
<td>
<div class="speed-indicator">
<span>0 B/s</span>
<div class="speed-bar">
<div class="speed-fill" style="width: 0%"></div>
</div>
</div>
</td>
<td>0 B</td>
</tr>
</tbody>
</table>
</div>
</main>
</div>
<script>
// 简单的交互效果
document.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
document.querySelectorAll('.nav-link').forEach(l => l.classList.remove('active'));
link.classList.add('active');
});
});
// 模拟数据更新
setInterval(() => {
document.querySelectorAll('.speed-fill').forEach(fill => {
const randomWidth = Math.random() * 100;
fill.style.width = randomWidth + '%';
});
}, 2000);
// 添加搜索功能
document.querySelector('.search-box').addEventListener('input', (e) => {
const searchTerm = e.target.value.toLowerCase();
document.querySelectorAll('tbody tr').forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(searchTerm) ? '' : 'none';
});
});
</script>
</body>
</html>
@@ -0,0 +1,726 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局虚拟滚动列表</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background-color: #f5f5f7;
color: #333;
}
.container {
max-width: 600px;
margin: 0 auto;
background-color: #fff;
min-height: 100vh;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.05);
}
.page-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
text-align: center;
position: relative;
overflow: hidden;
}
.page-header h1 {
font-size: 24px;
font-weight: 600;
position: relative;
z-index: 1;
}
.page-header::before {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
animation: shimmer 3s infinite;
}
@keyframes shimmer {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.list-section {
position: relative;
border-bottom: 1px solid #f1f3f5;
}
.list-header {
background: #f8f9fa;
padding: 15px 20px;
font-weight: 600;
font-size: 16px;
color: #495057;
border-bottom: 2px solid #e9ecef;
position: sticky;
top: 0;
z-index: 10;
backdrop-filter: blur(10px);
background: rgba(248, 249, 250, 0.95);
transition: all 0.3s ease;
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
justify-content: space-between;
}
.list-header:hover {
background: rgba(248, 249, 250, 1);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.list-header span {
display: flex;
align-items: center;
}
.list-header span::before {
content: '📋';
margin-right: 8px;
}
.header-info {
font-size: 12px;
opacity: 0.7;
margin-left: 10px;
}
.list-header::after {
content: '▼';
font-size: 12px;
transition: transform 0.3s ease;
margin-left: auto;
}
.list-header.collapsed::after {
transform: rotate(-90deg);
}
.virtual-list-container {
position: relative;
transition: max-height 0.3s ease-out;
}
.virtual-list-container.collapsed {
max-height: 0 !important;
overflow: hidden;
transition: max-height 0.3s ease-in;
}
.virtual-list-wrapper {
position: relative;
width: 100%;
}
.virtual-list-spacer {
width: 100%;
pointer-events: none;
}
.virtual-list-viewport {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.virtual-list-item {
position: absolute;
width: 100%;
height: 60px;
padding: 15px 20px;
border-bottom: 1px solid #f1f3f5;
transition: background-color 0.2s ease, padding-left 0.2s ease;
background: white;
display: flex;
align-items: center;
overflow: hidden;
}
.virtual-list-item::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 3px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
transform: translateX(-100%);
transition: transform 0.3s ease;
}
.virtual-list-item:hover {
background-color: #f8f9fa;
padding-left: 30px;
}
.virtual-list-item:hover::before {
transform: translateX(0);
}
.item-number {
display: inline-block;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
width: 28px;
height: 28px;
text-align: center;
line-height: 28px;
border-radius: 50%;
font-size: 12px;
font-weight: 600;
margin-right: 12px;
flex-shrink: 0;
}
.item-content {
flex: 1;
font-size: 14px;
}
/* 不同分类的颜色主题 */
.list-section:nth-child(2n) .list-header {
background: rgba(255, 243, 224, 0.95);
color: #ff6b6b;
}
.list-section:nth-child(2n) .list-header span::before {
content: '🍔';
}
.list-section:nth-child(3n) .list-header {
background: rgba(224, 255, 224, 0.95);
color: #51cf66;
}
.list-section:nth-child(3n) .list-header span::before {
content: '🥗';
}
.list-section:nth-child(4n) .list-header {
background: rgba(224, 240, 255, 0.95);
color: #339af0;
}
.list-section:nth-child(4n) .list-header span::before {
content: '🍰';
}
.list-section:nth-child(5n) .list-header {
background: rgba(255, 224, 255, 0.95);
color: #ae3ec9;
}
.list-section:nth-child(5n) .list-header span::before {
content: '🥤';
}
/* 滚动提示 */
.scroll-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px 15px;
border-radius: 20px;
font-size: 12px;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
.scroll-indicator.show {
opacity: 1;
}
.performance-info {
position: fixed;
top: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 8px 12px;
border-radius: 15px;
font-size: 11px;
font-family: monospace;
z-index: 1000;
max-width: 300px;
white-space: pre-line;
}
/* 移动端优化 */
@media (max-width: 768px) {
.container {
box-shadow: none;
}
.page-header h1 {
font-size: 20px;
}
.list-header {
padding: 12px 15px;
font-size: 15px;
}
.virtual-list-item {
padding: 12px 15px;
height: 55px;
}
.performance-info {
display: none;
}
}
</style>
</head>
<body>
<div class="performance-info" id="performanceInfo">
渲染: 0 / 0 项目
</div>
<div class="container">
<div class="page-header">
<h1>全局虚拟滚动列表演示</h1>
<p style="font-size: 14px; margin-top: 10px; opacity: 0.9;">支持大量数据,只有一个全局滚动条</p>
</div>
<div id="listContainer"></div>
</div>
<div class="scroll-indicator" id="scrollIndicator">继续滚动查看更多</div>
<script>
// 防抖函数
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 优化的节流函数
function throttle(func, delay) {
let timeoutId;
let lastExecTime = 0;
return function (...args) {
const currentTime = Date.now();
if (currentTime - lastExecTime > delay) {
func.apply(this, args);
lastExecTime = currentTime;
} else {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
lastExecTime = Date.now();
}, delay - (currentTime - lastExecTime));
}
};
}
class GlobalVirtualList {
constructor(container, data, options = {}) {
this.container = container;
this.data = data;
this.itemHeight = options.itemHeight || 60;
this.isCollapsed = options.collapsed || false;
this.startIndex = 0;
this.endIndex = 0;
this.bufferSize = 10;
this.listId = Math.random().toString(36).substr(2, 9);
// 稳定性优化参数
this.lastScrollTop = 0;
this.scrollDirection = 'down';
this.stableRangeCache = null;
this.lastRenderTime = 0;
this.minRenderInterval = 50; // 最小渲染间隔
this.init();
this.render();
}
init() {
// 创建虚拟列表容器
this.listContainer = document.createElement('div');
this.listContainer.className = 'virtual-list-container';
if (this.isCollapsed) {
this.listContainer.classList.add('collapsed');
}
// 创建包装器
this.wrapper = document.createElement('div');
this.wrapper.className = 'virtual-list-wrapper';
// 创建占位空间元素(维持总高度)
this.spacer = document.createElement('div');
this.spacer.className = 'virtual-list-spacer';
this.spacer.style.height = `${this.data.length * this.itemHeight}px`;
// 创建视口
this.viewport = document.createElement('div');
this.viewport.className = 'virtual-list-viewport';
this.wrapper.appendChild(this.spacer);
this.wrapper.appendChild(this.viewport);
this.listContainer.appendChild(this.wrapper);
this.container.appendChild(this.listContainer);
}
// 获取绝对位置
getAbsoluteOffset() {
const rect = this.wrapper.getBoundingClientRect();
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
return rect.top + scrollTop;
}
getVisibleRange() {
if (this.isCollapsed) {
return { start: 0, end: 0 };
}
// 获取当前滚动位置
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const windowHeight = window.innerHeight;
// 检测滚动方向
this.scrollDirection = scrollTop > this.lastScrollTop ? 'down' : 'up';
this.lastScrollTop = scrollTop;
// 获取容器的绝对位置
const containerTop = this.getAbsoluteOffset();
const containerHeight = this.data.length * this.itemHeight;
// 计算可见区域
const viewportTop = scrollTop;
const viewportBottom = scrollTop + windowHeight;
// 计算列表区域
const listTop = containerTop;
const listBottom = containerTop + containerHeight;
// 检查是否有交集
if (listBottom <= viewportTop || listTop >= viewportBottom) {
return { start: 0, end: 0 };
}
// 计算可见范围
const visibleStartInList = Math.max(0, viewportTop - listTop);
const visibleEndInList = Math.min(containerHeight, viewportBottom - listTop);
// 计算项目索引 - 使用稳定的整数运算
let startIndex = Math.floor(visibleStartInList / this.itemHeight);
let endIndex = Math.ceil(visibleEndInList / this.itemHeight);
// 确保索引在有效范围内
startIndex = Math.max(0, Math.min(startIndex, this.data.length - 1));
endIndex = Math.max(startIndex + 1, Math.min(endIndex, this.data.length));
// 动态调整缓冲区大小
const dynamicBuffer = this.scrollDirection === 'down'
? { top: Math.floor(this.bufferSize * 0.3), bottom: this.bufferSize }
: { top: this.bufferSize, bottom: Math.floor(this.bufferSize * 0.3) };
// 添加缓冲区
const bufferedStart = Math.max(0, startIndex - dynamicBuffer.top);
const bufferedEnd = Math.min(this.data.length, endIndex + dynamicBuffer.bottom);
return {
start: bufferedStart,
end: bufferedEnd
};
}
shouldUpdate(newStart, newEnd) {
// 时间限流
const now = Date.now();
if (now - this.lastRenderTime < this.minRenderInterval) {
return false;
}
// 如果范围完全相同,不更新
if (newStart === this.startIndex && newEnd === this.endIndex) {
return false;
}
// 计算重叠区域
const overlapStart = Math.max(this.startIndex, newStart);
const overlapEnd = Math.min(this.endIndex, newEnd);
const overlapSize = Math.max(0, overlapEnd - overlapStart);
const currentSize = this.endIndex - this.startIndex;
const newSize = newEnd - newStart;
// 如果重叠区域占当前显示区域的大部分,且变化不大,跳过更新
if (currentSize > 0 && overlapSize / currentSize > 0.7) {
const startDiff = Math.abs(newStart - this.startIndex);
const endDiff = Math.abs(newEnd - this.endIndex);
// 如果变化很小,跳过
if (startDiff <= 2 && endDiff <= 2) {
return false;
}
}
return true;
}
render() {
const { start, end } = this.getVisibleRange();
// 检查是否需要更新
if (!this.shouldUpdate(start, end)) {
return;
}
console.log(`List ${this.listId} 渲染范围: ${start} - ${end} (之前: ${this.startIndex} - ${this.endIndex})`);
this.startIndex = start;
this.endIndex = end;
this.lastRenderTime = Date.now();
// 清除现有项目
this.viewport.innerHTML = '';
// 如果折叠状态或没有可见项目,直接返回
if (this.isCollapsed || start >= end) {
return;
}
// 创建文档片段提高性能
const fragment = document.createDocumentFragment();
// 渲染可见项目
for (let i = start; i < end && i < this.data.length; i++) {
const item = this.createItem(this.data[i], i);
item.style.transform = `translateY(${i * this.itemHeight}px)`;
fragment.appendChild(item);
}
this.viewport.appendChild(fragment);
}
createItem(data, index) {
const item = document.createElement('div');
item.className = 'virtual-list-item';
item.innerHTML = `
<span class="item-number">${index + 1}</span>
<span class="item-content">${data}</span>
`;
// 添加点击效果
item.addEventListener('click', () => {
const scale = item.style.transform.includes('scale') ? '' : ' scale(0.98)';
item.style.transform = `translateY(${index * this.itemHeight}px)${scale}`;
if (scale) {
setTimeout(() => {
item.style.transform = `translateY(${index * this.itemHeight}px)`;
}, 200);
}
});
return item;
}
toggle() {
this.isCollapsed = !this.isCollapsed;
this.listContainer.classList.toggle('collapsed');
if (!this.isCollapsed) {
// 展开时重新渲染
setTimeout(() => {
this.startIndex = -1; // 强制重新渲染
this.endIndex = -1;
this.render();
}, 350);
} else {
// 折叠时清除内容
this.viewport.innerHTML = '';
}
}
updateOnScroll() {
this.render();
}
forceRefresh() {
this.startIndex = -1;
this.endIndex = -1;
this.lastRenderTime = 0;
this.render();
}
}
// 生成大量测试数据
function generateLargeDataset(prefix, count) {
const items = [];
const foods = [
'宫保鸡丁', '麻婆豆腐', '红烧肉', '鱼香肉丝', '青椒肉丝', '糖醋里脊',
'回锅肉', '蛋炒饭', '扬州炒饭', '牛肉面', '兰州拉面', '炸酱面',
'担担面', '热干面', '重庆小面', '小笼包', '煎饺', '蒸饺',
'包子', '馒头', '花卷', '烧饼', '油条', '豆浆', '胡辣汤', '煎蛋',
'培根', '香肠', '吐司', '三明治', '汉堡', '披萨', '意面', '拉面'
];
for (let i = 0; i < count; i++) {
const food = foods[i % foods.length];
const variant = Math.floor(i / foods.length) + 1;
const rating = '★'.repeat(Math.floor(Math.random() * 5) + 1);
items.push(`${prefix}${food}${variant > 1 ? ` (${variant})` : ''} ${rating}`);
}
return items;
}
// 创建数据集
const datasets = [
{
title: '早餐推荐',
data: generateLargeDataset('精品', 200),
collapsed: false
},
{
title: '主食类',
data: generateLargeDataset('招牌', 800),
collapsed: false
},
{
title: '素食专区',
data: generateLargeDataset('健康', 300),
collapsed: false
},
{
title: '甜品点心',
data: generateLargeDataset('甜蜜', 500),
collapsed: false
},
{
title: '饮品推荐',
data: generateLargeDataset('特调', 400),
collapsed: false
}
];
// 计算总项目数
window.totalItems = datasets.reduce((sum, d) => sum + d.data.length, 0);
// 初始化列表
const listContainer = document.getElementById('listContainer');
const virtualLists = [];
datasets.forEach((dataset, index) => {
// 创建列表区域
const section = document.createElement('div');
section.className = 'list-section';
// 创建表头
const header = document.createElement('div');
header.className = 'list-header';
if (dataset.collapsed) {
header.classList.add('collapsed');
}
header.innerHTML = `
<span>${dataset.title}</span>
<span class="header-info">${dataset.data.length} 项</span>
`;
section.appendChild(header);
listContainer.appendChild(section);
// 创建虚拟列表
const virtualList = new GlobalVirtualList(section, dataset.data, {
itemHeight: 60,
collapsed: dataset.collapsed
});
virtualLists.push(virtualList);
// 绑定表头点击事件
header.addEventListener('click', function() {
virtualList.toggle();
this.classList.toggle('collapsed');
// 添加点击动画
this.style.transform = 'scale(0.98)';
setTimeout(() => {
this.style.transform = 'scale(1)';
}, 200);
// 折叠/展开后需要重新渲染所有列表
setTimeout(() => {
virtualLists.forEach(list => list.forceRefresh());
updatePerformanceInfo();
}, 400);
});
});
// 全局滚动事件监听 - 使用节流
const throttledGlobalRender = throttle(() => {
virtualLists.forEach(list => list.updateOnScroll());
updatePerformanceInfo();
}, 32); // 约30fps,更保守的刷新率
window.addEventListener('scroll', throttledGlobalRender, { passive: true });
// 窗口大小变化时重新渲染
window.addEventListener('resize', debounce(() => {
virtualLists.forEach(list => list.forceRefresh());
updatePerformanceInfo();
}, 300));
// 更新性能信息
function updatePerformanceInfo() {
const allRendered = document.querySelectorAll('.virtual-list-item').length;
const perfInfo = document.getElementById('performanceInfo');
if (perfInfo) {
perfInfo.textContent = `渲染: ${allRendered} / ${window.totalItems} 项目`;
}
}
// 滚动提示功能
let scrollTimer;
const scrollIndicator = document.getElementById('scrollIndicator');
window.addEventListener('scroll', throttle(() => {
scrollIndicator.classList.add('show');
clearTimeout(scrollTimer);
scrollTimer = setTimeout(() => {
scrollIndicator.classList.remove('show');
}, 1000);
}, 100));
// 初始渲染
setTimeout(() => {
virtualLists.forEach(list => list.updateOnScroll());
updatePerformanceInfo();
}, 100);
</script>
</body>
</html>