跳至主要内容

JavaScript API

简介

... Widget JavaScript API 用于让宿主页面控制访客端小组件,例如打开/收起窗口、监听初始化和未读数变化、切换语言或主题、业务用户登录等。

安装 SDK

安装代码

<!-- Start of ... code -->
<script>
window.TWTChatWidget = window.TWTChatWidget || {
_q: [],
on: function () {
this._q.push(["on", Array.prototype.slice.call(arguments)]);
},
};

window.__twt = {
appid: "以项目APPID为准",
};

(function (n, t) {
var e = {
init: function () {
var n = t.createElement("script");
n.async = !0;
n.type = "text/javascript";
n.src = ".../install/core.js?version=v1.2";
t.head.appendChild(n);
},
};
e.init();
})(window, document);
</script>
<!-- End of ... code -->

配置选项

所有配置都写入 window.__twt,并在 core.js 引导阶段被读取。

必填配置

字段类型说明
appidstring应用唯一标识。生产接入统一使用小写 appid

可选配置

字段类型默认值说明
asyncInitbooleanfalse是否跳过自动 init()
languagestring浏览器语言兜底初始语言,支持值见语言配置
theme'light' | 'dark' | 'system'light初始主题
launcher'default' | 'custom'defaultlauncher 渲染模式
zIndexnumber由 host-sdk 默认样式决定widget 容器层级
sbsstring业务用户标识
sbs_mmstring业务用户签名
ranstrstring签名随机串
namestring访客名称
nicknamestring访客昵称
emailstring访客邮箱
phonestring访客手机号
refererstring当前页面 URL来源 URL
source_titlestring当前页面标题来源标题

示例:

window.__twt = {
appid: "YOUR_APPID",
language: "zh-cn",
theme: "dark",
launcher: "custom",
zIndex: 9999,
};

语言配置

window.__twt.language、和 window.TWTChatWidget.setLanguage(locale) 使用同一套 runtime locale。接入方推荐直接传下表中的标准值;大小写和常见地区变体会在 SDK / runtime 内归一化。

标准值语言
enEnglish
zh-cn简体中文
zh-tw繁體中文
ja日本語
ko한국어
deDeutsch
frFrançais
ptPortuguês
ruРусский

未显式配置语言时,SDK 会按 navigator.languages / navigator.language 选择第一个可支持语言;没有匹配项时回退到 en。不支持的显式值不会作为 runtime locale 保留,接入方应避免传入上表以外的语言值。

初始化时机

默认情况下,core.js 加载后会自动 init() 并创建 iframe。

如果需要延迟创建 widget iframe,可以设置 asyncInit: true,再在合适时机手动初始化:

<script>
window.TWTChatWidget = window.TWTChatWidget || {
_q: [],
on: function () {
this._q.push(["on", Array.prototype.slice.call(arguments)]);
},
};

window.__twt = {
appid: "YOUR_APPID",
asyncInit: true,
};

window.TWTChatWidget.on("initialized", function () {
console.log("Widget initialized");
});

(function (d) {
var sdk = d.createElement("script");
sdk.async = true;
sdk.src = ".../install/core.js?version=v1.2";
sdk.onload = function () {
document
.querySelector("#contact-us")
.addEventListener("click", function () {
window.TWTChatWidget.init();
});
};
d.head.appendChild(sdk);
})(document);
</script>

init() 是幂等的,重复调用不会重复创建实例。使用 asyncInit 时,必须等 core.js 加载完成后再调用 init()get() 是同步读取,必须在 init() 已完成后使用,否则会抛出错误。

入口约定

window.TWTChatWidget

window.TWTChatWidget 是统一的对外 API 入口。宿主页面调用方法、注册事件、读取状态时,都应该使用它:

window.TWTChatWidget.on("initialized", function () {
// SDK 小组件加载完,自动打开小组件
window.TWTChatWidget.call("maximize");
});

核心函数

函数说明初始化前是否可用
on(event, handler)注册事件监听最小安装 stub 支持排队
once(event, handler)注册一次性事件监听取决于安装 stub 是否提供
off(event, handler?)移除事件监听取决于安装 stub 是否提供
call(method, payload?)调用 Widget 方法建议 initialized 后调用
get(getter)同步读取状态只能初始化后调用
init()手动初始化core.js 加载后可用
login(...)业务用户登录便捷方法初始化后调用
setLanguage(locale)call('set_language', locale) 便捷方法初始化后调用
scriptTagVersion()返回当前 SDK 脚本版本core.js 加载后可用

