refactor: remove service detail modal component
This commit is contained in:
@@ -1,173 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { SERVICES } from '@/lib/constants';
|
||||
import { X, CheckCircle2, ArrowRight, Sparkles, Target, Zap, Shield, Code, Cloud, BarChart3 } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface ServiceDetailModalProps {
|
||||
serviceId: string;
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
const iconMap = {
|
||||
Code,
|
||||
Cloud,
|
||||
BarChart3,
|
||||
Shield,
|
||||
};
|
||||
|
||||
export function ServiceDetailModal({ serviceId, open, onOpenChange }: ServiceDetailModalProps) {
|
||||
const [activeTab, setActiveTab] = useState<'features' | 'benefits' | 'process'>('features');
|
||||
const service = SERVICES.find((s) => s.id === serviceId);
|
||||
const IconComponent = service ? iconMap[service.icon as keyof typeof iconMap] : Code;
|
||||
|
||||
if (!service) return null;
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto p-0">
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="absolute right-4 top-4 z-10 p-2 rounded-full bg-white/80 hover:bg-white transition-colors shadow-lg"
|
||||
>
|
||||
<X className="w-5 h-5 text-gray-600" />
|
||||
</button>
|
||||
|
||||
<div className="bg-gradient-to-br from-[#C41E3A] to-[#1C1C1C] p-8 text-white">
|
||||
<div className="flex items-start gap-4 mb-4">
|
||||
<div className="w-16 h-16 bg-white/10 backdrop-blur-sm rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<IconComponent className="w-8 h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<DialogTitle className="text-3xl font-bold mb-2">{service.title}</DialogTitle>
|
||||
<p className="text-white/80 text-lg">{service.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-8">
|
||||
<div className="mb-8">
|
||||
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-3 flex items-center gap-2">
|
||||
<Sparkles className="w-5 h-5 text-[#C41E3A]" />
|
||||
服务概述
|
||||
</h3>
|
||||
<p className="text-[#5C5C5C] leading-relaxed">{service.overview}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<div className="flex gap-2 mb-6 border-b border-gray-200">
|
||||
{[
|
||||
{ id: 'features', label: '核心功能', icon: Zap },
|
||||
{ id: 'benefits', label: '服务优势', icon: Target },
|
||||
{ id: 'process', label: '服务流程', icon: CheckCircle2 },
|
||||
].map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id as any)}
|
||||
className={`
|
||||
flex items-center gap-2 px-4 py-3 font-medium transition-all border-b-2 -mb-px
|
||||
${activeTab === tab.id
|
||||
? 'border-[#C41E3A] text-[#C41E3A]'
|
||||
: 'border-transparent text-[#5C5C5C] hover:text-[#1C1C1C]'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<tab.icon className="w-4 h-4" />
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key={activeTab}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
{activeTab === 'features' && (
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
{service.features.map((feature, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: index * 0.05 }}
|
||||
className="flex items-start gap-3 p-4 bg-[#F5F7FA] rounded-lg hover:bg-[#FFFBF5] transition-colors group"
|
||||
>
|
||||
<div className="w-6 h-6 bg-[#C41E3A] rounded-full flex items-center justify-center flex-shrink-0 mt-0.5">
|
||||
<CheckCircle2 className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="text-[#1C1C1C] group-hover:text-[#C41E3A] transition-colors">{feature}</span>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'benefits' && (
|
||||
<div className="space-y-4">
|
||||
{service.benefits.map((benefit, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: index * 0.05 }}
|
||||
className="flex items-start gap-3 p-4 bg-gradient-to-r from-[#FFFBF5] to-transparent rounded-lg border-l-4 border-[#C41E3A]"
|
||||
>
|
||||
<div className="w-8 h-8 bg-[#C41E3A] rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
<Target className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="text-[#1C1C1C] font-medium">{benefit}</span>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'process' && (
|
||||
<div className="space-y-4">
|
||||
{service.process.map((step, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: index * 0.05 }}
|
||||
className="flex items-start gap-4"
|
||||
>
|
||||
<div className="w-10 h-10 bg-[#C41E3A] rounded-full flex items-center justify-center flex-shrink-0 text-white font-bold">
|
||||
{index + 1}
|
||||
</div>
|
||||
<div className="flex-1 pb-4">
|
||||
<p className="text-[#1C1C1C] font-medium">{step}</p>
|
||||
{index < service.process.length - 1 && (
|
||||
<div className="absolute left-5 top-10 w-0.5 h-8 bg-[#C41E3A]/20" />
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-3 pt-6 border-t border-gray-200">
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
关闭
|
||||
</Button>
|
||||
<Button className="bg-[#C41E3A] hover:bg-[#A01830] text-white">
|
||||
立即咨询
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { InkBackground } from '@/components/ui/ink-decoration';
|
||||
import { DataParticleFlow } from '@/components/effects/data-particle-flow';
|
||||
import { SubtleDots } from '@/components/effects/subtle-dots';
|
||||
|
||||
interface PageHeaderProps {
|
||||
badge?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function PageHeader({ badge, title, description, className = '' }: PageHeaderProps) {
|
||||
const ref = useRef(null);
|
||||
const isInView = useInView(ref, { once: true, margin: '-100px' });
|
||||
|
||||
return (
|
||||
<div className="relative overflow-hidden bg-gradient-to-b from-[#FAFAFA] to-white">
|
||||
<InkBackground />
|
||||
<DataParticleFlow
|
||||
particleCount={40}
|
||||
color="#C41E3A"
|
||||
intensity="subtle"
|
||||
shape="square"
|
||||
effect="pulse"
|
||||
/>
|
||||
<SubtleDots color="#C41E3A" count={6} />
|
||||
|
||||
<div className="container-wide relative z-10 pt-32 pb-20" ref={ref}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6 }}
|
||||
className={`max-w-4xl mx-auto text-center ${className}`}
|
||||
>
|
||||
{badge && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: 0.1 }}
|
||||
className="mb-6"
|
||||
>
|
||||
<Badge variant="outline">{badge}</Badge>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
className="text-4xl sm:text-5xl font-bold text-[#1C1C1C] mb-6"
|
||||
>
|
||||
{title}
|
||||
</motion.h1>
|
||||
|
||||
{description && (
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: 0.3 }}
|
||||
className="text-lg text-[#5C5C5C] max-w-2xl mx-auto leading-relaxed"
|
||||
>
|
||||
{description}
|
||||
</motion.p>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user