Files
fad-trade-next/app/[locale]/about/page.tsx

156 lines
3.8 KiB
TypeScript
Raw Normal View History

2025-11-21 13:36:06 +08:00
import MDXComponents from "@/components/mdx/MDXComponents";
import TableOfContents from "@/components/mdx/TableOfContents.client";
2025-11-21 13:36:06 +08:00
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";
const options = {
parseFrontmatter: true,
mdxOptions: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
},
};
interface TableOfContentsItem {
id: string;
text: string;
level: number;
}
// 解析MDX内容并提取标题
async function parseMDXContent(content: string): Promise<TableOfContentsItem[]> {
if (!content) {
return [];
}
try {
const headingRegex = /^#{2,4}\s+(.+)$/gm;
const headings: TableOfContentsItem[] = [];
let match;
while ((match = headingRegex.exec(content)) !== null) {
const fullMatch = match[0];
const text = match[1]?.trim();
if (!text) continue;
// 确定标题级别
let level = 2;
if (fullMatch.startsWith("###")) {
level = fullMatch.startsWith("####") ? 4 : 3;
}
// 生成ID将文本转换为URL友好的格式
const id = text
.toLowerCase()
.replace(/[^a-z0-9\u4e00-\u9fa5\s-]/g, "")
.replace(/\s+/g, "-");
headings.push({ id, text, level });
}
return headings;
} catch (error) {
console.error("Error parsing MDX content for TOC:", error);
return [];
}
}
async function getMDXContent(locale: string, section: string): Promise<string> {
2025-11-21 13:36:06 +08:00
const filePath = path.join(
process.cwd(),
"content",
"about",
section,
2025-11-21 13:36:06 +08:00
`${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,
}: 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 }>;
}) {
2025-11-21 13:36:06 +08:00
const { locale } = await params;
const resolvedSearchParams = await searchParams;
const section = (resolvedSearchParams.section as string) || "company";
const content = await getMDXContent(locale, section);
const tocItems = await parseMDXContent(content);
// 获取多语言目录标题
const t = await getTranslations({ locale, namespace: "Common" });
const tocTitle = t("tableOfContents") || "目录";
2025-11-21 13:36:06 +08:00
return (
<div className="flex flex-col md:flex-row w-full gap-8">
{/* 侧边目录 - 在移动端显示在内容上方 */}
<div className="w-full md:w-1/4 lg:w-1/5 order-2 md:order-1">
<TableOfContents
items={tocItems || []}
title={tocTitle}
/>
</div>
{/* 主要内容 */}
<article className="w-full md:w-3/4 lg:w-4/5 px-2 md:px-8 ml-0 md:ml-64 order-1 md:order-2">
{content ? (
<MDXRemote
source={content}
components={MDXComponents}
options={options}
/>
) : (
<div className="text-center py-8 text-gray-500">
<p>...</p>
</div>
)}
</article>
</div>
2025-11-21 13:36:06 +08:00
);
}
export async function generateStaticParams() {
return LOCALES.map((locale) => ({
locale,
}));
}