233 lines
9.4 KiB
TypeScript
233 lines
9.4 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Check, CreditCard, GitBranch, Monitor, ArrowRight } from "lucide-react"
|
|
import { cn } from "@/lib/utils"
|
|
import { SectionReveal } from "@/components/ui/section-reveal"
|
|
import homeData from "@/data/home.json"
|
|
import Link from "next/link"
|
|
|
|
const iconMap: Record<string, React.ReactNode> = {
|
|
pos: <CreditCard className="w-7 h-7" />,
|
|
split: <GitBranch className="w-7 h-7" />,
|
|
monitor: <Monitor className="w-7 h-7" />,
|
|
}
|
|
|
|
const accentMap: Record<string, { bg: string; border: string; text: string; glow: string }> = {
|
|
blue: {
|
|
bg: "bg-primary",
|
|
border: "border-primary/20",
|
|
text: "text-primary",
|
|
glow: "shadow-primary/20",
|
|
},
|
|
indigo: {
|
|
bg: "bg-indigo-600",
|
|
border: "border-indigo-500/20",
|
|
text: "text-indigo-600",
|
|
glow: "shadow-indigo-500/20",
|
|
},
|
|
violet: {
|
|
bg: "bg-violet-600",
|
|
border: "border-violet-500/20",
|
|
text: "text-violet-600",
|
|
glow: "shadow-violet-500/20",
|
|
},
|
|
}
|
|
|
|
export function ProductsSection() {
|
|
const { products } = homeData
|
|
const productsAny = products as any
|
|
const sectionBadge = productsAny.sectionBadge ?? productsAny.sectionTag
|
|
const normalizedItems = (Array.isArray(productsAny.items) ? productsAny.items : []).map((item: any) => {
|
|
const id = item.id ?? ""
|
|
const iconType = item.iconType ?? (id === "cashier" ? "pos" : id === "split" ? "split" : "monitor")
|
|
const color = item.color ?? (id === "cashier" ? "blue" : id === "split" ? "indigo" : "violet")
|
|
return {
|
|
...item,
|
|
iconType,
|
|
color,
|
|
title: item.title ?? item.name ?? item.tag,
|
|
}
|
|
})
|
|
const [active, setActive] = useState(0)
|
|
const current = normalizedItems[active] ?? normalizedItems[0]
|
|
const accent = accentMap[current?.color] ?? accentMap.blue
|
|
|
|
if (!current) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<section id="products" className="section-panel bg-background">
|
|
<SectionReveal className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" delay={40}>
|
|
|
|
{/* Header */}
|
|
<div className="text-center max-w-2xl mx-auto mb-16">
|
|
<Badge variant="secondary" className="mb-4 text-primary border-primary/20 bg-primary/8 px-3 py-1">
|
|
{sectionBadge}
|
|
</Badge>
|
|
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-extrabold text-foreground text-balance mb-4 tracking-tight">
|
|
{products.title}
|
|
</h2>
|
|
<p className="text-muted-foreground text-lg leading-relaxed text-pretty">
|
|
{products.subtitle}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Tab switcher */}
|
|
<div className="flex justify-center mb-12">
|
|
<div className="inline-flex items-center gap-2 bg-muted p-1.5 rounded-2xl">
|
|
{normalizedItems.map((item, i) => {
|
|
const a = accentMap[item.color] ?? accentMap.blue
|
|
return (
|
|
<button
|
|
key={item.id}
|
|
onClick={() => setActive(i)}
|
|
className={cn(
|
|
"flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-semibold transition-all duration-200",
|
|
active === i
|
|
? `${a.bg} text-white shadow-md`
|
|
: "text-muted-foreground hover:text-foreground"
|
|
)}
|
|
aria-pressed={active === i}
|
|
>
|
|
{iconMap[item.iconType] ?? iconMap.monitor}
|
|
<span className="hidden sm:inline">{item.tag}</span>
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Detail panel */}
|
|
<div
|
|
key={current.id}
|
|
className={cn(
|
|
"grid lg:grid-cols-2 gap-10 items-center border rounded-3xl p-8 lg:p-12 shadow-xl",
|
|
accent.border,
|
|
accent.glow
|
|
)}
|
|
style={{ boxShadow: `0 20px 60px -10px var(--tw-shadow-color)` }}
|
|
>
|
|
{/* Left: info */}
|
|
<div className="flex flex-col gap-6">
|
|
<div className={cn("inline-flex items-center gap-2 rounded-xl px-3 py-1.5 w-fit", `${accent.bg}/10`)}>
|
|
<span className={accent.text}>{iconMap[current.iconType] ?? iconMap.monitor}</span>
|
|
<span className={cn("text-sm font-semibold", accent.text)}>{current.tag}</span>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="text-2xl lg:text-3xl font-extrabold text-foreground mb-3 tracking-tight">
|
|
{current.title}
|
|
</h3>
|
|
<p className={cn("text-sm font-semibold mb-3", accent.text)}>
|
|
{current.tagline}
|
|
</p>
|
|
<p className="text-muted-foreground leading-relaxed text-pretty">
|
|
{current.description}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-3 max-w-lg">
|
|
<div className={cn("rounded-2xl border px-4 py-3", accent.border, `${accent.bg}/5`)}>
|
|
<p className={cn("text-lg font-black tracking-tight", accent.text)}>{current.highlight}</p>
|
|
<p className="text-xs text-muted-foreground mt-1">关键性能指标</p>
|
|
</div>
|
|
<div className={cn("rounded-2xl border px-4 py-3", accent.border, `${accent.bg}/5`)}>
|
|
<p className="text-sm font-bold text-foreground">{current.highlightSub}</p>
|
|
<p className="text-xs text-muted-foreground mt-1">交付稳定性承诺</p>
|
|
</div>
|
|
</div>
|
|
|
|
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
{current.features.map((f) => (
|
|
<li key={f} className="flex items-center gap-2.5 text-sm text-foreground">
|
|
<span className={cn("w-5 h-5 rounded-full flex items-center justify-center shrink-0", `${accent.bg}/10`)}>
|
|
<Check className={cn("w-3 h-3", accent.text)} />
|
|
</span>
|
|
{f}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<Button
|
|
asChild
|
|
className={cn("w-fit px-6 group", accent.bg, "hover:opacity-90 text-white")}
|
|
>
|
|
<Link href="#contact" className="flex items-center gap-2">
|
|
了解详情
|
|
<ArrowRight className="w-4 h-4 group-hover:translate-x-0.5 transition-transform" />
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Right: visual mock */}
|
|
<div className="relative flex items-center justify-center min-h-[320px] lg:min-h-[380px]">
|
|
{/* Dark card base */}
|
|
<div className="relative w-full max-w-[420px] aspect-[4/3] rounded-2xl bg-surface-dark border border-white/8 shadow-2xl overflow-hidden">
|
|
{/* Top bar */}
|
|
<div className="flex items-center gap-1.5 px-4 py-3 border-b border-white/8">
|
|
<span className="w-2.5 h-2.5 rounded-full bg-red-500/70" />
|
|
<span className="w-2.5 h-2.5 rounded-full bg-yellow-500/70" />
|
|
<span className="w-2.5 h-2.5 rounded-full bg-green-500/70" />
|
|
<div className="ml-3 h-4 bg-white/10 rounded flex-1" />
|
|
</div>
|
|
|
|
{/* Body: chart + cards */}
|
|
<div className="p-5 flex flex-col gap-4">
|
|
{/* Stat row */}
|
|
<div className="grid grid-cols-3 gap-3">
|
|
{["日营业额", "交易笔数", "分账金额"].map((label, i) => (
|
|
<div key={label} className="bg-white/6 rounded-xl p-3">
|
|
<div className={cn("text-lg font-bold text-white mb-0.5", i === 0 && "text-primary")}>
|
|
{i === 0 ? "¥48,260" : i === 1 ? "1,203" : "¥12,400"}
|
|
</div>
|
|
<div className="text-white/40 text-xs">{label}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Bar chart */}
|
|
<div className="bg-white/6 rounded-xl p-3 flex-1">
|
|
<p className="text-white/40 text-xs mb-3">近 7 日趋势</p>
|
|
<div className="flex items-end gap-2 h-16">
|
|
{[55, 70, 45, 88, 65, 92, 78].map((h, i) => (
|
|
<div
|
|
key={i}
|
|
className="flex-1 rounded-sm transition-all"
|
|
style={{
|
|
height: `${h}%`,
|
|
background: i === 5
|
|
? `oklch(0.46 0.22 264)`
|
|
: `oklch(0.46 0.22 264 / 0.35)`,
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Glow overlay */}
|
|
<div
|
|
className="absolute inset-0 pointer-events-none"
|
|
style={{
|
|
background: `radial-gradient(ellipse at 80% 20%, oklch(0.46 0.22 264 / 0.12) 0%, transparent 60%)`,
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
{/* Floating badge */}
|
|
<div className="absolute -bottom-4 -right-4 bg-white border border-border shadow-lg rounded-2xl px-4 py-2.5 flex items-center gap-2">
|
|
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
|
|
<span className="text-xs font-semibold text-foreground">系统运行正常</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</SectionReveal>
|
|
</section>
|
|
)
|
|
}
|