[Next.js][React][TypeScript][TailwindCSS]4.最新消息、產品資訊

文、意如

最新消息

NewsSection.tsx

"use client";

import "keen-slider/keen-slider.min.css";
import { useKeenSlider } from "keen-slider/react";
import { useState } from "react";
import { ArrowLeft, ArrowRight } from "lucide-react";

type NewsItem = {
  title: string;
  image: string;
  summary: string;
  link: string;
};

const newsList: NewsItem[] = [
  {
    title: "全新官網上線啦!",
    image: "/images/news1.jpg",
    summary: "我們重新設計了網站介面,提供更流暢的使用體驗。",
    link: "#",
  },
  {
    title: "夏季優惠活動開跑",
    image: "/images/news2.jpg",
    summary: "限時折扣全面開跑,快來看看有沒有你喜歡的商品吧!",
    link: "#",
  },
  {
    title: "產品上架公告",
    image: "/images/news3.jpg",
    summary: "我們新增了一系列全新產品,立即瀏覽了解更多資訊。",
    link: "#",
  },
  {
    title: "售後服務升級",
    image: "/images/news4.jpg",
    summary: "全新客服系統上線,24 小時為您服務。",
    link: "#",
  },
];

export default function NewsCarousel() {
  const [currentSlide, setCurrentSlide] = useState(0);
  const [sliderRef, instanceRef] = useKeenSlider<HTMLDivElement>({
    loop: false,
    slides: {
      perView: 1,
      spacing: 16,
    },
    breakpoints: {
      "(min-width: 768px)": {
        slides: {
          perView: 3,
          spacing: 24,
        },
      },
    },
    slideChanged(slider) {
      setCurrentSlide(slider.track.details.rel);
    },
  });

  const totalPages = Math.ceil(newsList.length / 3);

  return (
    <section className="bg-white py-16 px-4">
      <div className="max-w-6xl mx-auto">
        <div className="flex justify-between items-center mb-6">
          <h2 className="text-4xl font-bold text-center w-full text-gray-900">
            最新消息
          </h2>
        </div>

        <div className="relative">
          <div ref={sliderRef} className="keen-slider">
            {newsList.map((item, idx) => (
              <div key={idx} className="keen-slider__slide">
                <a
                  href={item.link}
                  className="block bg-white border rounded-lg shadow-sm hover:shadow-md transition h-[400px] overflow-hidden flex flex-col"
                >
                  <img
                    src={item.image}
                    alt={item.title}
                    className="w-full h-48 object-cover rounded-t-lg"
                  />
                  <div className="p-4 flex flex-col flex-grow">
                    <h3 className="text-lg font-semibold text-gray-800 line-clamp-2">
                      {item.title}
                    </h3>
                    <p className="text-sm text-gray-700 mt-2 line-clamp-3">
                      {item.summary}
                    </p>
                  </div>
                </a>
              </div>
            ))}
          </div>
        </div>

        {/* 頁碼指示 */}
        <div className="flex justify-center mt-6 gap-2">
          {Array.from({ length: totalPages }).map((_, idx) => (
            <button
              key={idx}
              onClick={() => instanceRef.current?.moveToIdx(idx * 3)}
              className={`px-3 py-1 text-sm rounded ${
                currentSlide === idx * 3
                  ? "bg-black text-white"
                  : "bg-gray-200 text-gray-800"
              }`}
            >
              {idx + 1}
            </button>
          ))}
        </div>
      </div>
    </section>
  );
}

page.tsx

import Navbar from "@/components/Navbar";
import HeroCarousel from "@/components/HeroCarousel";
import NewsSection from "@/components/NewsSection";

export default function Home() {
  return (
    <>
      <Navbar />
      <main className="pt-15 min-h-screen">
        <HeroCarousel />
        <NewsSection />
        <div className="p-8">
          <h1 className="text-3xl font-bold text-black">首頁內容</h1>
        </div>
      </main>
    </>
  );
}
產品資訊

public/images/
├── product1.jpg
├── product2.jpg
├── product3.jpg
└── product4.jpg
 

src/components/ProductCard.tsx

// src/components/ProductCard.tsx
type Product = {
  id: number
  name: string
  image: string
  description: string
}

export default function ProductCard({ product }: { product: Product }) {
  return (
    <div className="border rounded-lg overflow-hidden shadow-sm hover:shadow-md transition bg-white">
      <img
        src={product.image}
        alt={product.name}
        className="w-full h-48 object-cover"
      />
      <div className="p-4">
        <h3 className="text-lg font-semibold text-gray-800">{product.name}</h3>
        <p className="text-sm text-gray-600 mt-1 line-clamp-2">{product.description}</p>
      </div>
    </div>
  )
}

src/components/ProductSection.tsx

// src/components/ProductSection.tsx
import ProductCard from "@/components/ProductCard"

const dummyProducts = Array.from({ length: 8 }).map((_, index) => ({
  id: index + 1,
  name: `產品名稱 ${index + 1}`,
  image: `/images/product${(index % 8) + 1}.jpg`,
  description: "這是一段產品介紹的範例內容,可以用兩行文字呈現。",
}))

export default function ProductSection() {
  return (
    <section className="py-12 bg-white">
      <div className="max-w-7xl mx-auto px-4">
        <h2 className="text-4xl font-bold text-center mb-8 text-gray-900">產品資訊</h2>
        <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-6">
          {dummyProducts.map((product) => (
            <ProductCard key={product.id} product={product} />
          ))}
        </div>
        <div className="mt-8 flex justify-center">
          <a
            href="/products"
            className="px-6 py-2 bg-black text-white rounded hover:bg-gray-800 transition"
          >
            查看更多
          </a>
        </div>
      </div>
    </section>
  )
}

src/app/page.tsx

// src/app/page.tsx
import Navbar from "@/components/Navbar";
import HeroCarousel from "@/components/HeroCarousel";
import NewsSection from "@/components/NewsSection";
import ProductSection from "@/components/ProductSection";

export default function Home() {
  return (
    <>
      <Navbar />
      <main className="pt-15 min-h-screen">
        <HeroCarousel />
        <NewsSection />
        <ProductSection />
        <div className="p-8">
          <h1 className="text-3xl font-bold text-black">首頁內容</h1>
        </div>
      </main>
    </>
  );
}

 

 

Yiru@Studio - 關於我 - 意如