""" 可复用的 StreamField Block 库。 参见 documents/设计方案分析与完善版.md §2.6 组件清单。 """ from django.utils.translation import gettext_lazy as _ from wagtail import blocks from wagtail.images.blocks import ImageChooserBlock from wagtail.snippets.blocks import SnippetChooserBlock class CTAButtonBlock(blocks.StructBlock): text = blocks.CharBlock(max_length=50, label=_("按钮文案")) link = blocks.URLBlock(label=_("按钮链接")) class Meta: icon = "link" label = _("按钮") class HeroBlock(blocks.StructBlock): title = blocks.CharBlock(max_length=120, label=_("标题")) subtitle = blocks.TextBlock(required=False, label=_("副标题")) background_image = ImageChooserBlock(required=False, label=_("背景图")) buttons = blocks.ListBlock(CTAButtonBlock(), label=_("按钮组")) class Meta: icon = "image" label = _("首屏 Hero") template = "core/blocks/hero_block.html" class StatItemBlock(blocks.StructBlock): value = blocks.CharBlock(max_length=20, label=_("数值")) label = blocks.CharBlock(max_length=50, label=_("说明文字")) class StatsBlock(blocks.StructBlock): items = blocks.ListBlock(StatItemBlock(), label=_("数据项")) class Meta: icon = "site" label = _("数据展示") template = "core/blocks/stats_block.html" class FeatureBlock(blocks.StructBlock): icon = blocks.CharBlock(max_length=50, required=False, label=_("图标")) title = blocks.CharBlock(max_length=100, label=_("标题")) description = blocks.TextBlock(label=_("描述")) class Meta: icon = "pick" label = _("能力/优势") template = "core/blocks/feature_block.html" class FeatureGridBlock(blocks.StructBlock): heading = blocks.CharBlock(max_length=100, required=False, label=_("模块标题")) items = blocks.ListBlock(FeatureBlock(), label=_("能力列表")) class Meta: icon = "grip" label = _("能力网格") class FAQItemBlock(blocks.StructBlock): question = blocks.CharBlock(max_length=200, label=_("问题")) answer = blocks.RichTextBlock(label=_("回答")) class FAQBlock(blocks.StructBlock): items = blocks.ListBlock(FAQItemBlock(), label=_("问答列表")) class Meta: icon = "help" label = _("常见问题") template = "core/blocks/faq_block.html" class CTABlock(blocks.StructBlock): heading = blocks.CharBlock(max_length=120, label=_("标题")) description = blocks.TextBlock(required=False, label=_("描述")) button = CTAButtonBlock(label=_("按钮")) class Meta: icon = "plus" label = _("行动号召 CTA") template = "core/blocks/cta_block.html" class LogoCloudBlock(blocks.StructBlock): heading = blocks.CharBlock(max_length=100, required=False, label=_("标题")) logos = blocks.ListBlock(ImageChooserBlock(), label=_("Logo 列表")) class Meta: icon = "image" label = _("客户 Logo 墙") template = "core/blocks/logo_cloud_block.html" class CaseStudyBlock(blocks.StructBlock): case_page = blocks.PageChooserBlock( page_type="cases.CaseStudyPage", label=_("案例页面") ) summary = blocks.CharBlock(max_length=250, required=False, label=_("摘要覆盖")) class Meta: icon = "doc-full" label = _("客户案例卡片") class FormBlock(blocks.StructBlock): """嵌入可复用的线索表单(apps.forms.FormDefinition Snippet)。""" form = SnippetChooserBlock("forms.FormDefinition", label=_("选择表单")) class Meta: icon = "form" label = _("线索表单") def get_api_representation(self, value, context=None): """ 默认 SnippetChooserBlock 只会序列化为主键,前端渲染动态表单需要完整字段定义, 因此这里手动展开 FormDefinition 及其子字段(详见 apps/forms/models.py)。 """ form_def = value.get("form") if form_def is None: return {"form": None} return { "form": { "id": form_def.pk, "name": form_def.name, "submit_button_text": form_def.submit_button_text, "success_message": form_def.success_message, "fields": [ { "label": field.label, "field_key": field.field_key, "field_type": field.field_type, "required": field.required, "choices": [ choice.strip() for choice in field.choices.split(",") if choice.strip() ], } for field in form_def.fields.all() ], } } COMMON_BLOCKS = [ ("hero", HeroBlock()), ("stats", StatsBlock()), ("feature_grid", FeatureGridBlock()), ("faq", FAQBlock()), ("cta", CTABlock()), ("logo_cloud", LogoCloudBlock()), ("case_study", CaseStudyBlock()), ("form", FormBlock()), ("richtext", blocks.RichTextBlock(label=_("富文本"))), ]