mcpField
O
objectstack-ui

objectstack-ui

github.com/objectstack-ai设计与 UI76229 次安装今天更新
代码仓库

关于 objectstack-ui

objectstack-ui 是一项使用 ObjectStack 规范设计界面的技能,涵盖视图、导航、仪表板、报表、页面和操作。

主要功能

  1. 01列表与表单视图设计指南
  2. 02主从表单模式
  3. 03应用导航结构
  4. 04仪表板与报表配置
  5. 05页面与操作设计

适用场景

  1. 01设计元数据驱动的 CRM 界面
  2. 02配置包含子记录的父表单
  3. 03组织应用导航与自定义页面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/objectstack-ai/objectstack --skill objectstack-ui

安装后在 Claude Code 中输入/objectstack-ui确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
README.md
analytics-inline-vs-dataset.json
views-apps-actions-pages.json
references/
_index.md
react-blocks.md
rules/
actions.md
list-views.md
dashboards.md
navigation.md
pages.md

详细介绍

UI Design — ObjectStack UI Protocol

使用 ObjectStack 规范设计用户界面的专家指南。本技能涵盖视图(列表、表单、看板、日历等)、 应用导航、仪表板、报表和操作。


视图类型

列表视图
类型适用场景
grid标准数据表——适用于大多数对象的默认视图
kanban带列的可视化看板(由状态驱动的工作流)
gallery基于卡片的瀑布流布局(可视化目录、联系人)
calendar基于日期的日程安排(事件、任务、预订)
timeline按时间顺序排列的活动流
gantt带依赖关系跟踪的项目管理
map含有 location 字段的地理空间记录
chart对象数据的聚合可视化(迷你图表视图)
tree自引用层级结构(树形网格)
表单视图
类型适用场景
simple单页表单——适用于字段不超过 15 个的对象
tabbed分标签页的区块——适用于包含多个字段组的复杂对象
wizard分步流程——引导式数据录入(入职、申请)
主从表单(父记录 + 子明细行)

要让用户连同子明细行一起录入记录(发票 + 明细、项目 + 任务),并原子性地保存,几乎从不需要自定义页面或表单配置。请按以下优先顺序选择:

  1. 关系 inlineEdit(默认,无需 UI 配置)。 在 DATA MODEL 中声明:在引用父记录的子对象 master_detail 字段上设置 inlineEdit。之后,父记录的每个标准新建/编辑表单(模态框、抽屉、整页表单)都会自动渲染子记录,并通过一次原子性 /api/v1/batch 保存父记录 + 子记录。无需视图元数据。 各值 true / 'grid' / 'form' 的选择规则:objectstack-data → Relationships → Inline Editing。

  2. 表单视图 subforms(覆盖 / 调整)。 仅当需要覆盖派生出的列或顺序,或显示关系未标记为内联编辑的子对象时,才添加到表单视图:

    form: {
      type: 'simple',
      sections: [{ group: 'invoice_header' }],  // a declared fieldGroup
      subforms: [
        { childObject: 'invoice_line', // relationshipField + columns are
          title: 'Line Items',         // derived from the child object;
          addLabel: 'Add line' },      // set `columns` here only to override.
      ],
    },
    
  3. object-master-detail-form 页面块(定制布局)。 只有自由布局时才使用页面。可使用相同的 details: [{ childObject }] 简写。

所有情况下,关系外键和网格列都根据子对象的元数据派生;选择项和查找字段也会一并沿用。父对象的 summary 字段会在服务器端汇总子对象的值(参见 objectstack-data)。

明细行网格行为(grid 模式)。 可编辑网格是真正的电子表格样式明细编辑器(QuickBooks / Stripe / NetSuite 模式),所有行为都由 DATA MODEL 派生——计算列、目录类型提示自动填充、持久化拖动排序,以及实时的小计 → 税额 → 总计:objectstack-data → Relationships → Inline Editing。这一侧只需编写三项配置:始终存在一个末尾的幽灵行,因此切勿添加“添加明细行”按钮(在幽灵行中输入内容会将其实体化为一行;未触碰的幽灵行永远不会持久化);autofill: false 可让某一列不参与类型提示复制;表单的 taxRateField 可覆盖用于计算总额的父对象字段。

