Browse Source

1

feature/strong-typing-refactor
root 1 month ago
parent
commit
4f88ce566c
  1. 245
      LTEMvcApp/Views/Home/Logs.cshtml

245
LTEMvcApp/Views/Home/Logs.cshtml

@ -265,21 +265,105 @@
}
/* Layer过滤器样式 */
.layer-filter-dropdown {
.layer-filter-container {
position: relative;
margin-left: 8px;
padding: 2px 6px;
font-size: 0.8em;
}
.layer-filter-trigger {
padding: 4px 8px;
border: 1px solid #ced4da;
border-radius: 3px;
background-color: #fff;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
min-width: 80px;
font-size: 0.8em;
}
.layer-filter-dropdown:focus {
outline: none;
.layer-filter-trigger:hover {
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}
.filter-text {
font-size: 0.8em;
}
.filter-arrow {
font-size: 0.8em;
transition: transform 0.2s;
}
.layer-filter-container.open .filter-arrow {
transform: rotate(180deg);
}
.layer-filter-dropdown {
position: absolute;
top: 100%;
left: 0;
width: 150px;
background-color: #fff;
border: 1px solid #ced4da;
border-radius: 3px;
max-height: 200px;
overflow-y: auto;
z-index: 1000;
display: none;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.layer-filter-container.open .layer-filter-dropdown {
display: block;
}
.filter-header {
padding: 8px 10px;
background-color: #f8f9fa;
border-bottom: 1px solid #dee2e6;
}
.select-all-label {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.8em;
cursor: pointer;
}
.select-all-label input {
margin: 0;
}
.filter-options {
padding: 8px 10px;
}
.filter-option {
margin-bottom: 6px;
}
.filter-option:last-child {
margin-bottom: 0;
}
.filter-option label {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.8em;
cursor: pointer;
padding: 2px 0;
}
.filter-option label:hover {
background-color: #f8f9fa;
}
.filter-option input {
margin: 0;
}
.log-layer {
@ -299,9 +383,22 @@
<span class="log-timestamp">Timestamp</span>
<span class="log-layer">
Layer
<select id="layer-filter" class="layer-filter-dropdown">
<option value="">全部</option>
</select>
<div class="layer-filter-container">
<div class="layer-filter-trigger" id="layer-filter-trigger">
<span class="filter-text">全部</span>
<span class="filter-arrow">▼</span>
</div>
<div class="layer-filter-dropdown" id="layer-filter-dropdown">
<div class="filter-header">
<label class="select-all-label">
<input type="checkbox" id="select-all-layers"> 全选
</label>
</div>
<div class="filter-options" id="layer-filter-options">
<!-- 选项将动态生成 -->
</div>
</div>
</div>
</span>
<span class="log-direction">Direction</span>
<span class="log-message">Message</span>
@ -362,10 +459,15 @@
const reconnectBtn = document.getElementById('reconnect-btn');
const logListPanel = document.querySelector('.log-list-panel');
const resizer = document.getElementById('drag-resizer');
const layerFilter = document.getElementById('layer-filter');
const layerFilter = document.getElementById('layer-filter-dropdown');
const layerFilterTrigger = document.getElementById('layer-filter-trigger');
const layerFilterOptions = document.getElementById('layer-filter-options');
const selectAllLayers = document.getElementById('select-all-layers');
const filterText = document.querySelector('.filter-text');
let allLogsData = [];
let availableLayers = new Set(); // 用于跟踪可用的日志层
let availableLayers = new Set(['PHY', 'MAC', 'RLC', 'PDCP', 'RRC', 'NAS']); // 初始化标准LTE层
let selectedLayers = new Set(); // 用于跟踪选中的日志层
let eventSource = null;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
@ -381,29 +483,86 @@
no_data_text: "正在等待日志..."
});
// 更新过滤器显示文本
function updateFilterText() {
if (selectedLayers.size === 0) {
filterText.textContent = '全部';
} else if (selectedLayers.size === availableLayers.size) {
filterText.textContent = '全部';
} else if (selectedLayers.size === 1) {
filterText.textContent = Array.from(selectedLayers)[0];
} else {
filterText.textContent = `${selectedLayers.size}个层`;
}
}
// 更新全选复选框状态
function updateSelectAllState() {
if (availableLayers.size === 0) {
selectAllLayers.checked = false;
selectAllLayers.indeterminate = false;
} else if (selectedLayers.size === 0) {
selectAllLayers.checked = false;
selectAllLayers.indeterminate = false;
} else if (selectedLayers.size === availableLayers.size) {
selectAllLayers.checked = true;
selectAllLayers.indeterminate = false;
} else {
selectAllLayers.checked = false;
selectAllLayers.indeterminate = true;
}
}
// 更新Layer过滤器选项
function updateLayerFilter() {
const currentValue = layerFilter.value;
const options = ['<option value="">全部</option>'];
const options = [];
// 按字母顺序排序
const sortedLayers = Array.from(availableLayers).sort();
sortedLayers.forEach(layer => {
const selected = layer === currentValue ? ' selected' : '';
options.push(`<option value="${layer}"${selected}>${layer}</option>`);
const isChecked = selectedLayers.has(layer) ? ' checked' : '';
options.push(`<div class="filter-option">
<label>
<input type="checkbox" value="${layer}"${isChecked}>
${layer}
</label>
</div>`);
});
layerFilter.innerHTML = options.join('');
layerFilterOptions.innerHTML = options.join('');
// 重新绑定事件
bindFilterEvents();
// 更新显示文本和全选状态
updateFilterText();
updateSelectAllState();
}
// 绑定过滤器事件
function bindFilterEvents() {
// 绑定单个复选框事件
layerFilterOptions.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
checkbox.addEventListener('change', function() {
if (this.checked) {
selectedLayers.add(this.value);
} else {
selectedLayers.delete(this.value);
}
updateFilterText();
updateSelectAllState();
refreshLogList();
});
});
}
// 根据当前过滤器重新渲染日志列表
function refreshLogList() {
const selectedLayer = layerFilter.value;
let filteredLogs = allLogsData;
if (selectedLayer) {
filteredLogs = allLogsData.filter(log => log.Layer === selectedLayer);
if (selectedLayers.size > 0 && selectedLayers.size < availableLayers.size) {
filteredLogs = allLogsData.filter(log => selectedLayers.has(log.Layer));
}
const rows = filteredLogs.map((log, i) => formatLogItem(log, i));
@ -442,6 +601,7 @@
function clearLogsDisplay() {
allLogsData = [];
availableLayers.clear();
selectedLayers.clear();
clusterize.clear();
totalLogsEl.textContent = '0';
newLogsCountEl.textContent = '';
@ -668,11 +828,51 @@
};
}
// Layer过滤器变化事件
layerFilter.addEventListener('change', function() {
// 切换下拉框显示状态
function toggleFilterDropdown() {
const container = layerFilterTrigger.parentElement;
container.classList.toggle('open');
}
// 关闭下拉框
function closeFilterDropdown() {
const container = layerFilterTrigger.parentElement;
container.classList.remove('open');
}
// 事件监听器
layerFilterTrigger.addEventListener('click', function(e) {
e.stopPropagation();
toggleFilterDropdown();
});
// 全选/取消全选
selectAllLayers.addEventListener('change', function() {
const isChecked = selectAllLayers.checked;
if (isChecked) {
// 全选
selectedLayers.clear();
availableLayers.forEach(layer => selectedLayers.add(layer));
} else {
// 取消全选
selectedLayers.clear();
}
updateLayerFilter();
refreshLogList();
});
// 点击外部关闭下拉框
document.addEventListener('click', function(e) {
if (!layerFilterTrigger.contains(e.target) && !layerFilter.contains(e.target)) {
closeFilterDropdown();
}
});
// 阻止下拉框内部点击事件冒泡
layerFilter.addEventListener('click', function(e) {
e.stopPropagation();
});
// 事件委托处理点击事件
contentArea.addEventListener('click', function(e) {
const item = e.target.closest('.log-item');
@ -785,6 +985,9 @@
// 初始化连接
connectSSE();
// 初始化Layer过滤器,显示标准LTE层
updateLayerFilter();
});
</script>
}

Loading…
Cancel
Save