feat: 新增Snippet全局内容模型(TeamMember/Testimonial/Partner/NavigationMenu/SiteSettings)

- 新增 apps.core.TeamMember/Testimonial/Partner:简单 register_snippet,含 order 排序字段
- 新增 apps.core.NavigationMenu+NavigationMenuItem:ClusterableModel+Orderable+InlinePanel,
  支持内部页面或外部链接二选一
- 新增 apps.core.SiteSettings:接入 wagtail.contrib.settings + BaseSiteSetting,
  存放公司信息/ICP备案号/社交账号等全局配置
- 新增只读 API:/api/v1/custom/core/{team,testimonials,partners,navigation,site-settings}/
- 补充 apps/core/tests.py 单元测试,pytest 用例由 11 个增至 24 个,全部通过
- 更新设计文档 §2.16 Phase 1 进度清单
This commit is contained in:
2026-08-07 13:06:22 +08:00
parent b04de9f5c1
commit 8fbbd2e6b0
9 changed files with 628 additions and 5 deletions
+74
View File
@@ -0,0 +1,74 @@
"""
apps.core 全局 Snippet 的只读序列化器,供自定义业务接口
/api/v1/custom/core/...)使用。图片字段统一序列化为
{"url": ..., "title": ...},与前端 frontend/types/wagtail.ts
中既有的 background_image/logos 字段结构保持一致。
"""
from rest_framework import serializers
def serialize_image(image):
if not image:
return None
return {"url": image.file.url, "title": image.title}
class TeamMemberSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
role = serializers.CharField()
bio = serializers.CharField()
photo = serializers.SerializerMethodField()
def get_photo(self, obj):
return serialize_image(obj.photo)
class TestimonialSerializer(serializers.Serializer):
id = serializers.IntegerField()
quote = serializers.CharField()
author_name = serializers.CharField()
author_title = serializers.CharField()
author_photo = serializers.SerializerMethodField()
def get_author_photo(self, obj):
return serialize_image(obj.author_photo)
class PartnerSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
website_url = serializers.CharField()
logo = serializers.SerializerMethodField()
def get_logo(self, obj):
return serialize_image(obj.logo)
class NavigationMenuItemSerializer(serializers.Serializer):
label = serializers.CharField()
url = serializers.CharField()
open_in_new_tab = serializers.BooleanField()
class NavigationMenuSerializer(serializers.Serializer):
name = serializers.CharField()
items = serializers.SerializerMethodField()
def get_items(self, obj):
return NavigationMenuItemSerializer(obj.items.all(), many=True).data
class SiteSettingsSerializer(serializers.Serializer):
company_name = serializers.CharField()
contact_phone = serializers.CharField()
contact_email = serializers.CharField()
address = serializers.CharField()
icp_number = serializers.CharField()
icp_url = serializers.CharField()
wechat_account = serializers.CharField()
weibo_url = serializers.CharField()
wechat_qrcode = serializers.SerializerMethodField()
def get_wechat_qrcode(self, obj):
return serialize_image(obj.wechat_qrcode)