Archive old component versions no longer in use: - home-content.tsx (orphan, superseded by v14) - solution-detail-content-v1/v2 (superseded by v3) - service-detail-content-v1/v2/v3 (superseded by v4) - news-detail-content-v1/v2 (superseded by v3) All pages verified to use CMS data layer: home, products, solutions, services, cases, news, about, team, contact Quality gates: type-check, 992 tests, coverage all passing.
456 lines
18 KiB
TypeScript
456 lines
18 KiB
TypeScript
'use client';
|
|
|
|
import { motion } from 'framer-motion';
|
|
import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal';
|
|
import { Button } from '@/components/ui/button';
|
|
import { ArrowRight, Factory, ShoppingCart, Heart, GraduationCap, Lightbulb, Settings, Users, TrendingUp, BookOpen, MessageSquare, Calendar, Smartphone, Package, BarChart3 } from 'lucide-react';
|
|
import { cn } from '@/lib/utils';
|
|
import type { Solution } from '@/lib/constants/solutions';
|
|
import { PRODUCTS } from '@/lib/constants/products';
|
|
|
|
const EASE_OUT = [0.22, 1, 0.36, 1] as const;
|
|
|
|
const INDUSTRY_ICONS: Record<string, React.ReactNode> = {
|
|
制造业: <Factory className="w-8 h-8" />,
|
|
零售业: <ShoppingCart className="w-8 h-8" />,
|
|
医疗: <Heart className="w-8 h-8" />,
|
|
教育: <GraduationCap className="w-8 h-8" />,
|
|
};
|
|
|
|
const VALUE_ICONS: Record<string, React.ReactNode> = {
|
|
Factory: <Factory className="w-6 h-6" />,
|
|
Package: <Package className="w-6 h-6" />,
|
|
BarChart3: <BarChart3 className="w-6 h-6" />,
|
|
Users: <Users className="w-6 h-6" />,
|
|
TrendingUp: <TrendingUp className="w-6 h-6" />,
|
|
ShoppingCart: <ShoppingCart className="w-6 h-6" />,
|
|
GraduationCap: <GraduationCap className="w-6 h-6" />,
|
|
BookOpen: <BookOpen className="w-6 h-6" />,
|
|
MessageSquare: <MessageSquare className="w-6 h-6" />,
|
|
Heart: <Heart className="w-6 h-6" />,
|
|
Calendar: <Calendar className="w-6 h-6" />,
|
|
Smartphone: <Smartphone className="w-6 h-6" />,
|
|
};
|
|
|
|
function GrainOverlay() {
|
|
return (
|
|
<div
|
|
className="absolute inset-0 pointer-events-none opacity-[0.03] mix-blend-overlay"
|
|
style={{
|
|
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E")`,
|
|
backgroundRepeat: 'repeat',
|
|
backgroundSize: '300px 300px',
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
|
|
function SectionLabel({ children, className = '' }: { children: React.ReactNode; className?: string }) {
|
|
return (
|
|
<div className={cn('flex items-center gap-5 mb-7', className)}>
|
|
<div className="w-14 h-px bg-brand" />
|
|
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
|
|
{children}
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface DetailHeroProps {
|
|
solution: Solution;
|
|
}
|
|
|
|
function DetailHero({ solution }: DetailHeroProps) {
|
|
const industryIcon = INDUSTRY_ICONS[solution.industry] || <Factory className="w-8 h-8" />;
|
|
|
|
return (
|
|
<section className="relative min-h-[80vh] flex items-center overflow-hidden bg-ink pt-24">
|
|
<GrainOverlay />
|
|
|
|
<div className="absolute inset-0 pointer-events-none">
|
|
<div
|
|
className="absolute top-0 right-0 w-[50%] h-full"
|
|
style={{
|
|
background: 'linear-gradient(135deg, transparent 20%, rgba(196, 30, 58, 0.08) 100%)',
|
|
clipPath: 'polygon(30% 0, 100% 0, 100% 100%, 0% 100%)',
|
|
}}
|
|
/>
|
|
|
|
<div className="absolute bottom-0 left-0 w-full h-1/3 bg-gradient-to-t from-ink to-transparent" />
|
|
|
|
<div className="absolute top-32 right-[15%] w-[400px] h-[400px] rounded-full opacity-10 blur-3xl bg-brand" />
|
|
</div>
|
|
|
|
<div className="max-w-container mx-auto px-6 lg:px-10 relative z-10 w-full py-24 lg:py-32">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.8, ease: EASE_OUT }}
|
|
className="flex items-center gap-4 mb-8"
|
|
>
|
|
<div className="w-14 h-14 rounded-2xl bg-brand-soft flex items-center justify-center text-brand">
|
|
{industryIcon}
|
|
</div>
|
|
<div>
|
|
<span className="px-3 py-1.5 text-xs font-bold text-brand bg-brand-soft/50">
|
|
{solution.industry}
|
|
</span>
|
|
<div className="text-dark-text-muted text-sm mt-2">{solution.subtitle}</div>
|
|
</div>
|
|
</motion.div>
|
|
|
|
<motion.h1
|
|
initial={{ opacity: 0, y: 50 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 1, delay: 0.15, ease: EASE_OUT }}
|
|
className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-white leading-tight tracking-tighter mb-8 max-w-4xl"
|
|
>
|
|
{solution.title}
|
|
</motion.h1>
|
|
|
|
<motion.p
|
|
initial={{ opacity: 0, y: 40 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.9, delay: 0.3, ease: EASE_OUT }}
|
|
className="text-xl lg:text-2xl text-dark-text-secondary max-w-3xl leading-relaxed mb-12"
|
|
>
|
|
{solution.description}
|
|
</motion.p>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 40 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.9, delay: 0.45, ease: EASE_OUT }}
|
|
className="flex flex-wrap gap-6"
|
|
>
|
|
<Button size="xl" asChild>
|
|
<a href="/contact">
|
|
获取方案详情
|
|
<ArrowRight className="w-5 h-5 ml-2" />
|
|
</a>
|
|
</Button>
|
|
<Button size="xl" variant="outline" asChild>
|
|
<a href="/products">查看产品</a>
|
|
</Button>
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
interface ChallengesSectionProps {
|
|
challenges: string[];
|
|
}
|
|
|
|
function ChallengesSection({ challenges }: ChallengesSectionProps) {
|
|
return (
|
|
<section className="relative py-32 lg:py-40 overflow-hidden bg-ink-light">
|
|
<GrainOverlay />
|
|
|
|
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-white/12 to-transparent" />
|
|
|
|
<div className="max-w-container mx-auto px-6 lg:px-10">
|
|
<div className="grid lg:grid-cols-2 gap-20 items-start">
|
|
<ScrollReveal>
|
|
<SectionLabel>Challenges</SectionLabel>
|
|
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold text-white tracking-tight leading-tight mb-8">
|
|
行业痛点
|
|
</h2>
|
|
<p className="text-dark-text-secondary text-lg leading-relaxed">
|
|
我们深入理解行业面临的核心挑战,提供针对性的解决方案
|
|
</p>
|
|
</ScrollReveal>
|
|
|
|
<div className="space-y-5">
|
|
<StaggerReveal staggerDelay={0.1} delayChildren={0.05}>
|
|
{challenges.map((challenge, idx) => (
|
|
<motion.div
|
|
key={idx}
|
|
className="group relative p-8 border border-white/10 bg-ink hover:border-brand/30 transition-all duration-500"
|
|
whileHover={{ x: 12 }}
|
|
>
|
|
<div className="absolute left-0 top-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-500 origin-top" />
|
|
<div className="flex items-start gap-6">
|
|
<div className="w-10 h-10 rounded-xl bg-brand-soft/50 flex items-center justify-center text-brand shrink-0 mt-0.5">
|
|
<span className="text-lg font-bold">{idx + 1}</span>
|
|
</div>
|
|
<p className="text-lg text-white leading-relaxed">{challenge}</p>
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</StaggerReveal>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
interface SolutionsSectionProps {
|
|
solutions: string[];
|
|
}
|
|
|
|
function SolutionsSection({ solutions }: SolutionsSectionProps) {
|
|
return (
|
|
<section className="relative py-32 lg:py-40 overflow-hidden bg-ink">
|
|
<GrainOverlay />
|
|
|
|
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-white/12 to-transparent" />
|
|
|
|
<div className="max-w-container mx-auto px-6 lg:px-10">
|
|
<ScrollReveal className="text-center max-w-3xl mx-auto mb-24">
|
|
<SectionLabel className="justify-center">
|
|
<div className="flex items-center gap-5">
|
|
<div className="w-14 h-px bg-brand" />
|
|
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
|
|
Solutions
|
|
</span>
|
|
<div className="w-14 h-px bg-brand" />
|
|
</div>
|
|
</SectionLabel>
|
|
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold text-white tracking-tight leading-tight">
|
|
解决方案
|
|
</h2>
|
|
</ScrollReveal>
|
|
|
|
<div className="grid md:grid-cols-2 gap-8">
|
|
<StaggerReveal staggerDelay={0.1} delayChildren={0.05}>
|
|
{solutions.map((solution, idx) => (
|
|
<motion.div
|
|
key={idx}
|
|
className="group relative p-10 border border-white/10 bg-ink-light hover:border-brand/40 transition-all duration-500 hover:-translate-y-2"
|
|
>
|
|
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-left" />
|
|
|
|
<div className="flex items-start gap-6">
|
|
<div className="w-14 h-14 rounded-2xl bg-brand-soft flex items-center justify-center text-brand shrink-0">
|
|
<Lightbulb className="w-7 h-7" />
|
|
</div>
|
|
<div>
|
|
<div className="text-xs font-bold text-brand mb-3 tracking-widest uppercase">
|
|
方案 {idx + 1}
|
|
</div>
|
|
<p className="text-xl text-white font-semibold leading-relaxed">{solution}</p>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</StaggerReveal>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
interface ValuePropositionProps {
|
|
valueProposition: Solution['valueProposition'];
|
|
}
|
|
|
|
function ValuePropositionSection({ valueProposition }: ValuePropositionProps) {
|
|
return (
|
|
<section className="relative py-32 lg:py-40 overflow-hidden bg-ink-light">
|
|
<GrainOverlay />
|
|
|
|
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-white/12 to-transparent" />
|
|
|
|
<div className="max-w-container mx-auto px-6 lg:px-10">
|
|
<ScrollReveal className="text-center max-w-3xl mx-auto mb-24">
|
|
<SectionLabel className="justify-center">
|
|
<div className="flex items-center gap-5">
|
|
<div className="w-14 h-px bg-brand" />
|
|
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
|
|
Value Proposition
|
|
</span>
|
|
<div className="w-14 h-px bg-brand" />
|
|
</div>
|
|
</SectionLabel>
|
|
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold text-white tracking-tight leading-tight">
|
|
{valueProposition.headline}
|
|
</h2>
|
|
</ScrollReveal>
|
|
|
|
<div className="grid md:grid-cols-3 gap-8">
|
|
<StaggerReveal staggerDelay={0.12} delayChildren={0.05}>
|
|
{valueProposition.points.map((point, idx) => {
|
|
const icon = VALUE_ICONS[point.icon] || <Settings className="w-6 h-6" />;
|
|
return (
|
|
<motion.div
|
|
key={idx}
|
|
className="group relative p-10 text-center border border-white/10 bg-ink hover:border-brand/40 transition-all duration-500 hover:-translate-y-2"
|
|
>
|
|
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-1/3 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-center" />
|
|
|
|
<div className="w-20 h-20 rounded-3xl bg-brand-soft flex items-center justify-center text-brand mx-auto mb-8 group-hover:scale-110 transition-transform duration-500">
|
|
{icon}
|
|
</div>
|
|
|
|
<h3 className="text-2xl font-bold text-white mb-4">{point.title}</h3>
|
|
<p className="text-dark-text-secondary leading-relaxed">{point.description}</p>
|
|
</motion.div>
|
|
);
|
|
})}
|
|
</StaggerReveal>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
interface SuiteCombinationProps {
|
|
suiteCombination: Solution['suiteCombination'];
|
|
}
|
|
|
|
function SuiteCombinationSection({ suiteCombination }: SuiteCombinationProps) {
|
|
const primaryProducts = suiteCombination.primaryProducts
|
|
.map(pid => PRODUCTS.find(p => p.id === pid))
|
|
.filter(Boolean);
|
|
|
|
return (
|
|
<section className="relative py-32 lg:py-40 overflow-hidden bg-ink">
|
|
<GrainOverlay />
|
|
|
|
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-white/12 to-transparent" />
|
|
|
|
<div className="max-w-container mx-auto px-6 lg:px-10">
|
|
<ScrollReveal className="text-center max-w-3xl mx-auto mb-24">
|
|
<SectionLabel className="justify-center">
|
|
<div className="flex items-center gap-5">
|
|
<div className="w-14 h-px bg-brand" />
|
|
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
|
|
Suite Combination
|
|
</span>
|
|
<div className="w-14 h-px bg-brand" />
|
|
</div>
|
|
</SectionLabel>
|
|
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold text-white tracking-tight leading-tight">
|
|
推荐产品组合
|
|
</h2>
|
|
</ScrollReveal>
|
|
|
|
<div className="grid lg:grid-cols-5 gap-8 mb-16">
|
|
<StaggerReveal staggerDelay={0.1} delayChildren={0.05}>
|
|
{primaryProducts.map((product) => product && (
|
|
<motion.div
|
|
key={product.id}
|
|
className="group relative p-8 border border-white/10 bg-ink-light hover:border-brand/40 transition-all duration-500 hover:-translate-y-2 text-center lg:col-span-2"
|
|
>
|
|
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-left" />
|
|
|
|
<div className="w-16 h-16 rounded-2xl bg-brand-soft flex items-center justify-center text-brand mx-auto mb-6 group-hover:scale-110 transition-transform duration-500">
|
|
<Package className="w-8 h-8" />
|
|
</div>
|
|
|
|
<div className="text-xs font-bold text-brand mb-2 tracking-widest uppercase">
|
|
核心产品
|
|
</div>
|
|
<h3 className="text-xl font-bold text-white mb-3">{product.title}</h3>
|
|
<p className="text-sm text-dark-text-muted leading-relaxed">{product.description}</p>
|
|
</motion.div>
|
|
))}
|
|
</StaggerReveal>
|
|
|
|
<ScrollReveal delay={0.2} className="flex items-center justify-center lg:col-span-1">
|
|
<div className="w-20 h-20 rounded-full bg-brand-soft/30 border-2 border-brand/30 flex items-center justify-center text-brand font-bold text-xl">
|
|
+
|
|
</div>
|
|
</ScrollReveal>
|
|
|
|
<ScrollReveal delay={0.3} className="lg:col-span-2">
|
|
<div className="group relative p-8 border border-brand/20 bg-brand-soft/10 hover:border-brand/40 transition-all duration-500 text-center h-full flex flex-col justify-center">
|
|
<div className="w-16 h-16 rounded-2xl bg-brand-soft flex items-center justify-center text-brand mx-auto mb-6">
|
|
<Users className="w-8 h-8" />
|
|
</div>
|
|
|
|
<div className="text-xs font-bold text-brand mb-2 tracking-widest uppercase">
|
|
配套服务
|
|
</div>
|
|
<h3 className="text-xl font-bold text-white mb-3">专业实施服务</h3>
|
|
<p className="text-sm text-dark-text-secondary leading-relaxed">
|
|
{suiteCombination.complementaryServices.length}项服务配套,确保方案落地见效
|
|
</p>
|
|
</div>
|
|
</ScrollReveal>
|
|
</div>
|
|
|
|
<ScrollReveal>
|
|
<div className="p-10 border border-white/10 bg-ink-light text-center">
|
|
<div className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand mb-4">
|
|
方案价值
|
|
</div>
|
|
<p className="text-xl lg:text-2xl text-white leading-relaxed max-w-4xl mx-auto font-medium">
|
|
{suiteCombination.rationale}
|
|
</p>
|
|
</div>
|
|
</ScrollReveal>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
interface CTASectionProps {
|
|
solution: Solution;
|
|
}
|
|
|
|
function CTASection({ solution }: CTASectionProps) {
|
|
return (
|
|
<section className="relative py-36 lg:py-44 overflow-hidden bg-ink-light">
|
|
<GrainOverlay />
|
|
|
|
<div className="absolute inset-0 pointer-events-none">
|
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] rounded-full opacity-[0.08] blur-3xl bg-brand" />
|
|
</div>
|
|
|
|
<div className="max-w-container mx-auto px-6 lg:px-10 relative z-10">
|
|
<ScrollReveal className="max-w-3xl mx-auto text-center">
|
|
<SectionLabel className="justify-center">
|
|
<div className="flex items-center gap-5">
|
|
<div className="w-14 h-px bg-brand" />
|
|
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
|
|
Get Started
|
|
</span>
|
|
<div className="w-14 h-px bg-brand" />
|
|
</div>
|
|
</SectionLabel>
|
|
|
|
<h2 className="text-3xl md:text-4xl lg:text-6xl font-bold text-white tracking-tight leading-tight mb-8">
|
|
开启您的数字化转型之旅
|
|
</h2>
|
|
|
|
<p className="text-xl text-dark-text-secondary leading-relaxed mb-12 max-w-2xl mx-auto">
|
|
联系我们,获取{solution.title}的详细方案和专属报价
|
|
</p>
|
|
|
|
<div className="flex flex-wrap gap-6 justify-center">
|
|
<Button size="xl" asChild>
|
|
<a href="/contact">
|
|
预约咨询
|
|
<ArrowRight className="w-5 h-5 ml-2" />
|
|
</a>
|
|
</Button>
|
|
<Button size="xl" variant="outline" asChild>
|
|
<a href="/solutions">查看更多方案</a>
|
|
</Button>
|
|
</div>
|
|
</ScrollReveal>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
interface SolutionDetailContentV1Props {
|
|
solution: Solution;
|
|
}
|
|
|
|
export default function SolutionDetailContentV1({ solution }: SolutionDetailContentV1Props) {
|
|
return (
|
|
<main>
|
|
<DetailHero solution={solution} />
|
|
<ChallengesSection challenges={solution.challenges} />
|
|
<SolutionsSection solutions={solution.solutions} />
|
|
<ValuePropositionSection valueProposition={solution.valueProposition} />
|
|
<SuiteCombinationSection suiteCombination={solution.suiteCombination} />
|
|
<CTASection solution={solution} />
|
|
</main>
|
|
);
|
|
}
|