Appearance
监听聊天窗事件
聊天窗发生状态变化时收到通知,用于埋点统计或联动你自己的业务逻辑。
什么时候用
- 想统计有多少访客真的发起了咨询
- 想在客服回复时给页面加个红点或提示音
- 想在客户身份绑定完成后触发自己的逻辑
开始之前
下面订阅事件的代码都写在 window.hecong(function (hc) { ... }) 内部,并默认你已按接入代码与配置项写好那行占位函数。
事件从什么时候开始产生
网页接入是懒加载的:访客点开聊天窗之前,不会下载聊天窗程序,也不会建立长连接。所以所有事件都要等访客点开聊天窗之后才可能触发。只有 launcher:click 例外 —— 它报的是聊天按钮被点这个动作本身,不依赖聊天窗加载。
session:started 比这更晚:只有真正建立长连接时才触发,点开翻看不算 —— 通常要等访客发出第一条消息,或者系统查到他还有没结束的对话。
有两处容易误读:
- 访客没点开聊天窗时,聊天按钮上的未读红点照样会亮 —— 那是按钮自己发了一次轻量请求查出来的,不用下载聊天窗程序。但这时
unread不会触发,拿它同步你自己页面上的角标,会漏掉这段时间的未读。 user:identified表示的是「已登录访客开始咨询」,只有点开聊天窗的访客会触发它。它的数量必然远低于登录数,别拿它统计登录量。
怎么订阅
js
const off = hc.on('message:incoming', function (msg) {
console.log('客服发来一条消息', msg.text)
})
// 不需要再监听时
off()hc.on() 返回一个退订函数。同一个事件可以挂多个回调,按注册顺序依次执行。某个回调里抛了异常不会影响其他回调,也不会影响聊天窗本身。
事件名写错不会抛异常,而是在控制台输出一条提示并列出可用的事件名,返回一个空的退订函数。
可订阅的事件
对话与消息
| 事件 | 什么时候触发 | 回调参数 |
|---|---|---|
message | 任意消息,包含客户自己发出并送达的 | PublicMessageInfo |
message:incoming | 只有对方的消息(客服、机器人、系统) | PublicMessageInfo |
unread | 未读数变化 | { count: number } |
message:button-click | 消息里的按钮或菜单项被点 | 见下 |
conversation:start | 新对话创建 | { conversationId: string } |
message:button-click 的回调参数是 { actionId?: string, text: string, url?: string }。 | conversation:end | 对话结束(客服关闭或超时归档) | { conversationId?: string } | | assignee:change | 接待人变化,机器人与人工之间切换 | PublicAssigneeInfo | | header:change | 聊天窗标题栏显示的身份变化 | PublicHeaderIdentity |
做红点、震动、消息推送用 message:incoming,它不会被客户自己发的消息触发。
assignee:change 常用来在转人工时给客户一个提示,或者统计机器人的解决率:
js
{
type: 'agent', // 'agent' 人工 / 'bot' 机器人 / null 无人接待(排队中或对话已结束)
name: '小王' // 接待人昵称,拿不到时没有这个字段
}header:change 用来在你自己的页面上显示「当前为您服务的客服」—— 比如把接待人的头像昵称画进你的页头。它报的是聊天窗标题栏此刻显示的身份:会话开始前是渠道的兜底身份,客服或机器人接待后换成接待者,转接时再变。和 assignee:change 的区别在无人接待时 —— assignee:change 给 null,header:change 仍给渠道身份,所以拿它画界面不用自己准备兜底。
js
hc.on('header:change', function (h) {
// h.nickname / h.avatar / h.signature 都可能缺,按字段可能不存在来写
// h.source:'agent' 人工 / 'bot' 机器人 / 'channel' 渠道兜底 / null 无
// h.pending 为 true 时身份还在解析中,建议先画占位,别画空白
renderServiceHeader(h)
})PublicMessageInfo 的字段:
| 字段 | 类型 | 说明 |
|---|---|---|
serverId | string? | 服务端消息 ID。本地还没发出去的占位消息没有这个字段 |
from | 'visitor' | 'agent' | 'bot' | 'system' | 谁发的 |
text | string | 纯文本内容。富消息取其可读文本,没有文本内容时是空串 |
contentType | string | text / image / video / file / audio / card 等 |
createdAt | number | 服务端时间戳,毫秒 |
消息对象只有这五个字段,其余属于内部结构,不要依赖。
身份
| 事件 | 什么时候触发 | 回调参数 |
|---|---|---|
user:identified | identify() 完成,包括再次调用更新资料 | IdentifyResult |
user:reset | reset() 完成 | IdentifyResult |
IdentifyResult 里的 customerCreatedOrMerged 告诉你这次是新建了档案还是合并到已有客户,acceptedKeys 是 data 里实际写进去的字段名。
连接
| 事件 | 什么时候触发 | 回调参数 |
|---|---|---|
session:started | 首次与合从建立连接,匿名访客也算 | 无 |
network:online | 连接恢复 | 无 |
network:offline | 连接断开 | 无 |
表单与评价
| 事件 | 什么时候触发 | 回调参数 |
|---|---|---|
form:submitted | 表单提交成功 | { formId?: string, formType?: string, fields: Record<string, unknown> } |
rating:submitted | 会话评价提交成功 | { score: number, comment?: string } |
三种表单都会触发 form:submitted,用 formType 区分是哪一种:
formType | 对应 |
|---|---|
prechat | 询前表单 |
leave | 留言表单 |
message | 消息里的表单 |
formId 只有消息里的表单才有,询前表单和留言表单没有这个字段。fields 是访客填写的内容,键名是字段标识。
只在提交成功后触发,提交失败不触发。
别拿它做业务对账
这是浏览器里的事件。访客提交完立刻关掉页面、网络断了、或者被浏览器插件拦掉,你都可能收不到这条通知。用它做实时提醒、埋点统计、同步到自己的 CRM 都合适,但不要当成一条都不会少的数据来源 —— 完整的表单内容以工作台里的记录为准。
选择器
| 事件 | 什么时候触发 | 回调参数 |
|---|---|---|
picker:request | 选择器打开但没有缓存数据,等你回填 | { type: 'product' | 'order' | 'article' } |
没有 ready 和 error 事件
拿到 hc 的时候就已经是就绪状态,不需要额外的 ready 事件。
内部错误也不通过事件暴露,失败信息在各个方法返回的 Promise 里。
窗口与按钮事件
这三个是网页接入渠道独有的,对话链接是整页聊天,没有窗口和按钮这两个概念。
| 事件 | 什么时候触发 | 回调参数 |
|---|---|---|
chat:open | 聊天窗打开 | 无 |
chat:close | 聊天窗关闭 | 无 |
launcher:click | 合从的聊天按钮被点击 | 无 |
聊天窗就绪的时机
前面说过 hc.on() 里没有 ready 事件。页面级的加载完成通知走的是另一条路 —— 原生 DOM 事件 hecong:ready,e.detail 即命令对象:
js
window.addEventListener('hecong:ready', function (e) {
e.detail.open()
})多数情况下用 window.hecong(cb) 就够了 —— 它早调晚调都能拿到命令对象,不需要你自己判断时机。原生事件适合你已经有一套统一的事件总线、希望聊天窗也接进去的场景,但它是一次性广播,监听要写在跟接入 script 标签同一处的内联脚本里;注册晚了就静默错过,不会报错。
埋点示例
js
window.hecong(function (hc) {
// 区分新老客户
hc.on('user:identified', function (r) {
analytics.track(r.customerCreatedOrMerged ? '新客户建档' : '老客户回访', {
userId: r.userId
})
})
// 客服回复时在标签页标题上加红点
hc.on('unread', function (e) {
document.title = e.count > 0 ? `(${e.count}) 我的网站` : '我的网站'
})
})