全部文档

工作区应用 · 界面描述规范(完整版)

读者:需要手写或审阅应用界面描述的人(含应用开发助手改草案时对照)

来源 sdk/appsdk/界面描述规范.md

文档版本:1.0(对应 app.json / AppSpec v1) 读者:需要手写或审阅应用界面描述的人(含应用开发助手改草案时对照) 关联appsdk 目录说明 · 视图与布局 · 手工搭建指南 · 工作区应用机制


1. 核心原则

  1. 界面由描述生成,不是手写网页。平台 Web 端的标准渲染器读取 app.json,按 views 画出列表与表单。
  2. 一份描述三件事:数据长什么样(entities)→ 页面有哪些块(views)→ 按钮调什么(actions)。
  3. 用户可见文案用 labelname / id / action 是程序标识(小写、下划线等),不要拿去当界面标题。
  4. 当前仅三种视图list | form | action_form。无独立布局模型;扩展与「按图做界面」见 视图与布局

打开应用时,渲染器会按 views 数组顺序从上到下依次渲染每一块。


2. 文件与包位置

文件作用
app.json界面与数据模型描述(本规范主体)
manifest.json应用清单:显示名、程序集、ui_mode=metadata 等(一般由平台写入)
data/app.dbentities 生成的业务库(与 Cadau 主库分离)
logic/handlers.pyimpl=script 的自定义动作实现(界面提交后调用)
schema/001_init.sql由实体自动生成的建表 SQL(创建/更新规格时维护)

手写界面时,通常只改 app.json;若新增了自定义动作,还需在 handlers.py 中实现对应 action id。


3. 根对象

{
  "version": 1,
  "entities": [ /* 至少一个 */ ],
  "views": [ /* 至少一个 */ ],
  "actions": [ /* action_form 引用的动作必须在此声明 */ ]
}
字段必填说明
version建议写当前为 1;省略时平台写入时会补为 1
entities业务对象(表)定义;至少 1 个
views界面块;至少 1 个
actions条件只要有 action_form,就必须声明其引用的动作

校验失败时无法保存/发布(见 §10)。


4. 实体 entities[]

表示应用内一张业务表(SQLite)。平台会自动增加主键列 id(自增),不必fields 里声明 id

{
  "name": "profile_reports",
  "label": "画像记录",
  "fields": [
    { "name": "emp_name", "label": "员工姓名", "type": "text", "required": true },
    { "name": "status", "label": "报告状态", "type": "select", "default": "待生成",
      "options": ["待生成", "已生成", "失败"] },
    { "name": "created_at", "label": "生成时间", "type": "datetime", "read_only": true, "auto": "now" }
  ]
}

4.1 实体属性

