Bladeren bron

feat: new page vibration monitor

未来全栈 1 week geleden
bovenliggende
commit
312bac1db1

+ 988 - 0
demo/device-monitor.html

@@ -0,0 +1,988 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>数控车床振幅监测系统</title>
+    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
+    <style>
+        :root {
+            --primary-color: #0066cc;
+            --secondary-color: #00b6c1;
+            --warning-color: #ff9800;
+            --danger-color: #f44336;
+            --success-color: #4caf50;
+            --dark-bg: #1a2a3a;
+            --card-bg: rgba(255, 255, 255, 0.1);
+        }
+        
+        * {
+            margin: 0;
+            padding: 0;
+            box-sizing: border-box;
+            font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
+        }
+        
+        body {
+            background: linear-gradient(135deg, var(--dark-bg) 0%, #0d1721 100%);
+            color: #fff;
+            min-height: 100vh;
+            padding: 20px;
+        }
+        
+        .container {
+            max-width: 1800px;
+            margin: 0 auto;
+        }
+        
+        header {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 20px 0;
+            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+            margin-bottom: 30px;
+        }
+        
+        .logo {
+            display: flex;
+            align-items: center;
+            gap: 15px;
+        }
+        
+        .logo h1 {
+            font-size: 28px;
+            font-weight: 700;
+            background: linear-gradient(90deg, var(--secondary-color), #6ec1e4);
+            -webkit-background-clip: text;
+            background-clip: text;
+            -webkit-text-fill-color: transparent;
+        }
+        
+        .logo .tagline {
+            font-size: 18px;
+            color: #aaa;
+            margin-top: 5px;
+        }
+        
+        .system-stats {
+            display: flex;
+            gap: 25px;
+        }
+        
+        .stat-card {
+            background: var(--card-bg);
+            border-radius: 12px;
+            padding: 15px 25px;
+            min-width: 200px;
+            text-align: center;
+            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
+            border: 1px solid rgba(255, 255, 255, 0.05);
+        }
+        
+        .stat-card h3 {
+            font-size: 14px;
+            font-weight: 500;
+            margin-bottom: 10px;
+            color: #ccc;
+        }
+        
+        .stat-card .value {
+            font-size: 26px;
+            font-weight: 700;
+            margin-bottom: 5px;
+        }
+        
+        .stat-card .highlight {
+            color: var(--secondary-color);
+            font-weight: 700;
+        }
+        
+        .alert-indicator {
+            position: absolute;
+            top: 10px;
+            right: 10px;
+            width: 12px;
+            height: 12px;
+            border-radius: 50%;
+            background-color: var(--success-color);
+            box-shadow: 0 0 10px var(--success-color);
+            transition: all 0.3s ease;
+        }
+        
+        .alert-indicator.warning {
+            background-color: var(--warning-color);
+            box-shadow: 0 0 10px var(--warning-color);
+        }
+        
+        .alert-indicator.danger {
+            background-color: var(--danger-color);
+            box-shadow: 0 0 10px var(--danger-color);
+            animation: pulse 1.5s infinite;
+        }
+        
+        @keyframes pulse {
+            0% { opacity: 1; }
+            50% { opacity: 0.4; }
+            100% { opacity: 1; }
+        }
+        
+        .dashboard {
+            display: grid;
+            grid-template-columns: 1fr 1fr;
+            gap: 25px;
+            margin-bottom: 30px;
+        }
+        
+        .card {
+            background: var(--card-bg);
+            border-radius: 15px;
+            padding: 25px;
+            box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
+            border: 1px solid rgba(255, 255, 255, 0.05);
+            position: relative;
+            overflow: hidden;
+        }
+        
+        .card-header {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: 20px;
+        }
+        
+        .card-title {
+            font-size: 20px;
+            font-weight: 600;
+            color: #fff;
+        }
+        
+        .chart-container {
+            height: 400px;
+            width: 100%;
+        }
+        
+        .dashboard-row {
+            display: grid;
+            grid-template-columns: 1fr 1fr;
+            gap: 25px;
+            margin-bottom: 30px;
+        }
+        
+        .cost-comparison {
+            display: grid;
+            grid-template-columns: 1fr 1fr;
+            gap: 20px;
+            margin-top: 20px;
+        }
+        
+        .cost-card {
+            background: rgba(0, 0, 0, 0.3);
+            border-radius: 10px;
+            padding: 15px;
+            text-align: center;
+        }
+        
+        .cost-card h4 {
+            font-size: 16px;
+            margin-bottom: 10px;
+            color: #ccc;
+        }
+        
+        .cost-value {
+            font-size: 24px;
+            font-weight: 700;
+        }
+        
+        .import-value {
+            color: #ff6b6b;
+        }
+        
+        .our-value {
+            color: var(--secondary-color);
+        }
+        
+        .key-metrics {
+            display: grid;
+            grid-template-columns: repeat(3, 1fr);
+            gap: 15px;
+            margin-top: 20px;
+        }
+        
+        .metric-card {
+            background: rgba(0, 0, 0, 0.3);
+            border-radius: 10px;
+            padding: 15px;
+            text-align: center;
+        }
+        
+        .metric-card h4 {
+            font-size: 14px;
+            margin-bottom: 10px;
+            color: #ccc;
+        }
+        
+        .metric-value {
+            font-size: 22px;
+            font-weight: 700;
+        }
+        
+        .controls {
+            display: flex;
+            gap: 15px;
+            margin-top: 20px;
+        }
+        
+        .btn {
+            background: var(--primary-color);
+            color: white;
+            border: none;
+            padding: 10px 20px;
+            border-radius: 6px;
+            cursor: pointer;
+            font-size: 16px;
+            font-weight: 500;
+            transition: all 0.3s ease;
+            flex: 1;
+        }
+        
+        .btn:hover {
+            background: #004d99;
+            transform: translateY(-2px);
+            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+        }
+        
+        .btn.secondary {
+            background: rgba(255, 255, 255, 0.1);
+        }
+        
+        .btn.secondary:hover {
+            background: rgba(255, 255, 255, 0.2);
+        }
+        
+        .btn.warning {
+            background: var(--warning-color);
+        }
+        
+        .btn.warning:hover {
+            background: #e68a00;
+        }
+        
+        .status-bar {
+            display: flex;
+            align-items: center;
+            gap: 15px;
+            margin-top: 20px;
+            padding: 10px 15px;
+            background: rgba(0, 0, 0, 0.3);
+            border-radius: 8px;
+        }
+        
+        .status-indicator {
+            width: 12px;
+            height: 12px;
+            border-radius: 50%;
+            background: var(--success-color);
+        }
+        
+        .status-text {
+            font-size: 16px;
+        }
+        
+        .status-indicator.warning {
+            background: var(--warning-color);
+        }
+        
+        .status-indicator.danger {
+            background: var(--danger-color);
+        }
+        
+        footer {
+            text-align: center;
+            padding: 30px 0;
+            color: #777;
+            font-size: 14px;
+            border-top: 1px solid rgba(255, 255, 255, 0.1);
+            margin-top: 40px;
+        }
+        
+        .alert-notification {
+            position: fixed;
+            top: 20px;
+            right: 20px;
+            background: var(--danger-color);
+            color: white;
+            padding: 20px 30px;
+            border-radius: 10px;
+            box-shadow: 0 10px 30px rgba(244, 67, 54, 0.3);
+            animation: slideIn 0.5s ease;
+            z-index: 1000;
+            display: flex;
+            align-items: center;
+            gap: 15px;
+            max-width: 400px;
+        }
+        
+        @keyframes slideIn {
+            from { transform: translateX(100%); opacity: 0; }
+            to { transform: translateX(0); opacity: 1; }
+        }
+        
+        .alert-icon {
+            font-size: 28px;
+        }
+        
+        .alert-content h3 {
+            font-size: 18px;
+            margin-bottom: 5px;
+        }
+        
+        .alert-content p {
+            font-size: 14px;
+            opacity: 0.9;
+        }
+        
+        .close-alert {
+            background: none;
+            border: none;
+            color: white;
+            font-size: 20px;
+            cursor: pointer;
+            margin-left: 10px;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <header>
+            <div class="logo">
+                <div>
+                    <h1>数控车床振幅监测系统</h1>
+                    <div class="tagline">工业监测领域的"歼-20"方案</div>
+                </div>
+            </div>
+            
+            <div class="system-stats">
+                <div class="stat-card">
+                    <h3>当前状态</h3>
+                    <div class="value" id="system-status">运行中</div>
+                    <div class="highlight" id="uptime">正常运行: 32小时</div>
+                </div>
+                
+                <div class="stat-card">
+                    <h3>检测响应速度</h3>
+                    <div class="value"><span id="response-time">8</span>ms</div>
+                    <div class="highlight">↑德国方案150倍</div>
+                </div>
+                
+                <div class="stat-card">
+                    <h3>误报率</h3>
+                    <div class="value"><span id="error-rate">0.8</span>%</div>
+                    <div class="highlight">↓德国方案86%</div>
+                </div>
+            </div>
+        </header>
+        
+        <div class="dashboard">
+            <div class="card">
+                <div class="card-header">
+                    <h2 class="card-title">实时振动波形</h2>
+                    <div class="alert-indicator" id="vibration-alert"></div>
+                </div>
+                <div class="chart-container" id="vibration-chart"></div>
+                <div class="status-bar">
+                    <div class="status-indicator" id="status-indicator"></div>
+                    <div class="status-text" id="status-text">系统运行正常</div>
+                </div>
+            </div>
+            
+            <div class="card">
+                <div class="card-header">
+                    <h2 class="card-title">成本控制与性能对比</h2>
+                </div>
+                <div class="chart-container" id="cost-chart"></div>
+                
+                <div class="cost-comparison">
+                    <div class="cost-card">
+                        <h4>单通道成本 (德国方案)</h4>
+                        <div class="cost-value import-value">¥10,284</div>
+                    </div>
+                    <div class="cost-card">
+                        <h4>单通道成本 (本系统)</h4>
+                        <div class="cost-value our-value">¥1,920</div>
+                    </div>
+                </div>
+                
+                <div class="key-metrics">
+                    <div class="metric-card">
+                        <h4>成本节约</h4>
+                        <div class="metric-value" style="color: var(--secondary-color);">92%</div>
+                    </div>
+                    <div class="metric-card">
+                        <h4>响应提升</h4>
+                        <div class="metric-value" style="color: var(--secondary-color);">150倍</div>
+                    </div>
+                    <div class="metric-card">
+                        <h4>误报率降低</h4>
+                        <div class="metric-value" style="color: var(--secondary-color);">86%</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        
+        <div class="dashboard-row">
+            <div class="card">
+                <div class="card-header">
+                    <h2 class="card-title">刀具寿命预测</h2>
+                    <div class="alert-indicator" id="tool-alert"></div>
+                </div>
+                <div class="chart-container" id="tool-chart"></div>
+                <div class="controls">
+                    <button class="btn">导出报告</button>
+                    <button class="btn secondary">维护计划</button>
+                    <button class="btn warning">更换刀具</button>
+                </div>
+            </div>
+            
+            <div class="card">
+                <div class="card-header">
+                    <h2 class="card-title">异常事件分析</h2>
+                </div>
+                <div class="chart-container" id="event-chart"></div>
+            </div>
+        </div>
+        
+        <footer>
+            <p>国产化高精度振动监测系统 © 2025 | 技术指标:微秒级采样(100kHz) | AI动态阈值算法(误报率0.8%) | 响应延迟<10ms</p>
+        </footer>
+    </div>
+    
+    <div class="alert-notification" id="alert-notification" style="display: none;">
+        <div class="alert-icon">⚠️</div>
+        <div class="alert-content">
+            <h3>振动异常报警!</h3>
+            <p>检测到异常振动幅度超过阈值,请立即检查设备!</p>
+        </div>
+        <button class="close-alert" onclick="closeAlert()">×</button>
+    </div>
+
+    <script>
+        // 初始化图表
+        const vibrationChart = echarts.init(document.getElementById('vibration-chart'));
+        const costChart = echarts.init(document.getElementById('cost-chart'));
+        const toolChart = echarts.init(document.getElementById('tool-chart'));
+        const eventChart = echarts.init(document.getElementById('event-chart'));
+        
+        // 振动图表配置
+        const vibrationOption = {
+            backgroundColor: 'transparent',
+            grid: {
+                top: 30,
+                right: 30,
+                bottom: 40,
+                left: 50
+            },
+            tooltip: {
+                trigger: 'axis'
+            },
+            legend: {
+                data: ['振动幅度', '动态阈值'],
+                textStyle: {
+                    color: '#ccc'
+                },
+                top: 0
+            },
+            xAxis: {
+                type: 'category',
+                data: [],
+                axisLine: {
+                    lineStyle: {
+                        color: '#666'
+                    }
+                },
+                axisLabel: {
+                    color: '#999'
+                }
+            },
+            yAxis: {
+                type: 'value',
+                name: '振幅 (g)',
+                nameTextStyle: {
+                    color: '#999'
+                },
+                axisLine: {
+                    lineStyle: {
+                        color: '#666'
+                    }
+                },
+                axisLabel: {
+                    color: '#999'
+                },
+                splitLine: {
+                    lineStyle: {
+                        color: 'rgba(255, 255, 255, 0.05)'
+                    }
+                }
+            },
+            series: [
+                {
+                    name: '振动幅度',
+                    type: 'line',
+                    smooth: true,
+                    data: [],
+                    lineStyle: {
+                        width: 3,
+                        color: '#00b6c1'
+                    },
+                    itemStyle: {
+                        color: '#00b6c1'
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                            { offset: 0, color: 'rgba(0, 182, 193, 0.7)' },
+                            { offset: 1, color: 'rgba(0, 182, 193, 0.1)' }
+                        ])
+                    },
+                    symbol: 'none'
+                },
+                {
+                    name: '动态阈值',
+                    type: 'line',
+                    smooth: true,
+                    data: [],
+                    lineStyle: {
+                        width: 2,
+                        color: '#ff9800',
+                        type: 'dashed'
+                    },
+                    itemStyle: {
+                        color: '#ff9800'
+                    },
+                    symbol: 'none'
+                }
+            ],
+            animation: false
+        };
+        
+        // 成本对比图表配置
+        const costOption = {
+            backgroundColor: 'transparent',
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'shadow'
+                }
+            },
+            legend: {
+                data: ['德国方案', '本系统'],
+                textStyle: {
+                    color: '#ccc'
+                },
+                top: 0
+            },
+            grid: {
+                top: 40,
+                right: 30,
+                bottom: 40,
+                left: 50
+            },
+            xAxis: {
+                type: 'category',
+                data: ['单通道成本', '误报率', '响应延迟'],
+                axisLine: {
+                    lineStyle: {
+                        color: '#666'
+                    }
+                },
+                axisLabel: {
+                    color: '#999',
+                    interval: 0
+                }
+            },
+            yAxis: [
+                {
+                    type: 'value',
+                    name: '成本 (元)',
+                    min: 0,
+                    max: 12000,
+                    axisLine: {
+                        lineStyle: {
+                            color: '#666'
+                        }
+                    },
+                    axisLabel: {
+                        color: '#999',
+                        formatter: '{value}'
+                    },
+                    splitLine: {
+                        lineStyle: {
+                            color: 'rgba(255, 255, 255, 0.05)'
+                        }
+                    }
+                },
+                {
+                    type: 'value',
+                    name: '比率/毫秒',
+                    min: 0,
+                    max: 6,
+                    axisLine: {
+                        lineStyle: {
+                            color: '#666'
+                        }
+                    },
+                    axisLabel: {
+                        color: '#999',
+                        formatter: function(value) {
+                            if (value < 1) return value * 100 + '%';
+                            return value + 'ms';
+                        }
+                    }
+                }
+            ],
+            series: [
+                {
+                    name: '德国方案',
+                    type: 'bar',
+                    barWidth: 30,
+                    itemStyle: {
+                        color: '#ff6b6b'
+                    },
+                    data: [10284, 0.058, 1.2]
+                },
+                {
+                    name: '本系统',
+                    type: 'bar',
+                    barWidth: 30,
+                    itemStyle: {
+                        color: '#00b6c1'
+                    },
+                    data: [1920, 0.008, 0.008]
+                }
+            ]
+        };
+        
+        // 刀具寿命图表配置
+        const toolOption = {
+            backgroundColor: 'transparent',
+            tooltip: {
+                trigger: 'axis'
+            },
+            legend: {
+                data: ['刀具磨损度'],
+                textStyle: {
+                    color: '#ccc'
+                },
+                top: 0
+            },
+            grid: {
+                top: 30,
+                right: 30,
+                bottom: 40,
+                left: 50
+            },
+            xAxis: {
+                type: 'category',
+                data: [],
+                axisLine: {
+                    lineStyle: {
+                        color: '#666'
+                    }
+                },
+                axisLabel: {
+                    color: '#999'
+                }
+            },
+            yAxis: {
+                type: 'value',
+                name: '磨损度 (%)',
+                min: 0,
+                max: 100,
+                axisLine: {
+                    lineStyle: {
+                        color: '#666'
+                    }
+                },
+                axisLabel: {
+                    color: '#999'
+                },
+                splitLine: {
+                    lineStyle: {
+                        color: 'rgba(255, 255, 255, 0.05)'
+                    }
+                }
+            },
+            series: [
+                {
+                    name: '刀具磨损度',
+                    type: 'line',
+                    data: [],
+                    smooth: true,
+                    lineStyle: {
+                        width: 3,
+                        color: '#ff9800'
+                    },
+                    itemStyle: {
+                        color: '#ff9800'
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                            { offset: 0, color: 'rgba(255, 152, 0, 0.5)' },
+                            { offset: 1, color: 'rgba(255, 152, 0, 0.1)' }
+                        ])
+                    },
+                    markLine: {
+                        silent: true,
+                        lineStyle: {
+                            color: '#f44336',
+                            width: 2,
+                            type: 'dashed'
+                        },
+                        data: [
+                            {
+                                yAxis: 85,
+                                label: {
+                                    formatter: '更换阈值',
+                                    position: 'start'
+                                }
+                            }
+                        ]
+                    }
+                }
+            ]
+        };
+        
+        // 异常事件图表配置
+        const eventOption = {
+            backgroundColor: 'transparent',
+            tooltip: {
+                trigger: 'axis'
+            },
+            legend: {
+                data: ['异常事件'],
+                textStyle: {
+                    color: '#ccc'
+                },
+                top: 0
+            },
+            grid: {
+                top: 30,
+                right: 30,
+                bottom: 40,
+                left: 50
+            },
+            xAxis: {
+                type: 'category',
+                data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
+                axisLine: {
+                    lineStyle: {
+                        color: '#666'
+                    }
+                },
+                axisLabel: {
+                    color: '#999'
+                }
+            },
+            yAxis: {
+                type: 'value',
+                name: '事件次数',
+                axisLine: {
+                    lineStyle: {
+                        color: '#666'
+                    }
+                },
+                axisLabel: {
+                    color: '#999'
+                },
+                splitLine: {
+                    lineStyle: {
+                        color: 'rgba(255, 255, 255, 0.05)'
+                    }
+                }
+            },
+            series: [
+                {
+                    name: '异常事件',
+                    type: 'bar',
+                    barWidth: 30,
+                    itemStyle: {
+                        color: '#00b6c1'
+                    },
+                    data: [2, 0, 1, 3, 1, 0, 0]
+                }
+            ]
+        };
+        
+        // 应用图表配置
+        vibrationChart.setOption(vibrationOption);
+        costChart.setOption(costOption);
+        toolChart.setOption(toolOption);
+        eventChart.setOption(eventOption);
+        
+        // 窗口大小调整时重新渲染图表
+        window.addEventListener('resize', function() {
+            vibrationChart.resize();
+            costChart.resize();
+            toolChart.resize();
+            eventChart.resize();
+        });
+        
+        // 模拟实时数据
+        let timeCounter = 0;
+        let vibrationData = [];
+        let thresholdData = [];
+        let toolWearData = [];
+        let isAlertActive = false;
+        
+        // 动态阈值算法
+        function calculateDynamicThreshold(data) {
+            // 简单实现:基于历史数据的均值和标准差
+            if (data.length < 20) return 0.8;
+            
+            const recentData = data.slice(-20);
+            const sum = recentData.reduce((a, b) => a + b, 0);
+            const mean = sum / recentData.length;
+            
+            const squareDiffs = recentData.map(value => Math.pow(value - mean, 2));
+            const variance = squareDiffs.reduce((a, b) => a + b, 0) / recentData.length;
+            const stdDev = Math.sqrt(variance);
+            
+            return mean + 4 * stdDev;  // 4倍标准差作为阈值
+        }
+        
+        // 生成模拟数据
+        function generateData() {
+            timeCounter++;
+            
+            // 生成振动数据(模拟微秒级采样)
+            const baseValue = Math.sin(timeCounter * 0.2) * 0.8;
+            const noise = (Math.random() - 0.5) * 0.3;
+            let amplitude = baseValue + noise;
+            
+            // 0.1%概率产生异常
+            if (Math.random() < 0.001) {
+                amplitude = 2.5 + Math.random() * 1.0; // 异常值范围2.5-3.5
+            }
+            
+            vibrationData.push(amplitude);
+            if (vibrationData.length > 200) vibrationData.shift();
+            
+            // 计算动态阈值
+            const threshold = calculateDynamicThreshold(vibrationData);
+            thresholdData.push(threshold);
+            if (thresholdData.length > 200) thresholdData.shift();
+            
+            // 更新刀具磨损数据
+            const wear = Math.min(85, 20 + timeCounter * 0.05);
+            toolWearData.push(wear);
+            if (toolWearData.length > 20) toolWearData.shift();
+            
+            // 更新图表
+            const xAxisData = Array.from({length: vibrationData.length}, (_, i) => i);
+            vibrationChart.setOption({
+                xAxis: {
+                    data: xAxisData
+                },
+                series: [
+                    { data: vibrationData },
+                    { data: thresholdData }
+                ]
+            });
+            
+            // 更新刀具寿命图表
+            const toolXAxisData = Array.from({length: toolWearData.length}, (_, i) => i);
+            toolChart.setOption({
+                xAxis: {
+                    data: toolXAxisData
+                },
+                series: [
+                    { data: toolWearData }
+                ]
+            });
+            
+            // 检查是否需要报警
+            const statusIndicator = document.getElementById('status-indicator');
+            const statusText = document.getElementById('status-text');
+            const alertIndicator = document.getElementById('vibration-alert');
+            
+            if (amplitude > threshold) {
+                statusIndicator.className = 'status-indicator danger';
+                statusText.textContent = '检测到异常振动!';
+                alertIndicator.className = 'alert-indicator danger';
+                
+                if (!isAlertActive) {
+                    showAlert();
+                    isAlertActive = true;
+                }
+            } else if (amplitude > threshold * 0.8) 
+            {
+                statusIndicator.className = 'status-indicator'
+                statusIndicator.className = 'status-indicator warning';
+                statusText.textContent = '振动接近阈值,请注意!';
+                alertIndicator.className = 'alert-indicator warning';
+                isAlertActive = false;
+            }
+             else
+             {
+                statusIndicator.className = 'status-indicator';
+                statusText.textContent = '系统运行正常';
+                alertIndicator.className = 'alert-indicator';
+                isAlertActive = false;
+            }
+        }
+        
+        // 显示报警通知
+        function showAlert() {
+            const alertNotification = document.getElementById('alert-notification');
+            alertNotification.style.display = 'flex';
+            
+            // 播放警报声
+            const audioContext = new (window.AudioContext || window.webkitAudioContext)();
+            const oscillator = audioContext.createOscillator();
+            const gainNode = audioContext.createGain();
+            
+            oscillator.connect(gainNode);
+            gainNode.connect(audioContext.destination);
+            
+            oscillator.type = 'sine';
+            oscillator.frequency.setValueAtTime(440, audioContext.currentTime);
+            oscillator.frequency.setValueAtTime(880, audioContext.currentTime + 0.1);
+            
+            gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
+            
+            oscillator.start();
+            oscillator.stop(audioContext.currentTime + 0.5);
+            
+            // 背景闪烁效果
+            let flashCount = 0;
+            const flashInterval = setInterval(() => {
+                document.body.style.backgroundColor = flashCount % 2 === 0 ? 'rgba(244, 67, 54, 0.2)' : '';
+                flashCount++;
+                
+                if (flashCount > 10) {
+                    clearInterval(flashInterval);
+                    document.body.style.backgroundColor = '';
+                }
+            }, 200);
+        }
+        
+        // 关闭报警通知
+        function closeAlert() {
+            document.getElementById('alert-notification').style.display = 'none';
+        }
+        
+        // 每秒生成4次数据(模拟100kHz采样率)
+        setInterval(generateData, 250);
+        
+        // 更新运行时间
+        let uptime = 0;
+        setInterval(() => {
+            uptime++;
+            const hours = Math.floor(uptime / 3600);
+            const minutes = Math.floor((uptime % 3600) / 60);
+            const seconds = uptime % 60;
+            document.getElementById('uptime').textContent = 
+                `正常运行: ${hours}小时${minutes}分${seconds}秒`;
+        }, 1000);
+    </script>
+</body>
+</html>

