跳到主要内容

自定义卡片消息结构设计

快速开始

什么是卡片消息?
卡片消息是一种富文本消息格式,支持文本、图片、按钮、表单等多种组件的组合,可以创建交互性强的消息卡片。

适用场景:

  • ✅ 审批流程(通过/拒绝按钮 + 审批信息)
  • ✅ 数据报表(图表 + 关键指标 + 筛选器)
  • ✅ 通知提醒(重要信息高亮 + 操作按钮)
  • ✅ 调研问卷(问题 + 选项 + 提交按钮)
  • ✅ 会议邀请(时间选择器 + 参会人员 + 确认按钮)

5 分钟快速上手

下面是一个简单的审批卡片示例:

{
"ver": 1,
"content": {
"blocks": [
{
"name": "section",
"text": {
"name": "text",
"content": "请假审批申请",
"type": 0,
"color": "#222222",
"size": 32
}
},
{
"name": "divider"
},
{
"name": "actionGroup",
"actions": [
{
"name": "button",
"key": "approve",
"type": 0,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "同意"
},
"actionType": 1,
"value": "approve"
}
},
{
"name": "button",
"key": "reject",
"type": 1,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "拒绝"
},
"actionType": 1,
"value": "reject"
}
}
]
}
]
}
}

效果预览:

┌─────────────────────────────┐
│ 请假审批申请 │
├─────────────────────────────┤
│ │
│ [同意] [拒绝] │
└─────────────────────────────┘

消息结构

卡片消息的整体结构如下:

基础 JSON 结构:

{
"[field1]": "",
"[field2]": "",
"content": {
"[globalField]": "[Object]",
"blocks": [
{
"name": "[blockName]",
"[blockAttr]": "[AttributeObject]"
}
]
}
}

名词解释

术语说明
field消息附加字段
content卡片内容部分
globalField卡片内容的全局字段
blocks块级元素
blockName块名称
blockAttr块级元素属性
Object对应字段的实体
AttributeObject属性实体

1. field 消息附加字段

{
"ver": 1 // 消息使用的组件版本
}

2. globalField 字段内容

当前版本暂无全局字段配置。

AttributeObject 属性实体

描述:代表一个元素所带的属性,比如文本、图片等等。

注意:属性不能直接放到 blocks 下渲染,只能在元素内部中作为属性使用。

格式如下

"[blockAttr]": {
"name": "[AttributeName]",
"[attr1]": "[value]",
"[attr2]": "[AttributeObject]"
}

支持的属性类型

1. 文本

{
"name": "text",
"content": "文本内容",
"color": "#FF00FF", // 非 markdown 下生效 颜色字符串 例如 #FF00FF
"size": 28, // 非 markdown 下生效 文字大小 单位 px 默认 28
"type": 0, // 0 普通文本 1 markdown
"maxLine": 2 // 最大行数,超出部分省略...
}

2. 图片

{
"name": "img",
"imgUrl": "" // 图片 url
}

3. 链接

{
"name": "link",
"url": "" // 跳转的 url
}

4. 选项

{
"name": "option",
"text": {}, // 文本对象,显示文字
"value": "" // 选项值
}

5. 操作

{
"name": "action",
"text": {}, // 文本对象,显示文字
"actionType": 0, // 操作类型 0 跳转地址 1 提交给服务器 2 提交整个消息的所有表单项
"link": {}, // 链接对象,跳转地址
"value": "" // type = 1,2 时作为值提交给服务器
}

组件元素详解

重要:组件元素不能直接在 blocks 下直接使用,需要使用容器元素包装一层。

1. 文本组件

用于显示文本内容的基础组件。

{
"name": "textBox",
"text": {
"name": "text",
"content": "这是一段文本内容",
"type": 0,
"color": "#333333",
"size": 28,
"maxLine": 2
}
}

属性说明:

属性类型默认值说明
contentstring-文本内容
typenumber00-普通文本,1-markdown
colorstring#222222颜色值,hex 格式
sizenumber28字体大小,单位 px
maxLinenumber-1最大行数,-1 表示不限制

使用技巧:

  • 使用 \n 实现换行
  • maxLine 超出后会显示省略号
  • markdown 模式下支持基础语法

2. 图片

用于展示图片内容。

{
"name": "imageBox",
"img": {
"name": "img",
"imgUrl": "https://example.com/image.png"
}
}

属性说明:

属性类型说明
imgUrlstring图片的 URL 地址

