fix: compact sentinel dashboard mobile density
This commit is contained in:
@@ -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 || "-";
|
||||
|
||||
Reference in New Issue
Block a user