59 lines
3.0 KiB
Python
59 lines
3.0 KiB
Python
"""新增组件动作"""
|
|
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import Field, model_validator
|
|
|
|
from app.schemas.actions.base import ActionModel
|
|
from app.schemas.screen import ScreenNode, json_equal
|
|
|
|
|
|
class AddAction(ActionModel):
|
|
"""按物料新增组件"""
|
|
|
|
type: Literal["add"] = "add"
|
|
node: ScreenNode = Field(
|
|
description="新组件。id、type、layout 必填,其余只写需要覆盖物料默认配置的字段;"
|
|
"显示条件、数据筛选、事件(visibleWhen、filters、events)也写在 node 里,不写在动作这一层,也可以之后用 set_interaction 设置;"
|
|
"后端按 type 找到物料,用默认配置补全:对象逐层覆盖,数组整体替换,null 明确覆盖。"
|
|
"写出的字段和 update 一样按字段定义检查类型和取值(跟随主题的颜色可以写 null)。"
|
|
"id 不能与当前大屏上的组件重复(包括之前提交里新增的),先 delete 的 ID 可以再用;type 用 list_components 返回的 type。"
|
|
'任何字段值都可以写成 {"$query": "SELECT ..."},后端替换成上传数据的查询结果:写入单个值的字段时结果要一行一列,取那个值;写入数字、文字数组的字段时结果要只有一列;其余是对象数组,键是结果的列名'
|
|
)
|
|
|
|
@model_validator(mode="before")
|
|
@classmethod
|
|
def node_fields_into_node(cls, data: Any) -> Any:
|
|
"""动作这一层写了节点字段时挪进 node:模型常照 set_interaction 的样子把 visibleWhen、filters、events
|
|
写在 node 旁边,严格校验只报"Extra inputs are not permitted",它看不懂,反复提交直到放弃。
|
|
按 ScreenNode 的字段判断,不列举;交付的动作里这些字段只在 node 里,前端协议不变;其他未知键照样报错
|
|
"""
|
|
if not isinstance(data, dict) or not isinstance(data.get("node"), dict):
|
|
return data
|
|
misplaced = [
|
|
key
|
|
for key in data
|
|
if key in ScreenNode.model_fields and key not in cls.model_fields
|
|
]
|
|
if not misplaced:
|
|
return data
|
|
data = dict(data)
|
|
node = dict(data["node"])
|
|
for key in misplaced:
|
|
value = data.pop(key)
|
|
# 按 JSON 比较:Python 的 != 把 true 和 1 当成一样,会让外层的值静默覆盖
|
|
if key not in node:
|
|
node[key] = value
|
|
elif not json_equal(node[key], value):
|
|
raise ValueError(
|
|
f"{key} 是组件的字段,同时写在 node 里和动作这一层且值不同;只写在 node 里"
|
|
)
|
|
data["node"] = node
|
|
return data
|
|
|
|
sample_data: bool = Field(
|
|
default=False,
|
|
description="true 表示组件展示的是自己编写的数据(不是来自上传的文件或大屏上已有的组件),交付时标为示例数据。"
|
|
"组件上留着物料示例数据时后端会自动标为示例数据,不用写",
|
|
)
|