更新记录

1.0.0(2026-11-11)

首个正式版本。

新增

基础能力

  • kefuInit:初始化客服 SDK(AppKey / TenantId / ConfigId),开启已读回执
  • kefuRegister:注册访客账号
  • kefuLogin:支持密码登录与 Token 登录
  • kefuLogout:退出登录,可选解绑推送 Token
  • kefuIsLoggedIn:查询登录状态
  • kefuOpenChat:Android 打开 EaseUI 原生会话页;iOS 为 stub(请自建页)

自建会话

  • kefuBindChat / kefuUnbindChat:绑定、解绑 IM 服务号会话
  • kefuSendText:发送文本(可附带访客信息)
  • kefuSendImage:发送图片(支持原图选项)
  • kefuSendEmoji:发送大表情 / 自定义表情(远程 URL)
  • kefuSendVoice:发送语音
  • kefuSendVideo:发送视频(可选封面、时长)
  • kefuSendFile:发送文件
  • kefuSendOrderCard:发送订单卡片(OrderInfo)
  • kefuSendProductCard:发送商品轨迹卡片(VisitorTrack)
  • kefuLoadHistory:加载本地历史消息(JSON,支持分页 startMsgId)
  • kefuOnMessages:实时消息监听(keepAlive,传 null 取消)
  • kefuOnMessageStatus:消息状态 / 已读回执变化回调

未读 / 已读 / 工具

  • kefuGetUnreadCount:单会话或全部未读数
  • kefuMarkAllAsRead:会话标已读并发送已读回执
  • kefuClearUnread:未读清零(可清全部)
  • kefuGetMsgCount:会话消息条数
  • kefuDetectMsgType:按消息 ID 检测类型
  • kefuProbeReadStates:批量探测消息已读状态
  • kefuResolveVideoUrl:解析历史视频可播放地址

满意度评价

  • kefuInviteEvaluation:主动发起评价邀请
  • kefuSubmitEvaluation:提交评价(1~5 分)

推送

  • kefuBindPushToken:绑定推送 Token(Android:hms / fcm;iOS:APNs hex)
  • kefuSetPushNickname:设置推送显示昵称

SDK 集成

  • Android:kefu-android-sdk-v1.3.3.9(EaseUI aar + player aar)
  • iOS:kefu-iossdk-v1.5.4(HelpDesk + HyphenateChat xcframework,真机 arm64)

文档

  • 新增详细 readme.md(API 参数、用法、消息结构、打包与 FAQ)
  • 新增本 changelog.md

说明

  • 仅支持 App 端;需自定义调试基座或云打包
  • wgt 热更新无法更新原生 SDK / UTS 代码
  • serviceIMNumber(IM 服务号)请使用小写

平台兼容性

uni-app(5.26)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ - - √ - √ √ -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

uni-app x(5.26)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - √ √ - -

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × × √

huanxin-kefu(环信客服访客端)

环信客服云访客端 UTS 原生插件,适用于 uni-app / uni-app x(Vue2 / Vue3)。
Android / iOS 均为 无 UI 封装:插件只提供 SDK 能力,聊天界面需业务侧自建。

项 说明
插件 ID huanxin-kefu
版本 1.0.0
类型 UTS 原生插件(仅 App)
引擎要求 HBuilderX ^3.96.0,uni-app / uni-app-x ^3.1.0
平台 Android、iOS(不支持小程序 / H5 / Harmony)

目录

  1. SDK 来源与目录
  2. 接入前准备
  3. 推荐接入流程
  4. 通用约定
  5. 类型定义
  6. API 详细说明
  7. 消息结构与类型
  8. 完整示例
  9. 打包说明
  10. 权限与后台配置
  11. 常见问题

1. SDK 来源与目录

平台 官方包 本插件已集成
Android kefu-android-sdk-v1.3.3.9 kefu-easeui-release.aar(内含 SDK jar + jni so)+ player-release.aar
iOS kefu-iossdk-v1.5.4 HelpDesk.xcframework + HyphenateChat.xcframework(仅 ios-arm64 真机)
uni_modules/huanxin-kefu/
├── package.json
├── readme.md
└── utssdk/
    ├── interface.uts          # 类型声明(只写 type)
    ├── app-android/
    │   ├── index.uts          # Android 实现
    │   ├── config.json
    │   └── libs/              # aar
    └── app-ios/
        ├── index.uts          # iOS 实现
        ├── HxMsgCodec.swift
        ├── Info.plist
        ├── config.json
        └── Frameworks/        # xcframework

