From 73d2465ef2f3bf2e49d890bac5ae01a975e8cd21 Mon Sep 17 00:00:00 2001 From: Zhengen TANG Date: Thu, 6 Aug 2026 16:24:08 +0800 Subject: [PATCH] feat: add solutions list/detail pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - types: SolutionPageSummary/Detail - services/solution.service.ts - app/solutions: list + detail pages - Header nav: add 解决方案 link --- app/solutions/[slug]/page.tsx | 39 +++++++++++++++++++++++++++++++++++ app/solutions/page.tsx | 38 ++++++++++++++++++++++++++++++++++ components/layout/Header.tsx | 1 + services/solution.service.ts | 26 +++++++++++++++++++++++ types/wagtail.ts | 9 ++++++++ 5 files changed, 113 insertions(+) create mode 100644 app/solutions/[slug]/page.tsx create mode 100644 app/solutions/page.tsx create mode 100644 services/solution.service.ts diff --git a/app/solutions/[slug]/page.tsx b/app/solutions/[slug]/page.tsx new file mode 100644 index 0000000..6b0b68f --- /dev/null +++ b/app/solutions/[slug]/page.tsx @@ -0,0 +1,39 @@ +import { notFound } from "next/navigation"; +import type { Metadata } from "next"; +import { getSolutionBySlug } from "@/services/solution.service"; +import { BlockRenderer } from "@/blocks/BlockRenderer"; + +interface SolutionDetailPageProps { + params: Promise<{ slug: string }>; +} + +export async function generateMetadata({ + params, +}: SolutionDetailPageProps): Promise { + const { slug } = await params; + const solution = await getSolutionBySlug(slug).catch(() => null); + return { title: solution?.title ?? "解决方案未找到" }; +} + +export default async function SolutionDetailPage({ + params, +}: SolutionDetailPageProps) { + const { slug } = await params; + const solution = await getSolutionBySlug(slug).catch(() => null); + + if (!solution) { + notFound(); + } + + return ( +
+

+ {solution.title} +

+ {solution.industry && ( +

{solution.industry}

+ )} + +
+ ); +} diff --git a/app/solutions/page.tsx b/app/solutions/page.tsx new file mode 100644 index 0000000..c8d33f5 --- /dev/null +++ b/app/solutions/page.tsx @@ -0,0 +1,38 @@ +import Link from "next/link"; +import type { Metadata } from "next"; +import { getSolutionList } from "@/services/solution.service"; + +export const metadata: Metadata = { + title: "解决方案", +}; + +export default async function SolutionListPage() { + const solutions = await getSolutionList().catch(() => []); + + return ( +
+

解决方案

+
    + {solutions.map((solution) => ( +
  • + + {solution.title} + + {solution.industry && ( +

    {solution.industry}

    + )} + {solution.summary && ( +

    {solution.summary}

    + )} +
  • + ))} + {solutions.length === 0 && ( +

    暂无解决方案,或后端服务未连接。

    + )} +
+
+ ); +} diff --git a/components/layout/Header.tsx b/components/layout/Header.tsx index 2788fcc..01abefd 100644 --- a/components/layout/Header.tsx +++ b/components/layout/Header.tsx @@ -3,6 +3,7 @@ import Link from "next/link"; const navItems = [ { title: "首页", href: "/" }, { title: "产品", href: "/products" }, + { title: "解决方案", href: "/solutions" }, { title: "案例", href: "/cases" }, { title: "博客", href: "/blog" }, ]; diff --git a/services/solution.service.ts b/services/solution.service.ts new file mode 100644 index 0000000..4e9ae6f --- /dev/null +++ b/services/solution.service.ts @@ -0,0 +1,26 @@ +import { fetchAPI } from "./api-client"; +import type { + SolutionPageDetail, + SolutionPageSummary, + WagtailListResponse, +} from "@/types/wagtail"; + +/** 获取解决方案列表。 */ +export async function getSolutionList(): Promise { + const res = await fetchAPI>( + "/api/v2/pages/?type=solutions.SolutionPage&fields=industry,summary", + { tags: ["solution-list"] }, + ); + return res.items; +} + +/** 根据 slug 获取解决方案详情。 */ +export async function getSolutionBySlug( + slug: string, +): Promise { + const res = await fetchAPI>( + `/api/v2/pages/?type=solutions.SolutionPage&slug=${encodeURIComponent(slug)}&fields=body,industry,summary&limit=1`, + { tags: [`solution:${slug}`] }, + ); + return res.items[0] ?? null; +} diff --git a/types/wagtail.ts b/types/wagtail.ts index 7b21303..8c95b0a 100644 --- a/types/wagtail.ts +++ b/types/wagtail.ts @@ -147,3 +147,12 @@ export interface CaseStudyPageSummary extends WagtailPageSummary { export interface CaseStudyPageDetail extends CaseStudyPageSummary { body: StreamFieldBlock[]; } + +export interface SolutionPageSummary extends WagtailPageSummary { + industry?: string; + summary?: string; +} + +export interface SolutionPageDetail extends SolutionPageSummary { + body: StreamFieldBlock[]; +}