mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-05 20:00:04 +08:00
✨ feat(ui): 增强表格构建器并完善组件模型文档 (#2048)
检查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
检查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>
This commit is contained in:
co-authored by
webjoin111
pre-commit-ci[bot]
parent
fb0a9813e1
commit
c7ef6fdb17
@@ -24,6 +24,7 @@ from .markdown import (
|
||||
from .notebook import NotebookData, NotebookElement
|
||||
from .table import (
|
||||
BaseCell,
|
||||
ComponentCell,
|
||||
ImageCell,
|
||||
RichTextCell,
|
||||
StatusBadgeCell,
|
||||
@@ -38,6 +39,7 @@ __all__ = [
|
||||
"BaseCell",
|
||||
"CardData",
|
||||
"CodeElement",
|
||||
"ComponentCell",
|
||||
"DetailsData",
|
||||
"DetailsItem",
|
||||
"HeadingElement",
|
||||
|
||||
@@ -20,10 +20,15 @@ class RenderableComponent(BaseModel, Renderable):
|
||||
"""
|
||||
|
||||
_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:
|
||||
|
||||
@@ -7,8 +7,11 @@ class CardData(ContainerComponent):
|
||||
"""通用卡片的数据模型,可以包含头部、内容和尾部"""
|
||||
|
||||
header: RenderableComponent | None = None
|
||||
"""卡片的头部内容组件"""
|
||||
content: RenderableComponent
|
||||
"""卡片的主要内容组件"""
|
||||
footer: RenderableComponent | None = None
|
||||
"""卡片的尾部内容组件"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
|
||||
@@ -9,14 +9,18 @@ class DetailsItem(BaseModel):
|
||||
"""描述列表中的单个项目"""
|
||||
|
||||
label: str = Field(..., description="项目的标签/键")
|
||||
"""项目的标签/键"""
|
||||
value: Any = Field(..., description="项目的值")
|
||||
"""项目的值"""
|
||||
|
||||
|
||||
class DetailsData(RenderableComponent):
|
||||
"""描述列表(键值对)的数据模型"""
|
||||
|
||||
title: str | None = Field(None, description="列表的可选标题")
|
||||
"""列表的可选标题"""
|
||||
items: list[DetailsItem] = Field(default_factory=list, description="键值对项目列表")
|
||||
"""键值对项目列表"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
|
||||
@@ -12,20 +12,26 @@ class LayoutItem(BaseModel):
|
||||
"""布局中的单个项目,现在持有可渲染组件的数据模型"""
|
||||
|
||||
component: RenderableComponent = Field(..., description="要渲染的组件的数据模型")
|
||||
"""要渲染的组件的数据模型"""
|
||||
metadata: dict[str, Any] | None = Field(None, description="传递给模板的额外元数据")
|
||||
"""传递给模板的额外元数据"""
|
||||
|
||||
|
||||
class LayoutData(ContainerComponent):
|
||||
"""布局构建器的数据模型"""
|
||||
|
||||
style_name: str | None = None
|
||||
"""应用于布局容器的样式名称"""
|
||||
layout_type: str = "column"
|
||||
"""布局类型 (如 'column', 'row', 'grid')"""
|
||||
children: list[LayoutItem] = Field(
|
||||
default_factory=list, description="要布局的项目列表"
|
||||
)
|
||||
"""要布局的项目列表"""
|
||||
options: dict[str, Any] = Field(
|
||||
default_factory=dict, description="传递给模板的选项"
|
||||
)
|
||||
"""传递给模板的选项"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
|
||||
@@ -12,6 +12,7 @@ class ListItem(BaseModel):
|
||||
"""列表中的单个项目,其内容可以是任何可渲染组件。"""
|
||||
|
||||
component: RenderableComponent = Field(..., description="要渲染的组件的数据模型")
|
||||
"""要渲染的组件的数据模型"""
|
||||
|
||||
|
||||
class ListData(ContainerComponent):
|
||||
@@ -19,7 +20,9 @@ class ListData(ContainerComponent):
|
||||
|
||||
component_type: Literal["list"] = "list"
|
||||
items: list[ListItem] = Field(default_factory=list, description="列表项目")
|
||||
"""列表项目"""
|
||||
ordered: bool = Field(default=False, description="是否为有序列表")
|
||||
"""是否为有序列表"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
|
||||
@@ -44,7 +44,9 @@ class TextElement(MarkdownElement):
|
||||
class HeadingElement(MarkdownElement):
|
||||
type: Literal["heading"] = "heading"
|
||||
text: str
|
||||
level: int = Field(..., ge=1, le=6)
|
||||
"""标题文本"""
|
||||
level: int = Field(..., ge=1, le=6, description="标题级别 (1-6)")
|
||||
"""标题级别 (1-6)"""
|
||||
|
||||
def to_markdown(self) -> str:
|
||||
return f"{'#' * self.level} {self.text}"
|
||||
@@ -53,7 +55,9 @@ class HeadingElement(MarkdownElement):
|
||||
class ImageElement(MarkdownElement):
|
||||
type: Literal["image"] = "image"
|
||||
src: str
|
||||
"""图片来源 (URL或data URI)"""
|
||||
alt: str = "image"
|
||||
"""图片的替代文本"""
|
||||
|
||||
def to_markdown(self) -> str:
|
||||
return f""
|
||||
@@ -62,7 +66,9 @@ class ImageElement(MarkdownElement):
|
||||
class CodeElement(MarkdownElement):
|
||||
type: Literal["code"] = "code"
|
||||
code: str
|
||||
"""代码字符串"""
|
||||
language: str = ""
|
||||
"""代码语言,用于语法高亮"""
|
||||
|
||||
def to_markdown(self) -> str:
|
||||
return f"```{self.language}\n{self.code}\n```"
|
||||
@@ -71,6 +77,7 @@ class CodeElement(MarkdownElement):
|
||||
class RawHtmlElement(MarkdownElement):
|
||||
type: Literal["raw_html"] = "raw_html"
|
||||
html: str
|
||||
"""原始HTML字符串"""
|
||||
|
||||
def to_markdown(self) -> str:
|
||||
return self.html
|
||||
@@ -79,8 +86,11 @@ class RawHtmlElement(MarkdownElement):
|
||||
class TableElement(MarkdownElement):
|
||||
type: Literal["table"] = "table"
|
||||
headers: list[str]
|
||||
"""表格的表头列表"""
|
||||
rows: list[list[str]]
|
||||
"""表格的数据行列表"""
|
||||
alignments: list[Literal["left", "center", "right"]] | None = None
|
||||
"""每列的对齐方式"""
|
||||
|
||||
def to_markdown(self) -> str:
|
||||
header_row = "| " + " | ".join(self.headers) + " |"
|
||||
@@ -102,7 +112,10 @@ class TableElement(MarkdownElement):
|
||||
|
||||
|
||||
class ContainerElement(MarkdownElement):
|
||||
content: list[MarkdownElement] = Field(default_factory=list)
|
||||
content: list[MarkdownElement] = Field(
|
||||
default_factory=list, description="容器内包含的Markdown元素列表"
|
||||
)
|
||||
"""容器内包含的Markdown元素列表"""
|
||||
|
||||
|
||||
class QuoteElement(ContainerElement):
|
||||
@@ -121,6 +134,7 @@ class ListItemElement(ContainerElement):
|
||||
class ListElement(ContainerElement):
|
||||
type: Literal["list"] = "list"
|
||||
ordered: bool = False
|
||||
"""是否为有序列表 (例如 1., 2.)"""
|
||||
|
||||
def to_markdown(self) -> str:
|
||||
lines = []
|
||||
@@ -137,6 +151,7 @@ class ComponentElement(MarkdownElement):
|
||||
|
||||
type: Literal["component"] = "component"
|
||||
component: RenderableComponent
|
||||
"""嵌入在Markdown中的可渲染组件"""
|
||||
|
||||
def to_markdown(self) -> str:
|
||||
return ""
|
||||
@@ -146,9 +161,15 @@ class MarkdownData(ContainerComponent):
|
||||
"""Markdown转图片的数据模型"""
|
||||
|
||||
style_name: str | None = None
|
||||
elements: list[MarkdownElement] = Field(default_factory=list)
|
||||
"""Markdown内容的样式名称"""
|
||||
elements: list[MarkdownElement] = Field(
|
||||
default_factory=list, description="构成Markdown文档的元素列表"
|
||||
)
|
||||
"""构成Markdown文档的元素列表"""
|
||||
width: int = 800
|
||||
"""最终渲染图片的宽度"""
|
||||
css_path: str | None = None
|
||||
"""自定义CSS文件的绝对路径"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
@@ -180,7 +201,6 @@ class MarkdownData(ContainerComponent):
|
||||
logger.warning(f"Markdown自定义CSS文件不存在: {self.css_path}")
|
||||
else:
|
||||
style_name = self.style_name or "light"
|
||||
# 使用上下文对象来解析路径
|
||||
css_path = await context.theme_manager.resolve_markdown_style_path(
|
||||
style_name, context
|
||||
)
|
||||
|
||||
@@ -22,21 +22,32 @@ class NotebookElement(BaseModel):
|
||||
"component",
|
||||
]
|
||||
text: str | None = None
|
||||
"""元素的文本内容 (用于标题、段落、引用)"""
|
||||
level: int | None = None
|
||||
"""标题的级别 (1-4)"""
|
||||
src: str | None = None
|
||||
"""图片的来源 (URL或data URI)"""
|
||||
caption: str | None = None
|
||||
"""图片的说明文字"""
|
||||
code: str | None = None
|
||||
"""代码块的内容"""
|
||||
language: str | None = None
|
||||
"""代码块的语言"""
|
||||
data: list[str] | None = None
|
||||
"""列表项的内容列表"""
|
||||
ordered: bool | None = None
|
||||
"""是否为有序列表"""
|
||||
component: RenderableComponent | None = None
|
||||
"""嵌入的自定义可渲染组件"""
|
||||
|
||||
|
||||
class NotebookData(ContainerComponent):
|
||||
"""Notebook转图片的数据模型"""
|
||||
|
||||
style_name: str | None = None
|
||||
"""Notebook的样式名称"""
|
||||
elements: list[NotebookElement]
|
||||
"""构成Notebook页面的元素列表"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
|
||||
from nonebot.compat import field_validator
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from ...models.components.progress_bar import ProgressBar
|
||||
from .base import RenderableComponent
|
||||
@@ -10,6 +8,7 @@ from .text import TextSpan
|
||||
|
||||
__all__ = [
|
||||
"BaseCell",
|
||||
"ComponentCell",
|
||||
"ImageCell",
|
||||
"ProgressBarCell",
|
||||
"RichTextCell",
|
||||
@@ -30,7 +29,7 @@ class TextCell(BaseCell):
|
||||
"""文本单元格"""
|
||||
|
||||
type: Literal["text"] = "text" # type: ignore
|
||||
content: str | float
|
||||
content: str
|
||||
bold: bool = False
|
||||
color: str | None = None
|
||||
|
||||
@@ -39,18 +38,12 @@ class ImageCell(BaseCell):
|
||||
"""图片单元格"""
|
||||
|
||||
type: Literal["image"] = "image" # type: ignore
|
||||
src: str | Path
|
||||
src: str
|
||||
width: int = 40
|
||||
height: int = 40
|
||||
shape: Literal["square", "circle"] = "square"
|
||||
alt: str = "image"
|
||||
|
||||
@field_validator("src", mode="before")
|
||||
def validate_src(cls, v: str) -> str:
|
||||
if isinstance(v, Path):
|
||||
v = v.resolve().as_uri()
|
||||
return v
|
||||
|
||||
|
||||
class StatusBadgeCell(BaseCell):
|
||||
"""状态徽章单元格"""
|
||||
@@ -70,20 +63,28 @@ class RichTextCell(BaseCell):
|
||||
"""富文本单元格,支持多个带样式的文本片段"""
|
||||
|
||||
type: Literal["rich_text"] = "rich_text" # type: ignore
|
||||
spans: list[TextSpan] = []
|
||||
spans: list[TextSpan] = Field(default_factory=list, description="文本片段列表")
|
||||
"""文本片段列表"""
|
||||
direction: Literal["column", "row"] = "column"
|
||||
direction: Literal["column", "row"] = Field("column", description="片段排列方向")
|
||||
"""片段排列方向"""
|
||||
gap: str = "4px"
|
||||
gap: str = Field("4px", description="片段之间的间距")
|
||||
"""片段之间的间距"""
|
||||
|
||||
|
||||
class ComponentCell(BaseCell):
|
||||
"""一个通用的单元格,可以容纳任何可渲染的组件。"""
|
||||
|
||||
type: str = "component"
|
||||
component: RenderableComponent
|
||||
|
||||
|
||||
TableCell = (
|
||||
TextCell
|
||||
| ImageCell
|
||||
| StatusBadgeCell
|
||||
| ProgressBarCell
|
||||
| RichTextCell
|
||||
| ComponentCell
|
||||
| str
|
||||
| int
|
||||
| float
|
||||
@@ -95,17 +96,22 @@ class TableData(RenderableComponent):
|
||||
"""通用表格的数据模型"""
|
||||
|
||||
style_name: str | None = None
|
||||
title: str
|
||||
"""应用于表格容器的样式名称"""
|
||||
title: str = Field(..., description="表格主标题")
|
||||
"""表格主标题"""
|
||||
tip: str | None = None
|
||||
tip: str | None = Field(None, description="表格下方的提示信息")
|
||||
"""表格下方的提示信息"""
|
||||
headers: list[str] = [] # noqa: RUF012
|
||||
headers: list[str] = Field(default_factory=list, description="表头列表")
|
||||
"""表头列表"""
|
||||
rows: list[list[TableCell]] = [] # noqa: RUF012
|
||||
rows: list[list[TableCell]] = Field(default_factory=list, description="数据行列表")
|
||||
"""数据行列表"""
|
||||
column_alignments: list[Literal["left", "center", "right"]] | None = None
|
||||
column_alignments: list[Literal["left", "center", "right"]] | None = Field(
|
||||
default=None, description="每列的对齐方式"
|
||||
)
|
||||
"""每列的对齐方式"""
|
||||
column_widths: list[str | int] | None = None
|
||||
column_widths: list[str | int] | None = Field(
|
||||
default=None, description="每列的宽度 (e.g., ['50px', 'auto', 100])"
|
||||
)
|
||||
"""每列的宽度 (e.g., ['50px', 'auto', 100])"""
|
||||
|
||||
@property
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import RenderableComponent
|
||||
|
||||
__all__ = ["TemplateComponent"]
|
||||
@@ -10,8 +12,11 @@ class TemplateComponent(RenderableComponent):
|
||||
"""基于独立模板文件的UI组件"""
|
||||
|
||||
_is_standalone_template: bool = True
|
||||
template_path: str | Path
|
||||
data: dict[str, Any]
|
||||
"""标记此组件为独立模板"""
|
||||
template_path: str | Path = Field(..., description="指向HTML模板文件的路径")
|
||||
"""指向HTML模板文件的路径"""
|
||||
data: dict[str, Any] = Field(..., description="传递给模板的上下文数据字典")
|
||||
"""传递给模板的上下文数据字典"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
|
||||
@@ -23,9 +23,11 @@ class TextData(RenderableComponent):
|
||||
"""轻量级富文本组件的数据模型"""
|
||||
|
||||
spans: list[TextSpan] = Field(default_factory=list, description="文本片段列表")
|
||||
"""文本片段列表"""
|
||||
align: Literal["left", "right", "center"] = Field(
|
||||
"left", description="整体文本对齐方式"
|
||||
)
|
||||
"""整体文本对齐方式"""
|
||||
|
||||
@property
|
||||
def template_name(self) -> str:
|
||||
|
||||
Reference in New Issue
Block a user