+ 32 - 0
industry-web/package-lock.json

@@ -14,6 +14,7 @@
         "@angular/forms": "^20.0.0",
         "@angular/platform-browser": "^20.0.0",
         "@angular/router": "^20.0.0",
+        "echarts": "^5.6.0",
         "rxjs": "~7.8.0",
         "tslib": "^2.3.0",
         "zone.js": "~0.15.0"
@@ -4334,6 +4335,22 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/echarts": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
+      "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "2.3.0",
+        "zrender": "5.6.1"
+      }
+    },
+    "node_modules/echarts/node_modules/tslib": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+      "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
+      "license": "0BSD"
+    },
     "node_modules/ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -8902,6 +8919,21 @@
       "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz",
       "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==",
       "license": "MIT"
+    },
+    "node_modules/zrender": {
+      "version": "5.6.1",
+      "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz",
+      "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "tslib": "2.3.0"
+      }
+    },
+    "node_modules/zrender/node_modules/tslib": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+      "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
+      "license": "0BSD"
     }
   }
 }

+ 1 - 0
industry-web/package.json

@@ -26,6 +26,7 @@
     "@angular/forms": "^20.0.0",
     "@angular/platform-browser": "^20.0.0",
     "@angular/router": "^20.0.0",
+    "echarts": "^5.6.0",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0",
     "zone.js": "~0.15.0"

