From c0b446226bf751f32a2ac0e163136e54eb2b8a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:53:36 +0800 Subject: [PATCH] Add files via upload --- web/static/css/style.css | 200 ++++++++++++++++++++++++++++++++++++--- web/static/js/monitor.js | 158 +++++++++++++++++++++++++++++++ web/templates/index.html | 2 +- 3 files changed, 345 insertions(+), 15 deletions(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index 1bb7439c..21eebb64 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -8853,7 +8853,15 @@ html[data-theme="dark"] .openapi-doc-btn:hover { min-width: 0; } +.mcp-monitor-page .monitor-sections { + gap: 16px; +} + /* ── MCP 监控页:页面级布局 ── */ +.mcp-monitor-page { + --mcp-monitor-card-radius: 12px; +} + .mcp-monitor-page .page-content { padding: 16px 20px; background: #f1f5f9; @@ -8896,7 +8904,7 @@ html[data-theme="dark"] .openapi-doc-btn:hover { .monitor-section { background: var(--bg-primary); border: 1px solid rgba(0, 0, 0, 0.06); - border-radius: 16px; + border-radius: var(--mcp-monitor-card-radius, 12px); padding: 20px 22px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04), 0 4px 16px rgba(0, 0, 0, 0.03); display: flex; @@ -8959,6 +8967,151 @@ html[data-theme="dark"] .openapi-doc-btn:hover { max-width: 200px; } +.monitor-filter-native-select { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.monitor-section .section-actions .monitor-filter-select-ui { + position: relative; + display: inline-block; + margin-left: 6px; + min-width: 140px; + max-width: 200px; + vertical-align: middle; +} + +.monitor-filter-select-trigger { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + width: 100%; + min-width: 0; + padding: 6px 10px; + border-radius: 999px; + border: 1px solid var(--border-color); + background: var(--bg-secondary); + color: var(--text-primary); + font-size: 0.875rem; + line-height: 1.25; + cursor: pointer; + font-family: inherit; + transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease; +} + +.monitor-filter-select-trigger:hover:not(:disabled) { + border-color: var(--accent-color); +} + +.monitor-filter-select-ui.open .monitor-filter-select-trigger { + border-color: var(--accent-color); + box-shadow: 0 0 0 2px rgba(0, 102, 255, 0.1); +} + +.monitor-filter-select-ui.open { + z-index: 120; +} + +.monitor-filter-select-ui.is-disabled .monitor-filter-select-trigger { + opacity: 0.6; + cursor: not-allowed; +} + +.monitor-filter-select-value { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: left; +} + +.monitor-filter-select-caret { + flex-shrink: 0; + color: var(--text-secondary); + transition: transform 0.15s ease; +} + +.monitor-filter-select-ui.open .monitor-filter-select-caret { + transform: rotate(180deg); +} + +.monitor-filter-select-dropdown { + display: none; + position: absolute; + top: calc(100% + 4px); + left: 0; + right: 0; + z-index: 200; + max-height: 280px; + overflow-y: auto; + padding: 4px; + background: var(--bg-primary); + border: 1px solid var(--border-color); + border-radius: 8px; + box-shadow: var(--shadow-lg); +} + +.monitor-filter-select-ui.open .monitor-filter-select-dropdown { + display: block; +} + +.monitor-filter-select-option { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 8px 10px; + border: none; + border-radius: 6px; + background: transparent; + color: var(--text-primary); + font-size: 0.8125rem; + font-family: inherit; + cursor: pointer; + text-align: left; + transition: background 0.12s ease, color 0.12s ease; +} + +.monitor-filter-select-option:hover { + background: var(--bg-secondary); +} + +.monitor-filter-select-option.is-selected { + background: rgba(59, 130, 246, 0.08); + color: #2563eb; + font-weight: 500; +} + +.monitor-filter-select-check { + width: 14px; + flex-shrink: 0; + visibility: hidden; + color: #2563eb; + font-size: 12px; + line-height: 1; +} + +.monitor-filter-select-option.is-selected .monitor-filter-select-check { + visibility: visible; +} + +.monitor-filter-select-label { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .monitor-section .section-actions select:focus { outline: none; border-color: var(--accent-color); @@ -9011,7 +9164,7 @@ html[data-theme="dark"] .openapi-doc-btn:hover { .mcp-exec-stats { display: flex; flex-direction: column; - gap: 12px; + gap: 16px; width: 100%; } @@ -9022,7 +9175,7 @@ html[data-theme="dark"] .openapi-doc-btn:hover { align-items: stretch; background: #fff; border: 1px solid rgba(0, 0, 0, 0.07); - border-radius: 12px; + border-radius: var(--mcp-monitor-card-radius, 12px); box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); overflow: hidden; } @@ -9151,7 +9304,7 @@ html[data-theme="dark"] .openapi-doc-btn:hover { .mcp-stats-combined { background: #fff; border: 1px solid rgba(0, 0, 0, 0.07); - border-radius: 10px; + border-radius: var(--mcp-monitor-card-radius, 12px); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04); overflow: hidden; } @@ -9888,9 +10041,6 @@ html[data-theme="dark"] .openapi-doc-btn:hover { } /* MCP 监控统计重构:工具卡 + 柱线趋势 */ -.mcp-stats-combined { - border-radius: 8px; -} .mcp-stats-combined__head { padding: 14px 18px; @@ -9901,12 +10051,12 @@ html[data-theme="dark"] .openapi-doc-btn:hover { } .mcp-stats-combined__main { - padding: 14px 16px 16px; + padding: 14px 16px 18px; } .mcp-stats-combined__body--full .mcp-stats-combined__timeline { gap: 8px; - padding: 14px 16px 12px; + padding: 14px 16px 18px; } .mcp-stats-tools-panel { @@ -10254,7 +10404,7 @@ html[data-theme="dark"] .mcp-stats-timeline-bar.is-hover { .mcp-stats-panel { background: #fff; border: 1px solid rgba(0, 0, 0, 0.07); - border-radius: 10px; + border-radius: var(--mcp-monitor-card-radius, 12px); overflow: hidden; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04); } @@ -10658,6 +10808,11 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { padding: 14px 16px; } +.mcp-monitor-page .monitor-section.monitor-executions:has(.monitor-filter-select-ui.open) { + overflow: visible; + z-index: 5; +} + .mcp-monitor-page .monitor-executions .section-header { margin-bottom: 10px; } @@ -10669,7 +10824,7 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { .mcp-monitor-page .monitor-table-container { border: 1px solid rgba(0, 0, 0, 0.06); - border-radius: 12px; + border-radius: var(--mcp-monitor-card-radius, 12px); box-shadow: none; } @@ -29162,7 +29317,8 @@ html[data-theme="dark"] .tasks-filter-select-option:hover, html[data-theme="dark"] .workflow-form-select-option:hover, html[data-theme="dark"] .projects-filter-select-option:hover, html[data-theme="dark"] .chat-files-filter-select-option:hover, -html[data-theme="dark"] #role-modal .role-form-select-option:hover { +html[data-theme="dark"] #role-modal .role-form-select-option:hover, +html[data-theme="dark"] .monitor-filter-select-option:hover { background: rgba(96, 165, 250, 0.12); } @@ -29177,7 +29333,8 @@ html[data-theme="dark"] .tasks-filter-select-option.is-selected, html[data-theme="dark"] .workflow-form-select-option.is-selected, html[data-theme="dark"] .projects-filter-select-option.is-selected, html[data-theme="dark"] .chat-files-filter-select-option.is-selected, -html[data-theme="dark"] #role-modal .role-form-select-option.is-selected { +html[data-theme="dark"] #role-modal .role-form-select-option.is-selected, +html[data-theme="dark"] .monitor-filter-select-option.is-selected { background: rgba(96, 165, 250, 0.18); color: var(--accent-hover); } @@ -29198,6 +29355,7 @@ html[data-theme="dark"] .workflow-tool-select-trigger, html[data-theme="dark"] .projects-filter-select-trigger, html[data-theme="dark"] .chat-files-filter-select-trigger, html[data-theme="dark"] .role-form-select-trigger, +html[data-theme="dark"] .monitor-filter-select-trigger, html[data-theme="dark"] .role-selector-btn, html[data-theme="dark"] .conversation-project-filter-trigger { background: #0f172a; @@ -29224,7 +29382,8 @@ html[data-theme="dark"] .tasks-filter-select-ui.open .tasks-filter-select-trigge html[data-theme="dark"] .workflow-form-select-ui.open .workflow-form-select-trigger, html[data-theme="dark"] .projects-filter-select-ui.open .projects-filter-select-trigger, html[data-theme="dark"] .chat-files-filter-select-ui.open .chat-files-filter-select-trigger, -html[data-theme="dark"] #role-modal .role-form-select-ui.open .role-form-select-trigger { +html[data-theme="dark"] #role-modal .role-form-select-ui.open .role-form-select-trigger, +html[data-theme="dark"] .monitor-filter-select-ui.open .monitor-filter-select-trigger { border-color: var(--accent-color); box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.16); } @@ -31138,6 +31297,19 @@ html[data-theme="dark"] #page-mcp-monitor .page-content { background: #0b1120 !important; } +html[data-theme="dark"] .content-area:has(#page-mcp-monitor.active) { + background: #0b1120 !important; +} + +/* 概览区保持透明,让 KPI 与工具统计之间的缝隙透出页面底色 */ +html[data-theme="dark"] #page-mcp-monitor .monitor-section.monitor-overview, +html[data-theme="dark"] #page-mcp-monitor .mcp-exec-stats-root, +html[data-theme="dark"] #page-mcp-monitor .mcp-exec-stats { + background: transparent !important; + border: none !important; + box-shadow: none !important; +} + html[data-theme="dark"] #page-mcp-monitor .page-header { background: #111827 !important; border-bottom-color: #263244 !important; diff --git a/web/static/js/monitor.js b/web/static/js/monitor.js index cb6238cb..b9ebdba6 100644 --- a/web/static/js/monitor.js +++ b/web/static/js/monitor.js @@ -4193,6 +4193,162 @@ async function refreshMonitorPanel(page = null) { // 处理工具搜索输入(防抖) let toolFilterDebounceTimer = null; + +const MONITOR_FILTER_SELECT_IDS = ['monitor-status-filter']; +const monitorFilterSelectMap = {}; +let monitorFilterSelectDocBound = false; +const MONITOR_FILTER_SELECT_CARET = ''; + +function closeAllMonitorFilterSelects() { + Object.keys(monitorFilterSelectMap).forEach(function (id) { + const reg = monitorFilterSelectMap[id]; + if (!reg || !reg.wrapper) return; + reg.wrapper.classList.remove('open'); + if (reg.trigger) reg.trigger.setAttribute('aria-expanded', 'false'); + }); +} + +function syncMonitorFilterSelect(selectId) { + const reg = monitorFilterSelectMap[selectId]; + if (!reg) return; + const select = reg.select; + const dropdown = reg.dropdown; + const trigger = reg.trigger; + const valueSpan = trigger.querySelector('.monitor-filter-select-value'); + + dropdown.innerHTML = ''; + Array.prototype.forEach.call(select.options, function (opt) { + const item = document.createElement('button'); + item.type = 'button'; + item.className = 'monitor-filter-select-option'; + item.setAttribute('role', 'option'); + item.setAttribute('data-value', opt.value); + if (opt.value === select.value) { + item.classList.add('is-selected'); + item.setAttribute('aria-selected', 'true'); + } else { + item.setAttribute('aria-selected', 'false'); + } + const check = document.createElement('span'); + check.className = 'monitor-filter-select-check'; + check.setAttribute('aria-hidden', 'true'); + check.textContent = '✓'; + const label = document.createElement('span'); + label.className = 'monitor-filter-select-label'; + label.textContent = opt.textContent; + item.appendChild(check); + item.appendChild(label); + dropdown.appendChild(item); + }); + + const selectedOpt = select.options[select.selectedIndex]; + if (valueSpan) { + valueSpan.textContent = selectedOpt ? selectedOpt.textContent : ''; + } + trigger.disabled = !!select.disabled; + reg.wrapper.classList.toggle('is-disabled', !!select.disabled); +} + +function syncAllMonitorFilterSelects() { + MONITOR_FILTER_SELECT_IDS.forEach(syncMonitorFilterSelect); +} + +function enhanceMonitorFilterSelect(selectId) { + const select = document.getElementById(selectId); + if (!select) return; + const existing = monitorFilterSelectMap[selectId]; + if (existing && existing.select !== select) { + delete monitorFilterSelectMap[selectId]; + } + if (select.dataset.monitorCustomSelect === '1') { + syncMonitorFilterSelect(selectId); + return; + } + select.dataset.monitorCustomSelect = '1'; + select.classList.add('monitor-filter-native-select'); + select.tabIndex = -1; + select.setAttribute('aria-hidden', 'true'); + + const wrapper = document.createElement('div'); + wrapper.className = 'monitor-filter-select-ui'; + + const trigger = document.createElement('button'); + trigger.type = 'button'; + trigger.className = 'monitor-filter-select-trigger'; + trigger.setAttribute('aria-haspopup', 'listbox'); + trigger.setAttribute('aria-expanded', 'false'); + const valueSpan = document.createElement('span'); + valueSpan.className = 'monitor-filter-select-value'; + trigger.appendChild(valueSpan); + trigger.insertAdjacentHTML('beforeend', MONITOR_FILTER_SELECT_CARET); + + const dropdown = document.createElement('div'); + dropdown.className = 'monitor-filter-select-dropdown'; + dropdown.setAttribute('role', 'listbox'); + + const parent = select.parentNode; + parent.insertBefore(wrapper, select); + wrapper.appendChild(trigger); + wrapper.appendChild(dropdown); + wrapper.appendChild(select); + + monitorFilterSelectMap[selectId] = { wrapper: wrapper, trigger: trigger, dropdown: dropdown, select: select }; + + trigger.addEventListener('click', function (e) { + e.stopPropagation(); + if (select.disabled) return; + const open = wrapper.classList.contains('open'); + closeAllMonitorFilterSelects(); + if (!open) { + wrapper.classList.add('open'); + trigger.setAttribute('aria-expanded', 'true'); + } + }); + + dropdown.addEventListener('click', function (e) { + const opt = e.target.closest('.monitor-filter-select-option'); + if (!opt) return; + e.stopPropagation(); + const val = opt.getAttribute('data-value'); + if (val === null) return; + if (select.value !== val) { + select.value = val; + select.dispatchEvent(new Event('change', { bubbles: true })); + } + wrapper.classList.remove('open'); + trigger.setAttribute('aria-expanded', 'false'); + syncMonitorFilterSelect(selectId); + }); + + select.addEventListener('change', function () { + syncMonitorFilterSelect(selectId); + }); + + if (!select.dataset.monitorFilterBound) { + select.dataset.monitorFilterBound = '1'; + select.addEventListener('change', function () { + applyMonitorFilters(); + }); + } + + syncMonitorFilterSelect(selectId); +} + +function initMonitorFilterSelects() { + if (!monitorFilterSelectDocBound) { + document.addEventListener('click', function (e) { + if (e.target.closest('.monitor-filter-select-ui')) return; + closeAllMonitorFilterSelects(); + }); + document.addEventListener('keydown', function (e) { + if (e.key === 'Escape') closeAllMonitorFilterSelects(); + }); + monitorFilterSelectDocBound = true; + } + MONITOR_FILTER_SELECT_IDS.forEach(enhanceMonitorFilterSelect); + syncAllMonitorFilterSelects(); +} + function handleToolFilterInput() { // 清除之前的定时器 if (toolFilterDebounceTimer) { @@ -6105,10 +6261,12 @@ document.addEventListener('languagechange', function () { updateBatchActionsState(); loadActiveTasks(); refreshProgressAndTimelineI18n(); + syncAllMonitorFilterSelects(); refreshMonitorPanelFromState(); }); document.addEventListener('DOMContentLoaded', function () { + initMonitorFilterSelects(); bindMonitorStatsPanelEvents(); if (window.i18nReady && typeof window.i18nReady.then === 'function') { window.i18nReady.then(function () { diff --git a/web/templates/index.html b/web/templates/index.html index e8a859cb..21ae2bde 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -1423,7 +1423,7 @@