檢視
列表、表單、看板、日曆、甘特圖等 —— 每個物件介面是如何宣告的。
檢視是使用者檢視和編輯記錄的方式。檢視是宣告式後設資料 —— 與物件同一套 生命週期:宣告一次,隨包釋出,在任何地方渲染。
分兩類:
- 列表檢視 —— 視覺化多條記錄(表格、看板、日曆 ……)
- 表單檢視 —— 檢視 / 編輯單條記錄(simple、tabbed、wizard ……)
Schema 原始碼:
packages/spec/src/ui/view.zod.ts。
最短的宣告
即使你一個檢視都不宣告,每個物件也會自動獲得一個預設的表格列表和一個
simple 表單。要覆蓋或擴充套件它們,就在一個 defineView 容器裡宣告該物件的
檢視,放在物件旁邊而不是物件內部:list 是預設列表,form 是預設表單,
每個檢視都通過 data 指定它的物件。
// src/ui/views/support_ticket.view.ts
import { defineView } from '@objectstack/spec';
const data = { provider: 'object' as const, object: 'support_ticket' };
export const SupportTicketViews = defineView({
list: {
type: 'grid',
data,
columns: ['subject', 'status', 'priority', 'assignee'],
},
form: {
type: 'simple',
data,
sections: [
{ name: 'details', label: 'Details', fields: ['subject', 'description', 'priority', 'status', 'assignee'] },
],
},
});把容器註冊到技術棧上,緊挨著它所描述的物件:
// objectstack.config.ts
export default defineStack({
// ...
views: [SupportTicketViews],
});對於有多個檢視的物件(例如同一批資料上既有看板又有日曆),在同一個
容器裡新增具名檢視:listViews 供檢視切換器使用,formViews 是按名稱
開啟的表單。每個列表檢視都保留一個頂層的 columns,無論它是什麼型別:
export const SupportTicketViews = defineView({
list: { type: 'grid', data, columns: ['subject', 'status', 'priority', 'assignee'] },
listViews: {
by_status: {
label: 'By status', type: 'kanban', data, columns: ['subject', 'priority'],
kanban: { groupByField: 'status', columns: ['subject', 'priority'] },
},
schedule: {
label: 'Schedule', type: 'calendar', data, columns: ['subject', 'due_at'],
calendar: { startDateField: 'due_at', titleField: 'subject' },
},
by_owner: {
label: 'By owner', type: 'grid', data, columns: ['assignee', 'subject', 'status', 'priority'],
sort: [{ field: 'assignee', order: 'asc' }],
},
},
formViews: {
quick: { type: 'modal', data, sections: [{ name: 'quick', label: 'Quick edit', fields: ['status', 'priority', 'assignee'] }] },
full: {
type: 'tabbed', data,
sections: [
{ name: 'details', label: 'Details', fields: ['subject', 'description'] },
{ name: 'triage', label: 'Triage', fields: ['status', 'priority', 'assignee'] },
],
},
},
});這個容器及其規則在 ObjectStack 的 View metadata 中有完整說明。
列表檢視型別
每個列表檢視都需要一個頂層的 columns,無論它是什麼型別。某個型別的其餘
設定放在以該型別命名的配置塊中:
type | 渲染為 | 配置塊及其必需的鍵 | 常用的可選鍵 |
|---|---|---|---|
grid | 資料表格(預設) | — | |
kanban | 分列的看板 | kanban: { groupByField, columns } | summarizeField、titleField |
gallery | 卡片牆 | gallery(無必需鍵) | coverField、titleField |
calendar | 月 / 周 / 日 | calendar: { startDateField } | endDateField、titleField、colorField |
timeline | 按時間排列的資訊流 | timeline: { startDateField, titleField } | endDateField、groupByField |
gantt | 專案時間線 + 依賴 | gantt: { startDateField, endDateField, titleField } | progressField、dependenciesField |
map | 地理位置標記 | map(無必需鍵) | locationField,或 latitudeField + longitudeField |
tree | 自引用的層級 | tree(無必需鍵) | parentField、labelField |
chart | 內嵌圖表 | chart: { dataset, values } | chartType、dimensions |
下面的片段都是單個列表檢視。每個都放在一個 defineView 容器中 —— 作為它
的 list,或作為 listViews 的一個條目 —— 並像上面那樣用 data 指定它的
物件。
常用列表選項
// One list view; the container and `data` are omitted (see above).
// P is the predicate template tag: import { P } from '@objectstack/spec'
{
type: 'grid',
columns: ['subject','status','priority','assignee','created_at'],
filter: [ { field: 'archived', operator: 'equals', value: false } ],
sort: [ { field: 'created_at', order: 'desc' } ],
pagination: { pageSize: 25 },
searchableFields: ['subject','description'],
filterableFields: ['status','priority','assignee'],
navigation: { mode: 'drawer' }, // 'page' | 'drawer' | 'modal' | 'split' | 'popover' | 'new_window' | 'none'
selection: { type: 'multiple' }, // 'none' | 'single' | 'multiple'
rowActions: ['close_ticket','assign_to_me'],
bulkActions: ['bulk_close','bulk_export'],
conditionalFormatting: [
{ condition: P`record.priority == 'urgent'`, style: { background: '#fef2f2', fontWeight: '600' } } // style values are strings
],
exportOptions: ['csv','xlsx'],
emptyState: { title: 'No tickets yet', message: 'Create one to get started', icon: 'inbox' }
}filter 和 sort 會編譯為 ObjectQL;
rowActions 和 bulkActions 按名稱引用動作。
看板
// One list view; the container and `data` are omitted (see above).
{
type: 'kanban',
columns: ['subject', 'priority', 'assignee'], // still required at the top level
kanban: {
groupByField: 'status', // discrete field — usually a select
summarizeField: 'amount', // optional total per column
columns: ['subject', 'priority', 'assignee'] // fields shown on each card
}
}看板的列來自 groupByField 的取值;列的順序和顏色取自該欄位的 select
選項定義。
在列之間拖放會發出一次更新,設定被分組的欄位 —— 與手動編輯適用同樣的許可權 規則。
日曆
// One list view; the container and `data` are omitted (see above).
{
type: 'calendar',
columns: ['subject', 'start_at', 'end_at', 'priority'],
calendar: {
startDateField: 'start_at',
endDateField: 'end_at', // optional — single-point if omitted
titleField: 'subject',
colorField: 'priority' // optional — colours events by value
}
}甘特圖
// One list view; the container and `data` are omitted (see above).
{
type: 'gantt',
columns: ['name', 'start_at', 'due_at', 'percent_complete'],
gantt: {
startDateField: 'start_at',
endDateField: 'due_at',
titleField: 'name',
progressField: 'percent_complete', // optional, drives the progress bar
dependenciesField: 'depends_on' // optional — multiselect lookup to same object
}
}樹
// One list view; the container and `data` are omitted (see above).
{
type: 'tree',
columns: ['name'],
tree: {
parentField: 'parent_id', // self-lookup that builds the hierarchy
labelField: 'name', // shown indented in the first column
defaultExpandedDepth: 1 // optional — 0 = roots only, omit = expand all
}
}對於自引用的物件(分類、BOM、組織單元、巢狀評論),樹形表格會按 parentField
指標把行巢狀起來。
圖表(內嵌)
chart 列表檢視繫結一個具名資料集 —— 語義分析層(ADR-0021)—— 並
按名稱選擇它的維度和度量。指標定義一次,繫結到同一個資料集的每個圖表、
儀表盤和報表就保持一致:
// One list view; the container and `data` are omitted (see above).
{
type: 'chart',
columns: ['status'],
chart: {
chartType: 'bar', // 'bar' | 'line' | 'pie' | 'area' | 'scatter'
dataset: 'tickets_by_status', // a dataset declared with defineDataset(...)
dimensions: ['status'], // dataset dimension name(s) — X / group / split
values: ['ticket_count'] // dataset measure name(s) — the value axis
}
}內聯的 xAxisField / yAxisFields / aggregation / groupByField 形態已在
9.0 中移除 —— 現在每個圖表、儀表盤元件和報表都繫結一個數據集。更豐富的
跨物件分析請使用專門的報表介面。
表單檢視型別
type | 佈局 |
|---|---|
simple | 單列或分割槽塊(預設) |
tabbed | 標籤頁式區塊 |
wizard | 分步流程 |
split | 主從雙欄 |
drawer | 側邊面板表單 |
modal | 對話方塊表單 |
// One form view; the container and `data` are omitted (see above).
{
type: 'tabbed',
sections: [
{ label: 'Overview', fields: ['subject','status','priority','assignee'] },
{ label: 'Customer', fields: ['customer','contact','email','phone'] },
{ label: 'Resolution', fields: ['resolution_notes','resolved_at'] }
],
submitBehavior: { kind: 'next-record' } // 'thank-you' | 'redirect' | 'continue' | 'next-record'
}公開表單
表單可以設為允許匿名訪問:
// One form view; the container and `data` are omitted (see above).
{
type: 'simple',
sections: [ { fields: ['name','email','message'] } ],
sharing: {
enabled: true,
publicLink: 'contact-us', // slug under /forms/
allowAnonymous: true
},
submitBehavior: { kind: 'thank-you' }
}這會自動暴露 GET /api/v1/forms/contact-us 和
POST /api/v1/forms/contact-us/submit —— 不需要認證的公開表單路由。參見
REST API → 公開表單。
可見性、ARIA、主題
visibleOn: P\...`` —— 用在表單的區塊和欄位上,根據使用者 / 記錄 / 環境隱藏它們(CEL 謂詞)aria: { label, description, ... }—— 供螢幕閱讀器使用的 ARIA 屬性 (列表檢視和表單檢視)appearance: { showDescription, allowedVisualizations: [...] }—— 用在列表 檢視上,限制終端使用者可以切換到哪些列表型別
用對話構建
你通常不需要手寫檢視後設資料。告訴 AI Builder:
"為 support_ticket 物件新增一個按狀態分組的看板檢視,卡片顯示主題和 優先順序。按狀態把列標成紅 / 黃 / 綠。"
它會呼叫後設資料工具、把 diff 排入佇列,你批准之後檢視就會出現。參見 AI Builder。
另請參閱
- 資料模型 —— 檢視讀取的物件
- 動作 ——
rowActions/bulkActions引用的內容 - ObjectQL ——
filter/sort編譯成什麼 - CEL ——
conditionalFormatting、visibleOn @objectstack/spec/ui/view.zod.ts—— Schema