1. 页面概览

1.1 是什么

「应用构建」是 LightFoundry 的低代码应用构建器(对应设计文档 TAD-09,对标 Palantir Foundry Workshop,源码 action/web/src/views/AppsPage.vue,约 1047 行)。它让业务人员不写代码、通过点选组件 + 声明式 JSON 绑定来搭建面向对象的数据应用:左侧点选组件类型,中间编辑「组件配置(DataBinding JSON)」,右侧实时预览(<component :is> 动态渲染 table / object_detail / metric_card / button / text 等组件,组件数据经 POST /apps/:id/pages/:pageId/components/:compId/data 加载)。

页面顶部注释块给出了关键架构说明:

数据模型三层:Application(应用)→ AppPage(页面)→ AppComponent(组件实例)。应用定义为 JSON 配置(DataBinding / ActionBinding / StyleConfig 存 PostgreSQL jsonb 或 SQLite TEXT),前端按 JSON 配置运行时渲染。也就是说,「做应用」的本质是配置一份份 JSON:应用是容器,页面是布局,组件是数据视图。

1.2 核心价值

维度说明
零代码建模应用/页面/组件全部通过表单 + JSON 配置完成,不写 Vue 代码
声明式数据绑定DataBinding JSON 声明「取什么对象、过滤什么、排序什么」,后端负责翻译执行
即时预览右侧预览区实时渲染组件,数据直接来自后端语义查询(带 RLS/CLS 只读安全)
对象驱动组件可绑定本体对象类型,数据口径与本体/语义层一致
发布即用应用可发布(draft → published),发布后状态固化
动作扩展按钮/表单组件支持 ActionBinding(action_trigger 模式),可触发本体 Action 写路径

1.3 一句话总结

用「应用 → 页面 → 组件」三层结构 + DataBinding JSON 组装出数据应用,后端语义查询统一取数,前端动态组件实时预览。

2. 访问入口

2.1 路由与菜单

2.2 认证与权限

2.3 端口与 API 前缀

3. 界面布局

页面是三级视图切换的单页应用,view 状态为 list / app / editor,标题随视图变化(viewTitle computed):

┌─────────────────────────────────────────────────────────────────────┐
│  [低代码应用构建器 / 应用页面 · xxx / 页面编辑器 · xxx]  [返回][发布应用]│
├─────────────────────────────────────────────────────────────────────┤
│ View1 list(应用列表):                                               │
│  ┌ 卡片: 创建应用(api_name* | 显示名称* | 分类下拉 | [创建])──────┐  │
│  │        描述输入                                               │  │
│  ┌ 卡片: 应用列表(app-grid 卡片墙)──────────────────────────────┐  │
│  │  卡片: 显示名 + 状态徽标 / api_name·分类 / 描述                 │  │
│  │        操作: [打开] [发布(未发布时)] [页面] [删除]              │  │
├─────────────────────────────────────────────────────────────────────┤
│ View2 app(应用详情):                                               │
│  ┌ 卡片: 应用信息(显示名/name/分类/状态)────────────────────────┐  │
│  ┌ 卡片: 添加页面(页面名称* | 路径 | 布局下拉 | [添加])─────────┐  │
│  ┌ 卡片: 页面列表(page-row: 名称 + 路径 + 布局标签)─────────────┐  │
│  │        操作: [编辑] [预览] [删除]                              │  │
├─────────────────────────────────────────────────────────────────────┤
│ View3 editor(页面编辑器,三栏布局):                                 │
│  ┌ 左栏(220px): 组件库(palette) ─────────────────────────────────┐  │
│  │  8 个组件类型按钮 + 已添加组件列表(点击选中)                  │  │
│  ├ 中栏(360px): 组件配置 ────────────────────────────────────────┤  │
│  │  组件类型下拉 | 标题* | 绑定对象类型下拉 | DataBinding JSON     │  │
│  │  ActionBinding JSON | [保存配置/创建组件] [删除组件]            │  │
│  ├ 右栏(自适应): 预览 ───────────────────────────────────────────┤  │
│  │  [刷新数据] + <component :is> 动态渲染各组件                  │  │
└─────────────────────────────────────────────────────────────────────┘
板块职责
页面头标题随视图变化;非 list 显示「返回」;editor 视图显示「发布应用」
操作结果提示顶部 alert 显示操作结果,可点「关闭」清除
创建应用表单填 api_name/显示名称/分类/描述创建应用
应用卡片墙展示全部应用,行内「打开」「发布」「页面」「删除」
应用信息卡片当前应用元信息
添加页面表单为当前应用添加页面(名称/路径/布局)
页面列表当前应用的页面,行内「编辑」「预览」「删除」
组件库面板8 类组件按钮,点击添加;已添加组件列表供选中
组件配置面板编辑选中组件的类型/标题/对象绑定/DataBinding/ActionBinding
预览面板按组件类型动态渲染数据结果,实时刷新

