Files
agent-desk-ng/docs/embedding.zh-CN.md

6.2 KiB
Raw Permalink Blame History

嵌入客服挂件

挂件会在任意网页上添加一个聊天入口:只需加载一个脚本,页面上显示悬浮按钮,点击后在 iframe 中打开客服聊天。访客可以同时保留多个请求并随时切换。

English | Русский

快速开始

将 https://support.example.com 替换为你的部署地址,将 YOUR_CHANNEL_ID 替换为控制台中的渠道 ID。

<script>
  window.AgentDeskConfig = {
    channelId: "YOUR_CHANNEL_ID",
  }
</script>
<script src="https://support.example.com/sdk/agent-desk-sdk.min.js" async></script>

两段标签放在 </body> 之前,且 AgentDeskConfig 必须在 SDK 脚本之前执行。挂件会自动挂载,按钮出现在页面角落。

标题、副标题、主题色、位置和宽度来自控制台的渠道设置,通常无需在代码中配置。

配置项

选项 类型 默认值 说明
channelId string – 必填,挂件所属渠道。
baseUrl string 脚本所在域名 Agent Desk 服务地址。
apiBaseUrl string baseUrl API 与页面不同源时使用。
widgetBaseUrl string 脚本所在域名 聊天页面的加载地址。
language "zh-CN" | "en-US" 服务端设置 界面语言,任何 en* 取值都会选择英文。
position "left" | "right" 渠道设置 按钮和窗口所在一侧。
themeColor string 渠道设置 主题色,例如 #0f6cbd。
width string 380px 桌面端窗口宽度。
title、subtitle string 渠道设置 窗口标题文字。
externalId string 浏览器本地 ID 你自己系统中的稳定用户 ID。
signExternalId (id) => {signature, issuedAt} – 证明 externalId 真实有效,见已识别访客。
externalName string – 显示名称,仅在首次创建访客时使用。
getUserToken () => string | Promise<string> – 返回由你的系统签发的 JWT。

屏幕宽度不超过 480px 时,聊天窗口会全屏打开。

JavaScript API

AgentDeskWidget.open()       // Promise<void>,打开聊天
AgentDeskWidget.close()      // 最小化聊天
AgentDeskWidget.mount(cfg)   // 使用显式配置挂载
AgentDeskWidget.destroy()    // 移除按钮和聊天窗口
AgentDeskWidget.getChatUrl() // Promise<string>,用于独立页面或新标签页

从自己的按钮打开聊天:

<button onclick="AgentDeskWidget.open()">联系客服</button>

React 与 Next.js

只在客户端加载一次脚本。Next.js(App Router)示例:

"use client"

import Script from "next/script"

export function SupportWidget({ channelId }: { channelId: string }) {
  return (
    <>
      <Script id="agent-desk-config" strategy="afterInteractive">
        {`window.AgentDeskConfig = ${JSON.stringify({ channelId })}`}
      </Script>
      <Script
        src="https://support.example.com/sdk/agent-desk-sdk.min.js"
        strategy="afterInteractive"
      />
    </>
  )
}

包含函数的配置(signExternalId、getUserToken)无法序列化,请手动挂载:

useEffect(() => {
  const script = document.createElement("script")
  script.src = "https://support.example.com/sdk/agent-desk-sdk.min.js"
  script.onload = () =>
    window.AgentDeskWidget?.mount({
      channelId,
      externalId: user.id,
      signExternalId: (id) => fetch(`/api/support/sign?id=${id}`).then((r) => r.json()),
    })
  document.body.appendChild(script)
  return () => window.AgentDeskWidget?.destroy()
}, [channelId, user.id])

已识别访客

默认情况下访客是由浏览器本地 ID 标识的匿名游客。若要把聊天关联到你自己的用户,需要同时传入 externalId 和签名,防止他人冒充:

signature = hex(HMAC-SHA256(identitySecret, channelId + "\n" + externalId + "\n" + issuedAt))

issuedAt 为当前 Unix 时间(秒)。服务端只接受未超过 identity.maxAgeMinutes 的签名,因此每次打开聊天时挂件都会重新获取签名。

Important

签名必须在你的服务端计算,身份密钥绝不能下发到浏览器。

Node.js 接口:

import crypto from "node:crypto"

app.get("/api/support/sign", requireLogin, (req, res) => {
  const issuedAt = Math.floor(Date.now() / 1000)
  const payload = `${CHANNEL_ID}\n${req.user.id}\n${issuedAt}`
  const signature = crypto
    .createHmac("sha256", process.env.AGENT_DESK_IDENTITY_SECRET)
    .update(payload)
    .digest("hex")
  res.json({ signature, issuedAt })
})

Go:

func sign(secret, channelID, externalID string) (string, int64) {
    issuedAt := time.Now().Unix()
    mac := hmac.New(sha256.New, []byte(secret))
    fmt.Fprintf(mac, "%s\n%s\n%d", channelID, externalID, issuedAt)
    return hex.EncodeToString(mac.Sum(nil)), issuedAt
}

注意:接口签名的 externalId 应取自已登录用户,而不是查询参数。签名缺失或无效不会导致挂件失效,访客只会保持游客身份。

内容安全策略(CSP)与跨域

如果站点设置了 CSP,需要放行 Agent Desk 的域名:

script-src https://support.example.com;
frame-src  https://support.example.com;
connect-src https://support.example.com;

挂件与 API 不同源时,请把你的站点加入部署的允许来源,否则配置请求会失败。

常见问题

现象 原因与解决
没有出现按钮 缺少 channelId,或 AgentDeskConfig 定义在 SDK 脚本之后。查看控制台是否有 channelId is required。
按钮出现但聊天空白 iframe 被浏览器拦截。检查 CSP 的 frame-src,以及代理是否为 Agent Desk 域名设置了 X-Frame-Options。
访客始终显示为游客 签名缺失、已过期,或使用了错误的密钥或拼接顺序。请对照 identity.maxAgeMinutes 检查服务器时钟偏差。
语言不对 显式设置 language,否则使用服务端默认语言。
单页应用中挂件重复 组件卸载时调用 AgentDeskWidget.destroy()。
聊天显示“出了点问题” 点击重新加载聊天。若仍然出现,请打开控制台并反馈 Support chat crashed 错误。