Appearance
商品、订单与文章选择器
客户点几下就能把「我说的是这个沙发」「我问的是这笔订单」发给客服,省掉来回追问的过程。
做出来是什么样
客户点输入框旁边的 📎(手机上是 ➕),菜单里多出一项「选商品」。点开是一个半屏列表,选中某个商品就直接把商品卡片发给了客服。
列表数据全部由你的系统提供,所以这件事必须写代码,工作台里没有对应的开关。
什么时候用
- 电商网站,客户咨询往往围绕某件商品或某笔订单
- 客服需要知道对方在看什么才能给出准确答复
- 你有商品、订单或文章的数据接口
开始之前
- 已完成接入代码的接入,能在
window.hecong(cb)里拿到命令对象 - 下面的代码片段都写在
window.hecong(function (hc) { ... })内部
入口放在哪
整件事分两步:先往输入区加一个入口,再把列表数据交给合从。这一节讲第一步,下面的「两步接上」给完整代码。
两个位置任选,区别在显眼程度,能挂的动作完全一样。
| 方法 | 位置 | 电脑上 | 手机上 | 什么时候用 |
|---|---|---|---|---|
hc.registerComposerAction(action) | 附件面板,收着 | 📎 菜单里追加一项 | ➕ 九宫格里追加一格 | 低频动作,不打扰主流程 |
hc.registerQuickReply(action) | 快捷按钮区,常驻 | 输入框正上方那排的末尾 | 同电脑 | 希望客户一眼看到并点它 |
呈现形态自动适配,你不用判断客户在用什么设备。
js
hc.registerComposerAction({
id: 'my-order',
label: '发送我的订单',
onClick: function () { hc.openPicker('order') }
})字段说明
| 字段 | 必填 | 说明 |
|---|---|---|
id | ✅ | 唯一标识。用同一个 id 再注册一次会覆盖前一个 |
label | ✅ | 显示文字。多语言需要你自己按客户语言传对应文案 |
icon | 内联 SVG 字符串,要以 <svg 开头。不传则用通用图标 | |
onClick | ✅ | 点击回调 |
入口追加在末尾,内置的图片、视频、文件排在自定义项前面。注册顺序就是显示顺序,想调整先后,把两行代码对调即可。
移除入口
两个方法都返回一个注销函数:
js
const off = hc.registerComposerAction({ /* … */ })
// 不再需要时
off()多语言的文字
label 不走聊天窗的语言包,需要你自己判断客户语言后传对应文案:
js
const labels = { 'zh-CN': '发送我的订单', en: 'Send my order' }
hc.registerQuickReply({
id: 'my-order',
label: labels[navigator.language] || labels.en,
onClick: () => hc.openPicker('order')
})两步接上
第一步,往输入区加一个入口;第二步,把列表数据交给合从。
js
// 1) 加入口 —— 收在附件面板里
hc.registerComposerAction({
id: 'products',
label: '选商品',
onClick: function () { hc.openPicker('product') }
})
// 2) 给数据 —— 客户点开时才去你的接口拿
hc.on('picker:request', function (e) {
fetch('/api/my-products')
.then(function (r) { return r.json() })
.then(function (list) { hc.setPickerData(e.type, list) })
})入口放在哪
有两个位置可选,用法一样,区别只是显眼程度:
| 方法 | 入口出现在 |
|---|---|
registerComposerAction(action) | 附件面板里(电脑端是回形针菜单的一项,手机端是加号面板的一格) |
registerQuickReply(action) | 输入框正上方那排快捷按钮,比附件面板显眼 |
两个方法都返回一个注销函数,调用它就把入口撤掉:
js
const off = hc.registerQuickReply({
id: 'orders',
label: '选订单',
onClick: function () { hc.openPicker('order') }
})
off() // 撤掉这个入口几条要点:
id相同就是覆盖。重复注册同一个id会替换掉之前那个,旧的注销函数不会误删新入口- 注册顺序就是显示顺序。附件面板里内置项永远在前、你注册的在后;快捷按钮区不提供排序参数
icon必须是内联 SVG 字符串,以<svg开头才会渲染,其他值一律回落到通用图标- 缺
id、label或onClick会被静默忽略,注册不报错,只是入口不出现
openPicker 会顺带把聊天窗打开
网页接入渠道上,openPicker() 和 open() 一样会触发聊天窗加载并打开。想在客户没点开聊天窗时先备好数据,用 setPickerData(),它不会打开窗口。
两种供数方式
静态预置
数据量小、页面加载时就已知的场景,直接塞进去:
js
hc.setPickerData('product', [
{
title: '示例商品 A',
description: '规格 / 颜色',
imageUrl: 'https://example.com/a.jpg',
price: { amount: 39900, currency: 'CNY' },
detailUrl: 'https://example.com/p/a'
}
])按需加载
数据要调接口取(比如「我的订单」需要登录态),监听 picker:request 事件,在客户真正打开选择器时再拉:
js
hc.on('picker:request', async function (e) {
if (e.type !== 'order') return
const orders = await fetch('/api/my-orders').then(r => r.json())
hc.setPickerData('order', orders.map(function (o) {
return {
orderId: o.orderNo,
title: '订单 ' + o.orderNo,
total: { amount: o.totalCents, currency: 'CNY' },
status: o.status, // pending / paid / shipped / delivered / refunded / cancelled
createdAt: o.createdAtMs, // 毫秒时间戳
items: o.items.map(function (it) {
return {
name: it.productName,
imageUrl: it.coverUrl,
quantity: it.quantity,
price: { amount: it.priceCents, currency: 'CNY' }
}
}),
detailUrl: o.detailUrl
}
}))
})客户第一次打开选择器时,聊天窗发现没有缓存数据就会触发这个事件,你回填后列表随即呈现。
字段定义
product(商品)、order(订单)、article(文章),每类最多 50 条,超出部分不会呈现。
三类的字段各不相同,字段名必须完全一致:写错名字的字段会被静默丢弃,不报错、不提示,只表现为卡片上少了图或少了描述。
product 商品卡
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
title | string | 是 | 商品名,≤200 字符 |
description | string | 否 | 规格、卖点等,≤2000 字符 |
imageUrl | string | 否 | 商品图外链,必须是合法 URL |
price | Money | 否 | 现价 |
originalPrice | Money | 否 | 划线原价 |
detailUrl | string | 否 | 商品详情页,客服点击可跳转 |
productId | string | 否 | 你自己的 SKU 编号,≤128 字符 |
layout | 'compact' | 'large' | 否 | 卡片形态,缺省由聊天窗决定 |
order 订单卡
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
orderId | string | 是 | 订单号,≤128 字符 |
title | string | 是 | 订单标题,≤200 字符 |
total | Money | 是 | 订单总额 |
status | 见下 | 是 | 订单状态 |
createdAt | number | 是 | 下单时间,毫秒时间戳 |
items | array | 是 | 商品行,≤30 条 |
items[].name | string | 是 | 商品名 |
items[].quantity | number | 是 | 数量,正整数 |
items[].price | Money | 是 | 单价 |
items[].imageUrl | string | 否 | 商品图外链 |
detailUrl | string | 否 | 订单详情页 |
status 取值:pending(待付款)、paid(已付款)、shipped(已发货)、delivered(已送达)、refunded(已退款)、cancelled(已取消)。传其他值会被拒绝。
商品行超过 30 条,这张卡整个发不出去,不是只显示前 30 条。卡片是摘要不是清单,行数多的订单挑主要商品放进去,其余让客服点 detailUrl 看详情。
article 文章卡
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
title | string | 是 | 文章标题,≤200 字符 |
description | string | 是 | 摘要,≤2000 字符 |
url | string | 是 | 文章链接 |
imageUrl | string | 否 | 封面图外链 |
source | string | 否 | 来源站点名,≤100 字符 |
Money 金额
js
{ amount: 39900, currency: 'CNY' } // ¥399.00
{ amount: 99, currency: 'USD' } // $0.99amount 是最小货币单位的整数(人民币为分),不接受浮点数。currency 用 ISO 4217 代码。展示时由聊天窗按客户语言格式化,你不用自己拼货币符号。
图片一律用外链
卡片图只能传 imageUrl,值是你自己的图片地址。
不要传 imageAttachmentId。那是客服在工作台上传素材时才用的字段,服务端会校验附件归属,用它发出去的卡片客户这边发不成功,而且不会有明确的报错提示。
客户选完之后
点中某一项,卡片直接作为一条消息发给客服,你不需要再做任何事。客服在工作台里看到的是完整卡片,能点进 detailUrl。
从你自己的按钮唤起
除了客户点聊天窗里的入口,你也可以程序化打开:
js
hc.openPicker('order')比如客户是从订单详情页点进来咨询的,就可以在打开聊天窗时直接弹出订单列表。
下一步
- 监听聊天窗事件 ——
picker:request及其他事件