Skip to content

Commit b762162

Browse files
author
csFan
committed
feat: 为简历下载按钮添加点击统计和样式优化
- 提取不蒜子统计 API 调用逻辑为独立函数 `fetchBusuanziStats` - 新增 `handleResumeDownload` 函数处理简历下载点击事件,发送 GA/Umami 事件并记录独立统计 - 添加 `.resume-download-group` 和 `.resume-download-stats` CSS 类优化按钮布局和统计显示 - 在移动端隐藏下载统计以保持界面简洁
1 parent 25af2bd commit b762162

2 files changed

Lines changed: 68 additions & 17 deletions

File tree

src/App.jsx

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,27 @@ useEcharts([RadarChart, RadarComponent, TooltipComponent, LegendComponent, Canva
1212

1313
const SLIDE_TRANSITION_MS = 820;
1414
const PUBLIC_BASE_URL = (import.meta.env.BASE_URL || "/").replace(/\/?$/, "/");
15+
const BUSUANZI_API_ENDPOINT = "https://cdn.busuanzi.cc/api.php";
16+
17+
function toBusuanziStats(data) {
18+
const pvValue = data.busuanzi_site_pv ?? data.busuanzi_value_site_pv;
19+
const uvValue = data.busuanzi_site_uv ?? data.busuanzi_value_site_uv;
20+
return {
21+
pv: pvValue != null ? String(pvValue) : "--",
22+
uv: uvValue != null ? String(uvValue) : "--",
23+
};
24+
}
25+
26+
async function fetchBusuanziStats(url, referrer = "") {
27+
const response = await fetch(BUSUANZI_API_ENDPOINT, {
28+
method: "POST",
29+
headers: { "Content-Type": "application/json" },
30+
body: JSON.stringify({ url, referrer }),
31+
});
32+
if (!response.ok) throw new Error(`busuanzi http ${response.status}`);
33+
const data = await response.json();
34+
return toBusuanziStats(data);
35+
}
1536

1637
const artPhotoModules = import.meta.glob("../public/art/**/*.webp");
1738

@@ -383,6 +404,7 @@ export default function App() {
383404
const [sharePosterGeneratedAt, setSharePosterGeneratedAt] = useState("");
384405
const [showPosterGeneratingModal, setShowPosterGeneratingModal] = useState(false);
385406
const [busuanziStats, setBusuanziStats] = useState({ pv: "--", uv: "--" });
407+
const [resumeDownloadStats, setResumeDownloadStats] = useState({ pv: "--", uv: "--" });
386408
const [themeMode, setThemeMode] = useState(() => {
387409
if (typeof window === "undefined") return "dark";
388410
const saved = window.localStorage.getItem("flowfolio-theme-mode");
@@ -486,24 +508,11 @@ export default function App() {
486508
if (!unlocked || typeof window === "undefined") return undefined;
487509

488510
let disposed = false;
511+
const pageUrl = `${window.location.origin}${window.location.pathname}`;
489512
const refreshBusuanzi = async () => {
490513
try {
491-
const url = `${window.location.origin}${window.location.pathname}`;
492-
const response = await fetch("https://cdn.busuanzi.cc/api.php", {
493-
method: "POST",
494-
headers: { "Content-Type": "application/json" },
495-
body: JSON.stringify({ url, referrer: document.referrer || "" }),
496-
});
497-
if (!response.ok) throw new Error(`busuanzi http ${response.status}`);
498-
const data = await response.json();
499-
514+
const next = await fetchBusuanziStats(pageUrl, document.referrer || "");
500515
if (disposed) return;
501-
const pvValue = data.busuanzi_site_pv ?? data.busuanzi_value_site_pv;
502-
const uvValue = data.busuanzi_site_uv ?? data.busuanzi_value_site_uv;
503-
const next = {
504-
pv: pvValue != null ? String(pvValue) : "--",
505-
uv: uvValue != null ? String(uvValue) : "--",
506-
};
507516
setBusuanziStats(next);
508517
} catch (error) {
509518
console.error("busuanzi refresh failed", error);
@@ -972,6 +981,25 @@ export default function App() {
972981
setSharePosterGeneratedAt("");
973982
};
974983

984+
const handleResumeDownload = () => {
985+
if (typeof window === "undefined") return;
986+
987+
const pageUrl = `${window.location.origin}${window.location.pathname}`;
988+
const metricUrl = `${pageUrl}?metric=resume-download`;
989+
const eventProps = { file_name: "resume.pdf", source: "header" };
990+
991+
if (typeof window.gtag === "function") {
992+
window.gtag("event", "resume_download", eventProps);
993+
}
994+
if (typeof window.umami?.track === "function") {
995+
window.umami.track("resume_download", eventProps);
996+
}
997+
998+
fetchBusuanziStats(metricUrl, pageUrl)
999+
.then((next) => setResumeDownloadStats(next))
1000+
.catch((error) => console.error("resume download stats failed", error));
1001+
};
1002+
9751003
const sectionNodes = [
9761004
{
9771005
id: "overview",
@@ -1455,6 +1483,12 @@ export default function App() {
14551483
<Box component="span" className="site-counter">
14561484
{" · "}UV {busuanziStats.uv}
14571485
</Box>
1486+
<Box component="span" className="site-counter">
1487+
{" · "}下载PV {resumeDownloadStats.pv}
1488+
</Box>
1489+
<Box component="span" className="site-counter">
1490+
{" · "}下载UV {resumeDownloadStats.uv}
1491+
</Box>
14581492
</Typography>
14591493
</Box>
14601494
</Stack>
@@ -1502,8 +1536,10 @@ export default function App() {
15021536
</Button>
15031537
))}
15041538
</Stack>
1505-
<Button href={resumeDownloadPath} download variant="contained" startIcon={<Download size={18} />} className="desktop-resume-download">下载简历</Button>
1506-
<Button href={resumeDownloadPath} download variant="contained" startIcon={<Download size={16} />} size="small" className="mobile-resume-download">简历</Button>
1539+
<Box className="resume-download-group">
1540+
<Button href={resumeDownloadPath} download onClick={handleResumeDownload} variant="contained" startIcon={<Download size={18} />} className="desktop-resume-download">下载简历</Button>
1541+
<Button href={resumeDownloadPath} download onClick={handleResumeDownload} variant="contained" startIcon={<Download size={16} />} size="small" className="mobile-resume-download">简历</Button>
1542+
</Box>
15071543
</Box>
15081544
</AppBar>
15091545

src/styles.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ button {
102102
white-space: nowrap;
103103
}
104104

105+
.resume-download-group {
106+
display: flex;
107+
align-items: center;
108+
gap: 8px;
109+
}
110+
111+
.resume-download-stats {
112+
white-space: nowrap;
113+
font-family: var(--font-mono) !important;
114+
}
115+
105116
.brand,
106117
.period-label,
107118
.terminal-title,
@@ -1435,6 +1446,10 @@ button {
14351446
display: inline-flex !important;
14361447
}
14371448

1449+
.resume-download-stats {
1450+
display: none !important;
1451+
}
1452+
14381453
.slide-shell {
14391454
padding-top: calc(var(--nav-height) + 42px);
14401455
padding-bottom: 68px;

0 commit comments

Comments
 (0)