diff --git a/Release/database/XNSim.db b/Release/database/XNSim.db index fb6ee21..17cf1d6 100644 Binary files a/Release/database/XNSim.db and b/Release/database/XNSim.db differ diff --git a/Release/lib/libQt6Core.so.6 b/Release/lib/libQt6Core.so.6 deleted file mode 120000 index 2f814d5..0000000 --- a/Release/lib/libQt6Core.so.6 +++ /dev/null @@ -1 +0,0 @@ -libQt6Core.so.6.9.0 \ No newline at end of file diff --git a/Release/lib/libQt6Core.so.6.9.0 b/Release/lib/libQt6Core.so.6.9.0 deleted file mode 100755 index e211213..0000000 Binary files a/Release/lib/libQt6Core.so.6.9.0 and /dev/null differ diff --git a/Release/lib/libQt6DBus.so.6 b/Release/lib/libQt6DBus.so.6 deleted file mode 120000 index 1e829e7..0000000 --- a/Release/lib/libQt6DBus.so.6 +++ /dev/null @@ -1 +0,0 @@ -libQt6DBus.so.6.9.0 \ No newline at end of file diff --git a/Release/lib/libQt6DBus.so.6.9.0 b/Release/lib/libQt6DBus.so.6.9.0 deleted file mode 100755 index 90d57ce..0000000 Binary files a/Release/lib/libQt6DBus.so.6.9.0 and /dev/null differ diff --git a/Release/lib/libQt6Gui.so.6 b/Release/lib/libQt6Gui.so.6 deleted file mode 120000 index 385b0e7..0000000 --- a/Release/lib/libQt6Gui.so.6 +++ /dev/null @@ -1 +0,0 @@ -libQt6Gui.so.6.9.0 \ No newline at end of file diff --git a/Release/lib/libQt6Gui.so.6.9.0 b/Release/lib/libQt6Gui.so.6.9.0 deleted file mode 100755 index 555dfc0..0000000 Binary files a/Release/lib/libQt6Gui.so.6.9.0 and /dev/null differ diff --git a/Release/lib/libQt6Network.so.6 b/Release/lib/libQt6Network.so.6 deleted file mode 120000 index 2d8d752..0000000 --- a/Release/lib/libQt6Network.so.6 +++ /dev/null @@ -1 +0,0 @@ -libQt6Network.so.6.9.0 \ No newline at end of file diff --git a/Release/lib/libQt6Network.so.6.9.0 b/Release/lib/libQt6Network.so.6.9.0 deleted file mode 100755 index 9d7d95a..0000000 Binary files a/Release/lib/libQt6Network.so.6.9.0 and /dev/null differ diff --git a/Release/lib/libQt6OpenGL.so.6 b/Release/lib/libQt6OpenGL.so.6 deleted file mode 120000 index 4cebb83..0000000 --- a/Release/lib/libQt6OpenGL.so.6 +++ /dev/null @@ -1 +0,0 @@ -libQt6OpenGL.so.6.9.0 \ No newline at end of file diff --git a/Release/lib/libQt6OpenGL.so.6.9.0 b/Release/lib/libQt6OpenGL.so.6.9.0 deleted file mode 100755 index 24cf6e7..0000000 Binary files a/Release/lib/libQt6OpenGL.so.6.9.0 and /dev/null differ diff --git a/Release/lib/libQt6PrintSupport.so.6 b/Release/lib/libQt6PrintSupport.so.6 deleted file mode 120000 index 389dfcb..0000000 --- a/Release/lib/libQt6PrintSupport.so.6 +++ /dev/null @@ -1 +0,0 @@ -libQt6PrintSupport.so.6.9.0 \ No newline at end of file diff --git a/Release/lib/libQt6PrintSupport.so.6.9.0 b/Release/lib/libQt6PrintSupport.so.6.9.0 deleted file mode 100755 index 0777c85..0000000 Binary files a/Release/lib/libQt6PrintSupport.so.6.9.0 and /dev/null differ diff --git a/Release/lib/libQt6WaylandClient.so.6 b/Release/lib/libQt6WaylandClient.so.6 deleted file mode 120000 index 40477fe..0000000 --- a/Release/lib/libQt6WaylandClient.so.6 +++ /dev/null @@ -1 +0,0 @@ -libQt6WaylandClient.so.6.9.0 \ No newline at end of file diff --git a/Release/lib/libQt6WaylandClient.so.6.9.0 b/Release/lib/libQt6WaylandClient.so.6.9.0 deleted file mode 100755 index f10fc0c..0000000 Binary files a/Release/lib/libQt6WaylandClient.so.6.9.0 and /dev/null differ diff --git a/Release/lib/libQt6Widgets.so.6 b/Release/lib/libQt6Widgets.so.6 deleted file mode 120000 index 0ff79f2..0000000 --- a/Release/lib/libQt6Widgets.so.6 +++ /dev/null @@ -1 +0,0 @@ -libQt6Widgets.so.6.9.0 \ No newline at end of file diff --git a/Release/lib/libQt6Widgets.so.6.9.0 b/Release/lib/libQt6Widgets.so.6.9.0 deleted file mode 100755 index 0c95687..0000000 Binary files a/Release/lib/libQt6Widgets.so.6.9.0 and /dev/null differ diff --git a/Release/lib/libQt6XcbQpa.so.6 b/Release/lib/libQt6XcbQpa.so.6 deleted file mode 120000 index 192df9d..0000000 --- a/Release/lib/libQt6XcbQpa.so.6 +++ /dev/null @@ -1 +0,0 @@ -libQt6XcbQpa.so.6.9.0 \ No newline at end of file diff --git a/Release/lib/libQt6XcbQpa.so.6.9.0 b/Release/lib/libQt6XcbQpa.so.6.9.0 deleted file mode 100755 index 24f69d6..0000000 Binary files a/Release/lib/libQt6XcbQpa.so.6.9.0 and /dev/null differ diff --git a/Release/lib/libQt6Xml.so.6 b/Release/lib/libQt6Xml.so.6 deleted file mode 120000 index b9e23ce..0000000 --- a/Release/lib/libQt6Xml.so.6 +++ /dev/null @@ -1 +0,0 @@ -libQt6Xml.so.6.9.0 \ No newline at end of file diff --git a/Release/lib/libQt6Xml.so.6.9.0 b/Release/lib/libQt6Xml.so.6.9.0 deleted file mode 100755 index 4a873df..0000000 Binary files a/Release/lib/libQt6Xml.so.6.9.0 and /dev/null differ diff --git a/Release/lib/libxcb.so.1 b/Release/lib/libxcb.so.1 deleted file mode 120000 index e858d46..0000000 --- a/Release/lib/libxcb.so.1 +++ /dev/null @@ -1 +0,0 @@ -libxcb.so.1.1.0 \ No newline at end of file diff --git a/Release/lib/libxcb.so.1.1.0 b/Release/lib/libxcb.so.1.1.0 deleted file mode 100755 index 949b9f9..0000000 Binary files a/Release/lib/libxcb.so.1.1.0 and /dev/null differ diff --git a/XNSimHtml/assets/icons/png/git.png b/XNSimHtml/assets/icons/png/git.png new file mode 100644 index 0000000..dfd1f71 Binary files /dev/null and b/XNSimHtml/assets/icons/png/git.png differ diff --git a/XNSimHtml/components/data-monitor.js b/XNSimHtml/components/data-monitor.js index eaa208c..2e2b89a 100644 --- a/XNSimHtml/components/data-monitor.js +++ b/XNSimHtml/components/data-monitor.js @@ -1,227 +1,324 @@ +/** + * @class DataMonitor + * @extends HTMLElement + * @description 数据监控组件的基础类 + */ class DataMonitor extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); - this.currentMode = 'udp'; // 默认显示UDP模式 - this.udpPort = 54321; // 默认UDP端口 - this.udpIp = '127.0.0.1'; // 默认监听所有接口 - this.isMonitoring = false; // 监控状态 - this.udpData = []; // 存储接收到的UDP数据 - - // UDP数据注入默认值 - this.injectIp = '127.0.0.1'; - this.injectPort = 12345; - this.injectData = '{"message": "测试数据"}'; + this.interfaces = []; // 存储接口数据 + this.filteredInterfaces = []; // 存储过滤后的接口数据 + this.searchText = ''; // 搜索文本 + this.searchTimeout = null; // 用于防抖的定时器 + this.cursorPosition = 0; // 存储光标位置 + this.tableData = []; // 表格数据,存储已添加的接口 + this.chart = null; // Chart.js 实例 + this.tableHeight = 220; // 表格初始高度(px) + this.isResizing = false; // 是否正在拖动分隔线 + this.startY = 0; // 拖动起始Y + this.startTableHeight = 0; // 拖动起始表格高度 + this.activeChartIndexes = []; // 当前绘图的行索引 + // 列宽数组,初始为像素 + this.colWidths = [160, 160, 120, 180, 320]; // px + this.dragColIndex = null; + this.dragStartX = 0; + this.dragStartWidth = 0; + this._colWidthInited = false; + this._resizeEventBinded = false; } connectedCallback() { - this.render(); - } - - switchMode(mode) { - this.currentMode = mode; - this.render(); - } - - async startMonitoring() { - if (this.isMonitoring) return; - - try { - const response = await fetch('/api/udp-monitor/start', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - port: this.udpPort, - ip: this.udpIp - }), - }); - - const data = await response.json(); - - if (data.success) { - this.isMonitoring = true; - this.updateMonitoringStatus(); - this.setupDataFetch(); - } else { - this.showError(data.error || '启动监控失败'); + this.loadInterfaces(); + // 延迟多次尝试,直到拿到有效宽度 + const tryInitColWidth = (tryCount = 0) => { + const section = this.shadowRoot && this.shadowRoot.querySelector('.table-section'); + if (section && section.clientWidth > 0) { + const totalWidth = section.clientWidth; + const ratios = [0.17, 0.17, 0.13, 0.19, 0.34]; + this.colWidths = ratios.map(r => Math.floor(totalWidth * r)); + this._colWidthInited = true; + this.render(); + } else if (tryCount < 10) { + setTimeout(() => tryInitColWidth(tryCount + 1), 50); } - } catch (error) { - this.showError('无法连接到服务器'); - console.error('启动UDP监控失败:', error); - } - } - - async stopMonitoring() { - if (!this.isMonitoring) return; - - try { - const response = await fetch('/api/udp-monitor/stop', { - method: 'POST', - }); - - const data = await response.json(); - - if (data.success) { - this.isMonitoring = false; - this.updateMonitoringStatus(); - if (this.dataFetchInterval) { - clearInterval(this.dataFetchInterval); - this.dataFetchInterval = null; + }; + tryInitColWidth(); + // 只绑定一次全局拖动事件 + if (!this._resizeEventBinded) { + window.addEventListener('mousemove', this._onWindowMouseMove = (e) => { + if (this.isResizing) { + const delta = e.clientY - this.startY; + let newHeight = this.startTableHeight + delta; + const minHeight = 60; + const maxHeight = Math.max(120, this.offsetHeight - 180); + if (newHeight < minHeight) newHeight = minHeight; + if (newHeight > maxHeight) newHeight = maxHeight; + this.tableHeight = newHeight; + this.render(); } - } else { - this.showError(data.error || '停止监控失败'); - } - } catch (error) { - this.showError('无法连接到服务器'); - console.error('停止UDP监控失败:', error); + }); + window.addEventListener('mouseup', this._onWindowMouseUp = () => { + if (this.isResizing) { + this.isResizing = false; + const divider = this.shadowRoot.querySelector('.divider'); + if (divider) divider.classList.remove('active'); + document.body.style.cursor = ''; + document.body.style.userSelect = ''; + } + }); + this._resizeEventBinded = true; } } - async injectUdpData() { + /** + * @description 从localStorage获取当前选择的配置 + * @returns {Object} 包含plane和configurationId的对象 + */ + getCurrentSelection() { + const selection = localStorage.getItem('xnsim-selection'); + return selection ? JSON.parse(selection) : { plane: '', configurationId: '' }; + } + + /** + * @description 加载接口数据 + */ + async loadInterfaces() { try { - // 验证数据格式 - let parsedData; - try { - parsedData = JSON.parse(this.injectData); - } catch (e) { - this.showError('数据格式无效,请输入有效的JSON'); + const { configurationId } = this.getCurrentSelection(); + if (!configurationId) { + console.warn('未找到配置ID'); return; } - - const response = await fetch('/api/udp-monitor/inject', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - targetIp: this.injectIp, - targetPort: this.injectPort, - data: parsedData - }), - }); - + + const response = await fetch(`/api/interface/list?systemName=XNSim&confID=${configurationId}`); const data = await response.json(); - - if (data.success) { - this.showSuccess('数据已成功发送'); - } else { - this.showError(data.error || '发送数据失败'); - } + this.interfaces = data; + this.filteredInterfaces = this.filterInterfaces(this.searchText); + this.render(); } catch (error) { - this.showError('无法连接到服务器'); - console.error('发送UDP数据失败:', error); + console.error('加载接口数据失败:', error); } } - setupDataFetch() { - // 清除可能存在的之前的定时器 - if (this.dataFetchInterval) { - clearInterval(this.dataFetchInterval); + /** + * @description 根据搜索文本过滤接口 + * @param {string} searchText - 搜索文本 + * @returns {Array} 过滤后的接口数据 + */ + filterInterfaces(searchText) { + if (!searchText) return this.interfaces; + + return this.interfaces.filter(item => + item.InterfaceName.toLowerCase().includes(searchText.toLowerCase()) || + item.ModelStructName.toLowerCase().includes(searchText.toLowerCase()) + ); + } + + /** + * @description 处理搜索输入 + * @param {Event} event - 输入事件 + */ + handleSearch(event) { + this.searchText = event.target.value; + this.cursorPosition = event.target.selectionStart; + + // 清除之前的定时器 + if (this.searchTimeout) { + clearTimeout(this.searchTimeout); } - // 每秒拉取一次新数据 - this.dataFetchInterval = setInterval(async () => { - try { - const response = await fetch('/api/udp-monitor/data'); - const data = await response.json(); - - if (data.success && data.data) { - this.updateDataDisplay(data.data); + // 设置新的定时器,300ms后执行搜索 + this.searchTimeout = setTimeout(() => { + this.filteredInterfaces = this.filterInterfaces(this.searchText); + this.render(); + + // 在下一个事件循环中恢复焦点和光标位置 + requestAnimationFrame(() => { + const searchInput = this.shadowRoot.querySelector('.search-input'); + if (searchInput) { + searchInput.focus(); + searchInput.setSelectionRange(this.cursorPosition, this.cursorPosition); + } + }); + }, 300); + } + + /** + * @description 处理树节点双击事件,将接口添加到表格 + * @param {Object} item - 接口对象 + */ + handleTreeItemDblClick(item) { + // 防止重复添加 + if (!this.tableData.some(row => row.InterfaceName === item.InterfaceName && row.ModelStructName === item.ModelStructName)) { + this.tableData.push({ + InterfaceName: item.InterfaceName, + ModelStructName: item.ModelStructName, + InjectValue: '', + Drawing: false, + color: this.getRandomColor() + }); + this.renderTable(); + } + } + + /** + * @description 渲染表格内容 + */ + renderTable() { + const tableBody = this.shadowRoot.querySelector('#monitor-table-body'); + if (tableBody) { + tableBody.innerHTML = this.tableData.map((row, idx) => ` + + ${row.InterfaceName} + ${row.ModelStructName} + + + + + + + + + + + + + `).join(''); + } + + // 注入值输入限制 + this.shadowRoot.querySelectorAll('.inject-input').forEach(input => { + input.oninput = (e) => { + // 只允许数字、负号、小数点、逗号 + let v = e.target.value.replace(/[^0-9\-.,]/g, ''); + e.target.value = v; + const idx = parseInt(e.target.getAttribute('data-index')); + this.tableData[idx].InjectValue = v; + }; + }); + + // 按钮事件 + this.shadowRoot.querySelectorAll('.chart-btn').forEach(btn => { + btn.onclick = (e) => { + const idx = parseInt(btn.getAttribute('data-index')); + // 多选支持 + if (this.tableData[idx].Drawing) { + this.tableData[idx].Drawing = false; + } else { + this.tableData[idx].Drawing = true; + } + // 更新activeChartIndexes + this.activeChartIndexes = this.tableData.map((row, i) => row.Drawing ? i : null).filter(i => i !== null); + this.renderTable(); + this.updateChart(); + }; + }); + this.shadowRoot.querySelectorAll('.delete-btn').forEach(btn => { + btn.onclick = (e) => { + const idx = parseInt(btn.getAttribute('data-index')); + this.tableData.splice(idx, 1); + // 删除后同步更新activeChartIndexes + this.activeChartIndexes = this.tableData.map((row, i) => row.Drawing ? i : null).filter(i => i !== null); + this.renderTable(); + this.updateChart(); + }; + }); + } + + /** + * @description 初始化图表 + */ + initChart() { + const chartElement = this.shadowRoot.querySelector('#monitor-chart'); + if (!chartElement) return; + if (typeof Chart === 'undefined') return; + if (this.chart) { + this.chart.destroy(); + this.chart = null; + } + const ctx = chartElement.getContext('2d'); + this.chart = new Chart(ctx, { + type: 'line', + data: { + labels: [], + datasets: [] + }, + options: { + responsive: true, + maintainAspectRatio: false, + scales: { + y: { beginAtZero: true } + }, + animation: false, + plugins: { + legend: { display: true }, + tooltip: { enabled: false } } - } catch (error) { - console.error('获取UDP数据失败:', error); } - }, 1000); + }); } - updateDataDisplay(newData) { - if (!newData || newData.length === 0) return; - - // 更新数据并限制显示的数据条数 - this.udpData = [...this.udpData, ...newData].slice(-100); - - const dataContainer = this.shadowRoot.querySelector('.data-container'); - if (!dataContainer) return; - - dataContainer.innerHTML = this.udpData.map(item => { - return `
- ${new Date(item.timestamp).toLocaleTimeString()} - ${item.source} - ${this.formatData(item.data)} -
`; - }).join(''); - - // 自动滚动到底部 - dataContainer.scrollTop = dataContainer.scrollHeight; - } - - formatData(data) { - // 简单显示为字符串,真实实现可能更复杂 - if (typeof data === 'object') { - return JSON.stringify(data); + /** + * @description 获取随机颜色 + * @returns {string} 颜色字符串 + */ + getRandomColor() { + const letters = '0123456789ABCDEF'; + let color = '#'; + for (let i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; } - return data; + return color; } - updateMonitoringStatus() { - const statusLabel = this.shadowRoot.querySelector('.status-label'); - const startButton = this.shadowRoot.querySelector('#start-monitoring'); - const stopButton = this.shadowRoot.querySelector('#stop-monitoring'); - const portInput = this.shadowRoot.querySelector('#udp-port'); - const ipInput = this.shadowRoot.querySelector('#udp-ip'); - - if (statusLabel) { - statusLabel.textContent = this.isMonitoring ? '监控中' : '未监控'; - statusLabel.className = `status-label ${this.isMonitoring ? 'active' : 'inactive'}`; - } - - if (startButton) { - startButton.disabled = this.isMonitoring; - } - - if (stopButton) { - stopButton.disabled = !this.isMonitoring; - } - - if (portInput) { - portInput.disabled = this.isMonitoring; - } - - if (ipInput) { - ipInput.disabled = this.isMonitoring; - } - } - - showError(message) { - const errorElement = this.shadowRoot.querySelector('.error-message'); - if (errorElement) { - errorElement.textContent = message; - errorElement.style.display = 'block'; - errorElement.className = 'message-box error-message'; - - // 3秒后自动隐藏错误消息 - setTimeout(() => { - errorElement.style.display = 'none'; - }, 3000); - } - } - - showSuccess(message) { - const successElement = this.shadowRoot.querySelector('.success-message'); - if (successElement) { - successElement.textContent = message; - successElement.style.display = 'block'; - - // 3秒后自动隐藏成功消息 - setTimeout(() => { - successElement.style.display = 'none'; - }, 3000); + /** + * @description 更新图表,支持多条曲线 + */ + updateChart() { + if (!this.chart) return; + if (!this.activeChartIndexes || this.activeChartIndexes.length === 0) { + this.chart.data.labels = []; + this.chart.data.datasets = []; + this.chart.update('none'); + return; } + // 多条曲线 + const labels = Array.from({length: 20}, (_, i) => i+1); + this.chart.data.labels = labels; + this.chart.data.datasets = this.activeChartIndexes.map(idx => { + const row = this.tableData[idx]; + return { + label: row.InterfaceName, + data: Array.from({length: 20}, () => (Math.random()*100).toFixed(2)), + borderColor: row.color, + fill: false + }; + }); + this.chart.update('none'); } render() { + // 计算所有列宽之和 + const totalColWidth = this.colWidths.reduce((a, b) => a + b, 0); + let tableWidthStyle = ''; + const section = this.shadowRoot && this.shadowRoot.querySelector('.table-section'); + const containerWidth = section ? section.clientWidth : 940; + if (totalColWidth < containerWidth) { + tableWidthStyle = 'width:100%;'; + } else { + tableWidthStyle = 'width:max-content;'; + } + + // 首次渲染时动态分配列宽(已移至connectedCallback,防止死循环) + // 按ModelStructName分组 + const groupedInterfaces = this.filteredInterfaces.reduce((groups, item) => { + const group = groups[item.ModelStructName] || []; + group.push(item); + groups[item.ModelStructName] = group; + return groups; + }, {}); + this.shadowRoot.innerHTML = `
-
- - +
+ +
+ ${Object.entries(groupedInterfaces).map(([groupName, items]) => ` +
+
+ + ${groupName} +
+
+ ${items.map(item => ` +
+ ${item.InterfaceName} +
+ `).join('')} +
+
+ `).join('')} +
- -
- ${this.currentMode === 'udp' - ? `
UDP数据监控
-
- -
-
UDP数据监控
- -
-
- - -
-
- - -
- - -
- - ${this.isMonitoring ? '监控中' : '未监控'} - -
-
- -
- -
-
-
-
- - -
- - -
-
UDP数据注入
- -
-
- - -
-
- - -
-
- -
- - -
- -
- -
- -
-
-
` - : `
FastDDS数据监控
-
FastDDS数据监控内容(待实现)
`} +
+
+
+ + + + + + + + + + + + + + + + + +
接口名称
结构体名
数据
注入值
操作
+
+
+ + + + + + + + + + + +
+
+
+
+
+
数据监控图表
+
+ +
+
`; - - // 添加切换模式的事件监听 - this.shadowRoot.getElementById('udp-mode').addEventListener('click', () => this.switchMode('udp')); - this.shadowRoot.getElementById('fastdds-mode').addEventListener('click', () => this.switchMode('fastdds')); - - // 如果是UDP模式,添加控制按钮的事件监听 - if (this.currentMode === 'udp') { - // 监控部分事件监听 - // IP输入事件 - const ipInput = this.shadowRoot.getElementById('udp-ip'); - if (ipInput) { - ipInput.addEventListener('change', (e) => { - const value = e.target.value.trim(); - // 简单的IP地址验证,可以接受0.0.0.0或具体IP - if (value === '0.0.0.0' || /^(\d{1,3}\.){3}\d{1,3}$/.test(value)) { - this.udpIp = value; - } else { - e.target.value = this.udpIp; - this.showError('请输入有效的IP地址'); - } - }); - } - - // 端口输入事件 - const portInput = this.shadowRoot.getElementById('udp-port'); - if (portInput) { - portInput.addEventListener('change', (e) => { - const value = parseInt(e.target.value, 10); - if (value >= 1024 && value <= 65535) { - this.udpPort = value; - } else { - e.target.value = this.udpPort; - this.showError('端口号必须在1024-65535之间'); - } - }); - } - - // 开始监控按钮 - const startButton = this.shadowRoot.getElementById('start-monitoring'); - if (startButton) { - startButton.addEventListener('click', () => this.startMonitoring()); - } - - // 停止监控按钮 - const stopButton = this.shadowRoot.getElementById('stop-monitoring'); - if (stopButton) { - stopButton.addEventListener('click', () => this.stopMonitoring()); - } - - // 数据注入部分事件监听 - // 注入IP输入事件 - const injectIpInput = this.shadowRoot.getElementById('inject-ip'); - if (injectIpInput) { - injectIpInput.addEventListener('change', (e) => { - const value = e.target.value.trim(); - if (/^(\d{1,3}\.){3}\d{1,3}$/.test(value)) { - this.injectIp = value; - } else { - e.target.value = this.injectIp; - this.showError('请输入有效的IP地址'); - } - }); - } - - // 注入端口输入事件 - const injectPortInput = this.shadowRoot.getElementById('inject-port'); - if (injectPortInput) { - injectPortInput.addEventListener('change', (e) => { - const value = parseInt(e.target.value, 10); - if (value >= 1024 && value <= 65535) { - this.injectPort = value; - } else { - e.target.value = this.injectPort; - this.showError('端口号必须在1024-65535之间'); - } - }); - } - - // 注入数据输入事件 - const injectDataInput = this.shadowRoot.getElementById('inject-data'); - if (injectDataInput) { - injectDataInput.addEventListener('change', (e) => { - this.injectData = e.target.value; - }); - } - - // 发送数据按钮 - const injectButton = this.shadowRoot.getElementById('inject-data-btn'); - if (injectButton) { - injectButton.addEventListener('click', () => this.injectUdpData()); - } + + // 搜索框事件 + const searchInput = this.shadowRoot.querySelector('.search-input'); + if (searchInput) { + searchInput.addEventListener('input', (e) => this.handleSearch(e)); } + + // 树节点双击事件 + this.shadowRoot.querySelectorAll('.interface-item').forEach(itemEl => { + itemEl.ondblclick = (e) => { + const name = itemEl.getAttribute('data-interfacename'); + const struct = itemEl.getAttribute('data-modelstructname'); + this.handleTreeItemDblClick({ InterfaceName: name, ModelStructName: struct }); + }; + }); + + // 渲染表格内容 + this.renderTable(); + + // 初始化图表 + this.initChart(); + + // 初始绘图 + this.updateChart(); + + // 分隔线拖动事件 + const divider = this.shadowRoot.querySelector('.divider'); + if (divider) { + divider.onmousedown = (e) => { + this.isResizing = true; + this.startY = e.clientY; + this.startTableHeight = this.tableHeight; + divider.classList.add('active'); + document.body.style.cursor = 'row-resize'; + document.body.style.userSelect = 'none'; + }; + } + + // 列宽拖动事件 + this.shadowRoot.querySelectorAll('.th-resize').forEach(handle => { + handle.onmousedown = (e) => { + e.preventDefault(); + this.dragColIndex = parseInt(handle.getAttribute('data-col')); + this.dragStartX = e.clientX; + this.dragStartWidth = this.colWidths[this.dragColIndex]; + document.body.style.cursor = 'col-resize'; + document.body.style.userSelect = 'none'; + handle.classList.add('active'); + // 高亮竖线 + const th = handle.parentElement; + if (th) th.classList.add('th-resize-active'); + }; + handle.onmouseup = () => { + handle.classList.remove('active'); + const th = handle.parentElement; + if (th) th.classList.remove('th-resize-active'); + }; + }); + window.onmousemove = (e) => { + if (this.dragColIndex !== null) { + const delta = e.clientX - this.dragStartX; + let newWidth = this.dragStartWidth + delta; + if (newWidth < 60) newWidth = 60; + this.colWidths[this.dragColIndex] = newWidth; + this.render(); + } + }; + window.onmouseup = () => { + if (this.dragColIndex !== null) { + // 移除所有th-resize的active + this.shadowRoot.querySelectorAll('.th-resize').forEach(h => h.classList.remove('active')); + // 移除所有th的高亮 + this.shadowRoot.querySelectorAll('.monitor-table th').forEach(th => th.classList.remove('th-resize-active')); + this.dragColIndex = null; + document.body.style.cursor = ''; + document.body.style.userSelect = ''; + } + }; } }