Files
wagtailcms-frontend/blocks/BlockRenderer.tsx
T
Zhengen b5fe5e13ab feat: add products/cases pages and lead-capture form block
- types: Product/CaseStudy page types, CaseStudyBlockValue, FormBlockValue
- services: product.service.ts, case.service.ts
- app/products, app/cases: list + detail pages
- blocks: CaseStudyCard, LeadForm (submits to /api/v1/custom/leads/)
- Header nav: add 产品/案例 links
2026-08-06 16:04:43 +08:00

56 lines
1.7 KiB
TypeScript

import type { ComponentType } from "react";
import Hero from "./hero/Hero";
import Stats from "./stats/Stats";
import FeatureGrid from "./feature-grid/FeatureGrid";
import FAQ from "./faq/FAQ";
import CTA from "./cta/CTA";
import LogoCloud from "./logo-cloud/LogoCloud";
import CaseStudyCard from "./case-study/CaseStudyCard";
import LeadForm from "./form/LeadForm";
import type { StreamFieldBlock } from "@/types/wagtail";
/**
* StreamField 类型 → 组件映射表。
* 必须与后端 apps/core/blocks.py 的 COMMON_BLOCKS 保持一致,
* 否则新增/重命名 Block 时前后端会出现字段不对齐问题
* (详见 documents/设计方案分析与完善版.md §2.6)。
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const components: Record<string, ComponentType<{ value: any }>> = {
hero: Hero,
stats: Stats,
feature_grid: FeatureGrid,
faq: FAQ,
cta: CTA,
logo_cloud: LogoCloud,
case_study: CaseStudyCard,
form: LeadForm,
};
export function BlockRenderer({ blocks }: { blocks: StreamFieldBlock[] }) {
return (
<>
{blocks?.map((block) => {
if (block.type === "richtext") {
return (
<div
key={block.id}
className="prose mx-auto max-w-3xl px-6 py-12"
dangerouslySetInnerHTML={{ __html: block.value }}
/>
);
}
const Component = components[block.type];
if (!Component) {
if (process.env.NODE_ENV === "development") {
console.warn(`未找到 Block 类型 "${block.type}" 对应的前端组件`);
}
return null;
}
return <Component key={block.id} value={block.value} />;
})}
</>
);
}