snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
---
title: 扩展对象
description: 向标准 Twenty 对象添加字段(Person、Company 等) 或使用 defineField 向其他应用的对象添加字段。
icon: wand-magic-sparkles
---
使用 `defineField()` 为你不拥有的对象添加字段——例如像 Person 或 Company 这样的标准 Twenty 对象,或由其他已安装应用提供的对象。 与在 [`defineObject`](/l/zh/developers/extend/apps/data/objects) 中声明的内联字段不同,独立字段需要一个 `objectUniversalIdentifier` 来指定它们要扩展的对象。
```ts src/fields/company-loyalty-tier.field.ts
import { defineField, FieldType } from 'twenty-sdk/define';
export default defineField({
universalIdentifier: 'f2a1b3c4-d5e6-7890-abcd-ef1234567890',
objectUniversalIdentifier: '701aecb9-eb1c-4d84-9d94-b954b231b64b', // Company object
name: 'loyaltyTier',
type: FieldType.SELECT,
label: 'Loyalty Tier',
icon: 'IconStar',
options: [
{ value: 'BRONZE', label: 'Bronze', position: 0, color: 'orange' },
{ value: 'SILVER', label: 'Silver', position: 1, color: 'gray' },
{ value: 'GOLD', label: 'Gold', position: 2, color: 'yellow' },
],
});
```
## 关键点
* `objectUniversalIdentifier` 用于标识目标对象。 对于标准 Twenty 对象,从 `twenty-sdk` 导入常量:
```ts
import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier
// …
```
* 在 **`defineObject()` 内联定义字段** 时,**不需要** `objectUniversalIdentifier`——它会从父对象继承。
* `defineField()` 是为非通过 `defineObject()` 创建的对象添加字段的唯一方式。
* 文件位置由你决定。 约定是使用 `src/fields/\<name>.field.ts`,但 SDK 会在整个 `src/` 中检测字段。
* `writability` 也可以按字段单独设置:`MetadataWritability.APPLICATION` 将写入限制为你应用自身的逻辑函数,`MetadataWritability.SYSTEM` 保留用于平台管理的数据。 字段的可写性只能比其所属对象的级别更严格,读取操作不会受到影响。 有关完整语义,请参见[对象](/l/zh/developers/extend/apps/data/objects)。
* 要在标准页面布局(例如任务或公司详情页面)中添加一个选项卡,请使用 [`definePageLayoutTab`](/l/zh/developers/extend/apps/layout/page-layouts#definepagelayouttab),并结合来自 `twenty-sdk/define` 的 `STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS`。
## 向现有对象添加关系
要添加关系字段(例如将你的自定义对象链接到标准 `Person`),请将 `defineField()` 与 `FieldType.RELATION` 一起使用。 模式与内联关系相同,但需要显式设置 `objectUniversalIdentifier`。 有关双向模式,请参见[关系](/l/zh/developers/extend/apps/data/relations)。

View File

@@ -0,0 +1,143 @@
---
title: 对象
description: 使用 defineObject 声明新的记录类型——具有其自身字段的自定义表。
icon: table
---
自定义**对象**是你的应用在工作区中添加的新记录类型——Post Card、Invoice、Subscription,或任何特定于你业务领域的内容。 每个对象都会声明其模式(字段、关系、默认值)以及一个在多次同步和部署中保持稳定的通用标识符。
```ts src/objects/post-card.object.ts
import { defineObject, FieldType } from 'twenty-sdk/define';
enum PostCardStatus {
DRAFT = 'DRAFT',
SENT = 'SENT',
DELIVERED = 'DELIVERED',
RETURNED = 'RETURNED',
}
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
name: 'content',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
},
{
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
name: 'recipientName',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
},
{
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
name: 'recipientAddress',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
},
{
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
name: 'status',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
},
{
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
name: 'deliveredAt',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
},
],
});
```
## 关键点
* `universalIdentifier` 必须在各次部署间保持唯一且稳定。
* 每个字段都需要 `name`、`type`、`label` 以及其自身稳定的 `universalIdentifier`。
* `fields` 数组是可选的——你可以定义没有自定义字段的对象。
* `openRecordIn` 设置在点击此对象的记录时,它们会在何处打开:`ObjectOpenRecordIn.USER_CHOICE`(默认,遵循每位工作区成员在 Settings → Experience 中的个人偏好)、`ObjectOpenRecordIn.SIDE_PANEL`,或 `ObjectOpenRecordIn.RECORD_PAGE`。 将其固定为 `RECORD_PAGE`,用于那些需要完整页面才能正常使用的记录(就像工作流和仪表盘那样),或者固定为 `SIDE_PANEL`,用于那些只适合作为快速面板显示的记录(就像日历事件那样)。
* `writability` 控制在角色权限生效之前,哪些主体可以对该对象的记录进行任何写入操作:`MetadataWritability.OPEN`(默认值 — 由工作区角色决定)、`MetadataWritability.APPLICATION`(只有你的应用自身的逻辑函数可以创建、更新或删除记录;将其用于类似配置的对象,这类对象的记录定义行为,因此具有广泛记录访问权限的工作区成员无法通过 API 编辑这些记录)、或者 `MetadataWritability.SYSTEM`(保留用于平台管理的数据)。 读取不受影响 — 这是在服务器端强制执行的,不同于 `isUIEditable`,后者只会隐藏 UI 的可操作入口。 它同样存在于每个字段上,但其限制只能比对象级别更严格。
* 此处定义的内联字段**不**需要 `objectUniversalIdentifier`——它会从父对象继承。 使用[`defineField()`](/l/zh/developers/extend/apps/data/extending-objects)为你不拥有的对象添加字段。
* 你可以使用 `yarn twenty dev:add object` 脚手架创建新对象,它会引导你完成命名、字段和关系。 参见 [Architecture → Scaffolding entities](/l/zh/developers/extend/apps/getting-started/scaffolding)。
<Note>
**基础字段会自动添加。** 当你定义自定义对象时,Twenty 会为你创建标准字段,例如 `id`、`name`、`createdAt`、`updatedAt`、`createdBy`、`updatedBy` 和 `deletedAt`。 你无需在 `fields` 数组中声明这些字段——只需声明你的自定义字段。 你可以通过声明一个同名字段来覆盖默认字段,但这么做通常并不是一个好主意。
</Note>
## 字段类型
从 `twenty-sdk/define` 导出的完整 `FieldType` 值集合:
| 类别 | 类型 |
| ------ | ----------------------------------------------------------------------------------------------------------- |
| 文本 | `TEXT`、`RICH_TEXT`、`ARRAY`(字符串数组)、`RAW_JSON` |
| 数值 | `NUMBER`(`universalSettings.dataType`:`'float'` / `'int'` / `'bigint'`)、`NUMERIC`(任意精度)、`RATING`、`POSITION` |
| 日期 | `DATE`, `DATE_TIME` |
| 选项 | `BOOLEAN`、`SELECT`、`MULTI_SELECT` |
| 复合 | `FULL_NAME`、`ADDRESS`、`EMAILS`、`PHONES`、`LINKS`、`CURRENCY`、`ACTOR`、`FILES` |
| 标识符和关联 | `UUID`、`RELATION`、`MORPH_RELATION`(参见 [Relations](/l/zh/developers/extend/apps/data/relations)) |
| 系统 | `TS_VECTOR`(全文搜索向量,由服务器管理) |
复合类型会存储多个子字段(例如,`FULL_NAME` = 名 + 姓;`CURRENCY` = `amountMicros` + `currencyCode`)。 `SELECT` 和 `MULTI_SELECT` 需要一个如上示例所示的 `options` 数组。
## 默认值
字面量字符串默认值必须在字符串**内部**用单引号包裹——应写成 `defaultValue: "'Draft'"`,而不是 `defaultValue: "Draft"`。 这就是上面的 `status` 字段使用 `` `'${PostCardStatus.DRAFT}'` `` 的原因。
未加引号的字符串保留用于计算得到的默认值,这些默认值会在创建记录时进行求值:
* `'uuid'` — 生成一个 UUID(用于 `UUID` 字段)
* `'now'` — 当前时间戳(用于 `DATE_TIME` 字段)
同样的约定也适用于复合默认值的字符串子字段(例如 `ACTOR` 字段上的 `{ source: "'MANUAL'" }`)以及 `SELECT`/`MULTI_SELECT` 值。 在构建应用时,未加引号的字面字符串默认值会触发警告。
## 可空性
`isNullable` 控制字段是否接受 `NULL`。 它默认是 `true` —— 对可选字段可以省略。 将 `isNullable` 设为 `false`,可在数据库层面将字段设为必填。
对 `isNullable` 的更改会在每次同步时应用,包括更新现有字段的同步——因此你可以通过编辑 manifest 并重新同步来切换字段的可空性。
<Note>
**将现有字段改为不可为空时需要提供默认值。** 当你将字段更改为 `isNullable: false` 时,还必须提供一个非空的 `defaultValue`。 在应用 `NOT NULL` 约束之前,默认值会为所有现有的 `NULL` 行进行回填;如果没有默认值,同步将失败,并出现错误:`Default value cannot be null for non-nullable fields`。 关系字段和 `TS_VECTOR` 字段始终可为空,因此 `isNullable` 对它们不起作用。
</Note>
```ts
{
universalIdentifier: 'b1a7c0de-1234-4f00-9abc-000000000000',
name: 'reference',
type: FieldType.TEXT,
label: 'Reference',
isNullable: false,
defaultValue: "'N/A'",
}
```
## 接下来
* **将此对象与其他对象关联**——关于双向关系模式,参见 [Relations](/l/zh/developers/extend/apps/data/relations)。
* **为其他应用的对象添加字段**——关于 `defineField()`,参见 [Extending Objects](/l/zh/developers/extend/apps/data/extending-objects)。
* **在界面中显示此对象** — 请参阅 [导航菜单项](/l/zh/developers/extend/apps/layout/navigation-menu-items) 以添加侧边栏条目;请参阅 [视图](/l/zh/developers/extend/apps/layout/views) 以添加自定义列表配置。

View File

@@ -0,0 +1,101 @@
---
title: 概览
description: 定义你的应用在工作区中添加的数据结构——对象、字段和关系。
icon: database
---
Twenty 应用的 **数据层(data layer)** 是你的应用*添加*到工作区中的数据——它声明的新记录类型、它为现有对象添加的列,以及这些记录之间如何相互关联。
```text
┌──────────────────────────────────────────────────┐
│ Object — a record type, e.g. PostCard │
│ ├─ Field (name, type, label) │
│ ├─ Field │
│ └─ Relation (link to another object) │
└──────────────────────────────────────────────────┘
│
├── lives in your app, OR
│
▼
┌──────────────────────────────────────────────────┐
│ Standard / other apps' objects │
│ └─ Field added by your app via defineField │
└──────────────────────────────────────────────────┘
```
## 本节内容
<CardGroup cols={2}>
<Card title="对象" icon="table" href="/l/zh/developers/extend/apps/data/objects">
`defineObject` —— 声明具有自有字段的新记录类型。
</Card>
<Card title="扩展对象" icon="wand-magic-sparkles" href="/l/zh/developers/extend/apps/data/extending-objects">
`defineField` —— 向标准对象或其他应用的对象添加字段。
</Card>
<Card title="关系" icon="diagram-project" href="/l/zh/developers/extend/apps/data/relations">
对象之间的双向 `MANY_TO_ONE` / `ONE_TO_MANY` 连接。
</Card>
<Card title="时间线活动类型" icon="clock-rotate-left" href="/l/zh/developers/extend/apps/data/timeline-activity-types">
为记录时间线定义自动审计事件和显式应用事件。
</Card>
</CardGroup>
## 实体一览
| 实体 | 目的 | 定义方式 |
| ----------- | ----------------------------------------------------- | ------------------------------------------- |
| **对象** | 具有自有字段的新自定义记录类型(例如 PostCard、Invoice) | `defineObject()` |
| **字段** | 对象上的一列。 独立字段可以扩展你未创建的对象(例如向 Company 添加 `loyaltyTier`) | `defineField()` |
| **关系** | 两个对象之间的双向链接——双方都声明为字段 | 使用 `defineField()` 并指定 `FieldType.RELATION` |
| **索引** | 用于加速在某个对象上经常执行的查询的数据库索引 | `defineIndex()` |
| **时间线活动类型** | 显示在记录时间线中的自动审计事件或显式域事件 | `defineTimelineActivityType()` |
SDK 会在构建时通过 AST 分析检测这些内容,因此文件组织方式由你决定——约定是使用 `src/objects/`、`src/fields/`、`src/indexes/` 和 `src/timeline-activity-types/`。 稳定的 `universalIdentifier` UUID 在不同部署之间将一切关联在一起。
## 索引(可选)
应用可以随对象一同提供索引,以确保经常执行的查询保持快速。 最常见的情况是某个你经常读取的状态列或外键列。
```ts src/indexes/post-card-status.index.ts
import { defineIndex } from 'twenty-sdk/define';
import {
POST_CARD_UNIVERSAL_IDENTIFIER,
STATUS_FIELD_UNIVERSAL_IDENTIFIER,
} from '../objects/post-card.object';
export default defineIndex({
universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff0',
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
fields: [
{
universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff1',
fieldUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER,
},
],
});
```
### 唯一索引
`defineIndex` 接受 `isUnique: true`,可同时用于单列和多列表达唯一性约束。 这是推荐的基础方式——`defineField({ isUnique: true })` 已被弃用,并将在未来的版本中移除。
```ts
defineIndex({
universalIdentifier: '…',
objectUniversalIdentifier: PERSON_UNIVERSAL_IDENTIFIER,
isUnique: true,
fields: [{ universalIdentifier: '…', fieldUniversalIdentifier: EMAIL_FIELD_UNIVERSAL_IDENTIFIER }],
});
```
### 其他约束
* 部分 `WHERE` 子句由管理员控制——应用无法声明它们。
* 每个对象最多只能有 10 个自定义索引(框架自身的索引不计入其中)。
按照 Postgres 使用索引的方式来排列 `fields` 数组——最左边的列放在最前面,就像电话簿一样。 索引不是免费的:对表的每一次写入都会更新索引。 只有当你确实有查询需要某个索引时才添加它。
<Note>
在找 **Application Config** 或 **Roles & Permissions** 吗? 这些描述的是应用本身而不是它添加的数据——相关内容位于 [Config](/l/zh/developers/extend/apps/config/overview) 下。 在找 **Connections**(Linear、GitHub、Slack OAuth)吗? 这些用于*从*逻辑函数中调用,并位于 [Logic](/l/zh/developers/extend/apps/logic/connections) 下。
</Note>

View File

@@ -0,0 +1,184 @@
---
title: 关系
description: 使用双向 MANY_TO_ONE / ONE_TO_MANY 关系将对象连接在一起。
icon: diagram-project
---
关系用于将两个对象连接在一起。 在 Twenty 中,关系始终是 **双向的**——每个关系都有两侧,每一侧都作为一个字段声明,并引用另一侧。
| 关系类型 | 描述 | 是否有外键? |
| ------------- | ------------------- | ------------------- |
| `MANY_TO_ONE` | 该对象的多条记录指向目标对象的一条记录 | 是(`joinColumnName`) |
| `ONE_TO_MANY` | 该对象的一条记录拥有目标对象的多条记录 | 否(反向侧) |
## 关系如何工作
每个关系都需要两个相互引用的字段:
1. **MANY_TO_ONE** 侧——位于持有外键的对象上。
2. **ONE_TO_MANY** 侧——位于拥有集合的对象上。
两个字段都使用 `FieldType.RELATION`,并通过 `relationTargetFieldMetadataUniversalIdentifier` 相互交叉引用。
## 示例:Post Card 拥有多个收件人
一个 `PostCard` 可以发送给多个 `PostCardRecipient` 记录。 每个收件人只隶属于一张 PostCard。
**步骤 1:在 PostCard 上定义 ONE_TO_MANY 侧**(“一”侧):
```ts src/fields/post-card-recipients-on-post-card.field.ts
import { defineField, FieldType, RelationType } from 'twenty-sdk/define';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';
// Export so the other side can reference it
export const POST_CARD_RECIPIENTS_FIELD_ID = 'a1111111-1111-1111-1111-111111111111';
// Import from the other side
import { POST_CARD_FIELD_ID } from './post-card-on-post-card-recipient.field';
export default defineField({
universalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'postCardRecipients',
label: 'Post Card Recipients',
icon: 'IconUsers',
relationTargetObjectMetadataUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: POST_CARD_FIELD_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});
```
**步骤 2:在 PostCardRecipient 上定义 MANY_TO_ONE 侧**(“多”侧——持有外键):
```ts src/fields/post-card-on-post-card-recipient.field.ts
import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';
// Export so the other side can reference it
export const POST_CARD_FIELD_ID = 'b2222222-2222-2222-2222-222222222222';
// Import from the other side
import { POST_CARD_RECIPIENTS_FIELD_ID } from './post-card-recipients-on-post-card.field';
export default defineField({
universalIdentifier: POST_CARD_FIELD_ID,
objectUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'postCard',
label: 'Post Card',
icon: 'IconMail',
relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.CASCADE,
joinColumnName: 'postCardId',
},
});
```
<Note>
\*\*循环导入:\*\*两个关系字段相互引用对方的 `universalIdentifier`。 为避免循环导入问题,请在各自文件中将字段 ID 作为具名常量导出,并在另一个文件中导入它们。 构建系统会在编译时解析这些引用。
</Note>
## 联结关系
联结对象将一条源记录连接到一条或多条目标记录。 例如,`PostCardRecipient` 可以同时包含 `postCard` 关系和 `recipient` 关系。 在源对象的 `ONE_TO_MANY` 字段上,将 `junctionTargetFieldUniversalIdentifier` 设置为联结对象上指向目标的关系字段:
```ts
export default defineField({
universalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'postCardRecipients',
label: 'Post Card Recipients',
relationTargetObjectMetadataUniversalIdentifier:
POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: POST_CARD_FIELD_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
junctionTargetFieldUniversalIdentifier: RECIPIENT_FIELD_ID,
},
});
```
`RECIPIENT_FIELD_ID` 必须标识联结对象上的 `MANY_TO_ONE` 或 morph 关系。 当[时间线活动扇出](/l/zh/developers/extend/apps/data/timeline-activity-types#automatic-and-explicit-events)等功能遍历联结时,它会告知 Twenty 哪条相关记录是目标。
## 与标准对象建立关系
要与内置的 Twenty 对象(Person、Company 等)建立关系,请使用 `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`:
```ts src/fields/person-on-self-hosting-user.field.ts
import {
defineField,
FieldType,
RelationType,
OnDeleteAction,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER } from '../objects/self-hosting-user.object';
export const PERSON_FIELD_ID = 'c3333333-3333-3333-3333-333333333333';
export const SELF_HOSTING_USER_REVERSE_FIELD_ID = 'd4444444-4444-4444-4444-444444444444';
export default defineField({
universalIdentifier: PERSON_FIELD_ID,
objectUniversalIdentifier: SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'person',
label: 'Person',
description: 'Person matching with the self hosting user',
isNullable: true,
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier: SELF_HOSTING_USER_REVERSE_FIELD_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'personId',
},
});
```
## 关系字段属性
| 属性 | 必填 | 描述 |
| ---------------------------------------------------------- | ------------------ | -------------------------------------------------------------- |
| `type` | 是 | 必须为 `FieldType.RELATION` |
| `relationTargetObjectMetadataUniversalIdentifier` | 是 | 目标对象的 `universalIdentifier` |
| `relationTargetFieldMetadataUniversalIdentifier` | 是 | 目标对象上匹配字段的 `universalIdentifier` |
| `universalSettings.relationType` | 是 | `RelationType.MANY_TO_ONE` 或 `RelationType.ONE_TO_MANY` |
| `universalSettings.onDelete` | 仅适用于 MANY_TO_ONE | 当被引用的记录被删除时的处理方式:`CASCADE`、`SET_NULL`、`RESTRICT` 或 `NO_ACTION` |
| `universalSettings.joinColumnName` | 仅适用于 MANY_TO_ONE | 外键的数据库列名(例如,`postCardId`) |
| `universalSettings.junctionTargetFieldUniversalIdentifier` | 仅限联结 `ONE_TO_MANY` | 联结对象上指向扇出目标的关系字段 |
## 内联关系字段
你也可以直接在 [`defineObject`](/l/zh/developers/extend/apps/data/objects) 中声明关系。 以内联方式声明时,省略 `objectUniversalIdentifier`——它会从父对象继承:
```ts
export default defineObject({
universalIdentifier: '...',
nameSingular: 'postCardRecipient',
// ...
fields: [
{
universalIdentifier: POST_CARD_FIELD_ID,
type: FieldType.RELATION,
name: 'postCard',
label: 'Post Card',
relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.CASCADE,
joinColumnName: 'postCardId',
},
},
// … other fields
],
});
```

View File

@@ -0,0 +1,205 @@
---
title: 定位系统元数据
description: 解析 Twenty 在每个对象上自动预配置的元数据的确定性通用标识符,这样你的应用无需硬编码就能引用它。
icon: gears
---
Twenty 中的每个对象都带有你无需自行声明的**系统元数据**,例如一组字段以及带有列的主列表视图。 当对象被预配置时,服务器会创建所有这些内容,并且随着 Twenty 的发展,这个集合也会随之增长。
由于你不会声明它,因此也就没有可供你导入的 `universalIdentifier` 常量。 相反,服务器会以确定性方式**派生**每个标识符,而 `twenty-sdk` 暴露了相同的派生逻辑,这样你的 manifest 就能解析出服务器实际使用的精确值。
## 系统字段
存在于每个对象上的标量字段,你不会使用 [`defineField()`](/l/zh/developers/extend/apps/data/extending-objects) 来声明其中任何一个:
`id`, `createdAt`, `updatedAt`, `deletedAt`, `createdBy`, `updatedBy`, `position`, `searchVector`
那么,如何在[视图](/l/zh/developers/extend/apps/layout/views)中将 `createdAt` 作为一列来引用呢?
### 问题
从 Twenty 2.19 起,系统字段的通用标识符由服务器根据三个输入**确定性派生**:应用通用标识符、对象通用标识符以及字段名称。 自行构造一个 id 并将其硬编码是行不通的:它在服务器上不会匹配任何内容,同步时会拒绝这个悬空引用:
```
Dev sync failed: viewField: INVALID_VIEW_DATA: Field metadata not found
```
### 解决方案
<Note>
`getFieldUniversalIdentifier` 从 `twenty-sdk` 2.21 版本开始可用。
</Note>
使用 `getFieldUniversalIdentifier` 来解析与服务器使用的值完全相同的字段标识符。 它接收这三个输入,并返回字段的通用标识符:
```ts
import { getFieldUniversalIdentifier } from 'twenty-sdk/define';
const createdAtFieldId = getFieldUniversalIdentifier({
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: MY_OBJECT_UNIVERSAL_IDENTIFIER,
name: 'createdAt',
});
```
* `applicationUniversalIdentifier` 是你的应用标识符,也就是你传递给 [`defineApplication()`](/l/zh/developers/extend/apps/config/application) 的那个。
* `objectUniversalIdentifier` 是该字段所属对象的标识符。
* `name` 是系统字段名称,为上面列出的值之一。
### 示例:视图中的 createdAt 列
典型场景是:为你某个自定义对象的视图添加一列 `createdAt`。 解析字段 id,并像引用其他 `fieldMetadataUniversalIdentifier` 一样引用它:
```ts src/views/example-view.ts
import {
defineView,
getFieldUniversalIdentifier,
} from 'twenty-sdk/define';
const APPLICATION_UNIVERSAL_IDENTIFIER =
'0b04e15c-27b2-4741-9046-b32e07469072';
const MY_OBJECT_UNIVERSAL_IDENTIFIER =
'c782b61c-70fd-4c88-9cd6-4e61ab8d7591';
export default defineView({
universalIdentifier: '70f10d44-144a-4da8-8c6f-3ec2422138c0',
name: 'All records',
objectUniversalIdentifier: MY_OBJECT_UNIVERSAL_IDENTIFIER,
icon: 'IconList',
position: 0,
fields: [
{
universalIdentifier: '75a90bc4-d901-4df4-85e0-af29db5e0104',
fieldMetadataUniversalIdentifier: getFieldUniversalIdentifier({
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: MY_OBJECT_UNIVERSAL_IDENTIFIER,
name: 'createdAt',
}),
position: 0,
isVisible: true,
size: 200,
},
],
});
```
同一个解析得到的 id 可在任何需要 `fieldMetadataUniversalIdentifier` 的地方使用:视图字段、筛选器、排序、分组以及页面布局挂件。
<Note>
解析 id,而不是将其硬编码。 由于服务器是根据应用 id、对象 id 和字段名来派生该值的,调用
`getFieldUniversalIdentifier` 能在这些输入发生变化时仍保持引用正确,并在派生逻辑演进时避免偏差。
</Note>
### 系统关系字段
<Note>
`getSystemRelationFieldUniversalIdentifier` 可在 `twenty-sdk`
2.23 及更高版本中使用,并且需要 Twenty 服务器版本为 2.23 或更高。
</Note>
除了上面的标量系统字段之外,服务器还会在每个对象上预置四个**系统关系字段**:`timelineActivities`、`attachments`、`noteTargets` 和 `taskTargets`,它们各自指向相应的标准关系对象。
这些字段不会通过 `getFieldUniversalIdentifier` 解析:其标识符不依赖名称,而是由承载该字段的对象和该字段指向的对象推导而来。 通过这种方式,重命名对象永远不会改变其关系字段的标识符。
使用 `getSystemRelationFieldUniversalIdentifier` 来解析它们:
```ts
import {
getSystemRelationFieldUniversalIdentifier,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
// rocket.attachments — the relation field hosted on your custom object
const rocketAttachmentsFieldId = getSystemRelationFieldUniversalIdentifier({
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: ROCKET_OBJECT_UNIVERSAL_IDENTIFIER,
relationTargetObjectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment.universalIdentifier,
});
```
* `objectUniversalIdentifier` 是**承载**该字段的对象。
* `relationTargetObjectUniversalIdentifier` 是该字段**指向**的对象。
方向由参数的顺序编码。 要解析反向端(例如 `attachment.targetRocket`,即服务器在标准关系对象上创建的 morph 字段),交换这两个参数:
```ts
// attachment.targetRocket — the reverse morph field on Attachment
const attachmentTargetRocketFieldId =
getSystemRelationFieldUniversalIdentifier({
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment.universalIdentifier,
relationTargetObjectUniversalIdentifier: ROCKET_OBJECT_UNIVERSAL_IDENTIFIER,
});
```
与标量系统字段一样,解析得到的 id 可在任何需要 `fieldMetadataUniversalIdentifier` 的地方使用。
## 系统视图
<Note>
`getSystemViewUniversalIdentifier` 和 `getSystemViewFieldUniversalIdentifier` 可在 `twenty-sdk` 2.26 及更高版本中使用,并且需要 Twenty 服务器版本为 2.26 或更高。
</Note>
服务器还会在每个对象上预配置一个**系统视图**:主列表视图(`All {objectLabelPlural}`,以 `ViewKey.INDEX`(由 `SYSTEM_VIEW_KEYS.INDEX` 派生)作为键),其中每个可显示字段对应一列。 与系统关联字段类似,它们的标识符是以**与名称无关**的方式派生的,因此重命名对象或字段都不会改变这些标识符。
使用 `getSystemViewUniversalIdentifier` 来解析该视图:
```ts
import {
getSystemViewUniversalIdentifier,
SYSTEM_VIEW_KEYS,
} from 'twenty-sdk/define';
const rocketIndexViewId = getSystemViewUniversalIdentifier({
objectMetadataApplicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: ROCKET_OBJECT_UNIVERSAL_IDENTIFIER,
viewKey: SYSTEM_VIEW_KEYS.INDEX,
});
```
* `objectMetadataApplicationUniversalIdentifier` 是拥有该**对象**的应用程序,也是视图命名空间所基于的应用。
* `objectUniversalIdentifier` 是该视图所列出的对象。
* `viewKey` 是系统视图的区分符:主列表视图使用 `SYSTEM_VIEW_KEYS.INDEX`,记录页面字段小部件视图使用 `SYSTEM_VIEW_KEYS.FIELDS_WIDGET`。 它作为派生的键;只有 `INDEX` 同时会存储在视图行中。
解析得到的 id 可用于任何需要 `viewUniversalIdentifier` 的地方,例如侧边栏中的 [`NavigationMenuItemType.VIEW`](/l/zh/developers/extend/apps/layout/navigation-menu-items) 条目。 如果只是要打开某个对象的主列表,优先使用带有 `targetObjectUniversalIdentifier` 的 `NavigationMenuItemType.OBJECT`:它不需要派生。
`getSystemViewFieldUniversalIdentifier` 会从视图以及其显示的字段中解析系统视图上的单个**列**:
```ts
import { getSystemViewFieldUniversalIdentifier } from 'twenty-sdk/define';
const rocketNameColumnId = getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
viewUniversalIdentifier: rocketIndexViewId,
fieldMetadataUniversalIdentifier: ROCKET_NAME_FIELD_UNIVERSAL_IDENTIFIER,
});
```
注意第一个参数:列是由**其显示的字段**所属的应用程序进行命名空间划分的,而不是由拥有该视图的应用程序进行划分。 你的应用向标准对象添加的字段,会在 Twenty 拥有的视图上,以你的应用为命名空间来派生其列。
<Warning>
系统视图及其列是由**服务器所有**的:解析它们的标识符是为了引用它们,而不是为了声明它们。 [`defineView()`](/l/zh/developers/extend/apps/layout/views) 上的 `key` 已被弃用且会被忽略,因此 manifest 视图永远无法占用 `INDEX` 键,并且服务器已经为你添加的每个字段预配置了一列,所以在系统视图上为同一个字段声明你自己的 `defineViewField()` 会与其发生冲突。
</Warning>
## 标准 Twenty 对象
对于**标准** Twenty 对象(Person、Company、Opportunity 等),你不需要进行任何派生:字段和视图的标识符都是预先计算好的常量,你可以直接导入。
```ts
import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.createdAt.universalIdentifier
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields.updatedAt.universalIdentifier
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.views.allPeople.universalIdentifier
```
当对象是由**你的应用**使用 [`defineObject()`](/l/zh/developers/extend/apps/data/objects) 定义、且不存在此类常量时,再使用上面的辅助方法。
<Note>
`name` 是一个**默认**字段,而不是系统字段。 它保留自己的硬编码
通用标识符,而不是通过
`getFieldUniversalIdentifier` 解析。 在你定义的对象上,请使用你在 `defineObject()` 中赋予它的标识符来引用
`name` 字段。
</Note>

View File

@@ -0,0 +1,184 @@
---
title: "时间线活动类型们。 "
description: 定义在记录时间线上呈现的自动审计事件和显式应用事件。
icon: clock-rotate-left
---
时间线活动类型定义了在记录时间线上显示的事件所使用的稳定词汇。 标准对象和应用对象使用相同的约定:类型具有标签和图标,可选择声明其发出时间,还可选择通过你的应用的[前端组件](/l/zh/developers/extend/apps/layout/front-components)之一进行呈现。
<Note>
时间线活动类型目前处于 Beta 阶段,将很快在 Twenty 2.34 中推出。 该
API 可能会随着我们了解应用开发者的使用场景而演进。
</Note>
使用脚手架工具创建一个:
```bash filename="Terminal"
yarn twenty dev:add timelineActivityType
```
或直接定义:
```ts filename="src/timeline-activity-types/post-card-created.ts"
import { defineTimelineActivityType } from 'twenty-sdk/define';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
export default defineTimelineActivityType({
universalIdentifier: 'f4fa646c-6e11-4d8f-a6be-c3b7a2fc7500',
name: 'postCardCreated',
label: 'created a post card',
icon: 'IconMail',
emit: {
on: 'created',
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
},
});
```
## 自动事件和显式事件
当 Twenty 应自动创建此类型时,添加 `emit`。 `emit.on` 支持 `created`、`updated`、`deleted`、`restored`、`linked` 和 `unlinked`,而 `emit.objectUniversalIdentifier` 用于标识源对象。 在一个工作区中,只有一个有效类型可以处理相同的 emit 键——`on`、对象和可选的通过关系。
如果没有 `emit.through`,事件将写入源记录自身的时间线。 要让它发到相关的记录,请设置 `emit.access'. elationFieldUniversalIdentifier` 指向一个直接的多对一个关系或源对象上的[一个对多的交接关系](/l/zh/developers/extend/apps/data/relations#junction-relations)。 变形关系向其摩擦组的每个成员粉碎,因此单一声明可以针对几个对象类型。
就直接关系而言,创建或恢复源记录产生`链接`,删除它产生`unlined`, 并重新指出关系会在前一个目标上产生`unlinked`加上新目标上的`linked`。 其他源更新不会产生链接事件。 这是附加物所使用的合同,其目标是一种直接的停尸关系。
对于联接关系,`universalSettings.crosstionTargetFieldUniversalIdentifier` 可以识别联接对象与目标之间的关系。 创建或删除一个命令行会产生`linked`或`unlinked`。 指明一个联接行的任何一方也会产生一个联接事件;不相关联的联接字段的更新则不会产生联接事件。
`linked`和`unlinked`事件需要`emit.that`, 因为它们的触发是对配置的直接关系或交叉关系的改变。
例如,这是备注、任务、消息和日历事件所使用的同一通用约定:
```ts filename="src/timeline-activity-types/post-card-linked.ts"
import { defineTimelineActivityType } from 'twenty-sdk/define';
import { POST_CARD_RECIPIENTS_FIELD_UNIVERSAL_IDENTIFIER } from '../fields/post-card-recipients-on-post-card.field';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
export default defineTimelineActivityType({
universalIdentifier: 'f4fa646c-6e11-4d8f-a6be-c3b7a2fc7502',
name: 'postCardLinked',
label: 'received a post card',
icon: 'IconMail',
emit: {
on: 'linked',
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
through: {
relationFieldUniversalIdentifier:
POST_CARD_RECIPIENTS_FIELD_UNIVERSAL_IDENTIFIER,
},
},
});
```
默认情况下,每次源更新都会发出一个 `updated` 通过事件。 当只有选定源字段的更改应显示在目标时间线上时,设置 `emit.through.triggerFieldUniversalIdentifiers`。
对于由你的逻辑函数自行创建的显式领域事件,请省略 `emit`。 这样可避免针对同一操作同时生成自动审计行和显式行。
```ts filename="src/timeline-activity-types/post-card-sent.ts"
import { defineTimelineActivityType } from 'twenty-sdk/define';
export default defineTimelineActivityType({
universalIdentifier: 'f4fa646c-6e11-4d8f-a6be-c3b7a2fc7501',
name: 'postCardSent',
label: 'sent a post card',
icon: 'IconSend',
});
```
使用 [`createTimelineActivity()`](/l/zh/developers/extend/apps/logic/logic-functions#create-a-timeline-activity) 创建显式事件。 应用代码使用稳定的通用标识符;Twenty 会解析特定于安装的元数据 ID。
## 自定义呈现
如果没有前端组件,Twenty 会根据类型标签、图标和关联对象元数据呈现原生通用行。 这适用于没有对象专用渲染器的标准对象和自定义对象。
如需自定义详细信息,请将 `frontComponentUniversalIdentifier` 设置为由同一应用拥有的前端组件:
```ts
export default defineTimelineActivityType({
universalIdentifier: 'f4fa646c-6e11-4d8f-a6be-c3b7a2fc7501',
name: 'postCardSent',
label: 'sent a post card',
icon: 'IconSend',
frontComponentUniversalIdentifier: '88c15ae2-5f87-4a6b-b48f-1974bbe62eb7',
});
```
原生行仍保持为折叠状态下的呈现形式。 Twenty 仅在用户展开该行后才挂载前端组件,从而避免为每个可见事件创建沙盒和工作线程。 在组件内部,从 `twenty-sdk/front-component` 调用 `useTimelineActivityId()` 以读取行 ID,并获取呈现所需的任何数据。 当组件在时间线行外部呈现时,它会返回 `null`。
## 覆盖其他应用的呈现方式
应用拥有其自身对象的自动时间线约定。 要自定义由其他应用拥有的对象上的事件,请使用 `replacesTimelineActivityTypeUniversalIdentifier` 显式声明现有类型:
```ts
export default defineTimelineActivityType({
universalIdentifier: 'f4fa646c-6e11-4d8f-a6be-c3b7a2fc7503',
name: 'companyCreatedWithDeploymentContext',
label: 'was created from a deployment',
emit: {
on: 'created',
objectUniversalIdentifier: COMPANY_UNIVERSAL_IDENTIFIER,
},
replacesTimelineActivityTypeUniversalIdentifier:
RECORD_CREATED_TIMELINE_ACTIVITY_TYPE_UNIVERSAL_IDENTIFIER,
frontComponentUniversalIdentifier: '88c15ae2-5f87-4a6b-b48f-1974bbe62eb7',
});
```
覆盖必须包含 `emit`,因为它替换的是现有的自动发出槽位,而不是仅显式类型。 被引用的类型必须属于目标对象的应用,并描述相同的操作和路由。 移除覆盖应用会恢复基础类型;移除基础类型会禁用覆盖。
## 工作区覆盖和静默
工作区管理员无需分叉应用即可更改类型的呈现方式,或将其自动和显式事件设为静默。 使用元数据 API 的 `updateTimelineActivityType` 变更,并传入特定于安装的类型 ID:
```graphql
mutation CustomizeTimelineActivityType(
$id: UUID!
$label: String
$icon: String
$isActive: Boolean
) {
updateTimelineActivityType(
input: { id: $id, label: $label, icon: $icon, isActive: $isActive }
) {
id
label
icon
isActive
}
}
```
`label` 和 `icon` 会存储为工作区覆盖,因此之后的应用更新不会覆盖管理员的选择。 将 `isActive` 设为 `false` 可停止自动发出,并拒绝该类型的新显式事件。 现有行仍然可见。
使用以下方式恢复应用默认值,包括活动状态:
```graphql
mutation ResetTimelineActivityType($id: UUID!) {
resetTimelineActivityType(id: $id) {
id
label
icon
isActive
}
}
```
## 配置字段
| 字段 | 必填 | 描述 |
| ------------------------------------------------- | ------- | ---------------------- |
| `universalIdentifier` | 是 | 跨安装和升级保持稳定的类型 UUID |
| `name` | 是 | 应用内本地编程名称;在应用内必须唯一 |
| `label` | 是 | 由原生渲染器使用的面向用户的操作文本 |
| `icon` | 否 | 显示在事件旁的 Twenty 图标名称 |
| `emit` | 否 | 自动发出声明;对于仅显式类型则省略 |
| `emit.on` | 使用 emit | 导致写入此类型的审计操作 |
| `emit.objectUniversalIdentifier` | 使用 emit | 其记录会发出此类型的对象 |
| `emit.through.relationFieldUniversalIdentifier` | 否 | 用于切换到相关时限的直接或交接关系 |
| `emit.through.triggerFieldUniversalIdentifiers` | 否 | 可触发 `updated` 通过事件的源字段 |
| `frontComponentUniversalIdentifier` | 否 | 当行扩展时挂载应用前台组件 |
| `replacesTimelineActivityTypeUniversalIdentifier` | 否 | 现有的发射类型已替换;需要 `emit` |
当创建事件时,二十张快照会出现语义特征和持久演示效果的退缩。 历史行保留其原始动作和对象含义。 此类型已安装,其当前翻译的标签、图标和前面组件正在运行;卸载后,快照回退将保持行可读。 分辨率使用通用标识符,因此在重新安装应用时也可以存活。