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 加载)。
页面顶部注释块给出了关键架构说明:
- 视图三级:① 应用列表(list,应用 CRUD + 发布);② 应用详情(app,页面列表 + 创建/删除);③ 页面编辑器(editor,左侧组件库 + 中部组件配置 + 右侧预览)。
- 组件数据加载「复用后端语义查询」:组件绑定的 DataBinding 会被翻译为语义查询(object_list / object_detail / aggregation / static),由后端连接真实数据源取数。
- 依赖 Foundry 后端
/api/v1/apps系列接口(JWT 鉴权)。
数据模型三层: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 一句话总结
2. 访问入口
2.1 路由与菜单
- 路由:
/foundry/apps - 路由名称:
FoundryApps - 菜单位置:Foundry 左侧边栏「应用构建」(
FoundryLayout.vue菜单项,位于「MLOps 模型」之后) - 源码文件:
action/web/src/views/AppsPage.vue(约 1047 行) - 路由注册:
action/web/src/router/index.js,meta: { title: '应用构建', requiresAuth: true }
2.2 认证与权限
- 需要登录:是(路由
meta.requiresAuth = true,父路由/foundry受保护) - 登录方式:AIP 统一登录;
localStorage.aip_token由client.js请求拦截器自动写入Authorization: Bearer <token> - 应用有
owner_id(创建时由currentUserID(c)写入),但页面本身不按 owner 过滤,所有登录用户可见全部应用;创建/删除等写操作需登录用户身份。 - 组件数据加载走语义查询,受后端 RLS/CLS 约束:
handleLoadComponentData会把user_id注入运行时参数,供 RLS/CLS 只读路径使用。 - 401 排错:被重定向到
/login说明 token 缺失/过期,重新登录即可。
2.3 端口与 API 前缀
- 后端端口:18081(Foundry)
- 前端 API 客户端:
action/web/src/api/client.js,baseURL = '/api/v1',超时 30 秒 - 本页所有请求路径形如
/api/v1/apps,完整 URL 为http://localhost:18081/api/v1/apps(开发态经 Vite 代理)
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/sidebar | 否 | grid | 选择布局 | 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 |
| 「绑定对象类型(数据源)」下拉 | 绑定本体对象类型(组件数据源) | 选项来自对象类型列表;「不绑定」= null | object_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 走 POST | PUT/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)。
位置/尺寸的部分更新语义(重要边界):UpdateComponent 用 defaultInt(in.PositionX, comp.PositionX) 等做部分更新(apps/service.go:649-652),即更新时传 0 会被当作「未提供」而保留原值——已存在组件的 position_x/position_y/width/height 无法经 PUT 置 0;CreateComponent 中 position_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(缺省 grid,apps/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):
| 组件类型 | 渲染行为 |
|---|---|
| table | 用 columns 做表头、rows 做行渲染表格;有 error 显示「加载失败:...」;无数据显示「暂无数据(请配置对象绑定与 DataBinding 后刷新)」 |
| object_detail | 取 rows[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):后端服务层 ExecuteActionTrigger(apps/service.go,含 findActionTriggerComponent/auditTrigger)与 HTTP 路由 POST /api/v1/apps/pages/:pageId/actions/:actionName/execute(handleExecuteActionTrigger)均已就绪,openapi 已收录。预览中 button 组件读取 data_binding.action_trigger.action_name,点击后按输入的「动作参数(JSON)」与自动生成的幂等键真实触发(writepath 五步流水线),组件内显示 status/rows_affected/operation_id 结果;未配置 action_name 时提示补充绑定。前端不再做「缺口拦截」。
5. 后端关联
5.1 API 客户端
baseURL: '/api/v1'(Vite 开发代理到 Foundry 18081)timeout: 30000(30 秒)- 请求拦截器:自动附带
Authorization: Bearer ${localStorage.aip_token} - 响应拦截器:401 时清除 token 并跳转
/login - 本页无独立 axios 实例。
5.2 端点表
| 方法 | 路径 | 请求体要点 | 成功响应 | 备注 |
|---|---|---|---|---|
| GET | /apps | - | {data: [...]} | 列出应用 |
| POST | /apps | name/display_name/description/category | 201 {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/pages | name/path/layout_type | 201 {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/components | component_type/title/object_type_id/data_binding | 201 {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.go | AppService:应用/页面/组件 CRUD、发布、数据加载(语义查询)、action_trigger |
action/products/foundry/server/apps_handlers.go | HTTP handler + RegisterAppsRoutes 路由注册 |
action/products/foundry/server/server.go | setupRouter 中 RegisterAppsRoutes(protected, s) |
action/products/foundry/ontology | 对象类型元数据(绑定下拉 GET /api/v1/ontology/objects) |
action/products/foundry/semantic + pipeline | 语义查询与连接器定位真实数据源 |
5.5 关键机制
- 视图状态机:list → app(选应用)→ editor(选页面);「返回」逐级回退并刷新数据。
- 组件数据加载:
LoadComponentData读组件 DataBinding → 参数引用替换(resolveBindingParams)→ 按mode分派:object_list(execObjectList)/ object_detail(execObjectDetail)/ aggregation(execAggregation)/ static(空数据)/ action_trigger(解析动作元数据)。user_id由服务端注入参数(RLS/CLS 只读路径用)。 - 页面联动加载:
LoadPageData遍历页面组件,先执行参数引用组件、结果注册为后续组件运行时参数(联动);单个组件失败不阻断整页。 - 发布语义:发布仅把应用状态 draft → published,不生成可独立访问的运行时 URL(低代码应用当前在前端编辑器内预览使用)。
- 渲染器:前端用 Vue
render函数按componentType动态渲染(runtime-only 构建),预览数据来自dataMap[comp.id]。
6. 核心流程详解
6.1 主流程一:创建应用
- 进入页面默认在「应用列表」视图。
- 填「api_name」与「显示名称」(必填),选「分类」,可填「描述」。
- 点「创建」→
createApp()校验 →POST /api/v1/apps→ 成功提示「应用创建成功」,清空表单并刷新列表。 - 新应用出现在卡片墙,状态为草稿(draft)。
6.2 主流程二:添加页面
- 在卡片墙点「打开」或「页面」→ 进入「应用详情」视图。
- 填「页面名称」(必填)、「路径」(默认
/)、「布局」(grid/tabs/sidebar)。 - 点「添加」→
POST /api/v1/apps/:id/pages→ 页面列表出现新页面,布局标签显示。
6.3 主流程三:编辑器内组装应用
- 在页面列表点「编辑」或「预览」→ 进入「页面编辑器」三栏视图,自动并行加载组件列表与对象类型列表。
- 左栏点组件类型按钮 → 自动创建组件并选中,中栏填充表单。
- 中栏选「绑定对象类型(数据源)」、写「DataBinding JSON」(如 object_list 模式)、可选「ActionBinding JSON」。
- 点「保存配置」(新建时显示「创建组件」)→ PUT/POST 组件接口 → 刷新组件列表与预览数据。
- 右栏点「刷新数据」手动重新加载全部组件数据;点击预览区的组件可快速选中。
- 保存后组件出现在「已添加组件」列表,预览区实时渲染真实数据。
6.4 主流程四:发布应用
- 在应用列表卡片点「发布」,或进入编辑器后点页面头「发布应用」→
POST /api/v1/apps/:id/publish→ 提示「应用已发布」,状态徽标变已发布。 - 已发布应用的「发布」按钮从卡片上消失;编辑器头部按钮变「已发布」并禁用。
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. 权限与安全
- 认证:全部接口走
/api/v1protected 组,JWT 必需;client.js自动带 Bearer token。 - 数据级安全:应用创建时写入
owner_id,但列表对所有登录用户可见(页面无过滤);组件数据加载时服务端注入user_id,语义查询路径受 RLS/CLS 约束。 - 写操作防护:删除应用/页面/组件均有
confirm二次确认;创建/保存/删除在busy期间按钮禁用防重复提交。 - 输入校验:前端校验 api_name/显示名称/页面名称/组件标题必填,DataBinding/ActionBinding 必须 JSON 可解析;后端校验 name 唯一、枚举(分类/组件类型/布局)合法。
- 数据加载安全:DataBinding 的 filters/条件由后端语义层翻译执行,参数替换保留数字类型,避免注入。
8. 常见问题与排错
8.1 预览区「加载失败:...」
- 现象:右栏预览组件显示红框「加载失败:<error>」。
- 原因:组件绑定了对象类型但 DataBinding 的字段/过滤条件与对象不匹配,或对象类型不可用,或
GET /ontology/objects失败导致没有绑定选项。 - 排查:① 确认「绑定对象类型(数据源)」已选(「不绑定」则表格组件无数据源);② 检查 DataBinding 的
properties/filters字段名是否真实存在于对象;③ 看「对象类型不可用不阻断编辑器」的提示是否出现(后端 ontology 服务是否正常)。
8.2 DataBinding JSON 格式错误
- 现象:点「保存配置」提示
DataBinding JSON 格式错误:...。 - 原因:textarea 内容不是合法 JSON(缺引号/多逗号)。
- 排查:① 用
JSON.parse思路自查:键与字符串值必须双引号;② 从模板 placeholder 复制改字段名而非手敲;③ 暂时把内容清空保存(空串会被解析为{},即无绑定模式)。
8.3 组件列表刷新后预览数据消失
- 现象:保存组件配置后「已添加组件」列表出现,但预览区数据为空/「暂无数据」。
- 原因:
fetchComponents后调用loadAllData,若组件 DataBinding 是static模式或未绑定对象,返回空数据属正常;若绑定了对象但查询无结果,是数据问题。 - 排查:① 点「刷新数据」看是否恢复;② 确认 DataBinding
mode是否为object_list/object_detail/aggregation;③ 确认对象中确实有符合 filter 的数据。
8.4 创建应用提示必填却无反应
- 现象:只填了 api_name 没填显示名称,点「创建」提示
api_name 与显示名称必填。 - 原因:
createApp()前端校验要求两个字段都非空。 - 排查:两个输入框都填上;注意
api_name只允许小写字母/数字/下划线(placeholder 提示)。
8.5 应用发布后找不到运行入口
- 现象:应用状态已变「已发布」,但页面只有编辑器预览,没有独立访问 URL。
- 原因:MVP 的「发布」仅固化了应用状态(draft → published),不生成可独立访问的运行时页面。
- 排查:这是当前边界而非 bug;通过「打开」进入编辑器即可预览真实数据。
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/archive(ArchiveApplication,published→archived;未发布 422)与 POST /apps/:id/unarchive(UnarchiveApplication,archived→published;未归档 422)。应用列表卡片与应用详情按状态显示「归档/恢复」按钮 |
| 应用可见性 | 后端按用户过滤:published 全员可见;draft 仅作者本人可见;archived 仅 owner 可见;他人按 id 直查 draft 返回 404 |
| 无分页 | 应用/页面/组件列表均一次拉全量 |
| 组件数据无缓存失效 | 数据在进入页面与点「刷新数据」时加载,无定时刷新 |