refactor(about): 重构关于页面路由为静态路径
将关于页面的查询参数路由改为静态路径结构,如/about/company 更新i18n消息中的链接路径 添加新的[section]页面处理逻辑 优化静态生成参数和错误处理
This commit is contained in:
120
app/[locale]/about/[section]/page.tsx
Normal file
120
app/[locale]/about/[section]/page.tsx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import MDXComponents from "@/components/mdx/MDXComponents";
|
||||||
|
import { Locale, LOCALES } from "@/i18n/routing";
|
||||||
|
import { constructMetadata } from "@/lib/metadata";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import { Metadata } from "next";
|
||||||
|
import { getTranslations } from "next-intl/server";
|
||||||
|
import { MDXRemote } from "next-mdx-remote-client/rsc";
|
||||||
|
import path from "path";
|
||||||
|
import remarkGfm from "remark-gfm";
|
||||||
|
|
||||||
|
// 强制静态渲染
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
parseFrontmatter: true,
|
||||||
|
mdxOptions: {
|
||||||
|
remarkPlugins: [remarkGfm],
|
||||||
|
rehypePlugins: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 增强:添加参数校验,防止undefined
|
||||||
|
async function getMDXContent(locale: string, section: string) {
|
||||||
|
// 兜底校验:确保参数是字符串
|
||||||
|
const validLocale = locale?.trim() || "en"; // 兜底为默认语言
|
||||||
|
const validSection = section?.trim() || "company"; // 兜底为默认section
|
||||||
|
|
||||||
|
// 校验LOCALES是否包含当前locale
|
||||||
|
if (!LOCALES.includes(validLocale)) {
|
||||||
|
console.error(`Locale "${validLocale}" not found in LOCALES!`);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(
|
||||||
|
process.cwd(),
|
||||||
|
"content",
|
||||||
|
"about",
|
||||||
|
validSection, // 使用校验后的section
|
||||||
|
`${validLocale}.mdx` // 使用校验后的locale
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 先检查文件是否存在,避免读取不存在的文件
|
||||||
|
await fs.access(filePath);
|
||||||
|
const content = await fs.readFile(filePath, "utf-8");
|
||||||
|
return content;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error accessing/reading MDX file (${filePath}):`, error);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修正:路由参数类型(locale + section)
|
||||||
|
type Params = {
|
||||||
|
locale: string;
|
||||||
|
section: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type MetadataProps = {
|
||||||
|
params: Params;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function generateMetadata({
|
||||||
|
params,
|
||||||
|
}: MetadataProps): Promise<Metadata> {
|
||||||
|
const { locale, section } = params;
|
||||||
|
const t = await getTranslations({ locale, namespace: "About" });
|
||||||
|
|
||||||
|
return constructMetadata({
|
||||||
|
page: `About - ${section}`,
|
||||||
|
title: t(`title`) || t("title"), // 适配不同section的标题
|
||||||
|
description: t(`description`) || t("description"),
|
||||||
|
locale: locale as Locale,
|
||||||
|
path: `/about/${section}`,
|
||||||
|
canonicalUrl: `/about/${section}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面组件:从params获取locale和section(路由参数)
|
||||||
|
export default async function AboutPage({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<Params>;
|
||||||
|
}) {
|
||||||
|
const { locale, section } = await params;
|
||||||
|
|
||||||
|
console.log(`Rendering AboutPage for locale: ${locale}, section: ${section}`);
|
||||||
|
// 调用带校验的getContent函数
|
||||||
|
const content = await getMDXContent(locale, section);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article className="w-full md:w-3/5 px-2 md:px-12">
|
||||||
|
<MDXRemote
|
||||||
|
source={content}
|
||||||
|
components={MDXComponents}
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关键:生成locale + section的所有静态组合(覆盖vi/organization等)
|
||||||
|
export async function generateStaticParams() {
|
||||||
|
const sections = ['company', 'awards', 'base', 'culture', 'history', 'organization'];
|
||||||
|
|
||||||
|
// 确保LOCALES包含"vi"(越南语),否则会生成undefined!
|
||||||
|
if (!LOCALES.includes("vi")) {
|
||||||
|
console.warn("⚠️ LOCALES does not include 'vi'! Add it to fix /vi/about/* paths.");
|
||||||
|
// 临时兜底:如果没有vi,手动添加(或检查你的i18n/routing.ts)
|
||||||
|
// LOCALES.push("vi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成所有locale × section的组合
|
||||||
|
return LOCALES.flatMap((locale) =>
|
||||||
|
sections.map((section) => ({
|
||||||
|
locale,
|
||||||
|
section,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,88 +1,32 @@
|
|||||||
import MDXComponents from "@/components/mdx/MDXComponents";
|
import { LOCALES } from "@/i18n/routing";
|
||||||
import { Locale, LOCALES } from "@/i18n/routing";
|
import { redirect } from "next/navigation";
|
||||||
import { constructMetadata } from "@/lib/metadata";
|
|
||||||
import fs from "fs/promises";
|
|
||||||
import { Metadata } from "next";
|
|
||||||
import { getTranslations } from "next-intl/server";
|
|
||||||
import { MDXRemote } from "next-mdx-remote-client/rsc";
|
|
||||||
import path from "path";
|
|
||||||
import remarkGfm from "remark-gfm";
|
|
||||||
|
|
||||||
const options = {
|
// 重定向到 /about/company
|
||||||
parseFrontmatter: true,
|
export default async function Page({
|
||||||
mdxOptions: {
|
|
||||||
remarkPlugins: [remarkGfm],
|
|
||||||
rehypePlugins: [],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getMDXContent(locale: string, section: string) {
|
|
||||||
const filePath = path.join(
|
|
||||||
process.cwd(),
|
|
||||||
"content",
|
|
||||||
"about",
|
|
||||||
section,
|
|
||||||
`${locale}.mdx`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
const content = await fs.readFile(filePath, "utf-8");
|
|
||||||
return content;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error reading MDX file: ${error}`);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Params = Promise<{
|
|
||||||
locale: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
type MetadataProps = {
|
|
||||||
params: Params;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function generateMetadata({
|
|
||||||
params,
|
params,
|
||||||
}: MetadataProps): Promise<Metadata> {
|
|
||||||
const { locale } = await params;
|
|
||||||
const t = await getTranslations({ locale, namespace: "About" });
|
|
||||||
|
|
||||||
return constructMetadata({
|
|
||||||
page: "About",
|
|
||||||
title: t("title"),
|
|
||||||
description: t("description"),
|
|
||||||
locale: locale as Locale,
|
|
||||||
path: `/about`,
|
|
||||||
canonicalUrl: `/about`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function AboutPage({
|
|
||||||
params,
|
|
||||||
searchParams
|
|
||||||
}: {
|
}: {
|
||||||
params: Params;
|
params: Promise<{ locale: string }>;
|
||||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
|
||||||
}) {
|
}) {
|
||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
const resolvedSearchParams = await searchParams;
|
return redirect(`/${locale}/about/company`);
|
||||||
const section = (resolvedSearchParams.section as string) || "company";
|
|
||||||
|
|
||||||
const content = await getMDXContent(locale, section);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<article className="w-full md:w-3/5 px-2 md:px-12">
|
|
||||||
<MDXRemote
|
|
||||||
source={content}
|
|
||||||
components={MDXComponents}
|
|
||||||
options={options}
|
|
||||||
/>
|
|
||||||
</article>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关键:生成locale + section的所有静态组合(覆盖vi/organization等)
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
return LOCALES.map((locale) => ({
|
const sections = ['company', 'awards', 'base', 'culture', 'history', 'organization'];
|
||||||
locale,
|
|
||||||
}));
|
// 确保LOCALES包含"vi"(越南语),否则会生成undefined!
|
||||||
|
if (!LOCALES.includes("vi")) {
|
||||||
|
console.warn("⚠️ LOCALES does not include 'vi'! Add it to fix /vi/about/* paths.");
|
||||||
|
// 临时兜底:如果没有vi,手动添加(或检查你的i18n/routing.ts)
|
||||||
|
// LOCALES.push("vi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成所有locale × section的组合
|
||||||
|
return LOCALES.flatMap((locale) =>
|
||||||
|
sections.map((section) => ({
|
||||||
|
locale,
|
||||||
|
section,
|
||||||
|
}))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -72,51 +72,6 @@ async function getMDXContent(locale: string, section: string): Promise<BlogPost>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析MDX内容,提取frontmatter和正文
|
|
||||||
*/
|
|
||||||
function parseMDXContent(content: string): {
|
|
||||||
frontmatter: Record<string, any>
|
|
||||||
content: string
|
|
||||||
} {
|
|
||||||
// 匹配frontmatter的正则表达式(---开头和结尾)
|
|
||||||
const frontmatterRegex = /^---\s*[\r\n]([\s\S]*?)[\r\n]---\s*[\r\n]/;
|
|
||||||
const match = frontmatterRegex.exec(content);
|
|
||||||
|
|
||||||
let frontmatter: Record<string, any> = {};
|
|
||||||
let postContent = content;
|
|
||||||
|
|
||||||
if (match && match[1]) {
|
|
||||||
// 提取frontmatter部分并解析
|
|
||||||
const frontmatterStr = match[1];
|
|
||||||
postContent = content.slice(match[0].length);
|
|
||||||
|
|
||||||
// 解析frontmatter的每一行
|
|
||||||
const lines = frontmatterStr.split(/[\r\n]+/);
|
|
||||||
lines.forEach(line => {
|
|
||||||
// 跳过空行和注释
|
|
||||||
if (!line.trim() || line.trim().startsWith('#')) return;
|
|
||||||
|
|
||||||
// 分割键值对(支持值中有冒号的情况)
|
|
||||||
const colonIndex = line.indexOf(':');
|
|
||||||
if (colonIndex === -1) return;
|
|
||||||
|
|
||||||
const key = line.substring(0, colonIndex).trim();
|
|
||||||
let value = line.substring(colonIndex + 1).trim();
|
|
||||||
|
|
||||||
// 移除值的引号(如果有)
|
|
||||||
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
||||||
(value.startsWith("'") && value.endsWith("'"))) {
|
|
||||||
value = value.substring(1, value.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
frontmatter[key] = value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { frontmatter, content: postContent };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params,
|
params,
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import { getTranslations } from "next-intl/server";
|
|||||||
|
|
||||||
type Params = Promise<{ locale: string }>;
|
type Params = Promise<{ locale: string }>;
|
||||||
|
|
||||||
type SearchParams = { page?: string; category?: string };
|
|
||||||
|
|
||||||
type MetadataProps = {
|
type MetadataProps = {
|
||||||
params: Params;
|
params: Params;
|
||||||
};
|
};
|
||||||
@@ -33,21 +31,21 @@ export async function generateMetadata({
|
|||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params,
|
params,
|
||||||
searchParams,
|
// searchParams,
|
||||||
}: {
|
}: {
|
||||||
params: Params;
|
params: Params;
|
||||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
|
||||||
}) {
|
}) {
|
||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
const resolvedSearchParams = await searchParams;
|
// const resolvedSearchParams = await searchParams;
|
||||||
const category = resolvedSearchParams.category as string || "";
|
// const category = resolvedSearchParams.category as string || "";
|
||||||
|
|
||||||
let { posts } = await getPosts(locale);
|
let { posts } = await getPosts(locale);
|
||||||
|
|
||||||
const t = await getTranslations("Blog");
|
const t = await getTranslations("Blog");
|
||||||
|
|
||||||
const pageRaw = resolvedSearchParams.page as string || "1";
|
// const pageRaw = resolvedSearchParams.page as string || "1";
|
||||||
const page = Math.max(1, parseInt(pageRaw, 10));
|
// const page = Math.max(1, parseInt(10, 10));
|
||||||
|
const page = 1;
|
||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
const totalPages = Math.max(1, Math.ceil(posts.length / pageSize));
|
const totalPages = Math.max(1, Math.ceil(posts.length / pageSize));
|
||||||
const start = (page - 1) * pageSize;
|
const start = (page - 1) * pageSize;
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import HomeComponent from "@/components/home";
|
import HomeComponent from "@/components/home";
|
||||||
|
|
||||||
// export const dynamic = "force-static";
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return <HomeComponent />;
|
return <HomeComponent />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
// return [
|
return [
|
||||||
// { locale: 'en' },
|
{ locale: 'en' },
|
||||||
// { locale: 'zh' },
|
{ locale: 'zh' },
|
||||||
// { locale: 'vi' },
|
{ locale: 'vi' },
|
||||||
// // { locale: 'ja' },
|
// { locale: 'ja' },
|
||||||
// ]
|
]
|
||||||
// }
|
}
|
||||||
|
|||||||
@@ -169,12 +169,12 @@ export async function generateStaticParams() {
|
|||||||
try {
|
try {
|
||||||
const defaultLocale = LOCALES[0];
|
const defaultLocale = LOCALES[0];
|
||||||
const { products }: { products: Product[] } = await getProducts(defaultLocale);
|
const { products }: { products: Product[] } = await getProducts(defaultLocale);
|
||||||
const validProducts = products.filter((product) => product.slug && product.title);
|
const validProducts = products.filter((product) => product.slug.toString() && product.title);
|
||||||
|
|
||||||
return LOCALES.flatMap((locale) =>
|
return LOCALES.flatMap((locale) =>
|
||||||
validProducts.map((product) => ({
|
validProducts.map((product) => ({
|
||||||
locale,
|
locale,
|
||||||
slug: product.slug,
|
slug: product.slug.toString(),
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { MetadataRoute } from 'next'
|
|||||||
|
|
||||||
const siteUrl = siteConfig.url
|
const siteUrl = siteConfig.url
|
||||||
|
|
||||||
// export const dynamic = "force-static"
|
export const dynamic = "force-static"
|
||||||
|
|
||||||
export default function robots(): MetadataRoute.Robots {
|
export default function robots(): MetadataRoute.Robots {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const siteUrl = siteConfig.url
|
|||||||
|
|
||||||
type ChangeFrequency = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never' | undefined
|
type ChangeFrequency = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never' | undefined
|
||||||
|
|
||||||
// export const dynamic = "force-static"
|
export const dynamic = "force-static"
|
||||||
|
|
||||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||||
// Static pages
|
// Static pages
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default function ProductTabsClient({ detail, spec, packaging, locale }: P
|
|||||||
className={`px-4 py-2 transition-colors ${activeTab === 'packaging' ? 'bg-orange-50 text-orange-600 font-medium' : 'hover:bg-gray-50'}`}
|
className={`px-4 py-2 transition-colors ${activeTab === 'packaging' ? 'bg-orange-50 text-orange-600 font-medium' : 'hover:bg-gray-50'}`}
|
||||||
onClick={() => setActiveTab('packaging')}
|
onClick={() => setActiveTab('packaging')}
|
||||||
>
|
>
|
||||||
{t('productPackaging')}
|
{t('productPacking')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/* 内容区 */}
|
{/* 内容区 */}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import LocaleSwitcher from "@/components/LocaleSwitcher";
|
|||||||
import { siteConfig } from "@/config/site";
|
import { siteConfig } from "@/config/site";
|
||||||
import { Link as I18nLink } from "@/i18n/routing";
|
import { Link as I18nLink } from "@/i18n/routing";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const t = useTranslations("Home");
|
const t = useTranslations("Home");
|
||||||
@@ -18,7 +17,7 @@ const Header = () => {
|
|||||||
prefetch={false}
|
prefetch={false}
|
||||||
className="flex items-center space-x-1 font-bold"
|
className="flex items-center space-x-1 font-bold"
|
||||||
>
|
>
|
||||||
<Image
|
<img
|
||||||
alt={siteConfig.name}
|
alt={siteConfig.name}
|
||||||
src="/logo.png"
|
src="/logo.png"
|
||||||
className="w-6 h-6"
|
className="w-6 h-6"
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ images:
|
|||||||
- http://kelunpuzhonggong.com/upload/20251102082125.jpg
|
- http://kelunpuzhonggong.com/upload/20251102082125.jpg
|
||||||
detail: Textured/Non-textured
|
detail: Textured/Non-textured
|
||||||
spec:
|
spec:
|
||||||
- Nominal thickness: 0.6-6.0
|
- 'Nominal thickness: 0.6-6.0'
|
||||||
- Nominal width: 800-1250
|
- 'Nominal width: 800-1250'
|
||||||
- Inner diameter of steel coil: 610
|
- 'Inner diameter of steel coil: 610'
|
||||||
packaging: Single coil
|
packaging: Single coil
|
||||||
slug: 1
|
slug: 1
|
||||||
---
|
---
|
||||||
@@ -7,9 +7,9 @@ images:
|
|||||||
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
||||||
detail:
|
detail:
|
||||||
spec:
|
spec:
|
||||||
- Nominal thickness: 0.16-3.0
|
- 'Nominal thickness: 0.16-3.0'
|
||||||
- Nominal width: 800-1250
|
- 'Nominal width: 800-1250'
|
||||||
- Inner diameter of steel coil: 610
|
- 'Inner diameter of steel coil: 610'
|
||||||
packaging:
|
packaging:
|
||||||
slug: 2
|
slug: 2
|
||||||
---
|
---
|
||||||
@@ -7,9 +7,9 @@ images:
|
|||||||
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
||||||
detail:
|
detail:
|
||||||
spec:
|
spec:
|
||||||
- Nominal thickness: 0.16-3.0
|
- 'Nominal thickness: 0.16-3.0'
|
||||||
- Nominal width: 800-1250
|
- 'Nominal width: 800-1250'
|
||||||
- Inner diameter of steel coil: 610
|
- 'Inner diameter of steel coil: 610'
|
||||||
packaging:
|
packaging:
|
||||||
slug: 3
|
slug: 3
|
||||||
---
|
---
|
||||||
@@ -7,9 +7,9 @@ images:
|
|||||||
- http://kelunpuzhonggong.com/upload/20251102082125.jpg
|
- http://kelunpuzhonggong.com/upload/20251102082125.jpg
|
||||||
detail: Có kết cấu/Không kết cấu
|
detail: Có kết cấu/Không kết cấu
|
||||||
spec:
|
spec:
|
||||||
- Độ dày danh nghĩa: 0.6-6.0
|
- 'Độ dày danh nghĩa: 0.6-6.0'
|
||||||
- Chiều rộng danh nghĩa: 800-1250
|
- 'Chiều rộng danh nghĩa: 800-1250'
|
||||||
- Đường kính trong cuộn thép: 610
|
- 'Đường kính trong cuộn thép: 610'
|
||||||
packaging: Cuộn đơn
|
packaging: Cuộn đơn
|
||||||
slug: 1
|
slug: 1
|
||||||
---
|
---
|
||||||
@@ -7,9 +7,9 @@ images:
|
|||||||
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
||||||
detail:
|
detail:
|
||||||
spec:
|
spec:
|
||||||
- Độ dày danh nghĩa: 0.16-3.0
|
- 'Độ dày danh nghĩa: 0.16-3.0'
|
||||||
- Chiều rộng danh nghĩa: 800-1250
|
- 'Chiều rộng danh nghĩa: 800-1250'
|
||||||
- Đường kính trong cuộn thép: 610
|
- 'Đường kính trong cuộn thép: 610'
|
||||||
packaging:
|
packaging:
|
||||||
slug: 2
|
slug: 2
|
||||||
---
|
---
|
||||||
@@ -7,9 +7,9 @@ images:
|
|||||||
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
||||||
detail:
|
detail:
|
||||||
spec:
|
spec:
|
||||||
- Độ dày danh nghĩa: 0.16-3.0
|
- 'Độ dày danh nghĩa: 0.16-3.0'
|
||||||
- Chiều rộng danh nghĩa: 800-1250
|
- 'Chiều rộng danh nghĩa: 800-1250'
|
||||||
- Đường kính trong cuộn thép: 610
|
- 'Đường kính trong cuộn thép: 610'
|
||||||
packaging:
|
packaging:
|
||||||
slug: 3
|
slug: 3
|
||||||
---
|
---
|
||||||
@@ -7,9 +7,9 @@ images:
|
|||||||
- http://kelunpuzhonggong.com/upload/20251102082125.jpg
|
- http://kelunpuzhonggong.com/upload/20251102082125.jpg
|
||||||
detail: 毛化/无毛化
|
detail: 毛化/无毛化
|
||||||
spec:
|
spec:
|
||||||
- 公称厚度:0.6-6.0
|
- '公称厚度:0.6-6.0'
|
||||||
- 公称宽度:800-1250
|
- '公称宽度:800-1250'
|
||||||
- 钢卷内径:610
|
- '钢卷内径:610'
|
||||||
packaging: 单卷
|
packaging: 单卷
|
||||||
slug: 1
|
slug: 1
|
||||||
---
|
---
|
||||||
@@ -7,9 +7,9 @@ images:
|
|||||||
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
||||||
detail:
|
detail:
|
||||||
spec:
|
spec:
|
||||||
- 公称厚度:0.16-3.0
|
- '公称厚度:0.16-3.0'
|
||||||
- 公称宽度:800-1250
|
- '公称宽度:800-1250'
|
||||||
- 钢卷内径:610
|
- '钢卷内径:610'
|
||||||
packaging:
|
packaging:
|
||||||
slug: 2
|
slug: 2
|
||||||
---
|
---
|
||||||
@@ -8,8 +8,8 @@ images:
|
|||||||
detail:
|
detail:
|
||||||
spec:
|
spec:
|
||||||
- 公称厚度:0.16-3.0
|
- 公称厚度:0.16-3.0
|
||||||
- 公称宽度:800-1250
|
- '公称宽度:800-1250'
|
||||||
- 钢卷内径:610
|
- '钢卷内径:610'
|
||||||
packaging:
|
packaging:
|
||||||
slug: 3
|
slug: 3
|
||||||
---
|
---
|
||||||
@@ -17,27 +17,27 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"name": "Company Profile",
|
"name": "Company Profile",
|
||||||
"href": "/about?section=company"
|
"href": "/about/company"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Corporate Culture",
|
"name": "Corporate Culture",
|
||||||
"href": "/about?section=culture"
|
"href": "/about/culture"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Production Base",
|
"name": "Production Base",
|
||||||
"href": "/about?section=base"
|
"href": "/about/base"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Organizational Structure",
|
"name": "Organizational Structure",
|
||||||
"href": "/about?section=organization"
|
"href": "/about/organization"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Honors and Qualifications",
|
"name": "Honors and Qualifications",
|
||||||
"href": "/about?section=awards"
|
"href": "/about/awards"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Development History",
|
"name": "Development History",
|
||||||
"href": "/about?section=history"
|
"href": "/about/history"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -75,32 +75,32 @@
|
|||||||
"title": "About Us",
|
"title": "About Us",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "/en/about?section=company",
|
"href": "/en/about/company",
|
||||||
"name": "Company Profile",
|
"name": "Company Profile",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/en/about?section=culture",
|
"href": "/en/about/culture",
|
||||||
"name": "Corporate Culture",
|
"name": "Corporate Culture",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/en/about?section=base",
|
"href": "/en/about/base",
|
||||||
"name": "Production Base",
|
"name": "Production Base",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/en/about?section=organization",
|
"href": "/en/about/organization",
|
||||||
"name": "Organizational Structure",
|
"name": "Organizational Structure",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/en/about?section=awards",
|
"href": "/en/about/awards",
|
||||||
"name": "Honors and Qualifications",
|
"name": "Honors and Qualifications",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/en/about?section=history",
|
"href": "/en/about/history",
|
||||||
"name": "Development History",
|
"name": "Development History",
|
||||||
"useA": true
|
"useA": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,27 +17,27 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"name": "Tổng quan công ty",
|
"name": "Tổng quan công ty",
|
||||||
"href": "/about?section=company"
|
"href": "/about/company"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Văn hóa doanh nghiệp",
|
"name": "Văn hóa doanh nghiệp",
|
||||||
"href": "/about?section=culture"
|
"href": "/about/culture"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Cơ sở sản xuất",
|
"name": "Cơ sở sản xuất",
|
||||||
"href": "/about?section=base"
|
"href": "/about/base"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Cơ cấu tổ chức",
|
"name": "Cơ cấu tổ chức",
|
||||||
"href": "/about?section=organization"
|
"href": "/about/organization"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Danh hiệu và chứng chỉ",
|
"name": "Danh hiệu và chứng chỉ",
|
||||||
"href": "/about?section=awards"
|
"href": "/about/awards"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Lịch sử phát triển",
|
"name": "Lịch sử phát triển",
|
||||||
"href": "/about?section=history"
|
"href": "/about/history"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -75,32 +75,32 @@
|
|||||||
"title": "Về chúng tôi",
|
"title": "Về chúng tôi",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "/vi/about?section=company",
|
"href": "/vi/about/company",
|
||||||
"name": "Tổng quan công ty",
|
"name": "Tổng quan công ty",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/vi/about?section=culture",
|
"href": "/vi/about/culture",
|
||||||
"name": "Văn hóa doanh nghiệp",
|
"name": "Văn hóa doanh nghiệp",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/vi/about?section=base",
|
"href": "/vi/about/base",
|
||||||
"name": "Cơ sở sản xuất",
|
"name": "Cơ sở sản xuất",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/vi/about?section=organization",
|
"href": "/vi/about/organization",
|
||||||
"name": "Cơ cấu tổ chức",
|
"name": "Cơ cấu tổ chức",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/vi/about?section=awards",
|
"href": "/vi/about/awards",
|
||||||
"name": "Danh hiệu và chứng chỉ",
|
"name": "Danh hiệu và chứng chỉ",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/vi/about?section=history",
|
"href": "/vi/about/history",
|
||||||
"name": "Lịch sử phát triển",
|
"name": "Lịch sử phát triển",
|
||||||
"useA": true
|
"useA": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,27 +17,27 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"name": "公司简介",
|
"name": "公司简介",
|
||||||
"href": "/about?section=company"
|
"href": "/about/company"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "企业文化",
|
"name": "企业文化",
|
||||||
"href": "/about?section=culture"
|
"href": "/about/culture"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "生产基地",
|
"name": "生产基地",
|
||||||
"href": "/about?section=base"
|
"href": "/about/base"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "组织架构",
|
"name": "组织架构",
|
||||||
"href": "/about?section=organization"
|
"href": "/about/organization"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "荣誉资质",
|
"name": "荣誉资质",
|
||||||
"href": "/about?section=awards"
|
"href": "/about/awards"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "发展历程",
|
"name": "发展历程",
|
||||||
"href": "/about?section=history"
|
"href": "/about/history"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -75,32 +75,32 @@
|
|||||||
"title": "关于我们",
|
"title": "关于我们",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "/zh/about?section=company",
|
"href": "/zh/about/company",
|
||||||
"name": "公司简介",
|
"name": "公司简介",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/zh/about?section=culture",
|
"href": "/zh/about/culture",
|
||||||
"name": "企业文化",
|
"name": "企业文化",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/zh/about?section=base",
|
"href": "/zh/about/base",
|
||||||
"name": "生产基地",
|
"name": "生产基地",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/zh/about?section=organization",
|
"href": "/zh/about/organization",
|
||||||
"name": "组织架构",
|
"name": "组织架构",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/zh/about?section=awards",
|
"href": "/zh/about/awards",
|
||||||
"name": "荣誉资质",
|
"name": "荣誉资质",
|
||||||
"useA": true
|
"useA": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": "/zh/about?section=history",
|
"href": "/zh/about/history",
|
||||||
"name": "发展历程",
|
"name": "发展历程",
|
||||||
"useA": true
|
"useA": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ const withNextIntl = createNextIntlPlugin();
|
|||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
output: "standalone",
|
output: "export",
|
||||||
|
// output: "standalone",
|
||||||
images: {
|
images: {
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
...(process.env.R2_PUBLIC_URL
|
...(process.env.R2_PUBLIC_URL
|
||||||
|
|||||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -45,6 +45,7 @@
|
|||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
|
"baseline-browser-mapping": "^2.9.5",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "16.0.0",
|
"eslint-config-next": "16.0.0",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
@@ -2666,9 +2667,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/baseline-browser-mapping": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.8.30",
|
"version": "2.9.5",
|
||||||
"resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.30.tgz",
|
"resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz",
|
||||||
"integrity": "sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==",
|
"integrity": "sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
|
"baseline-browser-mapping": "^2.9.5",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "16.0.0",
|
"eslint-config-next": "16.0.0",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
@@ -58,4 +59,4 @@
|
|||||||
"volta": {
|
"volta": {
|
||||||
"node": "20.19.5"
|
"node": "20.19.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user