428 lines
13 KiB
Markdown
428 lines
13 KiB
Markdown
# OKR ContentBlock 富文本格式
|
||
|
||
OKR 的 Objective、KeyResult 中的 content/notes 字段使用 `ContentBlock` 富文本格式。本文档描述其结构和使用方式。
|
||
|
||
## 两种输入输出风格
|
||
|
||
OKR shortcuts 支持 `--style` 标志控制 content/notes 字段的输入输出格式:
|
||
|
||
| `--style` 值 | 说明 | 适用场景 |
|
||
|--------------|--------------------------------------------------------------------|--------------------------|
|
||
| `simple`(默认) | 半纯文本格式 `SemiPlainContent`,简化的 JSON 结构,仅包含 text、mention、docs、images | 大多数场景,简单易用 |
|
||
| `richtext` | 原始 `ContentBlock` 富文本格式,完整的块结构和样式信息 | 需要精确控制@提及用户位置、包含图片/文档链接时 |
|
||
|
||
**重要**:输入时严格根据 `--style` 值验证格式,不会自动检测。输出时读操作(如 `+cycle-detail`、`+progress-get`)根据 `--style` 返回对应格式。
|
||
|
||
## ContentBlock 结构概览
|
||
|
||
```json
|
||
{
|
||
"blocks": [
|
||
{
|
||
"block_element_type": "paragraph",
|
||
"paragraph": {
|
||
"style": {
|
||
"list": {
|
||
"list_type": "bullet",
|
||
"indent_level": 0,
|
||
"number": 1
|
||
}
|
||
},
|
||
"elements": [
|
||
{
|
||
"paragraph_element_type": "textRun",
|
||
"text_run": {
|
||
"text": "Hello World",
|
||
"style": {
|
||
"bold": true,
|
||
"strike_through": false,
|
||
"back_color": {
|
||
"red": 255,
|
||
"green": 0,
|
||
"blue": 0,
|
||
"alpha": 1
|
||
},
|
||
"text_color": {
|
||
"red": 0,
|
||
"green": 255,
|
||
"blue": 0,
|
||
"alpha": 1
|
||
},
|
||
"link": {
|
||
"url": "https://example.com"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"paragraph_element_type": "docsLink",
|
||
"docs_link": {
|
||
"url": "https://larkoffice.com/docx/xxx",
|
||
"title": "Lark Document"
|
||
}
|
||
},
|
||
{
|
||
"paragraph_element_type": "mention",
|
||
"mention": {
|
||
"user_id": "ou_xxx"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
},
|
||
{
|
||
"block_element_type": "gallery",
|
||
"gallery": {
|
||
"images": [
|
||
{
|
||
"file_token": "file_xxx",
|
||
"src": "https://...",
|
||
"width": 800,
|
||
"height": 600
|
||
}
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
## 类型定义
|
||
|
||
### ContentBlock
|
||
|
||
根级别内容块。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------|-------------------------|---------|
|
||
| `blocks` | `ContentBlockElement[]` | 内容块元素数组 |
|
||
|
||
### ContentBlockElement
|
||
|
||
内容块元素,支持段落或图库。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------------|--------------------|--------------------------------------------|
|
||
| `block_element_type` | `BlockElementType` | 块类型:`paragraph` \| `gallery` |
|
||
| `paragraph` | `ContentParagraph` | 段落内容(当 `block_element_type="paragraph"` 时) |
|
||
| `gallery` | `ContentGallery` | 图库内容(当 `block_element_type="gallery"` 时) |
|
||
|
||
### ContentParagraph
|
||
|
||
段落内容。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------------|-----------------------------|-------------|
|
||
| `style` | `ContentParagraphStyle` | 段落样式(列表类型等) |
|
||
| `elements` | `ContentParagraphElement[]` | 段落内元素数组 |
|
||
|
||
### ContentParagraphElement
|
||
|
||
段落内元素,支持文本、文档链接、提及。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|--------------------------|------------------------|-------------------------------------------|
|
||
| `paragraph_element_type` | `ParagraphElementType` | 元素类型:`textRun` \| `docsLink` \| `mention` |
|
||
| `text_run` | `ContentTextRun` | 文本内容 |
|
||
| `docs_link` | `ContentDocsLink` | 飞书文档链接 |
|
||
| `mention` | `ContentMention` | 用户提及 |
|
||
|
||
### ContentTextRun
|
||
|
||
文本块。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---------|--------------------|------|
|
||
| `text` | `string` | 文本内容 |
|
||
| `style` | `ContentTextStyle` | 文本样式 |
|
||
|
||
### ContentTextStyle
|
||
|
||
文本样式。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------------------|----------------|-------|
|
||
| `bold` | `boolean` | 是否粗体 |
|
||
| `strike_through` | `boolean` | 是否删除线 |
|
||
| `back_color` | `ContentColor` | 背景颜色 |
|
||
| `text_color` | `ContentColor` | 文字颜色 |
|
||
| `link` | `ContentLink` | 链接 |
|
||
|
||
### ContentColor
|
||
|
||
颜色。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---------|-----------|--------------|
|
||
| `red` | `int32` | 红色通道 (0-255) |
|
||
| `green` | `int32` | 绿色通道 (0-255) |
|
||
| `blue` | `int32` | 蓝色通道 (0-255) |
|
||
| `alpha` | `float64` | 透明度 (0-1) |
|
||
|
||
### ContentParagraphStyle
|
||
|
||
段落样式。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|--------|---------------|------|
|
||
| `list` | `ContentList` | 列表样式 |
|
||
|
||
### ContentList
|
||
|
||
列表样式。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------|------------|---------------------------------------------------------------------|
|
||
| `list_type` | `ListType` | 列表类型:`bullet` \| `number` \| `checkBox` \| `checkedBox` \| `indent` |
|
||
| `indent_level` | `int32` | 缩进层级 |
|
||
| `number` | `int32` | 序号(当 `list_type="number"` 时) |
|
||
|
||
### ContentGallery
|
||
|
||
图片块。目前仅有进展记录中的富文本支持展示图片。
|
||
|
||
由于 OKR 应用中进展页面的布局排版限制,一个 ContentGallery 元素中**仅可放置一个图片元素**,需要插入多张图片时需使用多个 ContentGallery 元素
|
||
(同一个 ContentGallery 中添加多个 image 会导致这些图片在狭窄的横向排版空间中互相挤占,效果很差)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------|----------------------|-------|
|
||
| `images` | `ContentImageItem[]` | 图片项数组 |
|
||
|
||
### ContentImageItem
|
||
|
||
图片项。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|--------------|-----------|----------|
|
||
| `file_token` | `string` | 文件 token |
|
||
| `src` | `string` | 图片 URL |
|
||
| `width` | `float64` | 宽度 |
|
||
| `height` | `float64` | 高度 |
|
||
|
||
> **如何获取 `file_token`?** 使用 [`+upload-image`](lark-okr-image-upload.md) 命令上传本地图片,返回的 `file_token` 可用于构建 `ContentGallery` 图片块。
|
||
|
||
### ContentDocsLink
|
||
|
||
飞书文档链接。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---------|----------|--------|
|
||
| `url` | `string` | 链接 URL |
|
||
| `title` | `string` | 链接标题 |
|
||
|
||
### ContentMention
|
||
|
||
提及。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------|----------|-------|
|
||
| `user_id` | `string` | 用户 ID |
|
||
|
||
### ContentLink
|
||
|
||
链接。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------|----------|--------|
|
||
| `url` | `string` | 链接 URL |
|
||
|
||
## SemiPlainContent 半纯文本格式
|
||
|
||
`SemiPlainContent` 是 `ContentBlock` 的简化、有损表示形式,适用于大多数不需要复杂格式的场景。
|
||
|
||
### 结构
|
||
|
||
```json
|
||
{
|
||
"text": "任务一 @{ou_zhangsan} ,任务二 @{ou_lisi} ",
|
||
"mention": ["ou_zhangsan", "ou_lisi"],
|
||
"docs": [
|
||
{
|
||
"title": "产品需求文档",
|
||
"url": "https://larkoffice.com/docx/xxx"
|
||
}
|
||
],
|
||
"images": [
|
||
"https://example.com/image.png"
|
||
]
|
||
}
|
||
```
|
||
|
||
### 类型定义
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------|------------------|-----------------------------------------------------------------------------------------------------------|
|
||
| `text` | `string` | 纯文本内容(必填,不能为空)。**输出时**包含 ` @{userID} ` 占位符以保留提及的位置上下文;**输入时** `@{...}` 占位符会被自动 strip 掉,只识别 `mention` 字段内容 |
|
||
| `mention` | `string[]` | 用户 ID 列表(可选),与 text 中的 `@{userID}` 占位符一一对应,输入时按顺序转换为 mention 元素**置于文本末尾** |
|
||
| `docs` | `SemiPlainDoc[]` | 文档列表(仅输出时包含,输入时 simple 风格不支持) |
|
||
| `images` | `string[]` | 图片 URL 列表(仅输出时包含,输入时 simple 风格不支持) |
|
||
|
||
### SemiPlainDoc
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---------|----------|--------|
|
||
| `title` | `string` | 文档标题 |
|
||
| `url` | `string` | 文档 URL |
|
||
|
||
### 双向转换说明
|
||
|
||
- **ContentBlock → SemiPlainContent**(输出时):提取纯文本、提及用户、文档链接和图片 URL,丢弃格式信息(粗体、列表、颜色等)。**提及的位置信息通过 ` @{userID} ` 占位符保留在 text 中**,同时 userID 也会被收集到 mention 数组中
|
||
- **SemiPlainContent → ContentBlock**(输入时):自动 strip 掉 text 中的 `@{...}` 占位符,然后将 text 和 mention 合并为单个段落,mention 按顺序附加在文本末尾。docs 和 images 在输入时被忽略(simple 风格不支持)
|
||
|
||
## 使用示例
|
||
|
||
### 示例 0:--style simple 半纯文本格式
|
||
|
||
```json
|
||
{
|
||
"text": "提升用户满意度",
|
||
"mention": ["ou_123"]
|
||
}
|
||
```
|
||
|
||
使用方式:
|
||
```bash
|
||
lark-cli okr +patch --level objective --style simple --target-id 123 --content '{"text":"提升用户满意度","mention":["ou_123"]}'
|
||
```
|
||
|
||
### 示例 1:简单文本段落(richtext 风格)
|
||
|
||
```json
|
||
{
|
||
"blocks": [
|
||
{
|
||
"block_element_type": "paragraph",
|
||
"paragraph": {
|
||
"elements": [
|
||
{
|
||
"paragraph_element_type": "textRun",
|
||
"text_run": {
|
||
"text": "提升用户满意度"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 示例 2:带格式的文本段落
|
||
|
||
```json
|
||
{
|
||
"blocks": [
|
||
{
|
||
"block_element_type": "paragraph",
|
||
"paragraph": {
|
||
"elements": [
|
||
{
|
||
"paragraph_element_type": "textRun",
|
||
"text_run": {
|
||
"text": "Q2 目标",
|
||
"style": {
|
||
"bold": true
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"paragraph_element_type": "textRun",
|
||
"text_run": {
|
||
"text": " - 提升产品质量"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 示例 3:带列表的段落
|
||
|
||
```json
|
||
{
|
||
"blocks": [
|
||
{
|
||
"block_element_type": "paragraph",
|
||
"paragraph": {
|
||
"style": {
|
||
"list": {
|
||
"list_type": "bullet",
|
||
"indent_level": 0
|
||
}
|
||
},
|
||
"elements": [
|
||
{
|
||
"paragraph_element_type": "textRun",
|
||
"text_run": {
|
||
"text": "完成功能开发"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
},
|
||
{
|
||
"block_element_type": "paragraph",
|
||
"paragraph": {
|
||
"style": {
|
||
"list": {
|
||
"list_type": "bullet",
|
||
"indent_level": 0
|
||
}
|
||
},
|
||
"elements": [
|
||
{
|
||
"paragraph_element_type": "textRun",
|
||
"text_run": {
|
||
"text": "进行用户测试"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 示例 4:带用户提及和图片(仅进展记录支持)的段落
|
||
|
||
```json
|
||
{
|
||
"blocks": [
|
||
{
|
||
"block_element_type": "paragraph",
|
||
"paragraph": {
|
||
"elements": [
|
||
{
|
||
"paragraph_element_type": "mention",
|
||
"mention": {
|
||
"user_id": "ou_example_user"
|
||
}
|
||
},
|
||
{
|
||
"paragraph_element_type": "textRun",
|
||
"text_run": {
|
||
"text": " 请关注此进度并查看以下图片"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
},
|
||
{
|
||
"block_element_type": "gallery",
|
||
"gallery": {
|
||
"images": [
|
||
{
|
||
"file_token": "img_example_token",
|
||
"src": "https://example.com/image.png",
|
||
"width": 800,
|
||
"height": 600
|
||
}
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|