feat: add theme preset system with admin CRUD, public listing, and user theme settings

- Add ChromaticColor (17 Tailwind colors) and NeutralColor (5 grays) enums
- Add ThemePreset table with flat color columns and unique name constraint
- Add admin theme endpoints (CRUD + set default) at /api/v1/admin/theme
- Add public theme listing at /api/v1/site/themes
- Add user theme settings (PATCH /theme) with color snapshot on User model
- User.color_* columns store per-user overrides; fallback to default preset then builtin
- Initialize default theme preset in migration
- Remove legacy defaultTheme/themes settings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 19:34:41 +08:00
parent a99091ea7a
commit 4c1b7a8aad
29 changed files with 1832 additions and 404 deletions

117
sqlmodels/theme_preset.py Normal file
View File

@@ -0,0 +1,117 @@
from datetime import datetime
from uuid import UUID
from sqlmodel import Field
from .base import SQLModelBase
from .color import ChromaticColor, NeutralColor, ThemeColorsBase
from .mixin import UUIDTableBaseMixin
class ThemePresetBase(SQLModelBase):
"""主题预设基础字段"""
name: str = Field(max_length=100)
"""预设名称"""
is_default: bool = False
"""是否为默认预设"""
primary: ChromaticColor
"""主色调"""
secondary: ChromaticColor
"""辅助色"""
success: ChromaticColor
"""成功色"""
info: ChromaticColor
"""信息色"""
warning: ChromaticColor
"""警告色"""
error: ChromaticColor
"""错误色"""
neutral: NeutralColor
"""中性色"""
class ThemePreset(ThemePresetBase, UUIDTableBaseMixin):
"""主题预设表"""
name: str = Field(max_length=100, unique=True)
"""预设名称(唯一约束)"""
# ==================== DTO ====================
class ThemePresetCreateRequest(SQLModelBase):
"""创建主题预设请求 DTO"""
name: str = Field(max_length=100)
"""预设名称"""
colors: ThemeColorsBase
"""颜色配置"""
class ThemePresetUpdateRequest(SQLModelBase):
"""更新主题预设请求 DTO"""
name: str | None = Field(default=None, max_length=100)
"""预设名称(可选)"""
colors: ThemeColorsBase | None = None
"""颜色配置(可选)"""
class ThemePresetResponse(SQLModelBase):
"""主题预设响应 DTO"""
id: UUID
"""预设UUID"""
name: str
"""预设名称"""
is_default: bool
"""是否为默认预设"""
colors: ThemeColorsBase
"""颜色配置"""
created_at: datetime
"""创建时间"""
updated_at: datetime
"""更新时间"""
@classmethod
def from_preset(cls, preset: ThemePreset) -> 'ThemePresetResponse':
"""从数据库模型转换为响应 DTO平铺列 → 嵌套 colors 对象)"""
return cls(
id=preset.id,
name=preset.name,
is_default=preset.is_default,
colors=ThemeColorsBase(
primary=preset.primary,
secondary=preset.secondary,
success=preset.success,
info=preset.info,
warning=preset.warning,
error=preset.error,
neutral=preset.neutral,
),
created_at=preset.created_at,
updated_at=preset.updated_at,
)
class ThemePresetListResponse(SQLModelBase):
"""主题预设列表响应 DTO"""
themes: list[ThemePresetResponse]
"""主题预设列表"""