scriptTagVersion() 返回的值与 get('sdk_info').scriptTagVersion 等价,都是安装片段的 snippet 版本号;区别仅在于 scriptTagVersion()core.js 加载后即可调用,而 get('sdk_info') 必须等 init() 完成。

Methods

maximize

展开聊天面板。

window.TWTChatWidget.call("maximize");

minimize

收起聊天面板,仅保留内置 launcher。

window.TWTChatWidget.call("minimize");

hide

隐藏整个 widget。iframe 仍保持挂载。

window.TWTChatWidget.call("hide");

show

从隐藏态恢复到最小化态。

window.TWTChatWidget.call("show");

toggle

在展开态和最小化态之间切换。

window.TWTChatWidget.call("toggle");

destroy

销毁 host 侧 iframe、监听器和营销卡片 surface。

window.TWTChatWidget.call("destroy");

destroy 是 host-side API,不要求 runtime 先确认销毁。

login

用新的业务用户身份重载 iframe。

推荐使用便捷方法:

window.TWTChatWidget.login(
"session_token_123", // sbs,业务用户标识
"signature_abc", // sbs_mm,签名
"random_xyz", // ranstr,签名随机串
"张三", // name,可选
"小张", // nickname,可选
"zhangsan@example.com", // email,可选
"13800138000", // phone,可选
);

也可以直接调用 method:

window.TWTChatWidget.call("login", {
sbs: "session_token_123",
sbs_mm: "signature_abc",
ranstr: "random_xyz",
name: "张三",
nickname: "小张",
email: "zhangsan@example.com",
phone: "13800138000",
});

sbssbs_mmranstr 是必填签名字段。缺失时,runtime 会拒绝本次登录命令。

set_language

切换 runtime 语言。

window.TWTChatWidget.call("set_language", "en");

便捷写法:

window.TWTChatWidget.setLanguage("en");

支持的语言值与初始化配置一致,见语言配置set_language 会先把输入归一化成 runtime locale,再同步 launcher 和 panel;如果 runtime 当前会话拒绝语言切换,panel 文案不会更新。切换到不支持的语言时不会新增语言包,宿主页应只传支持列表中的值。

set_theme

切换主题。

window.TWTChatWidget.call("set_theme", "dark");

可选值:

说明
light浅色主题
dark深色主题
system跟随系统

Getters

get() 是同步方法,不会排队等待 runtime。请在 initialized 之后调用。

state

读取当前可见性。

const state = window.TWTChatWidget.get("state");
console.log(state.visibility);

返回:

字段类型说明
visibility'maximized' | 'minimized' | 'hidden'当前可见性

sdk_info

读取 SDK 版本和运行时信息。

const sdkInfo = window.TWTChatWidget.get("sdk_info");

返回:

字段类型说明
versionstringhost-sdk 运行时版本
scriptTagVersionstring脚本版本
compatibilityMode'off' | 'livechat'兼容模式,当前返回 off
transport'ws' | 'http-polling' | 'mixed'当前返回 mixed,表示 host bridge + runtime WS 的组合形态

unread_count

读取当前缓存的未读数。

const count = window.TWTChatWidget.get("unread_count");

未收到 runtime 的 unread_count_changed 前,默认返回 0

theme

读取当前 SDK 侧主题。

const theme = window.TWTChatWidget.get("theme");

返回 lightdarksystem

Events

事件通过 TWTChatWidget.on(event, handler) 监听。

window.TWTChatWidget.on("visibility_changed", function (payload) {
console.log(payload.visibility);
});

需要解绑时保留同一个 handler 引用:

function handleVisibilityChanged(payload) {
console.log(payload.visibility);
}

window.TWTChatWidget.on("visibility_changed", handleVisibilityChanged);
window.TWTChatWidget.off("visibility_changed", handleVisibilityChanged);

生命周期事件

事件Payload说明
initializedhost-sdk init() 完成
iframe_loaded{ surface: string }iframe DOM load 完成,仅用于诊断
bridge_readyruntime bridge handler 已注册,可以接受命令
runtime_mountedVue runtime 已挂载
visitor_auth_ready见下方访客 token 已解析或恢复
runtime_activated首次激活完成,初始会话列表已解析
destroyedwidget 已销毁

