siluzan-tso-cli 1.1.26 → 1.1.27-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.js +721 -102
- package/dist/skill/AGENTS.md +3 -1
- package/dist/skill/SKILL.md +5 -4
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/market-analysis-rules.md +134 -0
- package/dist/skill/references/README.md +3 -1
- package/dist/skill/references/accounts/accounts.md +18 -4
- package/dist/skill/references/accounts/finance.md +32 -32
- package/dist/skill/references/accounts/open-account-google-ui.md +1 -1
- package/dist/skill/references/analytics/market-analysis-guide.md +118 -0
- package/dist/skill/references/analytics/rag.md +1 -1
- package/dist/skill/references/analytics/reporting.md +5 -5
- package/dist/skill/references/analytics/website-diagnosis-guide.md +3 -3
- package/dist/skill/references/core/agent-conventions.md +1 -1
- package/dist/skill/references/core/cli-enums.md +140 -0
- package/dist/skill/references/core/playbooks.md +35 -2
- package/dist/skill/references/core/setup.md +5 -5
- package/dist/skill/references/core/skill-authoring.md +1 -1
- package/dist/skill/references/core/tips.md +1 -1
- package/dist/skill/references/core/workflows.md +4 -4
- package/dist/skill/references/misc/tso-home.md +2 -2
- package/dist/skill/report-templates/market-analysis-report.md +40 -0
- package/dist/skill/report-templates/website-diagnosis-report.html +1869 -420
- package/dist/skill/report-templates/website-diagnosis-report.md +11 -1
- package/dist/skill/report-templates/website-diagnosis-report.runtime.js +0 -0
- package/dist/skill/scripts/install.ps1 +3 -3
- package/dist/skill/scripts/install.sh +3 -3
- package/eval/cases/no-legacy-json-flag.scenario.json +1 -1
- package/package.json +1 -1
|
@@ -1,100 +1,1191 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
2
|
<!--
|
|
3
|
-
网站诊断报告 —
|
|
3
|
+
网站诊断报告 — 对齐 MarkAI WebsiteAnalysisReport/v3(ChatContent → WebsiteGuide)
|
|
4
4
|
|
|
5
|
-
数据:window.__WEBSITE_DIAGNOSIS__
|
|
5
|
+
数据:window.__WEBSITE_DIAGNOSIS__
|
|
6
6
|
生成:siluzan-tso website-diagnosis render --data ./diagnosis.json [--collect ./collect.json]
|
|
7
|
-
或 Agent 将诊断 JSON 写入下方锚点后保存为本文件。
|
|
8
7
|
-->
|
|
9
8
|
<html lang="zh-CN">
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.
|
|
20
|
-
.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
9
|
+
|
|
10
|
+
<head>
|
|
11
|
+
<meta charset="UTF-8" />
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
13
|
+
<title>网站诊断报告</title>
|
|
14
|
+
<script>
|
|
15
|
+
(function () {
|
|
16
|
+
var k = "wd-report-theme";
|
|
17
|
+
var s = localStorage.getItem(k);
|
|
18
|
+
var dark = s === "dark" || (s !== "light" && window.matchMedia("(prefers-color-scheme: dark)").matches);
|
|
19
|
+
if (dark) document.documentElement.setAttribute("data-theme", "dark");
|
|
20
|
+
})();
|
|
21
|
+
</script>
|
|
22
|
+
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js"></script>
|
|
23
|
+
<style>
|
|
24
|
+
* {
|
|
25
|
+
box-sizing: border-box;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
:root {
|
|
29
|
+
color-scheme: light;
|
|
30
|
+
--bg-page: #f5f7fa;
|
|
31
|
+
--surface: #ffffff;
|
|
32
|
+
--surface-muted: #f7f8fa;
|
|
33
|
+
--text-primary: #1d2129;
|
|
34
|
+
--text-secondary: #4e5969;
|
|
35
|
+
--text-muted: #86909c;
|
|
36
|
+
--border: #e5e6eb;
|
|
37
|
+
--border-light: #ebeef5;
|
|
38
|
+
--link: #165dff;
|
|
39
|
+
--accent: #165dff;
|
|
40
|
+
--shadow: rgba(0, 0, 0, 0.08);
|
|
41
|
+
--shadow-hover: rgba(0, 0, 0, 0.1);
|
|
42
|
+
--marketing-bg: linear-gradient(135deg, #f0f5ff 0%, #e8f3ff 100%);
|
|
43
|
+
--ring-track: #e5e6eb;
|
|
44
|
+
--table-head-bg: #f5f7fa;
|
|
45
|
+
--table-head-text: #909399;
|
|
46
|
+
--table-cell-text: #606266;
|
|
47
|
+
--table-title: #333333;
|
|
48
|
+
--priority-high-bg: #ffece8;
|
|
49
|
+
--priority-medium-bg: #fff3e8;
|
|
50
|
+
--priority-low-bg: #e8f8f2;
|
|
51
|
+
--lt-blue-bg: #eef6ff;
|
|
52
|
+
--lt-orange-bg: #fff7ec;
|
|
53
|
+
--lt-green-bg: #eafdf4;
|
|
54
|
+
--lt-purple-bg: #f4f2ff;
|
|
55
|
+
--status-excellent-bg: #e8f8f2;
|
|
56
|
+
--status-good-bg: #e8f3ff;
|
|
57
|
+
--status-normal-bg: #fff3e8;
|
|
58
|
+
--status-poor-bg: #ffece8;
|
|
59
|
+
--btn-theme-bg: #ffffff;
|
|
60
|
+
--btn-theme-border: #e5e6eb;
|
|
61
|
+
--btn-theme-text: #4e5969;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
html[data-theme="dark"] {
|
|
65
|
+
color-scheme: dark;
|
|
66
|
+
--bg-page: #0f1419;
|
|
67
|
+
--surface: #1a1f26;
|
|
68
|
+
--surface-muted: #232a33;
|
|
69
|
+
--text-primary: #e8eaed;
|
|
70
|
+
--text-secondary: #a8b0bb;
|
|
71
|
+
--text-muted: #7a8490;
|
|
72
|
+
--border: #2e3742;
|
|
73
|
+
--border-light: #2a323d;
|
|
74
|
+
--link: #6ba1ff;
|
|
75
|
+
--accent: #4d8dff;
|
|
76
|
+
--shadow: rgba(0, 0, 0, 0.35);
|
|
77
|
+
--shadow-hover: rgba(0, 0, 0, 0.45);
|
|
78
|
+
--marketing-bg: linear-gradient(135deg, #1a2438 0%, #152030 100%);
|
|
79
|
+
--ring-track: #3a4452;
|
|
80
|
+
--table-head-bg: #232a33;
|
|
81
|
+
--table-head-text: #8b949e;
|
|
82
|
+
--table-cell-text: #a8b0bb;
|
|
83
|
+
--table-title: #c9d1d9;
|
|
84
|
+
--priority-high-bg: #3d2224;
|
|
85
|
+
--priority-medium-bg: #3d2e1a;
|
|
86
|
+
--priority-low-bg: #1a3328;
|
|
87
|
+
--lt-blue-bg: #1a2840;
|
|
88
|
+
--lt-orange-bg: #332818;
|
|
89
|
+
--lt-green-bg: #1a3328;
|
|
90
|
+
--lt-purple-bg: #2a2240;
|
|
91
|
+
--status-excellent-bg: #1a3328;
|
|
92
|
+
--status-good-bg: #1a2840;
|
|
93
|
+
--status-normal-bg: #332818;
|
|
94
|
+
--status-poor-bg: #3d2224;
|
|
95
|
+
--btn-theme-bg: #232a33;
|
|
96
|
+
--btn-theme-border: #2e3742;
|
|
97
|
+
--btn-theme-text: #a8b0bb;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
body {
|
|
101
|
+
margin: 0;
|
|
102
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
|
|
103
|
+
"PingFang SC", "Microsoft YaHei", sans-serif;
|
|
104
|
+
background: var(--bg-page);
|
|
105
|
+
color: var(--text-primary);
|
|
106
|
+
transition: background 0.2s ease, color 0.2s ease;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
a {
|
|
110
|
+
color: var(--link);
|
|
111
|
+
text-decoration: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
a:hover {
|
|
115
|
+
text-decoration: underline;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#app {
|
|
119
|
+
min-height: 100vh;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* common.scss */
|
|
123
|
+
.report-container {
|
|
124
|
+
background: var(--bg-page);
|
|
125
|
+
container-type: inline-size;
|
|
126
|
+
padding: 24px 16px 40px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.report-content {
|
|
130
|
+
max-width: 1200px;
|
|
131
|
+
margin: 0 auto;
|
|
132
|
+
background: var(--surface);
|
|
133
|
+
border-radius: 8px;
|
|
134
|
+
padding: 28px;
|
|
135
|
+
box-shadow: 0 2px 8px var(--shadow);
|
|
136
|
+
transition: background 0.2s ease, box-shadow 0.2s ease;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.report-section {
|
|
140
|
+
margin-bottom: 40px;
|
|
141
|
+
background: var(--surface);
|
|
142
|
+
border-radius: 12px;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.report-section:last-child {
|
|
146
|
+
margin-bottom: 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.section-header {
|
|
150
|
+
margin-bottom: 20px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.section-header h2 {
|
|
154
|
+
font-size: 24px;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
color: var(--text-primary);
|
|
157
|
+
margin: 0;
|
|
158
|
+
padding-bottom: 12px;
|
|
159
|
+
display: inline-block;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.marketing-text {
|
|
163
|
+
padding: 16px;
|
|
164
|
+
background: var(--marketing-bg);
|
|
165
|
+
border-left: 4px solid var(--accent);
|
|
166
|
+
border-radius: 4px;
|
|
167
|
+
margin-bottom: 24px;
|
|
168
|
+
font-size: 15px;
|
|
169
|
+
line-height: 1.8;
|
|
170
|
+
color: var(--text-primary);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.marketing-text p {
|
|
174
|
+
margin: 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.marketing-text strong {
|
|
178
|
+
color: var(--accent);
|
|
179
|
+
font-size: 20px;
|
|
180
|
+
font-weight: 700;
|
|
181
|
+
margin: 0 4px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* ReportHeader */
|
|
185
|
+
.report-header {
|
|
186
|
+
margin-bottom: 32px;
|
|
187
|
+
padding-bottom: 24px;
|
|
188
|
+
border-bottom: 2px solid var(--border);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.header-title-wrapper {
|
|
192
|
+
display: flex;
|
|
193
|
+
justify-content: space-between;
|
|
194
|
+
align-items: center;
|
|
195
|
+
margin-bottom: 16px;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.main-title {
|
|
199
|
+
flex: 1;
|
|
200
|
+
font-size: 32px;
|
|
201
|
+
font-weight: 700;
|
|
202
|
+
color: var(--text-primary);
|
|
203
|
+
margin: 0;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.header-actions {
|
|
207
|
+
display: flex;
|
|
208
|
+
align-items: center;
|
|
209
|
+
gap: 8px;
|
|
210
|
+
flex-shrink: 0;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.btn-theme {
|
|
214
|
+
display: flex;
|
|
215
|
+
align-items: center;
|
|
216
|
+
gap: 6px;
|
|
217
|
+
padding: 10px 16px;
|
|
218
|
+
background: var(--btn-theme-bg);
|
|
219
|
+
border: 1px solid var(--btn-theme-border);
|
|
220
|
+
border-radius: 6px;
|
|
221
|
+
color: var(--btn-theme-text);
|
|
222
|
+
font-size: 14px;
|
|
223
|
+
font-weight: 500;
|
|
224
|
+
cursor: pointer;
|
|
225
|
+
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.btn-theme:hover {
|
|
229
|
+
border-color: var(--accent);
|
|
230
|
+
color: var(--accent);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.btn-export {
|
|
234
|
+
display: flex;
|
|
235
|
+
align-items: center;
|
|
236
|
+
gap: 6px;
|
|
237
|
+
padding: 10px 20px;
|
|
238
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
239
|
+
border: none;
|
|
240
|
+
border-radius: 6px;
|
|
241
|
+
color: #ffffff;
|
|
242
|
+
font-size: 14px;
|
|
243
|
+
font-weight: 500;
|
|
244
|
+
cursor: pointer;
|
|
245
|
+
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.btn-export:hover {
|
|
249
|
+
transform: translateY(-2px);
|
|
250
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.header-info {
|
|
254
|
+
display: flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
gap: 16px;
|
|
257
|
+
flex-wrap: wrap;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.info-item {
|
|
261
|
+
font-size: 15px;
|
|
262
|
+
color: var(--text-secondary);
|
|
263
|
+
font-weight: 500;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.info-separator {
|
|
267
|
+
color: var(--border);
|
|
268
|
+
font-size: 15px;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.website-url {
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
color: var(--link) !important;
|
|
274
|
+
word-break: break-all;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* HealthOverview */
|
|
278
|
+
.health-overview {
|
|
279
|
+
display: flex;
|
|
280
|
+
gap: 32px;
|
|
281
|
+
margin-bottom: 24px;
|
|
282
|
+
align-items: center;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.website-score-wrapper {
|
|
286
|
+
display: flex;
|
|
287
|
+
align-items: center;
|
|
288
|
+
flex: 1;
|
|
289
|
+
justify-content: center;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.score-ring-wrapper {
|
|
293
|
+
flex-shrink: 0;
|
|
294
|
+
display: flex;
|
|
295
|
+
flex-direction: column;
|
|
296
|
+
align-items: center;
|
|
297
|
+
position: relative;
|
|
298
|
+
padding: 20px;
|
|
299
|
+
width: 250px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.score-center {
|
|
303
|
+
position: absolute;
|
|
304
|
+
top: 110px;
|
|
305
|
+
left: 50%;
|
|
306
|
+
transform: translate(-50%, -50%);
|
|
307
|
+
text-align: center;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.score-number {
|
|
311
|
+
font-size: 42px;
|
|
312
|
+
font-weight: 700;
|
|
313
|
+
color: var(--text-primary);
|
|
314
|
+
line-height: 1;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.score-total {
|
|
318
|
+
font-size: 16px;
|
|
319
|
+
color: var(--text-muted);
|
|
320
|
+
margin-top: 4px;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.score-rating {
|
|
324
|
+
margin-top: 12px;
|
|
325
|
+
font-size: 20px;
|
|
326
|
+
font-weight: 600;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.score-desc {
|
|
330
|
+
margin-top: 8px;
|
|
331
|
+
font-size: 14px;
|
|
332
|
+
color: var(--text-secondary);
|
|
333
|
+
text-align: center;
|
|
334
|
+
max-width: 200px;
|
|
335
|
+
line-height: 1.5;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.divider-line {
|
|
339
|
+
width: 1px;
|
|
340
|
+
height: 300px;
|
|
341
|
+
background-color: var(--border);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.radar-chart {
|
|
345
|
+
flex: 1;
|
|
346
|
+
background: var(--surface);
|
|
347
|
+
border-radius: 8px;
|
|
348
|
+
padding: 16px;
|
|
349
|
+
display: flex;
|
|
350
|
+
flex-direction: column;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.radar-chart-title {
|
|
354
|
+
text-align: center;
|
|
355
|
+
font-size: 14px;
|
|
356
|
+
color: var(--text-secondary);
|
|
357
|
+
margin-top: 12px;
|
|
358
|
+
font-weight: 500;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* RiskMap */
|
|
362
|
+
.risk-map-content {
|
|
363
|
+
display: flex;
|
|
364
|
+
gap: 24px;
|
|
365
|
+
align-items: flex-start;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.bar-chart-wrapper {
|
|
369
|
+
flex-shrink: 0;
|
|
370
|
+
width: 40%;
|
|
371
|
+
height: 294px;
|
|
372
|
+
background: var(--surface);
|
|
373
|
+
border: 1px solid var(--border);
|
|
374
|
+
border-radius: 8px;
|
|
375
|
+
padding: 16px;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.risk-table-wrapper {
|
|
379
|
+
flex: 1;
|
|
380
|
+
height: 327px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.risk-table-title {
|
|
384
|
+
height: 24px;
|
|
385
|
+
font-size: 16px;
|
|
386
|
+
font-weight: bold;
|
|
387
|
+
text-align: right;
|
|
388
|
+
color: var(--table-title);
|
|
389
|
+
margin: 0 0 8px;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.risk-table {
|
|
393
|
+
width: 100%;
|
|
394
|
+
border-collapse: collapse;
|
|
395
|
+
font-size: 14px;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.risk-table th,
|
|
399
|
+
.risk-table td {
|
|
400
|
+
border: 1px solid var(--border-light);
|
|
401
|
+
padding: 10px 12px;
|
|
402
|
+
color: var(--table-cell-text);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.risk-table th {
|
|
406
|
+
background: var(--table-head-bg);
|
|
407
|
+
color: var(--table-head-text);
|
|
408
|
+
font-weight: 500;
|
|
409
|
+
text-align: left;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.risk-table .tc {
|
|
413
|
+
text-align: center;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/* ModuleDetail */
|
|
417
|
+
.module-detail {
|
|
418
|
+
margin-bottom: 32px;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.module-detail:last-child {
|
|
422
|
+
margin-bottom: 0;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.module-header {
|
|
426
|
+
display: flex;
|
|
427
|
+
justify-content: space-between;
|
|
428
|
+
align-items: center;
|
|
429
|
+
margin-bottom: 16px;
|
|
430
|
+
padding: 16px;
|
|
431
|
+
background: var(--surface-muted);
|
|
432
|
+
border-radius: 8px;
|
|
433
|
+
gap: 16px;
|
|
434
|
+
flex-wrap: wrap;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.module-header-left {
|
|
438
|
+
display: flex;
|
|
439
|
+
align-items: center;
|
|
440
|
+
gap: 16px;
|
|
441
|
+
flex: 1;
|
|
442
|
+
flex-wrap: wrap;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.module-title {
|
|
446
|
+
font-size: 18px;
|
|
447
|
+
font-weight: 600;
|
|
448
|
+
color: var(--text-primary);
|
|
449
|
+
margin: 0;
|
|
450
|
+
padding-left: 12px;
|
|
451
|
+
border-left: 4px solid #165dff;
|
|
452
|
+
white-space: nowrap;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.module-description {
|
|
456
|
+
font-size: 14px;
|
|
457
|
+
color: var(--text-secondary);
|
|
458
|
+
line-height: 1.5;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.module-score-info {
|
|
462
|
+
font-size: 14px;
|
|
463
|
+
color: var(--text-secondary);
|
|
464
|
+
white-space: nowrap;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.module-score-info .score-value {
|
|
468
|
+
font-size: 20px;
|
|
469
|
+
font-weight: 700;
|
|
470
|
+
color: #35bd4b;
|
|
471
|
+
margin: 0 4px;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.module-score-info .total-score-value {
|
|
475
|
+
font-weight: 600;
|
|
476
|
+
color: var(--text-primary);
|
|
477
|
+
margin: 0 4px;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.module-items-grid {
|
|
481
|
+
display: grid;
|
|
482
|
+
gap: 20px;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.module-item-card {
|
|
486
|
+
background: var(--surface);
|
|
487
|
+
border: 1px solid var(--border);
|
|
488
|
+
border-radius: 12px;
|
|
489
|
+
padding: 20px;
|
|
490
|
+
display: flex;
|
|
491
|
+
flex-direction: column;
|
|
492
|
+
gap: 16px;
|
|
493
|
+
min-width: 0;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.module-item-card:hover {
|
|
497
|
+
box-shadow: 0 4px 12px var(--shadow-hover);
|
|
498
|
+
transform: translateY(-2px);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.module-item-card .card-header {
|
|
502
|
+
display: flex;
|
|
503
|
+
justify-content: space-between;
|
|
504
|
+
align-items: flex-start;
|
|
505
|
+
gap: 12px;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.item-title {
|
|
509
|
+
font-size: 16px;
|
|
510
|
+
font-weight: 600;
|
|
511
|
+
color: var(--text-primary);
|
|
512
|
+
margin: 0;
|
|
513
|
+
flex: 1;
|
|
514
|
+
line-height: 1.5;
|
|
515
|
+
word-break: break-word;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.status-badge {
|
|
519
|
+
flex-shrink: 0;
|
|
520
|
+
padding: 4px 12px;
|
|
521
|
+
border-radius: 4px;
|
|
522
|
+
font-size: 12px;
|
|
523
|
+
font-weight: 500;
|
|
524
|
+
white-space: nowrap;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.item-description {
|
|
528
|
+
font-size: 14px;
|
|
529
|
+
color: var(--text-secondary);
|
|
530
|
+
line-height: 1.6;
|
|
531
|
+
margin: 0;
|
|
532
|
+
word-break: break-word;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.score-info {
|
|
536
|
+
display: flex;
|
|
537
|
+
justify-content: space-between;
|
|
538
|
+
align-items: center;
|
|
539
|
+
margin-bottom: 8px;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.score-label {
|
|
543
|
+
font-size: 14px;
|
|
544
|
+
color: var(--text-muted);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.score-percentage {
|
|
548
|
+
font-size: 16px;
|
|
549
|
+
font-weight: 600;
|
|
550
|
+
color: var(--text-primary);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.progress-bar {
|
|
554
|
+
width: 100%;
|
|
555
|
+
height: 12px;
|
|
556
|
+
background: var(--ring-track);
|
|
557
|
+
border-radius: 10px;
|
|
558
|
+
overflow: hidden;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.progress-fill {
|
|
562
|
+
height: 100%;
|
|
563
|
+
border-radius: 12px;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.suggestion-box {
|
|
567
|
+
display: flex;
|
|
568
|
+
align-items: flex-start;
|
|
569
|
+
gap: 8px;
|
|
570
|
+
padding: 12px;
|
|
571
|
+
border-left: 3px solid #ff7d00;
|
|
572
|
+
border-radius: 4px;
|
|
573
|
+
margin-top: 4px;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.suggestion-text {
|
|
577
|
+
font-size: 13px;
|
|
578
|
+
color: var(--text-secondary);
|
|
579
|
+
line-height: 1.5;
|
|
580
|
+
word-break: break-word;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/* LoadingSpeed */
|
|
584
|
+
.loading-speed-section {
|
|
585
|
+
margin-bottom: 24px;
|
|
586
|
+
padding: 24px;
|
|
587
|
+
background: var(--surface);
|
|
588
|
+
border: 1px solid var(--border);
|
|
589
|
+
border-radius: 12px;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.loading-speed-header {
|
|
593
|
+
display: flex;
|
|
594
|
+
justify-content: space-between;
|
|
595
|
+
align-items: center;
|
|
596
|
+
margin-bottom: 8px;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.loading-speed-title {
|
|
600
|
+
font-size: 16px;
|
|
601
|
+
font-weight: 600;
|
|
602
|
+
color: var(--text-primary);
|
|
603
|
+
margin: 0;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.loading-speed-status {
|
|
607
|
+
padding: 4px 12px;
|
|
608
|
+
border-radius: 4px;
|
|
609
|
+
font-size: 12px;
|
|
610
|
+
font-weight: 500;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.loading-speed-status.status-excellent {
|
|
614
|
+
background: var(--status-excellent-bg);
|
|
615
|
+
color: #00b42a;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.loading-speed-status.status-good {
|
|
619
|
+
background: var(--status-good-bg);
|
|
620
|
+
color: #165dff;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.loading-speed-status.status-normal {
|
|
624
|
+
background: var(--status-normal-bg);
|
|
625
|
+
color: #ff7d00;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.loading-speed-status.status-poor {
|
|
629
|
+
background: var(--status-poor-bg);
|
|
630
|
+
color: #f53f3f;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.loading-speed-desc {
|
|
634
|
+
font-size: 14px;
|
|
635
|
+
color: var(--text-secondary);
|
|
636
|
+
margin: 0 0 24px;
|
|
637
|
+
line-height: 1.6;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.loading-speed-charts {
|
|
641
|
+
display: flex;
|
|
642
|
+
justify-content: space-around;
|
|
643
|
+
gap: 40px;
|
|
644
|
+
margin-bottom: 24px;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.speed-chart-item {
|
|
648
|
+
flex: 1;
|
|
649
|
+
display: flex;
|
|
650
|
+
flex-direction: column;
|
|
651
|
+
align-items: center;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.chart-title {
|
|
655
|
+
font-size: 14px;
|
|
656
|
+
font-weight: 500;
|
|
657
|
+
color: var(--text-primary);
|
|
658
|
+
margin-bottom: 16px;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.speed-details {
|
|
662
|
+
width: 100%;
|
|
663
|
+
display: flex;
|
|
664
|
+
flex-direction: column;
|
|
665
|
+
gap: 8px;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.speed-detail-item {
|
|
669
|
+
font-size: 13px;
|
|
670
|
+
color: var(--text-secondary);
|
|
671
|
+
text-align: center;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.speed-value {
|
|
675
|
+
font-weight: 600;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.speed-legend {
|
|
679
|
+
display: flex;
|
|
680
|
+
justify-content: center;
|
|
681
|
+
gap: 24px;
|
|
682
|
+
padding-top: 16px;
|
|
683
|
+
border-top: 1px solid var(--border);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.legend-item {
|
|
687
|
+
display: flex;
|
|
688
|
+
align-items: center;
|
|
689
|
+
gap: 8px;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.legend-icon {
|
|
693
|
+
width: 12px;
|
|
694
|
+
height: 12px;
|
|
695
|
+
border-radius: 2px;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.legend-text {
|
|
699
|
+
font-size: 13px;
|
|
700
|
+
color: var(--text-secondary);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/* PriorityPlan */
|
|
704
|
+
.priority-columns {
|
|
705
|
+
display: grid;
|
|
706
|
+
grid-template-columns: repeat(3, 1fr);
|
|
707
|
+
gap: 24px;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.priority-column {
|
|
711
|
+
display: flex;
|
|
712
|
+
flex-direction: column;
|
|
713
|
+
padding: 20px;
|
|
714
|
+
border-radius: 8px;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
.priority-high {
|
|
718
|
+
background: var(--priority-high-bg);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
.priority-high .priority-title {
|
|
722
|
+
color: #991b25;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.priority-high .priority-badge {
|
|
726
|
+
color: #991b25;
|
|
727
|
+
border: 1px solid #991b25;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.priority-high .item-bullet,
|
|
731
|
+
.priority-high .impact-icon,
|
|
732
|
+
.priority-high .impact-text {
|
|
733
|
+
color: #f53f3f !important;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.priority-medium {
|
|
737
|
+
background: var(--priority-medium-bg);
|
|
738
|
+
--color: #ff7d00;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.priority-medium .priority-title {
|
|
742
|
+
color: var(--color);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
.priority-medium .priority-badge {
|
|
746
|
+
color: var(--color);
|
|
747
|
+
border: 1px solid var(--color);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.priority-medium .item-bullet,
|
|
751
|
+
.priority-medium .impact-icon,
|
|
752
|
+
.priority-medium .impact-text {
|
|
753
|
+
color: var(--color) !important;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.priority-low {
|
|
757
|
+
background: var(--priority-low-bg);
|
|
758
|
+
--color: #00b42a;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.priority-low .priority-title {
|
|
762
|
+
color: var(--color);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.priority-low .priority-badge {
|
|
766
|
+
color: var(--color);
|
|
767
|
+
border: 1px solid var(--color);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.priority-low .item-bullet,
|
|
771
|
+
.priority-low .impact-icon,
|
|
772
|
+
.priority-low .impact-text {
|
|
773
|
+
color: var(--color) !important;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.priority-header-top {
|
|
777
|
+
display: flex;
|
|
778
|
+
align-items: center;
|
|
779
|
+
gap: 12px;
|
|
780
|
+
margin-bottom: 8px;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.priority-icon {
|
|
784
|
+
font-size: 24px;
|
|
785
|
+
line-height: 1;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.priority-info {
|
|
789
|
+
display: flex;
|
|
790
|
+
justify-content: space-between;
|
|
791
|
+
align-items: center;
|
|
792
|
+
flex: 1;
|
|
793
|
+
gap: 8px;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.priority-title {
|
|
797
|
+
font-size: 18px;
|
|
798
|
+
font-weight: 700;
|
|
799
|
+
margin: 0;
|
|
800
|
+
line-height: 1.3;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.priority-subtitle {
|
|
804
|
+
font-size: 13px;
|
|
805
|
+
color: var(--text-secondary);
|
|
806
|
+
margin: 0 0 0 36px;
|
|
807
|
+
line-height: 1.5;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.priority-badge {
|
|
811
|
+
padding: 4px 12px;
|
|
812
|
+
border-radius: 4px;
|
|
813
|
+
font-size: 13px;
|
|
814
|
+
font-weight: 500;
|
|
815
|
+
white-space: nowrap;
|
|
816
|
+
background: transparent;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
.priority-items {
|
|
820
|
+
display: flex;
|
|
821
|
+
flex-direction: column;
|
|
822
|
+
gap: 16px;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
.priority-item-card {
|
|
826
|
+
--content-margin-left: 14px;
|
|
827
|
+
background: var(--surface);
|
|
828
|
+
border: 1px solid var(--border);
|
|
829
|
+
border-radius: 8px;
|
|
830
|
+
padding: 16px;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.priority-item-card .item-header {
|
|
834
|
+
display: flex;
|
|
835
|
+
align-items: center;
|
|
836
|
+
gap: 8px;
|
|
837
|
+
margin-bottom: 8px;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.item-bullet {
|
|
841
|
+
font-size: 20px;
|
|
842
|
+
line-height: 1.4;
|
|
843
|
+
font-weight: bold;
|
|
844
|
+
color: var(--text-primary);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
.item-title-text {
|
|
848
|
+
font-size: 15px;
|
|
849
|
+
font-weight: 700;
|
|
850
|
+
color: var(--text-primary);
|
|
851
|
+
margin: 0;
|
|
852
|
+
line-height: 1.4;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
.priority-item-card .item-description {
|
|
856
|
+
font-size: 14px;
|
|
857
|
+
color: var(--text-secondary);
|
|
858
|
+
line-height: 1.6;
|
|
859
|
+
margin: 0 0 12px var(--content-margin-left);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.item-impact {
|
|
863
|
+
display: flex;
|
|
864
|
+
align-items: flex-start;
|
|
865
|
+
gap: 4px;
|
|
866
|
+
margin-left: var(--content-margin-left);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
.impact-icon {
|
|
870
|
+
flex-shrink: 0;
|
|
871
|
+
font-size: 14px;
|
|
872
|
+
font-weight: bold;
|
|
873
|
+
line-height: 1.6;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
.impact-text {
|
|
877
|
+
font-size: 14px;
|
|
878
|
+
line-height: 1.6;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/* LongtermValue */
|
|
882
|
+
.longterm-cards {
|
|
883
|
+
display: grid;
|
|
884
|
+
grid-template-columns: repeat(2, 1fr);
|
|
885
|
+
gap: 20px;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
.longterm-card {
|
|
889
|
+
display: flex;
|
|
890
|
+
align-items: center;
|
|
891
|
+
gap: 16px;
|
|
892
|
+
padding: 24px;
|
|
893
|
+
border-radius: 12px;
|
|
894
|
+
border-left: 4px solid;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
.longterm-card:hover {
|
|
898
|
+
transform: translateY(-2px);
|
|
899
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
.card-blue {
|
|
903
|
+
background: var(--lt-blue-bg);
|
|
904
|
+
border-left-color: #165dff;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
.card-orange {
|
|
908
|
+
background: var(--lt-orange-bg);
|
|
909
|
+
border-left-color: #ff7d00;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
.card-green {
|
|
913
|
+
background: var(--lt-green-bg);
|
|
914
|
+
border-left-color: #00b42a;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.card-purple {
|
|
918
|
+
background: var(--lt-purple-bg);
|
|
919
|
+
border-left-color: #722ed1;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
.card-blue .card-icon {
|
|
923
|
+
--bgc: #dbeafe;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
.card-orange .card-icon {
|
|
927
|
+
--bgc: #ffedd5;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
.card-green .card-icon {
|
|
931
|
+
--bgc: #dcfce7;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
.card-purple .card-icon {
|
|
935
|
+
--bgc: #f3e8ff;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
html[data-theme="dark"] .card-blue .card-icon {
|
|
939
|
+
--bgc: #1e3a5f;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
html[data-theme="dark"] .card-orange .card-icon {
|
|
943
|
+
--bgc: #4a3520;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
html[data-theme="dark"] .card-green .card-icon {
|
|
947
|
+
--bgc: #1a4030;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
html[data-theme="dark"] .card-purple .card-icon {
|
|
951
|
+
--bgc: #3d2d5c;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
.card-icon {
|
|
955
|
+
flex-shrink: 0;
|
|
956
|
+
width: 46px;
|
|
957
|
+
height: 46px;
|
|
958
|
+
display: flex;
|
|
959
|
+
align-items: center;
|
|
960
|
+
justify-content: center;
|
|
961
|
+
font-size: 20px;
|
|
962
|
+
border-radius: 50%;
|
|
963
|
+
background-color: var(--bgc);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
.card-title {
|
|
967
|
+
font-size: 16px;
|
|
968
|
+
font-weight: 700;
|
|
969
|
+
color: var(--text-primary);
|
|
970
|
+
margin: 0 0 8px;
|
|
971
|
+
line-height: 1.3;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
.card-desc {
|
|
975
|
+
font-size: 14px;
|
|
976
|
+
color: var(--text-secondary);
|
|
977
|
+
margin: 0;
|
|
978
|
+
line-height: 1.5;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
@container (max-width: 860px) {
|
|
982
|
+
.health-overview {
|
|
983
|
+
flex-direction: column;
|
|
984
|
+
gap: 20px;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
.divider-line {
|
|
988
|
+
width: 90%;
|
|
989
|
+
height: 1px;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
.radar-chart {
|
|
993
|
+
width: 100%;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
@container (max-width: 900px) {
|
|
998
|
+
.risk-map-content {
|
|
999
|
+
flex-direction: column;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
.bar-chart-wrapper {
|
|
1003
|
+
width: 100%;
|
|
1004
|
+
height: 300px;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
.risk-table-wrapper {
|
|
1008
|
+
width: 100%;
|
|
1009
|
+
height: auto;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.risk-table-title {
|
|
1013
|
+
display: none;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
@container (max-width: 1200px) {
|
|
1018
|
+
.priority-columns {
|
|
1019
|
+
grid-template-columns: 1fr;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
.priority-items {
|
|
1023
|
+
display: grid !important;
|
|
1024
|
+
grid-template-columns: repeat(2, 1fr) !important;
|
|
1025
|
+
gap: 16px;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
@media (max-width: 768px) {
|
|
1030
|
+
.loading-speed-charts {
|
|
1031
|
+
flex-direction: column;
|
|
1032
|
+
align-items: center;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
.longterm-cards {
|
|
1036
|
+
grid-template-columns: 1fr;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
.main-title {
|
|
1040
|
+
font-size: 24px;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
.header-title-wrapper {
|
|
1044
|
+
flex-direction: column;
|
|
1045
|
+
align-items: flex-start;
|
|
1046
|
+
gap: 12px;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
.module-items-grid {
|
|
1050
|
+
grid-template-columns: 1fr !important;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
.priority-items {
|
|
1054
|
+
grid-template-columns: 1fr !important;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
@media print {
|
|
1059
|
+
body {
|
|
1060
|
+
background: var(--surface);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
.report-container {
|
|
1064
|
+
padding: 0;
|
|
1065
|
+
background: var(--surface);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
.report-content {
|
|
1069
|
+
box-shadow: none;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
.btn-export,
|
|
1073
|
+
.btn-theme {
|
|
1074
|
+
display: none;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
.report-section {
|
|
1078
|
+
break-inside: avoid;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
</style>
|
|
1082
|
+
</head>
|
|
1083
|
+
|
|
1084
|
+
<body>
|
|
1085
|
+
<div id="app"></div>
|
|
1086
|
+
<script>
|
|
1087
|
+
window.__WEBSITE_DIAGNOSIS__ = window.__WEBSITE_DIAGNOSIS__ || null;
|
|
1088
|
+
</script>
|
|
1089
|
+
<script>
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* 网站诊断报告运行时 — 对齐 MarkAI WebsiteAnalysisReport/v3
|
|
1093
|
+
* 数据源:window.__WEBSITE_DIAGNOSIS__(与 getWebsiteDiagnosisData.data 同结构)
|
|
1094
|
+
*/
|
|
1095
|
+
(function () {
|
|
1096
|
+
"use strict";
|
|
1097
|
+
|
|
1098
|
+
const THEME_KEY = "wd-report-theme";
|
|
1099
|
+
let radarChartInst = null;
|
|
1100
|
+
let riskChartInst = null;
|
|
1101
|
+
let chartModulesCache = [];
|
|
1102
|
+
let chartRiskCache = [];
|
|
1103
|
+
|
|
1104
|
+
function isDarkTheme() {
|
|
1105
|
+
return document.documentElement.getAttribute("data-theme") === "dark";
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
function getChartTheme() {
|
|
1109
|
+
const dark = isDarkTheme();
|
|
1110
|
+
return {
|
|
1111
|
+
axisName: dark ? "#a8b0bb" : "#4e5969",
|
|
1112
|
+
splitArea: dark ? ["#1a1f26", "#232a33"] : ["#ffffff", "#f7f8fa"],
|
|
1113
|
+
axisLabel: dark ? "#a8b0bb" : "#666666",
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
function getRingTrackColor() {
|
|
1118
|
+
return (
|
|
1119
|
+
getComputedStyle(document.documentElement).getPropertyValue("--ring-track").trim() || "#e5e6eb"
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
function updateThemeToggleLabel() {
|
|
1124
|
+
const btn = document.getElementById("btn-theme-toggle");
|
|
1125
|
+
if (btn) btn.textContent = isDarkTheme() ? "☀️ 白天" : "🌙 夜晚";
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
function updateScoreRingTracks() {
|
|
1129
|
+
const track = getRingTrackColor();
|
|
1130
|
+
document
|
|
1131
|
+
.querySelectorAll(".score-ring circle:first-of-type, .speed-ring circle:first-of-type")
|
|
1132
|
+
.forEach((el) => el.setAttribute("stroke", track));
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
function refreshCharts() {
|
|
1136
|
+
const theme = getChartTheme();
|
|
1137
|
+
if (radarChartInst && chartModulesCache.length) {
|
|
1138
|
+
radarChartInst.setOption({
|
|
1139
|
+
radar: {
|
|
1140
|
+
axisName: { color: theme.axisName },
|
|
1141
|
+
splitArea: { areaStyle: { color: theme.splitArea } },
|
|
1142
|
+
},
|
|
1143
|
+
});
|
|
1144
|
+
radarChartInst.resize();
|
|
1145
|
+
}
|
|
1146
|
+
if (riskChartInst && chartRiskCache.length) {
|
|
1147
|
+
riskChartInst.setOption({
|
|
1148
|
+
yAxis: { axisLabel: { color: theme.axisLabel } },
|
|
1149
|
+
series: [{ label: { color: theme.axisLabel } }, {}],
|
|
1150
|
+
});
|
|
1151
|
+
riskChartInst.resize();
|
|
1152
|
+
}
|
|
1153
|
+
updateScoreRingTracks();
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function applyTheme(theme) {
|
|
1157
|
+
document.documentElement.setAttribute("data-theme", theme === "dark" ? "dark" : "light");
|
|
1158
|
+
localStorage.setItem(THEME_KEY, theme);
|
|
1159
|
+
updateThemeToggleLabel();
|
|
1160
|
+
refreshCharts();
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
function toggleTheme() {
|
|
1164
|
+
applyTheme(isDarkTheme() ? "light" : "dark");
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
function ensureTheme() {
|
|
1168
|
+
const stored = localStorage.getItem(THEME_KEY);
|
|
1169
|
+
if (stored === "light" || stored === "dark") {
|
|
1170
|
+
document.documentElement.setAttribute("data-theme", stored === "dark" ? "dark" : "light");
|
|
1171
|
+
} else if (!document.documentElement.hasAttribute("data-theme")) {
|
|
1172
|
+
document.documentElement.setAttribute("data-theme", "light");
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
ensureTheme();
|
|
1177
|
+
|
|
1178
|
+
const ItemStatus = {
|
|
1179
|
+
Excellent: "优秀",
|
|
1180
|
+
Good: "良好",
|
|
1181
|
+
Normal: "一般",
|
|
1182
|
+
Poor: "较差",
|
|
1183
|
+
Full: "完整",
|
|
1184
|
+
NeedImprove: "需优化",
|
|
1185
|
+
Absent: "缺失",
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
const PAGE_SCORE_LEVEL = [
|
|
98
1189
|
{ id: "s1", level: "优秀", description: "可直接投放广告", color: "#00b42a", min: 90 },
|
|
99
1190
|
{ id: "s2", level: "良好", description: "小幅优化后可直接投放广告", color: "#00b42a", min: 80, max: 89.99 },
|
|
100
1191
|
{ id: "s3", level: "一般", description: "需要重点优化", color: "#ff7d00", min: 70, max: 79.99 },
|
|
@@ -102,397 +1193,755 @@
|
|
|
102
1193
|
{ id: "s5", level: "不建议投放", description: "不建议投放广告,需要全面整改", color: "#f53f3f", max: 59.99 },
|
|
103
1194
|
];
|
|
104
1195
|
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
};
|
|
1196
|
+
const INDUSTRY_TEXT_MAP = [
|
|
1197
|
+
{ status: "优秀", content: "网站表现优秀,远高于行业平均值,可放心开启广告投放", min: 90 },
|
|
1198
|
+
{ status: "良好", content: "网站处于行业中上水平,具备良好的优化基础", min: 80, max: 89 },
|
|
1199
|
+
{ status: "一般", content: "网站略低于行业平均值,建议进行针对性优化", min: 70, max: 79 },
|
|
1200
|
+
{ status: "不佳", content: "网站低于行业平均值,需要重点优化", min: 60, max: 69 },
|
|
1201
|
+
{ status: "较差", content: "网站远低于行业标准,建议马上优化", max: 59.99 },
|
|
1202
|
+
];
|
|
113
1203
|
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
1204
|
+
const CORE_ISSUES_DATA = [
|
|
1205
|
+
{ id: "ci1", issue: "首屏缺乏清晰CTA", impact: "广告落地页效果差", priority: "高" },
|
|
1206
|
+
{ id: "ci2", issue: "转化跟踪未部署", impact: "无法归因广告效果", priority: "高" },
|
|
1207
|
+
{ id: "ci3", issue: "移动端显示异常", impact: "移动端转化下降15%–25%", priority: "高" },
|
|
1208
|
+
{ id: "ci4", issue: "页面加载缓慢", impact: "首屏加载慢", priority: "高" },
|
|
1209
|
+
{ id: "ci5", issue: "导航栏与清晰度", impact: "用户找不到关键信息", priority: "中" },
|
|
1210
|
+
{ id: "ci6", issue: "表单复杂或缺乏清晰", impact: "用户找不到关键信息", priority: "中" },
|
|
1211
|
+
{ id: "ci7", issue: "类目页与详情页内容不匹配", impact: "广告相关性低", priority: "中" },
|
|
1212
|
+
{ id: "ci8", issue: "网站语言与广告不一致", impact: "用户体验差", priority: "中" },
|
|
1213
|
+
{ id: "ci9", issue: "缺少社媒分享入口", impact: "二次传播效率低", priority: "低" },
|
|
1214
|
+
{ id: "ci10", issue: "品牌风格不统一", impact: "品牌可信度低", priority: "低" },
|
|
1215
|
+
];
|
|
123
1216
|
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
1217
|
+
const PRIORITY_GROUP_DATA = [
|
|
1218
|
+
{
|
|
1219
|
+
优先级: "高",
|
|
1220
|
+
执行周期: "1-2周",
|
|
1221
|
+
影响内容: "影响转化和广告投放",
|
|
1222
|
+
优化项: [
|
|
1223
|
+
{
|
|
1224
|
+
优化方向: "核心Banner与首屏CTA",
|
|
1225
|
+
具体任务: "优化Banner图片清晰度、风格统一;突出首屏核心CTA和价值主张",
|
|
1226
|
+
广告影响说明: "提升Landing Page Experience评分,降低跳出率,直接影响Quality Score",
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
优化方向: "转化跟踪部署",
|
|
1230
|
+
具体任务: "部署GA4、GTM、Facebook Pixe等;设置表单提交、电话点击事件",
|
|
1231
|
+
广告影响说明: "完整数据采集,支持自动出价和再营销,提高广告ROI",
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
优化方向: "移动端适配优化",
|
|
1235
|
+
具体任务: "响应式布局优化;按钮触控目标≥44px;检查移动端首屏加载",
|
|
1236
|
+
广告影响说明: "移动端体验直接影响广告质量分和移动端转化率",
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
优化方向: "页面加载速度优化",
|
|
1240
|
+
具体任务: "压缩图片、JS/CSS;使用缓存/CDN;优化首屏加载",
|
|
1241
|
+
广告影响说明: "提升Google Ads速度评分,降低CPC成本,提高广告排名",
|
|
1242
|
+
},
|
|
1243
|
+
],
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
优先级: "中",
|
|
1247
|
+
执行周期: "2-4周",
|
|
1248
|
+
影响内容: "影响用户体验",
|
|
1249
|
+
优化项: [
|
|
1250
|
+
{
|
|
1251
|
+
优化方向: "导航栏与页面布局调整",
|
|
1252
|
+
具体任务: "栏目精简,导航层级≤3;首屏信息层级清晰",
|
|
1253
|
+
广告影响说明: "提升用户体验,降低跳出率,增加表单提交概率",
|
|
1254
|
+
},
|
|
1255
|
+
{
|
|
1256
|
+
优化方向: "表单优化",
|
|
1257
|
+
具体任务: "字段≤6个,必填≤4个;自动校验和清晰错误提示",
|
|
1258
|
+
广告影响说明: "提升表单提交率,增加转化量,优化广告ROI",
|
|
1259
|
+
},
|
|
1260
|
+
{
|
|
1261
|
+
优化方向: "产品类目页与详情页内容优化",
|
|
1262
|
+
具体任务: "每页英文说明≥200词;每产品≥3张高清图;规格参数齐全",
|
|
1263
|
+
广告影响说明: "提高Ad Relevance和转化率,降低广告质量分惩罚",
|
|
1264
|
+
},
|
|
1265
|
+
],
|
|
1266
|
+
},
|
|
1267
|
+
{
|
|
1268
|
+
优先级: "低",
|
|
1269
|
+
执行周期: "1-3个月",
|
|
1270
|
+
影响内容: "优化细节提高美观或品牌感",
|
|
1271
|
+
优化项: [
|
|
1272
|
+
{
|
|
1273
|
+
优化方向: "全站语言统一",
|
|
1274
|
+
具体任务: "全站英文(除语言切换外)",
|
|
1275
|
+
广告影响说明: "保证广告与落地页语言一致,提升Ad Relevance",
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
优化方向: "品牌视觉统一",
|
|
1279
|
+
具体任务: "图片、Banner风格统一;字体、Logo、配色一致",
|
|
1280
|
+
广告影响说明: "提升品牌认知度和广告可信度,间接增加转化",
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
优化方向: "社媒分享功能完善",
|
|
1284
|
+
具体任务: "添加OG标签、社交媒体Card;配置社媒分享按钮",
|
|
1285
|
+
广告影响说明: "提升社交可分享性,辅助再营销和广告推广",
|
|
1286
|
+
},
|
|
1287
|
+
],
|
|
1288
|
+
},
|
|
1289
|
+
];
|
|
132
1290
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
1291
|
+
/** 对齐 useWebsiteGuide/config allModuleConfigs(仅渲染所需字段) */
|
|
1292
|
+
const ALL_MODULE_CONFIGS = [
|
|
1293
|
+
{
|
|
1294
|
+
id: "m1",
|
|
1295
|
+
name: "网站内容及结构诊断",
|
|
1296
|
+
abbreviation: "内容与结构",
|
|
1297
|
+
desc: "评估网站结构合理性",
|
|
1298
|
+
优先级: "高",
|
|
1299
|
+
影响程度: 5,
|
|
1300
|
+
改进难度: 5,
|
|
1301
|
+
items: [
|
|
1302
|
+
{ id: "m1i1", name: "Banner轮播图", fullMarks: 5 },
|
|
1303
|
+
{ id: "m1i2", name: "导航栏", fullMarks: 2 },
|
|
1304
|
+
{ id: "m1i3", name: "首页信息布局", fullMarks: 4 },
|
|
1305
|
+
{ id: "m1i4", name: "产品类目页", fullMarks: 2 },
|
|
1306
|
+
{ id: "m1i5", name: "产品详情页", fullMarks: 4 },
|
|
1307
|
+
{ id: "m1i6", name: "公司介绍页", fullMarks: 3 },
|
|
1308
|
+
{ id: "m1i7", name: "联系我们页", fullMarks: 4 },
|
|
1309
|
+
{ id: "m1i8", name: "表单提交流程", fullMarks: 4 },
|
|
1310
|
+
{ id: "m1i9", name: "社交媒体链接", fullMarks: 2 },
|
|
1311
|
+
],
|
|
1312
|
+
},
|
|
1313
|
+
{
|
|
1314
|
+
id: "m2",
|
|
1315
|
+
name: "网站性能诊断",
|
|
1316
|
+
abbreviation: "网站技术性能",
|
|
1317
|
+
desc: "评估网站的基础性能指标",
|
|
1318
|
+
优先级: "高",
|
|
1319
|
+
影响程度: 5,
|
|
1320
|
+
改进难度: 3,
|
|
1321
|
+
items: [
|
|
1322
|
+
{ id: "m2i1", name: "加载速度", fullMarks: 8, isShowCardOnReport: false },
|
|
1323
|
+
{ id: "m2i2", name: "HTTPS安全", fullMarks: 5 },
|
|
1324
|
+
{ id: "m2i3", name: "404页面", fullMarks: 4 },
|
|
1325
|
+
{ id: "m2i4", name: "图片清晰度", fullMarks: 4 },
|
|
1326
|
+
{ id: "m2i5", name: "移动端适配", fullMarks: 4 },
|
|
1327
|
+
],
|
|
1328
|
+
},
|
|
1329
|
+
{
|
|
1330
|
+
id: "m3",
|
|
1331
|
+
name: "营销基础与广告落地页诊断",
|
|
1332
|
+
abbreviation: "营销基础与落地页",
|
|
1333
|
+
desc: "评估网站内容是否适合开启线上营销",
|
|
1334
|
+
优先级: "中",
|
|
1335
|
+
影响程度: 3,
|
|
1336
|
+
改进难度: 2,
|
|
1337
|
+
items: [
|
|
1338
|
+
{ id: "m3i1", name: "企业邮箱", fullMarks: 6 },
|
|
1339
|
+
{ id: "m3i2", name: "联系电话", fullMarks: 3 },
|
|
1340
|
+
{ id: "m3i4", name: "语言一致性", fullMarks: 3 },
|
|
1341
|
+
{ id: "m3i5", name: "地址地图", fullMarks: 3 },
|
|
1342
|
+
],
|
|
1343
|
+
},
|
|
1344
|
+
{
|
|
1345
|
+
id: "m4",
|
|
1346
|
+
name: "用户体验与转化分析诊断",
|
|
1347
|
+
abbreviation: "用户体验与转化",
|
|
1348
|
+
desc: "评估网站内容的用户友好度",
|
|
1349
|
+
优先级: "中",
|
|
1350
|
+
影响程度: 3,
|
|
1351
|
+
改进难度: 4,
|
|
1352
|
+
items: [
|
|
1353
|
+
{ id: "m4i1", name: "页面布局", fullMarks: 3 },
|
|
1354
|
+
{ id: "m4i2", name: "CTA按钮", fullMarks: 5 },
|
|
1355
|
+
{ id: "m4i3", name: "表单体验", fullMarks: 3 },
|
|
1356
|
+
{ id: "m4i4", name: "内容可读性", fullMarks: 3 },
|
|
1357
|
+
{ id: "m4i5", name: "社媒分享可扩展性", fullMarks: 1 },
|
|
1358
|
+
],
|
|
1359
|
+
},
|
|
1360
|
+
{
|
|
1361
|
+
id: "m5",
|
|
1362
|
+
name: "媒体广告投放辅助检查",
|
|
1363
|
+
abbreviation: "广告辅助检查",
|
|
1364
|
+
desc: "评估网站内容是否适合开启广告投放",
|
|
1365
|
+
优先级: "低",
|
|
1366
|
+
影响程度: 2,
|
|
1367
|
+
改进难度: 2,
|
|
1368
|
+
items: [
|
|
1369
|
+
{ id: "m5i1", name: "落地页速度", fullMarks: 3 },
|
|
1370
|
+
{ id: "m5i2", name: "询盘转化路径", fullMarks: 4 },
|
|
1371
|
+
{ id: "m5i4", name: "跟踪与分析", fullMarks: 3 },
|
|
1372
|
+
],
|
|
1373
|
+
},
|
|
1374
|
+
{
|
|
1375
|
+
id: "m6",
|
|
1376
|
+
name: "社交媒体辅助检查",
|
|
1377
|
+
abbreviation: "网站是否适合开启社交媒体",
|
|
1378
|
+
desc: "评估网站内容是否适合引用到社交媒体",
|
|
1379
|
+
优先级: "低",
|
|
1380
|
+
影响程度: 2,
|
|
1381
|
+
改进难度: 1,
|
|
1382
|
+
items: [
|
|
1383
|
+
{ id: "m6i1", name: "内容可引用性", fullMarks: 2 },
|
|
1384
|
+
{ id: "m6i2", name: "内容可分享性", fullMarks: 2 },
|
|
1385
|
+
{ id: "m6i3", name: "视觉风格统一", fullMarks: 1 },
|
|
1386
|
+
],
|
|
1387
|
+
},
|
|
1388
|
+
];
|
|
145
1389
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
1390
|
+
function formatDate(dateString) {
|
|
1391
|
+
if (!dateString) return "";
|
|
1392
|
+
const d = new Date(dateString);
|
|
1393
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
1394
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1395
|
+
return `${d.getFullYear()}年${pad(d.getMonth() + 1)}月${pad(d.getDate())}日 ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
function calcPercentage(score, totalScore) {
|
|
1399
|
+
if (!totalScore) return 0;
|
|
1400
|
+
return Math.round((Number(score) / Number(totalScore)) * 100);
|
|
1401
|
+
}
|
|
150
1402
|
|
|
151
|
-
function
|
|
1403
|
+
function getRatingInfo(ratingId, score) {
|
|
152
1404
|
if (ratingId) {
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
1405
|
+
const hit = PAGE_SCORE_LEVEL.find((x) => x.id === ratingId);
|
|
1406
|
+
if (hit) return hit;
|
|
155
1407
|
}
|
|
156
1408
|
const s = Number(score) || 0;
|
|
157
|
-
for (const
|
|
158
|
-
if (
|
|
159
|
-
if (
|
|
160
|
-
if (
|
|
1409
|
+
for (const info of PAGE_SCORE_LEVEL) {
|
|
1410
|
+
if (info.min != null && info.max != null && s >= info.min && s <= info.max) return info;
|
|
1411
|
+
if (info.min != null && info.max == null && s >= info.min) return info;
|
|
1412
|
+
if (info.max != null && info.min == null && s <= info.max) return info;
|
|
161
1413
|
}
|
|
162
|
-
return
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
data
|
|
192
|
-
data.
|
|
193
|
-
|
|
194
|
-
data.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
1414
|
+
return PAGE_SCORE_LEVEL[PAGE_SCORE_LEVEL.length - 1];
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
function getIndustryInfo(score) {
|
|
1418
|
+
const s = Number(score) || 0;
|
|
1419
|
+
for (const item of INDUSTRY_TEXT_MAP) {
|
|
1420
|
+
if (item.min != null && item.max != null && s >= item.min && s <= item.max) return item;
|
|
1421
|
+
if (item.min != null && item.max == null && s >= item.min) return item;
|
|
1422
|
+
if (item.max != null && item.min == null && s < item.max + 1) return item;
|
|
1423
|
+
}
|
|
1424
|
+
return INDUSTRY_TEXT_MAP[INDUSTRY_TEXT_MAP.length - 1];
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
function getPriorityDisplay(priority) {
|
|
1428
|
+
if (priority === "高" || priority === "High") return "🔴 高";
|
|
1429
|
+
if (priority === "低" || priority === "Low") return "🟢 低";
|
|
1430
|
+
return "🟡 中";
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
function getStarDisplay(level) {
|
|
1434
|
+
const n = Math.max(0, Math.min(5, Number(level) || 0));
|
|
1435
|
+
return "⭐".repeat(n);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
/** 对齐 useWebsiteGuide/utils parseAIResultData */
|
|
1439
|
+
function parseAIResultData(value) {
|
|
1440
|
+
if (!value) return {};
|
|
1441
|
+
if (value.websiteGuideResult) return value.websiteGuideResult;
|
|
1442
|
+
|
|
1443
|
+
const data = { ...value };
|
|
1444
|
+
if (!data.coreIssues || !Array.isArray(data.coreIssues)) data.coreIssues = [];
|
|
1445
|
+
|
|
1446
|
+
if (data.modules && Array.isArray(data.modules)) {
|
|
1447
|
+
data.modules = data.modules.map((module) => {
|
|
1448
|
+
const moduleConfig = ALL_MODULE_CONFIGS.find((c) => c.id === module.id);
|
|
1449
|
+
if (!moduleConfig) return module;
|
|
1450
|
+
|
|
1451
|
+
const enrichedModule = {
|
|
1452
|
+
...module,
|
|
1453
|
+
desc: moduleConfig.desc,
|
|
1454
|
+
name: moduleConfig.name,
|
|
1455
|
+
abbreviation: moduleConfig.abbreviation,
|
|
1456
|
+
priority: moduleConfig.优先级,
|
|
1457
|
+
impact: moduleConfig.影响程度,
|
|
1458
|
+
difficulty: moduleConfig.改进难度,
|
|
1459
|
+
totalScore: +moduleConfig.items.reduce((sum, item) => sum + item.fullMarks, 0).toFixed(2),
|
|
1460
|
+
};
|
|
1461
|
+
|
|
1462
|
+
if (module.items && Array.isArray(module.items)) {
|
|
1463
|
+
enrichedModule.items = module.items.map((item) => {
|
|
1464
|
+
const itemConfig = moduleConfig.items.find((c) => c.id === item.id);
|
|
1465
|
+
if (!itemConfig) return item;
|
|
1466
|
+
return {
|
|
1467
|
+
...item,
|
|
1468
|
+
isShowCardOnReport: itemConfig.isShowCardOnReport !== false,
|
|
1469
|
+
name: itemConfig.name,
|
|
1470
|
+
totalScore: itemConfig.fullMarks,
|
|
1471
|
+
};
|
|
1472
|
+
});
|
|
1473
|
+
enrichedModule.score = +enrichedModule.items
|
|
1474
|
+
.reduce((sum, item) => sum + (Number(item.score) || 0), 0)
|
|
1475
|
+
.toFixed(2);
|
|
1476
|
+
}
|
|
1477
|
+
return enrichedModule;
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
data.score = data.modules.reduce((sum, module) => sum + (Number(module.score) || 0), 0);
|
|
1481
|
+
const correctRating = getRatingInfo(undefined, data.score);
|
|
1482
|
+
if (correctRating) data.ratingId = correctRating.id;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
if (data.coreIssuesIds && Array.isArray(data.coreIssuesIds)) {
|
|
1486
|
+
data.coreIssues = CORE_ISSUES_DATA.filter((item) => data.coreIssuesIds.includes(item.id));
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
data.lighthouseResult = data.lighthouseResult || data.lighthouse || null;
|
|
199
1490
|
return data;
|
|
200
1491
|
}
|
|
201
1492
|
|
|
202
|
-
function
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
1493
|
+
function getItemStatusStyle(item) {
|
|
1494
|
+
const orange = [ItemStatus.NeedImprove, ItemStatus.Normal];
|
|
1495
|
+
const red = [ItemStatus.Poor, ItemStatus.Absent];
|
|
1496
|
+
const green = [ItemStatus.Full, ItemStatus.Excellent, ItemStatus.Good];
|
|
1497
|
+
const status = ItemStatus[item.status];
|
|
1498
|
+
if (orange.includes(status)) return { color: "#ff7d00", backgroundColor: "#fff3e8" };
|
|
1499
|
+
if (red.includes(status)) return { color: "#f53f3f", backgroundColor: "#ffece8" };
|
|
1500
|
+
if (green.includes(status)) return { color: "#00b42a", backgroundColor: "#e8f8f2" };
|
|
1501
|
+
return { color: "#4e5969", backgroundColor: "#f7f8fa" };
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
function getModuleItemsGridColumns(items) {
|
|
1505
|
+
const len = (items || []).filter((item) => item.isShowCardOnReport !== false).length;
|
|
1506
|
+
if (len <= 2 || len % 2 === 0) return 2;
|
|
1507
|
+
return 3;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
function getSpeedColor(score) {
|
|
1511
|
+
if (score >= 90) return "#00b42a";
|
|
1512
|
+
if (score >= 50) return "#ff7d00";
|
|
1513
|
+
return "#f53f3f";
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function getSpeedInfo(value) {
|
|
1517
|
+
const v = Number(value) || 0;
|
|
1518
|
+
if (v <= 2.5) return { color: "#00b42a", backgroundColor: "#e8f8f2", statusText: "优秀" };
|
|
1519
|
+
if (v <= 4) return { color: "#ff7d00", backgroundColor: "#fff3e8", statusText: "需改进" };
|
|
1520
|
+
return { color: "#f53f3f", backgroundColor: "#ffece8", statusText: "较差" };
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
function formatTimeSeconds(val) {
|
|
1524
|
+
if (!val) return "0秒";
|
|
1525
|
+
return `${val}秒`;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
function getLoadingSpeedStatus(desktopScore, mobileScore) {
|
|
1529
|
+
const avg = (Number(desktopScore) + Number(mobileScore)) / 2;
|
|
1530
|
+
if (avg >= 90) return "优秀";
|
|
1531
|
+
if (avg >= 70) return "良好";
|
|
1532
|
+
if (avg >= 50) return "一般";
|
|
1533
|
+
return "需优化";
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
function getLoadingSpeedStatusClass(desktopScore, mobileScore) {
|
|
1537
|
+
const avg = (Number(desktopScore) + Number(mobileScore)) / 2;
|
|
1538
|
+
if (avg >= 90) return "status-excellent";
|
|
1539
|
+
if (avg >= 70) return "status-good";
|
|
1540
|
+
if (avg >= 50) return "status-normal";
|
|
206
1541
|
return "status-poor";
|
|
207
1542
|
}
|
|
208
1543
|
|
|
209
|
-
function
|
|
210
|
-
const
|
|
211
|
-
const
|
|
1544
|
+
function getFinalStatusDescription(lh) {
|
|
1545
|
+
const desktop = lh?.desktop?.speedIndex;
|
|
1546
|
+
const mobile = lh?.mobile?.speedIndex;
|
|
1547
|
+
const avg = (Number(desktop) + Number(mobile)) / 2;
|
|
1548
|
+
const finalStatus = getSpeedInfo(avg);
|
|
1549
|
+
if (finalStatus.statusText === "优秀") {
|
|
1550
|
+
return "网站加载速度快,页面响应及时,达到行业领先水平";
|
|
1551
|
+
}
|
|
1552
|
+
return "网站加载速度较慢,建议进行全面优化,提升页面性能";
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
function escapeHtml(s) {
|
|
1556
|
+
return String(s ?? "")
|
|
1557
|
+
.replace(/&/g, "&")
|
|
1558
|
+
.replace(/</g, "<")
|
|
1559
|
+
.replace(/>/g, ">")
|
|
1560
|
+
.replace(/"/g, """);
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
function renderLoadingSpeed(lh) {
|
|
1564
|
+
if (!lh) return "";
|
|
1565
|
+
const desktopScore = lh.desktop?.score || 0;
|
|
1566
|
+
const mobileScore = lh.mobile?.score || 0;
|
|
1567
|
+
const statusClass = getLoadingSpeedStatusClass(desktopScore, mobileScore);
|
|
1568
|
+
const statusText = getLoadingSpeedStatus(desktopScore, mobileScore);
|
|
1569
|
+
|
|
1570
|
+
const ring = (score, prefix) => {
|
|
1571
|
+
const s = Number(score) || 0;
|
|
1572
|
+
const color = getSpeedColor(s);
|
|
1573
|
+
const d = lh[prefix] || {};
|
|
1574
|
+
return `
|
|
1575
|
+
<div class="speed-chart-item">
|
|
1576
|
+
<div class="chart-title">${prefix === "desktop" ? "PC端加载速度 💻" : "移动端加载速度 📱"}</div>
|
|
1577
|
+
<div class="speed-ring">
|
|
1578
|
+
<svg width="180" height="180" viewBox="0 0 180 180">
|
|
1579
|
+
<circle cx="90" cy="90" r="70" fill="none" stroke="#e5e6eb" stroke-width="20"/>
|
|
1580
|
+
<circle cx="90" cy="90" r="70" fill="none" stroke="${color}" stroke-width="20"
|
|
1581
|
+
stroke-dasharray="${s * 4.4} 440" stroke-linecap="round" transform="rotate(-90 90 90)"/>
|
|
1582
|
+
<text x="90" y="90" text-anchor="middle" dominant-baseline="middle" font-size="36" font-weight="600" fill="${color}">${s}</text>
|
|
1583
|
+
</svg>
|
|
1584
|
+
</div>
|
|
1585
|
+
<div class="speed-details">
|
|
1586
|
+
<div class="speed-detail-item">首屏加载时间:<span class="speed-value" style="color:${getSpeedInfo(d.firstContentfulPaint || 0).color}">${formatTimeSeconds(d.firstContentfulPaint)}</span></div>
|
|
1587
|
+
<div class="speed-detail-item">完全加载时间:<span class="speed-value" style="color:${getSpeedInfo(d.speedIndex || 0).color}">${formatTimeSeconds(d.speedIndex)}</span></div>
|
|
1588
|
+
</div>
|
|
1589
|
+
</div>`;
|
|
1590
|
+
};
|
|
1591
|
+
|
|
212
1592
|
return `
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
1593
|
+
<div class="loading-speed-section">
|
|
1594
|
+
<div class="loading-speed-header">
|
|
1595
|
+
<h4 class="loading-speed-title">加载速度</h4>
|
|
1596
|
+
<span class="loading-speed-status ${statusClass}">${statusText}</span>
|
|
1597
|
+
</div>
|
|
1598
|
+
<p class="loading-speed-desc">${escapeHtml(getFinalStatusDescription(lh))}</p>
|
|
1599
|
+
<div class="loading-speed-charts">${ring(desktopScore, "desktop")}${ring(mobileScore, "mobile")}</div>
|
|
1600
|
+
<div class="speed-legend">
|
|
1601
|
+
<div class="legend-item"><span class="legend-icon" style="background:#f53f3f"></span><span class="legend-text">0-49</span></div>
|
|
1602
|
+
<div class="legend-item"><span class="legend-icon" style="background:#ff7d00"></span><span class="legend-text">50-89</span></div>
|
|
1603
|
+
<div class="legend-item"><span class="legend-icon" style="background:#00b42a"></span><span class="legend-text">90-100</span></div>
|
|
1604
|
+
</div>
|
|
1605
|
+
</div>`;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
function renderModuleDetail(module, index, lighthouseResult) {
|
|
1609
|
+
const cols = getModuleItemsGridColumns(module.items);
|
|
1610
|
+
const cards = (module.items || [])
|
|
1611
|
+
.filter((item) => item.isShowCardOnReport !== false)
|
|
1612
|
+
.map((item) => {
|
|
1613
|
+
const style = getItemStatusStyle(item);
|
|
1614
|
+
const pct = calcPercentage(item.score, item.totalScore);
|
|
1615
|
+
const statusLabel = ItemStatus[item.status] || item.status || "正常";
|
|
1616
|
+
const suggestion =
|
|
1617
|
+
item.suggestion && pct < 100
|
|
1618
|
+
? `<div class="suggestion-box" style="border-left-color:${style.color};background-color:${style.backgroundColor}">
|
|
1619
|
+
<span class="suggestion-icon">💡</span>
|
|
1620
|
+
<span class="suggestion-text">${escapeHtml(item.suggestion)}</span>
|
|
1621
|
+
</div>`
|
|
1622
|
+
: "";
|
|
1623
|
+
return `
|
|
1624
|
+
<div class="module-item-card">
|
|
1625
|
+
<div class="card-header">
|
|
1626
|
+
<h4 class="item-title">${escapeHtml(item.name)}</h4>
|
|
1627
|
+
<span class="status-badge" style="color:${style.color};background-color:${style.backgroundColor}">${escapeHtml(statusLabel)}</span>
|
|
1628
|
+
</div>
|
|
1629
|
+
<div class="card-content">${item.issue ? `<p class="item-description">${escapeHtml(item.issue)}</p>` : ""}</div>
|
|
1630
|
+
<div class="card-footer">
|
|
1631
|
+
<div class="score-info">
|
|
1632
|
+
<span class="score-label">完成度</span>
|
|
1633
|
+
<span class="score-percentage">${pct}%</span>
|
|
1634
|
+
</div>
|
|
1635
|
+
<div class="progress-bar">
|
|
1636
|
+
<div class="progress-fill" style="width:${pct}%;background-color:${style.color}"></div>
|
|
1637
|
+
</div>
|
|
1638
|
+
</div>
|
|
1639
|
+
${suggestion}
|
|
221
1640
|
</div>`;
|
|
1641
|
+
})
|
|
1642
|
+
.join("");
|
|
1643
|
+
|
|
1644
|
+
const loading =
|
|
1645
|
+
module.id === "m2" && lighthouseResult ? renderLoadingSpeed(lighthouseResult) : "";
|
|
1646
|
+
|
|
1647
|
+
return `
|
|
1648
|
+
<div class="module-detail">
|
|
1649
|
+
<div class="module-header">
|
|
1650
|
+
<div class="module-header-left">
|
|
1651
|
+
<h3 class="module-title">${index + 1}. ${escapeHtml(module.abbreviation)}</h3>
|
|
1652
|
+
<span class="module-score-info">此项得分<span class="score-value">${module.score}分</span>(满分<span class="total-score-value">${module.totalScore}</span>分)</span>
|
|
1653
|
+
</div>
|
|
1654
|
+
<span class="module-description">${escapeHtml(module.desc)}</span>
|
|
1655
|
+
</div>
|
|
1656
|
+
${loading}
|
|
1657
|
+
<div class="module-items-grid" style="grid-template-columns:repeat(${cols}, 1fr)">${cards}</div>
|
|
1658
|
+
</div>`;
|
|
222
1659
|
}
|
|
223
1660
|
|
|
224
|
-
function
|
|
225
|
-
|
|
1661
|
+
function renderPriorityPlan() {
|
|
1662
|
+
return PRIORITY_GROUP_DATA.map((group) => {
|
|
1663
|
+
const cls =
|
|
1664
|
+
group.优先级 === "高"
|
|
1665
|
+
? "priority-high"
|
|
1666
|
+
: group.优先级 === "中"
|
|
1667
|
+
? "priority-medium"
|
|
1668
|
+
: "priority-low";
|
|
1669
|
+
const title =
|
|
1670
|
+
group.优先级 === "高" ? "急需优化!" : group.优先级 === "中" ? "建议优化" : "推荐优化";
|
|
1671
|
+
const icon = group.优先级 === "高" ? "🔥" : group.优先级 === "中" ? "💡" : "🚩";
|
|
1672
|
+
const items = group.优化项
|
|
1673
|
+
.map(
|
|
1674
|
+
(item) => `
|
|
1675
|
+
<div class="priority-item-card">
|
|
1676
|
+
<div class="item-header"><span class="item-bullet">•</span><h4 class="item-title-text">${escapeHtml(item.优化方向)}</h4></div>
|
|
1677
|
+
<p class="item-description">${escapeHtml(item.具体任务)}</p>
|
|
1678
|
+
<div class="item-impact"><span class="impact-icon">↑</span><span class="impact-text">${escapeHtml(item.广告影响说明)}</span></div>
|
|
1679
|
+
</div>`,
|
|
1680
|
+
)
|
|
1681
|
+
.join("");
|
|
1682
|
+
return `
|
|
1683
|
+
<div class="priority-column ${cls}">
|
|
1684
|
+
<div class="priority-header">
|
|
1685
|
+
<div class="priority-header-top">
|
|
1686
|
+
<div class="priority-icon-wrapper"><span class="priority-icon">${icon}</span></div>
|
|
1687
|
+
<div class="priority-info">
|
|
1688
|
+
<h3 class="priority-title">${title}</h3>
|
|
1689
|
+
<div class="priority-badge">${escapeHtml(group.执行周期)}</div>
|
|
1690
|
+
</div>
|
|
1691
|
+
</div>
|
|
1692
|
+
<p class="priority-subtitle">${escapeHtml(group.影响内容)}</p>
|
|
1693
|
+
</div>
|
|
1694
|
+
<div class="priority-items">${items}</div>
|
|
1695
|
+
</div>`;
|
|
1696
|
+
}).join("");
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
function renderLongtermValue() {
|
|
1700
|
+
const cards = [
|
|
1701
|
+
{ cls: "card-blue", icon: "📊", title: "季度诊断与复盘", desc: "定期评估优化效果,调整策略方向" },
|
|
1702
|
+
{ cls: "card-orange", icon: "📋", title: "数据仪表盘持续追踪", desc: "实时监控核心指标波动,及时发现问题" },
|
|
1703
|
+
{ cls: "card-green", icon: "🧪", title: "A/B测试与投放策略联动", desc: "通过科学测试优化广告创意与落地页" },
|
|
1704
|
+
{ cls: "card-purple", icon: "🎯", title: "监控核心指标", desc: "CPL、转化率、广告ROI、首屏加载速度全方位监测" },
|
|
1705
|
+
];
|
|
1706
|
+
return cards
|
|
1707
|
+
.map(
|
|
1708
|
+
(c) => `
|
|
1709
|
+
<div class="longterm-card ${c.cls}">
|
|
1710
|
+
<div class="card-icon"><span>${c.icon}</span></div>
|
|
1711
|
+
<div class="card-content">
|
|
1712
|
+
<h4 class="card-title">${c.title}</h4>
|
|
1713
|
+
<p class="card-desc">${c.desc}</p>
|
|
1714
|
+
</div>
|
|
1715
|
+
</div>`,
|
|
1716
|
+
)
|
|
1717
|
+
.join("");
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
function initRadarChart(el, modules) {
|
|
1721
|
+
if (!window.echarts || !el) return null;
|
|
1722
|
+
const theme = getChartTheme();
|
|
226
1723
|
const chart = echarts.init(el);
|
|
227
1724
|
chart.setOption({
|
|
228
1725
|
radar: {
|
|
229
|
-
indicator: modules.map((m) => ({ name: m.abbreviation, max: m.totalScore
|
|
230
|
-
radius: "
|
|
1726
|
+
indicator: modules.map((m) => ({ name: m.abbreviation, max: m.totalScore })),
|
|
1727
|
+
radius: "60%",
|
|
231
1728
|
splitNumber: 4,
|
|
232
|
-
axisName: { color:
|
|
1729
|
+
axisName: { color: theme.axisName, fontSize: 12 },
|
|
1730
|
+
splitArea: { areaStyle: { color: theme.splitArea } },
|
|
233
1731
|
},
|
|
234
|
-
series: [
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
1732
|
+
series: [
|
|
1733
|
+
{
|
|
1734
|
+
type: "radar",
|
|
1735
|
+
data: [
|
|
1736
|
+
{
|
|
1737
|
+
value: modules.map((m) => Number(m.score) || 0),
|
|
1738
|
+
name: "评分",
|
|
1739
|
+
areaStyle: { color: "rgba(22, 93, 255, 0.2)" },
|
|
1740
|
+
lineStyle: { color: "#165dff", width: 2 },
|
|
1741
|
+
itemStyle: { color: "#165dff" },
|
|
1742
|
+
},
|
|
1743
|
+
],
|
|
1744
|
+
},
|
|
1745
|
+
],
|
|
243
1746
|
});
|
|
244
|
-
|
|
1747
|
+
return chart;
|
|
245
1748
|
}
|
|
246
1749
|
|
|
247
|
-
function
|
|
248
|
-
if (!window.echarts || !el) return;
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
const
|
|
1750
|
+
function initRiskBarChart(el, riskMapData) {
|
|
1751
|
+
if (!window.echarts || !el) return null;
|
|
1752
|
+
const theme = getChartTheme();
|
|
1753
|
+
const data = [...riskMapData].reverse();
|
|
1754
|
+
const moduleNames = data.map((v) => v.module);
|
|
1755
|
+
const totalScores = data.map((v) => Number((v.score || "").split("/")[1]) || 0);
|
|
1756
|
+
const actualScores = data.map((v) => Number((v.score || "").split("/")[0]) || 0);
|
|
253
1757
|
const chart = echarts.init(el);
|
|
254
1758
|
chart.setOption({
|
|
255
1759
|
tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
|
|
256
|
-
grid: { left:
|
|
257
|
-
xAxis: { type: "value", show: false, max: Math.max(...
|
|
1760
|
+
grid: { left: "8px", right: "65px", top: "0", bottom: "0", containLabel: true },
|
|
1761
|
+
xAxis: { type: "value", show: false, max: Math.max(...totalScores, 1) },
|
|
258
1762
|
yAxis: {
|
|
259
1763
|
type: "category",
|
|
260
|
-
data:
|
|
261
|
-
axisLabel: {
|
|
1764
|
+
data: moduleNames,
|
|
1765
|
+
axisLabel: {
|
|
1766
|
+
color: theme.axisLabel,
|
|
1767
|
+
fontSize: 12,
|
|
1768
|
+
formatter: (value) => {
|
|
1769
|
+
const maxChars = 8;
|
|
1770
|
+
if (value.length <= maxChars) return value;
|
|
1771
|
+
const lines = [];
|
|
1772
|
+
for (let i = 0; i < value.length; i += maxChars) lines.push(value.slice(i, i + maxChars));
|
|
1773
|
+
return lines.join("\n");
|
|
1774
|
+
},
|
|
1775
|
+
},
|
|
262
1776
|
axisLine: { show: false },
|
|
263
1777
|
axisTick: { show: false },
|
|
264
1778
|
},
|
|
265
1779
|
series: [
|
|
266
1780
|
{
|
|
267
|
-
name: "
|
|
1781
|
+
name: "总分",
|
|
268
1782
|
type: "bar",
|
|
269
|
-
data:
|
|
270
|
-
barWidth:
|
|
271
|
-
itemStyle: { color: "#
|
|
1783
|
+
data: totalScores,
|
|
1784
|
+
barWidth: 20,
|
|
1785
|
+
itemStyle: { color: "#0099FF", borderRadius: [0, 4, 4, 0] },
|
|
272
1786
|
label: {
|
|
273
1787
|
show: true,
|
|
274
1788
|
position: "right",
|
|
275
|
-
formatter: (p) => `${
|
|
276
|
-
color:
|
|
277
|
-
fontSize:
|
|
1789
|
+
formatter: (p) => `${actualScores[p.dataIndex]}/${p.value}`,
|
|
1790
|
+
color: theme.axisLabel,
|
|
1791
|
+
fontSize: 12,
|
|
278
1792
|
},
|
|
279
1793
|
z: 1,
|
|
280
1794
|
},
|
|
281
1795
|
{
|
|
282
1796
|
name: "得分",
|
|
283
1797
|
type: "bar",
|
|
284
|
-
data:
|
|
285
|
-
barWidth:
|
|
1798
|
+
data: actualScores,
|
|
1799
|
+
barWidth: 20,
|
|
286
1800
|
barGap: "-100%",
|
|
287
|
-
itemStyle: { color: "#
|
|
1801
|
+
itemStyle: { color: "#00CC00", borderRadius: [0, 4, 4, 0] },
|
|
288
1802
|
z: 2,
|
|
289
1803
|
},
|
|
290
1804
|
],
|
|
291
1805
|
});
|
|
292
|
-
|
|
1806
|
+
return chart;
|
|
293
1807
|
}
|
|
294
1808
|
|
|
295
|
-
function
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
const
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
{
|
|
308
|
-
name: "桌面端",
|
|
309
|
-
type: "bar",
|
|
310
|
-
data: [
|
|
311
|
-
desktop.score ?? 0,
|
|
312
|
-
desktop.firstContentfulPaint != null ? (desktop.firstContentfulPaint / 1000).toFixed(2) : 0,
|
|
313
|
-
desktop.speedIndex != null ? (desktop.speedIndex / 1000).toFixed(2) : 0,
|
|
314
|
-
],
|
|
315
|
-
itemStyle: { color: "#2563eb" },
|
|
316
|
-
},
|
|
317
|
-
{
|
|
318
|
-
name: "移动端",
|
|
319
|
-
type: "bar",
|
|
320
|
-
data: [
|
|
321
|
-
mobile.score ?? 0,
|
|
322
|
-
mobile.firstContentfulPaint != null ? (mobile.firstContentfulPaint / 1000).toFixed(2) : 0,
|
|
323
|
-
mobile.speedIndex != null ? (mobile.speedIndex / 1000).toFixed(2) : 0,
|
|
324
|
-
],
|
|
325
|
-
itemStyle: { color: "#f97316" },
|
|
326
|
-
},
|
|
327
|
-
],
|
|
328
|
-
});
|
|
329
|
-
window.addEventListener("resize", () => chart.resize());
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function lighthouseTable(lh) {
|
|
333
|
-
if (!lh || (!lh.desktop && !lh.mobile)) return "";
|
|
334
|
-
const row = (label, d, m) => `
|
|
335
|
-
<tr class="border-t border-slate-100">
|
|
336
|
-
<td class="py-2 pr-4 text-slate-600">${label}</td>
|
|
337
|
-
<td class="py-2">${d ?? "—"}</td>
|
|
338
|
-
<td class="py-2">${m ?? "—"}</td>
|
|
339
|
-
</tr>`;
|
|
340
|
-
const fmtMs = (v) => (v != null ? `${(v / 1000).toFixed(2)} s` : "—");
|
|
341
|
-
return `
|
|
342
|
-
<table class="min-w-full text-left">
|
|
343
|
-
<thead><tr class="text-slate-500 border-b">
|
|
344
|
-
<th class="pb-2">指标</th><th class="pb-2">桌面端</th><th class="pb-2">移动端</th>
|
|
345
|
-
</tr></thead>
|
|
346
|
-
<tbody>
|
|
347
|
-
${row("性能得分", lh.desktop?.score, lh.mobile?.score)}
|
|
348
|
-
${row("FCP", fmtMs(lh.desktop?.firstContentfulPaint), fmtMs(lh.mobile?.firstContentfulPaint))}
|
|
349
|
-
${row("SI", fmtMs(lh.desktop?.speedIndex), fmtMs(lh.mobile?.speedIndex))}
|
|
350
|
-
</tbody>
|
|
351
|
-
</table>`;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function buildPriorityPlan(data) {
|
|
355
|
-
const blocks = [];
|
|
356
|
-
const priOrder = { 高: 0, 中: 1, 低: 2 };
|
|
357
|
-
const issues = [...(data.coreIssues || [])].sort(
|
|
358
|
-
(a, b) => (priOrder[a.priority] ?? 9) - (priOrder[b.priority] ?? 9),
|
|
359
|
-
);
|
|
360
|
-
if (issues.length) {
|
|
361
|
-
blocks.push("<h3 class=\"font-medium text-slate-800\">核心问题</h3><ul class=\"list-disc pl-5 space-y-1\">");
|
|
362
|
-
for (const ci of issues) {
|
|
363
|
-
blocks.push(
|
|
364
|
-
`<li><span class="font-medium">[${ci.priority}]</span> ${ci.issue} — ${ci.impact}</li>`,
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
blocks.push("</ul>");
|
|
368
|
-
}
|
|
369
|
-
const weak = [];
|
|
370
|
-
for (const mod of data.modules || []) {
|
|
371
|
-
for (const it of mod.items || []) {
|
|
372
|
-
const ratio = (Number(it.score) || 0) / (it.totalScore || 1);
|
|
373
|
-
if (ratio < 0.6 && it.suggestion) {
|
|
374
|
-
weak.push({ module: mod.abbreviation, name: it.name, suggestion: it.suggestion, ratio });
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
weak.sort((a, b) => a.ratio - b.ratio);
|
|
379
|
-
if (weak.length) {
|
|
380
|
-
blocks.push("<h3 class=\"font-medium text-slate-800 mt-4\">低分子项建议</h3><ol class=\"list-decimal pl-5 space-y-2\">");
|
|
381
|
-
for (const w of weak.slice(0, 12)) {
|
|
382
|
-
blocks.push(`<li><span class="text-slate-600">${w.module} · ${w.name}</span><br/>${w.suggestion}</li>`);
|
|
383
|
-
}
|
|
384
|
-
blocks.push("</ol>");
|
|
385
|
-
}
|
|
386
|
-
return blocks.join("") || "<p class=\"text-slate-500\">暂无待办项。</p>";
|
|
387
|
-
}
|
|
1809
|
+
function renderReport(raw) {
|
|
1810
|
+
const data = parseAIResultData(raw);
|
|
1811
|
+
const rating = getRatingInfo(data.ratingId, data.score);
|
|
1812
|
+
const industry = getIndustryInfo(data.score);
|
|
1813
|
+
const ratingColor = rating.color;
|
|
1814
|
+
const riskMapData = (data.modules || []).map((module) => ({
|
|
1815
|
+
module: module.abbreviation,
|
|
1816
|
+
score: `${module.score}/${module.totalScore}`,
|
|
1817
|
+
priority: getPriorityDisplay(module.priority),
|
|
1818
|
+
impact: getStarDisplay(module.impact),
|
|
1819
|
+
difficulty: getStarDisplay(module.difficulty),
|
|
1820
|
+
}));
|
|
388
1821
|
|
|
389
|
-
|
|
390
|
-
const r = getRating(data.score, data.ratingId);
|
|
391
|
-
const lines = [
|
|
392
|
-
`<p><strong>综合评级:${r.level}</strong> — ${r.description}</p>`,
|
|
393
|
-
`<p>当前总分 <strong>${Number(data.score).toFixed(1)}</strong> / 100。`,
|
|
394
|
-
];
|
|
395
|
-
if (r.id === "s1" || r.id === "s2") {
|
|
396
|
-
lines.push(" 建议:在保持落地页质量的前提下开启或扩大广告投放,并持续监控 Lighthouse 与转化跟踪。</p>");
|
|
397
|
-
} else if (r.id === "s3") {
|
|
398
|
-
lines.push(" 建议:优先处理营销基础、转化路径与加载速度后再加大预算,避免高 CPC 低转化。</p>");
|
|
399
|
-
} else {
|
|
400
|
-
lines.push(" 建议:先完成本报告「优先改进计划」中的高优先级项,再考虑投放;否则预算浪费风险较高。</p>");
|
|
401
|
-
}
|
|
402
|
-
lines.push(
|
|
403
|
-
"<p class=\"mt-2 text-slate-500\">本报告依据页面 HTML 与 Lighthouse 数据生成,未出现的指标请勿臆测。可与 Google 账户分析、广告诊断页配合使用(二者与「网站诊断」数据源不同)。</p>",
|
|
404
|
-
);
|
|
405
|
-
return lines.join("");
|
|
406
|
-
}
|
|
1822
|
+
const dash = ((Number(data.score) || 0) / 100) * 471;
|
|
407
1823
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
1824
|
+
document.getElementById("app").innerHTML = `
|
|
1825
|
+
<div class="report-container">
|
|
1826
|
+
<div class="report-content" id="report-root">
|
|
1827
|
+
<div class="report-header">
|
|
1828
|
+
<div class="header-title-wrapper">
|
|
1829
|
+
<h1 class="main-title">网站诊断报告</h1>
|
|
1830
|
+
<div class="export-btn-wrapper header-actions">
|
|
1831
|
+
<button id="btn-theme-toggle" class="btn-theme" type="button">🌙 夜晚</button>
|
|
1832
|
+
<button class="btn-export" type="button" onclick="window.print()">
|
|
1833
|
+
<svg viewBox="0 0 1024 1024" width="16" height="16"><path d="M832 512c-17.6 0-32 14.4-32 32v256c0 17.6-14.4 32-32 32H256c-17.6 0-32-14.4-32-32V544c0-17.6-14.4-32-32-32s-32 14.4-32 32v256c0 52.8 43.2 96 96 96h512c52.8 0 96-43.2 96-96V544c0-17.6-14.4-32-32-32z" fill="currentColor"/><path d="M505.6 646.4c6.4 6.4 14.4 9.6 22.4 9.6s16-3.2 22.4-9.6l192-192c12.8-12.8 12.8-32 0-44.8s-32-12.8-44.8 0L544 563.2V160c0-17.6-14.4-32-32-32s-32 14.4-32 32v403.2L326.4 409.6c-12.8-12.8-32-12.8-44.8 0s-12.8 32 0 44.8l224 192z" fill="currentColor"/></svg>
|
|
1834
|
+
导出PDF
|
|
1835
|
+
</button>
|
|
1836
|
+
</div>
|
|
1837
|
+
</div>
|
|
1838
|
+
<div class="header-info">
|
|
1839
|
+
<span class="info-item">网站地址:<a class="website-url" href="${escapeHtml(data.url)}" target="_blank" rel="noopener">${escapeHtml(data.url)}</a></span>
|
|
1840
|
+
<span class="info-separator">|</span>
|
|
1841
|
+
<span class="info-item">诊断时间:${escapeHtml(formatDate(data.analyzedAt))}</span>
|
|
1842
|
+
</div>
|
|
1843
|
+
</div>
|
|
411
1844
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
1845
|
+
<div class="report-section">
|
|
1846
|
+
<div class="section-header"><h2><span>一 </span><span class="section-title">网站健康快览</span></h2></div>
|
|
1847
|
+
<div class="marketing-text">
|
|
1848
|
+
<p><span>您的网站总评分</span><strong>${Number(data.score).toFixed(1)}</strong><span>分!</span></p>
|
|
1849
|
+
<p>若优化到位,广告投放回报率可提升约30%,直接带来成本节省和更多潜在客户!</p>
|
|
1850
|
+
</div>
|
|
1851
|
+
<div class="health-overview">
|
|
1852
|
+
<div class="website-score-wrapper">
|
|
1853
|
+
<div class="score-ring-wrapper">
|
|
1854
|
+
<svg class="score-ring" width="180" height="180" viewBox="0 0 180 180">
|
|
1855
|
+
<circle class="score-ring-track" cx="90" cy="90" r="75" fill="none" stroke="#e5e6eb" stroke-width="16"/>
|
|
1856
|
+
<circle cx="90" cy="90" r="75" fill="none" stroke="${ratingColor}" stroke-width="16" stroke-linecap="round" stroke-dasharray="${dash} 471" transform="rotate(-90 90 90)"/>
|
|
1857
|
+
</svg>
|
|
1858
|
+
<div class="score-center">
|
|
1859
|
+
<div class="score-number">${Number(data.score).toFixed(1)}</div>
|
|
1860
|
+
<div class="score-total">/ 100</div>
|
|
1861
|
+
</div>
|
|
1862
|
+
<div class="score-rating" style="color:${ratingColor}">${escapeHtml(industry.status)}</div>
|
|
1863
|
+
<div class="score-desc">${escapeHtml(industry.content)}</div>
|
|
1864
|
+
</div>
|
|
1865
|
+
</div>
|
|
1866
|
+
<div class="divider-line"></div>
|
|
1867
|
+
<div class="radar-chart">
|
|
1868
|
+
<div id="chart-radar" class="radar-chart-container" style="width:100%;height:350px"></div>
|
|
1869
|
+
<div class="radar-chart-title">各模块健康度分布</div>
|
|
1870
|
+
</div>
|
|
1871
|
+
</div>
|
|
1872
|
+
</div>
|
|
1873
|
+
|
|
1874
|
+
<div class="report-section">
|
|
1875
|
+
<div class="section-header"><h2><span>二 </span><span class="section-title">网站风险模块分级与优先级处理指南</span></h2></div>
|
|
1876
|
+
<div class="marketing-text">
|
|
1877
|
+
<p>从图中可以看到,高风险问题主要集中在营销基础和追踪模块。</p>
|
|
1878
|
+
<p>优先处理这些模块,可最快提升广告效果和转化率。</p>
|
|
1879
|
+
</div>
|
|
1880
|
+
<div class="risk-map-content">
|
|
1881
|
+
<div class="bar-chart-wrapper"><div id="chart-risk-bar" class="bar-chart-container" style="width:100%;height:100%"></div></div>
|
|
1882
|
+
<div class="risk-table-wrapper">
|
|
1883
|
+
<p class="risk-table-title">满分 100 分</p>
|
|
1884
|
+
<table class="risk-table">
|
|
1885
|
+
<thead><tr><th>模块</th><th>得分</th><th>影响程度</th><th>改进难度</th></tr></thead>
|
|
1886
|
+
<tbody>${riskMapData
|
|
436
1887
|
.map(
|
|
437
|
-
(
|
|
438
|
-
`<
|
|
439
|
-
<span class="font-medium text-rose-700">[${ci.priority}]</span> ${ci.issue}
|
|
440
|
-
<span class="text-slate-500"> — ${ci.impact}</span>
|
|
441
|
-
</li>`,
|
|
1888
|
+
(row) =>
|
|
1889
|
+
`<tr><td>${escapeHtml(row.module)}</td><td class="tc">${escapeHtml(row.score)}</td><td class="tc">${row.impact}</td><td class="tc">${row.difficulty}</td></tr>`,
|
|
442
1890
|
)
|
|
443
|
-
.join("")
|
|
444
|
-
} else {
|
|
445
|
-
ciList.innerHTML = '<li class="text-slate-500">未标注核心问题 ID。</li>';
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
const modBox = document.getElementById("modules-container");
|
|
449
|
-
modBox.innerHTML = (d.modules || [])
|
|
450
|
-
.map((mod, idx) => {
|
|
451
|
-
const rows = (mod.items || [])
|
|
452
|
-
.map(
|
|
453
|
-
(it) => `<tr class="border-t border-slate-100">
|
|
454
|
-
<td class="py-2 pr-2">${it.name}</td>
|
|
455
|
-
<td class="py-2 whitespace-nowrap">${it.score ?? 0} / ${it.totalScore}</td>
|
|
456
|
-
<td class="py-2 ${statusClass(it.status)}">${STATUS_ZH[it.status] || it.status || "—"}</td>
|
|
457
|
-
<td class="py-2 text-slate-600 max-w-md">${it.issue || "—"}</td>
|
|
458
|
-
<td class="py-2 text-slate-700 max-w-md">${it.suggestion || "—"}</td>
|
|
459
|
-
</tr>`,
|
|
460
|
-
)
|
|
461
|
-
.join("");
|
|
462
|
-
return `
|
|
463
|
-
<div>
|
|
464
|
-
<h3 class="font-semibold text-slate-900">${idx + 1}. ${mod.name}
|
|
465
|
-
<span class="text-sm font-normal text-slate-500">(${mod.score}/${mod.totalScore})</span>
|
|
466
|
-
</h3>
|
|
467
|
-
<table class="mt-2 w-full text-sm">
|
|
468
|
-
<thead><tr class="text-slate-500 border-b">
|
|
469
|
-
<th class="text-left pb-2">诊断项</th><th class="pb-2">得分</th><th class="pb-2">状态</th>
|
|
470
|
-
<th class="pb-2">问题</th><th class="pb-2">建议</th>
|
|
471
|
-
</tr></thead>
|
|
472
|
-
<tbody>${rows}</tbody>
|
|
1891
|
+
.join("")}</tbody>
|
|
473
1892
|
</table>
|
|
474
|
-
</div
|
|
475
|
-
|
|
476
|
-
|
|
1893
|
+
</div>
|
|
1894
|
+
</div>
|
|
1895
|
+
</div>
|
|
1896
|
+
|
|
1897
|
+
<div class="report-section">
|
|
1898
|
+
<div class="section-header"><h2><span>三 </span><span class="section-title">六大模块详细诊断结果</span></h2></div>
|
|
1899
|
+
${(data.modules || []).map((m, i) => renderModuleDetail(m, i, data.lighthouseResult)).join("")}
|
|
1900
|
+
</div>
|
|
477
1901
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
1902
|
+
<div class="report-section">
|
|
1903
|
+
<div class="section-header"><h2><span>四 </span><span class="section-title">优化优先级与行动计划</span></h2></div>
|
|
1904
|
+
<div class="marketing-text">
|
|
1905
|
+
<p>行动计划以任务化方式呈现,您可以直接分配给团队执行。</p>
|
|
1906
|
+
<p>高优先级任务可优先完成,快速释放广告投放价值!</p>
|
|
1907
|
+
</div>
|
|
1908
|
+
<div class="priority-columns">${renderPriorityPlan()}</div>
|
|
1909
|
+
</div>
|
|
1910
|
+
|
|
1911
|
+
<div class="report-section">
|
|
1912
|
+
<div class="section-header"><h2><span>五 </span><span class="section-title">长期价值</span></h2></div>
|
|
1913
|
+
<div class="marketing-text"><p>建议建立季度诊断机制,持续追踪ROI变化,为广告复盘与长期优化提供数据支撑。</p></div>
|
|
1914
|
+
<div class="longterm-cards">${renderLongtermValue()}</div>
|
|
1915
|
+
</div>
|
|
1916
|
+
</div>
|
|
1917
|
+
</div>`;
|
|
1918
|
+
|
|
1919
|
+
chartModulesCache = data.modules || [];
|
|
1920
|
+
chartRiskCache = riskMapData;
|
|
1921
|
+
radarChartInst = initRadarChart(document.getElementById("chart-radar"), chartModulesCache);
|
|
1922
|
+
riskChartInst = initRiskBarChart(document.getElementById("chart-risk-bar"), chartRiskCache);
|
|
1923
|
+
|
|
1924
|
+
document.getElementById("btn-theme-toggle")?.addEventListener("click", toggleTheme);
|
|
1925
|
+
updateThemeToggleLabel();
|
|
1926
|
+
updateScoreRingTracks();
|
|
1927
|
+
|
|
1928
|
+
const resize = () => {
|
|
1929
|
+
radarChartInst?.resize();
|
|
1930
|
+
riskChartInst?.resize();
|
|
1931
|
+
};
|
|
1932
|
+
window.addEventListener("resize", resize);
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
if (window.__WEBSITE_DIAGNOSIS__) {
|
|
1936
|
+
if (document.readyState === "loading") {
|
|
1937
|
+
document.addEventListener("DOMContentLoaded", () => renderReport(window.__WEBSITE_DIAGNOSIS__));
|
|
485
1938
|
} else {
|
|
486
|
-
|
|
487
|
-
initLighthouseChart(document.getElementById("chart-lighthouse"), lh);
|
|
488
|
-
document.getElementById("lighthouse-tables").innerHTML = lighthouseTable(lh);
|
|
1939
|
+
renderReport(window.__WEBSITE_DIAGNOSIS__);
|
|
489
1940
|
}
|
|
490
|
-
|
|
491
|
-
document.getElementById("priority-plan-body").innerHTML = buildPriorityPlan(d);
|
|
492
|
-
document.getElementById("longterm-value-body").innerHTML = longtermCopy(d);
|
|
493
1941
|
}
|
|
1942
|
+
})();
|
|
1943
|
+
|
|
1944
|
+
</script>
|
|
1945
|
+
</body>
|
|
494
1946
|
|
|
495
|
-
|
|
496
|
-
</script>
|
|
497
|
-
</body>
|
|
498
|
-
</html>
|
|
1947
|
+
</html>
|