Files
novalon-website/src/components/services/service-detail-modal.tsx
T
2026-02-26 17:58:59 +08:00

174 lines
7.4 KiB
TypeScript

'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>
);
}