initializedbridge_ready 不等于 WebSocket 已连接,也不等于认证已完成。需要关注连接状态时,请监听 connection_state_changedsession_connected

visitor_auth_ready

访客身份已准备好,payload 可能包含可回写的身份线索。

字段类型说明
bsstring访客身份线索
bs_mmstring访客身份签名线索
fk_idstring旧客户页兼容字段
fkidstringruntime / 后端记录字段,语义等同于 fk_id
hasTokenboolean当前 runtime 是否已有可用访客 token
source'initial' | 'resolved'来源
visitorUidstringruntime 内部访客 uid,不等同于 fk_id

host-sdk 会基于该事件把 ITP 身份写回当前存储策略。接入方如需同步到自己的业务存储,也可以自行监听:

window.TWTChatWidget.on("visitor_auth_ready", function (payload) {
if (payload.bs && payload.bs_mm) {
localStorage.setItem(
"twt_visitor_identity",
JSON.stringify({
bs: payload.bs,
bs_mm: payload.bs_mm,
fk_id: payload.fk_id || payload.fkid,
}),
);
}
});

状态事件

事件Payload说明
visibility_changed{ visibility }可见性变化
connection_state_changed{ status }连接状态变化
session_connected{ status: 'connected' }WebSocket 已连接
session_disconnected{ status }WebSocket 断开或重连中
auth_closed见下方访客认证已被服务端关闭
bootstrap_failed见下方embedded 启动必需链路失败
unread_count_changed{ count: number }未读数变化

visibility 可选值:

说明
maximized面板展开
minimized仅显示 launcher
hiddenlauncher 和面板都隐藏

status 可选值:

说明
disconnected未连接或已断开
connecting正在连接
connected已连接
reconnecting正在重连

auth_closed

认证关闭后,host-sdk 会隐藏 widget 并向宿主页面派发该事件。

常见 payload 字段:

字段类型说明
messagestring关闭原因描述
reasonstring关闭原因
sourcestring来源,如 http 或 WS 来源
clearTokenboolean是否需要清理本地 token
closeAuthTypestring服务端关闭类型
closeCodenumber服务端关闭码
terminalstring终端来源

bootstrap_failed

embedded 启动必需链路失败时触发。host-sdk 会隐藏并清理当前 widget。

常见 payload 字段:

字段类型说明
reason'domain_not_whitelisted' | 'login_failed' | 'invalid_bootstrap' | 'timeout' | string失败原因
sourcestring失败来源
mode'embedded'当前运行模式

该事件只表示嵌入启动失败,不用于普通会话内 HTTP 404。

常用场景

以下示例默认页面已经按上文完成 SDK 安装。

监听初始化后打开窗口

window.TWTChatWidget.on("initialized", function () {
window.TWTChatWidget.call("maximize");
});

自定义入口按钮

<button id="open-chat">联系客服</button>
<button id="close-chat">收起客服</button>

<script>
window.__twt = {
appid: "YOUR_APPID",
launcher: "custom",
};

document.querySelector("#open-chat").addEventListener("click", function () {
window.TWTChatWidget.call("maximize");
});

document.querySelector("#close-chat").addEventListener("click", function () {
window.TWTChatWidget.call("minimize");
});
</script>
<script
src=".../install/core.js?version=v1.2"
async
></script>

launcher: 'custom' 会关闭内置 launcher iframe。此时宿主页面需要自己提供入口按钮。

内置 launcher 悬停展示自定义咨询浮层

  • 配置使用 window.__twt
  • 就绪事件使用 window.TWTChatWidget.on("initialized", ...)
  • 打开聊天使用 window.TWTChatWidget.call("maximize")
  • 内置 launcher 元素使用 iframe[data-twt-widget-surface="launcher"]
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<title>... Widget Demo</title>
<style>
#my-custom-popup {
position: fixed;
right: 20px;
bottom: 100px;
z-index: 2147483647;
width: 280px;
padding: 20px;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
pointer-events: none;
background: rgba(255, 255, 255, 0.85);
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 16px;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
opacity: 0;
transform: translateY(15px);
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
}

#my-custom-popup.show {
pointer-events: auto;
opacity: 1;
transform: translateY(0);
}

