122 lines
3.9 KiB
TypeScript
122 lines
3.9 KiB
TypeScript
'use client';
|
||
|
||
import { useEffect, useState, useCallback } from 'react';
|
||
import Link from 'next/link';
|
||
import type { Banner } from '@/lib/types';
|
||
import { resolveUploadUrl, cn } from '@/lib/utils';
|
||
|
||
const INTERVAL = 5000;
|
||
|
||
export function BannerCarousel({ banners }: { banners: Banner[] }) {
|
||
const [idx, setIdx] = useState(0);
|
||
const total = banners.length;
|
||
|
||
const next = useCallback(() => {
|
||
setIdx((p) => (total === 0 ? 0 : (p + 1) % total));
|
||
}, [total]);
|
||
|
||
useEffect(() => {
|
||
if (total <= 1) return;
|
||
const t = setInterval(next, INTERVAL);
|
||
return () => clearInterval(t);
|
||
}, [next, total]);
|
||
|
||
if (total === 0) {
|
||
return <HeroPlaceholder />;
|
||
}
|
||
|
||
return (
|
||
<div className="relative h-[420px] overflow-hidden bg-gray-100">
|
||
{banners.map((b, i) => (
|
||
<div
|
||
key={b.id}
|
||
className={cn(
|
||
'absolute inset-0 transition-opacity duration-700',
|
||
i === idx ? 'opacity-100' : 'opacity-0',
|
||
)}
|
||
>
|
||
{b.link ? (
|
||
<Link href={b.link} target="_blank" rel="noreferrer" className="block h-full w-full">
|
||
<BG image={b.image} title={b.title} />
|
||
</Link>
|
||
) : (
|
||
<BG image={b.image} title={b.title} />
|
||
)}
|
||
</div>
|
||
))}
|
||
{total > 1 && (
|
||
<div className="absolute bottom-4 left-1/2 flex -translate-x-1/2 gap-2">
|
||
{banners.map((_, i) => (
|
||
<button
|
||
key={i}
|
||
aria-label={`第 ${i + 1} 张`}
|
||
onClick={() => setIdx(i)}
|
||
className={cn(
|
||
'h-2 rounded-full transition-all',
|
||
i === idx ? 'w-6 bg-white' : 'w-2 bg-white/50',
|
||
)}
|
||
/>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function BG({ image, title }: { image: string; title: string }) {
|
||
return (
|
||
<div
|
||
className="relative h-full w-full bg-cover bg-center"
|
||
style={{ backgroundImage: `url(${resolveUploadUrl(image)})` }}
|
||
>
|
||
<div className="absolute inset-0 bg-black/40" />
|
||
<div className="container-page flex h-full items-center">
|
||
<h2 className="max-w-xl text-2xl font-bold leading-snug text-white sm:text-3xl md:text-4xl">
|
||
{title}
|
||
</h2>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function HeroPlaceholder() {
|
||
return (
|
||
<div className="relative flex h-[480px] items-center overflow-hidden bg-gradient-to-br from-brand-700 via-brand-600 to-brand-800">
|
||
<div
|
||
className="absolute inset-0 opacity-30"
|
||
style={{
|
||
backgroundImage:
|
||
'radial-gradient(circle at 20% 50%, rgba(255,255,255,0.15) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(255,255,255,0.1) 0%, transparent 40%)',
|
||
}}
|
||
/>
|
||
<div className="container-page relative">
|
||
<span className="inline-block rounded-full bg-white/15 px-4 py-1.5 text-sm text-white backdrop-blur">
|
||
物业管理系统 · 小程序 · SaaS
|
||
</span>
|
||
<h1 className="mt-6 max-w-2xl text-4xl font-bold leading-tight text-white sm:text-5xl md:text-6xl">
|
||
让物业管理
|
||
<br />
|
||
像发微信一样简单
|
||
</h1>
|
||
<p className="mt-4 max-w-xl text-lg text-brand-100">
|
||
懂物业,更懂“省”心。一站式物业管理平台,覆盖缴费、报修、公告、巡检全流程。
|
||
</p>
|
||
<div className="mt-8 flex flex-col gap-4 sm:flex-row">
|
||
<Link
|
||
href="/contact"
|
||
className="rounded-lg bg-white px-7 py-3 text-sm font-semibold text-brand-700 shadow-lg transition-colors hover:bg-brand-50"
|
||
>
|
||
预约演示
|
||
</Link>
|
||
<Link
|
||
href="/products"
|
||
className="rounded-lg border border-white/40 px-7 py-3 text-sm font-medium text-white transition-colors hover:bg-white/10"
|
||
>
|
||
了解产品
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|