From f990df3023167fa27b22ef8a10bb93a4dee4f92b Mon Sep 17 00:00:00 2001 From: zhengen Date: Sun, 11 Oct 2026 19:44:54 +0800 Subject: [PATCH] feat: render CMS content fields and footer settings --- README.md | 20 +++++ app/blog/[slug]/page.tsx | 10 ++- app/blog/page.tsx | 6 +- app/cases/[slug]/page.tsx | 8 +- app/cases/page.tsx | 5 +- app/products/[slug]/page.tsx | 4 +- app/products/page.tsx | 4 +- app/solutions/[slug]/page.tsx | 7 +- app/solutions/page.tsx | 4 +- blocks/feature-grid/FeatureGrid.tsx | 1 + components/layout/Footer.tsx | 27 ++++-- components/layout/PageContent.tsx | 33 ++++++++ services/blog.service.ts | 4 +- services/case.service.ts | 4 +- services/page.service.ts | 8 +- services/product.service.ts | 4 +- services/solution.service.ts | 4 +- tests/seo.test.cjs | 126 +++++++++++++++++++++++++--- types/wagtail.ts | 9 ++ 19 files changed, 250 insertions(+), 38 deletions(-) create mode 100644 components/layout/PageContent.tsx diff --git a/README.md b/README.md index 8091764..20cd39c 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,26 @@ Frontend tests: `node --test tests/seo.test.cjs`. Backend tests (from the backend root): `python -m pytest apps/core/tests.py -k "page_api_exposes_seo_fields or seo_image_serializer or notify_frontend_revalidate"`. +## CMS Display Fields + +Products, solutions, cases and blog columns display the CMS title and introduction. +Lists and detail pages display optional cover images, summaries, industry/client +details, publication dates (Asia/Shanghai) and blog tags where applicable. Missing +images and optional fields are omitted. Detail previews request the same fields. + +The footer reads the `footer` navigation menu and site settings for contact details, +WeChat account/QR code and Weibo link. An unconfigured menu falls back to the privacy +policy link; the existing ICP fallback is retained. Feature-grid icons follow the +same text-symbol convention as tech-stack icons. + +Deploy the backend field declarations before rebuilding the frontend. These changes +use existing database fields and require no new migrations. Page publication uses +the shared cache tag; menu/site-setting changes retain the 60-second cache fallback. + +Display/API regression tests (from the backend root): +`python -m pytest apps/blog/tests.py apps/products/tests.py apps/solutions/tests.py apps/cases/tests.py`. +Frontend display regressions run alongside SEO tests: `node --test tests/seo.test.cjs`. + ## Learn More To learn more about Next.js, take a look at the following resources: diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx index dcd6054..33beacf 100644 --- a/app/blog/[slug]/page.tsx +++ b/app/blog/[slug]/page.tsx @@ -6,6 +6,7 @@ import { BlockRenderer } from "@/blocks/BlockRenderer"; import type { BlogPageDetail } from "@/types/wagtail"; import { createPageMetadata, getPageSEO } from "@/services/seo.service"; import PageSEO from "@/components/layout/PageSEO"; +import { BlogTags, PageCover, PublicationDate } from "@/components/layout/PageContent"; interface BlogDetailPageProps { params: Promise<{ slug: string }>; @@ -22,7 +23,7 @@ export default async function BlogDetailPage({ params }: BlogDetailPageProps) { const { slug } = await params; const post = await getPreviewOrFallback( "blog.blogpage", - "body,intro,published_at", + "body,intro,published_at,cover_image,tags", () => getBlogBySlug(slug).catch(() => null), ); @@ -34,9 +35,10 @@ export default async function BlogDetailPage({ params }: BlogDetailPageProps) {

{post.title}

- {post.published_at && ( -

{post.published_at}

- )} + + + {post.intro &&

{post.intro}

} +
); diff --git a/app/blog/page.tsx b/app/blog/page.tsx index cbc06ef..cd3704e 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -3,6 +3,7 @@ import type { Metadata } from "next"; import { getBlogList } from "@/services/blog.service"; import { createPageMetadata, getPageSEO } from "@/services/seo.service"; import PageSEO from "@/components/layout/PageSEO"; +import { BlogTags, ColumnHeading, PageCover, PublicationDate } from "@/components/layout/PageContent"; export async function generateMetadata(): Promise { return createPageMetadata(await getPageSEO("blog.BlogIndexPage", "blog").catch(() => null), "技术博客"); @@ -14,7 +15,7 @@ export default async function BlogListPage() { return (
-

技术博客

+