fix: compact sentinel dashboard mobile density

This commit is contained in:
Codex
2026-06-26 11:45:32 +00:00
parent d3fccec8b5
commit bab5e95f54
2 changed files with 61 additions and 5 deletions
@@ -457,6 +457,21 @@ select {
background: #eef7ff;
}
.runs-table tr.limit-row {
cursor: default;
}
.runs-table tr.limit-row:hover {
background: transparent;
}
.runs-table tr.limit-row td {
color: var(--muted);
font-size: 12px;
font-weight: 700;
text-align: center;
}
.runs-table td small {
display: block;
margin-top: 3px;
@@ -969,6 +984,10 @@ select {
border-bottom: 1px solid #edf0f4;
}
.runs-table tr.limit-row {
padding: 12px;
}
.runs-table td {
display: grid;
grid-template-columns: 82px minmax(0, 1fr);
@@ -985,6 +1004,16 @@ select {
text-transform: uppercase;
}
.runs-table tr.limit-row td {
display: block;
padding: 0;
text-align: left;
}
.runs-table tr.limit-row td::before {
content: none;
}
.run-identity div {
grid-template-columns: 54px minmax(0, 1fr);
}
@@ -75,6 +75,13 @@ const state = {
findingFilters: readFindingFiltersFromLocation(),
};
const dashboardLimits = {
timeline: { mobile: 6, tablet: 9, desktop: 12 },
runs: { mobile: 8, tablet: 12, desktop: 24 },
findingsRed: { mobile: 4, tablet: 6, desktop: 8 },
findingsOther: { mobile: 2, tablet: 3, desktop: 5 },
};
const dashboardApi = createDashboardApi();
const autoRefresh = createAutoRefresh({
storageKey: "hwlab.webProbeSentinel.dashboard.autoRefresh",
@@ -114,6 +121,12 @@ refs.findingClearFilters.addEventListener("click", () => {
document.addEventListener("visibilitychange", () => {
if (!document.hidden && autoRefresh.enabled()) loadDashboard({ silent: true });
});
window.addEventListener("resize", debounce(() => {
if (!state.overview) return;
renderTimeline();
renderRuns();
renderFindings();
}, 150));
hydrateControls();
writeFiltersToControls();
@@ -323,7 +336,7 @@ function renderOverview() {
}
function renderTimeline() {
const visibleRuns = state.runs.slice(0, 12);
const visibleRuns = state.runs.slice(0, responsiveLimit(dashboardLimits.timeline));
refs.timelineCount.textContent = `最近 ${formatNumber(visibleRuns.length)} / ${formatNumber(state.runs.length)}`;
if (state.runs.length === 0) {
refs.timeline.innerHTML = '<div class="empty-state">暂无时间线</div>';
@@ -343,12 +356,15 @@ function renderTimeline() {
}
function renderRuns() {
refs.runsCount.textContent = `${formatNumber(state.runs.length)} 条可见`;
const visibleLimit = responsiveLimit(dashboardLimits.runs);
const visibleRuns = state.runs.slice(0, visibleLimit);
const hiddenCount = Math.max(0, state.runs.length - visibleRuns.length);
refs.runsCount.textContent = `${formatNumber(visibleRuns.length)} / ${formatNumber(state.runs.length)} 条可见`;
if (state.runs.length === 0) {
refs.runsBody.innerHTML = '<tr><td class="empty-state" colspan="5">暂无运行</td></tr>';
return;
}
refs.runsBody.innerHTML = state.runs.map((run) => {
refs.runsBody.innerHTML = visibleRuns.map((run) => {
const runId = run.runId || run.id || "-";
const selected = state.selectedRunId === runId ? " selected-row" : "";
return `<tr class="${selected}" data-run-id="${escapeAttr(runId)}">
@@ -361,7 +377,7 @@ function renderRuns() {
<td data-label="发现项">${escapeHtml(String(run.findingCount ?? 0))}${run.maxSeverity ? ` <span class="severity-pill ${severityClass(run.maxSeverity)}">${escapeHtml(displaySeverity(run.maxSeverity))}</span>` : ""}</td>
<td data-label="更新时间"><span>${escapeHtml(run.updatedAt ? formatRelative(run.updatedAt) : "-")}</span><small>${escapeHtml(run.maintenance ? "维护窗口" : "")}</small></td>
</tr>`;
}).join("");
}).join("") + (hiddenCount > 0 ? `<tr class="limit-row"><td colspan="5">其余 ${formatNumber(hiddenCount)} 条运行通过筛选、搜索或 API drill-down 查看</td></tr>` : "");
for (const row of refs.runsBody.querySelectorAll("tr[data-run-id]")) {
row.addEventListener("click", () => selectRun(row.dataset.runId));
}
@@ -377,7 +393,7 @@ function renderFindings() {
const groups = groupedFindingsBySeverity(state.findings);
refs.findingsList.innerHTML = groups.map((group, groupIndex) => {
const open = group.key === "red" || groupIndex === 0;
const visibleItems = group.items.slice(0, group.key === "red" ? 8 : 5);
const visibleItems = group.items.slice(0, findingVisibleLimit(group.key));
const hiddenCount = Math.max(0, group.items.length - visibleItems.length);
return `<details class="finding-group finding-group-${escapeAttr(group.key)}" ${open ? "open" : ""}>
<summary>
@@ -396,6 +412,17 @@ function renderFindings() {
}
}
function responsiveLimit(limits) {
if (window.matchMedia("(max-width: 560px)").matches) return limits.mobile;
if (window.matchMedia("(max-width: 980px)").matches) return limits.tablet;
return limits.desktop;
}
function findingVisibleLimit(severity) {
const key = String(severity || "").toLowerCase();
return responsiveLimit(key === "red" || key === "critical" || key === "error" ? dashboardLimits.findingsRed : dashboardLimits.findingsOther);
}
function renderFindingItem(item) {
const code = item.code || item.findingId || "finding";
const latestRunId = item.latestRunId || "-";