本文介绍如何安装并使用扣子 Card SDK,开发者可以参考本文档自定义智能体对话消息中的卡片效果。
为了助力开发者打造更具个性化与交互性的消息展示体验,扣子提供 Card SDK。借助该 SDK,开发者可以灵活地定义卡片的结构和内容,并将渲染后的卡片集成到你自行实现的聊天应用中。
卡片是一种特殊的消息展示方式,它以结构化的布局和丰富的视觉元素呈现信息。与传统的纯文本消息相比,卡片可以包含图片、按钮、列表等多种元素,使得信息更加直观、易读。这不仅显著提升了用户体验,还提高了信息传递效率,增强了交互性。

| 功能 | 说明 |
|---|---|
| 自定义内容格式 | Card SDK 提供了强大的自定义功能,允许开发者根据具体需求定义卡片的布局和内容。开发者可以通过配置卡片的 DSL(Domain Specific Language,领域特定语言)数据,指定卡片中各个元素的类型、样式和交互逻辑。无论是简单的文本卡片,还是包含图片、按钮和列表的复杂卡片,都可以通过Card SDK 轻松实现。 |
| 自定义卡片样式 | Card SDK 还允许开发者自定义卡片中按钮、图片等组件的渲染逻辑,实现个性化的样式和交互效果。通过 customComponentMap 接口,开发者可以为每个组件定义独特的渲染函数,从而实现更加丰富的视觉效果和交互行为。开发者可以根据具体的业务需求,设计出个性化的卡片样式。 |
Card SDK 的运行环境要求如下表所示。
| 操作系统 | 浏览器 | 版本限制 |
|---|---|---|
| PC | Chrome | 87.0 及以上 |
| Edge | 100.0 及以上 | |
| Safari | 14.0 及以上 | |
| Firefox | 79.0 及以上 | |
| Android | Chrome | 100.0 及以上 |
| Edge | 100.0 及以上 | |
| iOS | Chrome | 87.0 及以上 |
| Safari | 14.0 及以上 |
在智能体的发布页面,选择 Chat SDK,并单击发布。发布的详细流程可参考发布到 Chat SDK。
创建一个新的 HTML 页面,并将以下代码添加到 <body> 的 <script> 标签中,保存并运行后会自动加载 Card SDK 的 JavaScript 代码。
说明
示例代码中的版本号(1.2.0-beta.8)需要替换为 Chat SDK 最新的版本号,版本信息请参见Chat SDK 发布历史。
let c = document.createElement('script');
c.src = 'https://lf-cdn.coze.cn/obj/unpkg/flow-platform/chat-app-sdk/1.2.0-beta.13/libs/cn/ui.js';
document.body.appendChild(c);
以下是基于 React 框架编写的示例代码,用于渲染卡片布局。
import React, { useCallback, useEffect, useRef } from 'react';
// 定义 CozeCard 组件,用于创建和管理 CozeWebSDK.WebCardRuntime 实例
const CozeCard = () => {
// 使用 useRef 创建一个可变的引用对象,用于保存 CozeWebSDK.WebCardRuntime 实例
const clientRef = useRef();
// 使用 useCallback 缓存函数,避免组件重新渲染时函数重新创建
const genCard = useCallback((elem) => {
// 如果传入的元素为空,直接返回
if (!elem) {
return;data
}
// 如果之前已经存在 CozeWebSDK.WebCardRuntime 实例,先销毁它
if (clientRef.current) {
clientRef.current.destroy();
}
// 创建新的 CozeWebSDK.WebCardRuntime 实例并保存到 clientRef 中
clientRef.current = new CozeWebSDK.WebCardRuntime({
// 指定要渲染的 DOM 元素
el: elem,
// 自定义组件映射,定义特定组件的渲染逻辑
customComponentMap: {
// 自定义 NewImage 组件的渲染逻辑
'@flowpd/cici-components/NewImage': ({ name, element, props }) => {
// 打印开发调试信息
console.log("[dev]", { name, element, props });
// 设置元素的 HTML 内容
element.innerHTML = `Test`;
// 设置元素的宽度
element.style.width = '100px';
// 设置元素的高度
element.style.height = '100px';
// 设置元素的背景颜色
element.style.background = 'red';
// 返回一个销毁函数,用于处理组件卸载时的清理工作
return () => {
// destroy, 例如react 等问题
};
},
// 自定义 ColumnLayout1_2 组件的渲染逻辑
'@flowpd/cici-components/ColumnLayout1_2': ({ name, element, props, renderChildren }) => {
// 从 props 中解构出 Columns 属性
const { Columns } = props;
// 遍历 Columns 数组,渲染每个子元素
Columns.map((column) => {
const { content } = column;
const elem = renderChildren(content());
element.appendChild(elem);
});
// 打印开发调试信息
console.log("[dev]", { name, element, props });
// 设置元素的背景颜色
element.style.background = 'yellow';
// 设置元素的显示方式为弹性布局
element.style.display = 'flex';
},
},
// 运行时配置选项
runtimeOptions: {
// 解析后的 JSON 数据,定义组件的结构和属性
dsl: JSON.parse(
'{"elements":{"7c1EN9VHwU":{"id":"7c1EN9VHwU","name":"FlowpdCiciComponentsColumnLayout1_2","type":"@flowpd/cici-components/ColumnLayout1_2","props":{"Columns":[{"children":["DQKOI54sJV"],"config":{"vertical":"top","width":"auto"},"type":"slot"},{"children":["FE8Y2GcSwb","L3gNU6QxiI"],"config":{"vertical":"top","weight":2,"width":"weighted"},"type":"slot"}],"action":"enableUrl","backgroundColor":"transparent","enableClickEvent":true,"url":{"type":"expression","value":"{{url}}"}},"directives":{}},"DQKOI54sJV":{"id":"DQKOI54sJV","name":"FlowpdCiciComponentsNewImage","type":"@flowpd/cici-components/NewImage","props":{"action":"enableUrl","crop":"aspectFill","enableClickEvent":false,"enableCrop":false,"fixedSize":"medium","mode":"fixed","ratio":"1:1","src":{"type":"expression","value":"{{image}}"}},"directives":{}},"FE8Y2GcSwb":{"id":"FE8Y2GcSwb","name":"FlowpdCiciComponentsText","type":"@flowpd/cici-components/Text","props":{"action":"enableUrl","color":"neutral-100","content":{"type":"expression","value":"{{title}}"},"enableClickEvent":false,"enableLines":true,"fontSize":"large","fontWeight":"bold","lines":1,"textAlign":"left"},"directives":{}},"L3gNU6QxiI":{"id":"L3gNU6QxiI","name":"FlowpdCiciComponentsText","type":"@flowpd/cici-components/Text","props":{"action":"enableUrl","color":"neutral-70","content":{"type":"expression","value":"{{content}}"},"enableClickEvent":false,"enableLines":true,"fontSize":"small","fontWeight":"normal","lines":2,"textAlign":"left"},"directives":{}},"root":{"id":"root","name":"Root","type":"@flowpd/cici-components/PageContainer","children":["7c1EN9VHwU"],"directives":{}}},"rootID":"root","variables":{"content":{"ID":"content","name":"content","defaultValue":"26/22℃ 无持续风向3-4级 28/24℃ 无持续风向<3级 30/25℃ 无持续风向<3级 26/22℃ 无持续风向3-4级 28/24℃ 无持续风向<3级 30/25℃ 无持续风向<3级"},"image":{"ID":"image","name":"image","defaultValue":""},"title":{"ID":"title","name":"title","defaultValue":"【深圳天气预报15天_深圳天气预报15天查询】-中国天气网"},"url":{"ID":"url","name":"url"}}}'
),
// 是否只读
readonly: false,
// 是否强制远程加载
remoteForce: false,
// 主题样式
theme: 'dark',
// 语言设置
lang: 'en',
// 渲染完成后的回调函数
afterRender: () => {
console.log('[dev] afterRender');
},
},
});
}, []);
// 使用 useEffect 处理组件卸载时的清理工作
useEffect(() => {
// 返回一个清理函数,在组件卸载时销毁 CozeWebSDK.WebCardRuntime 实例
return () => {
clientRef.current?.destroy();
};
}, []);
// 返回组件的 JSX 结构
return (
<>
{/* 定义一个按钮,点击时更新运行时的主题为 light */}
<button
onClick={() => {
clientRef.current?.updateRuntimeOptions({
theme: 'light',
});
}}
>
ChangeTheme
</button>
{/* 定义一个 div 元素,使用 ref 绑定 genCard 函数,用于初始化 CozeWebSDK.WebCardRuntime 实例 */}
<div ref={elem => genCard(elem)} style={{
margin: '10px',
width: '500px'
}} />
</>
);
};
// 定义 TestCardDemo 组件,用于渲染两个 CozeCard 组件
const TestCardDemo: React.FC = () => {
return (
<div>
<CozeCard />
<CozeCard />
</div>
);
};
// 导出 TestCardDemo 组件,供其他模块使用
export default TestCardDemo;
卡片初始化和个性化配置的关键参数说明如下表所示。
| 参数 | 类型 | 是否必选 | 说明 |
|---|---|---|---|
| el | HTML Element | 必选 | 卡片需要渲染的 HTML 元素。 |
| customComponentMap | Object | 必选 | 自定义组件样式,其中 key 为组件的名称。你可以从 data 字段的卡片配置中获取组件名称。例如:@flowpd/cici-components/NewImage。 |
| runtimeOptions | Object | 可选 | 运行时的选项。 |
| runtimeOptions.dsl | Object | 可选 | DSL 数据,定义卡片的结构和内容。 |
|
runtimeOptions.remoteForce |
Boolean |
可选 |
是否每次都从远程拉取组件。
|
|
runtimeOptions.debug |
Boolean |
可选 |
是否打印报错信息。
|
| runtimeOptions.readonly | Boolean | 可选 | 是否开启只读模式,只读模式下卡片的交互效果不生效,例如无法跳转到其他网页等。 |
| runtimeOptions.lang | String | 可选 | 设置卡片内容的语言。例如 zh。 |
|
runtimeOptions.theme |
String |
可选 |
卡片的深浅模式。
|
| runtimeOptions.afterRender | Function | 可选 | 回调函数,在组件加载成功时自动触发。 |
| runtimeOptions.onError | Function | 可选 | 回调函数,在渲染卡片失败时自动触发。 |
|
runtimeOptions.eventCallbacks |
Object |
可选 |
交互事件的回调函数,包括:
|
卡片中的组件使用默认的展示效果,你也可以通过 Card SDK 的 customComponentMap 接口自定义组件的渲染逻辑,实现个性化的样式和交互效果。
customComponentMap 接口的结构如下:
type DestoryFunc = () => void;
type RenderCustomComponentFunc = (params: {
name: string,
element: HTMLDivElement,
props: Record<string, any>,
renderChildren: (node: React.ReactNode) => HTMLDivElement}
) => DestoryFunc | void;
interface {
customComponentMap?: Record<string, RenderCustomComponentFunc>;
}
说明:
RenderCustomComponentFunc 是用于渲染卡片组件的渲染函数。你需要自行实现该函数,函数会接收到卡片组件传递的参数,并返回一个销毁函数。RenderCustomComponentFunc 支持的属性设置如下:
| 参数 | 类型 | 是否必选 | 示例 | 说明 |
|---|---|---|---|---|
| name | String | 必选 | @flowpd/cici-components/NewImage | 元素的名称,通常用于标识和描述元素。 |
| element | Object | 必选 | { style: { width: '100px', height: '100px' } } | 指定挂载自定义组件的 HTML 元素,默认为一个空的 容器。 |
|
props |
Object |
必选 |
props: { blockId:{element.id} children: {子节点} lang: zh cardState: (dsl.status ...elementProps (处理后的) } |
渲染组件时使用的属性,你可以从浏览器的开发者工具中查看 props 属性的值。 |
| renderChildren | Function | 必选 | - | 渲染子组件的方法。如果组件中有子组件,可以使用 renderChildren 函数将子组件渲染到指定的 HTML 元素中。 |
RenderCustomComponentFunc 的返回值是一个销毁函数,不需要某个组件时可以调用此函数销毁组件。你可以根据业务场景按需选择是否销毁组件。以下是通过customComponentMap接口为组件 @flowpd/cici-components/NewImage 定制样式的示例代码。
customComponentMap: {
'@flowpd/cici-components/NewImage': ({ name, element, props }) => {
element.style.width = '100px';
element.style.height = '100px';
const renderRoot = createRoot(element); //使用react渲染组件
renderRoot.render(<div style={{
width: '100%',
height: '100%',
background: 'red',
}}>
Image Test
</div>);
return () => {
renderRoot.unmount(); //销毁函数
}
}
}
集成 Chat SDK 后无需再集成 Card SDK。Chat SDK 本身已内置对卡片功能的支持,无需额外集成 Card SDK。如果你的聊天应用中未使用 Chat SDK,但需要实现卡片效果,可以直接使用 Card SDK 将渲染后的卡片集成到你自行实现的聊天应用中。