mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-07 21:00:21 +08:00
♻️ refactor(ui): 重构 UI 渲染系统并优化资源管理 (#2094)
* ♻️ refactor(ui): 重构 UI 渲染系统并优化资源管理 - 【重构】重构 `RendererService` 架构,解耦模板引擎、主题管理与截图引擎 - 【重构】重构 `ui` 模块,采用组件注册机制与数据模型驱动,移除旧版 `builders` - 【功能】新增 UI 热重载模式,支持在不重启的情况下实时预览 HTML/CSS 修改 - 【功能】增强启动项资源检查,支持基于 `resources.spec` 的版本校验与自动更新 - 【优化】统一内置插件的 UI 渲染逻辑,迁移至新的 `ui.table`、`ui.markdown` 等工厂接口 - 【优化】优化日志脱敏工具,支持自动折叠调试输出中冗长的样式标签 - 【优化】引入 `AssetResolutionService`,完善皮肤、组件、主题间的多级资源回退机制 - 【优化】新增组件生命周期钩子 `prepare`,支持渲染前的异步数据预处理 * 🚨 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:
co-authored by
webjoin111
pre-commit-ci[bot]
parent
bc8e1659ae
commit
5e30694663
@@ -1,18 +1,11 @@
|
||||
"""
|
||||
组件模型模块
|
||||
包含各种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
|
||||
from .data import KpiCard, Timeline, TimelineItem
|
||||
from .display import Avatar, AvatarGroup, Divider, Rectangle, UserInfoBlock
|
||||
from .feedback import Alert, Badge, ProgressBar
|
||||
|
||||
__all__ = [
|
||||
"Alert",
|
||||
"Avatar",
|
||||
"AvatarGroup",
|
||||
"Badge",
|
||||
"Divider",
|
||||
"KpiCard",
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
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"
|
||||
@@ -1,41 +0,0 @@
|
||||
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")
|
||||
"""头像的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'"
|
||||
)
|
||||
"""最多显示的头像数量,超出部分会显示为'+N'"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/avatar"
|
||||
@@ -1,24 +0,0 @@
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from ..core.base import RenderableComponent
|
||||
|
||||
__all__ = ["Badge"]
|
||||
|
||||
|
||||
class Badge(RenderableComponent):
|
||||
"""一个简单的徽章组件,用于显示状态或标签。"""
|
||||
|
||||
component_type: Literal["badge"] = "badge"
|
||||
text: str = Field(..., description="徽章上显示的文本")
|
||||
"""徽章上显示的文本"""
|
||||
color_scheme: Literal["primary", "success", "warning", "error", "info"] = Field(
|
||||
default="info",
|
||||
description="预设的颜色方案",
|
||||
)
|
||||
"""预设的颜色方案"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/badge"
|
||||
@@ -1,16 +1,17 @@
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from ..core.base import RenderableComponent
|
||||
|
||||
__all__ = ["KpiCard"]
|
||||
__all__ = ["KpiCard", "Timeline", "TimelineItem"]
|
||||
|
||||
|
||||
class KpiCard(RenderableComponent):
|
||||
"""一个用于展示关键性能指标(KPI)的统计卡片。"""
|
||||
|
||||
component_type: Literal["kpi_card"] = "kpi_card"
|
||||
"""组件类型"""
|
||||
label: str = Field(..., description="指标的标签或名称")
|
||||
"""指标的标签或名称"""
|
||||
value: Any = Field(..., description="指标的主要数值")
|
||||
@@ -33,3 +34,33 @@ class KpiCard(RenderableComponent):
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/kpi_card"
|
||||
|
||||
|
||||
class TimelineItem(BaseModel):
|
||||
"""时间轴中的单个事件点。"""
|
||||
|
||||
timestamp: str = Field(..., description="显示在时间点旁边的时间或标签")
|
||||
"""显示在时间点旁边的时间或标签"""
|
||||
title: str = Field(..., description="事件的标题")
|
||||
"""事件的标题"""
|
||||
content: str = Field(..., description="事件的详细描述")
|
||||
"""事件的详细描述"""
|
||||
icon: str | None = Field(default=None, description="可选的自定义图标SVG路径")
|
||||
"""可选的自定义图标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"
|
||||
@@ -0,0 +1,102 @@
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from ..core.base import RenderableComponent
|
||||
|
||||
__all__ = ["Avatar", "AvatarGroup", "Divider", "Rectangle", "UserInfoBlock"]
|
||||
|
||||
|
||||
class Avatar(RenderableComponent):
|
||||
"""单个头像组件。"""
|
||||
|
||||
component_type: Literal["avatar"] = "avatar"
|
||||
"""组件类型"""
|
||||
src: str = Field(..., description="头像的URL或Base64数据URI")
|
||||
"""头像的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'"
|
||||
)
|
||||
"""最多显示的头像数量,超出部分会显示为'+N'"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/avatar"
|
||||
|
||||
|
||||
class Divider(RenderableComponent):
|
||||
"""一个简单的分割线组件。"""
|
||||
|
||||
component_type: Literal["divider"] = "divider"
|
||||
"""组件类型"""
|
||||
margin: str = Field("2em 0", description="CSS margin属性,控制分割线上下的间距")
|
||||
"""CSS margin属性,控制分割线上下的间距"""
|
||||
color: str = Field("#f7889c", description="分割线颜色")
|
||||
"""分割线颜色"""
|
||||
style: Literal["solid", "dashed", "dotted"] = Field("solid", description="线条样式")
|
||||
"""线条样式"""
|
||||
thickness: str = Field("1px", description="线条粗细")
|
||||
"""线条粗细"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/divider"
|
||||
|
||||
|
||||
class Rectangle(RenderableComponent):
|
||||
"""一个矩形背景块组件。"""
|
||||
|
||||
component_type: Literal["rectangle"] = "rectangle"
|
||||
"""组件类型"""
|
||||
height: str = Field("50px", description="矩形的高度 (CSS value)")
|
||||
"""矩形的高度 (CSS value)"""
|
||||
background_color: str = Field("#fdf1f5", description="背景颜色")
|
||||
"""背景颜色"""
|
||||
border: str = Field("1px solid #fce4ec", description="CSS border属性")
|
||||
"""CSS border属性"""
|
||||
border_radius: str = Field("8px", description="CSS border-radius属性")
|
||||
"""CSS border-radius属性"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/rectangle"
|
||||
|
||||
|
||||
class UserInfoBlock(RenderableComponent):
|
||||
"""一个带头像、名称和副标题的用户信息块组件。"""
|
||||
|
||||
component_type: Literal["user_info_block"] = "user_info_block"
|
||||
"""组件类型"""
|
||||
avatar_url: str = Field(..., description="用户头像的URL")
|
||||
"""用户头像的URL"""
|
||||
name: str = Field(..., description="用户的名称")
|
||||
"""用户的名称"""
|
||||
subtitle: str | None = Field(
|
||||
default=None, description="显示在名称下方的副标题 (如UID或角色)"
|
||||
)
|
||||
"""显示在名称下方的副标题 (如UID或角色)"""
|
||||
tags: list[str] = Field(default_factory=list, description="附加的标签列表")
|
||||
"""附加的标签列表"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/user_info_block"
|
||||
@@ -1,43 +0,0 @@
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from ..core.base import RenderableComponent
|
||||
|
||||
__all__ = ["Divider", "Rectangle"]
|
||||
|
||||
|
||||
class Divider(RenderableComponent):
|
||||
"""一个简单的分割线组件。"""
|
||||
|
||||
component_type: Literal["divider"] = "divider"
|
||||
margin: str = Field("2em 0", description="CSS margin属性,控制分割线上下的间距")
|
||||
"""CSS margin属性,控制分割线上下的间距"""
|
||||
color: str = Field("#f7889c", description="分割线颜色")
|
||||
"""分割线颜色"""
|
||||
style: Literal["solid", "dashed", "dotted"] = Field("solid", description="线条样式")
|
||||
"""线条样式"""
|
||||
thickness: str = Field("1px", description="线条粗细")
|
||||
"""线条粗细"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/divider"
|
||||
|
||||
|
||||
class Rectangle(RenderableComponent):
|
||||
"""一个矩形背景块组件。"""
|
||||
|
||||
component_type: Literal["rectangle"] = "rectangle"
|
||||
height: str = Field("50px", description="矩形的高度 (CSS value)")
|
||||
"""矩形的高度 (CSS value)"""
|
||||
background_color: str = Field("#fdf1f5", description="背景颜色")
|
||||
"""背景颜色"""
|
||||
border: str = Field("1px solid #fce4ec", description="CSS border属性")
|
||||
"""CSS border属性"""
|
||||
border_radius: str = Field("8px", description="CSS border-radius属性")
|
||||
"""CSS border-radius属性"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/rectangle"
|
||||
@@ -0,0 +1,70 @@
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from ...registry import component
|
||||
from ..core.base import RenderableComponent
|
||||
|
||||
__all__ = ["Alert", "Badge", "ProgressBar"]
|
||||
|
||||
|
||||
@component(name="alert", namespace="core")
|
||||
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"
|
||||
|
||||
|
||||
class Badge(RenderableComponent):
|
||||
"""一个简单的徽章组件,用于显示状态或标签。"""
|
||||
|
||||
component_type: Literal["badge"] = "badge"
|
||||
"""组件类型"""
|
||||
text: str = Field(..., description="徽章上显示的文本")
|
||||
"""徽章上显示的文本"""
|
||||
color_scheme: Literal["primary", "success", "warning", "error", "info"] = Field(
|
||||
default="info",
|
||||
description="预设的颜色方案",
|
||||
)
|
||||
"""预设的颜色方案"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/badge"
|
||||
|
||||
|
||||
class ProgressBar(RenderableComponent):
|
||||
"""一个进度条组件。"""
|
||||
|
||||
component_type: Literal["progress_bar"] = "progress_bar"
|
||||
"""组件类型"""
|
||||
progress: float = Field(..., ge=0, le=100, description="进度百分比 (0-100)")
|
||||
"""进度百分比 (0-100)"""
|
||||
label: str | None = Field(default=None, description="显示在进度条上的可选文本")
|
||||
"""显示在进度条上的可选文本"""
|
||||
color_scheme: Literal["primary", "success", "warning", "error", "info"] = Field(
|
||||
default="primary",
|
||||
description="预设的颜色方案",
|
||||
)
|
||||
"""预设的颜色方案"""
|
||||
animated: bool = Field(default=False, description="是否显示动画效果")
|
||||
"""是否显示动画效果"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/progress_bar"
|
||||
@@ -1,28 +0,0 @@
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from ..core.base import RenderableComponent
|
||||
|
||||
__all__ = ["ProgressBar"]
|
||||
|
||||
|
||||
class ProgressBar(RenderableComponent):
|
||||
"""一个进度条组件。"""
|
||||
|
||||
component_type: Literal["progress_bar"] = "progress_bar"
|
||||
progress: float = Field(..., ge=0, le=100, description="进度百分比 (0-100)")
|
||||
"""进度百分比 (0-100)"""
|
||||
label: str | None = Field(default=None, description="显示在进度条上的可选文本")
|
||||
"""显示在进度条上的可选文本"""
|
||||
color_scheme: Literal["primary", "success", "warning", "error", "info"] = Field(
|
||||
default="primary",
|
||||
description="预设的颜色方案",
|
||||
)
|
||||
"""预设的颜色方案"""
|
||||
animated: bool = Field(default=False, description="是否显示动画效果")
|
||||
"""是否显示动画效果"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/progress_bar"
|
||||
@@ -1,36 +0,0 @@
|
||||
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路径")
|
||||
"""可选的自定义图标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"
|
||||
@@ -1,27 +0,0 @@
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from ..core.base import RenderableComponent
|
||||
|
||||
__all__ = ["UserInfoBlock"]
|
||||
|
||||
|
||||
class UserInfoBlock(RenderableComponent):
|
||||
"""一个带头像、名称和副标题的用户信息块组件。"""
|
||||
|
||||
component_type: Literal["user_info_block"] = "user_info_block"
|
||||
avatar_url: str = Field(..., description="用户头像的URL")
|
||||
"""用户头像的URL"""
|
||||
name: str = Field(..., description="用户的名称")
|
||||
"""用户的名称"""
|
||||
subtitle: str | None = Field(
|
||||
default=None, description="显示在名称下方的副标题 (如UID或角色)"
|
||||
)
|
||||
"""显示在名称下方的副标题 (如UID或角色)"""
|
||||
tags: list[str] = Field(default_factory=list, description="附加的标签列表")
|
||||
"""附加的标签列表"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
return "components/widgets/user_info_block"
|
||||
Reference in New Issue
Block a user