refactor: 优化网站页面结构和数据展示

- 增强服务数据模型,添加 challenges 和 outcomes 字段
- 简化统计数据配置,改为静态定义
- 重构多个页面组件,优化代码结构
- 新增产品、服务、解决方案相关的布局和组件
- 更新样式和动画配置
- 优化测试用例和类型定义
- 修复 ESLint 错误:移除不必要的 useEffect 和未使用的导入
This commit is contained in:
张翔
2026-04-25 08:44:23 +08:00
parent 9650e56dcf
commit 40384ec024
77 changed files with 3751 additions and 1226 deletions
@@ -0,0 +1,31 @@
import { SOLUTIONS } from '@/lib/constants/solutions';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { SolutionDetailClient } from './solution-detail-client';
interface PageProps {
params: Promise<{ id: string }>;
}
export async function generateStaticParams() {
return SOLUTIONS.map((solution) => ({
id: solution.id,
}));
}
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { id } = await params;
const solution = SOLUTIONS.find((s) => s.id === id);
if (!solution) {return { title: '解决方案 - 睿新致远' };}
return {
title: `${solution.industry}数字化转型方案 - 睿新致远`,
description: solution.description,
};
}
export default async function SolutionDetailPage({ params }: PageProps) {
const { id } = await params;
const solution = SOLUTIONS.find((s) => s.id === id);
if (!solution) {notFound();}
return <SolutionDetailClient solutionId={id} />;
}
@@ -0,0 +1,218 @@
'use client';
import { SOLUTIONS } from '@/lib/constants/solutions';
import { PRODUCTS } from '@/lib/constants/products';
import { StaticLink } from '@/components/ui/static-link';
import { CheckCircle, Phone } from 'lucide-react';
import {
InkReveal,
FadeUp,
RippleButton,
FloatingElement,
StaggerContainer,
StaggerItem,
InkCard,
SealStamp,
} from '@/lib/animations';
import { ScrollReveal, inkRevealVariants, slideInLeftVariants } from '@/components/ui/scroll-animations';
interface SolutionDetailClientProps {
solutionId: string;
}
export function SolutionDetailClient({ solutionId }: SolutionDetailClientProps) {
const solution = SOLUTIONS.find((s) => s.id === solutionId) || null;
if (!solution) {
return (
<div className="min-h-screen flex items-center justify-center">
<p className="text-[#999999]">...</p>
</div>
);
}
return (
<main className="min-h-screen">
{/* Section 1: Hero */}
<section className="relative py-24 md:py-32 bg-gradient-to-b from-white to-[#F8F8F8]">
<div className="container-wide">
<div className="max-w-4xl mx-auto text-center">
<SealStamp
delay={0.1}
className="inline-block px-4 py-2 bg-[#C41E3A]/20 rounded-full text-[#C41E3A] text-sm mb-6"
>
{solution.industry}
</SealStamp>
<InkReveal delay={0.2}>
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold text-[#1C1C1C] mb-4">
{solution.title}
</h1>
</InkReveal>
<InkReveal delay={0.4}>
<p className="text-lg md:text-xl text-[#5C5C5C] mb-4">{solution.subtitle}</p>
</InkReveal>
<InkReveal delay={0.5}>
<p className="text-base text-[#5C5C5C] leading-relaxed mb-10 max-w-2xl mx-auto">
{solution.description}
</p>
</InkReveal>
<InkReveal delay={0.6}>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<RippleButton
href="/contact"
className="border-2 border-[#C41E3A] text-[#C41E3A] hover:bg-[#C41E3A] hover:text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
rippleColor="rgba(196, 30, 58, 0.2)"
>
</RippleButton>
<RippleButton
href="/contact"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
rippleColor="rgba(255, 255, 255, 0.3)"
>
</RippleButton>
</div>
</InkReveal>
</div>
</div>
</section>
{/* Section 2: Challenges */}
<section className="py-20 md:py-28 bg-white">
<div className="container-wide">
<ScrollReveal variants={slideInLeftVariants} className="mb-12">
<h2 className="text-3xl md:text-4xl font-bold text-[#1C1C1C]"></h2>
<p className="text-[#5C5C5C] mt-2"></p>
</ScrollReveal>
<StaggerContainer className="grid md:grid-cols-2 gap-6 max-w-5xl">
{solution.challenges.map((challenge, index) => (
<StaggerItem key={index}>
<InkCard
className="p-6 bg-[#FFFBF5] rounded-2xl border border-[#C41E3A]/10"
hoverScale={1.02}
hoverShadow="0 20px 40px rgba(196, 30, 58, 0.08)"
>
<div className="flex items-start gap-3">
<span className="flex-shrink-0 w-8 h-8 rounded-full bg-[#C41E3A]/10 flex items-center justify-center text-[#C41E3A] font-bold text-sm">
{index + 1}
</span>
<p className="text-[#1C1C1C] leading-relaxed">{challenge}</p>
</div>
</InkCard>
</StaggerItem>
))}
</StaggerContainer>
</div>
</section>
{/* Section 3: Our Solutions */}
<section className="py-20 md:py-28 bg-[#F8F8F8]">
<div className="container-wide">
<ScrollReveal variants={inkRevealVariants} className="mb-12 text-center">
<h2 className="text-3xl md:text-4xl font-bold text-[#1C1C1C]"></h2>
<p className="text-[#5C5C5C] mt-2"></p>
</ScrollReveal>
<StaggerContainer className="max-w-3xl mx-auto space-y-6">
{solution.solutions.map((item, index) => (
<StaggerItem key={index}>
<div className="flex items-start gap-4 p-6 bg-white rounded-2xl border border-[#E5E5E5]">
<CheckCircle className="w-6 h-6 text-[#C41E3A] flex-shrink-0 mt-0.5" />
<p className="text-[#1C1C1C] leading-relaxed text-lg">{item}</p>
</div>
</StaggerItem>
))}
</StaggerContainer>
</div>
</section>
{/* Section 4: Related Products */}
{solution.relatedProducts.length > 0 && (
<section className="py-16 md:py-20 bg-white">
<div className="container-wide">
<div className="text-center mb-10">
<h2 className="text-2xl md:text-3xl font-bold text-[#1C1C1C]">
</h2>
<p className="text-[#5C5C5C] mt-2"></p>
</div>
<div className="grid sm:grid-cols-2 gap-6 max-w-3xl mx-auto">
{solution.relatedProducts.map((productId) => {
const product = PRODUCTS.find((p) => p.id === productId);
if (!product) {return null;}
return (
<FadeUp key={productId}>
<StaticLink href={`/products/${productId}`}>
<div className="group p-6 bg-[#F8F8F8] rounded-2xl border border-[#E5E5E5] hover:border-[#C41E3A]/30 transition-all">
<span className="inline-block px-3 py-1 bg-[#C41E3A]/10 text-[#C41E3A] text-xs font-semibold rounded-full mb-3">
{product.category}
</span>
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2 group-hover:text-[#C41E3A] transition-colors">
{product.title}
</h3>
<p className="text-sm text-[#5C5C5C] line-clamp-2">{product.description}</p>
</div>
</StaticLink>
</FadeUp>
);
})}
</div>
</div>
</section>
)}
{/* Section 6: CTA */}
<section className="relative py-24 md:py-32 bg-gradient-to-r from-[#C41E3A] to-[#E85D75] overflow-hidden">
<FloatingElement
amplitude={8}
duration={5}
delay={0.5}
className="absolute -top-20 -right-20 pointer-events-none"
>
<div className="w-[280px] h-[280px] bg-white/10 rounded-full" />
</FloatingElement>
<FloatingElement
amplitude={6}
duration={4}
delay={1}
className="absolute -bottom-16 -left-16 pointer-events-none"
>
<div className="w-[220px] h-[220px] bg-white/10 rounded-full" />
</FloatingElement>
<div className="container-wide">
<div className="max-w-3xl mx-auto text-center">
<InkReveal delay={0}>
<h2 className="text-3xl md:text-4xl font-bold text-white mb-6">
{solution.industry}
</h2>
</InkReveal>
<FadeUp delay={0.15}>
<p className="text-lg text-white/90 mb-10">
</p>
</FadeUp>
<FadeUp delay={0.3}>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<RippleButton
href="/contact"
rippleColor="rgba(196, 30, 58, 0.3)"
className="bg-white text-[#C41E3A] px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center w-full sm:w-auto"
>
</RippleButton>
<RippleButton
href="tel:+8613800138000"
rippleColor="rgba(255, 255, 255, 0.2)"
className="bg-transparent border-2 border-white text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center gap-2 w-full sm:w-auto"
>
<Phone className="w-5 h-5" />
</RippleButton>
</div>
</FadeUp>
</div>
</div>
</section>
</main>
);
}