> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coze.cn/llms.txt
> Use this file to discover all available pages before exploring further.

本文介绍如何安装并使用扣子 Card SDK，开发者可以参考本文档自定义智能体对话消息中的卡片效果。
## 功能简介 {#b509274e}
为了助力开发者打造更具个性化与交互性的消息展示体验，扣子提供 Card SDK。借助该 SDK，开发者可以灵活地定义卡片的结构和内容，并将渲染后的卡片集成到你自行实现的聊天应用中。
卡片是一种特殊的消息展示方式，它以结构化的布局和丰富的视觉元素呈现信息。与传统的纯文本消息相比，卡片可以包含图片、按钮、列表等多种元素，使得信息更加直观、易读。这不仅显著提升了用户体验，还提高了信息传递效率，增强了交互性。
## 效果预览 {#5efadaff}
![Image=400x355](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/efe1e35df664419ab425cea205221abd~tplv-goo7wpa0wc-image.image)
## 功能简介 {#08bd7843}
<!-- @cols-width: 166,706 -->
| | | \
|**功能** |**说明** |
|---|---|
| | | \
|自定义内容格式 |Card SDK 提供了强大的自定义功能，允许开发者根据具体需求定义卡片的布局和内容。开发者可以通过配置卡片的 DSL（Domain Specific Language，领域特定语言）数据，指定卡片中各个元素的类型、样式和交互逻辑。无论是简单的文本卡片，还是包含图片、按钮和列表的复杂卡片，都可以通过Card SDK 轻松实现。 |
| | | \
|自定义卡片样式 |Card SDK 还允许开发者自定义卡片中按钮、图片等组件的渲染逻辑，实现个性化的样式和交互效果。通过 `customComponentMap` 接口，开发者可以为每个组件定义独特的渲染函数，从而实现更加丰富的视觉效果和交互行为。开发者可以根据具体的业务需求，设计出个性化的卡片样式。 |

## 浏览器兼容性 {#783bdba5}
Card  SDK 的运行环境要求如下表所示。
<!-- @cols-width: 100,139,251 -->
| | | | \
|**操作系统** |**浏览器** |**版本限制** |
|---|---|---|
| | | | \
|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 及以上  |

## 配置流程 {#9e92dbac}
### 步骤一：发布智能体 {#2c5fef61}
在智能体的发布页面，选择 Chat SDK，并单击**发布**。发布的详细流程可参考[发布到 Chat SDK](/guides/publish_agent_to_chat_sdk)。
### 步骤二：安装 Card SDK {#37fdb1a4}
创建一个新的 HTML 页面，并将以下代码添加到 <body> 的 <script> 标签中，保存并运行后会自动加载 Card SDK 的 JavaScript 代码。
:::tip 说明
示例代码中的版本号（1.2.0-beta.8）需要替换为 Chat SDK 最新的版本号，版本信息请参见[Chat SDK 发布历史](/developer_guides/web_sdk_changelog)。
:::
```JavaScript
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);
```

### 步骤三：配置卡片属性 {#e8eff981}
#### 示例代码 {#48407e15}
以下是基于 React 框架编写的示例代码，用于渲染卡片布局。
```JavaScript
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;
```