File diff suppressed because it is too large
+ 0 - 195
industry-web/src/app/app.html


+ 11 - 1
industry-web/src/app/app.routes.ts

@@ -1,3 +1,13 @@
 import { Routes } from '@angular/router';
 
-export const routes: Routes = [];
+export const routes: Routes = [
+    {
+        path:"",
+        pathMatch:"full",
+        redirectTo:"inductry/machine/vibration"
+    },
+    {
+        path:"inductry/machine/vibration",
+        loadComponent: () => import('../modules/industry/machine/page-vibration-monitor/page-vibration-monitor').then(m => m.PageVibrationMonitorComponent)
+    }
+];

+ 112 - 0
industry-web/src/modules/industry/machine/page-vibration-monitor/page-vibration-monitor.html

@@ -0,0 +1,112 @@
+<div class="container">
+  <header>
+    <div class="logo">
+      <div>
+        <h1>数控车床振幅监测系统</h1>
+        <div class="tagline">工业监测领域的"歼-20"方案</div>
+      </div>
+    </div>
+    
+    <div class="system-stats">
+      <div class="stat-card">
+        <h3>当前状态</h3>
+        <div class="value">{{ systemStatus }}</div>
+        <div class="highlight">{{ uptime }}</div>
+      </div>
+      
+      <div class="stat-card">
+        <h3>检测响应速度</h3>
+        <div class="value">{{ responseTime }}ms</div>
+        <div class="highlight">↑德国方案150倍</div>
+      </div>
+      
+      <div class="stat-card">
+        <h3>误报率</h3>
+        <div class="value">{{ errorRate }}%</div>
+        <div class="highlight">↓德国方案86%</div>
+      </div>
+    </div>
+  </header>
+  
+  <div class="dashboard">
+    <div class="card">
+      <div class="card-header">
+        <h2 class="card-title">实时振动波形</h2>
+        <div class="alert-indicator" id="vibration-alert"></div>
+      </div>
+      <div class="chart-container" id="vibration-chart"></div>
+      <div class="status-bar">
+        <div class="status-indicator" id="status-indicator"></div>
+        <div class="status-text" id="status-text">系统运行正常</div>
+      </div>
+    </div>
+    
+    <div class="card">
+      <div class="card-header">
+        <h2 class="card-title">成本控制与性能对比</h2>
+      </div>
+      <div class="chart-container" id="cost-chart"></div>
+      
+      <div class="cost-comparison">
+        <div class="cost-card">
+          <h4>单通道成本 (德国方案)</h4>
+          <div class="cost-value import-value">¥10,284</div>
+        </div>
+        <div class="cost-card">
+          <h4>单通道成本 (本系统)</h4>
+          <div class="cost-value our-value">¥1,920</div>
+        </div>
+      </div>
+      
+      <div class="key-metrics">
+        <div class="metric-card">
+          <h4>成本节约</h4>
+          <div class="metric-value" style="color: var(--secondary-color);">92%</div>
+        </div>
+        <div class="metric-card">
+          <h4>响应提升</h4>
+          <div class="metric-value" style="color: var(--secondary-color);">150倍</div>
+        </div>
+        <div class="metric-card">
+          <h4>误报率降低</h4>
+          <div class="metric-value" style="color: var(--secondary-color);">86%</div>
+        </div>
+      </div>
+    </div>
+  </div>
+  
+  <div class="dashboard-row">
+    <div class="card">
+      <div class="card-header">
+        <h2 class="card-title">刀具寿命预测</h2>
+        <div class="alert-indicator" id="tool-alert"></div>
+      </div>
+      <div class="chart-container" id="tool-chart"></div>
+      <div class="controls">
+        <button class="btn">导出报告</button>
+        <button class="btn secondary">维护计划</button>
+        <button class="btn warning">更换刀具</button>
+      </div>
+    </div>
+    
+    <div class="card">
+      <div class="card-header">
+        <h2 class="card-title">异常事件分析</h2>
+      </div>
+      <div class="chart-container" id="event-chart"></div>
+    </div>
+  </div>
+  
+  <footer>
+    <p>国产化高精度振动监测系统 © 2025 | 技术指标:微秒级采样(100kHz) | AI动态阈值算法(误报率0.8%) | 响应延迟&lt;10ms</p>
+  </footer>
+</div>
+
+<div class="alert-notification" id="alert-notification" style="display: none;">
+  <div class="alert-icon">⚠️</div>
+  <div class="alert-content">
+    <h3>振动异常报警!</h3>
+    <p>检测到异常振动幅度超过阈值,请立即检查设备!</p>
+  </div>
+  <button class="close-alert" (click)="closeAlert()">×</button>
+</div>