读取侧——详情页关联列表。 inlineEdit 在父记录详情页上的对应功能是关联列表。无需手动编写:默认情况下,每个子对象关系都会显示为关联列表(所属的 master_detail 子对象优先)。可通过关系进行调整:

  • relatedList: 'primary' — 将关系标记为核心关系;详情页会将其提升为独立标签页(见下方布局)。这表示重要性意图,而非布局切换(ADR-0085)。
  • relatedList: false — 从详情页中隐藏不重要的子对象。
  • relatedListTitle / relatedListColumns — 覆盖派生出的标题 / 列(两者均为可选;否则列会根据子对象的 highlightFields 自动派生)。参见 objectstack-data → Relationships → Detail-page related lists。

关联列表布局。 在合成的记录详情页上,每个 relatedList: 'primary' 子对象都会获得独立标签页;所有其他关联列表则汇总到一个共享的关联标签页中。因此,只需在关系上更改一个词,就能将子表提升为一级标签页——无需自定义页面。对象仍不声明各表面专用的布局提示:旧的 detail.relatedLayout 开关和对象级 detail: {...} 块均已移除(ADR-0085);relatedLayout: 'tabs' | 'stack' 仅保留为应用级默认覆盖项,不能作为对象键使用。对于关系层无法表达的布局方式——筛选拆分(例如“打开”和“已关闭”标签页)、图表/报告标签页、精确的标签页顺序——请为对象分配一个自定义记录页面,并使用 record:related_list(或可内联编辑的 line_items)块显式排版。

表单中的字段条件规则

属于字段生命周期的条件规则 — visibleWhen、readonlyWhen、 requiredWhen — 应声明在数据模型字段上,而不是表单视图中; ObjectUI 表单会使用这些规则。其语义和服务端行为详见: objectstack-data → Conditional Field Rules。

UI 侧:内联主从网格会针对每个子行逐行评估这些规则。使用 requiredWhen — conditionalRequired 别名已在协议 17 中移除,现在会导致解析错误。编写复杂的 CEL 时,请加载 objectstack-formula。


详细规则

  • 列表、看板与甘特视图 — defineView、data、列、筛选、userFilters、工具栏搜索、排序、看板、甘特图。
  • 导航与运行模式 — App.create、三种运行模式、记录呈现。
  • 仪表板、报表与多维数据集 — 小组件、数据集绑定、筛选、compareTo、分桶、options、下钻、报表配置、多维数据集。
  • 页面与文档 — 页面类型、区域、组件、打印页面、html/react 源代码层级、样式、软件包文档。
  • 操作 — locations、可见性、示例、ctx、新标签页、参数。

应用导航

应用将对象、仪表板、报表和自定义页面整合为结构化的导航树。使用 @objectstack/spec/ui 中的 App.create({...}) 构建,并在 defineStack({ apps: [...] }) 中注册。

⛔ 每个 type: 'app' 软件包中只能有一个 App — 根据 ADR-0019(app-as-consumer-unit)D3,defineStack 在加载时会拒绝第二个 App。更多受众应通过这一个应用内部受 requiredPermissions 控制的分组来实现,绝不能创建第二个应用。

导航项类型
类型属性用途
grouplabel、icon、expanded、children[]可折叠的导航项分组
objectobjectName、viewName?、recordId?、filters?、label、icon链接到对象列表、命名视图、记录深层链接,或基础数据界面上的 filters 切片
dashboarddashboardName、label、icon链接到仪表板
reportreportName、label、icon链接到报表
pagepageName、label、icon链接到自定义页面
urlurl、label、icon外部或自定义 URL
actionactionDef({ actionName, params? })、label、icon执行操作,而非导航
componentcomponentRef、params?、label、icon内置平台组件;componentRef 是以冒号连接的注册表键(metadata:resource)
docbook? / doc?(必填其一)、label、icon软件包文档;继承文档受众
separator—视觉分隔符

requiresObject / requiresService: 对任何依赖可选系统对象或内核服务的导航项使用这些属性,这样对应项在依赖缺失时会自动隐藏 — 切勿硬编码条件 UI。


报表类型

类型适用场景
tabular包含列和筛选条件的扁平数据表
summary带小计的分组数据(例如按地区统计收入)
matrix交叉表 / 数据透视表(rows 向下 × columns 横向展开)
joined多区块分析界面(组合多个子报表)

没有 chart 报表类型 — 报表通过嵌入式 chart: 配置进行可视化。


CRM UI Blueprint(元数据优先)

将此 CRM 风格的结构用作规范的 UI 组装参考:

