feat: 优化全局样式与组件视觉升级
- 更新 globals.css 全局样式 - 优化 not-found 404 页面 - 升级 footer, mega-dropdown, mobile-tab-bar 布局组件 - 改进 challenge, cta, hero 等核心 section 组件 - 优化 challenge-card, ink-glow-card, product-card 等 UI 组件 - 更新产品常量配置
This commit is contained in:
@@ -56,21 +56,14 @@ export function ChallengeSection() {
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 md:gap-8">
|
||||
{CHALLENGES.map((challenge, index) => (
|
||||
<motion.div
|
||||
<ChallengeCard
|
||||
key={challenge.id}
|
||||
initial={shouldReduceMotion ? {} : { opacity: 0, x: index === 0 ? -32 : index === 1 ? 0 : 32 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true, margin: '-80px' }}
|
||||
transition={{ duration: 0.55, delay: index * 0.12, ease: [0.16, 1, 0.3, 1] }}
|
||||
>
|
||||
<ChallengeCard
|
||||
title={challenge.title}
|
||||
description={challenge.description}
|
||||
scenario={challenge.scenario}
|
||||
href={challenge.href}
|
||||
index={index}
|
||||
/>
|
||||
</motion.div>
|
||||
title={challenge.title}
|
||||
description={challenge.description}
|
||||
scenario={challenge.scenario}
|
||||
href={challenge.href}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -55,64 +55,18 @@ export function CTASection({
|
||||
style={{ backgroundColor: 'var(--color-cta-bg)' }}
|
||||
>
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<svg className="absolute inset-0 w-full h-full" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<filter id="cta-grain">
|
||||
<feTurbulence
|
||||
type="fractalNoise"
|
||||
baseFrequency="0.75"
|
||||
numOctaves="4"
|
||||
stitchTiles="stitch"
|
||||
result="noise"
|
||||
/>
|
||||
<feColorMatrix type="saturate" values="0" in="noise" result="desaturated" />
|
||||
<feComponentTransfer in="desaturated" result="faded">
|
||||
<feFuncA type="linear" slope="0.04" />
|
||||
</feComponentTransfer>
|
||||
</filter>
|
||||
<rect width="100%" height="100%" filter="url(#cta-grain)" />
|
||||
</svg>
|
||||
|
||||
{!shouldReduceMotion && (
|
||||
<>
|
||||
<motion.div
|
||||
className="absolute inset-[-20%]"
|
||||
style={{
|
||||
background:
|
||||
'radial-gradient(circle at center, rgba(196, 30, 58, 0.08) 0%, transparent 50%)',
|
||||
x: glowX,
|
||||
y: glowY,
|
||||
opacity: isHovered ? 1 : 0.5,
|
||||
}}
|
||||
transition={{ opacity: { duration: 0.6 } }}
|
||||
/>
|
||||
<motion.div
|
||||
animate={{
|
||||
scale: [1, 1.15, 1],
|
||||
opacity: [0.06, 0.12, 0.06],
|
||||
}}
|
||||
transition={{
|
||||
duration: 8,
|
||||
repeat: Infinity,
|
||||
ease: 'easeInOut',
|
||||
}}
|
||||
className="absolute left-[10%] top-[20%] w-64 h-64 rounded-full"
|
||||
style={{ background: 'radial-gradient(circle, rgba(196,30,58,0.2), transparent 70%)' }}
|
||||
/>
|
||||
<motion.div
|
||||
animate={{
|
||||
scale: [1, 1.2, 1],
|
||||
opacity: [0.05, 0.1, 0.05],
|
||||
}}
|
||||
transition={{
|
||||
duration: 10,
|
||||
repeat: Infinity,
|
||||
ease: 'easeInOut',
|
||||
delay: 2,
|
||||
}}
|
||||
className="absolute right-[5%] bottom-[25%] w-80 h-80 rounded-full"
|
||||
style={{ background: 'radial-gradient(circle, rgba(196,30,58,0.15), transparent 70%)' }}
|
||||
/>
|
||||
</>
|
||||
<motion.div
|
||||
className="absolute inset-[-20%]"
|
||||
style={{
|
||||
background:
|
||||
'radial-gradient(circle at center, rgba(196, 30, 58, 0.08) 0%, transparent 50%)',
|
||||
x: glowX,
|
||||
y: glowY,
|
||||
opacity: isHovered ? 1 : 0.5,
|
||||
}}
|
||||
transition={{ opacity: { duration: 0.6 } }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
@@ -122,13 +76,6 @@ export function CTASection({
|
||||
'linear-gradient(to top, rgba(196, 30, 58, 0.06) 0%, transparent 40%), linear-gradient(135deg, transparent 40%, rgba(196, 30, 58, 0.03) 100%)',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage: `url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23C41E3A' fill-opacity='0.02'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="container-wide relative z-10">
|
||||
|
||||
@@ -54,61 +54,61 @@ export function HeroSectionV2() {
|
||||
>
|
||||
<HeroInkBackground />
|
||||
|
||||
<div className="container-wide py-24 md:py-32 lg:py-40 relative z-10 flex-1 flex items-center">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-20 items-center w-full">
|
||||
<div className="container-wide py-16 md:py-24 lg:py-32 relative z-10 flex-1 flex items-center">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10 lg:gap-16 items-center w-full">
|
||||
<div>
|
||||
<motion.div
|
||||
{...fadeUp(0)}
|
||||
className="mb-8"
|
||||
className="mb-6"
|
||||
>
|
||||
<span className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-[var(--color-brand-primary-bg)] text-[var(--color-brand-primary)] text-sm font-medium border border-[var(--color-brand-primary)]/10">
|
||||
<span className="inline-flex items-center gap-2 px-3 py-1.5 sm:px-4 sm:py-2 rounded-full bg-[var(--color-brand-primary-bg)] text-[var(--color-brand-primary)] text-xs sm:text-sm font-medium border border-[var(--color-brand-primary)]/10">
|
||||
{COMPANY_INFO.slogan}
|
||||
</span>
|
||||
</motion.div>
|
||||
|
||||
<motion.h1
|
||||
id="hero-heading"
|
||||
{...fadeUp(0.1)}
|
||||
className="text-6xl sm:text-7xl lg:text-8xl tracking-tight mb-6 font-brand"
|
||||
style={{ fontWeight: 'normal' }}
|
||||
>
|
||||
{COMPANY_INFO.shortName}
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
{...fadeUp(0.2)}
|
||||
className="text-xl sm:text-2xl text-[var(--color-text-primary)] mb-4"
|
||||
{...fadeUp(0.1)}
|
||||
className="text-lg sm:text-xl lg:text-2xl text-[var(--color-text-primary)] mb-3"
|
||||
>
|
||||
<span className="font-semibold text-[var(--color-brand-primary)] font-calligraphy">
|
||||
企业数字化转型服务商
|
||||
</span>
|
||||
</motion.p>
|
||||
|
||||
<motion.h1
|
||||
id="hero-heading"
|
||||
{...fadeUp(0.15)}
|
||||
className="text-5xl sm:text-6xl lg:text-7xl tracking-tight mb-5 font-brand leading-[1.1]"
|
||||
style={{ fontWeight: 'normal' }}
|
||||
>
|
||||
{COMPANY_INFO.shortName}
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
{...fadeUp(0.3)}
|
||||
className="text-lg text-[var(--color-text-muted)] max-w-xl leading-relaxed mb-10"
|
||||
{...fadeUp(0.25)}
|
||||
className="text-base sm:text-lg text-[var(--color-text-muted)] max-w-lg leading-relaxed mb-8"
|
||||
>
|
||||
{COMPANY_INFO.description}
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
{...fadeUp(0.4)}
|
||||
{...fadeUp(0.35)}
|
||||
className="flex flex-col sm:flex-row items-start gap-4"
|
||||
>
|
||||
<Button size="lg" asChild>
|
||||
<Button size="lg" asChild className="min-h-[52px] px-8 text-base font-semibold shadow-lg shadow-[var(--color-brand-primary)]/20">
|
||||
<StaticLink href="/contact">
|
||||
免费获取定制方案
|
||||
<ArrowRight className="w-4 h-4 ml-2" />
|
||||
</StaticLink>
|
||||
</Button>
|
||||
<Button size="lg" variant="outline" asChild>
|
||||
<Button size="lg" variant="ghost" asChild className="text-[var(--color-text-muted)]">
|
||||
<StaticLink href="/products">探索产品</StaticLink>
|
||||
</Button>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
{...fadeUp(0.5)}
|
||||
className="flex flex-wrap gap-3 mt-10 lg:hidden"
|
||||
{...fadeUp(0.45)}
|
||||
className="flex flex-wrap gap-2 sm:gap-3 mt-8"
|
||||
>
|
||||
{CAPABILITIES.map((cap) => {
|
||||
const Icon = cap.icon;
|
||||
@@ -129,7 +129,6 @@ export function HeroSectionV2() {
|
||||
initial={shouldReduceMotion ? {} : { opacity: 0, y: 32 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.5, ease: EASE }}
|
||||
className="hidden lg:block"
|
||||
>
|
||||
<div
|
||||
ref={cardRef}
|
||||
@@ -152,35 +151,35 @@ export function HeroSectionV2() {
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="relative rounded-2xl bg-[var(--color-bg-primary)]/80 backdrop-blur-sm p-8 lg:p-10">
|
||||
<div className="w-14 h-14 rounded-xl bg-[var(--color-brand-primary-bg)] flex items-center justify-center mb-6">
|
||||
<Handshake className="w-6 h-6 text-[var(--color-brand-primary)]" strokeWidth={1.8} />
|
||||
<div className="relative rounded-2xl bg-[var(--color-bg-primary)]/80 backdrop-blur-sm p-6 sm:p-8 lg:p-10">
|
||||
<div className="w-12 h-12 lg:w-14 lg:h-14 rounded-xl bg-[var(--color-brand-primary-bg)] flex items-center justify-center mb-5 lg:mb-6">
|
||||
<Handshake className="w-5 h-5 lg:w-6 lg:h-6 text-[var(--color-brand-primary)]" strokeWidth={1.8} />
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-semibold text-[var(--color-text-primary)] mb-2">
|
||||
<h3 className="text-lg lg:text-xl font-semibold text-[var(--color-text-primary)] mb-2">
|
||||
从沟通到落地,全程陪伴
|
||||
</h3>
|
||||
<p className="text-sm text-[var(--color-text-muted)] leading-relaxed mb-8">
|
||||
<p className="text-sm text-[var(--color-text-muted)] leading-relaxed mb-6 lg:mb-8">
|
||||
四步合作流程,确保每个项目科学推进、高效交付
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-3 lg:gap-4 lg:space-y-0 lg:block lg:space-y-4">
|
||||
{JOURNEY_STEPS.map((step, idx) => {
|
||||
const Icon = step.icon;
|
||||
return (
|
||||
<div
|
||||
key={step.label}
|
||||
className="flex items-center gap-4 p-3 rounded-lg bg-[var(--color-bg-section)]/60"
|
||||
className="flex items-center gap-3 lg:gap-4 p-2.5 lg:p-3 rounded-lg bg-[var(--color-bg-section)]/60"
|
||||
>
|
||||
<div className="w-9 h-9 rounded-lg bg-[var(--color-brand-primary-bg)] flex items-center justify-center shrink-0">
|
||||
<Icon className="w-4 h-4 text-[var(--color-brand-primary)]" strokeWidth={2.2} />
|
||||
<div className="w-8 h-8 lg:w-9 lg:h-9 rounded-lg bg-[var(--color-brand-primary-bg)] flex items-center justify-center shrink-0">
|
||||
<Icon className="w-3.5 h-3.5 lg:w-4 lg:h-4 text-[var(--color-brand-primary)]" strokeWidth={2.2} />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs font-bold text-[var(--color-brand-primary)]">0{idx + 1}</span>
|
||||
<span className="text-sm font-medium text-[var(--color-text-primary)]">{step.label}</span>
|
||||
<div className="flex items-center gap-1.5 lg:gap-2">
|
||||
<span className="text-[10px] lg:text-xs font-bold text-[var(--color-brand-primary)]">0{idx + 1}</span>
|
||||
<span className="text-xs lg:text-sm font-medium text-[var(--color-text-primary)]">{step.label}</span>
|
||||
</div>
|
||||
<p className="text-xs text-[var(--color-text-muted)] mt-0.5">{step.desc}</p>
|
||||
<p className="text-[10px] lg:text-xs text-[var(--color-text-muted)] mt-0.5 hidden sm:block">{step.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -7,12 +7,7 @@ import { METHODOLOGY } from '@/lib/constants/methodology';
|
||||
import { CheckCircle2 } from 'lucide-react';
|
||||
import { InkGlowCard } from '@/components/ui/ink-glow-card';
|
||||
|
||||
const phaseAccents: { rgb: string; glowStart: string; glowEnd: string }[] = [
|
||||
{ rgb: '196, 30, 58', glowStart: 'var(--color-brand-primary)', glowEnd: 'var(--color-warning)' },
|
||||
{ rgb: '217, 119, 6', glowStart: 'var(--color-warning)', glowEnd: 'var(--color-success)' },
|
||||
{ rgb: '22, 163, 74', glowStart: 'var(--color-success)', glowEnd: 'var(--color-accent-cyan)' },
|
||||
{ rgb: '37, 99, 235', glowStart: 'var(--color-accent-blue)', glowEnd: 'var(--color-accent-purple)' },
|
||||
];
|
||||
const phaseBgOpacities = [0.1, 0.08, 0.12, 0.06];
|
||||
|
||||
export function MethodologySection() {
|
||||
const ref = useRef(null);
|
||||
@@ -41,19 +36,16 @@ export function MethodologySection() {
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 md:gap-8">
|
||||
{METHODOLOGY.map((phase, idx) => {
|
||||
const accent = phaseAccents[idx % phaseAccents.length]!;
|
||||
const bgOpacity = phaseBgOpacities[idx % phaseBgOpacities.length];
|
||||
return (
|
||||
<InkGlowCard
|
||||
key={phase.id}
|
||||
index={idx}
|
||||
accentColorRgb={accent.rgb}
|
||||
glowStart={accent.glowStart}
|
||||
glowEnd={accent.glowEnd}
|
||||
>
|
||||
<div className="p-6 md:p-8">
|
||||
<div
|
||||
className="w-10 h-10 rounded-full flex items-center justify-center mb-5 text-sm font-bold"
|
||||
style={{ backgroundColor: accent.glowStart, color: 'var(--color-bg-primary)' }}
|
||||
style={{ backgroundColor: `rgba(var(--color-brand-primary-rgb), ${bgOpacity})`, color: 'var(--color-brand-primary)' }}
|
||||
>
|
||||
{phase.number}
|
||||
</div>
|
||||
|
||||
@@ -33,21 +33,14 @@ export function ProductMatrixSection() {
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-8">
|
||||
{PRODUCTS.map((product, index) => (
|
||||
<motion.div
|
||||
<ProductCard
|
||||
key={product.id}
|
||||
initial={shouldReduceMotion ? {} : { opacity: 0, y: 28 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: '-60px' }}
|
||||
transition={{ duration: 0.5, delay: index * 0.08, ease: [0.16, 1, 0.3, 1] }}
|
||||
>
|
||||
<ProductCard
|
||||
title={product.title}
|
||||
description={product.description}
|
||||
href={`/products/${product.id}`}
|
||||
index={index}
|
||||
status={product.status}
|
||||
/>
|
||||
</motion.div>
|
||||
title={product.title}
|
||||
description={product.description}
|
||||
href={`/products/${product.id}`}
|
||||
index={index}
|
||||
status={product.status}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@ export function SocialProofSection() {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
return (
|
||||
<section id="social-proof" role="region" aria-labelledby="social-proof-heading" className="py-16 md:py-20 bg-[var(--color-bg-section)]">
|
||||
<section id="social-proof" role="region" aria-labelledby="social-proof-heading" className="py-12 md:py-16 lg:py-20 bg-[var(--color-bg-section)]">
|
||||
<div className="container-wide">
|
||||
<motion.div
|
||||
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
|
||||
|
||||
Reference in New Issue
Block a user