注意事项:

  • 图片会自动适应容器宽度
  • 建议使用 HTTPS 协议的图片链接
  • 推荐图片尺寸:宽度不超过 600px

3. 日期选择器

允许用户选择日期和时间。

{
"name": "datetimePicker",
"key": "meeting_time",
"defaultValue": 15333333333
}

属性说明:

属性类型说明
keystring字段标识,用于提交时识别
defaultValuenumber默认时间戳(毫秒)

使用场景:

  • 会议时间选择
  • 预约时间设置
  • 截止日期选择

示例 - 会议预约:

{
"name": "section",
"text": {
"name": "text",
"content": "请选择会议时间"
},
"extra": {
"name": "datetimePicker",
"key": "meeting_time",
"defaultValue": 1672531200000
}
}

4. 文本输入框

允许用户输入文本内容。

{
"name": "textInput",
"key": "feedback",
"line": 3,
"defaultValue": {
"name": "text",
"content": "请输入您的反馈"
}
}

属性说明:

属性类型默认值说明
keystring-字段标识
linenumber1显示行数
defaultValuetext 对象-默认文本

使用场景:

  • 意见反馈
  • 备注说明
  • 自定义输入

5. 下拉菜单

提供选项列表供用户选择。

{
"name": "dropdown",
"key": "priority",
"defaultValue": "medium",
"options": [
{
"name": "option",
"text": {
"name": "text",
"content": "高优先级"
},
"value": "high"
},
{
"name": "option",
"text": {
"name": "text",
"content": "中优先级"
},
"value": "medium"
},
{
"name": "option",
"text": {
"name": "text",
"content": "低优先级"
},
"value": "low"
}
]
}

属性说明:

属性类型说明
keystring字段标识
defaultValuestring默认选中的 value
optionsarrayoption 对象列表

注意事项:

  • 每个 option 必须包含 text 和 value
  • value 必须是唯一的字符串
  • 建议选项数量控制在 3-8 个

6. 按钮

可点击的交互按钮。

{
"name": "button",
"key": "submit_btn",
"type": 0,
"width": 0,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "提交"
},
"actionType": 1,
"value": "submit_data"
}
}

属性说明:

属性类型默认值说明
keystring-按钮标识
typenumber00-主要按钮,1-次要按钮,2-常用按钮
widthnumber00-铺满整行,-1-自适应
actionaction 对象-点击后的操作

按钮类型效果对比:

  • type: 0 (主要按钮):实心填充,用于最重要的操作
  • type: 1 (次要按钮):描边样式,用于取消、返回等次要操作
  • type: 2 (常用按钮):浅色背景,用于常规操作

actionType 说明:

  • 0: 跳转地址 - 打开链接
  • 1: 提交给服务器 - 提交当前表单数据
  • 2: 提交整个消息 - 提交消息中所有表单项

7. 图标链接

带图标的可点击链接。

{
"name": "iconLink",
"key": "help_link",
"action": {
"name": "action",
"text": {
"name": "text",
"content": "帮助中心"
},
"actionType": 0,
"link": {
"name": "link",
"url": "https://example.com/help"
}
},
"icon": {
"name": "img",
"imgUrl": "https://example.com/icon.png"
}
}

属性说明:

属性类型说明
keystring链接标识
actionaction 对象点击后的操作
iconimg 对象显示的图标

8. 图标链接组(横向排列)

横向排列的多个图标链接,最多支持两项。

{
"name": "iconLinkGroup",
"links": [
{
"name": "iconLink",
"key": "like",
"action": {
"name": "action",
"text": {
"name": "text",
"content": "点赞"
},
"actionType": 1,
"value": "like"
},
"icon": {
"name": "img",
"imgUrl": "https://example.com/like.png"
}
},
{
"name": "iconLink",
"key": "share",
"action": {
"name": "action",
"text": {
"name": "text",
"content": "分享"
},
"actionType": 1,
"value": "share"
},
"icon": {
"name": "img",
"imgUrl": "https://example.com/share.png"
}
}
]
}

注意事项:

  • ⚠️ 最多只能添加 2 个图标链接
  • 会自动横向排列显示
  • 适合用于点赞、收藏、分享等操作

容器元素 block

块级元素直接在 blocks 下,也可以存在 block 嵌套。

1. 分割线

{
"name": "divider"
}

2. 块布局

{
"name": "section",
"text": {
"name": "text",
"content": "块布局文本内容"
},
"extra": {
"name": "imageBox",
"img": {
"name": "img",
"imgUrl": "https://example.com/image.png"
}
}
}

