diff --git a/app/[...slug]/page.jsx b/app/[...slug]/page.jsx index f5248fc..cd031e9 100644 --- a/app/[...slug]/page.jsx +++ b/app/[...slug]/page.jsx @@ -54,7 +54,7 @@ export async function generateMetadata({ params }) { }; } -export default async function DynamicPage({ params }) { +export default async function DynamicPage({ params, searchParams }) { const resolvedParams = await params; const slugArr = resolvedParams.slug; @@ -96,6 +96,7 @@ export default async function DynamicPage({ params }) { columns={listColumns} pageSize={pageSize} totalItems={total} + searchParams={searchParams} /> diff --git a/app/api/get-page-data/route.js b/app/api/get-page-data/route.js new file mode 100644 index 0000000..501ec37 --- /dev/null +++ b/app/api/get-page-data/route.js @@ -0,0 +1,125 @@ +import axios from 'axios'; +import fs from 'fs'; +import path from 'path'; + +const JINGROW_SERVER_URL = process.env.JINGROW_SERVER_URL; +const PUBLIC_FILES_DIR = path.join(process.cwd(), 'public/files'); + +if (!fs.existsSync(PUBLIC_FILES_DIR)) { + fs.mkdirSync(PUBLIC_FILES_DIR, { recursive: true }); +} + +async function downloadToLocal(fileUrl) { + try { + let fullUrl = fileUrl; + if (!/^https?:\/\//.test(fileUrl)) { + fullUrl = `${JINGROW_SERVER_URL}${fileUrl}`; + } + const fileName = path.basename(fullUrl.split('?')[0]); + const localPath = path.join(PUBLIC_FILES_DIR, fileName); + const localUrl = `/files/${fileName}`; + if (!fs.existsSync(localPath)) { + const response = await axios.get(fullUrl, { responseType: 'stream' }); + await new Promise((resolve, reject) => { + const writer = fs.createWriteStream(localPath); + response.data.pipe(writer); + writer.on('finish', resolve); + writer.on('error', reject); + }); + } + return localUrl; + } catch (e) { + return fileUrl; + } +} + +// 提取富文本中的所有图片链接 +function extractImageUrlsFromHtml(html) { + if (!html) return []; + const regex = /]+src=["']([^"'>]+)["']/g; + const urls = []; + let match; + while ((match = regex.exec(html)) !== null) { + urls.push(match[1]); + } + return urls; +} + +// 替换富文本中的图片链接为本地路径 +async function replaceImageUrlsInHtml(html) { + if (!html) return html; + const regex = /]+)src=["']([^"'>]+)["']/g; + return await html.replace(regex, async (match, pre, url) => { + const localUrl = await downloadToLocal(url); + return ` ({ - slug: item.slug, - title: item.title, - image: item.image || item.cover || item.img || '', - content: item.content || item.description || '', - })); - return ( - <> - -
-
-
- -
- -
- -
-
-
-
-
- - ); - } else if (data) { - return ( - <> - -
-
-
- -
-
- {/* 图片和附加信息并排显示,响应式优化 */} - {(data.image || data.subtitle) && ( -
- {data.image && ( -
- {data.title} -
- )} - {data.subtitle && ( -
- {data.subtitle} -
- )} -
- )} - {/* Product Description 标题 */} -
- Product Description -
-
- {data.additional_content && ( - <> - {/* Testing Report 标题 */} -
- Testing Report -
-
- - )} -
-
-
-
-
- - ); - } else { - notFound(); - } -} diff --git a/app/applications/page.jsx b/app/applications/page.jsx deleted file mode 100644 index 6d3efe8..0000000 --- a/app/applications/page.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import Banner from "@/components/banner/Banner"; -import Category from "@/components/sidebar/Category"; -import Gallery from "@/components/common/Gallery"; -import Pagination1 from "@/components/common/Pagination1"; -import { getSiteSettings } from "@/utils/siteSettings"; -import ListPageTemplate from "@/components/common/ListPageTemplate"; -import { notFound } from 'next/navigation'; - -const baseSlug = 'applications'; - -export const revalidate = 3600; - -export async function generateMetadata() { - // 频道首页只需要基础 meta 信息 - return { - title: 'Applications', - description: 'Applications Channel Home', - }; -} - -export default async function Page({ searchParams }) { - const currentPage = Number(searchParams?.page) || 1; - const siteSettings = await getSiteSettings(process.env.SITE_URL); - const pageSize = Number(siteSettings.page_size) || 12; - // 频道首页只查根 slug - const res = await fetch(`${process.env.SITE_URL}/api/get-page-data`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ slug_list: [baseSlug], page: currentPage, page_size: pageSize }) - }); - const result = await res.json(); - const { data, error, total } = result; - if (error) notFound(); - const bannerComponentName = 'Banner-' + baseSlug; - const categoryComponentName = 'Category-' + baseSlug; - const galleryComponentName = 'Gallery-' + baseSlug; - const currentPath = '/' + baseSlug; - const totalPages = Math.ceil((total || 0) / pageSize); - const listItems = Array.isArray(data) ? data.map(item => ({ - slug: item.slug, - title: item.title, - image: item.image || item.cover || item.img || '', - content: item.content || item.description || '', - })) : []; - - return ( - <> - -
-
-
- -
- - -
- -
-
-
-
-
- - ); -} diff --git a/app/products/[...slug]/page.jsx b/app/products/[...slug]/page.jsx index e01aa5f..462a858 100644 --- a/app/products/[...slug]/page.jsx +++ b/app/products/[...slug]/page.jsx @@ -40,7 +40,7 @@ export async function generateMetadata({ params }) { }; } -export default async function Page({ params }) { +export default async function Page({ params, searchParams }) { const resolvedParams = await params; const slug = resolvedParams.slug || []; const slugArr = [baseSlug, ...(Array.isArray(slug) ? slug : [slug])]; @@ -79,6 +79,7 @@ export default async function Page({ params }) { columns={listColumns} pageSize={pageSize} totalItems={total} + searchParams={searchParams} />
diff --git a/components/common/DynamicListPage.jsx b/components/common/DynamicListPage.jsx deleted file mode 100644 index 12a02a9..0000000 --- a/components/common/DynamicListPage.jsx +++ /dev/null @@ -1,77 +0,0 @@ -'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/common/DynamicListPage/DynamicListPageUI.jsx b/components/common/DynamicListPage/DynamicListPageUI.jsx new file mode 100644 index 0000000..cf05617 --- /dev/null +++ b/components/common/DynamicListPage/DynamicListPageUI.jsx @@ -0,0 +1,35 @@ +'use client'; + +import ListPageTemplate from '@/components/common/ListPageTemplate'; +import Pagination1 from '@/components/common/Pagination1'; +import { useSearchParams } from 'next/navigation'; + +export default function DynamicListPageUI({ initialItems, basePath, columns, pageSize, totalItems }) { + const searchParams = useSearchParams(); + const page = searchParams.get('page'); + const currentPage = Number(page) || 1; + + const listItems = initialItems.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((totalItems || 0) / pageSize); + + return ( + <> + +
+ +
+ + ); +} \ No newline at end of file diff --git a/components/common/DynamicListPage/index.jsx b/components/common/DynamicListPage/index.jsx new file mode 100644 index 0000000..2a1325a --- /dev/null +++ b/components/common/DynamicListPage/index.jsx @@ -0,0 +1,40 @@ +import DynamicListPageUI from './DynamicListPageUI'; +import { getPageData } from '@/utils/data'; + +export default async function DynamicListPage({ slugArr, basePath, columns, pageSize = 10, searchParams }) { + // 先 await searchParams + const resolvedSearchParams = await searchParams; + // 解析当前页码 + const currentPage = Number(resolvedSearchParams?.page) || 1; + + let items = []; + let total = 0; + let error = null; + + try { + const result = await getPageData({ + slug_list: slugArr, + page: currentPage, + page_size: pageSize, + downloadFiles: true + }); + items = result.data; + total = result.total; + error = result.error; + } catch (e) { + items = []; + total = 0; + error = e; + } + + return ( + + ); +} \ No newline at end of file