From a0d2f9529f26ed34b9f148da30c8b5ee2982ff2b Mon Sep 17 00:00:00 2001 From: jingrow Date: Sat, 21 Jun 2025 02:41:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ISR=E9=9D=99=E6=80=81?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/[...slug]/page.jsx | 72 ++++++++++++--------- app/api/get-all-slugs/route.js | 26 ++++++++ app/products/[...slug]/page.jsx | 50 ++++++++------- components/common/DynamicListPage.jsx | 77 +++++++++++++++++++++++ components/homes/home-15/SwiperItems.jsx | 21 ++----- public/assets/img/favicon.png | Bin 1667 -> 1538 bytes public/files/1699843604725442.jpg | Bin 0 -> 16708 bytes public/files/1699843611321512.jpg | Bin 0 -> 14034 bytes public/files/1704270166722004.jpg | Bin 0 -> 60064 bytes public/files/1704270743246019.jpg | Bin 0 -> 25900 bytes public/files/1704270756288208.jpg | Bin 0 -> 16508 bytes public/files/1704270762694484.jpg | Bin 0 -> 17988 bytes public/files/1704270769321643.jpg | Bin 0 -> 18515 bytes public/files/1710486900857804.jpg | Bin 0 -> 42566 bytes public/files/1710494249198915.jpg | Bin 0 -> 41180 bytes public/files/1710566765423279.jpg | Bin 0 -> 36182 bytes public/files/1710575819476429.jpg | Bin 0 -> 41855 bytes public/files/1710576759732944.jpg | Bin 0 -> 24606 bytes public/files/1710576969890036.jpg | Bin 0 -> 29522 bytes public/files/1710577152564664.jpg | Bin 0 -> 43254 bytes public/files/1710577216359592.jpg | Bin 0 -> 34307 bytes public/files/1710577374139802.jpg | Bin 0 -> 54535 bytes public/files/1710577433621321.jpg | Bin 0 -> 43511 bytes public/files/1710577543664512.jpg | Bin 0 -> 45957 bytes public/files/1710733111909648.jpg | Bin 0 -> 35127 bytes public/files/1710739452158728.jpg | Bin 0 -> 30542 bytes public/files/1710741261641604.jpg | Bin 0 -> 31192 bytes public/files/1710742432108514.jpg | Bin 0 -> 30397 bytes public/files/1710743375655253.jpg | Bin 0 -> 38718 bytes public/files/1717143531519950.jpg | Bin 0 -> 42108 bytes public/files/1717143636904772.jpg | Bin 0 -> 22544 bytes public/files/1717144385476848.jpg | Bin 0 -> 38780 bytes public/files/1717144484134102.jpg | Bin 0 -> 32581 bytes public/files/1717145318983421.jpg | Bin 0 -> 34173 bytes public/files/1717145850514382.jpg | Bin 0 -> 25004 bytes public/files/1717145941383395.jpg | Bin 0 -> 8188 bytes public/files/1717145966959952.jpg | Bin 0 -> 9951 bytes public/files/1717574637276125.jpg | Bin 0 -> 4997 bytes public/files/1717574656467011.jpg | Bin 0 -> 35694 bytes public/files/1717575648120734.jpg | Bin 0 -> 28719 bytes public/files/1717637294480917.jpg | Bin 0 -> 44743 bytes public/files/1717637816329322.jpg | Bin 0 -> 36193 bytes public/files/1717638179135036.jpg | Bin 0 -> 50851 bytes public/files/1717641526287621.jpg | Bin 0 -> 38380 bytes public/files/1717641859508907.jpg | Bin 0 -> 40050 bytes public/files/1732343152434863.jpg | Bin 0 -> 45557 bytes public/files/1732343157943173.jpg | Bin 0 -> 84684 bytes public/files/1732343158629210.jpg | Bin 0 -> 62416 bytes public/files/1732343158693230.jpg | Bin 0 -> 60689 bytes public/files/1732344469125604.jpg | Bin 0 -> 34777 bytes public/files/1732344469178325.jpg | Bin 0 -> 60249 bytes public/files/1732344469533749.jpg | Bin 0 -> 64958 bytes public/files/1732344470524903.jpg | Bin 0 -> 110149 bytes public/files/1732345158643012.jpg | Bin 0 -> 47098 bytes public/files/1732345333383085.jpg | Bin 0 -> 43040 bytes public/files/1732346044959105.jpg | Bin 0 -> 37261 bytes public/files/1732347046147477.jpg | Bin 0 -> 50279 bytes public/files/1732347086497612.jpg | Bin 0 -> 54815 bytes public/files/1732347086688416.jpg | Bin 0 -> 110115 bytes public/files/1732347086989659.jpg | Bin 0 -> 63558 bytes public/files/1732347086994637.jpg | Bin 0 -> 70219 bytes public/files/1732348617250323.jpg | Bin 0 -> 57822 bytes public/files/1732348617939505.jpg | Bin 0 -> 60295 bytes public/files/1732348618266933.jpg | Bin 0 -> 38331 bytes public/files/1732348618269171.jpg | Bin 0 -> 42899 bytes public/files/1732348708631476.jpg | Bin 0 -> 44673 bytes public/files/1732349424211378.jpg | Bin 0 -> 43697 bytes public/files/1732349586520237.jpg | Bin 0 -> 45637 bytes public/files/1732349587341394.jpg | Bin 0 -> 82899 bytes public/files/1732349587835967.jpg | Bin 0 -> 51147 bytes public/files/1732349587999431.jpg | Bin 0 -> 89439 bytes public/files/about4.jpg | Bin 0 -> 27659 bytes public/files/bg7.jpg | Bin 0 -> 139718 bytes public/files/bg8.jpg | Bin 0 -> 163494 bytes public/files/bg9.jpg | Bin 0 -> 377780 bytes public/files/bg95fddcf.jpg | Bin 0 -> 77115 bytes public/files/test001.jpg | Bin 0 -> 40083 bytes 77 files changed, 179 insertions(+), 67 deletions(-) create mode 100644 app/api/get-all-slugs/route.js create mode 100644 components/common/DynamicListPage.jsx create mode 100644 public/files/1699843604725442.jpg create mode 100644 public/files/1699843611321512.jpg create mode 100644 public/files/1704270166722004.jpg create mode 100644 public/files/1704270743246019.jpg create mode 100644 public/files/1704270756288208.jpg create mode 100644 public/files/1704270762694484.jpg create mode 100644 public/files/1704270769321643.jpg create mode 100644 public/files/1710486900857804.jpg create mode 100644 public/files/1710494249198915.jpg create mode 100644 public/files/1710566765423279.jpg create mode 100644 public/files/1710575819476429.jpg create mode 100644 public/files/1710576759732944.jpg create mode 100644 public/files/1710576969890036.jpg create mode 100644 public/files/1710577152564664.jpg create mode 100644 public/files/1710577216359592.jpg create mode 100644 public/files/1710577374139802.jpg create mode 100644 public/files/1710577433621321.jpg create mode 100644 public/files/1710577543664512.jpg create mode 100644 public/files/1710733111909648.jpg create mode 100644 public/files/1710739452158728.jpg create mode 100644 public/files/1710741261641604.jpg create mode 100644 public/files/1710742432108514.jpg create mode 100644 public/files/1710743375655253.jpg create mode 100644 public/files/1717143531519950.jpg create mode 100644 public/files/1717143636904772.jpg create mode 100644 public/files/1717144385476848.jpg create mode 100644 public/files/1717144484134102.jpg create mode 100644 public/files/1717145318983421.jpg create mode 100644 public/files/1717145850514382.jpg create mode 100644 public/files/1717145941383395.jpg create mode 100644 public/files/1717145966959952.jpg create mode 100644 public/files/1717574637276125.jpg create mode 100644 public/files/1717574656467011.jpg create mode 100644 public/files/1717575648120734.jpg create mode 100644 public/files/1717637294480917.jpg create mode 100644 public/files/1717637816329322.jpg create mode 100644 public/files/1717638179135036.jpg create mode 100644 public/files/1717641526287621.jpg create mode 100644 public/files/1717641859508907.jpg create mode 100644 public/files/1732343152434863.jpg create mode 100644 public/files/1732343157943173.jpg create mode 100644 public/files/1732343158629210.jpg create mode 100644 public/files/1732343158693230.jpg create mode 100644 public/files/1732344469125604.jpg create mode 100644 public/files/1732344469178325.jpg create mode 100644 public/files/1732344469533749.jpg create mode 100644 public/files/1732344470524903.jpg create mode 100644 public/files/1732345158643012.jpg create mode 100644 public/files/1732345333383085.jpg create mode 100644 public/files/1732346044959105.jpg create mode 100644 public/files/1732347046147477.jpg create mode 100644 public/files/1732347086497612.jpg create mode 100644 public/files/1732347086688416.jpg create mode 100644 public/files/1732347086989659.jpg create mode 100644 public/files/1732347086994637.jpg create mode 100644 public/files/1732348617250323.jpg create mode 100644 public/files/1732348617939505.jpg create mode 100644 public/files/1732348618266933.jpg create mode 100644 public/files/1732348618269171.jpg create mode 100644 public/files/1732348708631476.jpg create mode 100644 public/files/1732349424211378.jpg create mode 100644 public/files/1732349586520237.jpg create mode 100644 public/files/1732349587341394.jpg create mode 100644 public/files/1732349587835967.jpg create mode 100644 public/files/1732349587999431.jpg create mode 100644 public/files/about4.jpg create mode 100644 public/files/bg7.jpg create mode 100644 public/files/bg8.jpg create mode 100644 public/files/bg9.jpg create mode 100644 public/files/bg95fddcf.jpg create mode 100644 public/files/test001.jpg diff --git a/app/[...slug]/page.jsx b/app/[...slug]/page.jsx index e82c78d..e8dc7cd 100644 --- a/app/[...slug]/page.jsx +++ b/app/[...slug]/page.jsx @@ -1,11 +1,38 @@ import Banner from "@/components/banner/Banner"; import Category from "@/components/sidebar/Category"; -import Pagination1 from "@/components/common/Pagination1"; import { getSiteSettings } from "@/utlis/siteSettings"; -import ListPageTemplate from "@/components/common/ListPageTemplate"; import { notFound } from 'next/navigation'; +import DynamicListPage from "@/components/common/DynamicListPage"; +import { Suspense } from 'react'; + +const LoadingSpinner = () => ( +
+
+
+); export const revalidate = 3600; +export const dynamicParams = true; + +export async function generateStaticParams() { + try { + const res = await fetch(`${process.env.SITE_URL}/api/get-all-slugs`); + if (!res.ok) { + throw new Error(`Failed to fetch slugs: ${res.status}`); + } + const slugs = await res.json(); + if (!Array.isArray(slugs)) { + console.error("generateStaticParams: received non-array from /api/get-all-slugs", slugs); + return []; + } + return slugs.map(slug => ({ + slug: slug, + })); + } catch (error) { + console.error("Could not generate static params:", error); + return []; + } +} export async function generateMetadata({ params }) { const resolvedParams = await params; @@ -35,20 +62,18 @@ export async function generateMetadata({ params }) { }; } -export default async function DynamicPage({ params, searchParams }) { +export default async function DynamicPage({ params }) { const resolvedParams = await params; - const resolvedSearchParams = await searchParams; const slugArr = resolvedParams.slug; - const currentPage = Number(resolvedSearchParams?.page) || 1; - // Get global site settings const siteSettings = await getSiteSettings(process.env.SITE_URL); const pageSize = Number(siteSettings.page_size) || 12; + // 始终获取第一页的数据用于静态生成 const res = await fetch(`${process.env.SITE_URL}/api/get-page-data`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ slug_list: slugArr, page: currentPage, page_size: pageSize }) + body: JSON.stringify({ slug_list: slugArr, page: 1, page_size: pageSize }) }); const result = await res.json(); const { data, error, total } = result; @@ -60,26 +85,9 @@ export default async function DynamicPage({ params, searchParams }) { const bannerComponentName = 'Banner-' + (slugArr[0] || 'home'); const categoryComponentName = 'Category-' + (slugArr[0] || 'home'); - // Infer pagetype and name from slugArr - const pagetype = slugArr?.[0] || ''; - const name = slugArr?.[1] || ''; - if (Array.isArray(data)) { const currentPath = '/' + (Array.isArray(slugArr) ? slugArr.join('/') : ''); - const lastSlug = Array.isArray(slugArr) ? slugArr[slugArr.length - 1] : ''; - const totalPages = Math.ceil((total || 0) / pageSize); - - // Adapt template required fields - const listItems = data.map(item => ({ - slug: item.slug, - title: item.title, - image: item.image || item.cover || item.img || '', - additional_title: item.additional_title || '', - subtitle: item.subtitle || item.content || '', - })); - - // 新增:允许通过变量控制列数,默认4 - const listColumns = 4; // 可根据需要改为变量 + const listColumns = 4; return ( <> @@ -89,14 +97,16 @@ export default async function DynamicPage({ params, searchParams }) {
- -
- }> + -
+
diff --git a/app/api/get-all-slugs/route.js b/app/api/get-all-slugs/route.js new file mode 100644 index 0000000..2433fb1 --- /dev/null +++ b/app/api/get-all-slugs/route.js @@ -0,0 +1,26 @@ +import axios from 'axios'; + +const JINGROW_SERVER_URL = process.env.JINGROW_SERVER_URL; + +export async function GET() { + try { + const response = await axios.get( + `${JINGROW_SERVER_URL}/api/method/jsite.api.v1.get_all_slugs` + ); + + const slugs = response.data.message?.data; + + if (!Array.isArray(slugs)) { + console.error('API did not return an array of slugs:', response.data); + return Response.json({ error: '返回的slugs格式不正确' }, { status: 500 }); + } + + return Response.json(slugs); + } catch (error) { + console.error('Error fetching slugs:', error.message, error?.response?.data); + return Response.json( + { error: '获取slugs失败', detail: error?.response?.data || error.message }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/app/products/[...slug]/page.jsx b/app/products/[...slug]/page.jsx index c60ee83..ad942be 100644 --- a/app/products/[...slug]/page.jsx +++ b/app/products/[...slug]/page.jsx @@ -1,16 +1,24 @@ import Banner from "@/components/banner/Banner"; import Category from "@/components/sidebar/Category"; -import Pagination1 from "@/components/common/Pagination1"; import { getSiteSettings } from "@/utlis/siteSettings"; -import ListPageTemplate from "@/components/common/ListPageTemplate"; import { notFound } from 'next/navigation'; +import DynamicListPage from "@/components/common/DynamicListPage"; +import { Suspense } from 'react'; const baseSlug = 'products'; +const LoadingSpinner = () => ( +
+
+
+); + export const revalidate = 3600; export async function generateMetadata({ params }) { - const slugArr = [baseSlug, ...(params.slug ? (Array.isArray(params.slug) ? params.slug : [params.slug]) : [])]; + const resolvedParams = await params; + const slug = resolvedParams.slug || []; + const slugArr = [baseSlug, ...(Array.isArray(slug) ? slug : [slug])]; const res = await fetch(`${process.env.SITE_URL}/api/get-page-data`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -36,15 +44,19 @@ export async function generateMetadata({ params }) { }; } -export default async function Page({ params, searchParams }) { - const slugArr = [baseSlug, ...(params.slug ? (Array.isArray(params.slug) ? params.slug : [params.slug]) : [])]; - const currentPage = Number(searchParams?.page) || 1; +export default async function Page({ params }) { + const resolvedParams = await params; + const slug = resolvedParams.slug || []; + const slugArr = [baseSlug, ...(Array.isArray(slug) ? slug : [slug])]; + const siteSettings = await getSiteSettings(process.env.SITE_URL); const pageSize = Number(siteSettings.page_size) || 12; + + // Fetch only page 1 for initial static render const res = await fetch(`${process.env.SITE_URL}/api/get-page-data`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ slug_list: slugArr, page: currentPage, page_size: pageSize }) + body: JSON.stringify({ slug_list: slugArr, page: 1, page_size: pageSize }) }); const result = await res.json(); const { data, error, total } = result; @@ -54,14 +66,8 @@ export default async function Page({ params, searchParams }) { const bannerComponentName = 'Banner-' + baseSlug; const categoryComponentName = 'Category-' + baseSlug; if (Array.isArray(data)) { - const currentPath = '/' + [baseSlug, ...(params.slug ? (Array.isArray(params.slug) ? params.slug : [params.slug]) : [])].join('/'); - const totalPages = Math.ceil((total || 0) / pageSize); - const listItems = data.map(item => ({ - slug: item.slug, - title: item.title, - image: item.image || item.cover || item.img || '', - content: item.content || item.description || '', - })); + const currentPath = '/' + slugArr.join('/'); + const listColumns = 4; return ( <> @@ -70,14 +76,16 @@ export default async function Page({ params, searchParams }) {
- -
- }> + -
+
diff --git a/components/common/DynamicListPage.jsx b/components/common/DynamicListPage.jsx new file mode 100644 index 0000000..12a02a9 --- /dev/null +++ b/components/common/DynamicListPage.jsx @@ -0,0 +1,77 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useSearchParams } from 'next/navigation'; +import ListPageTemplate from '@/components/common/ListPageTemplate'; +import Pagination1 from '@/components/common/Pagination1'; +import axios from 'axios'; + +const LoadingSpinner = () => ( +
+
+
+); + +export default function DynamicListPage({ initialItems, slugArr, basePath, columns, pageSize, totalItems }) { + const [items, setItems] = useState(initialItems); + const [total, setTotal] = useState(totalItems); + const [isLoading, setIsLoading] = useState(false); + const searchParams = useSearchParams(); + const page = searchParams.get('page'); + const currentPage = Number(page) || 1; + + useEffect(() => { + const fetchPageData = async () => { + if (currentPage === 1) { + setItems(initialItems); + setTotal(totalItems); + return; + } + setIsLoading(true); + try { + const res = await axios.post('/api/get-page-data', { + slug_list: slugArr, + page: currentPage, + page_size: pageSize, + }); + setItems(res.data.data); + setTotal(res.data.total); + } catch (error) { + console.error('Failed to fetch page data:', error); + // Optionally, handle the error in the UI + } finally { + setIsLoading(false); + } + }; + + fetchPageData(); + }, [currentPage, slugArr, pageSize, initialItems, totalItems]); + + const listItems = items.map(item => ({ + ...item, + slug: item.slug, + title: item.title, + image: item.image || item.cover || item.img || '', + additional_title: item.additional_title || '', + subtitle: item.subtitle || item.content || '', + })); + + const totalPages = Math.ceil((total || 0) / pageSize); + + return ( + <> + {isLoading ? ( + + ) : ( + + )} +
+ +
+ + ); +} \ No newline at end of file diff --git a/components/homes/home-15/SwiperItems.jsx b/components/homes/home-15/SwiperItems.jsx index 883d77b..62169a6 100644 --- a/components/homes/home-15/SwiperItems.jsx +++ b/components/homes/home-15/SwiperItems.jsx @@ -1,5 +1,5 @@ "use client"; -import { useEffect, useState, useRef } from "react"; +import { useEffect, useState } from "react"; import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, Pagination, Grid } from "swiper/modules"; import Link from "next/link"; @@ -24,9 +24,6 @@ export default function SwiperItems(props) { const rows = (data?.p1 && !isNaN(Number(data.p1))) ? Number(data.p1) : (props.rows || 1); const button_text = data?.button_text || props.button_text; - const prevRef = useRef(null); - const nextRef = useRef(null); - useEffect(() => { async function fetchComponentData() { try { @@ -192,13 +189,9 @@ export default function SwiperItems(props) { slidesPerGroup={columns * rows} grid={{ rows }} pagination={{ clickable: true, el: ".spdb15" }} - navigation={{ prevEl: prevRef.current, nextEl: nextRef.current }} - onInit={swiper => { - // Swiper 8+ 需要手动传递 ref.current - swiper.params.navigation.prevEl = prevRef.current; - swiper.params.navigation.nextEl = nextRef.current; - swiper.navigation.init(); - swiper.navigation.update(); + navigation={{ + prevEl: '.swiper-nav-prev-swiperitems', + nextEl: '.swiper-nav-next-swiperitems' }} breakpoints={{ 0: { slidesPerView: 1, slidesPerGroup: 1, grid: { rows: 1 } }, @@ -249,8 +242,7 @@ export default function SwiperItems(props) { {/* 美化后的导航按钮 */}