自定义卡片消息结构设计
快速开始
什么是卡片消息?
卡片消息是一种富文本消息格式,支持文本、图片、按钮、表单等多种组件的组合,可以创建交互性强的消息卡片。
适用场景:
- ✅ 审批流程(通过/拒绝按钮 + 审批信息)
- ✅ 数据报表(图表 + 关键指标 + 筛选器)
- ✅ 通知提醒(重要信息高亮 + 操作按钮)
- ✅ 调研问卷(问题 + 选项 + 提交按钮)
- ✅ 会议邀请(时间选择器 + 参会人员 + 确认按钮)
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
}
}
属性说明:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| content | string | - | 文本内容 |
| type | number | 0 | 0-普通文本,1-markdown |
| color | string | #222222 | 颜色值,hex 格式 |
| size | number | 28 | 字体大小,单位 px |
| maxLine | number | -1 | 最大行数,-1 表示不限制 |
使用技巧:
- 使用
\n实现换行 maxLine超出后会显示省略号- markdown 模式下支持基础语法
2. 图片
用于展示图片内容。
{
"name": "imageBox",
"img": {
"name": "img",
"imgUrl": "https://example.com/image.png"
}
}
属性说明:
| 属性 | 类型 | 说明 |
|---|---|---|
| imgUrl | string | 图片的 URL 地址 |
注意事项:
- 图片会自动适应容器宽度
- 建议使用 HTTPS 协议的图片链接
- 推荐图片尺寸:宽度不超过 600px
3. 日期选择器
允许用户选择日期和时间。
{
"name": "datetimePicker",
"key": "meeting_time",
"defaultValue": 15333333333
}
属性说明:
| 属性 | 类型 | 说明 |
|---|---|---|
| key | string | 字段标识,用于提交时识别 |
| defaultValue | number | 默认时间戳(毫秒) |
使用场景:
- 会议时间选择
- 预约时间设置
- 截止日期选择
示例 - 会议预约:
{
"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": "请输入您的反馈"
}
}
属性说明:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| key | string | - | 字段标识 |
| line | number | 1 | 显示行数 |
| defaultValue | text 对象 | - | 默认文本 |
使用场景:
- 意见反馈
- 备注说明
- 自定义输入
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"
}
]
}
属性说明:
| 属性 | 类型 | 说明 |
|---|---|---|
| key | string | 字段标识 |
| defaultValue | string | 默认选中的 value |
| options | array | option 对象列表 |
注意事项:
- 每个 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"
}
}
属性说明:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| key | string | - | 按钮标识 |
| type | number | 0 | 0-主要按钮,1-次要按钮,2-常用按钮 |
| width | number | 0 | 0-铺满整行,-1-自适应 |
| action | action 对象 | - | 点击后的操作 |
按钮类型效果对比:
- 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"
}
}
属性说明:
| 属性 | 类型 | 说明 |
|---|---|---|
| key | string | 链接标识 |
| action | action 对象 | 点击后的操作 |
| icon | img 对象 | 显示的图标 |
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: 使用 textInput、dropdown、datetimePicker等输入组件,配合actionType: 1或2 的按钮提交数据。
Q: 按钮点击后能触发什么操作?
A: 支持三种操作:
actionType: 0- 打开链接actionType: 1- 提交当前表单数据到服务器actionType: 2- 提交整个消息的所有表单项
Q: 如何实现多行文本显示?
A: 在 text 的 content 中使用 \n 换行符,或者设置maxLine 属性控制最大行数。
Q: 图片显示不出来怎么办?
A: 检查以下几点:
- 图片 URL 是否可访问
- 是否使用了 HTTPS 协议
- 图片尺寸是否过大(建议宽度≤600px)
- 是否正确使用了 imageBox 容器
Q: 最多可以添加多少个组件?
A: 理论上没有限制,但为了用户体验,建议单个卡片不超过 10 个组件。
Q: 如何调试卡片消息?
A: 可以使用以下方法:
- 使用 JSON 格式化工具检查语法
- 先发送简单版本,逐步添加组件
- 查看返回的错误码定位问题