From 216076ff2a17113698929012b85c80f6e890147a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A0=82=E7=B3=96?= Date: Fri, 21 Nov 2025 17:49:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E5=AF=BC=E8=88=AA?= =?UTF-8?q?=E5=92=8C=E5=8D=9A=E5=AE=A2=E7=B3=BB=E7=BB=9F=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=A4=9A=E7=BA=A7=E8=8F=9C=E5=8D=95=E5=92=8CAPI?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构导航菜单支持多级子菜单结构 - 博客系统改为从API获取数据,移除本地文件存储 - 删除旧的关于页面,准备重构 - 修复博客详情页slug匹配问题 - 默认首页重定向到中文版本 --- app/[locale]/about/base/page.tsx | 5 - app/[locale]/about/course/page.tsx | 5 - app/[locale]/about/culture/page.tsx | 5 - app/[locale]/about/green/page.tsx | 5 - app/[locale]/about/honor/page.tsx | 5 - app/[locale]/about/introduction/page.tsx | 5 - app/[locale]/blog/[slug]/page.tsx | 5 +- app/[locale]/blog/page.tsx | 61 ++++------- app/page.tsx | 6 ++ components/header/HeaderLinks.tsx | 123 +++++------------------ i18n/messages/zh.json | 56 +++++++++-- lib/getBlogs.ts | 118 +++++++++++++--------- types/common.ts | 1 + 13 files changed, 178 insertions(+), 222 deletions(-) delete mode 100644 app/[locale]/about/base/page.tsx delete mode 100644 app/[locale]/about/course/page.tsx delete mode 100644 app/[locale]/about/culture/page.tsx delete mode 100644 app/[locale]/about/green/page.tsx delete mode 100644 app/[locale]/about/honor/page.tsx delete mode 100644 app/[locale]/about/introduction/page.tsx create mode 100644 app/page.tsx diff --git a/app/[locale]/about/base/page.tsx b/app/[locale]/about/base/page.tsx deleted file mode 100644 index 19a9c39..0000000 --- a/app/[locale]/about/base/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export default function Page() { - return ( -
基础介绍
- ); -} \ No newline at end of file diff --git a/app/[locale]/about/course/page.tsx b/app/[locale]/about/course/page.tsx deleted file mode 100644 index 6ff4440..0000000 --- a/app/[locale]/about/course/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export default function Page() { - return ( -
发展历程
- ); -} \ No newline at end of file diff --git a/app/[locale]/about/culture/page.tsx b/app/[locale]/about/culture/page.tsx deleted file mode 100644 index 9217f56..0000000 --- a/app/[locale]/about/culture/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export default function Page() { - return ( -
企业文化
- ); -} \ No newline at end of file diff --git a/app/[locale]/about/green/page.tsx b/app/[locale]/about/green/page.tsx deleted file mode 100644 index eeaa1e2..0000000 --- a/app/[locale]/about/green/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export default function Page() { - return ( -
绿色发展
- ); -} \ No newline at end of file diff --git a/app/[locale]/about/honor/page.tsx b/app/[locale]/about/honor/page.tsx deleted file mode 100644 index b0dc8ef..0000000 --- a/app/[locale]/about/honor/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export default function Page() { - return ( -
荣誉资质
- ); -} \ No newline at end of file diff --git a/app/[locale]/about/introduction/page.tsx b/app/[locale]/about/introduction/page.tsx deleted file mode 100644 index c1e9a6c..0000000 --- a/app/[locale]/about/introduction/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export default function Page() { - return ( -
公司介绍
- ); -} \ No newline at end of file diff --git a/app/[locale]/blog/[slug]/page.tsx b/app/[locale]/blog/[slug]/page.tsx index bbbb47f..ce36de0 100644 --- a/app/[locale]/blog/[slug]/page.tsx +++ b/app/[locale]/blog/[slug]/page.tsx @@ -22,8 +22,9 @@ export async function generateMetadata({ }: MetadataProps): Promise { const { locale, slug } = await params; let { posts }: { posts: BlogPost[] } = await getPosts(locale); - const post = posts.find((post) => post.slug === "/" + slug); + const post = posts.find((post) => post.slug === slug); + console.log(post, posts); if (!post) { return constructMetadata({ title: "404", @@ -50,7 +51,7 @@ export default async function BlogPage({ params }: { params: Params }) { const { locale, slug } = await params; let { posts }: { posts: BlogPost[] } = await getPosts(locale); - const post = posts.find((item) => item.slug === "/" + slug); + const post = posts.find((item) => item.slug === slug); if (!post) { return notFound(); diff --git a/app/[locale]/blog/page.tsx b/app/[locale]/blog/page.tsx index 2fcf49d..bdead21 100644 --- a/app/[locale]/blog/page.tsx +++ b/app/[locale]/blog/page.tsx @@ -9,7 +9,7 @@ import { getTranslations } from "next-intl/server"; type Params = Promise<{ locale: string }>; -type SearchParams = { page?: string; type?: string }; +type SearchParams = { page?: string; category?: string }; type MetadataProps = { params: Params; @@ -36,34 +36,18 @@ export default async function Page({ searchParams, }: { params: Params; - searchParams?: SearchParams; + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; }) { const { locale } = await params; - let { posts } = await getPosts(locale); + const resolvedSearchParams = await searchParams; + const category = resolvedSearchParams.category as string || ""; + + let { posts } = await getPosts(locale, category); const t = await getTranslations("Blog"); - // 分类筛选(公司新闻/专家访谈/行业动态/公告) - const type = (searchParams?.type || "").toLowerCase(); - const typeLabelMap: Record = { - company: "公司新闻", - experts: "专家访谈", - industry: "行业动态", - notice: "公告", - }; - const typeLabel = typeLabelMap[type]; - if (type) { - posts = posts.filter((p) => { - const metaType = String((p.metadata as any)?.type || "").toLowerCase(); - const tags: string[] = (p.tags as any) || []; - const hitMeta = metaType === type; - const hitTag = Array.isArray(tags) && tags.some((t) => String(t).toLowerCase() === type); - return hitMeta || hitTag; - }); - } - - const pageRaw = searchParams?.page; - const page = Math.max(1, parseInt(pageRaw || "1", 10)); + const pageRaw = resolvedSearchParams.page as string || "1"; + const page = Math.max(1, parseInt(pageRaw, 10)); const pageSize = 10; const totalPages = Math.max(1, Math.ceil(posts.length / pageSize)); const start = (page - 1) * pageSize; @@ -77,14 +61,14 @@ export default async function Page({ {/* 顶部操作区:标签 + 面包屑 */}
-
+ {/*
-
+
*/}
首页 @@ -93,12 +77,12 @@ export default async function Page({ 新闻中心 - {typeLabel && ( + {/* {typeLabel && ( <> / {typeLabel} - )} + )} */}
@@ -111,7 +95,7 @@ export default async function Page({ return ( @@ -139,9 +123,8 @@ export default async function Page({ {"<"} @@ -167,11 +150,10 @@ export default async function Page({ key={it} href={`/blog?page=${it}`} prefetch={false} - className={`px-2.5 py-1 border rounded-sm text-sm ${ - isActive - ? "bg-red-600 text-white border-red-600" - : "bg-white hover:bg-gray-50" - }`} + className={`px-2.5 py-1 border rounded-sm text-sm ${isActive + ? "bg-red-600 text-white border-red-600" + : "bg-white hover:bg-gray-50" + }`} > {it} @@ -181,9 +163,8 @@ export default async function Page({ {">"} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..3892dcd --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,6 @@ +// 默认跳转到中文版本 +import { redirect } from 'next/navigation'; + +export default function HomePage() { + redirect('/zh'); +} \ No newline at end of file diff --git a/components/header/HeaderLinks.tsx b/components/header/HeaderLinks.tsx index 5c86438..6515aae 100644 --- a/components/header/HeaderLinks.tsx +++ b/components/header/HeaderLinks.tsx @@ -3,7 +3,6 @@ import { Link as I18nLink, usePathname, useRouter } from "@/i18n/routing"; import { cn } from "@/lib/utils"; import { HeaderLink } from "@/types/common"; -import { ExternalLink } from "lucide-react"; import { useTranslations } from "next-intl"; const HeaderLinks = () => { @@ -17,102 +16,34 @@ const HeaderLinks = () => { return (
{headerLinks.map((link) => { - const isAbout = link.href === "/about"; - const isNews = link.href === "/blog"; - if (isAbout) { - return ( -
- - {link.name} - - {/* 移除触发器与菜单间的间隙,使用 pt-2 保持视觉间距但不中断 hover 区域 */} -
-
- 友发愿景 - 产品体系 - 应用场景 - 联系方式 -
-
-
- ); - } - if (isNews) { - return ( -
- - {link.name} - -
-
- {[ - { key: "company", label: "公司新闻" }, - { key: "experts", label: "专家访谈" }, - { key: "industry", label: "行业动态" }, - { key: "notice", label: "公告" }, - ].map((it) => ( - - ))} -
-
-
- ); - } + return ( - - {link.name} - {link.target && link.target === "_blank" && ( - - - - )} - +
+ + {link.name} + + { + link?.children && ( +
+
+ {link.children.map((child) => ( + + {child.name} + + ))} +
+
+ ) + } +
); })}
diff --git a/i18n/messages/zh.json b/i18n/messages/zh.json index 28198eb..276752f 100644 --- a/i18n/messages/zh.json +++ b/i18n/messages/zh.json @@ -13,7 +13,33 @@ }, { "name": "走进福安德", - "href": "/about" + "href": "/about", + "children": [ + { + "name": "公司简介", + "href": "/about?section=company" + }, + { + "name": "企业文化", + "href": "/about?section=culture" + }, + { + "name": "生产基地", + "href": "/about?section=base" + }, + { + "name": "组织架构", + "href": "/about?section=organization" + }, + { + "name": "荣誉资质", + "href": "/about?section=awards" + }, + { + "name": "发展历程", + "href": "/about?section=history" + } + ] }, { "name": "产品中心", @@ -21,7 +47,21 @@ }, { "name": "新闻中心", - "href": "/blog" + "href": "/blog", + "children": [ + { + "name": "公告", + "href": "/blog?category=announce" + }, + { + "name": "新闻", + "href": "/blog?category=news" + }, + { + "name": "活动", + "href": "/blog?category=event" + } + ] } ] }, @@ -35,32 +75,32 @@ "title": "关于我们", "links": [ { - "href": "/about?section=company", + "href": "/zh/about?section=company", "name": "公司简介", "useA": true }, { - "href": "/about?section=culture", + "href": "/zh/about?section=culture", "name": "企业文化", "useA": true }, { - "href": "/about?section=base", + "href": "/zh/about?section=base", "name": "生产基地", "useA": true }, { - "href": "/about?section=organization", + "href": "/zh/about?section=organization", "name": "组织架构", "useA": true }, { - "href": "/about?section=awards", + "href": "/zh/about?section=awards", "name": "荣誉资质", "useA": true }, { - "href": "/about?section=history", + "href": "/zh/about?section=history", "name": "发展历程", "useA": true } diff --git a/lib/getBlogs.ts b/lib/getBlogs.ts index 357c4c8..f4f9353 100644 --- a/lib/getBlogs.ts +++ b/lib/getBlogs.ts @@ -1,66 +1,92 @@ import { DEFAULT_LOCALE } from '@/i18n/routing'; import { BlogPost } from '@/types/blog'; -import fs from 'fs'; -import matter from 'gray-matter'; -import path from 'path'; -const POSTS_BATCH_SIZE = 10; - -export async function getPosts(locale: string = DEFAULT_LOCALE): Promise<{ posts: BlogPost[] }> { - const postsDirectory = path.join(process.cwd(), 'blogs', locale); - - // is directory exist - if (!fs.existsSync(postsDirectory)) { - return { posts: [] }; +export async function getPosts(locale: string = DEFAULT_LOCALE, category?: string): Promise<{ posts: BlogPost[], total: number }> { + const Id_Map: Record = { + 'announce': '1989174108560080897', + 'news': '1988814504336605185', + 'event': '1989174018231549954', } - let filenames = await fs.promises.readdir(postsDirectory); - filenames = filenames.reverse(); + let url = 'http://49.232.154.205:18081/export/article/list?pageNum=1&pageSize=10&langCode=' + locale; + if (category) { + url += '&categoryId=' + Id_Map[category || 'announce'] + } - let allPosts: BlogPost[] = []; + console.log(url); + + const response = await fetch(url); + const data = await response.json(); + const posts = data.rows.map((item: any) => { + return { + locale, // use locale parameter + title: item.title, + description: item.summary, + image: item.cover || '', + slug: item.articleId, + tags: '', + date: item.publishedTime, + // visible: data.visible || 'published', + pin: false, + content: item.content, + metadata: data, + } + + }) || []; + + // // is directory exist + // if (!fs.existsSync(postsDirectory)) { + // return { posts: [] }; + // } + + // let filenames = await fs.promises.readdir(postsDirectory); + // filenames = filenames.reverse(); + + // let allPosts: BlogPost[] = []; // read file by batch - for (let i = 0; i < filenames.length; i += POSTS_BATCH_SIZE) { - const batchFilenames = filenames.slice(i, i + POSTS_BATCH_SIZE); + // for (let i = 0; i < filenames.length; i += POSTS_BATCH_SIZE) { + // const batchFilenames = filenames.slice(i, i + POSTS_BATCH_SIZE); - const batchPosts: BlogPost[] = await Promise.all( - batchFilenames.map(async (filename) => { - const fullPath = path.join(postsDirectory, filename); - const fileContents = await fs.promises.readFile(fullPath, 'utf8'); + // const batchPosts: BlogPost[] = await Promise.all( + // batchFilenames.map(async (filename) => { + // const fullPath = path.join(postsDirectory, filename); + // const fileContents = await fs.promises.readFile(fullPath, 'utf8'); - const { data, content } = matter(fileContents); + // const { data, content } = matter(fileContents); - return { - locale, // use locale parameter - title: data.title, - description: data.description, - image: data.image || '', - slug: data.slug, - tags: data.tags, - date: data.date, - visible: data.visible || 'published', - pin: data.pin || false, - content, - metadata: data, - }; - }) - ); + // return { + // locale, // use locale parameter + // title: data.title, + // description: data.description, + // image: data.image || '', + // slug: data.slug, + // tags: data.tags, + // date: data.date, + // visible: data.visible || 'published', + // pin: data.pin || false, + // content, + // metadata: data, + // }; + // }) + // ); - allPosts.push(...batchPosts); - } + // allPosts.push(...batchPosts); + // } // filter out non-published articles - allPosts = allPosts.filter(post => post.visible === 'published'); + // allPosts = allPosts.filter(post => post.visible === 'published'); // sort posts by pin and date - allPosts = allPosts.sort((a, b) => { - if (a.pin !== b.pin) { - return (b.pin ? 1 : 0) - (a.pin ? 1 : 0); - } - return new Date(b.date).getTime() - new Date(a.date).getTime(); - }); + // allPosts = allPosts.sort((a, b) => { + // if (a.pin !== b.pin) { + // return (b.pin ? 1 : 0) - (a.pin ? 1 : 0); + // } + // return new Date(b.date).getTime() - new Date(a.date).getTime(); + // }); return { - posts: allPosts, + posts, + total: data.total, }; } \ No newline at end of file diff --git a/types/common.ts b/types/common.ts index b0790c7..4414075 100644 --- a/types/common.ts +++ b/types/common.ts @@ -4,6 +4,7 @@ export interface HeaderLink { href: string; target?: string; rel?: string; + children?: HeaderLink[]; }; export interface FooterLink {