feat:新增物料

This commit is contained in:
2026-10-13 04:16:00 -07:00
parent b117f21e3a
commit a80727b7a0
15 changed files with 174 additions and 54 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ class AddAction(ActionModel):
description="新组件。id、type、layout 必填,其余只写需要覆盖物料默认配置的字段;"
"后端按 type 找到物料,用默认配置补全:对象逐层覆盖,数组整体替换,null 明确覆盖。"
"id 不能与当前大屏上的组件重复(包括之前提交里新增的),先 delete 的 ID 可以再用;type 用 list_components 返回的 type。"
'任何字段值都可以写成 {"$query": "SELECT ..."},后端替换成上传数据的查询结果(对象数组,键是结果的列名)'
'任何字段值都可以写成 {"$query": "SELECT ..."},后端替换成上传数据的查询结果:写入单个值的字段时结果要一行一列,取那个值;写入数字、文字数组的字段时结果要只有一列;其余是对象数组,键是结果的列名'
)
sample_data: bool = Field(
default=False,
+1 -1
View File
@@ -23,7 +23,7 @@ class UpdateAction(BaseAction):
)
value: JsonValue = Field(
description="替换该字段的值,必须符合字段定义的类型和取值;"
'可以写成 {"$query": "SELECT ..."},后端替换成上传数据的查询结果(对象数组,键是结果的列名)'
'可以写成 {"$query": "SELECT ..."},后端替换成上传数据的查询结果:写入单个值的字段时结果要一行一列,取那个值;写入数字、文字数组的字段时结果要只有一列;其余是对象数组,键是结果的列名'
)
@model_validator(mode="after")
+2 -1
View File
@@ -15,5 +15,6 @@ class UpdateCanvasAction(ActionModel):
type: Literal["update_canvas"] = "update_canvas"
properties: dict[NonBlankString, JsonValue] = Field(
min_length=1,
description="要替换的 canvas 顶层字段及最终值,如 width、height、backgroundColor;未提供的字段保持不变。改变画布尺寸不会自动缩放或移动组件",
description="要替换的 canvas 顶层字段及最终值,如 width、height、theme(light 亮色 / dark 深色)、backgroundColor;未提供的字段保持不变。"
"改 theme 会让组件里跟随主题的颜色一起变;backgroundColor 写 null 表示用主题的背景色。改变画布尺寸不会自动缩放或移动组件",
)
+4 -1
View File
@@ -74,11 +74,14 @@ class ComponentDefinition(ExtensibleModel):
def _default_problem(field, template: JsonObject) -> str | None:
"""默认值只有一个来源:默认配置有该路径时取它,没有时字段必须声明 default"""
"""默认值只有一个来源:默认配置有该路径时取它,没有时字段必须声明 default;
跟随主题的颜色由前端按画布主题取,默认配置里不能写死"""
try:
current = lookup(template, field.segments)
except ValueError:
return "的路径与默认配置结构冲突"
if field.themed:
return None if current is MISSING else "跟随主题,默认配置里不能写死颜色"
if current is MISSING:
if field.default is None:
return "在默认配置中不存在,需要声明 default"
+11 -1
View File
@@ -20,10 +20,11 @@ ValueType = Literal[
ItemType = Literal["string", "number", "integer", "boolean", "color", "object"]
# 值类型对应的前端属性控件;array / object 没有通用控件,只能不在面板显示
# number 可以输入小数,integer 只能输入整数
PANEL_CONTROLS: dict[str, str] = {
"string": "input",
"number": "number",
"integer": "number",
"integer": "integer",
"boolean": "checkbox",
"color": "color",
"enum": "select",
@@ -101,6 +102,10 @@ class MaterialField(ExtensibleModel):
aliases: list[NonBlankString] = Field(
default_factory=list, description="用户可能的叫法,供字段搜索使用"
)
themed: bool = Field(
default=False,
description="颜色跟随画布主题:默认配置里不写值,由前端按画布主题(亮 / 暗)取色;写入 null 恢复跟随",
)
editable: bool = Field(default=True, description="是否允许修改")
panel: bool = Field(default=True, description="是否显示在前端属性面板")
span: int | None = Field(default=None, description="属性面板栅格宽度,24 为整行")
@@ -115,10 +120,15 @@ class MaterialField(ExtensibleModel):
raise ValueError(f"只有 array 字段提供 item_type,且 array 必须提供: {self.path}")
if self.panel and self.value_type not in PANEL_CONTROLS:
raise ValueError(f"{self.value_type} 字段没有属性面板控件,panel 必须为 false: {self.path}")
if self.themed and (self.value_type != "color" or self.default is not None):
raise ValueError(f"只有 color 字段可以跟随主题,且不能声明 default: {self.path}")
return self
def value_error(self, value: JsonValue) -> str | None:
"""检查值是否符合字段类型和取值约束,符合返回 None"""
# 跟随主题的颜色写 null 表示去掉单独设置的颜色、恢复跟随
if self.themed and value is None:
return None
if self.value_type == "enum":
allowed = [option.value for option in self.options]
return None if value in allowed else f"取值必须是 {allowed} 之一"
+8 -1
View File
@@ -1,6 +1,6 @@
"""大屏公共结构;组件属性、事件与数据源配置保持开放"""
from typing import Annotated, Self
from typing import Annotated, Literal, Self
from pydantic import Field, model_validator
@@ -13,6 +13,13 @@ PositiveNumber = Annotated[Number, Field(gt=0)]
class Canvas(ExtensibleModel):
width: PositiveNumber
height: PositiveNumber
theme: Literal["light", "dark"] | None = Field(
default=None,
description="画布主题:组件里没单独设置的文字、坐标轴、分隔线、卡片底等颜色按主题取;旧大屏没有时前端按背景色推断",
)
backgroundColor: str | None = Field(
default=None, description="画布背景色;为 null 时用主题的背景色"
)
class Layout(ExtensibleModel):