4. 交互元素详解

4.1 页面级元素

元素位置含义操作效果
「返回」按钮页面头(非 list 视图)返回上一级goBack():editor → app(刷新页面列表);app → list(清空 appDetail 并刷新应用列表)
「发布应用」按钮页面头(editor 视图)发布当前应用publishApp(appDetail);已发布时按钮文字变「已发布」并禁用
操作结果提示页面顶部显示成功/失败提示点「关闭」清空 alert.message

4.2 View1 应用列表

4.2.1 创建应用表单

四列栅格 + 下方描述输入:

元素含义必填默认值操作效果后端调用
「api_name *」应用唯一标识,小写字母/数字/下划线,如 order_ops校验非空组装到 body
「显示名称 *」展示名,如「订单运营」校验非空display_name
「分类」下拉运营/分析/审批/其他运营选择分类category
「描述」应用用途说明-description
按钮「创建」提交--createApp():校验 api_name 与显示名称必填 → POST → 成功清空表单并刷新POST /api/v1/apps

4.2.2 应用列表卡片墙

每张卡片(app-card)包含:显示名 + 状态徽标(statusLabel:draft=草稿/published=已发布/archived=已归档)、api_name: xxx · 分类:xxx、描述(无则「无描述」)。

按钮含义操作效果后端调用
「打开」进入应用详情openApp(app):设 appDetail、view=app、刷新页面列表GET /api/v1/apps/:id/pages
「发布」(未发布时显示)发布应用publishApp(app)POST /api/v1/apps/:id/publish
「页面」进入应用详情(同「打开」)openApp(app)同上
「删除」删除应用confirm(文案「确认删除应用「xxx」?其页面与组件将一并删除。」)→ DELETE /api/v1/apps/:id级联删页面/组件

空列表显示「暂无应用,先创建一个吧。」

4.3 View2 应用详情

4.3.1 添加页面表单

元素含义必填默认值操作效果后端调用
「页面名称 *」页面名,如「概览」校验非空name
「路径」页面路由路径/-path
「布局」下拉grid/tabs/sidebargrid选择布局layout_type
按钮「添加」提交--createPage()POST /api/v1/apps/:id/pages

4.3.2 页面列表

每行(page-row):页面名 + 路径(灰色 page-path)+ 布局标签(status-tag status-draft)。

按钮含义操作效果后端调用
「编辑」进入编辑器openPage(page):view=editor,并行加载组件与对象类型GET .../components + GET /api/v1/ontology/objects
「预览」进入编辑器预览同「编辑」同上
「删除」删除页面confirm(「确认删除页面「xxx」?其组件将一并删除。」)DELETE /api/v1/apps/:id/pages/:pageId

空列表显示「暂无页面,添加一个页面开始搭建应用。」

4.4 View3 页面编辑器

4.4.1 组件库(左栏)

