> ## Documentation Index
> Fetch the complete documentation index at: https://twenty-claude-cool-pascal-5ay683.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 页面布局

> 使用 `definePageLayout` 和 `definePageLayoutTab` 自定义记录详情页——包括选项卡、小部件以及前端组件的渲染位置。

**页面布局（page layout）** 控制记录详情页的排布方式：显示哪些选项卡，以及这些选项卡中包含哪些小部件。 使用 `definePageLayout()` 为你拥有的对象声明一个布局，或者使用 `definePageLayoutTab()` 为已存在的布局（你的自定义布局或标准的 Twenty 布局）添加单个选项卡。

| 用例                           | 实体                    |
| ---------------------------- | --------------------- |
| 为你拥有的对象上的记录页面定义完整布局          | `definePageLayout`    |
| 向现有布局中添加一个选项卡（你自己的对象或一个标准对象） | `definePageLayoutTab` |

## definePageLayout

当你拥有整个详情页时使用此方式——通常适用于你自己定义的自定义对象。

```ts src/page-layouts/example-record-page-layout.ts theme={null}
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object';
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';

export default definePageLayout({
  universalIdentifier: '203aeb94-6701-46d6-9af1-be2bbcc9e134',
  name: 'Example Record Page',
  type: 'RECORD_PAGE',
  objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
  tabs: [
    {
      universalIdentifier: '6ed26b60-a51d-4ad7-86dd-1c04c7f3cac5',
      title: 'Hello World',
      position: 50,
      icon: 'IconWorld',
      layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
      widgets: [
        {
          universalIdentifier: 'aa4234e0-2e5f-4c02-a96a-573449e2351d',
          title: 'Hello World',
          type: 'FRONT_COMPONENT',
          configuration: {
            configurationType: 'FRONT_COMPONENT',
            frontComponentUniversalIdentifier:
              HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
          },
        },
      ],
    },
  ],
});
```

### 关键点

* `type` 为 `'RECORD_INDEX'`、`'RECORD_PAGE'`、`'DASHBOARD'` 或 `'STANDALONE_PAGE'` 之一。 使用 `'RECORD_PAGE'` 来自定义特定对象的详情视图。
* `objectUniversalIdentifier` 指定此布局适用于哪个对象。
* 每个 `tab` 使用 `title`、`position` 和 `layoutMode` 定义页面的一个部分：记录页面和独立页面使用 `VERTICAL_LIST`，仪表板使用 `GRID`。 在 `VERTICAL_LIST` 选项卡中，单个小部件会以全宽方式渲染并占据整个选项卡；如果有多个小部件，它们会堆叠显示为卡片。 `GRID` 选项卡始终在 12 列网格上将其小部件布局为卡片，无论小部件数量多少都是如此，因此当你希望单个小部件填满页面时，请选择 `VERTICAL_LIST`。
* 请显式设置 `layoutMode`。 如果省略它，在 `STANDALONE_PAGE` 上会得到 `VERTICAL_LIST`，在其他地方会得到 `GRID`，而这在记录页面上通常不是你想要的效果。
* 选项卡内的每个 `widget` 可以渲染一个[前端组件](/l/zh/developers/extend/apps/layout/front-components)、关系列表或其他内置小部件类型。
* 选项卡上的 `position` 控制其顺序。 使用更高的值（例如 50）可将自定义选项卡放在内置选项卡之后。

### 字段小部件

`FIELD` 小部件会渲染记录中的一个字段。 对于关系字段，它还可以嵌入相关记录的列表：

```ts theme={null}
{
  universalIdentifier: 'c1c2c3c4-c5c6-4000-8000-000000000003',
  title: 'People → Opportunities',
  type: 'FIELD',
  configuration: {
    configurationType: 'FIELD',
    fieldMetadataId: PEOPLE_FIELD_UNIVERSAL_IDENTIFIER,
    fieldDisplayMode: 'TABLE',
    nestedRelationFieldMetadataId: OPPORTUNITIES_FIELD_UNIVERSAL_IDENTIFIER,
  },
}
```

* `fieldMetadataId` 接收布局对象上某个字段的全局标识符。
* `fieldDisplayMode` 的取值之一为 `'FIELD'`、`'CARD'`、`'EDITOR'`、`'VIEW'` 或 `'TABLE'`。 `TABLE` 会嵌入一个视图，用于列出一对多关系字段中的记录。
* `nestedRelationFieldMetadataId` 是可选的，并接收关系目标对象上一个一对多关系字段的通用标识符，用于列出跨两个关系跳数的记录（例如，在 Company 页面中列出该公司人员的机会，或在 Person 页面中列出该人员所在公司的机会）。 第一跳可以是一对多或多对一的关系字段，第二跳必须是一对多（不支持连接关系），并且需要 `fieldDisplayMode: 'TABLE'` — 将其与任何其他显示模式组合都会导致验证错误，因为嵌套小部件始终呈现为嵌入视图。

## definePageLayoutTab

当你只想在现有布局中**添加**一个选项卡时使用此方式——例如，在标准 Company 页面上添加一个分析选项卡，或者在你自己的对象布局上附加一个 AI 摘要选项卡。

```ts src/page-layouts/example-extra-tab.ts theme={null}
import {
  definePageLayoutTab,
  PageLayoutTabLayoutMode,
  STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';

export default definePageLayoutTab({
  universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001',
  pageLayoutUniversalIdentifier:
    STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage
      .universalIdentifier,
  title: 'Hello World',
  position: 1000,
  icon: 'IconWorld',
  layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
  widgets: [
    {
      universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000002',
      title: 'Hello World',
      type: 'FRONT_COMPONENT',
      configuration: {
        configurationType: 'FRONT_COMPONENT',
        frontComponentUniversalIdentifier:
          HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
      },
    },
  ],
});
```

### 关键点

* `pageLayoutUniversalIdentifier` 是**必需的**，并且必须在安装时指向一个已存在的页面布局——可以是标准的 Twenty 布局，也可以是由你自己的应用定义的布局。 当前不支持跨应用引用由其他已安装应用拥有的布局。 当父布局缺失时，安装会失败，并给出清晰的验证错误。

* 对于标准 Twenty 布局，从 `twenty-sdk/define` 导入标识符：

  ```ts theme={null}
  import { STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';

  // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage.universalIdentifier
  // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.personRecordPage.universalIdentifier
  // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.universalIdentifier
  // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.opportunityRecordPage.universalIdentifier
  // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.noteRecordPage.universalIdentifier
  // …
  ```

  每个布局条目还会公开其 `tabs` 及其 `widgets`，因此你可以引用任意层级：

  ```ts theme={null}
  STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.universalIdentifier
  STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.widgets.fields.universalIdentifier
  ```

  还提供了一个简短别名 `STANDARD_PAGE_LAYOUT`：

  ```ts theme={null}
  import { STANDARD_PAGE_LAYOUT } from 'twenty-sdk/define';

  STANDARD_PAGE_LAYOUT.companyRecordPage.universalIdentifier;
  ```

* `widgets` 仅作用于此选项卡——它们引用[前端组件](/l/zh/developers/extend/apps/layout/front-components)、视图等，其方式与在 `definePageLayout` 中内联定义的小部件完全相同。

* `position` 控制目标布局中相对于现有选项卡的排序。 选择一个取值，使你的选项卡相对于内置选项卡位于你想要的位置。

* 当你只想向现有布局进行添加时，请使用此功能，而不是 `definePageLayout`。 当你拥有整个布局时，请使用 `definePageLayout`。
