76個工業組件庫示例匯總
實驗室測控系統開發組件
這是一個專為實驗室設備數據采集與分析設計的可視化測控系統組件。采用工業風格界面設計,提供了豐富的動態數據展示與分析功能,可應用于各類實驗室環境中的設備監控和數據處理。
功能特點
- 多設備管理與控制:支持多種測量設備的狀態監控和控制管理
- 實時數據可視化:提供動態更新的數據圖表,直觀展示各傳感器數據變化
- 數據分析工具:內置FFT分析、統計分析和趨勢分析等常用數據分析功能
- 報警系統:可配置的多參數報警機制,及時響應異常情況
- 靈活配置選項:提供采樣率、緩沖區大小等核心參數的自定義配置
- 通知系統:系統事件與報警的集中管理和展示
- 數據導出:支持將采集數據導出為標準格式,便于后續處理
- 工業風格界面:專業的深色工業設計風格,降低視覺疲勞
組件區域說明
組件包含以下主要功能區域:
- 設備狀態與控制區:顯示連接的實驗設備狀態,提供基本控制功能
- 數據可視化區:動態圖表顯示采集數據,支持多種顯示模式(實時、歷史、相關性)
- 數據分析與配置區:
- 數據分析:提供快速分析工具和結果顯示
- 測量設置:采樣參數和存儲選項配置
- 報警設置:溫度和壓力等參數的警報閾值設置
- 通知系統:匯總顯示系統報警和事件通知
- 狀態欄:顯示數據采集狀態信息(數據點數、采集時間、存儲空間)
可定制選項
組件提供多種定制選項,可以通過修改代碼來調整:
添加新的傳感器/設備
- 在HTML的
device-list
區域添加新的設備項 - 在JavaScript中的事件監聽器中為新設備添加控制邏輯
- 在數據結構和模擬函數中添加對應的數據處理邏輯
調整界面設計
- 修改CSS中的顏色變量(
:root
部分)可以改變整體配色 - 調整面板的flex屬性可以改變各區域的寬度比例
- 修改媒體查詢可以優化不同屏幕尺寸下的顯示效果
增強數據分析功能
- 在
performFFTAnalysis
、performStatisticalAnalysis
和performTrendAnalysis
函數中添加實際的數據處理算法 - 擴展分析結果的展示方式,如添加更多的可視化圖表
連接實際設備
組件目前使用模擬數據進行演示。要連接實際的實驗設備,需要:
- 根據設備API替換
simulateNewData
函數中的數據生成邏輯 - 調整數據采集頻率和處理方式,適應實際設備的特性
- 在設備控制函數中實現與實際設備的通信邏輯
圖表使用說明
組件默認嘗試使用Chart.js來繪制圖表。如需啟用完整的Chart.js功能:
- 或者修改
initializeCharts
函數,添加動態加載Chart.js的邏輯
如果Chart.js不可用,組件會自動使用內置的SVG簡易圖表進行數據展示。
瀏覽器兼容性
組件使用了現代Web技術(Flexbox、CSS變量、ES6+),建議在較新版本的瀏覽器中使用:
- Chrome 60+
- Firefox 55+
- Edge 16+
- Safari 11+
性能優化
對于大規模數據采集場景,建議:
- 調整更新頻率,減少DOM操作次數
- 優化數據存儲邏輯,避免內存占用過高
- 考慮使用Web Worker處理數據分析任務,提高響應性
項目結構
效果展示
源碼
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>實驗室測控系統</title><link rel="stylesheet" href="styles.css"></head>
<body><div id="lab-monitoring-system"><!-- 頂部標題欄 --><div class="lab-header"><div class="lab-title">實驗室測控系統</div><div class="lab-status"><span class="status-label">系統狀態:</span><span class="status-value" id="system-status">數據采集中</span><div class="status-indicator active"></div></div></div><!-- 主內容區域 --><div class="lab-content"><!-- 左側:設備狀態與控制 --><div class="panel device-panel"><div class="panel-header"><h3>設備狀態與控制</h3><div class="panel-tools"><button class="tool-btn" id="refresh-devices" title="刷新設備狀態"><i class="tool-icon">?</i></button></div></div><div class="panel-body"><div class="device-list" id="device-list"><div class="device-item"><div class="device-header"><span class="device-name">溫度傳感器陣列</span><span class="device-status online">在線</span></div><div class="device-controls"><button class="control-btn" id="temp-start">啟動</button><button class="control-btn" id="temp-stop">停止</button><button class="control-btn" id="temp-calibrate">校準</button></div><div class="device-parameters"><div class="parameter"><span class="param-label">采樣率:</span><span class="param-value">5Hz</span></div><div class="parameter"><span class="param-label">精度:</span><span class="param-value">±0.1°C</span></div><div class="parameter"><span class="param-label">狀態:</span><span class="param-value">采集中</span></div></div></div><div class="device-item"><div class="device-header"><span class="device-name">壓力傳感器</span><span class="device-status online">在線</span></div><div class="device-controls"><button class="control-btn" id="pressure-start">啟動</button><button class="control-btn" id="pressure-stop">停止</button><button class="control-btn" id="pressure-calibrate">校準</button></div><div class="device-parameters"><div class="parameter"><span class="param-label">采樣率:</span><span class="param-value">10Hz</span></div><div class="parameter"><span class="param-label">精度:</span><span class="param-value">±0.01MPa</span></div><div class="parameter"><span class="param-label">狀態:</span><span class="param-value">采集中</span></div></div></div><div class="device-item"><div class="device-header"><span class="device-name">光譜分析儀</span><span class="device-status offline">離線</span></div><div class="device-controls"><button class="control-btn" id="spectral-start" disabled>啟動</button><button class="control-btn" id="spectral-stop" disabled>停止</button><button class="control-btn" id="spectral-calibrate" disabled>校準</button></div><div class="device-parameters"><div class="parameter"><span class="param-label">波長范圍:</span><span class="param-value">380-780nm</span></div><div class="parameter"><span class="param-label">分辨率:</span><span class="param-value">1.5nm</span></div><div class="parameter"><span class="param-label">狀態:</span><span class="param-value">未連接</span></div></div></div></div></div></div><!-- 中間:數據可視化 --><div class="panel visualization-panel"><div class="panel-header"><h3>數據可視化</h3><div class="panel-tools"><select id="chart-type"><option value="realtime">實時圖表</option><option value="historical">歷史數據</option><option value="correlation">相關性分析</option></select><select id="timeframe"><option value="1m">1分鐘</option><option value="5m">5分鐘</option><option value="15m">15分鐘</option><option value="1h">1小時</option><option value="1d">1天</option></select><button class="tool-btn" id="export-data" title="導出數據"><i class="tool-icon">↓</i></button></div></div><div class="panel-body"><div class="chart-container"><div class="chart-header"><div class="chart-title" id="chart-title">溫度傳感器實時數據</div><div class="chart-legend"><div class="legend-item"><span class="legend-color" style="background-color: #2196F3;"></span><span class="legend-text">傳感器 1</span></div><div class="legend-item"><span class="legend-color" style="background-color: #FF5722;"></span><span class="legend-text">傳感器 2</span></div><div class="legend-item"><span class="legend-color" style="background-color: #4CAF50;"></span><span class="legend-text">傳感器 3</span></div></div></div><div class="chart-wrapper" id="chart-area"><!-- 圖表將通過JavaScript繪制 --></div><div class="chart-info"><div class="info-item"><span class="info-label">最大值:</span><span class="info-value" id="max-value">27.8°C</span></div><div class="info-item"><span class="info-label">最小值:</span><span class="info-value" id="min-value">21.2°C</span></div><div class="info-item"><span class="info-label">平均值:</span><span class="info-value" id="avg-value">24.5°C</span></div><div class="info-item"><span class="info-label">標準差:</span><span class="info-value" id="std-value">1.23</span></div></div></div></div></div><!-- 右側:數據分析與配置 --><div class="panel analysis-panel"><div class="panel-header"><h3>數據分析與配置</h3><div class="panel-tools"><button class="tool-btn" id="save-config" title="保存配置"><i class="tool-icon">?</i></button></div></div><div class="panel-body"><div class="tabs"><div class="tab-header"><div class="tab-btn active" data-tab="analysis">數據分析</div><div class="tab-btn" data-tab="settings">測量設置</div><div class="tab-btn" data-tab="alarms">報警設置</div></div><div class="tab-content"><!-- 數據分析內容 --><div class="tab-pane active" id="analysis-tab"><div class="analysis-tools"><div class="tool-group"><div class="tool-header">快速分析</div><div class="tool-buttons"><button class="analysis-btn" id="btn-fft">FFT分析</button><button class="analysis-btn" id="btn-statistics">統計分析</button><button class="analysis-btn" id="btn-trend">趨勢分析</button></div></div><div class="analysis-result" id="analysis-result"><div class="result-header">分析結果</div><div class="result-content"><p>選擇一個分析工具開始數據分析。分析結果將顯示在此處。</p></div></div></div></div><!-- 測量設置內容 --><div class="tab-pane" id="settings-tab"><div class="settings-form"><div class="setting-group"><div class="setting-header">采集設置</div><div class="setting-item"><label for="sample-rate">采樣率 (Hz)</label><input type="number" id="sample-rate" class="setting-input" value="10" min="1" max="1000"></div><div class="setting-item"><label for="buffer-size">緩沖區大小</label><input type="number" id="buffer-size" class="setting-input" value="1000" min="100" max="10000"></div><div class="setting-item"><label for="averaging">平均次數</label><input type="number" id="averaging" class="setting-input" value="5" min="1" max="50"></div></div><div class="setting-group"><div class="setting-header">存儲設置</div><div class="setting-item"><label for="auto-save">自動保存</label><div class="toggle-switch"><input type="checkbox" id="auto-save" checked><span class="toggle-slider"></span></div></div><div class="setting-item"><label for="save-interval">保存間隔 (分鐘)</label><input type="number" id="save-interval" class="setting-input" value="5" min="1" max="60"></div></div></div></div><!-- 報警設置內容 --><div class="tab-pane" id="alarms-tab"><div class="alarm-config"><div class="alarm-header">溫度報警設置</div><div class="alarm-item"><div class="alarm-param">高溫報警</div><div class="alarm-inputs"><input type="number" id="temp-high" class="alarm-input" value="30" step="0.1"><span class="alarm-unit">°C</span></div><div class="toggle-switch"><input type="checkbox" id="temp-high-enabled" checked><span class="toggle-slider"></span></div></div><div class="alarm-item"><div class="alarm-param">低溫報警</div><div class="alarm-inputs"><input type="number" id="temp-low" class="alarm-input" value="18" step="0.1"><span class="alarm-unit">°C</span></div><div class="toggle-switch"><input type="checkbox" id="temp-low-enabled" checked><span class="toggle-slider"></span></div></div><div class="alarm-header">壓力報警設置</div><div class="alarm-item"><div class="alarm-param">高壓報警</div><div class="alarm-inputs"><input type="number" id="pressure-high" class="alarm-input" value="2.5" step="0.01"><span class="alarm-unit">MPa</span></div><div class="toggle-switch"><input type="checkbox" id="pressure-high-enabled" checked><span class="toggle-slider"></span></div></div><div class="alarm-item"><div class="alarm-param">低壓報警</div><div class="alarm-inputs"><input type="number" id="pressure-low" class="alarm-input" value="0.5" step="0.01"><span class="alarm-unit">MPa</span></div><div class="toggle-switch"><input type="checkbox" id="pressure-low-enabled"><span class="toggle-slider"></span></div></div></div></div></div></div></div></div></div><!-- 底部狀態欄 --><div class="lab-footer"><div class="notifications"><div class="notification-btn" id="notification-btn"><i class="notification-icon">!</i><span class="notification-count" id="notification-count">2</span></div><div class="notification-dropdown" id="notification-dropdown"><div class="notification-header"><span>系統通知</span><button class="clear-btn" id="clear-notifications">清除所有</button></div><div class="notification-list" id="notification-list"><div class="notification-item warning"><div class="notification-time">08:42:15</div><div class="notification-content">溫度傳感器1數值超出警戒范圍 (31.2°C)</div></div><div class="notification-item info"><div class="notification-time">08:15:20</div><div class="notification-content">自動保存已完成,數據已存儲至 lab_data_20250409.csv</div></div></div></div></div><div class="system-info"><div class="info-segment"><span class="info-label">數據點數:</span><span class="info-value" id="data-points">12,458</span></div><div class="info-segment"><span class="info-label">采集時間:</span><span class="info-value" id="acquisition-time">02:15:37</span></div><div class="info-segment"><span class="info-label">存儲空間:</span><span class="info-value" id="storage-usage">45.8MB / 1GB</span></div></div></div></div> <script src="script.js"></script>
</body>
</html>
styles.css
/* 實驗室測控系統 - 工業風格CSS */
:root {--primary-dark: #1a2639;--primary-medium: #263852;--primary-light: #3d5174;--accent-blue: #0288d1;--accent-blue-light: #5eb8ff;--accent-blue-dark: #005b9f;--accent-green: #4caf50;--accent-amber: #ffb300;--accent-red: #e53935;--text-primary: #ffffff;--text-secondary: #c5c8d9;--text-muted: #7a8499;--border-color: #364761;--shadow-color: rgba(0, 0, 0, 0.3);--chart-grid: #2c3e50;
}/* 基本布局和容器樣式 */
#lab-monitoring-system {font-family: 'Roboto', 'Segoe UI', Arial, sans-serif;background-color: var(--primary-dark);color: var(--text-primary);display: flex;flex-direction: column;height: 100%;width: 100%;min-height: 750px;border-radius: 5px;box-shadow: 0 4px 16px var(--shadow-color);overflow: hidden;border: 1px solid var(--border-color);
}/* 頂部標題欄 */
.lab-header {background-color: var(--primary-medium);padding: 12px 16px;display: flex;justify-content: space-between;align-items: center;border-bottom: 2px solid var(--border-color);
}.lab-title {font-size: 18px;font-weight: 600;letter-spacing: 0.5px;color: var(--text-primary);text-transform: uppercase;
}.lab-status {display: flex;align-items: center;gap: 8px;
}.status-label {color: var(--text-secondary);font-size: 14px;
}.status-value {font-weight: 500;color: var(--text-primary);
}.status-indicator {width: 12px;height: 12px;border-radius: 50%;background-color: var(--accent-blue);position: relative;
}.status-indicator.active::after {content: '';position: absolute;top: 0;left: 0;width: 100%;height: 100%;border-radius: 50%;background-color: var(--accent-blue);animation: pulse 1.5s infinite;
}@keyframes pulse {0% { transform: scale(1); opacity: 1; }70% { transform: scale(1.5); opacity: 0; }100% { transform: scale(1.5); opacity: 0; }
}/* 主內容區域 */
.lab-content {display: flex;flex: 1;overflow: hidden;padding: 10px;gap: 10px;
}/* 面板通用樣式 */
.panel {background-color: var(--primary-medium);border-radius: 5px;border: 1px solid var(--border-color);box-shadow: 0 2px 8px var(--shadow-color);display: flex;flex-direction: column;overflow: hidden;
}.panel-header {padding: 10px 14px;background-color: var(--primary-light);border-bottom: 1px solid var(--border-color);display: flex;justify-content: space-between;align-items: center;
}.panel-header h3 {margin: 0;font-size: 15px;font-weight: 500;color: var(--text-primary);
}.panel-tools {display: flex;gap: 8px;align-items: center;
}.panel-body {flex: 1;padding: 12px;overflow: auto;
}/* 設備面板樣式 */
.device-panel {flex: 0 0 280px;
}.device-list {display: flex;flex-direction: column;gap: 12px;
}.device-item {background-color: var(--primary-dark);border-radius: 4px;border: 1px solid var(--border-color);overflow: hidden;
}.device-header {padding: 10px 12px;background-color: var(--primary-light);display: flex;justify-content: space-between;align-items: center;border-bottom: 1px solid var(--border-color);
}.device-name {font-weight: 500;font-size: 14px;
}.device-status {font-size: 12px;padding: 2px 8px;border-radius: 10px;font-weight: 500;
}.device-status.online {background-color: var(--accent-green);color: #ffffff;
}.device-status.offline {background-color: var(--text-muted);color: var(--primary-dark);
}.device-controls {display: flex;padding: 8px 12px;gap: 6px;border-bottom: 1px solid var(--border-color);
}.control-btn {flex: 1;background-color: var(--primary-light);color: var(--text-primary);border: 1px solid var(--border-color);border-radius: 3px;padding: 5px 8px;font-size: 12px;cursor: pointer;transition: all 0.2s;
}.control-btn:hover {background-color: var(--accent-blue);
}.control-btn:disabled {background-color: var(--primary-medium);color: var(--text-muted);cursor: not-allowed;
}.device-parameters {padding: 8px 12px;display: flex;flex-direction: column;gap: 6px;
}.parameter {display: flex;justify-content: space-between;font-size: 12px;
}.param-label {color: var(--text-secondary);
}.param-value {font-weight: 500;
}/* 可視化面板樣式 */
.visualization-panel {flex: 1;min-width: 400px;
}.chart-container {background-color: var(--primary-dark);border-radius: 4px;border: 1px solid var(--border-color);overflow: hidden;display: flex;flex-direction: column;height: 100%;
}.chart-header {padding: 10px 12px;display: flex;justify-content: space-between;align-items: center;background-color: var(--primary-light);border-bottom: 1px solid var(--border-color);
}.chart-title {font-size: 14px;font-weight: 500;
}.chart-legend {display: flex;gap: 12px;
}.legend-item {display: flex;align-items: center;gap: 5px;font-size: 12px;
}.legend-color {width: 12px;height: 12px;border-radius: 2px;
}.chart-wrapper {flex: 1;padding: 10px;min-height: 350px;position: relative;background: linear-gradient(to bottom,transparent 9px,var(--chart-grid) 10px),linear-gradient(to right,transparent 9px,var(--chart-grid) 10px);background-size: 10% 10%;background-color: var(--primary-dark);
}.chart-info {display: flex;justify-content: space-between;padding: 8px 12px;background-color: var(--primary-medium);border-top: 1px solid var(--border-color);
}.info-item {display: flex;gap: 5px;font-size: 12px;
}.info-label {color: var(--text-secondary);
}.info-value {font-weight: 500;color: var(--text-primary);
}/* 分析面板樣式 */
.analysis-panel {flex: 0 0 280px;
}/* 標簽頁樣式 */
.tabs {display: flex;flex-direction: column;height: 100%;
}.tab-header {display: flex;border-bottom: 1px solid var(--border-color);
}.tab-btn {padding: 8px 12px;font-size: 13px;cursor: pointer;border-bottom: 2px solid transparent;transition: all 0.2s;color: var(--text-secondary);
}.tab-btn:hover {color: var(--text-primary);
}.tab-btn.active {color: var(--accent-blue);border-bottom-color: var(--accent-blue);
}.tab-content {flex: 1;overflow: auto;
}.tab-pane {display: none;padding: 10px 0;
}.tab-pane.active {display: block;
}/* 數據分析標簽頁 */
.analysis-tools {display: flex;flex-direction: column;gap: 12px;
}.tool-group {background-color: var(--primary-dark);border-radius: 4px;border: 1px solid var(--border-color);padding: 8px;
}.tool-header {font-size: 13px;font-weight: 500;margin-bottom: 8px;color: var(--text-secondary);
}.tool-buttons {display: flex;gap: 6px;
}.analysis-btn {flex: 1;background-color: var(--primary-light);color: var(--text-primary);border: 1px solid var(--border-color);border-radius: 3px;padding: 6px 8px;font-size: 12px;cursor: pointer;transition: all 0.2s;
}.analysis-btn:hover {background-color: var(--accent-blue);
}.analysis-result {background-color: var(--primary-dark);border-radius: 4px;border: 1px solid var(--border-color);overflow: hidden;
}.result-header {padding: 8px 10px;font-size: 13px;background-color: var(--primary-light);border-bottom: 1px solid var(--border-color);font-weight: 500;
}.result-content {padding: 8px 10px;font-size: 12px;color: var(--text-secondary);min-height: 80px;
}/* 測量設置標簽頁 */
.settings-form {display: flex;flex-direction: column;gap: 16px;
}.setting-group {background-color: var(--primary-dark);border-radius: 4px;border: 1px solid var(--border-color);padding: 10px;
}.setting-header {font-size: 13px;font-weight: 500;margin-bottom: 10px;color: var(--text-secondary);
}.setting-item {display: flex;justify-content: space-between;align-items: center;margin-bottom: 8px;font-size: 12px;
}.setting-item:last-child {margin-bottom: 0;
}.setting-item label {color: var(--text-primary);
}.setting-input {width: 80px;background-color: var(--primary-medium);color: var(--text-primary);border: 1px solid var(--border-color);border-radius: 3px;padding: 4px 6px;font-size: 12px;
}/* 開關按鈕 */
.toggle-switch {position: relative;display: inline-block;width: 36px;height: 18px;
}.toggle-switch input {opacity: 0;width: 0;height: 0;
}.toggle-slider {position: absolute;cursor: pointer;top: 0;left: 0;right: 0;bottom: 0;background-color: var(--primary-light);transition: .4s;border-radius: 18px;border: 1px solid var(--border-color);
}.toggle-slider:before {position: absolute;content: "";height: 12px;width: 12px;left: 2px;bottom: 2px;background-color: var(--text-secondary);transition: .4s;border-radius: 50%;
}input:checked + .toggle-slider {background-color: var(--accent-blue);
}input:checked + .toggle-slider:before {transform: translateX(18px);background-color: white;
}/* 報警設置標簽頁 */
.alarm-config {display: flex;flex-direction: column;gap: 6px;
}.alarm-header {font-size: 13px;font-weight: 500;color: var(--text-secondary);padding: 5px 0;margin-top: 5px;
}.alarm-item {display: flex;align-items: center;background-color: var(--primary-dark);border-radius: 4px;border: 1px solid var(--border-color);padding: 8px 10px;
}.alarm-param {flex: 0 0 80px;font-size: 12px;
}.alarm-inputs {flex: 1;display: flex;align-items: center;gap: 4px;
}.alarm-input {width: 60px;background-color: var(--primary-medium);color: var(--text-primary);border: 1px solid var(--border-color);border-radius: 3px;padding: 4px 6px;font-size: 12px;
}.alarm-unit {font-size: 12px;color: var(--text-secondary);
}/* 工具按鈕 */
.tool-btn {width: 26px;height: 26px;background-color: var(--primary-dark);color: var(--text-secondary);border: 1px solid var(--border-color);border-radius: 3px;display: flex;align-items: center;justify-content: center;cursor: pointer;transition: all 0.2s;
}.tool-btn:hover {background-color: var(--accent-blue);color: var(--text-primary);
}.tool-icon {font-style: normal;font-size: 14px;
}/* 下拉選擇框 */
select {background-color: var(--primary-dark);color: var(--text-primary);border: 1px solid var(--border-color);border-radius: 3px;padding: 4px 8px;font-size: 12px;min-width: 100px;cursor: pointer;appearance: none;background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="6" viewBox="0 0 12 6"><path fill="%237a8499" d="M0 0l6 6 6-6z"/></svg>');background-repeat: no-repeat;background-position: right 8px center;padding-right: 24px;
}/* 底部狀態欄 */
.lab-footer {background-color: var(--primary-medium);padding: 10px 16px;border-top: 2px solid var(--border-color);display: flex;justify-content: space-between;align-items: center;
}.system-info {display: flex;gap: 16px;
}.info-segment {display: flex;align-items: center;gap: 5px;font-size: 12px;
}/* 通知系統 */
.notifications {position: relative;
}.notification-btn {position: relative;width: 30px;height: 30px;background-color: var(--primary-light);border-radius: 50%;display: flex;align-items: center;justify-content: center;cursor: pointer;
}.notification-icon {font-style: normal;color: var(--text-primary);font-weight: bold;
}.notification-count {position: absolute;top: -3px;right: -3px;min-width: 16px;height: 16px;background-color: var(--accent-red);color: white;border-radius: 8px;font-size: 10px;display: flex;align-items: center;justify-content: center;padding: 0 4px;
}.notification-dropdown {position: absolute;bottom: 40px;left: 0;width: 300px;background-color: var(--primary-medium);border-radius: 5px;border: 1px solid var(--border-color);box-shadow: 0 4px 12px var(--shadow-color);overflow: hidden;z-index: 100;display: none;
}.notification-dropdown.show {display: block;
}.notification-header {padding: 10px;background-color: var(--primary-light);border-bottom: 1px solid var(--border-color);display: flex;justify-content: space-between;align-items: center;
}.notification-header span {font-size: 13px;font-weight: 500;
}.clear-btn {background-color: transparent;color: var(--accent-blue);border: none;font-size: 12px;cursor: pointer;padding: 0;
}.notification-list {max-height: 250px;overflow-y: auto;
}.notification-item {padding: 8px 10px;border-bottom: 1px solid var(--border-color);font-size: 12px;
}.notification-item:last-child {border-bottom: none;
}.notification-time {color: var(--text-muted);margin-bottom: 3px;
}.notification-item.warning .notification-content {color: var(--accent-amber);
}.notification-item.error .notification-content {color: var(--accent-red);
}.notification-item.info .notification-content {color: var(--accent-blue-light);
}/* 響應式調整 */
@media (max-width: 1200px) {.lab-content {flex-direction: column;}.device-panel, .analysis-panel {flex: 0 0 auto;width: 100%;}.visualization-panel {min-height: 400px;}
}
script.js
// 實驗室測控系統JavaScript
(function() {// 圖表實例和數據let temperatureChart;let pressureChart;const temperatureData = {timestamps: [],sensor1: [],sensor2: [],sensor3: []};const pressureData = {timestamps: [],values: []};// 初始化函數function initLabMonitoringSystem() {console.log("實驗室測控系統初始化...");setupEventListeners();initializeCharts();startDataSimulation();setupTabNavigation();}// 設置事件監聽function setupEventListeners() {// 設備控制按鈕document.getElementById('temp-start').addEventListener('click', () => controlDevice('溫度傳感器陣列', 'start'));document.getElementById('temp-stop').addEventListener('click', () => controlDevice('溫度傳感器陣列', 'stop'));document.getElementById('temp-calibrate').addEventListener('click', () => calibrateDevice('溫度傳感器陣列'));document.getElementById('pressure-start').addEventListener('click', () => controlDevice('壓力傳感器', 'start'));document.getElementById('pressure-stop').addEventListener('click', () => controlDevice('壓力傳感器', 'stop'));document.getElementById('pressure-calibrate').addEventListener('click', () => calibrateDevice('壓力傳感器'));// 刷新設備狀態document.getElementById('refresh-devices').addEventListener('click', refreshDeviceStatus);// 圖表類型和時間范圍選擇document.getElementById('chart-type').addEventListener('change', updateChartType);document.getElementById('timeframe').addEventListener('change', updateTimeframe);// 導出數據按鈕document.getElementById('export-data').addEventListener('click', exportData);// 分析按鈕document.getElementById('btn-fft').addEventListener('click', performFFTAnalysis);document.getElementById('btn-statistics').addEventListener('click', performStatisticalAnalysis);document.getElementById('btn-trend').addEventListener('click', performTrendAnalysis);// 保存配置document.getElementById('save-config').addEventListener('click', saveConfiguration);// 通知系統document.getElementById('notification-btn').addEventListener('click', toggleNotifications);document.getElementById('clear-notifications').addEventListener('click', clearNotifications);}// 初始化圖表function initializeCharts() {// 創建溫度圖表的Canvas元素const tempChartCanvas = document.createElement('canvas');tempChartCanvas.id = 'temperature-chart';document.getElementById('chart-area').appendChild(tempChartCanvas);// 如果Chart.js可用,則創建圖表if (typeof Chart !== 'undefined') {setupChartJS();} else {// 如果Chart.js不可用,創建一個簡單的模擬圖表createSimulatedChart();}}// 使用Chart.js創建圖表function setupChartJS() {const ctx = document.getElementById('temperature-chart').getContext('2d');temperatureChart = new Chart(ctx, {type: 'line',data: {labels: [],datasets: [{label: '傳感器 1',data: [],borderColor: '#2196F3',backgroundColor: 'rgba(33, 150, 243, 0.1)',borderWidth: 2,pointRadius: 0,tension: 0.3},{label: '傳感器 2',data: [],borderColor: '#FF5722',backgroundColor: 'rgba(255, 87, 34, 0.1)',borderWidth: 2,pointRadius: 0,tension: 0.3},{label: '傳感器 3',data: [],borderColor: '#4CAF50',backgroundColor: 'rgba(76, 175, 80, 0.1)',borderWidth: 2,pointRadius: 0,tension: 0.3}]},options: {responsive: true,maintainAspectRatio: false,animation: {duration: 0},scales: {x: {grid: {color: 'rgba(200, 200, 200, 0.1)'},ticks: {color: '#a7b6c2'}},y: {grid: {color: 'rgba(200, 200, 200, 0.1)'},ticks: {color: '#a7b6c2'}}},plugins: {legend: {display: false}}}});}// 創建模擬圖表(當Chart.js不可用時)function createSimulatedChart() {const chartArea = document.getElementById('chart-area');chartArea.innerHTML = `<div class="simulated-chart"><svg width="100%" height="300" viewBox="0 0 600 300" preserveAspectRatio="none"><!-- 圖表網格 --><g class="grid"><!-- 水平線 --><line x1="0" y1="0" x2="600" y2="0" stroke="#2c3e50" stroke-width="1" /><line x1="0" y1="60" x2="600" y2="60" stroke="#2c3e50" stroke-width="1" /><line x1="0" y1="120" x2="600" y2="120" stroke="#2c3e50" stroke-width="1" /><line x1="0" y1="180" x2="600" y2="180" stroke="#2c3e50" stroke-width="1" /><line x1="0" y1="240" x2="600" y2="240" stroke="#2c3e50" stroke-width="1" /><line x1="0" y1="299" x2="600" y2="299" stroke="#2c3e50" stroke-width="1" /><!-- 垂直線 --><line x1="0" y1="0" x2="0" y2="300" stroke="#2c3e50" stroke-width="1" /><line x1="120" y1="0" x2="120" y2="300" stroke="#2c3e50" stroke-width="1" /><line x1="240" y1="0" x2="240" y2="300" stroke="#2c3e50" stroke-width="1" /><line x1="360" y1="0" x2="360" y2="300" stroke="#2c3e50" stroke-width="1" /><line x1="480" y1="0" x2="480" y2="300" stroke="#2c3e50" stroke-width="1" /><line x1="599" y1="0" x2="599" y2="300" stroke="#2c3e50" stroke-width="1" /></g><!-- 數據線 - 傳感器1 --><path id="sensor1-path" stroke="#2196F3" stroke-width="2" fill="none" /><!-- 數據線 - 傳感器2 --><path id="sensor2-path" stroke="#FF5722" stroke-width="2" fill="none" /><!-- 數據線 - 傳感器3 --><path id="sensor3-path" stroke="#4CAF50" stroke-width="2" fill="none" /></svg></div>`;// 初始化模擬數據路徑updateSimulatedChart();}// 更新模擬圖表function updateSimulatedChart() {const generateRandomPath = () => {let path = `M0,150`;for (let i = 1; i <= 60; i++) {const x = i * 10;const y = 150 + (Math.random() * 100 - 50);path += ` L${x},${y}`;}return path;};const sensor1Path = document.getElementById('sensor1-path');const sensor2Path = document.getElementById('sensor2-path');const sensor3Path = document.getElementById('sensor3-path');if (sensor1Path) sensor1Path.setAttribute('d', generateRandomPath());if (sensor2Path) sensor2Path.setAttribute('d', generateRandomPath());if (sensor3Path) sensor3Path.setAttribute('d', generateRandomPath());}// 開始數據模擬function startDataSimulation() {// 初始化一些歷史數據點const now = new Date();for (let i = 60; i >= 0; i--) {const time = new Date(now - i * 1000);const timeStr = time.toLocaleTimeString();temperatureData.timestamps.push(timeStr);temperatureData.sensor1.push(22 + Math.random() * 6);temperatureData.sensor2.push(23 + Math.random() * 5);temperatureData.sensor3.push(21 + Math.random() * 7);pressureData.timestamps.push(timeStr);pressureData.values.push(1.8 + Math.random() * 0.5);}updateChart();// 每秒更新一次數據setInterval(() => {simulateNewData();updateChart();}, 1000);// 每30秒隨機添加一個通知setInterval(() => {if (Math.random() < 0.3) {addRandomNotification();}}, 30000);// 更新采集時間setInterval(updateAcquisitionTime, 1000);}// 模擬新數據function simulateNewData() {const now = new Date();const timeStr = now.toLocaleTimeString();// 保留最近60個數據點if (temperatureData.timestamps.length > 60) {temperatureData.timestamps.shift();temperatureData.sensor1.shift();temperatureData.sensor2.shift();temperatureData.sensor3.shift();pressureData.timestamps.shift();pressureData.values.shift();}// 添加新的溫度數據點 (模擬一些波動)const lastTemp1 = temperatureData.sensor1[temperatureData.sensor1.length - 1];const lastTemp2 = temperatureData.sensor2[temperatureData.sensor2.length - 1];const lastTemp3 = temperatureData.sensor3[temperatureData.sensor3.length - 1];const newTemp1 = lastTemp1 + (Math.random() * 0.6 - 0.3);const newTemp2 = lastTemp2 + (Math.random() * 0.6 - 0.3);const newTemp3 = lastTemp3 + (Math.random() * 0.6 - 0.3);temperatureData.timestamps.push(timeStr);temperatureData.sensor1.push(newTemp1);temperatureData.sensor2.push(newTemp2);temperatureData.sensor3.push(newTemp3);// 添加新的壓力數據點const lastPressure = pressureData.values[pressureData.values.length - 1];const newPressure = lastPressure + (Math.random() * 0.1 - 0.05);pressureData.timestamps.push(timeStr);pressureData.values.push(newPressure);// 更新統計信息updateStatistics();// 檢查警報checkAlarms(newTemp1, newTemp2, newTemp3, newPressure);// 更新數據點計數updateDataPointCount();}// 更新圖表function updateChart() {const chartType = document.getElementById('chart-type').value;if (typeof Chart !== 'undefined' && temperatureChart) {// 使用Chart.js更新圖表if (chartType === 'realtime') {temperatureChart.data.labels = temperatureData.timestamps;temperatureChart.data.datasets[0].data = temperatureData.sensor1;temperatureChart.data.datasets[1].data = temperatureData.sensor2;temperatureChart.data.datasets[2].data = temperatureData.sensor3;} else if (chartType === 'historical') {// 切換到歷史數據視圖// 這里為了演示,僅展示了同一數據的不同時間范圍temperatureChart.data.labels = temperatureData.timestamps;temperatureChart.data.datasets[0].data = temperatureData.sensor1;temperatureChart.data.datasets[1].data = temperatureData.sensor2;temperatureChart.data.datasets[2].data = temperatureData.sensor3;} else if (chartType === 'correlation') {// 在實際應用中,這里會顯示相關性分析的視圖temperatureChart.data.labels = temperatureData.timestamps;temperatureChart.data.datasets[0].data = temperatureData.sensor1;temperatureChart.data.datasets[1].data = temperatureData.sensor2;temperatureChart.data.datasets[2].data = temperatureData.sensor3;}temperatureChart.update();} else {// 更新模擬圖表updateSimulatedChart();}}// 更新圖表類型function updateChartType() {const chartType = document.getElementById('chart-type').value;const chartTitle = document.getElementById('chart-title');switch (chartType) {case 'realtime':chartTitle.textContent = '溫度傳感器實時數據';break;case 'historical':chartTitle.textContent = '溫度傳感器歷史數據';break;case 'correlation':chartTitle.textContent = '溫度與壓力相關性分析';break;}updateChart();}// 更新時間范圍function updateTimeframe() {updateChart();}// 更新統計信息function updateStatistics() {// 計算所有傳感器的統計數據const allValues = [...temperatureData.sensor1, ...temperatureData.sensor2, ...temperatureData.sensor3];const max = Math.max(...allValues).toFixed(1);const min = Math.min(...allValues).toFixed(1);// 計算平均值const sum = allValues.reduce((a, b) => a + b, 0);const avg = (sum / allValues.length).toFixed(1);// 計算標準差const variance = allValues.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / allValues.length;const std = Math.sqrt(variance).toFixed(2);// 更新DOMdocument.getElementById('max-value').textContent = max + '°C';document.getElementById('min-value').textContent = min + '°C';document.getElementById('avg-value').textContent = avg + '°C';document.getElementById('std-value').textContent = std;}// 檢查警報條件function checkAlarms(temp1, temp2, temp3, pressure) {// 獲取警報閾值const tempHighThreshold = parseFloat(document.getElementById('temp-high').value);const tempLowThreshold = parseFloat(document.getElementById('temp-low').value);const pressureHighThreshold = parseFloat(document.getElementById('pressure-high').value);const pressureLowThreshold = parseFloat(document.getElementById('pressure-low').value);// 檢查溫度高警報if (document.getElementById('temp-high-enabled').checked) {if (temp1 > tempHighThreshold) {addNotification(`溫度傳感器1數值超出警戒范圍 (${temp1.toFixed(1)}°C)`, 'warning');} else if (temp2 > tempHighThreshold) {addNotification(`溫度傳感器2數值超出警戒范圍 (${temp2.toFixed(1)}°C)`, 'warning');} else if (temp3 > tempHighThreshold) {addNotification(`溫度傳感器3數值超出警戒范圍 (${temp3.toFixed(1)}°C)`, 'warning');}}// 檢查溫度低警報if (document.getElementById('temp-low-enabled').checked) {if (temp1 < tempLowThreshold) {addNotification(`溫度傳感器1數值低于警戒范圍 (${temp1.toFixed(1)}°C)`, 'warning');} else if (temp2 < tempLowThreshold) {addNotification(`溫度傳感器2數值低于警戒范圍 (${temp2.toFixed(1)}°C)`, 'warning');} else if (temp3 < tempLowThreshold) {addNotification(`溫度傳感器3數值低于警戒范圍 (${temp3.toFixed(1)}°C)`, 'warning');}}// 檢查壓力高警報if (document.getElementById('pressure-high-enabled').checked) {if (pressure > pressureHighThreshold) {addNotification(`壓力傳感器數值超出警戒范圍 (${pressure.toFixed(2)} MPa)`, 'warning');}}// 檢查壓力低警報if (document.getElementById('pressure-low-enabled').checked) {if (pressure < pressureLowThreshold) {addNotification(`壓力傳感器數值低于警戒范圍 (${pressure.toFixed(2)} MPa)`, 'warning');}}}// 設備控制function controlDevice(deviceName, action) {if (action === 'start') {addNotification(`${deviceName}已啟動`, 'info');} else if (action === 'stop') {addNotification(`${deviceName}已停止`, 'info');}}// 校準設備function calibrateDevice(deviceName) {addNotification(`${deviceName}校準中...`, 'info');// 模擬校準過程setTimeout(() => {addNotification(`${deviceName}校準完成`, 'info');}, 3000);}// 刷新設備狀態function refreshDeviceStatus() {const refreshBtn = document.getElementById('refresh-devices');refreshBtn.classList.add('rotating');// 模擬刷新延遲setTimeout(() => {refreshBtn.classList.remove('rotating');addNotification('設備狀態已更新', 'info');}, 1000);}// 添加CSS動畫function addCSSAnimation() {const style = document.createElement('style');style.textContent = `@keyframes rotate {from { transform: rotate(0deg); }to { transform: rotate(360deg); }}.rotating {animation: rotate 1s linear;}`;document.head.appendChild(style);}// 導出數據function exportData() {const today = new Date().toISOString().slice(0, 10);addNotification(`數據已導出至 lab_data_${today}.csv`, 'info');}// 執行FFT分析function performFFTAnalysis() {const resultContent = document.querySelector('.result-content');resultContent.innerHTML = '<p>正在執行FFT分析...</p>';// 模擬分析過程setTimeout(() => {resultContent.innerHTML = `<p>FFT分析結果:</p><ul><li>主頻率分量: 0.05 Hz</li><li>諧波成分: 0.1 Hz, 0.15 Hz</li><li>信噪比: 24.6 dB</li></ul><p>此溫度數據顯示周期性變化,可能與環境控制系統相關。</p>`;}, 1500);}// 執行統計分析function performStatisticalAnalysis() {const resultContent = document.querySelector('.result-content');resultContent.innerHTML = '<p>正在執行統計分析...</p>';// 模擬分析過程setTimeout(() => {// 獲取當前統計數據const max = document.getElementById('max-value').textContent;const min = document.getElementById('min-value').textContent;const avg = document.getElementById('avg-value').textContent;const std = document.getElementById('std-value').textContent;resultContent.innerHTML = `<p>統計分析結果:</p><ul><li>最大值: ${max}</li><li>最小值: ${min}</li><li>平均值: ${avg}</li><li>標準差: ${std}</li><li>峰度: 2.86</li><li>偏度: 0.12</li></ul><p>數據分布接近正態分布,溫度變化穩定。</p>`;}, 1500);}// 執行趨勢分析function performTrendAnalysis() {const resultContent = document.querySelector('.result-content');resultContent.innerHTML = '<p>正在執行趨勢分析...</p>';// 模擬分析過程setTimeout(() => {resultContent.innerHTML = `<p>趨勢分析結果:</p><ul><li>線性趨勢: 微增 (0.02°C/分鐘)</li><li>預測30分鐘后溫度: ${(parseFloat(document.getElementById('avg-value').textContent) + 0.6).toFixed(1)}°C</li><li>周期性: 存在10分鐘周期</li><li>異常點數量: 0</li></ul><p>溫度呈現穩定微增趨勢,建議監控制冷系統狀態。</p>`;}, 1500);}// 保存配置function saveConfiguration() {addNotification('系統配置已保存', 'info');}// 切換通知下拉菜單function toggleNotifications() {const dropdown = document.getElementById('notification-dropdown');dropdown.classList.toggle('show');}// 添加通知function addNotification(message, type = 'info') {const now = new Date();const timeStr = now.toLocaleTimeString();const notificationList = document.getElementById('notification-list');const notificationItem = document.createElement('div');notificationItem.className = `notification-item ${type}`;notificationItem.innerHTML = `<div class="notification-time">${timeStr}</div><div class="notification-content">${message}</div>`;notificationList.insertBefore(notificationItem, notificationList.firstChild);// 更新通知計數updateNotificationCount();}// 添加隨機通知function addRandomNotification() {const notifications = [{ message: '系統自檢完成,所有模塊工作正常', type: 'info' },{ message: '溫度數據波動性增加,建議檢查環境穩定性', type: 'warning' },{ message: '數據備份已完成', type: 'info' },{ message: '壓力傳感器需要定期維護', type: 'warning' },{ message: '網絡連接穩定性檢查已完成', type: 'info' }];const randomNotification = notifications[Math.floor(Math.random() * notifications.length)];addNotification(randomNotification.message, randomNotification.type);}// 清除所有通知function clearNotifications() {document.getElementById('notification-list').innerHTML = '';updateNotificationCount();addNotification('所有通知已清除', 'info');}// 更新通知計數function updateNotificationCount() {const count = document.getElementById('notification-list').children.length;document.getElementById('notification-count').textContent = count;}// 設置標簽頁導航function setupTabNavigation() {const tabButtons = document.querySelectorAll('.tab-btn');const tabPanes = document.querySelectorAll('.tab-pane');tabButtons.forEach(button => {button.addEventListener('click', () => {const targetTab = button.dataset.tab;// 更新標簽按鈕狀態tabButtons.forEach(btn => btn.classList.remove('active'));button.classList.add('active');// 更新內容面板tabPanes.forEach(pane => pane.classList.remove('active'));document.getElementById(`${targetTab}-tab`).classList.add('active');});});}// 更新數據點計數function updateDataPointCount() {const totalPoints = temperatureData.sensor1.length * 3 + pressureData.values.length;document.getElementById('data-points').textContent = totalPoints.toLocaleString();}// 更新采集時間function updateAcquisitionTime() {const element = document.getElementById('acquisition-time');const currentText = element.textContent;// 將當前時間文本解析為小時、分鐘和秒const [hours, minutes, seconds] = currentText.split(':').map(Number);// 計算總秒數并增加1秒let totalSeconds = hours * 3600 + minutes * 60 + seconds + 1;// 將總秒數轉換回時分秒格式const newHours = Math.floor(totalSeconds / 3600);totalSeconds %= 3600;const newMinutes = Math.floor(totalSeconds / 60);const newSeconds = totalSeconds % 60;// 更新顯示element.textContent = `${String(newHours).padStart(2, '0')}:${String(newMinutes).padStart(2, '0')}:${String(newSeconds).padStart(2, '0')}`;}// 添加CSS動畫addCSSAnimation();// 當DOM加載完成后初始化if (document.readyState === 'loading') {document.addEventListener('DOMContentLoaded', initLabMonitoringSystem);} else {initLabMonitoringSystem();}
})();