字段必填规则
name表名;^[a-z][a-z0-9_]*$(小写字母开头,仅小写、数字、下划线)
label用户可见名称(如「画像记录」)
fields至少 1 个字段(不含系统 id

4.2 字段 fields[]

字段必填说明
name列名;规则同实体名风格;不要id(系统保留)
label表头 / 表单标签
type建议见下表;空字符串按 text 处理
requiredtrue 时库列 NOT NULL;表单标 *
default默认值(写入 DDL;表单初始值视控件而定)
min仅对 number:HTML min
read_onlytrue:不出现在可编辑表单
auto目前支持 "now":创建时由前端写入 UTC ISO(不展示输入框)
hiddentrue:默认不出现在列表列与表单
optionsselect 的选项字符串列表

字段类型与界面控件

type控件SQLite 列类型
text(默认)单行输入TEXT
numbertype=numberINTEGER
textarea多行文本TEXT
datetimedatetime-local(按账号显示时区墙钟)TEXT(存 UTC ISO
select下拉;须配 optionsTEXT

时间约定(与产品「时间显示与时区偏好」一致):

  • 存盘datetime / auto=now 均为 UTC 绝对时刻字符串;不要在库里存「无时区的北京时间假本地」。
  • 列表展示:平台按当前用户的显示时区、日期格式与 12/24 小时制格式化;不是浏览器系统时区。
  • 表单输入:用户按显示时区填墙钟;提交时转为 UTC ISO。
  • 账号偏好本身不能写进 app.json;引导用户到「我的 → 偏好设置 → 时间显示」。

4.3 列表上的特殊约定

若实体含字段名 upload_id(存附件编号),列表在「操作」列会为有值的行提供 打开 / 下载(用于员工画像等 HTML 报告)。这是渲染器约定,不是单独的视图类型。


5. 视图 views[](界面怎么画)

每个元素是一块界面。type 决定画什么。

5.1 列表 list

展示某实体的表格;数据来自内置动作 crud.list

{
  "type": "list",
  "id": "history",
  "label": "历史画像",
  "entity": "profile_reports",
  "columns": ["emp_name", "report_title", "status", "created_at"],
  "limit": 100,
  "allow_delete": true
}
字段必填说明
type"list"
entity必须是已声明的实体 name
label建议区块标题;默认「列表」
id前端 key;建议填写便于稳定
columns列顺序(字段 name);省略则用实体全部非 hidden、非 id 字段
limit拉取条数;默认 100
allow_delete省略或 true:显示删除;false:隐藏删除。删除前弹出产品风格确认框(非浏览器原生)

界面行为:标题旁有「刷新」;删除调用 crud.delete

5.2 实体表单 form

向某实体插入一行;提交调用 crud.create

{
  "type": "form",
  "label": "新建记事",
  "entity": "notes",
  "mode": "create",
  "fields": ["title", "body"]
}
字段必填说明
type"form"
entity目标实体
mode建议当前实现按 创建 处理;写 "create"
fields要显示的字段名子集;省略则显示实体中所有可编辑字段
label建议区块标题;默认「新增」

不会出现在表单里的字段idhidden=trueread_only=trueauto=nowauto=now 在提交时由前端写入时间,不展示输入框)。

5.3 动作表单 action_form

收集参数并调用自定义动作(通常 impl=script)。

{
  "type": "action_form",
  "id": "generate",
  "label": "生成员工画像",
  "action": "profile.generate"
}
字段必填说明
type"action_form"
action必须对应 actions[].id
label建议区块标题
id建议填写
fields不驱动控件见下方「重要」

重要(与直觉不同):标准渲染器画动作表单时,输入项来自 actions[].params不是 views[].fieldsviews[].fields 可作文档/助手提示,但当前前端不会用它生成控件。手写时请把表单字段写在对应动作的 params 里。

提交后:调用 invoke(action, params)。若返回结果含 upload_id(及可选文件名),界面可提供打开/下载报告(与员工画像一致)。


6. 动作 actions[]

{
  "id": "profile.generate",
  "label": "生成员工画像",
  "impl": "script",
  "params": [
    { "name": "emp_name", "label": "员工姓名", "type": "text", "required": true },
    { "name": "emp_id", "label": "员工ID(重名时填写)", "type": "text" }
  ]
}
字段必填说明
id动作标识;建议 领域.动词(如 profile.generate
label用户可见名称
impl"script":走 logic/handlers.py"crud.create":内置创建(少见,一般用 form 视图即可)
params建议动作表单的字段定义;决定 action_form 上有哪些输入框

6.1 参数 params[]

字段说明
name提交给脚本的参数名
label表单标签
type同字段类型:text / number / …;默认 text
required是否必填

select 在动作参数上:若需下拉,当前动作参数结构options 字段;下拉选项请放在实体字段上,用 form/list;动作表单目前以 text/number/textarea/datetime 为主(与渲染器 resolveActionFields 一致)。需要枚举时可用 text 并在脚本内校验,或后续扩展契约。

6.2 内置动作(不必写进 actions

由平台提供,列表/表单会自动调用:

action用途
crud.list列表加载
crud.create实体表单提交
crud.update(API 有;标准 UI 尚未提供编辑视图)
crud.delete列表删除
schema.tables列出业务表
ping健康检查

自定义动作未写在 actions 却被 action_form 引用 → 校验失败

6.3 与 handlers.py 的对应

impl=script 时,运行时调用包内脚本入口,大致约定:

handle(action, params, ctx) -> dict
  • action 等于 actions[].id(如 profile.generate
  • params 为表单提交的键值
  • 返回中建议含 ok;失败时含 error;可选 upload_id / warnings 等供界面展示

脚本细节与平台桥(取数、存附件)见机制文档「invoke」;界面能否出现按钮,仍以本文件的 actions + action_form 为准


7. 手写最小示例(记事本)

{
  "version": 1,
  "entities": [
    {
      "name": "notes",
      "label": "记事",
      "fields": [
        { "name": "title", "label": "标题", "type": "text", "required": true },
        { "name": "body", "label": "内容", "type": "textarea" },
        { "name": "created_at", "label": "创建时间", "type": "datetime", "read_only": true, "auto": "now" }
      ]
    }
  ],
  "views": [
    {
      "type": "form",
      "label": "新建记事",
      "entity": "notes",
      "mode": "create",
      "fields": ["title", "body"]
    },
    {
      "type": "list",
      "label": "全部记事",
      "entity": "notes",
      "columns": ["title", "created_at"],
      "allow_delete": true
    }
  ]
}

无需 actions:只有 form + list,全部走内置 CRUD。


8. 手写带自定义动作示例(示意)

{
  "version": 1,
  "entities": [
    {
      "name": "jobs",
      "label": "任务记录",
      "fields": [
        { "name": "keyword", "label": "关键词", "type": "text", "required": true },
        { "name": "status", "label": "状态", "type": "select", "options": ["待处理", "完成", "失败"] },
        { "name": "upload_id", "label": "附件ID", "type": "text", "hidden": true },
        { "name": "created_at", "label": "时间", "type": "datetime", "read_only": true, "auto": "now" }
      ]
    }
  ],
  "views": [
    {
      "type": "action_form",
      "id": "run",
      "label": "执行任务",
      "action": "job.run"
    },
    {
      "type": "list",
      "label": "历史",
      "entity": "jobs",
      "columns": ["keyword", "status", "created_at"]
    }
  ],
  "actions": [
    {
      "id": "job.run",
      "label": "执行任务",
      "impl": "script",
      "params": [
        { "name": "keyword", "label": "关键词", "type": "text", "required": true }
      ]
    }
  ]
}

同时需在 logic/handlers.py 实现 job.run(生成结果、可选写 jobs 表)。仅改界面描述而没有脚本,提交会失败。


9. 员工画像对照(现网结构)

用户看到的描述里对应
「生成员工画像」表单viewstype=action_formaction=profile.generate;字段来自该动作的 params
「历史画像」表格viewstype=listentity=profile_reports
删除历史allow_delete 未设为 false
打开/下载报告实体含 upload_id,且行内有值
点提交后的取数与 HTML不属于界面描述;在 logic/ + references/pipeline.json

完整样例可直接打开运行时应用包中的 app.json(员工画像模板)。


10. 校验清单(手写后自检)

  • [ ] entities ≥ 1,且每个有 name/label/fields
  • [ ] 实体名、字段名符合小写标识规则;字段有 label;类型合法
  • [ ] views ≥ 1;type 仅为 list | form | action_form
  • [ ] list/formentity 存在
  • [ ] 每个 action_formactionactions 中存在
  • [ ] 每个 actions[]id/labelimplscriptcrud.create
  • [ ] 动作表单字段写在 actions[].params,不要只写在 views[].fields
  • [ ] 自定义动作已在 handlers.py 实现(若 impl=script
  • [ ] 用户可见文案都在 label,没有把英文 id 直接当标题

实现侧校验函数:workspaceapp.ValidateSpec


11. 明确不在本规范内

需求去向
自由布局、自有 CSS/路由的完整 Web平台插件 SDK · 视图与布局
取数步骤、HTML 模板、组装脚本取数与流水线动作与handlers
对话里「做一个应用」的流程工作区应用.md §对话创建
产品帮助(给最终用户)help/product-features/workspace-apps.md
从零手工搭包手工搭建指南 · 应用包结构

12. 修订约定

  • 契约变更(新增视图类型、字段属性、动作参数 options 等)须同步:本文件spec.go 校验、WorkspaceAppRenderer.tsx 渲染行为,并 bump 本节文档版本号。
  • 机制总览 工作区应用.md 可保留短摘要,完整手写说明以本文为准