说明

  • Android 自带 EaseUI,可用 kefuOpenChat 打开原生会话页;推荐仍用自建页。
  • iOS 官方访客包 不含 HelpDeskUI,kefuOpenChat 为 stub,请用自建聊天页。

2. 接入前准备

  1. 在环信客服云创建应用,拿到:
    • appKey
    • tenantId(租户 ID)
    • configId(可选,渠道配置 ID)
    • serviceIMNumber(IM 服务号,必须小写,如 kefuchannelimid_xxxx)
  2. 准备访客账号:建议服务端注册 + Token 登录;也可客户端 kefuRegister + 密码登录。
  3. 制作 自定义调试基座(勾选本插件),再用自定义基座运行;正式发版走云打包。

文档参考:创建 APP 关联


3. 推荐接入流程

在客服页内初始化(不要写在 App.vue),顺序建议:

kefuInit → kefuLogin → kefuBindChat → kefuOnMessages / kefuOnMessageStatus → kefuLoadHistory
         → 收发消息 / 未读 / 评价 / 推送
离开页面 → kefuOnMessages(null) → kefuUnbindChat →(可选)kefuLogout
// #ifdef APP-PLUS
import {
  kefuInit, kefuLogin, kefuBindChat, kefuUnbindChat,
  kefuSendText, kefuLoadHistory, kefuOnMessages, kefuOnMessageStatus
} from '@/uni_modules/huanxin-kefu'

const appKey = '你的AppKey'
const tenantId = '你的租户ID'
const serviceIMNumber = 'kefuchannelimid_xxxx' // 必须小写

export default {
  onLoad() {
    const initRes = kefuInit({ appKey, tenantId, consoleLog: true })
    if (initRes.code !== 0) return

    kefuLogin({
      username: 'visitor_001',
      password: '123456', // 或 token: 'xxx'
      complete: (res) => {
        if (res.code !== 0) return
        kefuBindChat(serviceIMNumber)
        kefuOnMessages((raw) => {
          const list = typeof raw === 'string' ? JSON.parse(raw) : raw
          // 刷新消息列表
        })
        kefuOnMessageStatus(() => {
          // 已读状态变化,可配合 kefuProbeReadStates 刷新
        })
        const hist = kefuLoadHistory({ serviceIMNumber, pageSize: 30 })
        const messages = typeof hist === 'string' ? JSON.parse(hist) : hist
      }
    })
  },
  onUnload() {
    kefuOnMessages(null)
    kefuOnMessageStatus(null)
    kefuUnbindChat()
  }
}
// #endif

4. 通用约定

4.1 导入

import {
  kefuInit, kefuRegister, kefuLogin, kefuLogout, kefuIsLoggedIn,
  kefuOpenChat, kefuBindChat, kefuUnbindChat,
  kefuSendText, kefuSendImage, kefuSendEmoji, kefuSendVoice,
  kefuSendVideo, kefuSendFile, kefuSendOrderCard, kefuSendProductCard,
  kefuLoadHistory, kefuOnMessages, kefuOnMessageStatus,
  kefuGetUnreadCount, kefuMarkAllAsRead, kefuClearUnread, kefuGetMsgCount,
  kefuDetectMsgType, kefuInviteEvaluation, kefuSubmitEvaluation,
  kefuBindPushToken, kefuSetPushNickname,
  kefuProbeReadStates, kefuResolveVideoUrl
} from '@/uni_modules/huanxin-kefu'

4.2 返回值

类型 结构 说明
KefuResult { code, message } 同步返回或异步 complete
KefuCountResult { code, message, count } 未读数、消息条数
KefuMsgTypeResult { code, message, msgType } 消息类型检测
  • code === 0 表示成功;非 0 为失败,message 为人可读原因。
  • 常见错误码(插件侧约定,非环信官方错误码全集):
code 含义
0 成功
-1 未初始化 / 通用失败
-2 未登录 / appKey 为空等
-3 必填参数为空(如 IM 号、content、path)
-4 会话不存在 / SDK init 失败等
-5 本地文件不存在 / 创建消息失败
-6 iOS 登录/注册进行中(防重入)
-7 iOS 调用崩溃兜底

4.3 同步 vs 异步