属性说明:

  • text: text 对象,显示在块布局中的文本内容
  • extra: 支持任何组件,通常用于放置图片、按钮等额外元素

使用场景:

  • 左侧文本 + 右侧图片的图文混排
  • 标题 + 图标的组合展示
  • 多行信息 + 操作按钮的垂直布局

示例 - 图文混排:

{
"name": "section",
"text": {
"name": "text",
"content": "新产品上线通知\n点击查看详细信息",
"type": 0,
"color": "#333333",
"size": 28
},
"extra": {
"name": "imageBox",
"img": {
"name": "img",
"imgUrl": "https://example.com/product.png"
}
}
}

示例 - 带按钮的块布局:

{
"name": "section",
"text": {
"name": "text",
"content": "待审批事项\n共 3 条待处理",
"type": 0,
"color": "#222222",
"size": 28
},
"extra": {
"name": "button",
"type": 0,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "立即处理"
},
"actionType": 0,
"link": {
"name": "link",
"url": "https://example.com/approve"
}
}
}
}

3. 操作组

{
"name": "actionGroup",
"actions": [
{
"name": "button",
"key": "btn1",
"type": 0,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "主要按钮"
},
"actionType": 0,
"link": {
"name": "link",
"url": "https://example.com"
}
}
}
]
}

属性说明:

  • actions: 操作列表,可包含按钮、日期选择器、下拉菜单等可交互组件

注意事项:

  • 操作组内的组件会按照添加顺序依次排列
  • 建议一个操作组内不要超过 5 个操作项
  • 多个按钮会自动换行显示

完整示例

示例 1:审批流程卡片

┌─────────────────────────────────┐
│ 请假审批申请 │
│ │
│ 申请人:张三 │
│ 请假类型:年假 │
│ 请假时间:2024-01-15 至 2024-01-17 │
│ 请假事由:家庭事务 │
│ │
│ [同意] [拒绝] │
└─────────────────────────────────┘

JSON 代码:

{
"ver": 1,
"content": {
"blocks": [
{
"name": "section",
"text": {
"name": "text",
"content": "请假审批申请",
"type": 0,
"color": "#222222",
"size": 32
}
},
{
"name": "divider"
},
{
"name": "section",
"text": {
"name": "text",
"content": "申请人:张三\n请假类型:年假\n请假时间:2024-01-15 至 2024-01-17\n请假事由:家庭事务",
"type": 0,
"color": "#666666",
"size": 28
}
},
{
"name": "actionGroup",
"actions": [
{
"name": "button",
"key": "approve",
"width": -1,
"type": 0,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "同意"
},
"actionType": 1,
"value": "approve"
}
},
{
"name": "button",
"key": "reject",
"width": -1,
"type": 1,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "拒绝"
},
"actionType": 1,
"value": "reject"
}
}
]
}
]
}
}

示例 2:数据报表卡片

┌─────────────────────────────────┐
│ 销售数据日报 │
│ 2024 年 1 月 15 日 │
│ │
│ ┌───────┐ ┌───────┐ │
│ │ ¥12.5W│ │ 156 │ │
│ │销售额 │ │ 订单数│ │
│ └───────┘ └───────┘ │
│ │
│ [查看详情] [导出数据] │
└─────────────────────────────────┘

JSON 代码:

{
"ver": 1,
"content": {
"blocks": [
{
"name": "section",
"text": {
"name": "text",
"content": "销售数据日报",
"type": 0,
"color": "#222222",
"size": 32
}
},
{
"name": "divider"
},
{
"name": "section",
"text": {
"name": "text",
"content": "2024 年 1 月 15 日",
"type": 0,
"color": "#666666",
"size": 28
}
},
{
"name": "section",
"text": {
"name": "text",
"content": "```\n┌───────┐ ┌───────┐\n│ ¥12.5W│ │ 156 │\n│销售额 │ │ 订单数│\n└───────┘ └───────┘\n```",
"type": 0,
"color": "#666666",
"size": 28
}
},
{
"name": "actionGroup",
"actions": [
{
"name": "button",
"key": "view_detail",
"width": -1,
"type": 0,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "查看详情"
},
"actionType": 0,
"link": {
"name": "link",
"url": "https://example.com/detail"
}
}
},
{
"name": "button",
"key": "export_data",
"width": -1,
"type": 1,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "导出数据"
},
"actionType": 1,
"value": "export_data"
}
}
]
}
]
}
}

