chore(cleanup): 清理废弃组件与旧版本归档
- 删除 detail-v2 旧版叙事组件 - 删除 narrative 旧版区块组件 - 删除 theme-toggle 主题切换与 theme-context 上下文 - 删除 detail-data-proof/service-value/solution-value 旧版组件 - 删除 home-content-v2/v3 旧版首页 - 归档旧版营销页面内容至 _archive 目录
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import type { DataProof } from '@/lib/constants/products';
|
||||
|
||||
interface DataProofGridProps {
|
||||
proofs: DataProof[];
|
||||
accentColor?: string;
|
||||
}
|
||||
|
||||
export function DataProofGrid({ proofs, accentColor = '#C41E3A' }: DataProofGridProps) {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-3">
|
||||
{proofs.map((proof, index) => (
|
||||
<motion.div
|
||||
key={proof.metric}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: '-50px' }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
className="rounded-xl border border-gray-200 bg-white p-6 text-center shadow-sm"
|
||||
>
|
||||
<p
|
||||
className="text-3xl font-bold tracking-tight"
|
||||
style={{ color: accentColor }}
|
||||
>
|
||||
{proof.value}
|
||||
</p>
|
||||
<p className="mt-1 text-sm font-semibold text-gray-900">{proof.metric}</p>
|
||||
<p className="mt-1 text-xs text-gray-500">{proof.description}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
CheckCircle2,
|
||||
Clock,
|
||||
Target,
|
||||
Rocket,
|
||||
ClipboardCheck,
|
||||
Headphones,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react';
|
||||
import type { Service } from '@/lib/constants/services';
|
||||
|
||||
interface ServiceValueSectionProps {
|
||||
service: Service;
|
||||
}
|
||||
|
||||
const processIcons: LucideIcon[] = [Target, Rocket, ClipboardCheck, Headphones];
|
||||
|
||||
export function ServiceValueSection({ service }: ServiceValueSectionProps) {
|
||||
return (
|
||||
<section className="bg-white py-16 lg:py-20">
|
||||
<div className="container-wide">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="mb-10"
|
||||
>
|
||||
<p className="text-sm leading-relaxed text-gray-600">{service.overview}</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-12 lg:grid-cols-2">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<h2 className="mb-6 text-xl font-bold text-gray-900">服务能力</h2>
|
||||
<ul className="space-y-3">
|
||||
{service.features.map((feature, index) => (
|
||||
<motion.li
|
||||
key={feature}
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: index * 0.05 }}
|
||||
className="flex items-start gap-3"
|
||||
>
|
||||
<CheckCircle2 className="mt-0.5 h-5 w-5 shrink-0 text-[#C41E3A]" />
|
||||
<span className="text-sm leading-relaxed text-gray-700">{feature}</span>
|
||||
</motion.li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<h3 className="mb-4 mt-8 text-lg font-bold text-gray-900">核心价值</h3>
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
{service.benefits.map((benefit, index) => (
|
||||
<motion.div
|
||||
key={benefit}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: index * 0.06 }}
|
||||
className="rounded-lg border border-red-50 bg-red-50/30 p-3"
|
||||
>
|
||||
<p className="text-sm text-gray-700">{benefit}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.15 }}
|
||||
>
|
||||
<h2 className="mb-6 flex items-center gap-2 text-xl font-bold text-gray-900">
|
||||
<Clock className="h-5 w-5 text-[#C41E3A]" />
|
||||
服务流程
|
||||
</h2>
|
||||
<div className="relative">
|
||||
{service.process.map((step, index) => {
|
||||
const Icon = processIcons[index % processIcons.length]!;
|
||||
const isLast = index === service.process.length - 1;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={step}
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: index * 0.08 }}
|
||||
className="relative flex gap-4 pb-6 last:pb-0"
|
||||
>
|
||||
{!isLast && (
|
||||
<div className="absolute left-[19px] top-11 h-full w-px bg-gray-200" />
|
||||
)}
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-[#C41E3A] text-white shadow-md">
|
||||
<Icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="pt-1">
|
||||
<div className="mb-1 flex items-center gap-2">
|
||||
<span className="text-xs font-semibold text-[#C41E3A]">
|
||||
步骤 {index + 1}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{step.split(':')[0]}
|
||||
</p>
|
||||
<p className="mt-0.5 text-xs leading-relaxed text-gray-500">
|
||||
{step.split(':')[1] || step.split(':')[1] || ''}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { AlertTriangle, CheckCircle2, ArrowRight, Package, Wrench } from 'lucide-react';
|
||||
import type { Solution } from '@/lib/constants/solutions';
|
||||
import { PRODUCTS } from '@/lib/constants/products';
|
||||
import { SERVICES } from '@/lib/constants/services';
|
||||
|
||||
interface SolutionValueSectionProps {
|
||||
solution: Solution;
|
||||
}
|
||||
|
||||
export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
|
||||
const primaryProducts = solution.suiteCombination.primaryProducts
|
||||
.map((id) => PRODUCTS.find((p) => p.id === id))
|
||||
.filter(Boolean);
|
||||
|
||||
const complementaryServices = solution.suiteCombination.complementaryServices
|
||||
.map((id) => SERVICES.find((s) => s.id === id))
|
||||
.filter(Boolean);
|
||||
|
||||
return (
|
||||
<section className="bg-white py-16 lg:py-20">
|
||||
<div className="container-wide">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="mb-12 text-center"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-gray-900 lg:text-3xl">
|
||||
{solution.valueProposition.headline}
|
||||
</h2>
|
||||
</motion.div>
|
||||
|
||||
<div className="mb-12 grid grid-cols-1 gap-6 md:grid-cols-3">
|
||||
{solution.valueProposition.points.map((point, index) => (
|
||||
<motion.div
|
||||
key={point.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.4, delay: index * 0.1 }}
|
||||
className="rounded-xl border border-gray-100 bg-gray-50/50 p-6 text-center"
|
||||
>
|
||||
<div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-xl bg-[#C41E3A]/10 text-[#C41E3A]">
|
||||
<span className="text-lg">{point.icon}</span>
|
||||
</div>
|
||||
<h3 className="mb-2 text-base font-semibold text-gray-900">{point.title}</h3>
|
||||
<p className="text-sm leading-relaxed text-gray-600">{point.description}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-8 lg:grid-cols-2">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<h3 className="mb-4 flex items-center gap-2 text-lg font-bold text-gray-900">
|
||||
<AlertTriangle className="h-5 w-5 text-orange-500" />
|
||||
行业挑战
|
||||
</h3>
|
||||
<ul className="space-y-3">
|
||||
{solution.challenges.map((challenge, index) => (
|
||||
<motion.li
|
||||
key={challenge}
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: index * 0.05 }}
|
||||
className="flex items-start gap-2 text-sm text-gray-700"
|
||||
>
|
||||
<span className="mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full bg-orange-400" />
|
||||
{challenge}
|
||||
</motion.li>
|
||||
))}
|
||||
</ul>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.15 }}
|
||||
>
|
||||
<h3 className="mb-4 flex items-center gap-2 text-lg font-bold text-gray-900">
|
||||
<CheckCircle2 className="h-5 w-5 text-green-500" />
|
||||
解决方案
|
||||
</h3>
|
||||
<ul className="space-y-3">
|
||||
{solution.solutions.map((sol, index) => (
|
||||
<motion.li
|
||||
key={sol}
|
||||
initial={{ opacity: 0, x: 10 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: index * 0.05 }}
|
||||
className="flex items-start gap-2 text-sm text-gray-700"
|
||||
>
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-green-500" />
|
||||
{sol}
|
||||
</motion.li>
|
||||
))}
|
||||
</ul>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{(primaryProducts.length > 0 || complementaryServices.length > 0) && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="mt-12 rounded-2xl bg-gradient-to-br from-gray-50 to-gray-100 p-8"
|
||||
>
|
||||
<h3 className="mb-2 text-lg font-bold text-gray-900">推荐套装组合</h3>
|
||||
<p className="mb-6 text-sm leading-relaxed text-gray-600">
|
||||
{solution.suiteCombination.rationale}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
{primaryProducts.map((product) =>
|
||||
product ? (
|
||||
<a
|
||||
key={product.id}
|
||||
href={`/products/${product.id}`}
|
||||
className="inline-flex items-center gap-2 rounded-lg border border-blue-200 bg-white px-4 py-2 text-sm font-medium text-blue-700 transition-all hover:bg-blue-50 hover:border-blue-300"
|
||||
>
|
||||
<Package className="h-4 w-4" />
|
||||
{product.title}
|
||||
<ArrowRight className="h-3 w-3" />
|
||||
</a>
|
||||
) : null,
|
||||
)}
|
||||
{complementaryServices.map((service) =>
|
||||
service ? (
|
||||
<a
|
||||
key={service.id}
|
||||
href={`/services/${service.id}`}
|
||||
className="inline-flex items-center gap-2 rounded-lg border border-purple-200 bg-white px-4 py-2 text-sm font-medium text-purple-700 transition-all hover:bg-purple-50 hover:border-purple-300"
|
||||
>
|
||||
<Wrench className="h-4 w-4" />
|
||||
{service.title}
|
||||
<ArrowRight className="h-3 w-3" />
|
||||
</a>
|
||||
) : null,
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user