diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 9f0be8d..c038553 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,6 +5,7 @@ import { Suspense } from "react"; import { AdDialogSlot } from "@/components/ad/ad-dialog-slot"; import { AddressDialogSlot } from "@/components/address/address-dialog-slot"; import { AgeGateDialogHost } from "@/components/age-gate/age-gate-dialog"; +import { SiteAnalytics } from "@/components/analytics/site-analytics"; import { RequestHeadersPrime } from "@/components/api/request-headers-prime"; import { AuthUnauthorizedBridge } from "@/components/auth/auth-unauthorized-bridge"; import { GuestAuthBootstrap } from "@/components/auth/guest-auth-bootstrap"; @@ -92,6 +93,9 @@ export default function RootLayout({ children }: LayoutProps<"/">) { + + + {children} diff --git a/src/components/analytics/site-analytics.tsx b/src/components/analytics/site-analytics.tsx new file mode 100644 index 0000000..68f0543 --- /dev/null +++ b/src/components/analytics/site-analytics.tsx @@ -0,0 +1,66 @@ +"use client"; + +import { usePathname, useSearchParams } from "next/navigation"; +import Script from "next/script"; +import { useEffect, useRef } from "react"; + +/** Google Analytics 4 */ +const GA_ID = "G-5XKDE5QPTF"; +/** Yandex.Metrika 计数器 */ +const YM_ID = 113808100; + +declare global { + interface Window { + ym?: (id: number, action: string, ...args: unknown[]) => void; + } +} + +/** + * 站点统计:GA4 + Yandex.Metrika。 + * GA4 增强型衡量会按浏览器历史变化自动记录浏览量; + * Metrika 在客户端路由切换时需手动发送 hit(首屏由 init 统计,不重复发)。 + */ +export function SiteAnalytics() { + const pathname = usePathname(); + const searchParams = useSearchParams(); + const lastUrl = useRef(null); + + useEffect(() => { + const query = searchParams.toString(); + const url = `${pathname}${query ? `?${query}` : ""}`; + if (lastUrl.current === null) { + lastUrl.current = url; + return; + } + if (lastUrl.current === url) return; + const referer = new URL(lastUrl.current, location.origin).href; + lastUrl.current = url; + window.ym?.(YM_ID, "hit", location.href, { referer, title: document.title }); + }, [pathname, searchParams]); + + return ( + <> + + + + + ); +}