From 6a68a96287e0787fcec49b0ef7815322fc92c16a Mon Sep 17 00:00:00 2001 From: JanYork Date: Wed, 18 Mar 2026 10:50:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=BF=9E=E9=94=81?= =?UTF-8?q?=E9=97=A8=E5=BA=97=E7=B3=BB=E7=BB=9F=E9=A6=96=E9=A1=B5=E5=8F=8A?= =?UTF-8?q?=E6=A0=B8=E5=BF=83UI=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增首页布局、导航栏、页脚及多个核心UI组件(按钮、卡片、表格等) 添加图片资源、工具函数和样式配置 实现响应式设计和主题支持 包含行业解决方案展示区块 --- .gitignore | 17 + app/globals.css | 170 ++ app/layout.tsx | 37 + app/page.tsx | 27 + components.json | 21 + components/footer.tsx | 98 + components/navbar.tsx | 150 + components/sections/about-section.tsx | 76 + components/sections/advantages-section.tsx | 90 + components/sections/cases-section.tsx | 90 + components/sections/contact-section.tsx | 154 + components/sections/cta-banner.tsx | 66 + components/sections/hero-section.tsx | 335 ++ components/sections/news-section.tsx | 81 + components/sections/partners-section.tsx | 71 + components/sections/process-section.tsx | 111 + components/sections/products-section.tsx | 232 ++ components/sections/services-section.tsx | 194 ++ components/sections/solutions-section.tsx | 124 + components/sections/team-section.tsx | 65 + components/theme-provider.tsx | 11 + components/ui/accordion.tsx | 66 + components/ui/alert-dialog.tsx | 157 + components/ui/alert.tsx | 66 + components/ui/aspect-ratio.tsx | 11 + components/ui/avatar.tsx | 53 + components/ui/badge.tsx | 46 + components/ui/breadcrumb.tsx | 109 + components/ui/button-group.tsx | 83 + components/ui/button.tsx | 60 + components/ui/calendar.tsx | 213 ++ components/ui/card.tsx | 92 + components/ui/carousel.tsx | 241 ++ components/ui/chart.tsx | 353 +++ components/ui/checkbox.tsx | 32 + components/ui/collapsible.tsx | 33 + components/ui/command.tsx | 184 ++ components/ui/context-menu.tsx | 252 ++ components/ui/dialog.tsx | 143 + components/ui/drawer.tsx | 135 + components/ui/dropdown-menu.tsx | 257 ++ components/ui/empty.tsx | 104 + components/ui/field.tsx | 244 ++ components/ui/form.tsx | 167 + components/ui/hover-card.tsx | 44 + components/ui/input-group.tsx | 169 ++ components/ui/input-otp.tsx | 77 + components/ui/input.tsx | 21 + components/ui/item.tsx | 193 ++ components/ui/kbd.tsx | 28 + components/ui/label.tsx | 24 + components/ui/menubar.tsx | 276 ++ components/ui/navigation-menu.tsx | 166 + components/ui/pagination.tsx | 127 + components/ui/popover.tsx | 48 + components/ui/progress.tsx | 31 + components/ui/radio-group.tsx | 45 + components/ui/resizable.tsx | 56 + components/ui/scroll-area.tsx | 58 + components/ui/section-reveal.tsx | 61 + components/ui/select.tsx | 185 ++ components/ui/separator.tsx | 28 + components/ui/sheet.tsx | 139 + components/ui/sidebar.tsx | 726 +++++ components/ui/skeleton.tsx | 13 + components/ui/slider.tsx | 63 + components/ui/sonner.tsx | 25 + components/ui/spinner.tsx | 16 + components/ui/switch.tsx | 31 + components/ui/table.tsx | 116 + components/ui/tabs.tsx | 66 + components/ui/textarea.tsx | 18 + components/ui/toast.tsx | 129 + components/ui/toaster.tsx | 35 + components/ui/toggle-group.tsx | 73 + components/ui/toggle.tsx | 47 + components/ui/tooltip.tsx | 61 + components/ui/use-mobile.tsx | 19 + components/ui/use-toast.ts | 191 ++ data/home.json | 293 ++ data/site.json | 60 + hooks/use-mobile.ts | 19 + hooks/use-parallax.ts | 48 + hooks/use-toast.ts | 191 ++ lib/utils.ts | 6 + next-env.d.ts | 6 + next.config.mjs | 11 + package.json | 72 + pnpm-lock.yaml | 3208 ++++++++++++++++++++ postcss.config.mjs | 8 + public/apple-icon.png | Bin 0 -> 2626 bytes public/icon-dark-32x32.png | Bin 0 -> 585 bytes public/icon-light-32x32.png | Bin 0 -> 566 bytes public/icon.svg | 26 + public/images/about.jpg | Bin 0 -> 132240 bytes public/images/hero-bg.jpg | Bin 0 -> 166934 bytes public/images/news-1.jpg | Bin 0 -> 127523 bytes public/images/news-2.jpg | Bin 0 -> 109582 bytes public/images/news-3.jpg | Bin 0 -> 75010 bytes public/images/team-1.jpg | Bin 0 -> 77113 bytes public/images/team-2.jpg | Bin 0 -> 50587 bytes public/images/team-3.jpg | Bin 0 -> 60903 bytes public/images/team-4.jpg | Bin 0 -> 43118 bytes public/placeholder-logo.png | Bin 0 -> 568 bytes public/placeholder-logo.svg | 1 + public/placeholder-user.jpg | Bin 0 -> 1635 bytes public/placeholder.jpg | Bin 0 -> 1064 bytes public/placeholder.svg | 1 + styles/globals.css | 125 + tsconfig.json | 41 + 110 files changed, 12842 insertions(+) create mode 100644 .gitignore create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/page.tsx create mode 100644 components.json create mode 100644 components/footer.tsx create mode 100644 components/navbar.tsx create mode 100644 components/sections/about-section.tsx create mode 100644 components/sections/advantages-section.tsx create mode 100644 components/sections/cases-section.tsx create mode 100644 components/sections/contact-section.tsx create mode 100644 components/sections/cta-banner.tsx create mode 100644 components/sections/hero-section.tsx create mode 100644 components/sections/news-section.tsx create mode 100644 components/sections/partners-section.tsx create mode 100644 components/sections/process-section.tsx create mode 100644 components/sections/products-section.tsx create mode 100644 components/sections/services-section.tsx create mode 100644 components/sections/solutions-section.tsx create mode 100644 components/sections/team-section.tsx create mode 100644 components/theme-provider.tsx create mode 100644 components/ui/accordion.tsx create mode 100644 components/ui/alert-dialog.tsx create mode 100644 components/ui/alert.tsx create mode 100644 components/ui/aspect-ratio.tsx create mode 100644 components/ui/avatar.tsx create mode 100644 components/ui/badge.tsx create mode 100644 components/ui/breadcrumb.tsx create mode 100644 components/ui/button-group.tsx create mode 100644 components/ui/button.tsx create mode 100644 components/ui/calendar.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/carousel.tsx create mode 100644 components/ui/chart.tsx create mode 100644 components/ui/checkbox.tsx create mode 100644 components/ui/collapsible.tsx create mode 100644 components/ui/command.tsx create mode 100644 components/ui/context-menu.tsx create mode 100644 components/ui/dialog.tsx create mode 100644 components/ui/drawer.tsx create mode 100644 components/ui/dropdown-menu.tsx create mode 100644 components/ui/empty.tsx create mode 100644 components/ui/field.tsx create mode 100644 components/ui/form.tsx create mode 100644 components/ui/hover-card.tsx create mode 100644 components/ui/input-group.tsx create mode 100644 components/ui/input-otp.tsx create mode 100644 components/ui/input.tsx create mode 100644 components/ui/item.tsx create mode 100644 components/ui/kbd.tsx create mode 100644 components/ui/label.tsx create mode 100644 components/ui/menubar.tsx create mode 100644 components/ui/navigation-menu.tsx create mode 100644 components/ui/pagination.tsx create mode 100644 components/ui/popover.tsx create mode 100644 components/ui/progress.tsx create mode 100644 components/ui/radio-group.tsx create mode 100644 components/ui/resizable.tsx create mode 100644 components/ui/scroll-area.tsx create mode 100644 components/ui/section-reveal.tsx create mode 100644 components/ui/select.tsx create mode 100644 components/ui/separator.tsx create mode 100644 components/ui/sheet.tsx create mode 100644 components/ui/sidebar.tsx create mode 100644 components/ui/skeleton.tsx create mode 100644 components/ui/slider.tsx create mode 100644 components/ui/sonner.tsx create mode 100644 components/ui/spinner.tsx create mode 100644 components/ui/switch.tsx create mode 100644 components/ui/table.tsx create mode 100644 components/ui/tabs.tsx create mode 100644 components/ui/textarea.tsx create mode 100644 components/ui/toast.tsx create mode 100644 components/ui/toaster.tsx create mode 100644 components/ui/toggle-group.tsx create mode 100644 components/ui/toggle.tsx create mode 100644 components/ui/tooltip.tsx create mode 100644 components/ui/use-mobile.tsx create mode 100644 components/ui/use-toast.ts create mode 100644 data/home.json create mode 100644 data/site.json create mode 100644 hooks/use-mobile.ts create mode 100644 hooks/use-parallax.ts create mode 100644 hooks/use-toast.ts create mode 100644 lib/utils.ts create mode 100644 next-env.d.ts create mode 100644 next.config.mjs create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 postcss.config.mjs create mode 100644 public/apple-icon.png create mode 100644 public/icon-dark-32x32.png create mode 100644 public/icon-light-32x32.png create mode 100644 public/icon.svg create mode 100644 public/images/about.jpg create mode 100644 public/images/hero-bg.jpg create mode 100644 public/images/news-1.jpg create mode 100644 public/images/news-2.jpg create mode 100644 public/images/news-3.jpg create mode 100644 public/images/team-1.jpg create mode 100644 public/images/team-2.jpg create mode 100644 public/images/team-3.jpg create mode 100644 public/images/team-4.jpg create mode 100644 public/placeholder-logo.png create mode 100644 public/placeholder-logo.svg create mode 100644 public/placeholder-user.jpg create mode 100644 public/placeholder.jpg create mode 100644 public/placeholder.svg create mode 100644 styles/globals.css create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c138a60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# v0 sandbox internal files +__v0_runtime_loader.js +__v0_devtools.tsx +__v0_jsx-dev-runtime.ts +.npmrc +.snowflake/ +.v0-trash/ +.vercel/ +next.user-config.* + +# Environment variables +.env*.local + +# Common ignores +node_modules/ +.next/ +.DS_Store \ No newline at end of file diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..6f9653c --- /dev/null +++ b/app/globals.css @@ -0,0 +1,170 @@ +@import 'tailwindcss'; +@import 'tw-animate-css'; + +@custom-variant dark (&:is(.dark *)); + +:root { + /* 企业深蓝主题 — #1B5CE6 */ + --background: oklch(1 0 0); + --foreground: oklch(0.18 0.025 260); + + --card: oklch(1 0 0); + --card-foreground: oklch(0.18 0.025 260); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.18 0.025 260); + + /* #1B5CE6 → oklch(0.46 0.22 264) */ + --primary: oklch(0.46 0.22 264); + --primary-foreground: oklch(1 0 0); + --primary-dark: oklch(0.36 0.20 264); + --primary-light: oklch(0.95 0.04 264); + --primary-glow: oklch(0.46 0.22 264 / 0.18); + + /* 辅助中性 */ + --secondary: oklch(0.96 0.008 260); + --secondary-foreground: oklch(0.18 0.025 260); + --muted: oklch(0.96 0.008 260); + --muted-foreground: oklch(0.50 0.018 260); + --accent: oklch(0.95 0.04 264); + --accent-foreground: oklch(0.36 0.20 264); + + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.91 0.012 260); + --input: oklch(0.91 0.012 260); + --ring: oklch(0.46 0.22 264); + --radius: 0.5rem; + + /* 深色背景用 */ + --surface-dark: oklch(0.13 0.04 264); + --surface-dark-2: oklch(0.17 0.035 264); + + --sidebar: oklch(0.97 0.006 260); + --sidebar-foreground: oklch(0.18 0.025 260); + --sidebar-primary: oklch(0.46 0.22 264); + --sidebar-primary-foreground: oklch(1 0 0); + --sidebar-accent: oklch(0.95 0.04 264); + --sidebar-accent-foreground: oklch(0.36 0.20 264); + --sidebar-border: oklch(0.91 0.012 260); + --sidebar-ring: oklch(0.46 0.22 264); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); +} + +@theme inline { + --font-sans: var(--font-inter), 'Inter', sans-serif; + --font-mono: 'Geist Mono', 'Geist Mono Fallback'; + --color-primary-dark: var(--primary-dark); + --color-primary-light: var(--primary-light); + --color-primary-glow: var(--primary-glow); + --color-surface-dark: var(--surface-dark); + --color-surface-dark-2: var(--surface-dark-2); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + html { + scroll-behavior: smooth; + } + body { + @apply bg-background text-foreground; + overflow-x: hidden; + } +} + +@layer components { + .section-panel { + min-height: 100svh; + scroll-snap-align: start; + scroll-snap-stop: always; + position: relative; + display: flex; + align-items: center; + padding-block: clamp(5.5rem, 8vh, 7rem) clamp(2.5rem, 4vh, 4rem); + } +} + +@media (min-width: 1024px) { + html { + scroll-snap-type: y mandatory; + scroll-padding-top: 0; + } +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + scroll-snap-type: none; + } +} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..10b104d --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,37 @@ +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import { Analytics } from '@vercel/analytics/next' +import './globals.css' + +const inter = Inter({ + subsets: ['latin'], + variable: '--font-inter', + display: 'swap', +}) + +export const metadata: Metadata = { + title: '连锁门店收银、分账、监管系统 | 智店软件', + description: '面向连锁餐饮、零售与加盟品牌,提供门店收银、自动分账、总部监管一体化系统定制与升级服务。兼容现有设备与流程,支持不停业切换。', + keywords: '连锁门店系统, 收银系统, 自动分账系统, 总部监管系统, 加盟门店结算, 门店数字化, 智店软件', + authors: [{ name: '智店软件' }], + openGraph: { + title: '连锁门店收银、分账、监管系统 | 智店软件', + description: '为连锁餐饮、零售与加盟品牌提供门店收银、自动分账、总部监管一体化系统定制与升级服务。', + type: 'website', + }, +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + + {children} + + + + ) +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..520b85f --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,27 @@ +import { Navbar } from "@/components/navbar" +import { Footer } from "@/components/footer" +import { HeroSection } from "@/components/sections/hero-section" +import { ProductsSection } from "@/components/sections/products-section" +import { AdvantagesSection } from "@/components/sections/advantages-section" +import { SolutionsSection } from "@/components/sections/solutions-section" +import { ProcessSection } from "@/components/sections/process-section" +import { CasesSection } from "@/components/sections/cases-section" +import { ContactSection } from "@/components/sections/contact-section" + +export default function Home() { + return ( +
+ +
+ + + + + + + +
+
+
+ ) +} diff --git a/components.json b/components.json new file mode 100644 index 0000000..4ee62ee --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/components/footer.tsx b/components/footer.tsx new file mode 100644 index 0000000..49be1b9 --- /dev/null +++ b/components/footer.tsx @@ -0,0 +1,98 @@ +import Link from "next/link" +import { LayoutGrid, Phone, Mail, MapPin, ArrowRight } from "lucide-react" +import siteData from "@/data/site.json" + +export function Footer() { + const { company, footer } = siteData + + return ( +
+ {/* Main grid */} +
+
+ + {/* Brand column */} +
+
+
+ +
+
+ {company.name} + {company.nameEn.toUpperCase()} +
+
+ +

+ {company.description} +

+ +
+ + + {company.phone} + + + + {company.email} + + + + {company.address} + +
+
+ + {/* Link columns */} + {footer.columns.map((col) => ( +
+

+ {col.title} +

+
    + {col.links.map((link) => ( +
  • + + + {link.label} + +
  • + ))} +
+
+ ))} +
+
+ + {/* Bottom bar */} +
+
+

{footer.copyright}

+
+ {footer.legal.map((item) => ( + + {item.label} + + ))} + {company.icp && ( + {company.icp} + )} +
+
+
+
+ ) +} diff --git a/components/navbar.tsx b/components/navbar.tsx new file mode 100644 index 0000000..47b7222 --- /dev/null +++ b/components/navbar.tsx @@ -0,0 +1,150 @@ +"use client" + +import { useState, useEffect } from "react" +import Link from "next/link" +import { Button } from "@/components/ui/button" +import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet" +import { Menu, X } from "lucide-react" +import { cn } from "@/lib/utils" +import siteData from "@/data/site.json" + +export function Navbar() { + const [scrolled, setScrolled] = useState(false) + const [open, setOpen] = useState(false) + const { company, nav } = siteData + + useEffect(() => { + const onScroll = () => setScrolled(window.scrollY > 40) + window.addEventListener("scroll", onScroll, { passive: true }) + return () => window.removeEventListener("scroll", onScroll) + }, []) + + return ( +
+
+
+ + {/* Logo */} + +
+ + {company.name} + + + {company.nameEn.toUpperCase()} + +
+ + + {/* Desktop Nav */} + + + {/* CTA */} +
+ +
+ + {/* Mobile Menu */} +
+ + + + + +
+
+
+ {company.name} + + {company.nameEn.toUpperCase()} + +
+ +
+ +
+ +
+
+
+
+
+ +
+
+
+ ) +} diff --git a/components/sections/about-section.tsx b/components/sections/about-section.tsx new file mode 100644 index 0000000..5c34a09 --- /dev/null +++ b/components/sections/about-section.tsx @@ -0,0 +1,76 @@ +import { Badge } from "@/components/ui/badge" +import { Card, CardContent } from "@/components/ui/card" +import { + Zap, + ShieldCheck, + Layers, + BarChart3, + Headset, + Cloud, +} from "lucide-react" +import homeData from "@/data/home.json" + +const iconMap: Record = { + "zap": , + "shield-check": , + "layers": , + "bar-chart-3": , + "headset": , + "cloud": , +} + +export function AboutSection() { + const { advantages } = homeData + + return ( +
+
+ {/* Header */} +
+ + {advantages.sectionBadge} + +

+ {advantages.title} +

+

+ {advantages.subtitle} +

+
+ + {/* 3x2 grid */} +
+ {advantages.items.map((item, index) => ( + + {/* Top accent line */} +
+ + + {/* Icon */} +
+ {iconMap[item.icon]} +
+ + {/* Index number */} +
+

{item.title}

+ + {String(index + 1).padStart(2, "0")} + +
+ +

{item.description}

+
+ + ))} +
+
+
+ ) +} diff --git a/components/sections/advantages-section.tsx b/components/sections/advantages-section.tsx new file mode 100644 index 0000000..b620d99 --- /dev/null +++ b/components/sections/advantages-section.tsx @@ -0,0 +1,90 @@ +"use client" + +import { Badge } from "@/components/ui/badge" +import { + Zap, ShieldCheck, Layers, BarChart3, Headset, Cloud, Settings2, CloudCog, Link2, TrendingUp, +} from "lucide-react" +import { SectionReveal } from "@/components/ui/section-reveal" +import homeData from "@/data/home.json" + +const iconMap: Record = { + "zap": , + "shield-check": , + "layers": , + "bar-chart-3": , + "headset": , + "cloud": , + "Zap": , + "Settings2": , + "CloudCog": , + "Headphones": , + "Link2": , + "TrendingUp": , +} + +export function AdvantagesSection() { + const { advantages } = homeData + + return ( +
+ {/* Subtle background decoration */} +
+ ) +} diff --git a/components/sections/cases-section.tsx b/components/sections/cases-section.tsx new file mode 100644 index 0000000..6ab9c21 --- /dev/null +++ b/components/sections/cases-section.tsx @@ -0,0 +1,90 @@ +"use client" + +import { Badge } from "@/components/ui/badge" +import { SectionReveal } from "@/components/ui/section-reveal" +import { TrendingUp, Quote } from "lucide-react" +import homeData from "@/data/home.json" + +export function CasesSection() { + const { cases } = homeData + const sectionBadge = (cases as any).sectionBadge ?? (cases as any).sectionTag + const caseItems = (Array.isArray((cases as any).items) ? (cases as any).items : []).map((item: any) => ({ + ...item, + result: item.result ?? item.solution ?? "", + metrics: Array.isArray(item.metrics) + ? item.metrics + : (Array.isArray(item.results) ? item.results.map((m: any) => ({ l: m.metric, v: m.value })) : []), + })) + + return ( +
+ + + {/* Header */} +
+ + {sectionBadge} + +

+ {cases.title} +

+

+ {cases.subtitle} +

+
+ + {/* Case cards */} +
+ {caseItems.map((c) => ( +
+
+ {/* Brand & scale */} +
+
+ + {c.industry} + +

{c.brand}

+ + {c.scale} + +
+ +
+ + {/* Challenge */} +
+

遇到的挑战

+

{c.challenge}

+
+ + {/* Divider */} +
+ + {/* Result */} +
+ +

{c.result}

+
+ + {/* Metrics */} +
+ {c.metrics.map((m) => ( +
+ {m.v} + {m.l} +
+ ))} +
+
+
+ ))} +
+ +
+
+ ) +} diff --git a/components/sections/contact-section.tsx b/components/sections/contact-section.tsx new file mode 100644 index 0000000..dd7ff11 --- /dev/null +++ b/components/sections/contact-section.tsx @@ -0,0 +1,154 @@ +"use client" + +import { useState } from "react" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" +import { SectionReveal } from "@/components/ui/section-reveal" +import { Phone, Mail, MapPin, Clock3, ArrowRight, CheckCircle2 } from "lucide-react" +import homeData from "@/data/home.json" + +const iconMap: Record = { + phone: , + mail: , + mappin: , + clock: , +} + +export function ContactSection() { + const { contact } = homeData + const sectionBadge = (contact as any).sectionBadge ?? (contact as any).sectionTag + const info = ((contact as any).info ?? + ((contact as any).infoItems ?? []).map((item: any) => ({ + ...item, + icon: String(item.icon ?? "").toLowerCase(), + sub: item.sub ?? "", + }))) as Array<{ icon: string; label: string; value: string; sub?: string }> + const submitLabel = (contact.form as any).submitLabel ?? (contact.form as any).submit + const [submitted, setSubmitted] = useState(false) + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + setSubmitted(true) + } + + return ( +
+ + + {/* Header */} +
+ + {sectionBadge} + +

+ {contact.title} +

+

+ {contact.subtitle} +

+
+ +
+ + {/* Left: info */} +
+ {info.map((item) => ( +
+
+ {iconMap[item.icon] ?? iconMap.phone} +
+
+

{item.label}

+

{item.value}

+

{item.sub}

+
+
+ ))} + + {/* Trust note */} +
+

响应时效说明

+

+ 工作时间内,专属顾问将在{" "} + 2 小时内 + {" "}联系您;非工作时间提交的信息,将在次一工作日上午统一回复。 +

+
+
+ + {/* Right: form */} +
+ {submitted ? ( +
+
+ +
+
+

信息已提交

+

+ 工作时间内 2 小时响应,非工作时间次一工作日上午回复。 +

+
+ +
+ ) : ( +
+

留下信息,先拿一版初步建议

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +