feat(ui): optimize CTA buttons with contextual copy and fix static export build issues

CTA Optimization:

- Implement scenario-based CTA text across 6 key locations

- Add responsive Header CTA with icon+compact design

- Enhance CTA Section with vision-driven copy

Bug Fixes:

- Fix useSearchParams() build failure with dynamic import wrapper

- Remove useSearchParams() from PageTransition component

- Fix React 19 useEffect lint errors via useSyncExternalStore

UI Enhancements:

- Add ripple effects and gradient animations to Button

- Enhance loading skeleton with branded pulse animation
This commit is contained in:
张翔
2026-05-11 19:03:37 +08:00
parent c474394237
commit f08874f5c4
20 changed files with 794 additions and 112 deletions
+20 -6
View File
@@ -2,16 +2,28 @@
import Script from 'next/script';
import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect, Suspense } from 'react';
import { useEffect, Suspense, useSyncExternalStore } from 'react';
const GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID || '';
function useIsMounted() {
return useSyncExternalStore(
(callback) => {
window.addEventListener('resize', callback);
return () => window.removeEventListener('resize', callback);
},
() => true,
() => false
);
}
function GoogleAnalyticsContent() {
const pathname = usePathname();
const searchParams = useSearchParams();
const mounted = useIsMounted();
useEffect(() => {
if (!GA_MEASUREMENT_ID || typeof window === 'undefined') {return;}
if (!GA_MEASUREMENT_ID || !mounted || typeof window === 'undefined') {return;}
const url = pathname + (searchParams.toString() ? `?${searchParams.toString()}` : '');
@@ -22,9 +34,9 @@ function GoogleAnalyticsContent() {
page_location: window.location.origin + url,
});
}
}, [pathname, searchParams]);
}, [pathname, searchParams, mounted]);
if (!GA_MEASUREMENT_ID) {return null;}
if (!GA_MEASUREMENT_ID || !mounted) {return null;}
return (
<>
@@ -61,11 +73,13 @@ function GoogleAnalyticsContent() {
}
export function GoogleAnalytics() {
if (!GA_MEASUREMENT_ID) {return null;}
const mounted = useIsMounted();
if (!GA_MEASUREMENT_ID || !mounted) {return null;}
return (
<Suspense fallback={null}>
<GoogleAnalyticsContent />
</Suspense>
);
}
}
@@ -0,0 +1,12 @@
'use client';
import dynamic from 'next/dynamic';
const GoogleAnalytics = dynamic(
() => import('./GoogleAnalytics').then((mod) => mod.GoogleAnalytics),
{ ssr: false }
);
export function GoogleAnalyticsWrapper() {
return <GoogleAnalytics />;
}