增加blog路由
184
app/blog/[...slug]/page.jsx
Normal file
@ -0,0 +1,184 @@
|
||||
import Banner from "@/components/banner/Banner";
|
||||
import Category from "@/components/sidebar/Category";
|
||||
import { getSiteSettings } from "@/utils/data";
|
||||
import { notFound } from 'next/navigation';
|
||||
import DynamicListPage from "@/components/common/DynamicListPage";
|
||||
import { Suspense } from 'react';
|
||||
import { getPageData } from "@/utils/data";
|
||||
import ProductImageGallery from "@/components/products/ProductImageGallery";
|
||||
|
||||
const baseSlug = 'blog';
|
||||
|
||||
const LoadingSpinner = () => (
|
||||
<div className="flex justify-center items-center p-8">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const revalidate = 3600;
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const resolvedParams = await params;
|
||||
const slug = resolvedParams.slug || [];
|
||||
const slugArr = [baseSlug, ...(Array.isArray(slug) ? slug : [slug])];
|
||||
const { data, error, page_info } = await getPageData({ slug_list: slugArr });
|
||||
const siteSettings = await getSiteSettings();
|
||||
const siteTitle = siteSettings.site_title || '';
|
||||
const siteName = siteSettings.site_name || '';
|
||||
const siteNameInPageTitles = siteSettings.site_name_in_page_titles || 'None';
|
||||
|
||||
let title = '';
|
||||
if (error) {
|
||||
title = error.title || 'Page Error';
|
||||
return {
|
||||
title,
|
||||
description: error.message || '',
|
||||
};
|
||||
}
|
||||
if (Array.isArray(data) && page_info) {
|
||||
title = page_info.meta_title || page_info.title || '';
|
||||
if (siteName && title) {
|
||||
if (siteNameInPageTitles === 'After') {
|
||||
title = `${title} - ${siteName}`;
|
||||
} else if (siteNameInPageTitles === 'Before') {
|
||||
title = `${siteName} - ${title}`;
|
||||
}
|
||||
}
|
||||
return {
|
||||
title,
|
||||
description: page_info.meta_description || '',
|
||||
};
|
||||
}
|
||||
title = data?.meta_title || data?.title || '';
|
||||
if (siteName && title) {
|
||||
if (siteNameInPageTitles === 'After') {
|
||||
title = `${title} - ${siteName}`;
|
||||
} else if (siteNameInPageTitles === 'Before') {
|
||||
title = `${siteName} - ${title}`;
|
||||
}
|
||||
}
|
||||
return {
|
||||
title,
|
||||
description: data?.meta_description || '',
|
||||
};
|
||||
}
|
||||
|
||||
export default async function Page({ params, searchParams }) {
|
||||
const resolvedParams = await params;
|
||||
const slug = resolvedParams.slug || [];
|
||||
const slugArr = [baseSlug, ...(Array.isArray(slug) ? slug : [slug])];
|
||||
|
||||
const siteSettings = await getSiteSettings();
|
||||
const pageSize = Number(siteSettings.page_size) || 12;
|
||||
|
||||
const { data, error, total } = await getPageData({
|
||||
slug_list: slugArr,
|
||||
page: 1,
|
||||
page_size: pageSize,
|
||||
downloadFiles: true,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
notFound();
|
||||
}
|
||||
const bannerComponentName = 'Banner-' + baseSlug;
|
||||
const categoryComponentName = 'Category-' + baseSlug;
|
||||
if (Array.isArray(data)) {
|
||||
const currentPath = '/' + slugArr.join('/');
|
||||
const listColumns = 4;
|
||||
return (
|
||||
<>
|
||||
<Banner componentName={bannerComponentName} />
|
||||
<div className="wrapper !bg-[#ffffff]">
|
||||
<div className="container py-[4.5rem] xl:!py-24 lg:!py-24 md:!py-24">
|
||||
<div className="flex flex-col md:flex-row mx-[-15px] xl:mx-[-35px] lg:mx-[-20px]">
|
||||
<Category componentName={categoryComponentName} />
|
||||
<div className="flex-1 min-w-0 !px-[15px] max-w-full md:!px-[20px] lg:!px-[20px] xl:!px-[35px]">
|
||||
<Suspense fallback={<LoadingSpinner />}>
|
||||
<DynamicListPage
|
||||
initialItems={data}
|
||||
slugArr={slugArr}
|
||||
basePath={currentPath}
|
||||
columns={listColumns}
|
||||
pageSize={pageSize}
|
||||
totalItems={total}
|
||||
searchParams={searchParams}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
} else if (data) {
|
||||
return (
|
||||
<>
|
||||
<Banner componentName={bannerComponentName} />
|
||||
<div className="wrapper !bg-[#ffffff]">
|
||||
<div className="container py-[4.5rem] xl:!py-24 lg:!py-24 md:!py-24">
|
||||
<div className="flex flex-col md:flex-row mx-[-15px] xl:mx-[-35px] lg:mx-[-20px]">
|
||||
<Category componentName={categoryComponentName} />
|
||||
<div className="flex-1 min-w-0 !px-[15px] max-w-full md:!px-[20px] lg:!px-[20px] xl:!px-[35px]">
|
||||
<div className="bg-white rounded-lg shadow-md p-6 md:p-10">
|
||||
{/* 图片和附加信息并排显示,响应式优化 */}
|
||||
{(data.image || data.subtitle) && (
|
||||
<div className="flex flex-col md:flex-row gap-4 md:gap-10 mb-6 md:mb-8 items-center md:items-start w-full max-w-full">
|
||||
{/* 图片轮播区块 */}
|
||||
<ProductImageGallery data={data} />
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
{/* 产品标题 */}
|
||||
{data.title && (
|
||||
<h1 className="text-2xl md:text-3xl font-bold text-gray-900 mb-10 md:mb-10 leading-tight">
|
||||
{data.title}
|
||||
</h1>
|
||||
)}
|
||||
{/* 产品副标题 */}
|
||||
{data.subtitle && (
|
||||
<div className="bg-gray-50 whitespace-pre-line text-gray-700 w-full max-w-full prose prose-sm pt-6">
|
||||
{data.subtitle}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Content 和 Additional Content 标签式排版 */}
|
||||
<ul className="nav nav-tabs nav-tabs-basic !mt-[70px]">
|
||||
<li className="nav-item">
|
||||
<a className="nav-link active" data-bs-toggle="tab" href="#tab1-1">
|
||||
Description
|
||||
</a>
|
||||
</li>
|
||||
{data.additional_content && (
|
||||
<li className="nav-item">
|
||||
<a className="nav-link" data-bs-toggle="tab" href="#tab1-2">
|
||||
Additional
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
|
||||
{/* 标签内容 */}
|
||||
<div className="tab-content !mt-0 md:!mt-5">
|
||||
<div className="tab-pane fade show active" id="tab1-1">
|
||||
<div className="prose max-w-none text-gray-700 px-1 md:px-0 prose-img:max-w-full prose-img:h-auto prose-table:border prose-table:border-gray-300 prose-th:border prose-th:border-gray-300 prose-td:border prose-td:border-gray-300 prose-th:bg-gray-50 prose-table:rounded-lg prose-table:overflow-hidden" dangerouslySetInnerHTML={{ __html: data.content || '' }} />
|
||||
</div>
|
||||
|
||||
{data.additional_content && (
|
||||
<div className="tab-pane fade" id="tab1-2">
|
||||
<div className="prose max-w-none text-gray-700 px-1 md:px-0 prose-img:max-w-full prose-img:h-auto prose-table:border prose-table:border-gray-300 prose-th:border prose-th:border-gray-300 prose-td:border prose-td:border-gray-300 prose-th:bg-gray-50 prose-table:rounded-lg prose-table:overflow-hidden" dangerouslySetInnerHTML={{ __html: data.additional_content }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
notFound();
|
||||
}
|
||||
}
|
||||
BIN
public/files/5oi1dqhaj5_27741ab5.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/files/697qlr5qkt_6da2a7a5.png
Normal file
|
After Width: | Height: | Size: 908 KiB |
BIN
public/files/6d7fumipp2_563418ea.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/files/6gf02l7c2r_7af165e5.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/files/6ka43a6d4l_8a532a29.png
Normal file
|
After Width: | Height: | Size: 919 KiB |
BIN
public/files/6phtiuie9s_8100085c.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
public/files/6ssqbuu4u6_903c606a.png
Normal file
|
After Width: | Height: | Size: 926 KiB |
BIN
public/files/70uq17d12c_8b9ead18.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
public/files/75jcdqvku5_18fd7939.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/files/79gag53jn0_6bcb976f.png
Normal file
|
After Width: | Height: | Size: 966 KiB |
BIN
public/files/7d8bstk1q9_842ca16b.png
Normal file
|
After Width: | Height: | Size: 951 KiB |
BIN
public/files/aietdral22_150a4ecb.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/files/aietdral22_79b5b015.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/files/aietdral22_98a79c2d.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/files/aietdral22_fe39b7ae.png
Normal file
|
After Width: | Height: | Size: 1009 KiB |
BIN
public/files/alqodu24nk_4b3bd4e3.png
Normal file
|
After Width: | Height: | Size: 999 KiB |
BIN
public/files/alqodu24nk_54e1e825.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
public/files/alqodu24nk_a5957cdb.png
Normal file
|
After Width: | Height: | Size: 827 KiB |
BIN
public/files/alqodu24nk_ef2b39eb.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
public/files/ap2bod0qtt_6c2e0fc2.png
Normal file
|
After Width: | Height: | Size: 994 KiB |
BIN
public/files/ap2bod0qtt_802a282b.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/files/ap2bod0qtt_c18fa696.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/files/ap2bod0qtt_e2f01576.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/files/asbnv2d2v7_d13b3323.png
Normal file
|
After Width: | Height: | Size: 628 KiB |
BIN
public/files/av08akuvvq_d8178987.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/files/b1d747h38e_1744b9ac.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/files/b1d747h38e_328d12bc.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
public/files/b1d747h38e_c77c07bf.png
Normal file
|
After Width: | Height: | Size: 1016 KiB |
BIN
public/files/b1d747h38e_ec2b44fc.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/files/b4egrujm6o_ad007d00.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/files/b7j2a141ug_896519aa.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
public/files/b7j2a141ug_b875c435.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/files/b7j2a141ug_c7802b05.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/files/b7j2a141ug_c7b8e64f.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/files/ba9vsf1hb3_f918c57d.png
Normal file
|
After Width: | Height: | Size: 916 KiB |
BIN
public/files/bdhiflcjfd_05b4fe91.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/files/bgp4hu8cdh_2ce23f98.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
public/files/bjkobigne3_bd150dda.png
Normal file
|
After Width: | Height: | Size: 828 KiB |
BIN
public/files/bmp2nuesfh_97f06aea.png
Normal file
|
After Width: | Height: | Size: 943 KiB |
BIN
public/files/bq77d4je3k_729aba41.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/files/bthq10th5l_43a3bb17.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
public/files/c0lkhlu027_35de88da.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/files/c3hfe4iru4_0a459749.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
public/files/c3hfe4iru4_4088b2d5.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/files/c3hfe4iru4_6f33c7bf.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
public/files/c3hfe4iru4_e6e83513.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/files/v8lal09pnf_005776e3.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |