✨ feat(page-template): 添加页面模板服务,支持前端布局组件和数据提交处理

- 新增页面模板服务模块,提供页面模板配置、字段定义和数据验证功能。
- 实现了前端布局组件模型,包括行、列、文本、按钮、卡片等,支持灵活的页面布局。
- 引入 FastAPI 路由,提供统一的API接口以获取模板配置和处理数据提交。
- 注册用户表单模板示例,包含提交、重置和取消按钮的功能。
- 增强了数据验证和处理逻辑,确保提交数据的有效性和安全性。
This commit is contained in:
HibiKier
2025-12-23 14:31:17 +08:00
parent c9f0a8b9d9
commit 4c082e9f07
7 changed files with 874 additions and 0 deletions
@@ -0,0 +1,207 @@
from enum import Enum
from typing import Any
from pydantic import BaseModel
class ComponentType(str, Enum):
"""组件类型,名称与前端 tag 保持一致"""
ROW = "row"
COL = "col"
TEXT = "text"
BUTTON = "button"
CARD = "card"
DIVIDER = "divider"
SPACE = "space"
FORM = "form"
FORM_ITEM = "form_item"
TABLE = "table"
class RowProps(BaseModel):
"""行组件属性"""
gutter: int | None = None # 行间距
justify: str | None = None # 主轴对齐方式
align: str | None = None # 交叉轴对齐方式
class ColProps(BaseModel):
"""列组件属性"""
span: int | None = None # 栅格占比
offset: int | None = None # 左侧偏移
push: int | None = None # 向右移动
pull: int | None = None # 向左移动
class TextProps(BaseModel):
"""文本组件属性"""
content: str = "" # 文本内容
tag: str | None = None # HTML 标签,如 h1/h2/p/span
type: str | None = None # 文本类型,对应 el-text 的 type
size: str | None = None # 文本大小
truncated: bool = False # 是否截断
line_clamp: int | None = None # 最多显示行数
class ButtonProps(BaseModel):
"""按钮组件属性"""
text: str # 按钮文本(必填)
type: str | None = None # 按钮类型 primary/success/warning/danger/info/default
size: str | None = None # 按钮尺寸 large/default/small
plain: bool = False # 朴素按钮
round: bool = False # 圆角按钮
circle: bool = False # 圆形按钮
link: bool = False # 文字按钮
icon: str | None = None # 图标名称
action: str | None = None # 按钮行为:submit/reset/cancel/custom
confirm: bool = False # 是否需要二次确认
confirm_text: str | None = None # 确认提示文案
api: str | None = None # 自定义调用的后端 API(action=custom 时使用)
api_method: str = "POST" # 自定义 API 的 HTTP 方法
class CardProps(BaseModel):
"""卡片组件属性"""
header: str | None = None # 卡片标题
shadow: str | None = None # 阴影类型
body_style: dict[str, Any] | None = None # 卡片主体样式
class FormProps(BaseModel):
"""表单组件属性"""
label_width: str | None = None # 标签宽度
inline: bool = False # 行内表单
size: str | None = None # 表单尺寸
class FormItemProps(BaseModel):
"""表单项组件属性"""
label: str | None = None # 标签文本
prop: str | None = None # 绑定字段名
required: bool = False # 是否必填
class TableProps(BaseModel):
"""表格组件属性"""
columns: list[dict[str, Any]] | None = None # 列配置
data: list[dict[str, Any]] | None = None # 数据源
class BaseComponent(BaseModel):
# 这些字段在 BaseModel 中有默认值,调用时都不是必传
# 用 Any 以便可以直接传 TextProps / ButtonProps / FormProps 等模型实例
props: Any | None
# 用 Any 避免 list 协变问题
children: list[Any] | None = None
class Config:
arbitrary_types_allowed = True
def __init__(self, **data: Any):
super().__init__(**data)
_validate_children_impl(self.__dict__)
def _validate_children_impl(values: dict[str, Any]) -> dict[str, Any]:
"""
限定哪些组件可以拥有 children:
- 允许 children: row, col, card, space, form, form_item, table
- 不允许 children: text, button, divider
"""
t = values.get("type")
children = values.get("children") or []
no_children = {
ComponentType.TEXT.value,
ComponentType.BUTTON.value,
ComponentType.DIVIDER.value,
}
if t in no_children and children:
raise ValueError(f"组件 '{t}' 不允许包含 children")
return values
class Row(BaseComponent):
"""行组件(对应 el-row)"""
type = ComponentType.ROW.value
props: RowProps # 必填
class Col(BaseComponent):
"""列组件(对应 el-col)"""
type = ComponentType.COL.value
props: ColProps # 必填
class Text(BaseComponent):
"""文本组件(对应 el-text 或基础标签)"""
type = ComponentType.TEXT.value
props: TextProps # 必填
class Button(BaseComponent):
"""按钮组件(对应 el-button)"""
type = ComponentType.BUTTON.value
props: ButtonProps # 必填
class Card(BaseComponent):
"""卡片组件(对应 el-card)"""
type = ComponentType.CARD.value
props: CardProps # 必填
class Divider(BaseComponent):
"""分割线组件(对应 el-divider)"""
type = ComponentType.DIVIDER.value
class Space(BaseComponent):
"""间距组件(对应 el-space)"""
type = ComponentType.SPACE.value
class Form(BaseComponent):
"""表单组件(对应 el-form)"""
type = ComponentType.FORM.value
props: FormProps # 必填
class FormItem(BaseComponent):
"""表单项组件(对应 el-form-item)"""
type = ComponentType.FORM_ITEM.value
props: FormItemProps # 必填
children: list[Any] | None = None
bind_field: str | None = None
class Table(BaseComponent):
"""表格组件(对应 el-table)"""
type = ComponentType.TABLE.value
Component = Row | Col | Text | Button | Card | Divider | Space | Form | FormItem | Table
try:
BaseComponent.model_rebuild()
except AttributeError:
BaseComponent.update_forward_refs()