Files
ThearchyHelios 277982ead3 普通用户建应用(后端):
- 蓝图:bp_* 操作和逐卡白名单、蓝图检查、留痕、按蓝图判权限;五个场景模板加自由形(骨架、关节、应用骨架、定时任务),实例化与骨架比对;BlueprintApplier / TemplateApplier(只收模板声明过的关节)与自查;look 新类别口径卡;蓝图对话和填关节两个流式接口,不同组的关节并行填、出错的组串行重试;模板列表
- 试运行与发布单:四种起点(对话、模板、问数据的一轮、已发布版本);试运行是探索运行,草稿应用按这次运行解析、印章写试运行,流程类派给自己、不触发升级;发布单九步、预检只读、每步留痕、停下后接着做、核对卡片指纹和应用管理权、启动时整理中断的单;白话层覆盖全部问题编号
- 记录表与文件:平台托管只追加的记录表、按高水位固定数据、连接上的临时视图做行级可见、写入走动作(records_append)、表版本在发布时切换、草稿不影响线上、记录列表遮敏感列;文件按哈希存、两步上传、提交时绑定运行、下载按读权限、清理、file_text、按配方导入一期(table_import_period)
- 个人应用:存成个人应用(问数据的约束)、刷新(遮罩、只读、每日上限)、只有拥有者看得见、提交审核与共享副本、开关下发到认人接口;页面编辑的候选;隐藏组件不下发;三条新检查
2026-10-15 12:09:29 +08:00