#### **卡片参数** {#a014bab9}
卡片初始化和个性化配置的关键参数说明如下表所示。
<!-- @cols-width: 252,100,100,480 -->
| | | | | \
|**参数** |**类型** |**是否必选** |**说明** |
|---|---|---|---|
| | | | | \
|el  |HTML Element |必选 | 卡片需要渲染的 HTML 元素。  |
| | | | | \
|customComponentMap |Object |必选 |自定义组件样式，其中 key 为组件的名称。你可以从 data 字段的卡片配置中获取组件名称。例如：@flowpd/cici-components/NewImage。 |
| | | | | \
|runtimeOptions |Object |可选 |运行时的选项。 |
| | | | | \
|runtimeOptions.dsl |Object |可选 | DSL 数据，定义卡片的结构和内容。  |
| | | | | \
|runtimeOptions.remoteForce |Boolean  |可选 |是否每次都从远程拉取组件。 |\
| | | | |\
| | | |* **true**：每次自动拉取。 |\
| | | |* **false**：不自动拉取。 |
| | | | | \
|runtimeOptions.debug |Boolean  |可选 |是否打印报错信息。 |\
| | | | |\
| | | |* **true**：打印。 |\
| | | |* **false**：不打印。 |
| | | | | \
|runtimeOptions.readonly |Boolean  |可选 |是否开启只读模式，只读模式下卡片的交互效果不生效，例如无法跳转到其他网页等。 |
| | | | | \
|runtimeOptions.lang |String |可选 |设置卡片内容的语言。例如 zh。 |
| | | | | \
|runtimeOptions.theme |String |可选 |卡片的深浅模式。 |\
| | | | |\
| | | |* **light**：浅色模式。 |\
| | | |* **dark**：深色模式。 |
| | | | | \
|runtimeOptions.afterRender |Function  |可选 |回调函数，在组件加载成功时自动触发。 |
| | | | | \
|runtimeOptions.onError |Function  |可选 |回调函数，在渲染卡片失败时自动触发。 |
| | | | | \
|runtimeOptions.eventCallbacks |Object |可选 |交互事件的回调函数，包括： |\
| | | | |\
| | | |* **onElementOut（targetElemtent, linkurl）=> void**：当鼠标从卡片链接上移出的时候，触发此函数。 |\
| | | |* **onElementOver（targetElemtent, linkurl）=> void**：当鼠标在卡片链接上移动的时候，触发此函数。 |\
| | | |* **sendMsg**：当点击发送消息按钮时触发此函数。 |\
| | | |* **previewImage（{url}）=> void**：当点击图片的时候触发此函数。 |

#### 自定义卡片组件的样式 {#87fc5c91}
卡片中的组件使用默认的展示效果，你也可以通过 Card SDK 的 `customComponentMap` 接口自定义组件的渲染逻辑，实现个性化的样式和交互效果。
`customComponentMap` 接口的结构如下：
```TypeScript
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 支持的属性设置如下：
   <!-- @cols-width: 100,100,100,273,422 -->
   | | | | | | \
   |**参数** |**类型** |**是否必选** |**示例** |**说明** |
   |---|---|---|---|---|
   | | | | | | \
   |name |String  |必选 |@flowpd/cici-components/NewImage |元素的名称，通常用于标识和描述元素。 |
   | | | | | | \
   |element |Object |必选 |{ style: { width: '100px', height: '100px' } } |指定挂载自定义组件的 HTML 元素，默认为一个空的 <div> 容器。 |
   | | | | | | \
   |props |Object |必选 |props:  |\
   | | | |{ |\
   | | | |  blockId:{element.id} |\
   | | | |  children: {子节点} |\
   | | | |  lang: zh |\
   | | | |  cardState： （dsl.status || {}) |\
   | | | |  ...elementProps (处理后的） |\
   | | | |} |渲染组件时使用的属性，你可以从浏览器的开发者工具中查看 props 属性的值。 |
   | | | | | | \
   |renderChildren |Function |必选 |\- |渲染子组件的方法。如果组件中有子组件，可以使用 renderChildren 函数将子组件渲染到指定的 HTML 元素中。 |

* `RenderCustomComponentFunc` 的返回值是一个销毁函数，不需要某个组件时可以调用此函数销毁组件。你可以根据业务场景按需选择是否销毁组件。

#### **设置样式的示例代码** {#eaf9d12b}
以下是通过`customComponentMap`接口为组件 @flowpd/cici-components/NewImage 定制样式的示例代码。
```JavaScript
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(); //销毁函数
      }
    }
}
```

##  常见问题  {#36eed146}
### Card SDK 可以在 Chat SDK 中使用吗？ {#51823aba}
集成 Chat SDK 后无需再集成 Card SDK。Chat SDK 本身已内置对卡片功能的支持，无需额外集成 Card SDK。如果你的聊天应用中未使用 Chat SDK，但需要实现卡片效果，可以直接使用 Card SDK 将渲染后的卡片集成到你自行实现的聊天应用中。