示例 3:会议邀请卡片

┌─────────────────────────────────┐
│ 产品评审会议邀请 │
│ │
│ 会议时间:[选择日期] ▼ │
│ 参会人员: │
│ ○ 张三 ○ 李四 ○ 王五 │
│ │
│ 备注: │
│ [______________________] │
│ │
│ [确认参加] [拒绝] │
└─────────────────────────────────┘

JSON 代码:

{
"ver": 1,
"content": {
"blocks": [
{
"name": "section",
"text": {
"name": "text",
"content": "产品评审会议邀请",
"type": 0,
"color": "#222222",
"size": 32
}
},
{
"name": "divider"
},
{
"name": "section",
"text": {
"name": "text",
"content": "会议时间:",
"type": 0,
"color": "#666666",
"size": 28
},
"extra": {
"name": "datetimePicker",
"key": "meeting_time",
"defaultValue": 0
}
},
{
"name": "section",
"text": {
"name": "text",
"content": "```\n○ 张三\n○ 李四\n○ 王五\n```",
"type": 0,
"color": "#666666",
"size": 28
}
},
{
"name": "section",
"text": {
"name": "text",
"content": "备注:",
"type": 0,
"color": "#666666",
"size": 28
},
"extra": {
"name": "textInput",
"key": "notes",
"line": 1,
"defaultValue": ""
}
},
{
"name": "actionGroup",
"actions": [
{
"name": "button",
"key": "confirm",
"width": -1,
"type": 0,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "确认参加"
},
"actionType": 1,
"value": "confirm"
}
},
{
"name": "button",
"key": "reject",
"width": -1,
"type": 1,
"action": {
"name": "action",
"text": {
"name": "text",
"content": "拒绝"
},
"actionType": 1,
"value": "reject"
}
}
]
}
]
}
}

开发最佳实践

1. 设计原则

  • 简洁明了:一个卡片只传达一个核心信息
  • 层次清晰:使用分割线和块布局区分不同内容区域
  • 操作明确:按钮文案要直白,避免歧义
  • 视觉友好:合理使用颜色和间距,不要过度装饰

2. 性能优化

  • 图片大小:建议图片控制在 100KB 以内
  • 组件数量:单个卡片建议不超过 10 个组件
  • 文本长度:大段文本建议分段显示,每段不超过 100 字

3. 兼容性考虑

  • 移动端优先:设计时优先考虑手机屏幕的显示效果
  • 文字适配:重要信息不要依赖特定字体大小
  • 颜色对比:确保文字和背景有足够的对比度

4. 常见错误

错误示例 1:组件直接在 blocks 下

{
"blocks": [
{
"name": "button", // 错误!按钮不能直接放在 blocks 下
"type": 0
}
]
}

正确做法:使用容器包装

{
"blocks": [
{
"name": "actionGroup",
"actions": [
{
"name": "button",
"type": 0
}
]
}
]
}

错误示例 2:属性直接放在 blocks 下

{
"blocks": [
{
"name": "text", // 错误!属性不能作为 block
"content": "文本"
}
]
}

正确做法:使用 textBox 组件

{
"blocks": [
{
"name": "textBox",
"text": {
"name": "text",
"content": "文本"
}
}
]
}

常见问题 FAQ

Q: 如何制作可交互的表单?
A: 使用 textInputdropdowndatetimePicker等输入组件,配合actionType: 12 的按钮提交数据。

Q: 按钮点击后能触发什么操作?
A: 支持三种操作:

  • actionType: 0 - 打开链接
  • actionType: 1 - 提交当前表单数据到服务器
  • actionType: 2 - 提交整个消息的所有表单项

Q: 如何实现多行文本显示?
A: 在 text 的 content 中使用 \n 换行符,或者设置maxLine 属性控制最大行数。

Q: 图片显示不出来怎么办?
A: 检查以下几点:

  1. 图片 URL 是否可访问
  2. 是否使用了 HTTPS 协议
  3. 图片尺寸是否过大(建议宽度≤600px)
  4. 是否正确使用了 imageBox 容器

Q: 最多可以添加多少个组件?
A: 理论上没有限制,但为了用户体验,建议单个卡片不超过 10 个组件。

Q: 如何调试卡片消息?
A: 可以使用以下方法:

  1. 使用 JSON 格式化工具检查语法
  2. 先发送简单版本,逐步添加组件
  3. 查看返回的错误码定位问题