feat: add products/cases page models and forms lead-capture app
- apps/products: ProductIndexPage/ProductPage - apps/cases: CaseStudyIndexPage/CaseStudyPage - apps/forms: FormDefinition/FormDefinitionField/Lead + submit API - core/blocks: CaseStudyBlock, FormBlock (with API representation) - register new apps, custom leads API route, migrations
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
"""
|
||||
线索表单:FormDefinition 是可复用的表单定义 Snippet,通过 core.blocks.FormBlock
|
||||
嵌入任意 StreamField 页面;提交结果保存为 Lead。
|
||||
详见 documents/设计方案分析与完善版.md §2.6(FormBlock)与 B2B 官网核心转化路径。
|
||||
"""
|
||||
from django.db import models
|
||||
from modelcluster.fields import ParentalKey
|
||||
from modelcluster.models import ClusterableModel
|
||||
from wagtail.admin.panels import FieldPanel, InlinePanel
|
||||
from wagtail.models import Orderable
|
||||
from wagtail.snippets.models import register_snippet
|
||||
|
||||
FIELD_TYPE_CHOICES = [
|
||||
("text", "单行文本"),
|
||||
("textarea", "多行文本"),
|
||||
("email", "邮箱"),
|
||||
("tel", "电话"),
|
||||
("select", "下拉选择"),
|
||||
]
|
||||
|
||||
|
||||
@register_snippet
|
||||
class FormDefinition(ClusterableModel):
|
||||
"""可复用的线索表单定义。"""
|
||||
|
||||
name = models.CharField(max_length=100, verbose_name="表单名称")
|
||||
submit_button_text = models.CharField(
|
||||
max_length=50, default="提交", verbose_name="提交按钮文案"
|
||||
)
|
||||
success_message = models.CharField(
|
||||
max_length=200,
|
||||
default="提交成功,我们会尽快与您联系。",
|
||||
verbose_name="提交成功提示",
|
||||
)
|
||||
notification_email = models.EmailField(blank=True, verbose_name="通知邮箱")
|
||||
|
||||
panels = [
|
||||
FieldPanel("name"),
|
||||
FieldPanel("submit_button_text"),
|
||||
FieldPanel("success_message"),
|
||||
FieldPanel("notification_email"),
|
||||
InlinePanel("fields", label="表单字段"),
|
||||
]
|
||||
|
||||
class Meta:
|
||||
verbose_name = "线索表单"
|
||||
verbose_name_plural = "线索表单"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class FormDefinitionField(Orderable):
|
||||
"""FormDefinition 的子字段,编辑器在后台可自由增删排序。"""
|
||||
|
||||
form = ParentalKey(
|
||||
FormDefinition, on_delete=models.CASCADE, related_name="fields"
|
||||
)
|
||||
label = models.CharField(max_length=100, verbose_name="字段标签")
|
||||
field_key = models.SlugField(
|
||||
max_length=50,
|
||||
verbose_name="字段标识",
|
||||
help_text="提交数据中使用的 key,例如 name / email / phone",
|
||||
)
|
||||
field_type = models.CharField(
|
||||
max_length=20, choices=FIELD_TYPE_CHOICES, default="text", verbose_name="字段类型"
|
||||
)
|
||||
required = models.BooleanField(default=True, verbose_name="是否必填")
|
||||
choices = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
verbose_name="可选项",
|
||||
help_text="逗号分隔,仅“下拉选择”类型使用",
|
||||
)
|
||||
|
||||
panels = [
|
||||
FieldPanel("label"),
|
||||
FieldPanel("field_key"),
|
||||
FieldPanel("field_type"),
|
||||
FieldPanel("required"),
|
||||
FieldPanel("choices"),
|
||||
]
|
||||
|
||||
|
||||
@register_snippet
|
||||
class Lead(models.Model):
|
||||
"""表单提交产生的线索记录,供销售/市场团队在后台查看。"""
|
||||
|
||||
form = models.ForeignKey(
|
||||
FormDefinition,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
related_name="leads",
|
||||
verbose_name="来源表单",
|
||||
)
|
||||
data = models.JSONField(default=dict, verbose_name="提交数据")
|
||||
source_url = models.URLField(blank=True, verbose_name="来源页面")
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="提交时间")
|
||||
|
||||
panels = [
|
||||
FieldPanel("form"),
|
||||
FieldPanel("data"),
|
||||
FieldPanel("source_url"),
|
||||
]
|
||||
|
||||
class Meta:
|
||||
verbose_name = "线索"
|
||||
verbose_name_plural = "线索"
|
||||
ordering = ["-created_at"]
|
||||
|
||||
def __str__(self):
|
||||
form_name = self.form.name if self.form_id else "(表单已删除)"
|
||||
return f"{form_name} - {self.created_at:%Y-%m-%d %H:%M}"
|
||||
Reference in New Issue
Block a user