模式 方法示例
同步 kefuInit、kefuIsLoggedIn、kefuBindChat、kefuLoadHistory、kefuGetUnreadCount、kefuMarkAllAsRead、kefuClearUnread、kefuGetMsgCount、kefuDetectMsgType、kefuSetPushNickname、kefuProbeReadStates
异步(complete) kefuRegister、kefuLogin、kefuLogout、各类 kefuSend*、评价、推送绑定、kefuResolveVideoUrl
长生命周期回调 kefuOnMessages、kefuOnMessageStatus(@UTSJS.keepAlive,传 null 取消)

4.4 serviceIMNumber

  • 环信 IM 服务号,一律小写。插件内部会对传入值执行 toLowerCase(),但业务侧也建议直接传小写,避免会话 ID 不一致。
  • 为空时部分接口行为不同,例如:kefuGetUnreadCount('') 返回全部会话未读合计;kefuClearUnread('') 清全部会话未读。

4.5 本地文件路径

发图片 / 语音 / 视频 / 文件时:

  • 支持绝对路径、file:// 前缀(Android 会规范化)。
  • Android 部分场景支持 content://(视频、文件)。
  • 文件必须真实存在,否则返回 code: -5。

4.6 历史消息返回格式

kefuLoadHistory / kefuOnMessages 在双端均可能返回 JSON 字符串(数组)。业务侧建议统一:

function parseMsgs(raw) {
  if (raw == null) return []
  if (typeof raw === 'string') {
    try { return JSON.parse(raw) } catch (e) { return [] }
  }
  return Array.isArray(raw) ? raw : []
}

5. 类型定义

以下与 utssdk/interface.uts 一致。

5.1 KefuInitOptions

字段 类型 必填 说明
appKey string 是 环信 AppKey
tenantId string 是 租户 ID
configId string 否 渠道配置 ID
consoleLog boolean 否 Android:打开 SDK 控制台日志

5.2 KefuVisitorInfo(访客信息,发送消息时可附带)

字段 类型 必填 说明
nickName string 是 昵称
name string 否 真实姓名
phone string 否 手机
email string 否 邮箱
companyName string 否 公司
qq string 否 QQ
desc string 否 描述
userDefineColumn string 否 iOS 自定义字段 JSON 字符串;Android 扩展需后台配合

5.3 KefuChatMessage(历史 / 实时消息统一结构)

字段 类型 说明
msgId string 消息 ID
from string 发送方
to string 接收方
content string 文本内容或占位文案(如 [图片])
msgType string 见 消息类型
direction string send / receive
timestamp number 毫秒时间戳
mediaUrl string? 图片/语音/视频/文件/表情地址
thumbUrl string? 缩略图
duration number? 语音/视频时长(秒)
title string? 订单/商品标题
orderTitle string? 订单号等
price string? 价格
imageUrl string? 卡片图
itemUrl string? 跳转链接
desc string? 描述
inviteId string? 评价邀请 ID(提交评价时回传,iOS 更完整)
sessionId string? 会话 ID(评价用)
agentNick string? 坐席昵称(区分人工 / 自动回复)
isRead boolean? 已读:发送=对方是否已读;接收=我方是否已读

6. API 详细说明

6.1 基础:初始化 / 账号

kefuInit(options): KefuResult

初始化客服 SDK。必须最先调用;重复调用若已成功会返回 already_inited。

const res = kefuInit({
  appKey: 'xxx#xxx',
  tenantId: '12345',
  configId: '',       // 可选
  consoleLog: true    // Android 调试建议开
})
if (res.code !== 0) {
  console.error('init fail', res.message)
}
成功 message 说明
init_ok 首次初始化成功
already_inited 已初始化,跳过

插件会开启单聊已读回执(requireAck / enableRequireReadAck),以便 isRead 能正确反映对方已读。


kefuRegister({ username, password, complete? })

注册访客 IM 账号。需先 kefuInit。

kefuRegister({
  username: 'visitor_001',
  password: '123456',
  complete: (res) => {
    if (res.code === 0) console.log('注册成功')
    else console.error(res.message)
  }
})
参数 必填 说明
username 是 账号
password 是 密码
complete 否 异步结果

生产环境建议服务端注册,避免客户端暴露注册能力。


kefuLogin({ username, password?, token?, complete? })

登录。token 优先于 password;二者至少传一个。