127 lines
9.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""受信组件目录 v1(docs/design/app-platform-and-look.md §2.2、§3.5)。
一份目录,四个使用方:应用页、人工节点表单、agent 拟的询问、助手生成。模型只能从这里挑组件;前端只有一张
「名字 → 渲染器」的表,遇到不认识的组件显示「此组件需要更新前端」,不报错、不空白。
每个组件登记:名字、中文名、类别、版本、props 的 JSON Schema、允许的绑定种类、能不能有子组件。
props 里每一项都写 title(属性面板上的中文名),取值不直观的另写 description;枚举写 x-enum-labels(和 enum 按位置
对应,同表单 agentlab.form/1 的写法);给人看的文字(标题、名称、说明)标 x-text,应用页上直接改的「改文字」只改这几项,
列名、换算这类写给解析器的字符串不标。这几样是注解,不参与校验。
服务端校验器和解析器由 app/apps/ 的实现按名字注册(BE-3);开发者扩展组件时,目录项、校验器、解析器、前端渲染器
四样都要给,缺一样启动即报错(check_registry)。
"""
from __future__ import annotations
from typing import Any
CATEGORIES = ("layout", "display", "input", "process")
CATEGORY_LABEL = {"layout": "布局", "display": "展示", "input": "输入", "process": "流程"}
def _props(**properties: Any) -> dict[str, Any]:
return {"type": "object", "additionalProperties": False, "properties": properties}
def _text(title: str, **more: Any) -> dict[str, Any]:
"""给人看的一行字(标题、名称、单位):x-text 标出来,页面上直接改的「改文字」改它(normal-user-apps N6)。"""
return {"type": "string", "maxLength": 200, "title": title, "x-text": True, **more}
def _long(title: str) -> dict[str, Any]:
return {"type": "string", "maxLength": 4000, "title": title, "x-text": True}
def _code(title: str, **more: Any) -> dict[str, Any]:
"""写给解析器的短字符串(列名、换算):不是给人看的文字,「改文字」不碰。"""
return {"type": "string", "maxLength": 200, "title": title, **more}
def _enum(title: str, **labels: str) -> dict[str, Any]:
"""枚举:值是键,中文是 x-enum-labels(按位置对应)。"""
return {"enum": list(labels), "x-enum-labels": list(labels.values()), "title": title}
_CHART = _enum("图形", line="折线图", bar="柱状图")
COMPONENTS: list[dict[str, Any]] = [
# ---- 布局 ----
{"name": "page", "label": "页面", "category": "layout", "version": 1, "children": True, "bindings": [],
"props": _props(title=_text("标题"))},
{"name": "section", "label": "分区", "category": "layout", "version": 1, "children": True, "bindings": [],
"props": _props(title=_text("标题"), description=_long("说明"))},
{"name": "columns", "label": "分栏", "category": "layout", "version": 1, "children": True, "bindings": [],
"props": _props(count={"type": "integer", "minimum": 1, "maximum": 4, "title": "列数"})},
{"name": "tabs", "label": "页签", "category": "layout", "version": 1, "children": True, "bindings": [],
"props": _props(labels={"type": "array", "items": {"type": "string", "maxLength": 200}, "maxItems": 8,
"title": "页签名", "description": "按顺序对应里面的子组件"})},
{"name": "divider", "label": "分隔线", "category": "layout", "version": 1, "children": False, "bindings": [],
"props": _props()},
# ---- 展示 ----
{"name": "stamp", "label": "印章", "category": "display", "version": 1, "children": False,
"bindings": ["run"], "props": _props(compact={"type": "boolean", "title": "紧凑显示"})},
{"name": "metric", "label": "指标块", "category": "display", "version": 1, "children": False,
"bindings": ["metric", "cell"],
"props": _props(label=_text("名称"),
unit=_text("单位", description="绑定单元格时写在数下面;绑定指标时数里已经带着口径卡的单位"),
conv=_code("换算", description="万、亿、pct(百分比)、int(取整)或 .N(保留 N 位小数)"))},
{"name": "series", "label": "趋势", "category": "display", "version": 1, "children": False,
"bindings": ["series"], "props": _props(label=_text("名称"), chart=_CHART)},
{"name": "table", "label": "表格", "category": "display", "version": 1, "children": False,
"bindings": ["query"],
"props": _props(title=_text("标题"),
columns={"type": "array", "items": {"type": "string", "maxLength": 200}, "title": "显示的列",
"description": "列名,不填显示全部列"},
sort={"type": "string", "title": "排序列", "description": "按哪一列排序,写列名;前面加「-」为降序"},
limit={"type": "integer", "minimum": 1, "title": "最多显示行数"})},
{"name": "chart", "label": "图表", "category": "display", "version": 1, "children": False,
"bindings": ["query"],
"props": _props(title=_text("标题"), chart=_CHART, x=_code("横轴列"),
y={"type": "array", "items": {"type": "string", "maxLength": 200}, "maxItems": 4,
"title": "纵轴列", "description": "最多 4 列"})},
{"name": "report", "label": "报告", "category": "display", "version": 1, "children": False,
"bindings": ["report"], "props": _props(title=_text("标题"))},
{"name": "text", "label": "文字", "category": "display", "version": 1, "children": False, "bindings": [],
"props": _props(markdown=_long("正文(Markdown)"))},
{"name": "notice", "label": "提示", "category": "display", "version": 1, "children": False, "bindings": [],
"props": _props(text=_long("提示文字"), tone=_enum("样式", info="说明", warn="提醒", error="错误"))},
{"name": "field", "label": "字段", "category": "display", "version": 1, "children": False,
"bindings": ["input", "output", "run"], "props": _props(label=_text("名称"))},
{"name": "status", "label": "状态", "category": "display", "version": 1, "children": False,
"bindings": ["run"], "props": _props(label=_text("名称"))},
# ---- 输入 ----
{"name": "form", "label": "表单", "category": "input", "version": 1, "children": False, "bindings": [],
"props": _props(form={"type": "string", "title": "表单"}, submit_label=_text("提交按钮文字"))},
# ---- 流程 ----
{"name": "entry_button", "label": "发起按钮", "category": "process", "version": 1, "children": False,
"bindings": [], "props": _props(entry={"type": "string", "title": "入口"}, label=_text("按钮文字"))},
{"name": "instance_list", "label": "实例列表", "category": "process", "version": 1, "children": False,
"bindings": [], "props": _props(entry={"type": "string", "title": "入口"},
who=_enum("显示谁的", mine="我发起的", all="全部实例"),
limit={"type": "integer", "minimum": 1, "maximum": 200, "title": "最多显示几条"})},
{"name": "instance_header", "label": "实例概况", "category": "process", "version": 1, "children": False,
"bindings": [], "props": _props()},
{"name": "instance_timeline", "label": "实例进度", "category": "process", "version": 1, "children": False,
"bindings": [], "props": _props()},
{"name": "task_list", "label": "待办列表", "category": "process", "version": 1, "children": False,
"bindings": [], "props": _props(scope=_enum("范围", mine="派给我的", instance="本实例的"))},
{"name": "task_form", "label": "待办表单", "category": "process", "version": 1, "children": False,
"bindings": ["task"], "props": _props()},
{"name": "signoffs", "label": "会签进度", "category": "process", "version": 1, "children": False,
"bindings": ["task"], "props": _props()},
{"name": "action_status", "label": "动作状态", "category": "process", "version": 1, "children": False,
"bindings": ["action"], "props": _props(label=_text("名称"))},
# 记录表的逐行(docs/design/normal-user-apps.md N8):每次打开按看的人实时过滤(use 只看自己提交的),不走封存
{"name": "records_list", "label": "记录列表", "category": "process", "version": 1, "children": False,
"bindings": [], "props": _props(title=_text("标题"),
table=_code("记录表", description="不填就是这个应用的第一张记录表"),
who=_enum("显示谁的", mine="我提交的", all="全部(要有查看全部的权限)"),
limit={"type": "integer", "minimum": 1, "maximum": 200, "title": "最多显示几条"})},
]
BY_NAME: dict[str, dict[str, Any]] = {c["name"]: c for c in COMPONENTS}
def catalog_payload() -> dict[str, Any]:
"""GET /api/apps/catalog 的返回:组件目录 + 类别名。助手生成时也给模型看这一份(只给名字、中文名、props、绑定)。"""
return {"version": 1, "categories": CATEGORY_LABEL, "components": COMPONENTS}