from django.db import models import uuid def default_design(): """默认设计配置(整副牌共享)""" return { # 全局背景色(整副牌默认用这个,个别牌可覆盖) 'background_color': '#FFFFFF', 'background_image': None, # 整副牌背景图,相对 media 的路径 # 整副牌边框 'border_color': '#333333', 'border_width': 2, # 4 个花色符号:可以上传图片,也可保持 None(用字体符号) 'suit_symbols': { 'spade': {'type': 'text', 'value': '♠', 'asset_id': None, 'color': '#000000'}, 'heart': {'type': 'text', 'value': '♥', 'asset_id': None, 'color': '#E53935'}, 'club': {'type': 'text', 'value': '♣', 'asset_id': None, 'color': '#000000'}, 'diamond': {'type': 'text', 'value': '♦', 'asset_id': None, 'color': '#E53935'}, }, # 数字牌角标和中心花色符号的大小(占牌面宽度比例) 'corner_size_ratio': 0.13, 'pip_size_ratio': 0.16, # 字体 'font_family': 'Times New Roman', 'font_color': '#000000', # 角标数字颜色 # 角标布局微调(相对位置 0~1) 'corner_offset': {'x': 0, 'y': 0}, } def default_card_overrides(): """每张牌可独立覆盖的项目级设置(key=card_key, value 覆盖项)""" return { # 例如 'joker-big': { 'background_color': '#1B5E20' } } def default_back_design(): """背面专用设计配置(独立于正面)""" return { 'background_color': '#1A237E', 'border_color': '#C0A050', 'border_width': 3, 'pattern_color': None, 'image': None, 'image_dx': 0, 'image_dy': 0, 'image_scale': 1, } class Project(models.Model): """项目配置模型""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=100) template_id = models.CharField(max_length=50, default='classic') card_width = models.IntegerField(default=750) card_height = models.IntegerField(default=1050) # 项目级设计配置 design = models.JSONField(default=default_design) # 每张牌对项目级配置的覆盖 card_overrides = models.JSONField(default=default_card_overrides) # 背面专用设计配置(独立于正面 design) back_design = models.JSONField(default=default_back_design) # 数字牌花色位置微调(相对 0~1) # { '1': [{'dx':0,'dy':0,'scale':1}, ...], '2': [...], ... } number_layout = models.JSONField(default=dict) # JQK 人物图的水平翻转(每张牌独立) face_orientations = models.JSONField(default=dict) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) # 导出设置 export_resolution = models.CharField(max_length=20, default='standard') export_include_back = models.BooleanField(default=True) def __str__(self): return self.name class Meta: ordering = ['-updated_at'] class Asset(models.Model): """项目素材模型""" project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='assets') asset_type = models.CharField(max_length=20) # 'suit_symbol', 'face_card', 'joker', 'back', 'border', 'background' asset_key = models.CharField(max_length=50) # 如 'spade', 'heart-J', 'big_joker' file_path = models.CharField(max_length=255, blank=True) # 相对于media目录 file_name = models.CharField(max_length=100, blank=True) width = models.IntegerField(null=True, blank=True) height = models.IntegerField(null=True, blank=True) uploaded_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.asset_type}:{self.asset_key}" class Meta: ordering = ['-uploaded_at'] class CardLayer(models.Model): """牌面图层配置模型(图层顺序、可见性等)""" project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='layers') card_type = models.CharField(max_length=20) # 'number', 'face', 'joker', 'back' card_key = models.CharField(max_length=30) # 'spade-A', 'heart-K', 'joker-big' layer_name = models.CharField(max_length=50) layer_type = models.CharField(max_length=20) # 'background', 'border', 'pattern', 'image', 'text', 'symbol' visible = models.BooleanField(default=True) locked = models.BooleanField(default=False) opacity = models.FloatField(default=1.0) z_index = models.IntegerField(default=0) # 图层属性(JSON存储) properties = models.JSONField(default=dict) def __str__(self): return f"{self.card_key}-{self.layer_name}" class Meta: ordering = ['card_key', 'z_index'] class LibraryAsset(models.Model): """预设素材库(与 Project 无关,全局共享) 主题分类:classical/modern/astronomy/minimal ... 角色:J / Q / K / joker """ id = models.BigAutoField(primary_key=True) theme_id = models.CharField(max_length=50, db_index=True) # classical / modern ... theme_name = models.CharField(max_length=100) # 古典宫廷 / 现代人物 ... role = models.CharField(max_length=10, db_index=True) # 'J' / 'Q' / 'K' / 'joker' role_name = models.CharField(max_length=50) # '王子' / '皇后' ... asset_id = models.CharField(max_length=50) # 'prince' / 'queen' ... label = models.CharField(max_length=100) # '古典·王子' description = models.CharField(max_length=255, blank=True) file_path = models.CharField(max_length=255) # library/classical/prince.svg file_name = models.CharField(max_length=100) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f'{self.theme_name}/{self.role_name}' class Meta: ordering = ['theme_id', 'role', 'asset_id'] unique_together = [['theme_id', 'asset_id']]