元素含义操作效果后端调用
「表格」(table,☰)对象列表表格addComponent('table')POST .../components
「对象详情」(object_detail,⧉)单个对象全部属性addComponent('object_detail')同上
「指标卡」(metric_card,#️⃣)大数字指标addComponent('metric_card')同上
「图表」(chart,📈)图表(MVP 预览占位)addComponent('chart')同上
「按钮」(button,🔘)触发动作addComponent('button')同上
「筛选栏」(filter_bar,⌕)动态筛选(MVP 预览占位)addComponent('filter_bar')同上
「表单」(form,✎)收集输入提交(MVP 预览占位)addComponent('form')同上
「文本」(text,T)静态内容addComponent('text')同上

点击任意组件按钮:busy 期间禁用;POST 创建组件(title 自动取组件类型中文标签,data_binding: {}),成功后提示「组件已添加,请在中间面板配置数据绑定」,刷新组件列表并自动选中新建组件。「已添加组件」列表:显示 title || 类型标签 与组件类型英文,点击选中(selectComp)。

4.4.2 组件配置(中栏)

选中组件后表单填充(selectComp 回填),可编辑:

元素含义操作效果后端调用
「组件类型」下拉修改组件类型改变 form.component_type保存时生效
「标题 *」组件显示标题保存时校验非空title
「绑定对象类型(数据源)」下拉绑定本体对象类型(组件数据源)选项来自对象类型列表;「不绑定」= nullobject_type_id
「位置与尺寸(12 列网格)」四个数值输入position_x(列偏移 0-11)/ position_y(行序)/ width(占列 1-12)/ height(占行,1 行约 60px)selectComp 回填(width/height 沿用后端默认 6/4);保存时随 payload 下发position_x/position_y/width/height
「DataBinding JSON」textarea核心数据绑定配置保存时 JSON 解析校验data_binding
「ActionBinding JSON(可选)」textarea动作绑定(按钮/表单)可选,非空时 JSON 校验action_binding
按钮「保存配置」/「创建组件」提交校验标题必填 + JSON 合法;有 id 走 PUT,无 id 走 POSTPUT/POST .../components[/:compId]
按钮「删除组件」删除当前组件confirm → DELETE → 清空表单并刷新DELETE .../components/:compId

DataBinding JSON 示例(模板 placeholder 逐字):

{"mode":"object_list","properties":["order_id","amount","status"],"filters":[{"field":"order_id","op":"eq","value":"{{selectedRowId}}"}],"page_size":50}

mode 支持 object_list / object_detail / aggregation / static / action_trigger(后端 BindingMode* 常量)。{{...}} 是运行时参数引用,会在加载组件数据时用运行时参数替换(类型保留,未解析参数置 null)。

位置/尺寸的部分更新语义(重要边界)UpdateComponentdefaultInt(in.PositionX, comp.PositionX) 等做部分更新(apps/service.go:649-652),即更新时传 0 会被当作「未提供」而保留原值——已存在组件的 position_x/position_y/width/height 无法经 PUT 置 0;CreateComponentposition_x/y 原样写入(创建时可 0),width/height 为 0 时回落为 6/4(apps/service.go:591-594)。前端编辑态四个输入 min=1,保存时仅下发 ≥1 的字段(<1 的字段省略,后端保留原值),并在结果提示中列出被保留的字段。

4.4.3 预览(右栏,按 page.layout_type 渲染)

元素含义操作效果后端调用
顶部 layout_type 标签 + 布局说明显示当前页面布局类型与渲染规则pageDetail.layout_type(缺省 gridapps/models.go:152无(本地展示)
「刷新数据」按钮重新加载全部组件数据loadAllData() → 逐组件 POST 数据接口POST .../components/:compId/data

预览区按 layout_type 真实排版(previewLayout),组件按 position_y → position_x → id 排序(previewComps):

布局渲染行为
grid(默认)CSS Grid 12 列;组件按 grid-column: position_x+1 / span width 摆放(span 上限补足到 12 列内),height 换算 minHeight = height × 60px;按 position_y → position_x 排序后自动换行
tabs顶部渲染标签条(每组件一个 tab,名称取 title || 类型标签),点标签切换显示对应组件;首个组件为默认激活页
sidebar第一个组件固定渲染在左侧 200px 窄栏,其余组件在主区依次排列

预览区每个组件一个 preview-item,点击可选中(联动中栏表单)。渲染器按类型分派(rendererFor):

组件类型渲染行为
tablecolumns 做表头、rows 做行渲染表格;有 error 显示「加载失败:...」;无数据显示「暂无数据(请配置对象绑定与 DataBinding 后刷新)」
object_detailrows[0] 首行,按列名/值渲染详情行;无数据「暂无对象(请配置 object_id 参数后刷新)」
metric_card大号数字显示 rows[0][0](聚合首行首列),缺省
button渲染按钮 + 动作参数 JSON 输入框;读取 data_binding.action_trigger.action_name,点击后经 POST /apps/pages/:pageId/actions/:actionName/execute 真实触发,组件内显示结果/错误提示(不再用 window.alert)。见下方「动作触发」
text显示 style_config.content || title 的静态文本
chart / filter_bar / form占位提示「组件类型 xxx 预览待接入(MVP 支持 table/object_detail/metric_card/button/text)」

动作触发(ActionBinding)已接线(2026-09-14):后端服务层 ExecuteActionTriggerapps/service.go,含 findActionTriggerComponent/auditTrigger)与 HTTP 路由 POST /api/v1/apps/pages/:pageId/actions/:actionName/executehandleExecuteActionTrigger)均已就绪,openapi 已收录。预览中 button 组件读取 data_binding.action_trigger.action_name,点击后按输入的「动作参数(JSON)」与自动生成的幂等键真实触发(writepath 五步流水线),组件内显示 status/rows_affected/operation_id 结果;未配置 action_name 时提示补充绑定。前端不再做「缺口拦截」。

