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 { 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";
|
||||
import { LOCALES } from "@/i18n/routing";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
const options = {
|
||||
parseFrontmatter: true,
|
||||
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({
|
||||
// 重定向到 /about/company
|
||||
export default async function Page({
|
||||
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;
|
||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
const resolvedSearchParams = await searchParams;
|
||||
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>
|
||||
);
|
||||
return redirect(`/${locale}/about/company`);
|
||||
}
|
||||
|
||||
// 关键:生成locale + section的所有静态组合(覆盖vi/organization等)
|
||||
export async function generateStaticParams() {
|
||||
return LOCALES.map((locale) => ({
|
||||
locale,
|
||||
}));
|
||||
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,
|
||||
}))
|
||||
);
|
||||
}
|
||||
@@ -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({
|
||||
params,
|
||||
|
||||
@@ -9,8 +9,6 @@ import { getTranslations } from "next-intl/server";
|
||||
|
||||
type Params = Promise<{ locale: string }>;
|
||||
|
||||
type SearchParams = { page?: string; category?: string };
|
||||
|
||||
type MetadataProps = {
|
||||
params: Params;
|
||||
};
|
||||
@@ -33,21 +31,21 @@ export async function generateMetadata({
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
searchParams,
|
||||
// searchParams,
|
||||
}: {
|
||||
params: Params;
|
||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
const resolvedSearchParams = await searchParams;
|
||||
const category = resolvedSearchParams.category as string || "";
|
||||
// const resolvedSearchParams = await searchParams;
|
||||
// const category = resolvedSearchParams.category as string || "";
|
||||
|
||||
let { posts } = await getPosts(locale);
|
||||
|
||||
const t = await getTranslations("Blog");
|
||||
|
||||
const pageRaw = resolvedSearchParams.page as string || "1";
|
||||
const page = Math.max(1, parseInt(pageRaw, 10));
|
||||
// const pageRaw = resolvedSearchParams.page as string || "1";
|
||||
// const page = Math.max(1, parseInt(10, 10));
|
||||
const page = 1;
|
||||
const pageSize = 10;
|
||||
const totalPages = Math.max(1, Math.ceil(posts.length / pageSize));
|
||||
const start = (page - 1) * pageSize;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import HomeComponent from "@/components/home";
|
||||
|
||||
// export const dynamic = "force-static";
|
||||
export const dynamic = "force-static";
|
||||
|
||||
export default function Home() {
|
||||
return <HomeComponent />;
|
||||
}
|
||||
|
||||
// export async function generateStaticParams() {
|
||||
// return [
|
||||
// { locale: 'en' },
|
||||
// { locale: 'zh' },
|
||||
// { locale: 'vi' },
|
||||
// // { locale: 'ja' },
|
||||
// ]
|
||||
// }
|
||||
export async function generateStaticParams() {
|
||||
return [
|
||||
{ locale: 'en' },
|
||||
{ locale: 'zh' },
|
||||
{ locale: 'vi' },
|
||||
// { locale: 'ja' },
|
||||
]
|
||||
}
|
||||
|
||||
@@ -169,12 +169,12 @@ export async function generateStaticParams() {
|
||||
try {
|
||||
const defaultLocale = LOCALES[0];
|
||||
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) =>
|
||||
validProducts.map((product) => ({
|
||||
locale,
|
||||
slug: product.slug,
|
||||
slug: product.slug.toString(),
|
||||
}))
|
||||
);
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { MetadataRoute } from 'next'
|
||||
|
||||
const siteUrl = siteConfig.url
|
||||
|
||||
// export const dynamic = "force-static"
|
||||
export const dynamic = "force-static"
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
|
||||
@@ -7,7 +7,7 @@ const siteUrl = siteConfig.url
|
||||
|
||||
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> {
|
||||
// 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'}`}
|
||||
onClick={() => setActiveTab('packaging')}
|
||||
>
|
||||
{t('productPackaging')}
|
||||
{t('productPacking')}
|
||||
</button>
|
||||
</div>
|
||||
{/* 内容区 */}
|
||||
|
||||
@@ -4,7 +4,6 @@ import LocaleSwitcher from "@/components/LocaleSwitcher";
|
||||
import { siteConfig } from "@/config/site";
|
||||
import { Link as I18nLink } from "@/i18n/routing";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Image from "next/image";
|
||||
|
||||
const Header = () => {
|
||||
const t = useTranslations("Home");
|
||||
@@ -18,7 +17,7 @@ const Header = () => {
|
||||
prefetch={false}
|
||||
className="flex items-center space-x-1 font-bold"
|
||||
>
|
||||
<Image
|
||||
<img
|
||||
alt={siteConfig.name}
|
||||
src="/logo.png"
|
||||
className="w-6 h-6"
|
||||
|
||||
@@ -7,9 +7,9 @@ images:
|
||||
- http://kelunpuzhonggong.com/upload/20251102082125.jpg
|
||||
detail: Textured/Non-textured
|
||||
spec:
|
||||
- Nominal thickness: 0.6-6.0
|
||||
- Nominal width: 800-1250
|
||||
- Inner diameter of steel coil: 610
|
||||
- 'Nominal thickness: 0.6-6.0'
|
||||
- 'Nominal width: 800-1250'
|
||||
- 'Inner diameter of steel coil: 610'
|
||||
packaging: Single coil
|
||||
slug: 1
|
||||
---
|
||||
@@ -7,9 +7,9 @@ images:
|
||||
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
||||
detail:
|
||||
spec:
|
||||
- Nominal thickness: 0.16-3.0
|
||||
- Nominal width: 800-1250
|
||||
- Inner diameter of steel coil: 610
|
||||
- 'Nominal thickness: 0.16-3.0'
|
||||
- 'Nominal width: 800-1250'
|
||||
- 'Inner diameter of steel coil: 610'
|
||||
packaging:
|
||||
slug: 2
|
||||
---
|
||||
@@ -7,9 +7,9 @@ images:
|
||||
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
||||
detail:
|
||||
spec:
|
||||
- Nominal thickness: 0.16-3.0
|
||||
- Nominal width: 800-1250
|
||||
- Inner diameter of steel coil: 610
|
||||
- 'Nominal thickness: 0.16-3.0'
|
||||
- 'Nominal width: 800-1250'
|
||||
- 'Inner diameter of steel coil: 610'
|
||||
packaging:
|
||||
slug: 3
|
||||
---
|
||||
@@ -7,9 +7,9 @@ images:
|
||||
- http://kelunpuzhonggong.com/upload/20251102082125.jpg
|
||||
detail: Có kết cấu/Không kết cấu
|
||||
spec:
|
||||
- Độ dày danh nghĩa: 0.6-6.0
|
||||
- Chiều rộng danh nghĩa: 800-1250
|
||||
- Đường kính trong cuộn thép: 610
|
||||
- 'Độ dày danh nghĩa: 0.6-6.0'
|
||||
- 'Chiều rộng danh nghĩa: 800-1250'
|
||||
- 'Đường kính trong cuộn thép: 610'
|
||||
packaging: Cuộn đơn
|
||||
slug: 1
|
||||
---
|
||||
@@ -7,9 +7,9 @@ images:
|
||||
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
||||
detail:
|
||||
spec:
|
||||
- Độ dày danh nghĩa: 0.16-3.0
|
||||
- Chiều rộng danh nghĩa: 800-1250
|
||||
- Đường kính trong cuộn thép: 610
|
||||
- 'Độ dày danh nghĩa: 0.16-3.0'
|
||||
- 'Chiều rộng danh nghĩa: 800-1250'
|
||||
- 'Đường kính trong cuộn thép: 610'
|
||||
packaging:
|
||||
slug: 2
|
||||
---
|
||||
@@ -7,9 +7,9 @@ images:
|
||||
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
||||
detail:
|
||||
spec:
|
||||
- Độ dày danh nghĩa: 0.16-3.0
|
||||
- Chiều rộng danh nghĩa: 800-1250
|
||||
- Đường kính trong cuộn thép: 610
|
||||
- 'Độ dày danh nghĩa: 0.16-3.0'
|
||||
- 'Chiều rộng danh nghĩa: 800-1250'
|
||||
- 'Đường kính trong cuộn thép: 610'
|
||||
packaging:
|
||||
slug: 3
|
||||
---
|
||||
@@ -7,9 +7,9 @@ images:
|
||||
- http://kelunpuzhonggong.com/upload/20251102082125.jpg
|
||||
detail: 毛化/无毛化
|
||||
spec:
|
||||
- 公称厚度:0.6-6.0
|
||||
- 公称宽度:800-1250
|
||||
- 钢卷内径:610
|
||||
- '公称厚度:0.6-6.0'
|
||||
- '公称宽度:800-1250'
|
||||
- '钢卷内径:610'
|
||||
packaging: 单卷
|
||||
slug: 1
|
||||
---
|
||||
@@ -7,9 +7,9 @@ images:
|
||||
- http://kelunpuzhonggong.com/upload/20251021161717.jpg
|
||||
detail:
|
||||
spec:
|
||||
- 公称厚度:0.16-3.0
|
||||
- 公称宽度:800-1250
|
||||
- 钢卷内径:610
|
||||
- '公称厚度:0.16-3.0'
|
||||
- '公称宽度:800-1250'
|
||||
- '钢卷内径:610'
|
||||
packaging:
|
||||
slug: 2
|
||||
---
|
||||
@@ -8,8 +8,8 @@ images:
|
||||
detail:
|
||||
spec:
|
||||
- 公称厚度:0.16-3.0
|
||||
- 公称宽度:800-1250
|
||||
- 钢卷内径:610
|
||||
- '公称宽度:800-1250'
|
||||
- '钢卷内径:610'
|
||||
packaging:
|
||||
slug: 3
|
||||
---
|
||||
@@ -17,27 +17,27 @@
|
||||
"children": [
|
||||
{
|
||||
"name": "Company Profile",
|
||||
"href": "/about?section=company"
|
||||
"href": "/about/company"
|
||||
},
|
||||
{
|
||||
"name": "Corporate Culture",
|
||||
"href": "/about?section=culture"
|
||||
"href": "/about/culture"
|
||||
},
|
||||
{
|
||||
"name": "Production Base",
|
||||
"href": "/about?section=base"
|
||||
"href": "/about/base"
|
||||
},
|
||||
{
|
||||
"name": "Organizational Structure",
|
||||
"href": "/about?section=organization"
|
||||
"href": "/about/organization"
|
||||
},
|
||||
{
|
||||
"name": "Honors and Qualifications",
|
||||
"href": "/about?section=awards"
|
||||
"href": "/about/awards"
|
||||
},
|
||||
{
|
||||
"name": "Development History",
|
||||
"href": "/about?section=history"
|
||||
"href": "/about/history"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -75,32 +75,32 @@
|
||||
"title": "About Us",
|
||||
"links": [
|
||||
{
|
||||
"href": "/en/about?section=company",
|
||||
"href": "/en/about/company",
|
||||
"name": "Company Profile",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/en/about?section=culture",
|
||||
"href": "/en/about/culture",
|
||||
"name": "Corporate Culture",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/en/about?section=base",
|
||||
"href": "/en/about/base",
|
||||
"name": "Production Base",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/en/about?section=organization",
|
||||
"href": "/en/about/organization",
|
||||
"name": "Organizational Structure",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/en/about?section=awards",
|
||||
"href": "/en/about/awards",
|
||||
"name": "Honors and Qualifications",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/en/about?section=history",
|
||||
"href": "/en/about/history",
|
||||
"name": "Development History",
|
||||
"useA": true
|
||||
}
|
||||
|
||||
@@ -17,27 +17,27 @@
|
||||
"children": [
|
||||
{
|
||||
"name": "Tổng quan công ty",
|
||||
"href": "/about?section=company"
|
||||
"href": "/about/company"
|
||||
},
|
||||
{
|
||||
"name": "Văn hóa doanh nghiệp",
|
||||
"href": "/about?section=culture"
|
||||
"href": "/about/culture"
|
||||
},
|
||||
{
|
||||
"name": "Cơ sở sản xuất",
|
||||
"href": "/about?section=base"
|
||||
"href": "/about/base"
|
||||
},
|
||||
{
|
||||
"name": "Cơ cấu tổ chức",
|
||||
"href": "/about?section=organization"
|
||||
"href": "/about/organization"
|
||||
},
|
||||
{
|
||||
"name": "Danh hiệu và chứng chỉ",
|
||||
"href": "/about?section=awards"
|
||||
"href": "/about/awards"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"links": [
|
||||
{
|
||||
"href": "/vi/about?section=company",
|
||||
"href": "/vi/about/company",
|
||||
"name": "Tổng quan công ty",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/vi/about?section=culture",
|
||||
"href": "/vi/about/culture",
|
||||
"name": "Văn hóa doanh nghiệp",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/vi/about?section=base",
|
||||
"href": "/vi/about/base",
|
||||
"name": "Cơ sở sản xuất",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/vi/about?section=organization",
|
||||
"href": "/vi/about/organization",
|
||||
"name": "Cơ cấu tổ chức",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/vi/about?section=awards",
|
||||
"href": "/vi/about/awards",
|
||||
"name": "Danh hiệu và chứng chỉ",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/vi/about?section=history",
|
||||
"href": "/vi/about/history",
|
||||
"name": "Lịch sử phát triển",
|
||||
"useA": true
|
||||
}
|
||||
|
||||
@@ -17,27 +17,27 @@
|
||||
"children": [
|
||||
{
|
||||
"name": "公司简介",
|
||||
"href": "/about?section=company"
|
||||
"href": "/about/company"
|
||||
},
|
||||
{
|
||||
"name": "企业文化",
|
||||
"href": "/about?section=culture"
|
||||
"href": "/about/culture"
|
||||
},
|
||||
{
|
||||
"name": "生产基地",
|
||||
"href": "/about?section=base"
|
||||
"href": "/about/base"
|
||||
},
|
||||
{
|
||||
"name": "组织架构",
|
||||
"href": "/about?section=organization"
|
||||
"href": "/about/organization"
|
||||
},
|
||||
{
|
||||
"name": "荣誉资质",
|
||||
"href": "/about?section=awards"
|
||||
"href": "/about/awards"
|
||||
},
|
||||
{
|
||||
"name": "发展历程",
|
||||
"href": "/about?section=history"
|
||||
"href": "/about/history"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -75,32 +75,32 @@
|
||||
"title": "关于我们",
|
||||
"links": [
|
||||
{
|
||||
"href": "/zh/about?section=company",
|
||||
"href": "/zh/about/company",
|
||||
"name": "公司简介",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/zh/about?section=culture",
|
||||
"href": "/zh/about/culture",
|
||||
"name": "企业文化",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/zh/about?section=base",
|
||||
"href": "/zh/about/base",
|
||||
"name": "生产基地",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/zh/about?section=organization",
|
||||
"href": "/zh/about/organization",
|
||||
"name": "组织架构",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/zh/about?section=awards",
|
||||
"href": "/zh/about/awards",
|
||||
"name": "荣誉资质",
|
||||
"useA": true
|
||||
},
|
||||
{
|
||||
"href": "/zh/about?section=history",
|
||||
"href": "/zh/about/history",
|
||||
"name": "发展历程",
|
||||
"useA": true
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ const withNextIntl = createNextIntlPlugin();
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: "standalone",
|
||||
output: "export",
|
||||
// output: "standalone",
|
||||
images: {
|
||||
remotePatterns: [
|
||||
...(process.env.R2_PUBLIC_URL
|
||||
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -45,6 +45,7 @@
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"baseline-browser-mapping": "^2.9.5",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.0.0",
|
||||
"postcss": "^8.4.38",
|
||||
@@ -2666,9 +2667,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.8.30",
|
||||
"resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.30.tgz",
|
||||
"integrity": "sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==",
|
||||
"version": "2.9.5",
|
||||
"resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz",
|
||||
"integrity": "sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"baseline-browser-mapping": "^2.9.5",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.0.0",
|
||||
"postcss": "^8.4.38",
|
||||
@@ -58,4 +59,4 @@
|
||||
"volta": {
|
||||
"node": "20.19.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user