refactor: update SVG circle radius and improve code readability in ReportExportDialog
Build and Push CodeReview / build (push) Waiting to run Details

This commit is contained in:
vinland100 2026-01-12 15:08:33 +08:00
parent 7f951d5451
commit 7c0d95385f
2 changed files with 537 additions and 333 deletions

View File

@ -1,5 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -32,8 +33,17 @@
--code-bg: #0d1117; --code-bg: #0d1117;
} }
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } *,
html { scroll-behavior: smooth; } *::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body { body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
@ -43,7 +53,11 @@
font-size: 14px; font-size: 14px;
} }
.container { max-width: 900px; margin: 0 auto; padding: 0 1.5rem; } .container {
max-width: 900px;
margin: 0 auto;
padding: 0 1.5rem;
}
/* Header */ /* Header */
.header { .header {
@ -90,7 +104,11 @@
color: white; color: white;
} }
.brand-text { font-size: 1rem; font-weight: 700; color: var(--text-primary); } .brand-text {
font-size: 1rem;
font-weight: 700;
color: var(--text-primary);
}
.header-title { .header-title {
font-size: 1.25rem; font-size: 1.25rem;
@ -137,19 +155,24 @@
} }
.score-ring svg { .score-ring svg {
transform: rotate(-90deg); transform: rotate(90deg) scaleY(-1);
width: 56px; width: 56px;
height: 56px; height: 56px;
} }
.score-ring-bg { fill: none; stroke: var(--border); stroke-width: 5; } .score-ring-bg {
fill: none;
stroke: var(--border);
stroke-width: 5;
}
.score-ring-progress { .score-ring-progress {
fill: none; fill: none;
stroke: #ef4444; stroke: #ef4444;
stroke-width: 5; stroke-width: 5;
stroke-linecap: round; stroke-linecap: round;
stroke-dasharray: 251.32741228718345; stroke-dasharray: 144.51326206513048;
stroke-dashoffset: 161.35219868837177; stroke-dashoffset: 92.48848772168351;
filter: drop-shadow(0 0 4px #ef444440); filter: drop-shadow(0 0 4px #ef444440);
} }
@ -219,15 +242,48 @@
font-weight: 700; font-weight: 700;
} }
.stat-card-icon.critical { background: var(--critical-bg); color: var(--critical); } .stat-card-icon.critical {
.stat-card-icon.high { background: var(--high-bg); color: var(--high); } background: var(--critical-bg);
.stat-card-icon.total { background: var(--info-bg); color: var(--info); } color: var(--critical);
.stat-card-icon.verified { background: rgba(16,185,129,0.1); color: var(--success); } }
.stat-card-label { font-size: 0.6rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; } .stat-card-icon.high {
.stat-card-value { font-size: 1.25rem; font-weight: 700; color: var(--text-primary); font-family: 'SF Mono', monospace; line-height: 1; } background: var(--high-bg);
.stat-card-value.critical { color: var(--critical); } color: var(--high);
.stat-card-value.high { color: var(--high); } }
.stat-card-icon.total {
background: var(--info-bg);
color: var(--info);
}
.stat-card-icon.verified {
background: rgba(16, 185, 129, 0.1);
color: var(--success);
}
.stat-card-label {
font-size: 0.6rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.stat-card-value {
font-size: 1.25rem;
font-weight: 700;
color: var(--text-primary);
font-family: 'SF Mono', monospace;
line-height: 1;
}
.stat-card-value.critical {
color: var(--critical);
}
.stat-card-value.high {
color: var(--high);
}
/* Severity Bar */ /* Severity Bar */
.severity-section { .severity-section {
@ -257,11 +313,25 @@
background: var(--border); background: var(--border);
} }
.severity-segment { height: 100%; } .severity-segment {
.severity-segment.critical { background: var(--critical); } height: 100%;
.severity-segment.high { background: var(--high); } }
.severity-segment.medium { background: var(--medium); }
.severity-segment.low { background: var(--low); } .severity-segment.critical {
background: var(--critical);
}
.severity-segment.high {
background: var(--high);
}
.severity-segment.medium {
background: var(--medium);
}
.severity-segment.low {
background: var(--low);
}
.severity-legend { .severity-legend {
display: flex; display: flex;
@ -276,11 +346,27 @@
color: var(--text-muted); color: var(--text-muted);
} }
.severity-dot { width: 6px; height: 6px; border-radius: 50%; } .severity-dot {
.severity-dot.critical { background: var(--critical); } width: 6px;
.severity-dot.high { background: var(--high); } height: 6px;
.severity-dot.medium { background: var(--medium); } border-radius: 50%;
.severity-dot.low { background: var(--low); } }
.severity-dot.critical {
background: var(--critical);
}
.severity-dot.high {
background: var(--high);
}
.severity-dot.medium {
background: var(--medium);
}
.severity-dot.low {
background: var(--low);
}
/* Main Content */ /* Main Content */
.main-content { .main-content {
@ -296,7 +382,10 @@
} }
/* Typography */ /* Typography */
h1, h2, h3, h4 { h1,
h2,
h3,
h4 {
color: var(--text-primary); color: var(--text-primary);
font-weight: 600; font-weight: 600;
letter-spacing: -0.01em; letter-spacing: -0.01em;
@ -312,7 +401,11 @@
gap: 0.5rem; gap: 0.5rem;
} }
h1::before { content: "§"; color: var(--accent); font-weight: 400; } h1::before {
content: "§";
color: var(--accent);
font-weight: 400;
}
h2 { h2 {
font-size: 1.1rem; font-size: 1.1rem;
@ -321,7 +414,13 @@
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
h2::before { content: "//"; color: var(--accent); margin-right: 0.35rem; font-weight: 400; opacity: 0.7; } h2::before {
content: "//";
color: var(--accent);
margin-right: 0.35rem;
font-weight: 400;
opacity: 0.7;
}
h3 { h3 {
font-size: 1rem; font-size: 1rem;
@ -330,9 +429,16 @@
border-left: 2px solid var(--accent); border-left: 2px solid var(--accent);
} }
h4 { font-size: 0.9rem; margin: 0.75rem 0 0.35rem; color: var(--text-secondary); } h4 {
font-size: 0.9rem;
margin: 0.75rem 0 0.35rem;
color: var(--text-secondary);
}
p { margin-bottom: 0.6rem; font-size: 0.875rem; } p {
margin-bottom: 0.6rem;
font-size: 0.875rem;
}
/* Code Blocks */ /* Code Blocks */
pre { pre {
@ -369,7 +475,9 @@
color: #e2e8f0; color: #e2e8f0;
} }
p code, li code, td code { p code,
li code,
td code {
background: var(--bg-tertiary); background: var(--bg-tertiary);
color: var(--accent); color: var(--accent);
padding: 0.15em 0.35em; padding: 0.15em 0.35em;
@ -406,13 +514,28 @@
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
tr:last-child td { border-bottom: none; } tr:last-child td {
tr:hover td { background: rgba(255, 255, 255, 0.02); } border-bottom: none;
}
tr:hover td {
background: rgba(255, 255, 255, 0.02);
}
/* Lists */ /* Lists */
ul, ol { margin: 0.5rem 0 0.5rem 1.25rem; } ul,
li { margin-bottom: 0.25rem; font-size: 0.875rem; } ol {
li::marker { color: var(--accent); } margin: 0.5rem 0 0.5rem 1.25rem;
}
li {
margin-bottom: 0.25rem;
font-size: 0.875rem;
}
li::marker {
color: var(--accent);
}
/* Blockquotes */ /* Blockquotes */
blockquote { blockquote {
@ -424,11 +547,19 @@
font-size: 0.85rem; font-size: 0.85rem;
} }
blockquote p:last-child { margin-bottom: 0; } blockquote p:last-child {
margin-bottom: 0;
}
/* Links */ /* Links */
a { color: var(--accent); text-decoration: none; } a {
a:hover { text-decoration: underline; } color: var(--accent);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* HR */ /* HR */
hr { hr {
@ -438,8 +569,14 @@
margin: 1.5rem 0; margin: 1.5rem 0;
} }
strong { color: var(--text-primary); font-weight: 600; } strong {
em { color: var(--text-muted); } color: var(--text-primary);
font-weight: 600;
}
em {
color: var(--text-muted);
}
/* Footer */ /* Footer */
.report-footer { .report-footer {
@ -481,14 +618,41 @@
/* Responsive */ /* Responsive */
@media (max-width: 768px) { @media (max-width: 768px) {
.container { padding: 0 1rem; } .container {
.stats-grid { flex-direction: column; gap: 0.75rem; } padding: 0 1rem;
.score-ring-container { width: 100%; justify-content: center; } }
.stats-cards { flex-wrap: wrap; }
.stat-card { min-width: calc(50% - 0.25rem); } .stats-grid {
.severity-bar-wrap { flex-direction: column; align-items: stretch; gap: 0.5rem; } flex-direction: column;
.severity-legend { justify-content: center; } gap: 0.75rem;
.content-wrapper { padding: 1rem; } }
.score-ring-container {
width: 100%;
justify-content: center;
}
.stats-cards {
flex-wrap: wrap;
}
.stat-card {
min-width: calc(50% - 0.25rem);
}
.severity-bar-wrap {
flex-direction: column;
align-items: stretch;
gap: 0.5rem;
}
.severity-legend {
justify-content: center;
}
.content-wrapper {
padding: 1rem;
}
} }
/* Print */ /* Print */
@ -505,16 +669,42 @@
--border: #e2e8f0; --border: #e2e8f0;
--code-bg: #f8fafc; --code-bg: #f8fafc;
} }
body { background: white; font-size: 11pt; }
.header::before { display: none; } body {
.content-wrapper { border: none; padding: 0; } background: white;
pre { break-inside: avoid; } font-size: 11pt;
code { color: #1e293b; } }
p code, li code { background: #f1f5f9; color: #c2410c; }
a { color: #2563eb; } .header::before {
display: none;
}
.content-wrapper {
border: none;
padding: 0;
}
pre {
break-inside: avoid;
}
code {
color: #1e293b;
}
p code,
li code {
background: #f1f5f9;
color: #c2410c;
}
a {
color: #2563eb;
}
} }
</style> </style>
</head> </head>
<body> <body>
<header class="header"> <header class="header">
<div class="container"> <div class="container">
@ -592,10 +782,18 @@
</div> </div>
<div class="severity-legend"> <div class="severity-legend">
<div class="severity-legend-item"><div class="severity-dot critical"></div>2</div> <div class="severity-legend-item">
<div class="severity-legend-item"><div class="severity-dot high"></div>3</div> <div class="severity-dot critical"></div>2
<div class="severity-legend-item"><div class="severity-dot medium"></div>2</div> </div>
<div class="severity-legend-item"><div class="severity-dot low"></div>1</div> <div class="severity-legend-item">
<div class="severity-dot high"></div>3
</div>
<div class="severity-legend-item">
<div class="severity-dot medium"></div>2
</div>
<div class="severity-legend-item">
<div class="severity-dot low"></div>1
</div>
</div> </div>
</div> </div>
</div> </div>
@ -614,7 +812,8 @@
<th>内容</th> <th>内容</th>
</tr> </tr>
</thead> </thead>
<tbody><tr> <tbody>
<tr>
<td><strong>项目名称</strong></td> <td><strong>项目名称</strong></td>
<td>VulnWebApp - 安全演示项目</td> <td>VulnWebApp - 安全演示项目</td>
</tr> </tr>
@ -634,10 +833,12 @@
<td><strong>耗时</strong></td> <td><strong>耗时</strong></td>
<td>13.0 分钟</td> <td>13.0 分钟</td>
</tr> </tr>
</tbody></table> </tbody>
</table>
<h2>执行摘要</h2> <h2>执行摘要</h2>
<p><strong>安全评分: 35/100</strong> [未通过] <p><strong>安全评分: 35/100</strong> [未通过]
<em>严重 - 需要立即进行修复</em></p> <em>严重 - 需要立即进行修复</em>
</p>
<h3>漏洞发现概览</h3> <h3>漏洞发现概览</h3>
<table> <table>
<thead> <thead>
@ -647,7 +848,8 @@
<th>已验证</th> <th>已验证</th>
</tr> </tr>
</thead> </thead>
<tbody><tr> <tbody>
<tr>
<td><strong>严重 (CRITICAL)</strong></td> <td><strong>严重 (CRITICAL)</strong></td>
<td>2</td> <td>2</td>
<td>2</td> <td>2</td>
@ -672,7 +874,8 @@
<td>8</td> <td>8</td>
<td>6</td> <td>6</td>
</tr> </tr>
</tbody></table> </tbody>
</table>
<h3>审计指标</h3> <h3>审计指标</h3>
<ul> <ul>
<li><strong>分析文件数:</strong> 48 / 48</li> <li><strong>分析文件数:</strong> 48 / 48</li>
@ -1049,4 +1252,5 @@ app.secret_key = os.environ.get(&#39;SECRET_KEY&#39;, os.urandom(24))
</div> </div>
</footer> </footer>
</body> </body>
</html> </html>

View File

@ -866,8 +866,8 @@ export const ReportExportDialog = memo(function ReportExportDialog({
}; };
const scoreInfo = getScoreGrade(score); const scoreInfo = getScoreGrade(score);
// SVG 圆环进度条 // SVG 圆环进度条 (逆时针方向)
const circumference = 2 * Math.PI * 40; // r=40 (smaller) const circumference = 2 * Math.PI * 23; // r=23 matches SVG circle radius
const strokeDashoffset = circumference - (score / 100) * circumference; const strokeDashoffset = circumference - (score / 100) * circumference;
return `<!DOCTYPE html> return `<!DOCTYPE html>
@ -1012,8 +1012,8 @@ export const ReportExportDialog = memo(function ReportExportDialog({
transform: rotate(-90deg); transform: rotate(-90deg);
width: 56px; width: 56px;
height: 56px; height: 56px;
display: block;
} }
.score-ring-bg { fill: none; stroke: var(--border); stroke-width: 5; } .score-ring-bg { fill: none; stroke: var(--border); stroke-width: 5; }
.score-ring-progress { .score-ring-progress {
fill: none; fill: none;