5. 后端关联

5.1 API 客户端

5.2 端点表

方法路径请求体要点成功响应备注
GET/apps-{data: [...]}列出应用
POST/appsname/display_name/description/category201 {data:{id}}owner 由服务端注入当前用户
GET/apps/:id-{data:{...}}应用详情(前端未直接用)
PUT/apps/:id同创建{data:{id}}前端未使用
DELETE/apps/:id-{data:{deleted:true,id}}级联页面/组件
POST/apps/:id/publish-{data:{published:true,id}}draft → published
POST/apps/:id/archive-{data:{archived:true,id}}published → archived(未发布 422)
POST/apps/:id/unarchive-{data:{unarchived:true,id}}archived → published(未归档 422)
GET/apps/:id/pages-{data:[...]}列出页面
POST/apps/:id/pagesname/path/layout_type201 {data:{id}}添加页面
PUT/apps/:id/pages/:pageId同创建{data:{id}}前端未使用
DELETE/apps/:id/pages/:pageId-{data:{deleted:true,id}}级联组件
GET/apps/:id/pages/:pageId/components-{data:[...]}列出组件
POST/apps/:id/pages/:pageId/componentscomponent_type/title/object_type_id/data_binding201 {data:{id}}添加组件
PUT/apps/:id/pages/:pageId/components/:compId同创建(可含 action_binding){data:{id}}更新组件
DELETE/apps/:id/pages/:pageId/components/:compId-{data:{deleted:true,id}}删除组件
POST/apps/:id/pages/:pageId/components/:compId/data{"params":{...}}{data:{component_id,component_type,title,columns,rows,action?,error?}}加载组件数据
POST/apps/:id/pages/:pageId/data同左{data:[...]}页面联动加载(前端未直接用)
POST/apps/pages/:pageId/actions/:actionName/execute{"params":{...},"idempotency_key":"..."}{data:{mode,status,rows_affected,result,operation_id,...}}触发页面 action_trigger 绑定动作(button 预览调用)

5.3 响应结构

组件数据加载响应示例(table 组件):

{
  "data": {
    "component_id": 3,
    "component_type": "table",
    "title": "订单列表",
    "columns": ["order_id", "amount", "status"],
    "rows": [
      [1001, 2000, "已发货"],
      [1002, 1500, "待审核"]
    ]
  }
}

加载失败时前端把 err.response?.data?.error 存入 dataMap[comp.id].error,预览区渲染「加载失败:<error>」。无绑定的组件(文本/按钮)返回空 columns/rows,预览走各自渲染分支。

5.4 关联模块表