+ 341 - 0
industry-web/src/modules/industry/machine/page-vibration-monitor/page-vibration-monitor.scss

@@ -0,0 +1,341 @@
+:host {
+  --primary-color: #0066cc;
+  --secondary-color: #00b6c1;
+  --warning-color: #ff9800;
+  --danger-color: #f44336;
+  --success-color: #4caf50;
+  --dark-bg: #1a2a3a;
+  --card-bg: rgba(255, 255, 255, 0.1);
+}
+
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
+}
+
+.container {
+  background: linear-gradient(135deg, var(--dark-bg) 0%, #0d1721 100%);
+  color: #fff;
+  min-height: 100vh;
+  padding: 20px;
+  max-width: 1800px;
+  margin: 0 auto;
+}
+
+header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 20px 0;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+  margin-bottom: 30px;
+}
+
+.logo {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+}
+
+.logo h1 {
+  font-size: 28px;
+  font-weight: 700;
+  background: linear-gradient(90deg, var(--secondary-color), #6ec1e4);
+  -webkit-background-clip: text;
+  background-clip: text;
+  -webkit-text-fill-color: transparent;
+}
+
+.logo .tagline {
+  font-size: 18px;
+  color: #aaa;
+  margin-top: 5px;
+}
+
+.system-stats {
+  display: flex;
+  gap: 25px;
+}
+
+.stat-card {
+  background: var(--card-bg);
+  border-radius: 12px;
+  padding: 15px 25px;
+  min-width: 200px;
+  text-align: center;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
+  border: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.stat-card h3 {
+  font-size: 14px;
+  font-weight: 500;
+  margin-bottom: 10px;
+  color: #ccc;
+}
+
+.stat-card .value {
+  font-size: 26px;
+  font-weight: 700;
+  margin-bottom: 5px;
+}
+
+.stat-card .highlight {
+  color: var(--secondary-color);
+  font-weight: 700;
+}
+
+.alert-indicator {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+  background-color: var(--success-color);
+  box-shadow: 0 0 10px var(--success-color);
+  transition: all 0.3s ease;
+}
+
+.alert-indicator.warning {
+  background-color: var(--warning-color);
+  box-shadow: 0 0 10px var(--warning-color);
+}
+
+.alert-indicator.danger {
+  background-color: var(--danger-color);
+  box-shadow: 0 0 10px var(--danger-color);
+  animation: pulse 1.5s infinite;
+}
+
+@keyframes pulse {
+  0% { opacity: 1; }
+  50% { opacity: 0.4; }
+  100% { opacity: 1; }
+}
+
+.dashboard {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 25px;
+  margin-bottom: 30px;
+}
+
+.card {
+  background: var(--card-bg);
+  border-radius: 15px;
+  padding: 25px;
+  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
+  border: 1px solid rgba(255, 255, 255, 0.05);
+  position: relative;
+  overflow: hidden;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.card-title {
+  font-size: 20px;
+  font-weight: 600;
+  color: #fff;
+}
+
+.chart-container {
+  height: 400px;
+  width: 100%;
+}
+
+.dashboard-row {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 25px;
+  margin-bottom: 30px;
+}
+
+.cost-comparison {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 20px;
+  margin-top: 20px;
+}
+
+.cost-card {
+  background: rgba(0, 0, 0, 0.3);
+  border-radius: 10px;
+  padding: 15px;
+  text-align: center;
+}
+
+.cost-card h4 {
+  font-size: 16px;
+  margin-bottom: 10px;
+  color: #ccc;
+}
+
+.cost-value {
+  font-size: 24px;
+  font-weight: 700;
+}
+
+.import-value {
+  color: #ff6b6b;
+}
+
+.our-value {
+  color: var(--secondary-color);
+}
+
+.key-metrics {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 15px;
+  margin-top: 20px;
+}
+
+.metric-card {
+  background: rgba(0, 0, 0, 0.3);
+  border-radius: 10px;
+  padding: 15px;
+  text-align: center;
+}
+
+.metric-card h4 {
+  font-size: 14px;
+  margin-bottom: 10px;
+  color: #ccc;
+}
+
+.metric-value {
+  font-size: 22px;
+  font-weight: 700;
+}
+
+.controls {
+  display: flex;
+  gap: 15px;
+  margin-top: 20px;
+}
+
+.btn {
+  background: var(--primary-color);
+  color: white;
+  border: none;
+  padding: 10px 20px;
+  border-radius: 6px;
+  cursor: pointer;
+  font-size: 16px;
+  font-weight: 500;
+  transition: all 0.3s ease;
+  flex: 1;
+}
+
+.btn:hover {
+  background: #004d99;
+  transform: translateY(-2px);
+  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+}
+
+.btn.secondary {
+  background: rgba(255, 255, 255, 0.1);
+}
+
+.btn.secondary:hover {
+  background: rgba(255, 255, 255, 0.2);
+}
+
+.btn.warning {
+  background: var(--warning-color);
+}
+
+.btn.warning:hover {
+  background: #e68a00;
+}
+
+.status-bar {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  margin-top: 20px;
+  padding: 10px 15px;
+  background: rgba(0, 0, 0, 0.3);
+  border-radius: 8px;
+}
+
+.status-indicator {
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+  background: var(--success-color);
+}
+
+.status-text {
+  font-size: 16px;
+}
+
+.status-indicator.warning {
+  background: var(--warning-color);
+}
+
+.status-indicator.danger {
+  background: var(--danger-color);
+}
+
+footer {
+  text-align: center;
+  padding: 30px 0;
+  color: #777;
+  font-size: 14px;
+  border-top: 1px solid rgba(255, 255, 255, 0.1);
+  margin-top: 40px;
+}
+
+.alert-notification {
+  position: fixed;
+  top: 20px;
+  right: 20px;
+  background: var(--danger-color);
+  color: white;
+  padding: 20px 30px;
+  border-radius: 10px;
+  box-shadow: 0 10px 30px rgba(244, 67, 54, 0.3);
+  animation: slideIn 0.5s ease;
+  z-index: 1000;
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  max-width: 400px;
+}
+
+@keyframes slideIn {
+  from { transform: translateX(100%); opacity: 0; }
+  to { transform: translateX(0); opacity: 1; }
+}
+
+.alert-icon {
+  font-size: 28px;
+}
+
+.alert-content h3 {
+  font-size: 18px;
+  margin-bottom: 5px;
+}
+
+.alert-content p {
+  font-size: 14px;
+  opacity: 0.9;
+}
+
+.close-alert {
+  background: none;
+  border: none;
+  color: white;
+  font-size: 20px;
+  cursor: pointer;
+  margin-left: 10px;
+}

+ 23 - 0
industry-web/src/modules/industry/machine/page-vibration-monitor/page-vibration-monitor.spec.ts

@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageVibrationMonitor } from './page-vibration-monitor';
+
+describe('PageVibrationMonitor', () => {
+  let component: PageVibrationMonitor;
+  let fixture: ComponentFixture<PageVibrationMonitor>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [PageVibrationMonitor]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(PageVibrationMonitor);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 592 - 0
industry-web/src/modules/industry/machine/page-vibration-monitor/page-vibration-monitor.ts

@@ -0,0 +1,592 @@
+import { Component, OnInit, OnDestroy, AfterViewInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import * as echarts from 'echarts';
+
+@Component({
+  selector: 'app-page-vibration-monitor',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './page-vibration-monitor.html',
+  styleUrls: ['./page-vibration-monitor.scss']
+})
+export class PageVibrationMonitorComponent implements OnInit, AfterViewInit, OnDestroy {
+  // 系统状态变量
+  systemStatus = '运行中';
+  uptime = '正常运行: 0小时0分0秒';
+  responseTime = 8;
+  errorRate = 0.8;
+  
+  // 图表实例
+  vibrationChart!: echarts.ECharts;
+  costChart!: echarts.ECharts;
+  toolChart!: echarts.ECharts;
+  eventChart!: echarts.ECharts;
+  
+  // 数据变量
+  timeCounter = 0;
+  vibrationData: number[] = [];
+  thresholdData: number[] = [];
+  toolWearData: number[] = [];
+  isAlertActive = false;
+  uptimeSeconds = 0;
+  
+  // 定时器
+  private dataInterval!: any;
+  private uptimeInterval!: any;
+  
+  // 样式变量
+  colors = {
+    primary: '#0066cc',
+    secondary: '#00b6c1',
+    warning: '#ff9800',
+    danger: '#f44336',
+    success: '#4caf50',
+    darkBg: '#1a2a3a',
+    cardBg: 'rgba(255, 255, 255, 0.1)'
+  };
+  
+  ngOnInit() {
+    // 初始化数据
+    this.startDataGeneration();
+    this.startUptimeCounter();
+  }
+  
+  ngAfterViewInit() {
+    this.initCharts();
+    window.addEventListener('resize', this.onWindowResize);
+  }
+  
+  ngOnDestroy() {
+    // 清理定时器和事件监听
+    clearInterval(this.dataInterval);
+    clearInterval(this.uptimeInterval);
+    window.removeEventListener('resize', this.onWindowResize);
+    
+    // 销毁图表实例
+    this.vibrationChart?.dispose();
+    this.costChart?.dispose();
+    this.toolChart?.dispose();
+    this.eventChart?.dispose();
+  }
+  
+  private initCharts() {
+    // 初始化振动图表
+    this.vibrationChart = echarts.init(document.getElementById('vibration-chart') as HTMLElement);
+    this.vibrationChart.setOption(this.getVibrationChartOption());
+    
+    // 初始化成本图表
+    this.costChart = echarts.init(document.getElementById('cost-chart') as HTMLElement);
+    this.costChart.setOption(this.getCostChartOption());
+    
+    // 初始化刀具寿命图表
+    this.toolChart = echarts.init(document.getElementById('tool-chart') as HTMLElement);
+    this.toolChart.setOption(this.getToolChartOption());
+    
+    // 初始化异常事件图表
+    this.eventChart = echarts.init(document.getElementById('event-chart') as HTMLElement);
+    this.eventChart.setOption(this.getEventChartOption());
+  }
+  
+  private onWindowResize = () => {
+    this.vibrationChart?.resize();
+    this.costChart?.resize();
+    this.toolChart?.resize();
+    this.eventChart?.resize();
+  };
+  
+  private startDataGeneration() {
+    this.dataInterval = setInterval(() => this.generateData(), 250);
+  }
+  
+  private startUptimeCounter() {
+    this.uptimeInterval = setInterval(() => {
+      this.uptimeSeconds++;
+      const hours = Math.floor(this.uptimeSeconds / 3600);
+      const minutes = Math.floor((this.uptimeSeconds % 3600) / 60);
+      const seconds = this.uptimeSeconds % 60;
+      this.uptime = `正常运行: ${hours}小时${minutes}分${seconds}秒`;
+    }, 1000);
+  }
+  
+  private generateData() {
+    this.timeCounter++;
+    
+    // 生成振动数据
+    const baseValue = Math.sin(this.timeCounter * 0.2) * 0.8;
+    const noise = (Math.random() - 0.5) * 0.3;
+    let amplitude = baseValue + noise;
+    
+    // 0.1%概率产生异常
+    if (Math.random() < 0.001) {
+      amplitude = 2.5 + Math.random() * 1.0;
+    }
+    
+    this.vibrationData.push(amplitude);
+    if (this.vibrationData.length > 200) this.vibrationData.shift();
+    
+    // 计算动态阈值
+    const threshold = this.calculateDynamicThreshold(this.vibrationData);
+    this.thresholdData.push(threshold);
+    if (this.thresholdData.length > 200) this.thresholdData.shift();
+    
+    // 更新刀具磨损数据
+    const wear = Math.min(85, 20 + this.timeCounter * 0.05);
+    this.toolWearData.push(wear);
+    if (this.toolWearData.length > 20) this.toolWearData.shift();
+    
+    // 更新图表
+    const xAxisData = Array.from({length: this.vibrationData.length}, (_, i) => i);
+    this.vibrationChart.setOption({
+      xAxis: {
+        data: xAxisData
+      },
+      series: [
+        { data: this.vibrationData },
+        { data: this.thresholdData }
+      ]
+    });
+    
+    // 更新刀具寿命图表
+    const toolXAxisData = Array.from({length: this.toolWearData.length}, (_, i) => i);
+    this.toolChart.setOption({
+      xAxis: {
+        data: toolXAxisData
+      },
+      series: [
+        { data: this.toolWearData }
+      ]
+    });
+    
+    // 检查报警状态
+    this.checkAlertStatus(amplitude, threshold);
+  }
+  
+  private calculateDynamicThreshold(data: number[]): number {
+    if (data.length < 20) return 0.8;
+    
+    const recentData = data.slice(-20);
+    const sum = recentData.reduce((a, b) => a + b, 0);
+    const mean = sum / recentData.length;
+    
+    const squareDiffs = recentData.map(value => Math.pow(value - mean, 2));
+    const variance = squareDiffs.reduce((a, b) => a + b, 0) / recentData.length;
+    const stdDev = Math.sqrt(variance);
+    
+    return mean + 4 * stdDev;
+  }
+  
+  private checkAlertStatus(amplitude: number, threshold: number) {
+    const statusIndicator = document.getElementById('status-indicator');
+    const statusText = document.getElementById('status-text');
+    const alertIndicator = document.getElementById('vibration-alert');
+    
+    if (!statusIndicator || !statusText || !alertIndicator) return;
+    
+    if (amplitude > threshold) {
+      statusIndicator.className = 'status-indicator danger';
+      statusText.textContent = '检测到异常振动!';
+      alertIndicator.className = 'alert-indicator danger';
+      
+      if (!this.isAlertActive) {
+        this.showAlert();
+        this.isAlertActive = true;
+      }
+    } else if (amplitude > threshold * 0.8) {
+      statusIndicator.className = 'status-indicator warning';
+      statusText.textContent = '振动接近阈值,请注意!';
+      alertIndicator.className = 'alert-indicator warning';
+      this.isAlertActive = false;
+    } else {
+      statusIndicator.className = 'status-indicator';
+      statusText.textContent = '系统运行正常';
+      alertIndicator.className = 'alert-indicator';
+      this.isAlertActive = false;
+    }
+  }
+  
+  private showAlert() {
+    const alertNotification = document.getElementById('alert-notification');
+    if (alertNotification) {
+      alertNotification.style.display = 'flex';
+    }
+    
+    // 播放警报声
+    const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
+    const oscillator = audioContext.createOscillator();
+    const gainNode = audioContext.createGain();
+    
+    oscillator.connect(gainNode);
+    gainNode.connect(audioContext.destination);
+    
+    oscillator.type = 'sine';
+    oscillator.frequency.setValueAtTime(440, audioContext.currentTime);
+    oscillator.frequency.setValueAtTime(880, audioContext.currentTime + 0.1);
+    
+    gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
+    
+    oscillator.start();
+    oscillator.stop(audioContext.currentTime + 0.5);
+    
+    // 背景闪烁效果
+    let flashCount = 0;
+    const flashInterval = setInterval(() => {
+      document.body.style.backgroundColor = flashCount % 2 === 0 ? 'rgba(244, 67, 54, 0.2)' : '';
+      flashCount++;
+      
+      if (flashCount > 10) {
+        clearInterval(flashInterval);
+        document.body.style.backgroundColor = '';
+      }
+    }, 200);
+  }
+  
+  closeAlert() {
+    const alertNotification = document.getElementById('alert-notification');
+    if (alertNotification) {
+      alertNotification.style.display = 'none';
+    }
+  }
+  
+  private getVibrationChartOption(): echarts.EChartsOption {
+    return {
+      backgroundColor: 'transparent',
+      grid: {
+        top: 30,
+        right: 30,
+        bottom: 40,
+        left: 50
+      },
+      tooltip: {
+        trigger: 'axis'
+      },
+      legend: {
+        data: ['振动幅度', '动态阈值'],
+        textStyle: {
+          color: '#ccc'
+        },
+        top: 0
+      },
+      xAxis: {
+        type: 'category',
+        data: [],
+        axisLine: {
+          lineStyle: {
+            color: '#666'
+          }
+        },
+        axisLabel: {
+          color: '#999'
+        }
+      },
+      yAxis: {
+        type: 'value',
+        name: '振幅 (g)',
+        nameTextStyle: {
+          color: '#999'
+        },
+        axisLine: {
+          lineStyle: {
+            color: '#666'
+          }
+        },
+        axisLabel: {
+          color: '#999'
+        },
+        splitLine: {
+          lineStyle: {
+            color: 'rgba(255, 255, 255, 0.05)'
+          }
+        }
+      },
+      series: [
+        {
+          name: '振动幅度',
+          type: 'line',
+          smooth: true,
+          data: [],
+          lineStyle: {
+            width: 3,
+            color: '#00b6c1'
+          },
+          itemStyle: {
+            color: '#00b6c1'
+          },
+          areaStyle: {
+            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+              { offset: 0, color: 'rgba(0, 182, 193, 0.7)' },
+              { offset: 1, color: 'rgba(0, 182, 193, 0.1)' }
+            ])
+          },
+          symbol: 'none'
+        },
+        {
+          name: '动态阈值',
+          type: 'line',
+          smooth: true,
+          data: [],
+          lineStyle: {
+            width: 2,
+            color: '#ff9800',
+            type: 'dashed'
+          },
+          itemStyle: {
+            color: '#ff9800'
+          },
+          symbol: 'none'
+        }
+      ],
+      animation: false
+    };
+  }
+  
+  private getCostChartOption(): echarts.EChartsOption {
+    return {
+      backgroundColor: 'transparent',
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          type: 'shadow'
+        }
+      },
+      legend: {
+        data: ['德国方案', '本系统'],
+        textStyle: {
+          color: '#ccc'
+        },
+        top: 0
+      },
+      grid: {
+        top: 40,
+        right: 30,
+        bottom: 40,
+        left: 50
+      },
+      xAxis: {
+        type: 'category',
+        data: ['单通道成本', '误报率', '响应延迟'],
+        axisLine: {
+          lineStyle: {
+            color: '#666'
+          }
+        },
+        axisLabel: {
+          color: '#999',
+          interval: 0
+        }
+      },
+      yAxis: [
+        {
+          type: 'value',
+          name: '成本 (元)',
+          min: 0,
+          max: 12000,
+          axisLine: {
+            lineStyle: {
+              color: '#666'
+            }
+          },
+          axisLabel: {
+            color: '#999',
+            formatter: '{value}'
+          },
+          splitLine: {
+            lineStyle: {
+              color: 'rgba(255, 255, 255, 0.05)'
+            }
+          }
+        },
+        {
+          type: 'value',
+          name: '比率/毫秒',
+          min: 0,
+          max: 6,
+          axisLine: {
+            lineStyle: {
+              color: '#666'
+            }
+          },
+          axisLabel: {
+            color: '#999',
+            formatter: (value: number) => {
+              if (value < 1) return value * 100 + '%';
+              return value + 'ms';
+            }
+          }
+        }
+      ],
+      series: [
+        {
+          name: '德国方案',
+          type: 'bar',
+          barWidth: 30,
+          itemStyle: {
+            color: '#ff6b6b'
+          },
+          data: [10284, 0.058, 1.2]
+        },
+        {
+          name: '本系统',
+          type: 'bar',
+          barWidth: 30,
+          itemStyle: {
+            color: '#00b6c1'
+          },
+          data: [1920, 0.008, 0.008]
+        }
+      ]
+    };
+  }
+  
+  private getToolChartOption(): echarts.EChartsOption {
+    return {
+      backgroundColor: 'transparent',
+      tooltip: {
+        trigger: 'axis'
+      },
+      legend: {
+        data: ['刀具磨损度'],
+        textStyle: {
+          color: '#ccc'
+        },
+        top: 0
+      },
+      grid: {
+        top: 30,
+        right: 30,
+        bottom: 40,
+        left: 50
+      },
+      xAxis: {
+        type: 'category',
+        data: [],
+        axisLine: {
+          lineStyle: {
+            color: '#666'
+          }
+        },
+        axisLabel: {
+          color: '#999'
+        }
+      },
+      yAxis: {
+        type: 'value',
+        name: '磨损度 (%)',
+        min: 0,
+        max: 100,
+        axisLine: {
+          lineStyle: {
+            color: '#666'
+          }
+        },
+        axisLabel: {
+          color: '#999'
+        },
+        splitLine: {
+          lineStyle: {
+            color: 'rgba(255, 255, 255, 0.05)'
+          }
+        }
+      },
+      series: [
+        {
+          name: '刀具磨损度',
+          type: 'line',
+          data: [],
+          smooth: true,
+          lineStyle: {
+            width: 3,
+            color: '#ff9800'
+          },
+          itemStyle: {
+            color: '#ff9800'
+          },
+          areaStyle: {
+            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+              { offset: 0, color: 'rgba(255, 152, 0, 0.5)' },
+              { offset: 1, color: 'rgba(255, 152, 0, 0.1)' }
+            ])
+          },
+          markLine: {
+            silent: true,
+            lineStyle: {
+              color: '#f44336',
+              width: 2,
+              type: 'dashed'
+            },
+            data: [
+              {
+                yAxis: 85,
+                label: {
+                  formatter: '更换阈值',
+                  position: 'start'
+                }
+              }
+            ]
+          }
+        }
+      ]
+    };
+  }
+  
+  private getEventChartOption(): echarts.EChartsOption {
+    return {
+      backgroundColor: 'transparent',
+      tooltip: {
+        trigger: 'axis'
+      },
+      legend: {
+        data: ['异常事件'],
+        textStyle: {
+          color: '#ccc'
+        },
+        top: 0
+      },
+      grid: {
+        top: 30,
+        right: 30,
+        bottom: 40,
+        left: 50
+      },
+      xAxis: {
+        type: 'category',
+        data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
+        axisLine: {
+          lineStyle: {
+            color: '#666'
+          }
+        },
+        axisLabel: {
+          color: '#999'
+        }
+      },
+      yAxis: {
+        type: 'value',
+        name: '事件次数',
+        axisLine: {
+          lineStyle: {
+            color: '#666'
+          }
+        },
+        axisLabel: {
+          color: '#999'
+        },
+        splitLine: {
+          lineStyle: {
+            color: 'rgba(255, 255, 255, 0.05)'
+          }
+        }
+      },
+      series: [
+        {
+          name: '异常事件',
+          type: 'bar',
+          barWidth: 30,
+          itemStyle: {
+            color: '#00b6c1'
+          },
+          data: [2, 0, 1, 3, 1, 0, 0]
+        }
+      ]
+    };
+  }
+}

Some files were not shown because too many files changed in this diff