// 密码登录
kefuLogin({
  username: 'visitor_001',
  password: '123456',
  complete: (res) => { /* res.code === 0 */ }
})

// Token 登录(推荐)
kefuLogin({
  username: 'visitor_001',
  token: '从服务端拿到的token',
  complete: (res) => {}
})
行为 说明
已登录 直接 complete({ code: 0, message: 'already_logged_in' }),不重复登录
iOS 密码登录 同步调用 SDK,通过 complete 回传结果
iOS Token 登录 异步 completion
缺少凭证 code: -1,请传 password 或 token

kefuIsLoggedIn(): boolean

是否处于已登录状态(同步)。

if (!kefuIsLoggedIn()) {
  // 去登录
}

kefuLogout(unbindDeviceToken, complete)

退出登录。

参数 类型 说明
unbindDeviceToken boolean 是否解绑推送 deviceToken;一般退出账号传 true
complete function \| null 结果回调,可为 null
kefuLogout(true, (res) => {
  console.log(res.code, res.message)
})

kefuOpenChat(options)

打开原生客服会话页。

参数 必填 说明
serviceIMNumber 是 IM 服务号
visitorInfo 否 访客信息
queue 否 指定技能组(Android EaseUI)
agent 否 指定坐席(Android EaseUI)
kefuOpenChat({
  serviceIMNumber: 'kefuchannelimid_xxxx',
  visitorInfo: { nickName: '访客小明', phone: '***' },
  queue: '',
  agent: ''
})
平台 行为
Android 启动 EaseUI IntentBuilder 原生页(需登录且已 init)
iOS 空实现(无 HelpDeskUI),请用自建页 + kefuBindChat

6.2 自建会话:绑定 / 解绑

kefuBindChat(serviceIMNumber): KefuResult

绑定与指定服务号的会话。自建页登录成功后必须调用。

const res = kefuBindChat('kefuchannelimid_xxxx')
// 成功:{ code: 0, message: 'bind_ok' }

前置条件:已 kefuInit、已登录、serviceIMNumber 非空。


kefuUnbindChat(): void

解绑当前会话。离开客服页时调用。

kefuUnbindChat()

6.3 发送消息

所有发送类接口共性:

  1. 需已初始化 + 已登录 + serviceIMNumber 有效。
  2. 可选 visitorInfo 附带访客资料(Android 文本/图片等会 addContent;卡片类同样支持)。
  3. 通过 complete({ code, message }) 获知结果。

kefuSendText(options)

字段 必填 说明
serviceIMNumber 是 服务号
content 是 文本,不能为空
visitorInfo 否 访客信息
complete 否 回调
kefuSendText({
  serviceIMNumber,
  content: '你好,我想咨询订单',
  visitorInfo: { nickName: '小明' },
  complete: (res) => console.log(res)
})

kefuSendImage(options)

字段 必填 说明
serviceIMNumber 是 服务号
path 是 本地图片路径
original 否 true 原图,默认压缩
visitorInfo 否 访客信息
complete 否 回调
uni.chooseImage({
  count: 1,
  success: (r) => {
    kefuSendImage({
      serviceIMNumber,
      path: r.tempFilePaths[0],
      original: false,
      complete: (res) => console.log(res)
    })
  }
})

kefuSendEmoji(options)

发送大表情 / 自定义表情(远程 URL,不是本地上传图片)。

字段 必填 说明
serviceIMNumber 是 服务号
url 是 表情资源远程地址
emojiName 否 表情名(类型已声明,按业务需要传)
visitorInfo 否 访客信息
complete 否 回调
kefuSendEmoji({
  serviceIMNumber,
  url: 'https://example.com/emoji/smile.gif',
  emojiName: 'smile',
  complete: (res) => console.log(res)
})

kefuSendVoice(options)

字段 必填 说明
serviceIMNumber 是 服务号
path 是 本地语音文件路径
duration 是 时长(秒)
visitorInfo 否 访客信息
complete 否 回调
kefuSendVoice({
  serviceIMNumber,
  path: localVoicePath,
  duration: 3,
  complete: (res) => console.log(res)
})

kefuSendVideo(options)

