@@ -12,6 +12,27 @@ useEcharts([RadarChart, RadarComponent, TooltipComponent, LegendComponent, Canva
1212
1313const SLIDE_TRANSITION_MS = 820 ;
1414const 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
1637const 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
0 commit comments