52 lines
1.6 KiB
TypeScript
52 lines
1.6 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 type { StreamFieldBlock } from "@/types/wagtail";
|
|
|
|
/**
|
|
* StreamField type → 组件映射表。
|
|
* 必须与后端 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,
|
|
};
|
|
|
|
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} />;
|
|
})}
|
|
</>
|
|
);
|
|
}
|