mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-14 21:52:56 +08:00
Some checks failed
检查bot是否运行正常 / bot check (push) Has been cancelled
CodeQL Code Security Analysis / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
CodeQL Code Security Analysis / Analyze (${{ matrix.language }}) (none, python) (push) Has been cancelled
Sequential Lint and Type Check / ruff-call (push) Has been cancelled
Release Drafter / Update Release Draft (push) Has been cancelled
Force Sync to Aliyun / sync (push) Has been cancelled
Update Version / update-version (push) Has been cancelled
Sequential Lint and Type Check / pyright-call (push) Has been cancelled
* ✨ feat(table): 添加 ComponentCell 以支持表格单元格中嵌入可渲染组件 * ✨ feat(ui): 增强表格构建器并完善组件模型文档 - 增强 `TableBuilder`,新增 `_normalize_cell` 辅助方法,支持自动将原生数据类型(如 `str`, `int`, `Path`)转换为 `TableCell` 模型,简化了表格行的创建。 - 完善 `zhenxun/ui/models` 目录下所有组件模型字段的 `description` 属性和文档字符串,显著提升了代码可读性和开发者体验。 - 优化 `shop/_data_source.py` 中 `gold_rank` 函数的平台路径判断格式,并统一 `my_props` 函数中图标路径的处理逻辑。 * 🚨 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>
103 lines
3.6 KiB
Python
103 lines
3.6 KiB
Python
from abc import ABC, abstractmethod
|
||
from collections.abc import Awaitable, Iterable
|
||
from typing import Any
|
||
|
||
from pydantic import BaseModel
|
||
|
||
from zhenxun.services.renderer.protocols import Renderable
|
||
from zhenxun.utils.pydantic_compat import compat_computed_field, model_dump
|
||
|
||
__all__ = ["ContainerComponent", "RenderableComponent"]
|
||
|
||
|
||
class RenderableComponent(BaseModel, Renderable):
|
||
"""
|
||
所有可渲染UI组件的数据模型基类。
|
||
|
||
它继承自 Pydantic 的 `BaseModel` 用于数据校验和结构化,同时实现了 `Renderable`
|
||
协议,确保其能够被 `RendererService` 正确处理。
|
||
它还提供了一些所有组件通用的样式属性,如 `inline_style`, `variant` 等。
|
||
"""
|
||
|
||
_is_standalone_template: bool = False
|
||
"""标记此组件是否为独立模板"""
|
||
inline_style: dict[str, str] | None = None
|
||
"""应用于组件根元素的内联CSS样式"""
|
||
component_css: str | None = None
|
||
"""注入到页面的额外CSS字符串"""
|
||
extra_classes: list[str] | None = None
|
||
"""应用于组件根元素的额外CSS类名列表"""
|
||
variant: str | None = None
|
||
"""组件的变体/皮肤名称"""
|
||
|
||
@property
|
||
def template_name(self) -> str:
|
||
"""
|
||
返回用于渲染此组件的Jinja2模板的路径。
|
||
这是一个抽象属性,所有子类都必须覆盖它。
|
||
"""
|
||
raise NotImplementedError(
|
||
"Subclasses must implement the 'template_name' property."
|
||
)
|
||
|
||
async def prepare(self) -> None:
|
||
"""[可选] 生命周期钩子,默认无操作。"""
|
||
pass
|
||
|
||
def get_children(self) -> Iterable["RenderableComponent"]:
|
||
"""默认实现:非容器组件没有子组件。"""
|
||
return []
|
||
|
||
def get_required_scripts(self) -> list[str]:
|
||
"""[可选] 返回此组件所需的JS脚本路径列表 (相对于assets目录)。"""
|
||
return []
|
||
|
||
def get_required_styles(self) -> list[str]:
|
||
"""[可选] 返回此组件所需的CSS样式表路径列表 (相对于assets目录)。"""
|
||
return []
|
||
|
||
def get_render_data(self) -> dict[str, Any | Awaitable[Any]]:
|
||
"""默认实现,返回模型自身的数据字典。"""
|
||
return model_dump(
|
||
self, exclude={"inline_style", "component_css", "inline_style_str"}
|
||
)
|
||
|
||
@compat_computed_field
|
||
def inline_style_str(self) -> str:
|
||
"""[新增] 一个辅助属性,将内联样式字典转换为CSS字符串"""
|
||
if not self.inline_style:
|
||
return ""
|
||
return "; ".join(f"{k}: {v}" for k, v in self.inline_style.items())
|
||
|
||
def get_extra_css(self, context: Any) -> str | Awaitable[str]:
|
||
return ""
|
||
|
||
|
||
class ContainerComponent(RenderableComponent, ABC):
|
||
"""
|
||
一个为容器类组件设计的抽象基类,封装了预渲染子组件的通用逻辑。
|
||
"""
|
||
|
||
@abstractmethod
|
||
def get_children(self) -> Iterable[RenderableComponent]:
|
||
"""
|
||
一个抽象方法,子类必须实现它来返回一个可迭代的子组件。
|
||
"""
|
||
raise NotImplementedError
|
||
|
||
def get_required_scripts(self) -> list[str]:
|
||
"""[新增] 聚合所有子组件的脚本依赖。"""
|
||
scripts = set(super().get_required_scripts())
|
||
for child in self.get_children():
|
||
if child:
|
||
scripts.update(child.get_required_scripts())
|
||
return list(scripts)
|
||
|
||
def get_required_styles(self) -> list[str]:
|
||
"""[新增] 聚合所有子组件的样式依赖。"""
|
||
styles = set(super().get_required_styles())
|
||
for child in self.get_children():
|
||
if child:
|
||
styles.update(child.get_required_styles())
|
||
return list(styles)
|