UI 界面典型位置遵循的模式
多视图对象 UIsrc/views/*.view.ts定义默认的 list + form,然后为不同场景定义命名的 listViews / formViews
公开表单src/views/*.view.ts(包含 sharing: { enabled: true, allowAnonymous: true, publicLink: 'slug' } 的 formView)Web-to-Lead,位于 GET/POST /api/v1/forms/:slug
应用导航src/apps/*.app.ts使用分组导航树、viewName 快捷方式和 requiresObject 实现感知能力的可见性
分析数据集src/datasets/*.dataset.ts每个需要用于报告的对象定义一个数据集——仪表板和报告会按名称绑定已声明的数据集,因此没有数据集的对象无法用于分析
仪表板src/dashboards/*.dashboard.ts组合 KPI、图表和表格组件,并共享 dateRange 与 globalFilters
报告src/reports/*.report.ts从该数据集中选择 rows(维度)+ values(度量);支持表格、摘要、矩阵和联接
记录页面src/pages/*.page.ts组合 regions + 组件(page:header、record:highlights、相关列表、标签页)
用户操作src/actions/*.actions.ts使用 flow 进行编排,使用 script 执行参数化批量变更

在“构建完整的元数据应用 UI”任务中,默认采用此蓝图。


日期宏 — 筛选占位符

列表视图、仪表板、报告和页面中的筛选值支持相对日期标记({today}、{current_month_start}、{30_days_ago})以及两个会话标记 {current_user_id} / {current_org_id}。**词汇表、两种可接受的拼写、两个解析器和易混淆项列表由 objectstack-query → rules/filters.md 维护——编写筛选标记前请先加载该文件。**无法识别的标记会导致构建错误和运行时抛出异常,绝不会被静默视为字面值。

本包负责以下两条规则,因为它们涉及的是 UI 界面,而非标记词汇表:

  • 筛选标记属于展示范围,而非安全机制。它决定界面显示什么;RLS 决定调用方可以读取什么。移除 {current_user_id} 筛选条件会扩大视图范围——绝不能让它成为阻止用户访问他人数据的唯一屏障。
  • AppContextSelector ID(例如 {active_package})仅在导航的 recordId / params 中解析。筛选条件不会使用侧边栏选择器的状态进行求值。

操作

操作是附加到对象或视图、由用户触发的操作。 在 defineStack({ actions: [...] }) 下注册。

操作类型
type用途必填字段
script在服务器上运行内联 L2 钩子代码体(沙盒化 JS)body(或将 target 设为已注册函数名称)
url导航至内部路由或外部 URLtarget
modal打开由 target 命名的页面;仅 script 会运行 bodytarget
flow按名称启动屏幕流或自动启动流target
api调用已注册的 API 端点target
form按名称打开 FormView(路由至 /_console/forms/:name)target

常见陷阱

  1. 在网格视图中放入过多列。 用户很少需要默认显示超过 6–8 列。次要列请使用 hidden。

  2. 忘记在主列上设置 link: true。 第一个有实际意义的列(通常是名称/主题)应作为指向记录详情的导航链接。

  3. 将组件的网格布局放在 position 中。 网格布局字段是 layout: { x, y, w, h }——position 不是组件键,封闭式架构会按名称拒绝它(在 protocol 17 之前,它会被静默丢弃)。layout 是可选的:省略时,组件会自动流式排列(Studio 设计器依赖此行为);只有在需要明确指定网格位置时才设置它。


验证你的工作

编写任何 *.view.ts / *.action.ts / *.dashboard.ts 后,在报告完成前运行创作时检查:

os validate     # CEL predicates (record.<field>) + widget bindings + schema
# or: os build  # the same gates, plus emits dist/

它会捕获两个特定于 UI 的陷阱,否则这两种情况在运行时都不会发出提示:

  • 操作/字段谓词 — 在操作的 visible/disabled 或字段的 visibleWhen 中使用裸字段引用(done 而不是 record.done),结果会变为 null,并在每条记录上隐藏控件(即“按钮从不显示”陷阱)。
  • 仪表板组件绑定 — 组件的 dataset/dimensions/values 无法解析到已声明的数据集/字段时,会渲染出空图表(ADR-0021)。

在 os validate 通过之前,不要报告视图/操作/仪表板已完成。在脚手架项目中,检查命令是 npm run validate。


参考资料

完整的 Zod 架构列表(附单行说明)请参阅 references/_index.md——其中包含指向 node_modules/@objectstack/spec/src/ 的引用。请始终Read源文件以确认准确的字段结构;不要依赖对属性名称的记忆。

评论

支持 Markdown