460 lines
21 KiB
TypeScript
460 lines
21 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect, useRef } from 'react';
|
||
import { ChevronLeft, ChevronRight, Play, Pause, Star, Clock, Gift, Zap } from 'lucide-react';
|
||
|
||
interface BannerSlide {
|
||
id: string;
|
||
type: 'hero' | 'product' | 'promotion';
|
||
title: string;
|
||
subtitle?: string;
|
||
description: string;
|
||
image?: string;
|
||
gradient: string;
|
||
cta: {
|
||
primary: { text: string; href: string };
|
||
secondary?: { text: string; href: string };
|
||
};
|
||
badge?: {
|
||
text: string;
|
||
type: 'new' | 'hot' | 'discount' | 'limited';
|
||
};
|
||
features?: string[];
|
||
price?: {
|
||
original?: string;
|
||
current: string;
|
||
discount?: string;
|
||
};
|
||
}
|
||
|
||
interface HeroBannerProps {
|
||
currentLang: string;
|
||
translations: any;
|
||
}
|
||
|
||
export function HeroBanner({ currentLang, translations }: HeroBannerProps) {
|
||
const [currentSlide, setCurrentSlide] = useState(0);
|
||
const [isPlaying, setIsPlaying] = useState(true);
|
||
const [isHovered, setIsHovered] = useState(false);
|
||
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
||
|
||
const t = translations[currentLang];
|
||
|
||
// Banner slides data
|
||
const slides: BannerSlide[] = [
|
||
{
|
||
id: 'hero-main',
|
||
type: 'hero',
|
||
title:
|
||
currentLang === 'zh'
|
||
? '专业云服务器解决方案'
|
||
: 'Professional Cloud Server Solutions',
|
||
subtitle:
|
||
currentLang === 'zh'
|
||
? '为您的业务提供稳定、高效、安全的云计算服务'
|
||
: 'Stable, Efficient, and Secure Cloud Computing Services',
|
||
description:
|
||
currentLang === 'zh'
|
||
? '我们是领先的云服务器代理商,提供全球顶级云服务商的产品与服务,助力企业数字化转型'
|
||
: 'Leading cloud server reseller providing top-tier global cloud services to accelerate your digital transformation',
|
||
gradient: 'from-amber-900 via-yellow-800 to-orange-700',
|
||
cta: {
|
||
primary: { text: t.hero.cta, href: '/contact' },
|
||
secondary: { text: t.hero.demo, href: '/demo' },
|
||
},
|
||
features: [
|
||
currentLang === 'zh' ? '99.9% 可用性保证' : '99.9% Uptime Guarantee',
|
||
currentLang === 'zh' ? '24/7 技术支持' : '24/7 Technical Support',
|
||
currentLang === 'zh' ? '全球数据中心' : 'Global Data Centers',
|
||
],
|
||
},
|
||
{
|
||
id: 'product-ecs',
|
||
type: 'product',
|
||
title:
|
||
currentLang === 'zh'
|
||
? '云服务器 ECS - 弹性计算'
|
||
: 'Cloud Server ECS - Elastic Computing',
|
||
description:
|
||
currentLang === 'zh'
|
||
? '高性能云服务器,支持弹性扩展,按需付费,助力业务快速发展'
|
||
: 'High-performance cloud servers with elastic scaling and pay-as-you-go pricing',
|
||
gradient: 'from-blue-900 via-indigo-800 to-purple-700',
|
||
badge: { text: currentLang === 'zh' ? '热销' : 'Hot', type: 'hot' },
|
||
cta: {
|
||
primary: {
|
||
text: currentLang === 'zh' ? '立即购买' : 'Buy Now',
|
||
href: '/products/ecs',
|
||
},
|
||
secondary: {
|
||
text: currentLang === 'zh' ? '免费试用' : 'Free Trial',
|
||
href: '/trial/ecs',
|
||
},
|
||
},
|
||
price: {
|
||
original: currentLang === 'zh' ? '¥199/月' : '$29/month',
|
||
current: currentLang === 'zh' ? '¥99/月' : '$15/month',
|
||
discount: '50%',
|
||
},
|
||
features: [
|
||
currentLang === 'zh' ? '2-64核 CPU' : '2-64 Core CPU',
|
||
currentLang === 'zh' ? '1GB-256GB 内存' : '1GB-256GB RAM',
|
||
currentLang === 'zh' ? 'SSD 高速存储' : 'SSD High-Speed Storage',
|
||
],
|
||
},
|
||
{
|
||
id: 'promotion-new-year',
|
||
type: 'promotion',
|
||
title:
|
||
currentLang === 'zh'
|
||
? '新年特惠 - 云服务大促销'
|
||
: 'New Year Special - Cloud Services Sale',
|
||
description:
|
||
currentLang === 'zh'
|
||
? '限时优惠!所有云产品享受超低折扣,新用户更有专属礼包'
|
||
: 'Limited time offer! All cloud products at super low prices, exclusive packages for new users',
|
||
gradient: 'from-red-900 via-pink-800 to-rose-700',
|
||
badge: { text: currentLang === 'zh' ? '限时' : 'Limited', type: 'limited' },
|
||
cta: {
|
||
primary: {
|
||
text: currentLang === 'zh' ? '抢购优惠' : 'Grab Deal',
|
||
href: '/promotions/new-year',
|
||
},
|
||
secondary: {
|
||
text: currentLang === 'zh' ? '查看详情' : 'View Details',
|
||
href: '/promotions',
|
||
},
|
||
},
|
||
features: [
|
||
currentLang === 'zh' ? '最高7折优惠' : 'Up to 30% Off',
|
||
currentLang === 'zh' ? '免费迁移服务' : 'Free Migration Service',
|
||
currentLang === 'zh' ? '专属技术支持' : 'Dedicated Support',
|
||
],
|
||
},
|
||
{
|
||
id: 'product-database',
|
||
type: 'product',
|
||
title:
|
||
currentLang === 'zh'
|
||
? '云数据库 RDS - 高可用数据库'
|
||
: 'Cloud Database RDS - High Availability',
|
||
description:
|
||
currentLang === 'zh'
|
||
? '企业级云数据库服务,支持MySQL、PostgreSQL等多种数据库引擎'
|
||
: 'Enterprise-grade cloud database service supporting MySQL, PostgreSQL and more',
|
||
gradient: 'from-green-900 via-emerald-800 to-teal-700',
|
||
badge: { text: currentLang === 'zh' ? '新品' : 'New', type: 'new' },
|
||
cta: {
|
||
primary: {
|
||
text: currentLang === 'zh' ? '立即体验' : 'Try Now',
|
||
href: '/products/rds',
|
||
},
|
||
secondary: {
|
||
text: currentLang === 'zh' ? '技术咨询' : 'Consultation',
|
||
href: '/support',
|
||
},
|
||
},
|
||
price: {
|
||
current: currentLang === 'zh' ? '¥199/月起' : 'From $29/month',
|
||
},
|
||
features: [
|
||
currentLang === 'zh' ? '自动备份恢复' : 'Auto Backup & Recovery',
|
||
currentLang === 'zh' ? '读写分离' : 'Read-Write Splitting',
|
||
currentLang === 'zh' ? '监控告警' : 'Monitoring & Alerts',
|
||
],
|
||
},
|
||
];
|
||
|
||
// Auto-play functionality
|
||
useEffect(() => {
|
||
if (isPlaying && !isHovered) {
|
||
intervalRef.current = setInterval(() => {
|
||
setCurrentSlide((prev) => (prev + 1) % slides.length);
|
||
}, 5000);
|
||
} else {
|
||
if (intervalRef.current) {
|
||
clearInterval(intervalRef.current);
|
||
}
|
||
}
|
||
|
||
return () => {
|
||
if (intervalRef.current) {
|
||
clearInterval(intervalRef.current);
|
||
}
|
||
};
|
||
}, [isPlaying, isHovered, slides.length]);
|
||
|
||
const goToSlide = (index: number) => {
|
||
setCurrentSlide(index);
|
||
};
|
||
|
||
const nextSlide = () => {
|
||
setCurrentSlide((prev) => (prev + 1) % slides.length);
|
||
};
|
||
|
||
const prevSlide = () => {
|
||
setCurrentSlide((prev) => (prev - 1 + slides.length) % slides.length);
|
||
};
|
||
|
||
const togglePlayPause = () => {
|
||
setIsPlaying(!isPlaying);
|
||
};
|
||
|
||
const getBadgeIcon = (type: string) => {
|
||
switch (type) {
|
||
case 'new':
|
||
return <Star className="w-3 h-3" />;
|
||
case 'hot':
|
||
return <Zap className="w-3 h-3" />;
|
||
case 'discount':
|
||
return <Gift className="w-3 h-3" />;
|
||
case 'limited':
|
||
return <Clock className="w-3 h-3" />;
|
||
default:
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const getBadgeColor = (type: string) => {
|
||
switch (type) {
|
||
case 'new':
|
||
return 'bg-green-500 text-white';
|
||
case 'hot':
|
||
return 'bg-red-500 text-white';
|
||
case 'discount':
|
||
return 'bg-purple-500 text-white';
|
||
case 'limited':
|
||
return 'bg-orange-500 text-white';
|
||
default:
|
||
return 'bg-gray-500 text-white';
|
||
}
|
||
};
|
||
|
||
const currentSlideData = slides[currentSlide];
|
||
|
||
return (
|
||
<section
|
||
className="relative h-[600px] md:h-[700px] overflow-hidden"
|
||
onMouseEnter={() => setIsHovered(true)}
|
||
onMouseLeave={() => setIsHovered(false)}
|
||
role="banner"
|
||
aria-label="Hero banner carousel"
|
||
>
|
||
{/* Background with gradient */}
|
||
<div
|
||
className={`absolute inset-0 bg-gradient-to-br ${currentSlideData.gradient} transition-all duration-1000`}
|
||
/>
|
||
|
||
{/* Background pattern */}
|
||
<div className="absolute inset-0 opacity-10">
|
||
<div className="absolute inset-0 bg-[url('data:image/svg+xml,%3Csvg width=%2260%22 height=%2260%22 viewBox=%220 0 60 60%22 xmlns=%22http://www.w3.org/2000/svg%22%3E%3Cg fill=%22none%22 fill-rule=%22evenodd%22%3E%3Cg fill=%22%23ffffff%22 fill-opacity=%220.1%22%3E%3Ccircle cx=%2230%22 cy=%2230%22 r=%222%22/%3E%3C/g%3E%3C/g%3E%3C/svg%3E')] bg-repeat" />
|
||
</div>
|
||
|
||
{/* Content */}
|
||
<div className="relative h-full flex items-center">
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 w-full">
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||
{/* Left Content */}
|
||
<div className="text-white space-y-6">
|
||
{/* Badge */}
|
||
{currentSlideData.badge && (
|
||
<div className="inline-flex items-center space-x-2">
|
||
<span
|
||
className={`inline-flex items-center space-x-1 px-3 py-1 rounded-full text-xs font-semibold ${getBadgeColor(currentSlideData.badge.type)}`}
|
||
>
|
||
{getBadgeIcon(currentSlideData.badge.type)}
|
||
<span>{currentSlideData.badge.text}</span>
|
||
</span>
|
||
</div>
|
||
)}
|
||
|
||
{/* Title */}
|
||
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold leading-tight">
|
||
{currentSlideData.title}
|
||
</h1>
|
||
|
||
{/* Subtitle */}
|
||
{currentSlideData.subtitle && (
|
||
<p className="text-xl md:text-2xl text-white/90 leading-relaxed">
|
||
{currentSlideData.subtitle}
|
||
</p>
|
||
)}
|
||
|
||
{/* Description */}
|
||
<p className="text-lg text-white/80 leading-relaxed max-w-2xl">
|
||
{currentSlideData.description}
|
||
</p>
|
||
|
||
{/* Features */}
|
||
{currentSlideData.features && (
|
||
<div className="flex flex-wrap gap-4">
|
||
{currentSlideData.features.map((feature, index) => (
|
||
<div
|
||
key={index}
|
||
className="flex items-center space-x-2 bg-white/10 backdrop-blur-sm rounded-full px-4 py-2"
|
||
>
|
||
<div className="w-2 h-2 bg-yellow-400 rounded-full" />
|
||
<span className="text-sm font-medium">{feature}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
{/* Price */}
|
||
{currentSlideData.price && (
|
||
<div className="flex items-center space-x-4">
|
||
<div className="text-3xl font-bold text-yellow-300">
|
||
{currentSlideData.price.current}
|
||
</div>
|
||
{currentSlideData.price.original && (
|
||
<div className="flex items-center space-x-2">
|
||
<span className="text-lg text-white/60 line-through">
|
||
{currentSlideData.price.original}
|
||
</span>
|
||
{currentSlideData.price.discount && (
|
||
<span className="bg-red-500 text-white px-2 py-1 rounded text-sm font-semibold">
|
||
-{currentSlideData.price.discount}
|
||
</span>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{/* CTA Buttons */}
|
||
<div className="flex flex-col sm:flex-row gap-4 pt-4">
|
||
<a
|
||
href={currentSlideData.cta.primary.href}
|
||
className="inline-flex items-center justify-center px-8 py-4 bg-yellow-500 hover:bg-yellow-600 text-white font-semibold rounded-lg transition-all duration-200 transform hover:scale-105 shadow-lg hover:shadow-xl"
|
||
>
|
||
{currentSlideData.cta.primary.text}
|
||
</a>
|
||
{currentSlideData.cta.secondary && (
|
||
<a
|
||
href={currentSlideData.cta.secondary.href}
|
||
className="inline-flex items-center justify-center px-8 py-4 border-2 border-white/30 text-white hover:bg-white/10 font-semibold rounded-lg transition-all duration-200 backdrop-blur-sm"
|
||
>
|
||
{currentSlideData.cta.secondary.text}
|
||
</a>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Right Content - Visual Element */}
|
||
<div className="hidden lg:block">
|
||
<div className="relative">
|
||
{/* Floating Cards */}
|
||
<div className="space-y-4">
|
||
<div className="bg-white/10 backdrop-blur-md rounded-xl p-6 transform rotate-3 hover:rotate-0 transition-transform duration-300">
|
||
<div className="flex items-center space-x-3 mb-3">
|
||
<div className="w-3 h-3 bg-green-400 rounded-full" />
|
||
<span className="text-white font-medium">
|
||
{currentLang === 'zh'
|
||
? '服务状态:正常'
|
||
: 'Service Status: Normal'}
|
||
</span>
|
||
</div>
|
||
<div className="text-2xl font-bold text-white">99.9%</div>
|
||
<div className="text-white/70 text-sm">
|
||
{currentLang === 'zh'
|
||
? '可用性保证'
|
||
: 'Uptime Guarantee'}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white/10 backdrop-blur-md rounded-xl p-6 transform -rotate-2 hover:rotate-0 transition-transform duration-300 ml-8">
|
||
<div className="flex items-center space-x-3 mb-3">
|
||
<div className="w-3 h-3 bg-blue-400 rounded-full" />
|
||
<span className="text-white font-medium">
|
||
{currentLang === 'zh' ? '全球节点' : 'Global Nodes'}
|
||
</span>
|
||
</div>
|
||
<div className="text-2xl font-bold text-white">50+</div>
|
||
<div className="text-white/70 text-sm">
|
||
{currentLang === 'zh' ? '数据中心' : 'Data Centers'}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white/10 backdrop-blur-md rounded-xl p-6 transform rotate-1 hover:rotate-0 transition-transform duration-300">
|
||
<div className="flex items-center space-x-3 mb-3">
|
||
<div className="w-3 h-3 bg-yellow-400 rounded-full" />
|
||
<span className="text-white font-medium">
|
||
{currentLang === 'zh'
|
||
? '客户满意度'
|
||
: 'Customer Satisfaction'}
|
||
</span>
|
||
</div>
|
||
<div className="text-2xl font-bold text-white">4.9/5</div>
|
||
<div className="text-white/70 text-sm">
|
||
{currentLang === 'zh' ? '用户评分' : 'User Rating'}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Navigation Controls */}
|
||
<div className="absolute bottom-6 left-1/2 transform -translate-x-1/2 flex items-center space-x-4">
|
||
{/* Slide Indicators */}
|
||
<div className="flex space-x-2">
|
||
{slides.map((_, index) => (
|
||
<button
|
||
key={index}
|
||
onClick={() => goToSlide(index)}
|
||
className={`w-3 h-3 rounded-full transition-all duration-200 ${
|
||
index === currentSlide
|
||
? 'bg-yellow-400 w-8'
|
||
: 'bg-white/30 hover:bg-white/50'
|
||
}`}
|
||
aria-label={`Go to slide ${index + 1}`}
|
||
/>
|
||
))}
|
||
</div>
|
||
|
||
{/* Play/Pause Button */}
|
||
<button
|
||
onClick={togglePlayPause}
|
||
className="p-2 bg-white/10 backdrop-blur-sm rounded-full hover:bg-white/20 transition-colors duration-200"
|
||
aria-label={isPlaying ? 'Pause slideshow' : 'Play slideshow'}
|
||
>
|
||
{isPlaying ? (
|
||
<Pause className="w-4 h-4 text-white" />
|
||
) : (
|
||
<Play className="w-4 h-4 text-white" />
|
||
)}
|
||
</button>
|
||
</div>
|
||
|
||
{/* Navigation Arrows */}
|
||
<button
|
||
onClick={prevSlide}
|
||
className="absolute left-4 top-1/2 transform -translate-y-1/2 p-3 bg-white/10 backdrop-blur-sm rounded-full hover:bg-white/20 transition-colors duration-200 group"
|
||
aria-label="Previous slide"
|
||
>
|
||
<ChevronLeft className="w-6 h-6 text-white group-hover:scale-110 transition-transform" />
|
||
</button>
|
||
|
||
<button
|
||
onClick={nextSlide}
|
||
className="absolute right-4 top-1/2 transform -translate-y-1/2 p-3 bg-white/10 backdrop-blur-sm rounded-full hover:bg-white/20 transition-colors duration-200 group"
|
||
aria-label="Next slide"
|
||
>
|
||
<ChevronRight className="w-6 h-6 text-white group-hover:scale-110 transition-transform" />
|
||
</button>
|
||
|
||
{/* Progress Bar */}
|
||
<div className="absolute bottom-0 left-0 w-full h-1 bg-white/20">
|
||
<div
|
||
className="h-full bg-yellow-400 transition-all duration-300 ease-linear"
|
||
style={{
|
||
width: `${((currentSlide + 1) / slides.length) * 100}%`,
|
||
}}
|
||
/>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|