From 64ad7a9cce84925998b482353833b4407bf5bad0 Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Fri, 21 Nov 2025 16:10:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(about):=20=E6=B7=BB=E5=8A=A0=E5=85=B3?= =?UTF-8?q?=E4=BA=8E=E9=A1=B5=E9=9D=A2=E7=9A=84=E5=A4=9A=E4=B8=AA=E5=AD=90?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/[locale]/about/arch/page.tsx | 37 +++++++ 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/page.tsx | 38 +++++-- components/header/HeaderLinks.tsx | 124 +++++++++++++++++++---- components/header/SectionDropdown.tsx | 41 ++++++++ i18n/messages/zh.json | 4 +- next-env.d.ts | 2 +- stores/sectionStore.ts | 15 +++ 13 files changed, 260 insertions(+), 31 deletions(-) create mode 100644 components/header/SectionDropdown.tsx create mode 100644 stores/sectionStore.ts diff --git a/app/[locale]/about/arch/page.tsx b/app/[locale]/about/arch/page.tsx index e69de29..53bc7fb 100644 --- a/app/[locale]/about/arch/page.tsx +++ b/app/[locale]/about/arch/page.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { useEffect } from "react"; +import { useSectionStore } from "@/stores/sectionStore"; + +export default function Page() { + const { setSections } = useSectionStore(); + useEffect(() => { + setSections([ + { id: "arch-vision", name: "友发愿景" }, + { id: "arch-products", name: "产品体系" }, + { id: "arch-application", name: "应用场景" }, + { id: "arch-contact", name: "联系方式" }, + ]); + }, [setSections]); + + return ( +
+
+

友发愿景

+

内容占位

+
+
+

产品体系

+

内容占位

+
+
+

应用场景

+

内容占位

+
+
+

联系方式

+

内容占位

+
+
+ ); +} \ No newline at end of file diff --git a/app/[locale]/about/base/page.tsx b/app/[locale]/about/base/page.tsx index e69de29..19a9c39 100644 --- a/app/[locale]/about/base/page.tsx +++ b/app/[locale]/about/base/page.tsx @@ -0,0 +1,5 @@ +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 index e69de29..6ff4440 100644 --- a/app/[locale]/about/course/page.tsx +++ b/app/[locale]/about/course/page.tsx @@ -0,0 +1,5 @@ +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 index e69de29..9217f56 100644 --- a/app/[locale]/about/culture/page.tsx +++ b/app/[locale]/about/culture/page.tsx @@ -0,0 +1,5 @@ +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 index e69de29..eeaa1e2 100644 --- a/app/[locale]/about/green/page.tsx +++ b/app/[locale]/about/green/page.tsx @@ -0,0 +1,5 @@ +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 index e69de29..b0dc8ef 100644 --- a/app/[locale]/about/honor/page.tsx +++ b/app/[locale]/about/honor/page.tsx @@ -0,0 +1,5 @@ +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 index e69de29..c1e9a6c 100644 --- a/app/[locale]/about/introduction/page.tsx +++ b/app/[locale]/about/introduction/page.tsx @@ -0,0 +1,5 @@ +export default function Page() { + return ( +
公司介绍
+ ); +} \ No newline at end of file diff --git a/app/[locale]/blog/page.tsx b/app/[locale]/blog/page.tsx index f8d2e14..2fcf49d 100644 --- a/app/[locale]/blog/page.tsx +++ b/app/[locale]/blog/page.tsx @@ -1,16 +1,15 @@ -import { Locale, LOCALES, Link as I18nLink } from "@/i18n/routing"; +import ParallaxHero from "@/app/[locale]/blog/ParallaxHero"; +import TablerEyeFilled from "@/components/icons/eye"; +import { Link as I18nLink, Locale, LOCALES } from "@/i18n/routing"; import { getPosts } from "@/lib/getBlogs"; import { constructMetadata } from "@/lib/metadata"; +import dayjs from "dayjs"; import { Metadata } from "next"; import { getTranslations } from "next-intl/server"; -import Image from "next/image"; -import dayjs from "dayjs"; -import TablerEyeFilled from "@/components/icons/eye"; -import ParallaxHero from "@/app/[locale]/blog/ParallaxHero"; type Params = Promise<{ locale: string }>; -type SearchParams = { page?: string }; +type SearchParams = { page?: string; type?: string }; type MetadataProps = { params: Params; @@ -40,10 +39,29 @@ export default async function Page({ searchParams?: SearchParams; }) { const { locale } = await params; - const { posts } = await getPosts(locale); + let { posts } = await getPosts(locale); 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 pageSize = 10; @@ -75,6 +93,12 @@ export default async function Page({ 新闻中心 + {typeLabel && ( + <> + / + {typeLabel} + + )} diff --git a/components/header/HeaderLinks.tsx b/components/header/HeaderLinks.tsx index 0e9cf88..5c86438 100644 --- a/components/header/HeaderLinks.tsx +++ b/components/header/HeaderLinks.tsx @@ -1,6 +1,6 @@ "use client"; -import { Link as I18nLink, usePathname } from "@/i18n/routing"; +import { Link as I18nLink, usePathname, useRouter } from "@/i18n/routing"; import { cn } from "@/lib/utils"; import { HeaderLink } from "@/types/common"; import { ExternalLink } from "lucide-react"; @@ -9,32 +9,112 @@ import { useTranslations } from "next-intl"; const HeaderLinks = () => { const tHeader = useTranslations("Header"); const pathname = usePathname(); + const router = useRouter(); const headerLinks: HeaderLink[] = tHeader.raw("links"); + const localePrefix = `/${(pathname || "/zh").split("/")[1] || "zh"}`; return (
- {headerLinks.map((link) => ( - - {link.name} - {link.target && link.target === "_blank" && ( - - - - )} - - ))} + {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" && ( + + + + )} + + ); + })}
); }; diff --git a/components/header/SectionDropdown.tsx b/components/header/SectionDropdown.tsx new file mode 100644 index 0000000..06b2a9d --- /dev/null +++ b/components/header/SectionDropdown.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { useSectionStore } from "@/stores/sectionStore"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; + +export default function SectionDropdown() { + const { sections } = useSectionStore(); + if (!sections || sections.length === 0) return null; + + return ( + + + 页面目录 + + + + {sections.map((s) => ( + { + const el = document.getElementById(s.id); + if (!el) return; + const rect = el.getBoundingClientRect(); + const top = rect.top + window.scrollY - 80; + window.scrollTo({ top, behavior: "smooth" }); + history.replaceState(null, "", `#${s.id}`); + }} + > + {s.name} + + ))} + + + + ); +} \ No newline at end of file diff --git a/i18n/messages/zh.json b/i18n/messages/zh.json index e3d2459..994ed1c 100644 --- a/i18n/messages/zh.json +++ b/i18n/messages/zh.json @@ -130,7 +130,9 @@ "Home": { "title": "Next Forge", "tagLine": "Next.js 多语言启动模板", - "description": "内置多语言支持的 Next.js 16 启动模板,助您快速构建面向全球的出海网站。简洁高效,开箱即用,完全优化的SEO基础架构。" + "description": "内置多语言支持的 Next.js 16 启动模板,助您快速构建面向全球的出海网站。简洁高效,开箱即用,完全优化的SEO基础架构。", + "carousel": "轮播图", + "company_video": "企业视频" }, "Blog": { "title": "博客列表", diff --git a/next-env.d.ts b/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/stores/sectionStore.ts b/stores/sectionStore.ts new file mode 100644 index 0000000..023bec3 --- /dev/null +++ b/stores/sectionStore.ts @@ -0,0 +1,15 @@ +import { create } from "zustand"; + +export type SectionItem = { id: string; name: string }; + +type SectionStore = { + sections: SectionItem[]; + setSections: (items: SectionItem[]) => void; + clear: () => void; +}; + +export const useSectionStore = create((set) => ({ + sections: [], + setSections: (items) => set({ sections: items }), + clear: () => set({ sections: [] }), +})); \ No newline at end of file