字段 必填 说明
serviceIMNumber 是 服务号
path 是 本地视频路径(Android 支持 content://)
thumbPath 否 封面图本地路径
duration 否 时长(秒)
visitorInfo 否 访客信息
complete 否 回调
kefuSendVideo({
  serviceIMNumber,
  path: videoPath,
  thumbPath: coverPath,
  duration: 12,
  complete: (res) => console.log(res)
})

历史消息中的视频播放地址请用 kefuResolveVideoUrl 解析(见 6.7)。


kefuSendFile(options)

字段 必填 说明
serviceIMNumber 是 服务号
path 是 本地文件路径
displayName 否 展示文件名(iOS 使用;Android 主要靠路径)
visitorInfo 否 访客信息
complete 否 回调
kefuSendFile({
  serviceIMNumber,
  path: filePath,
  displayName: '说明书.pdf',
  complete: (res) => console.log(res)
})

kefuSendOrderCard(options)

发送订单卡片(底层 OrderInfo)。

字段 必填 说明
serviceIMNumber 是 服务号
title 是 标题
orderTitle 否 订单号 / 订单副标题
price 否 价格
desc 否 描述
imageUrl 否 商品图 URL
itemUrl 否 点击跳转 URL
visitorInfo 否 访客信息
complete 否 回调
kefuSendOrderCard({
  serviceIMNumber,
  title: '无线耳机',
  orderTitle: 'ORD20261111001',
  price: '¥199.00',
  desc: '待发货',
  imageUrl: 'https://example.com/p.png',
  itemUrl: 'https://example.com/order/1',
  complete: (res) => console.log(res)
})

kefuSendProductCard(options)

发送商品轨迹卡片(底层 VisitorTrack),字段同卡片共用类型,一般不用 orderTitle。

kefuSendProductCard({
  serviceIMNumber,
  title: '春季新款外套',
  price: '¥299',
  desc: '用户正在浏览',
  imageUrl: 'https://example.com/goods.png',
  itemUrl: 'https://example.com/goods/1001',
  complete: (res) => console.log(res)
})

历史/实时消息中:订单 msgType === 'order',商品 msgType === 'track'。


6.4 历史消息与实时收消息

kefuLoadHistory(options): any

从本地会话加载历史(必要时 Android 会回退 EM 漫游)。

字段 必填 默认 说明
serviceIMNumber 是 — 服务号
pageSize 否 Android 默认 20,iOS 默认 30 条数
startMsgId 否 空 从该消息 ID 之前继续翻页;空串勿传当有效 ID(会导致查不到)
// 第一页
let raw = kefuLoadHistory({ serviceIMNumber, pageSize: 30 })
let list = parseMsgs(raw)

// 上拉加载更多:传当前列表最旧一条的 msgId
if (list.length > 0) {
  raw = kefuLoadHistory({
    serviceIMNumber,
    pageSize: 30,
    startMsgId: list[0].msgId
  })
  const older = parseMsgs(raw)
  list = older.concat(list)
}
说明 详情
返回值 JSON 字符串数组(推荐按字符串 JSON.parse)
排序 时间升序(旧 → 新)
首屏 Android 加载成功后会 markAllMessagesAsRead(无 startMsgId 时)
失败/空 返回 '[]'

kefuOnMessages(callback | null): void

注册实时消息监听。长生命周期回调,传 null 取消。

kefuOnMessages((raw) => {
  const msgs = parseMsgs(raw)
  msgs.forEach((m) => {
    console.log(m.msgType, m.content, m.direction)
  })
  // 追加到 UI 列表
})

// 离开页面
kefuOnMessages(null)

实现使用 @UTSJS.keepAlive + export function,避免 UTS 回调只触发一次。


kefuOnMessageStatus(callback | null): void

发送状态 / 已读回执变化时回调(无参数)。页面可据此刷新气泡已读状态。

kefuOnMessageStatus(() => {
  // 例如对当前列表 msgId 调 kefuProbeReadStates 后更新 isRead
})

kefuOnMessageStatus(null) // 取消

6.5 未读 / 已读 / 消息数

kefuGetUnreadCount(serviceIMNumber): KefuCountResult

const one = kefuGetUnreadCount('kefuchannelimid_xxxx')
// { code: 0, message: 'success', count: 3 }

const all = kefuGetUnreadCount('') // 全部会话未读合计
serviceIMNumber 行为
非空 指定会话未读;无会话时 count: 0,message: 'no_conversation'
空串 全部会话未读合计

kefuMarkAllAsRead(serviceIMNumber): KefuResult

将会话标为已读,并向对方发送已读回执(便于对方气泡变「已读」)。

const res = kefuMarkAllAsRead(serviceIMNumber)
// 成功:{ code: 0, message: 'marked' }

kefuClearUnread(serviceIMNumber): KefuResult

未读清零。

kefuClearUnread(serviceIMNumber) // 指定会话
kefuClearUnread('')              // 全部会话

成功 message:cleared / all_cleared(Android 清全部时)。


kefuGetMsgCount(serviceIMNumber): KefuCountResult

指定会话本地消息总条数。

const res = kefuGetMsgCount(serviceIMNumber)
console.log(res.count)

kefuDetectMsgType(msgId): KefuMsgTypeResult

按消息 ID 检测类型。

const t = kefuDetectMsgType(msgId)
// { code: 0, message: 'success', msgType: 'image' }

msgType 可能值见 7.2。


kefuProbeReadStates(msgIdsJson): string

按消息 ID 批量探测已读(不拉历史)。

| 入参 | '["id1","id2"]' JSON 字符串 | | 返回 | '{"id1":true,"id2":false}' JSON 字符串 |

语义:

  • 自己发的:对方是否已读(isAcked)
  • 对方发的:我方是否已读(!isUnread)
const ids = list.map((m) => m.msgId)
const map = JSON.parse(kefuProbeReadStates(JSON.stringify(ids)))
list.forEach((m) => { m.isRead = !!map[m.msgId] })

6.6 满意度评价

kefuInviteEvaluation({ serviceIMNumber, sessionId?, complete? })

访客侧主动发起满意度评价邀请(是否允许取决于后台策略)。

kefuInviteEvaluation({
  serviceIMNumber,
  sessionId: '', // 可选
  complete: (res) => console.log(res)
})

kefuSubmitEvaluation(options)

提交评价(score 1~5,插件内会钳制)。

字段 必填 说明
msgId 是 评价邀请消息 ID
score 是 1~5 分
detail 否 评价文案
evaluateWay 否 visitor / agent / system(类型声明;Android 当前主要走 MessageHelper)
inviteId 否 邀请 ID(iOS 提交时使用)
sessionId 否 会话 ID
serviceIMNumber iOS 强烈建议 iOS 实现要求非空;Android 可不依赖
complete 否 回调
kefuSubmitEvaluation({
  msgId: evalMsg.msgId,
  score: 5,
  detail: '服务很好',
  inviteId: evalMsg.inviteId,
  sessionId: evalMsg.sessionId,
  serviceIMNumber,
  complete: (res) => console.log(res)
})

收到 msgType === 'evaluation' 的消息时,用其 msgId(及 inviteId / sessionId)提交。


6.7 视频地址解析

kefuResolveVideoUrl({ msgId, serviceIMNumber?, complete? })

解析历史视频可播放地址:优先本地文件 → 触发 SDK 下载 → 失败回退远程直链。
成功时 complete 的 message 字段即为可播 URL(code === 0)。

kefuResolveVideoUrl({
  msgId: videoMsg.msgId,
  serviceIMNumber,
  complete: (res) => {
    if (res.code === 0) {
      const playUrl = res.message
      // 用 video 组件播放
    }
  }
})

6.8 推送

本插件只负责把 token / 昵称交给环信;厂商推送通道需 App 自行集成,拿到 token 后再调用。

kefuBindPushToken({ token, vendor?, complete? })

平台 token vendor
Android 厂商推送 token 字符串 hms(默认)或 fcm
iOS 设备 token 的 hex 字符串 可省略(内部按 APNs 绑定)
// Android 华为
kefuBindPushToken({
  token: hmsToken,
  vendor: 'hms',
  complete: (res) => console.log(res)
})

// Android FCM
kefuBindPushToken({ token: fcmToken, vendor: 'fcm', complete: (res) => {} })

// iOS:把 NSData deviceToken 转成 hex 再传
kefuBindPushToken({
  token: deviceTokenHex,
  complete: (res) => console.log(res)
})

kefuSetPushNickname(nickname): KefuResult

设置推送显示昵称(同步)。

const res = kefuSetPushNickname('访客小明')
// 成功:{ code: 0, message: 'nickname_ok' }

7. 消息结构与类型

7.1 使用建议

自建 UI 时按 msgType 分支渲染:

msgType UI 建议
txt 文本气泡
image / emoji 图片;表情可用 mediaUrl
voice 语音条,duration + mediaUrl
video 封面 thumbUrl,点击后 kefuResolveVideoUrl
file 文件行,mediaUrl / content
order 订单卡片:title / orderTitle / price / imageUrl / itemUrl
track 商品卡片:title / price / imageUrl / itemUrl
evaluation 评价邀请 UI → kefuSubmitEvaluation
robot_menu / articles / form / cmd 按业务扩展或降级为文本提示

direction:send 右对齐,receive 左对齐。
agentNick 可用于展示坐席名;无坐席时多为系统/机器人。

7.2 msgType 取值

txt | image | voice | video | file | emoji | order | track | evaluation | robot_menu | articles | form | cmd | general | unknown


8. 完整示例

// pages/kefu/chat.vue 片段(APP-PLUS)
import {
  kefuInit, kefuLogin, kefuIsLoggedIn,
  kefuBindChat, kefuUnbindChat,
  kefuSendText, kefuSendImage, kefuSendVoice,
  kefuLoadHistory, kefuOnMessages, kefuOnMessageStatus,
  kefuMarkAllAsRead, kefuGetUnreadCount,
  kefuSubmitEvaluation, kefuResolveVideoUrl, kefuProbeReadStates
} from '@/uni_modules/huanxin-kefu'

const CFG = {
  appKey: 'YOUR_APP_KEY',
  tenantId: 'YOUR_TENANT_ID',
  serviceIMNumber: 'kefuchannelimid_xxxx'
}

function parseMsgs(raw) {
  if (typeof raw === 'string') {
    try { return JSON.parse(raw) } catch (e) { return [] }
  }
  return Array.isArray(raw) ? raw : []
}

export default {
  data() {
    return { messages: [], serviceIMNumber: CFG.serviceIMNumber }
  },
  onLoad() {
    kefuInit({
      appKey: CFG.appKey,
      tenantId: CFG.tenantId,
      consoleLog: true
    })
    const afterLogin = () => {
      kefuBindChat(this.serviceIMNumber)
      kefuOnMessages((raw) => {
        this.messages = this.messages.concat(parseMsgs(raw))
      })
      kefuOnMessageStatus(() => this.refreshReadStates())
      this.messages = parseMsgs(kefuLoadHistory({
        serviceIMNumber: this.serviceIMNumber,
        pageSize: 30
      }))
      kefuMarkAllAsRead(this.serviceIMNumber)
    }
    if (kefuIsLoggedIn()) afterLogin()
    else {
      kefuLogin({
        username: 'visitor_001',
        token: 'SERVER_TOKEN',
        complete: (res) => { if (res.code === 0) afterLogin() }
      })
    }
  },
  onUnload() {
    kefuOnMessages(null)
    kefuOnMessageStatus(null)
    kefuUnbindChat()
  },
  methods: {
    sendText(text) {
      kefuSendText({
        serviceIMNumber: this.serviceIMNumber,
        content: text,
        visitorInfo: { nickName: '访客' },
        complete: (res) => {
          if (res.code === 0) {
            // 可乐观插入或重新 loadHistory
          }
        }
      })
    },
    refreshReadStates() {
      const ids = this.messages.map((m) => m.msgId)
      if (!ids.length) return
      const map = JSON.parse(kefuProbeReadStates(JSON.stringify(ids)))
      this.messages.forEach((m) => { m.isRead = !!map[m.msgId] })
    },
    playVideo(msg) {
      kefuResolveVideoUrl({
        msgId: msg.msgId,
        serviceIMNumber: this.serviceIMNumber,
        complete: (res) => {
          if (res.code === 0) {
            // res.message = 可播 url
          }
        }
      })
    },
    submitEval(msg, score, detail) {
      kefuSubmitEvaluation({
        msgId: msg.msgId,
        score,
        detail,
        inviteId: msg.inviteId,
        sessionId: msg.sessionId,
        serviceIMNumber: this.serviceIMNumber,
        complete: (res) => console.log(res)
      })
    },
    badge() {
      return kefuGetUnreadCount(this.serviceIMNumber).count
    }
  }
}

9. 打包说明

  1. 运行 → 制作自定义调试基座(勾选 huanxin-kefu)
  2. 安装基座后,用 自定义调试基座 运行调试
  3. 正式发版:发行 → 原生 App-云打包
  4. wgt 热更新无法更新原生 SDK / UTS;改 index.uts、aar、xcframework 后必须重打基座或云打包

Android 注意

  • 不要再单独放 kefu-sdk-*.jar 或重复 libhyphenate.so,否则 Duplicate class / so 冲突。
  • minSdkVersion:21;ABI:armeabi-v7a、arm64-v8a。
  • 音视频坐席(Agora 等)未写入 config.json,避免云打包依赖失败;需要时再加。

iOS 注意

  • Framework 仅 ios-arm64(真机);模拟器需自行换含模拟器切片的包。
  • 部署版本:iOS 12.0+。
  • 云打包勿保留 HyphenateChat.swiftmodule(会导致 module interface 报错)。
  • Info.plist 已声明相机 / 麦克风 / 相册用途说明。

10. 权限与后台配置

权限(聊天收发媒体)

平台 用途
Android 相机、麦克风、存储(随业务申请)
iOS NSCameraUsageDescription、NSMicrophoneUsageDescription、NSPhotoLibraryUsageDescription 等(插件 Info.plist 已带)

环信后台

  • AppKey、TenantId、IM 服务号(小写)
  • APP 渠道关联:文档
  • 用户账号建议服务端注册,客户端 Token 登录
  • 推送:在环信控制台配置厂商证书 / APNs,App 取 token 后调 kefuBindPushToken

隐私声明(插件 package.json)

  • 广告:无
  • 数据:插件不采集业务数据,消息由环信云处理
  • 权限:相机、麦克风、存储用于图片/语音等

11. 常见问题

Q: 标准基座里调用无反应 / 报找不到模块?
A: UTS 原生插件必须用自定义调试基座或云打包后的包。

Q: 历史一直为空?
A: 确认 serviceIMNumber 小写、已登录、已发过消息产生会话;startMsgId 不要传空串当有效 ID;可先不传 startMsgId 拉首页。

Q: iOS kefuOpenChat 没页面?
A: 预期行为。请自建聊天页,使用 kefuBindChat + 发送/历史/监听 API。

Q: 发送图片失败「文件不存在」?
A: 检查 path 是否为真实本地路径;Android 注意 file:// 与相册临时路径有效期。

Q: 已读状态不更新?
A: 确保走过 kefuInit(已开 ack);进入会话调 kefuMarkAllAsRead;监听 kefuOnMessageStatus 并用 kefuProbeReadStates 刷新。

Q: 视频点了播不了?
A: 历史视频的 mediaUrl 可能不可直接播,请用 kefuResolveVideoUrl,成功后用返回的 message 作为播放地址。

Q: 改了插件代码标准运行仍是旧逻辑?
A: 重做自定义基座 / 重新云打包;wgt 无法更新原生层。


API 速查表

方法 同步/异步 一句话
kefuInit 同步 初始化 SDK
kefuRegister 异步 注册访客
kefuLogin 异步 密码或 Token 登录
kefuLogout 异步 退出
kefuIsLoggedIn 同步 是否已登录
kefuOpenChat — 原生页(仅 Android EaseUI)
kefuBindChat 同步 绑定会话
kefuUnbindChat — 解绑会话
kefuSendText 异步 发文本
kefuSendImage 异步 发图片
kefuSendEmoji 异步 发大表情(远程 URL)
kefuSendVoice 异步 发语音
kefuSendVideo 异步 发视频
kefuSendFile 异步 发文件
kefuSendOrderCard 异步 发订单卡片
kefuSendProductCard 异步 发商品轨迹
kefuLoadHistory 同步 拉历史(JSON 字符串)
kefuOnMessages 回调 收消息;null 取消
kefuOnMessageStatus 回调 状态/已读变化;null 取消
kefuGetUnreadCount 同步 未读数
kefuMarkAllAsRead 同步 会话标已读 + 回执
kefuClearUnread 同步 未读清零
kefuGetMsgCount 同步 会话消息条数
kefuDetectMsgType 同步 检测消息类型
kefuProbeReadStates 同步 批量探测已读 JSON
kefuInviteEvaluation 异步 发起评价邀请
kefuSubmitEvaluation 异步 提交评价 1~5
kefuResolveVideoUrl 异步 解析视频可播 URL
kefuBindPushToken 异步 绑定推送 token
kefuSetPushNickname 同步 推送昵称

隐私、权限声明

1. 本插件需要申请的系统权限列表:

相机、麦克风、存储权限用于客服聊天收发图片语音

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集数据,消息由环信云服务处理

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无