后端包/文件职责
action/products/foundry/apps/models.go三张表:applications / app_pages / app_components;状态/类型常量
action/products/foundry/apps/service.goAppService:应用/页面/组件 CRUD、发布、数据加载(语义查询)、action_trigger
action/products/foundry/server/apps_handlers.goHTTP handler + RegisterAppsRoutes 路由注册
action/products/foundry/server/server.gosetupRouter 中 RegisterAppsRoutes(protected, s)
action/products/foundry/ontology对象类型元数据(绑定下拉 GET /api/v1/ontology/objects
action/products/foundry/semantic + pipeline语义查询与连接器定位真实数据源

5.5 关键机制

6. 核心流程详解

6.1 主流程一:创建应用

  1. 进入页面默认在「应用列表」视图。
  2. 填「api_name」与「显示名称」(必填),选「分类」,可填「描述」。
  3. 点「创建」→ createApp() 校验 → POST /api/v1/apps → 成功提示「应用创建成功」,清空表单并刷新列表。
  4. 新应用出现在卡片墙,状态为草稿(draft)。

6.2 主流程二:添加页面

  1. 在卡片墙点「打开」或「页面」→ 进入「应用详情」视图。
  2. 填「页面名称」(必填)、「路径」(默认 /)、「布局」(grid/tabs/sidebar)。
  3. 点「添加」→ POST /api/v1/apps/:id/pages → 页面列表出现新页面,布局标签显示。

6.3 主流程三:编辑器内组装应用

  1. 在页面列表点「编辑」或「预览」→ 进入「页面编辑器」三栏视图,自动并行加载组件列表与对象类型列表。
  2. 左栏点组件类型按钮 → 自动创建组件并选中,中栏填充表单。
  3. 中栏选「绑定对象类型(数据源)」、写「DataBinding JSON」(如 object_list 模式)、可选「ActionBinding JSON」。
  4. 点「保存配置」(新建时显示「创建组件」)→ PUT/POST 组件接口 → 刷新组件列表与预览数据。
  5. 右栏点「刷新数据」手动重新加载全部组件数据;点击预览区的组件可快速选中。
  6. 保存后组件出现在「已添加组件」列表,预览区实时渲染真实数据。

6.4 主流程四:发布应用

6.5 数据流 / 任务终态语义

用户点击组件类型 ──▶ POST components ──▶ 组件落库(component_type/title/data_binding)
      │
      ▼
选中组件 → 中栏表单回填 → 编辑 DataBinding JSON
      │
      ▼
保存配置(PUT/POST) ──▶ fetchComponents ──▶ loadAllData
                                            │
            ┌───────────────────────────────┤
            ▼                               ▼
  POST components/:compId/data      (逐组件并行)
            │
            ▼
  resolveBindingParams({{selectedRowId}} 等)
            ▼
  mode 分派: object_list / object_detail / aggregation / static / action_trigger
            ▼
  dataMap[comp.id] ──▶ 右栏 <component :is> 渲染

状态机:应用 draft(草稿)→ published(已发布)→ archived(已归档,后端支持、前端无 UI)。组件无独立状态字段,以是否存在为准。组件数据加载是同步 HTTP,无任务轮询。

7. 权限与安全

8. 常见问题与排错

8.1 预览区「加载失败:...」

8.2 DataBinding JSON 格式错误

8.3 组件列表刷新后预览数据消失

8.4 创建应用提示必填却无反应

8.5 应用发布后找不到运行入口

9. 已知缺陷与边界

项目说明
预览组件有限MVP 仅 table/object_detail/metric_card/button/text 有真实渲染,chart/filter_bar/form 为占位提示
布局渲染(已收口)编辑器预览已按 page.layout_type 真实排版:grid 用 12 列 CSS Grid 按 position/size 摆放、tabs 用标签条切换、sidebar 首组件固定左栏;组件排序 position_y → position_x → id(2026-09-13 收口)
无拖拽调整(现实边界)已提供 position_x/position_y/width/height 数值输入编辑(12 列网格),不含鼠标拖拽;且因后端部分更新语义,已存在组件的位置/宽高不能置 0(更新传 0 保留原值,apps/service.go:649-652),创建时可填 0
发布为状态标记发布仅置状态,无独立运行入口/路由挂载
按钮动作触发(已修复 2026-09-14)button 组件不再用 window.alert,也不再是「缺口说明」:读取 data_binding.action_trigger.action_name,点击后经 POST /apps/pages/:pageId/actions/:actionName/execute 真实触发(参数取组件内 JSON 输入框,幂等键自动生成),组件内显示 status/rows_affected 结果。路由 handleExecuteActionTrigger 已注册并收录 openapi;触发前有页面归属应用写路径属主/admin 校验
归档/恢复(已修复 2026-09-14)后端已接线 POST /apps/:id/archiveArchiveApplication,published→archived;未发布 422)与 POST /apps/:id/unarchiveUnarchiveApplication,archived→published;未归档 422)。应用列表卡片与应用详情按状态显示「归档/恢复」按钮
应用可见性后端按用户过滤:published 全员可见;draft 仅作者本人可见;archived 仅 owner 可见;他人按 id 直查 draft 返回 404
无分页应用/页面/组件列表均一次拉全量
组件数据无缓存失效数据在进入页面与点「刷新数据」时加载,无定时刷新