feat: 模板预设机制 - 4 套模板各绑一组 JQK/Joker + 背景

经典 → 古典宫廷(王子/皇后/国王/小丑)
现代 → 现代人物(小孩/女青年/男青年/小丑鱼)
卡通 → 现代人物 + 暖色调 + 圆边框
复古 → 简笔符号 + 深色边框 + 米色背景

后端:
- CardTemplate 新增 theme_id(绑预设主题)+ design_override(背景/边框/字体等覆盖)
- 新增 apply_template_to_project():把 LibraryAsset 复制到项目素材 + 写 design
- 创建项目时支持传 template_id,自动套用整套预设
- 模板列表 API 附加 library 预览(4 张图缩略)

前端 Home.vue:
- 4 套模板卡片每张带 4 张缩略图(来自 library 预览)
- 点模板一键创建项目 + 跳转到编辑器
- '新建空白项目' 保留为独立按钮

init_system 同步:4 套模板配置 + 应用到示例项目
This commit is contained in:
Developer
2026-06-02 15:08:37 +08:00
parent 5ca000b8ab
commit 7417a4a893
7 changed files with 309 additions and 78 deletions

View File

@@ -16,47 +16,79 @@ class Command(BaseCommand):
self.stdout.write(self.style.SUCCESS('Initialization complete!'))
def create_templates(self):
"""创建示例模板"""
"""创建示例模板:每个模板绑定到一个预设主题"""
templates = [
{
'id': 'classic',
'name': '经典风格',
'description': '标准扑克牌设计,传统花色和字体',
'description': '标准扑克牌设计:王冠/权杖/小丑,黑色红色彩色花色',
'color_spade': '#000000',
'color_heart': '#E53935',
'color_club': '#000000',
'color_diamond': '#E53935',
'color_background': '#FFFFFF',
'theme_id': 'classical',
'design_override': {
'border_color': '#333333',
'border_width': 2,
'pip_size_ratio': 0.16,
'corner_size_ratio': 0.13,
'font_family': 'Times New Roman',
},
},
{
'id': 'modern',
'name': '现代简约',
'description': '扁平化设计,简洁线条',
'description': '现代人物主题:小孩/女青年/男青年/小丑鱼,浅色背景',
'color_spade': '#333333',
'color_heart': '#E53935',
'color_club': '#333333',
'color_diamond': '#E53935',
'color_background': '#FAFAFA',
'theme_id': 'modern',
'design_override': {
'border_color': '#888888',
'border_width': 1,
'pip_size_ratio': 0.15,
'corner_size_ratio': 0.12,
'font_family': 'Arial',
},
},
{
'id': 'cartoon',
'name': '卡通风格',
'description': 'Q版可爱人像圆润花色图案',
'description': 'Q版可爱人像圆润花色图案,暖黄背景',
'color_spade': '#4A4A4A',
'color_heart': '#FF6B9D',
'color_club': '#4A4A4A',
'color_diamond': '#FF6B9D',
'color_background': '#FFF9E6',
'theme_id': 'modern',
'design_override': {
'border_color': '#FF8E72',
'border_width': 3,
'pip_size_ratio': 0.17,
'corner_size_ratio': 0.14,
'font_family': 'Comic Sans MS',
},
},
{
'id': 'vintage',
'name': '复古风格',
'description': '复古色调和纹理,装饰性边框',
'description': '复古色调和纹理,深色边框,米色背景',
'color_spade': '#2C1810',
'color_heart': '#8B4513',
'color_club': '#2C1810',
'color_diamond': '#8B4513',
'color_background': '#F5DEB3',
'theme_id': 'minimal',
'design_override': {
'border_color': '#5D3A1A',
'border_width': 4,
'pip_size_ratio': 0.15,
'corner_size_ratio': 0.13,
'font_family': 'Georgia',
},
},
]
@@ -71,24 +103,32 @@ class Command(BaseCommand):
'color_club': td['color_club'],
'color_diamond': td['color_diamond'],
'color_background': td['color_background'],
'theme_id': td['theme_id'],
'design_override': td['design_override'],
'default_assets': td,
},
)
verb = 'created' if created else 'updated'
self.stdout.write(f' template {template.id} {verb}')
self.stdout.write(f' template {template.id} {verb} (theme={td["theme_id"]})')
def create_sample_project(self):
"""创建示例项目:完整可玩的 54 张牌"""
project, created = Project.objects.update_or_create(
"""创建示例项目:完整可玩的 54 张牌(应用经典模板)"""
from apps.templates.template_apply import apply_template_to_project
# 删除旧示例项目
Project.objects.filter(name="示例项目").delete()
project = Project.objects.create(
name="示例项目",
defaults=dict(
template_id='classic',
card_width=750,
card_height=1050,
export_resolution='standard',
export_include_back=True,
),
template_id='classic',
card_width=750,
card_height=1050,
export_resolution='standard',
export_include_back=True,
)
verb = 'created' if created else 'updated'
self.stdout.write(f' project "{project.name}" {verb}')
# 应用经典模板(自动写入 design + 复制 4 套古典素材到 asset
tpl = CardTemplate.objects.get(pk='classic')
result = apply_template_to_project(project, tpl)
self.stdout.write(f' applied template: {result["applied"]} assets')
self.stdout.write(self.style.SUCCESS(f'示例项目 ID: {project.id}'))

View File

@@ -26,10 +26,27 @@ def project_list(request):
data['card_overrides'] = Project._meta.get_field('card_overrides').default()
if 'number_layout' not in data:
data['number_layout'] = Project._meta.get_field('number_layout').default()
# 抽出 template_id不写进 Project 字段)
template_id = data.pop('template_id', None)
serializer = ProjectSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
project = serializer.save()
# 如果传了 template_id自动套用模板预设
template_apply_result = None
if template_id:
from apps.templates.models import CardTemplate
from apps.templates.template_apply import apply_template_to_project
try:
tpl = CardTemplate.objects.get(pk=template_id)
template_apply_result = apply_template_to_project(project, tpl)
except CardTemplate.DoesNotExist:
template_apply_result = {'error': f'template {template_id} not found'}
# 重新读一次apply_template 已修改了 design 和 assets
project.refresh_from_db()
return Response({
**ProjectSerializer(project).data,
'template_apply': template_apply_result,
}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)