#my-custom-popup .btn-consult {
display: block;
padding: 10px;
font-size: 14px;
font-weight: 500;
color: #fff;
text-align: center;
text-decoration: none;
margin-bottom: 8px;
background: #2563eb;
border-radius: 8px;
transition:
background 0.2s,
transform 0.1s;
}

#my-custom-popup .btn-consult:hover {
background: #1d4ed8;
}

#my-custom-popup .btn-consult:active {
transform: scale(0.98);
}
</style>
</head>
<body>
<div id="my-custom-popup">
<div
style="font-size: 16px; font-weight: 600; color: #1e293b; margin-bottom: 8px"
>
您好,欢迎咨询!
</div>
<div
style="font-size: 13px; color: #64748b; line-height: 1.5; margin-bottom: 16px"
>
我们随时为您提供贴心服务。点击下方按钮即可发起实时在线沟通。
</div>
<div style="display: flex; flex-direction: column">
<a href="javascript:void(0)" class="btn-consult" id="btn-start-chat"
>立即咨询</a
>
<div style="font-size: 11px; text-align: center; color: #94a3b8">
服务时间: 9:00 - 18:00
</div>
</div>
</div>

<!-- Start of ... code -->
<script>
window.TWTChatWidget = window.TWTChatWidget || {
_q: [],
on: function () {
this._q.push(["on", Array.prototype.slice.call(arguments)]);
},
};

window.__twt = {
appid: "以项目APPID为准",
launcher: "default",
};

(function (n, t) {
var e = {
init: function () {
var n = t.createElement("script");
n.async = !0;
n.type = "text/javascript";
n.src = ".../install/core.js?version=v1.2";
t.head.appendChild(n);
},
};
e.init();
})(window, document);

(function () {
var hoverTimer = null;
var isHovered = false;
var popup = document.getElementById("my-custom-popup");

function showPopup() {
if (popup) {
popup.classList.add("show");
}
}

function hidePopup() {
if (popup) {
popup.classList.remove("show");
}
}

function bindPopupHover(launcherFrame) {
if (!launcherFrame || !popup) {
return;
}

function onEnter() {
isHovered = true;
if (hoverTimer) {
clearTimeout(hoverTimer);
}
showPopup();
}

function onLeave() {
isHovered = false;
if (hoverTimer) {
clearTimeout(hoverTimer);
}
hoverTimer = setTimeout(function () {
if (!isHovered) {
hidePopup();
}
}, 200);
}

launcherFrame.addEventListener("mouseenter", onEnter);
launcherFrame.addEventListener("mouseleave", onLeave);

popup.addEventListener("mouseenter", function () {
isHovered = true;
if (hoverTimer) {
clearTimeout(hoverTimer);
}
});
popup.addEventListener("mouseleave", onLeave);

var startChatButton = document.getElementById("btn-start-chat");
if (startChatButton) {
startChatButton.addEventListener("click", function () {
hidePopup();
window.TWTChatWidget.call("maximize");
});
}
}

window.TWTChatWidget.on("initialized", function () {
var launcherFrame = document.querySelector(
'iframe[data-twt-widget-surface="launcher"]',
);
bindPopupHover(launcherFrame);
});
})();
</script>
<!-- End of ... code -->
</body>
</html>

如果配置了 launcher: "custom",host-sdk 不会创建内置 launcher iframe,上面的 hover 绑定目标也就不存在;这种情况下应改用宿主页面自己的按钮或入口元素。

未读数同步页面标题

var originalTitle = document.title;

window.TWTChatWidget.on("unread_count_changed", function (payload) {
document.title =
payload.count > 0
? "(" + payload.count + ") " + originalTitle
: originalTitle;
});

连接状态观测

window.TWTChatWidget.on("connection_state_changed", function (payload) {
console.log("connection status:", payload.status);
});

window.TWTChatWidget.on("session_connected", function () {
console.log("session connected");
});

window.TWTChatWidget.on("session_disconnected", function (payload) {
console.log("session disconnected:", payload.status);
});

切换主题

window.TWTChatWidget.on("initialized", function () {
window.TWTChatWidget.call("set_theme", "dark");
});

切换语言

window.TWTChatWidget.on("initialized", function () {
window.TWTChatWidget.setLanguage("en");
});