✨ feat!(ui): 重构图表组件架构,实现数据与样式分离 (#2035)

* ✨ feat!(ui): 重构图表组件架构,实现数据与样式分离

🏗️ **架构重构**
- 移除charts.py中所有硬编码样式参数(grid、tooltip、legend等)
- 将样式配置迁移至主题层style.json文件
- 统一图表模板消费样式文件的能力

📊 **图表组件优化**
- bar_chart: 移除grid和坐标轴show参数
- pie_chart: 移除tooltip、legend样式和series视觉参数
- line_chart: 移除tooltip、grid和坐标轴配置
- radar_chart: 移除tooltip硬编码

🎨 **主题系统增强**
- 新增pie_chart、line_chart、radar_chart的style.json配置
- 更新bar_chart/style.json,添加grid、xAxis、yAxis样式
- 所有图表模板支持deepMerge样式合并逻辑

🔧 **Breaking Changes**
- 图表工厂函数不再接受样式参数
- 主题开发者现可通过style.json完全定制图表外观
- 提升组件可维护性和主题灵活性

* 📦️ build(pyinstaller): 引入 resources.spec 并更新 .gitignore 规则

* 🚨 auto fix by pre-commit hooks

---------

Co-authored-by: webjoin111 <455457521@qq.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Rumio
2025-08-28 09:20:15 +08:00
committed by GitHub
co-authored by webjoin111 pre-commit-ci[bot]
parent d9e65057cf
commit 7472cabd48
61 changed files with 2272 additions and 648 deletions
+7
View File
@@ -3,15 +3,22 @@
包含各种UI组件的数据模型
"""
from .alert import Alert
from .badge import Badge
from .divider import Divider, Rectangle
from .kpi_card import KpiCard
from .progress_bar import ProgressBar
from .timeline import Timeline, TimelineItem
from .user_info_block import UserInfoBlock
__all__ = [
"Alert",
"Badge",
"Divider",
"KpiCard",
"ProgressBar",
"Rectangle",
"Timeline",
"TimelineItem",
"UserInfoBlock",
]
+23
View File
@@ -0,0 +1,23 @@
from typing import Literal
from pydantic import Field
from ..core.base import RenderableComponent
__all__ = ["Alert"]
class Alert(RenderableComponent):
"""一个带样式的提示框组件,用于显示重要信息。"""
component_type: Literal["alert"] = "alert"
type: Literal["info", "success", "warning", "error"] = Field(
default="info", description="提示框的类型,决定了颜色和图标"
)
title: str = Field(..., description="提示框的标题")
content: str = Field(..., description="提示框的主要内容")
show_icon: bool = Field(default=True, description="是否显示与类型匹配的图标")
@property
def template_name(self) -> str:
return "components/widgets/alert"
+35
View File
@@ -0,0 +1,35 @@
from typing import Literal
from pydantic import Field
from ..core.base import RenderableComponent
__all__ = ["Avatar", "AvatarGroup"]
class Avatar(RenderableComponent):
"""单个头像组件。"""
component_type: Literal["avatar"] = "avatar"
src: str = Field(..., description="头像的URL或Base64数据URI")
shape: Literal["circle", "square"] = Field("circle", description="头像形状")
size: int = Field(50, description="头像尺寸(像素)")
@property
def template_name(self) -> str:
return "components/widgets/avatar"
class AvatarGroup(RenderableComponent):
"""一组堆叠的头像组件。"""
component_type: Literal["avatar_group"] = "avatar_group"
avatars: list[Avatar] = Field(default_factory=list, description="头像列表")
spacing: int = Field(-15, description="头像间的间距(负数表示重叠)")
max_count: int | None = Field(
None, description="最多显示的头像数量,超出部分会显示为'+N'"
)
@property
def template_name(self) -> str:
return "components/widgets/avatar"
+29
View File
@@ -0,0 +1,29 @@
from typing import Any, Literal
from pydantic import Field
from ..core.base import RenderableComponent
__all__ = ["KpiCard"]
class KpiCard(RenderableComponent):
"""一个用于展示关键性能指标(KPI)的统计卡片。"""
component_type: Literal["kpi_card"] = "kpi_card"
label: str = Field(..., description="指标的标签或名称")
value: Any = Field(..., description="指标的主要数值")
unit: str | None = Field(default=None, description="数值的单位,可选")
change: str | None = Field(
default=None, description="与上一周期的变化,例如 '+15%' 或 '-100'"
)
change_type: Literal["positive", "negative", "neutral"] = Field(
default="neutral", description="变化的类型,用于决定颜色"
)
icon_svg: str | None = Field(
default=None, description="卡片中显示的可选图标 (SVG path data)"
)
@property
def template_name(self) -> str:
return "components/widgets/kpi_card"
+30
View File
@@ -0,0 +1,30 @@
from typing import Literal
from pydantic import BaseModel, Field
from ..core.base import RenderableComponent
__all__ = ["Timeline", "TimelineItem"]
class TimelineItem(BaseModel):
"""时间轴中的单个事件点。"""
timestamp: str = Field(..., description="显示在时间点旁边的时间或标签")
title: str = Field(..., description="事件的标题")
content: str = Field(..., description="事件的详细描述")
icon: str | None = Field(default=None, description="可选的自定义图标SVG路径")
color: str | None = Field(default=None, description="可选的自定义颜色,覆盖默认")
class Timeline(RenderableComponent):
"""一个垂直时间轴组件,用于按顺序展示事件。"""
component_type: Literal["timeline"] = "timeline"
items: list[TimelineItem] = Field(
default_factory=list, description="时间轴项目列表"
)
@property
def template_name(self) -> str:
return "components/widgets/timeline"