更新记录
2.0.0(2026-07-23) 下载此版本
2.0.0
- 支持 uni-app 与 uni-app x,并提供 Vue/UVue easycom 聊天组件。
- 支持用户端与员工端独立实例、会话列表、消息收发、未读统计和 WebSocket 重连。
- 新增聊天页面路由构造与打开 API,统一从插件根目录导入。
- 图片和视频上传支持自定义 multipart 字段名、表单参数、请求头和超时时间。
- 修复 Web 图片上传失败、Android 页面样式异常及切换服务配置后沿用旧会话的问题。
- 修复 uni-app x App-iOS 实例子模块调用和持续状态订阅失效的问题。
- 位置选择入口支持按需启用。
平台兼容性
uni-app(3.8.1)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ | √ | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
uni-app x(5.0)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| × | × | √ |
JSZN IM SDK
为 uni-app 与 uni-app x 提供用户端、员工端即时通讯 API、WebSocket、会话与未读 管理,以及 Vue/UVue easycom 聊天 UI。
安装
通过 DCloud 插件市场导入后,插件目录应为:
uni_modules/jszn-im-sdk
API、常量、错误和 UTS 类型统一从插件根目录导入。不要依赖 /index、core/、
utssdk/ 或其他内部路径。
import {
createUserIMSDK,
type IMSDKConfig,
} from '@/uni_modules/jszn-im-sdk'
传统 uni-app JavaScript 页面不需要导入 UTS 类型:
import { createUserIMSDK } from '@/uni_modules/jszn-im-sdk'
完整配置
uni-app 可在 config/im.js 中导出配置对象;uni-app x 建议在 config/im.uts
中声明为 IMSDKConfig。以下值均为占位值,请替换为服务提供方分配的配置。
import type { IMSDKConfig } from '@/uni_modules/jszn-im-sdk'
export const imConfig: IMSDKConfig = {
// 聊天 UI 主色。默认值:#4a90e2。
themeColor: '#4a90e2',
// 主色的浅色变体,用于弱强调状态。默认值:#6faef0。
themeColorLight: '#6faef0',
// 主色的深色变体,用于按下或强调状态。默认值:#3a7bc8。
themeColorDark: '#3a7bc8',
api: {
// HTTP API 网关。所有角色必填,不包含末尾业务路径。
baseURL: 'https://api.example.com',
// 补全后端返回的相对图片、视频和文件地址;返回值均为绝对地址时可留空。
resourceBaseURL: 'https://resource.example.com',
// 图片上传接口,multipart 文件字段名默认使用 image。
uploadImageURL: 'https://resource.example.com/system/uploadImage',
// 视频和通用文件上传接口,multipart 文件字段名默认使用 file。
uploadFileURL: 'https://resource.example.com/system/uploadFile',
// 请求签名密钥。所有角色必填,由服务提供方分配并妥善保管。
signKey: 'your-sign-key',
// 员工端站点 ID。createStaffIMSDK 必填;用户端可传空字符串。
siteId: 'your-site-id',
// 后端未返回头像时使用的 UI 占位地址;不需要远程占位图时可留空。
defaultAvatar: 'https://example.com/avatar.png',
// HTTP、上传和下载超时,单位毫秒。默认值:30000。
timeout: 30000,
},
websocket: {
// WebSocket 服务地址。留空时根据 api.baseURL 推导 ws:// 或 wss:// 地址。
url: 'wss://api.example.com',
// 角色平台标识。工厂会按 user/staff 自动覆盖,通常无需手动切换。
platform: 'user',
// 首次重连间隔,单位毫秒;后续异常重连采用退避策略。默认值:3000。
reconnectInterval: 3000,
// 心跳发送间隔,单位毫秒。默认值:3000。
heartbeatInterval: 3000,
// 单轮连接允许的最大重连次数。默认值:5。
maxReconnectAttempts: 5,
// 等待连接成功的最长时间,单位毫秒。默认值:30000。
connectTimeout: 30000,
// 是否启用协议跟踪。默认值:false。
traceEnabled: false,
// WebSocket 协议映射。
socketProtocol: {
// 握手与消息控制器。工厂会按角色自动覆盖为 User 或 Staff。
controller: 'User',
action: {
// 发送消息动作名。
send: 'send',
// 上报消息已读动作名。
msgRead: 'msgRead',
// 建立连接后的握手动作名。
onConnect: 'onConnect',
// 心跳动作名。
heartbeat: 'heartbeat',
},
// 收到这些信号时作为心跳处理,不进入普通消息列表。
heartbeatSignals: ['ping', 'pong', 'heartbeat'],
},
},
chat: {
// friend_type=4 且页面未传 phone 时使用的平台客服手机号。
platformServicePhone: 'your-service-phone',
},
}
必填规则:
| 场景 | 必填配置 |
|---|---|
| 所有角色 | api.baseURL、api.signKey |
| 员工端 | 额外需要 api.siteId |
| uni-app x | 首次创建实例时传入完整 IMSDKConfig |
| 图片发送 | api.uploadImageURL |
| 视频发送 | api.uploadFileURL |
| 相对资源地址 | 建议配置 api.resourceBaseURL |
用户端与员工端必须创建独立实例,不能通过修改配置把已有实例切换角色。修改
api.baseURL、api.signKey、api.siteId 或 websocket.url 时,SDK 会关闭旧
连接并清理当前角色会话,防止跨环境复用凭据。
接入示例
示例采用 DCloud 标准项目结构。最小接入用于快速跑通用户端聊天;完整接入适用于 需要同时管理用户端、员工端、未读状态、业务路由和退出登录的正式项目。
最小接入
最小接入直接打开插件通过 pages_init.json 自动注册的兼容页面,因此宿主不需要
创建聊天承载页。请先把上文完整配置保存为项目配置文件。
uni-app 最小接入
项目结构:
├── config/
│ └── im.js
├── pages/
│ └── index/index.vue
├── uni_modules/
│ └── jszn-im-sdk/
└── pages.json
config/im.js:
// 将上文“完整配置”原样放入此文件,示例值替换为实际服务配置。
export const imConfig = {
// themeColor、api、websocket、chat 等全部字段
}
pages/index/index.vue:
<template>
<view class="page">
<!-- 点击后完成 IM 鉴权并打开插件内置聊天页。 -->
<button :disabled="loading" @click="openChat">
{{ loading ? '正在进入...' : '打开在线客服' }}
</button>
</view>
</template>
<script>
import {
createUserIMSDK,
openIM,
} from '@/uni_modules/jszn-im-sdk'
import { imConfig } from '@/config/im'
// 放在模块作用域,确保页面重新显示时复用同一个用户端实例。
const sdk = createUserIMSDK(imConfig)
export default {
data() {
return { loading: false }
},
methods: {
async openChat() {
if (this.loading) return
this.loading = true
try {
// 登录资料应来自宿主现有账号系统,而不是让 SDK 负责账号登录。
const authorized = await sdk.ensureRoleAuthorized({
phone: 'current-user-phone',
username: '当前用户名称',
avatar: 'https://example.com/avatar.png',
})
if (!authorized) {
uni.showToast({ title: '用户资料不完整', icon: 'none' })
return
}
// 最小接入使用插件自动注册的兼容页面。
const opened = await openIM({
path: '/uni_modules/jszn-im-sdk/page/index',
role: 'user',
})
if (!opened) uni.showToast({ title: '聊天页面打开失败', icon: 'none' })
} catch (error) {
uni.showToast({
title: error?.errMsg || error?.message || '即时通讯服务暂不可用',
icon: 'none',
})
} finally {
this.loading = false
}
},
},
}
</script>
<style scoped>
.page {
padding: 32rpx;
}
</style>
pages.json 只需要注册宿主自己的首页;插件页面由导入插件时自动合并:
{
"pages": [
{
"path": "pages/index/index"
}
]
}
uni-app x 最小接入
项目结构:
├── config/
│ └── im.uts
├── pages/
│ └── index/index.uvue
├── uni_modules/
│ └── jszn-im-sdk/
└── pages.json
config/im.uts:
import type { IMSDKConfig } from '@/uni_modules/jszn-im-sdk'
// 将上文“完整配置”原样放入此文件,并替换全部占位值。
export const imConfig : IMSDKConfig = {
// themeColor、api、websocket、chat 等全部字段
}
pages/index/index.uvue:
<template>
<view class="page">
<button :disabled="loading" @click="openChat">
{{ loading ? '正在进入...' : '打开在线客服' }}
</button>
</view>
</template>
<script setup lang="uts">
import {
createUserIMSDK,
openIM
} from '@/uni_modules/jszn-im-sdk'
import type {
IMSDKLoginInfo,
IMSDKOpenOptions
} from '@/uni_modules/jszn-im-sdk'
import { imConfig } from '@/config/im.uts'
// 模块级实例不会进入响应式系统,适合保存 SDK 连接与订阅状态。
const sdk = createUserIMSDK(imConfig)
const loading = ref(false)
const openChat = async () : Promise<void> => {
if (loading.value) return
loading.value = true
try {
// 登录资料由宿主账号系统提供。
const loginInfo : IMSDKLoginInfo = {
phone: 'current-user-phone',
username: '当前用户名称',
avatar: 'https://example.com/avatar.png',
site: null
}
const authorized = await sdk.ensureRoleAuthorized(loginInfo)
if (!authorized) {
uni.showToast({ title: '用户资料不完整', icon: 'none' })
return
}
const options : IMSDKOpenOptions = {
path: '/uni_modules/jszn-im-sdk/page/index',
role: 'user'
}
const opened = await openIM(options)
if (!opened) uni.showToast({ title: '聊天页面打开失败', icon: 'none' })
} catch (error) {
const message = error instanceof Error ? error.message : '即时通讯服务暂不可用'
uni.showToast({ title: message, icon: 'none' })
} finally {
loading.value = false
}
}
</script>
<style scoped>
.page {
display: flex;
flex-direction: column;
padding: 32rpx;
}
</style>
pages.json:
{
"pages": [
{
"path": "pages/index/index"
}
]
}
完整接入
完整接入使用宿主自己的 /pages/im-chat/index 短路由,并把实例管理、鉴权、状态
订阅和退出清理集中在独立服务模块。业务页面只负责提供当前登录用户资料和聊天目标。
uni-app 完整接入
项目结构:
├── config/
│ └── im.js
├── services/
│ └── im.js
├── pages/
│ ├── index/index.vue
│ └── im-chat/index.vue
├── uni_modules/
│ └── jszn-im-sdk/
└── pages.json
config/im.js 使用上文完整配置。services/im.js:
import {
createStaffIMSDK,
createUserIMSDK,
openIM,
} from '@/uni_modules/jszn-im-sdk'
import { imConfig } from '@/config/im'
// 用户端与员工端必须是两个独立实例,分别维护 token、缓存和 WebSocket。
const instances = {
user: null,
staff: null,
}
function normalizeRole(role) {
return role === 'staff' ? 'staff' : 'user'
}
export function getIMInstance(role = 'user') {
const normalizedRole = normalizeRole(role)
if (instances[normalizedRole]) return instances[normalizedRole]
instances[normalizedRole] = normalizedRole === 'staff'
? createStaffIMSDK(imConfig)
: createUserIMSDK(imConfig)
return instances[normalizedRole]
}
export async function authorizeIM(role, account, forceRefresh = false) {
const normalizedRole = normalizeRole(role)
const loginInfo = {
phone: String(account.phone || '').trim(),
username: String(account.username || '').trim(),
avatar: String(account.avatar || '').trim(),
}
// 员工端必须传站点;用户端不发送 site。
if (normalizedRole === 'staff') loginInfo.site = String(account.site || '').trim()
return getIMInstance(normalizedRole).ensureRoleAuthorized(loginInfo, { forceRefresh })
}
export async function openIMPage(role, account, target = {}) {
const normalizedRole = normalizeRole(role)
const authorized = await authorizeIM(normalizedRole, account)
if (!authorized) return false
return openIM({
path: '/pages/im-chat/index',
role: normalizedRole,
session_id: target.session_id || '',
phone: target.phone || '',
friend_type: Number(target.friend_type || 0),
customerOnly: target.customerOnly === true,
})
}
export function subscribeIMState(role, listener) {
return getIMInstance(role).subscribeState(listener)
}
export function logoutIM(role) {
getIMInstance(role).logout()
}
pages/index/index.vue 展示业务层如何传入账号和聊天目标:
<template>
<view class="page">
<button :disabled="loading" @click="openSessionList">会话列表</button>
<button :disabled="loading" @click="openCustomerService">在线客服</button>
<button @click="logout">退出 IM</button>
<text class="unread">未读消息:{{ unread }}</text>
</view>
</template>
<script>
import {
logoutIM,
openIMPage,
subscribeIMState,
} from '@/services/im'
// 订阅句柄不放进响应式 data,避免函数或 SDK 状态被 Vue 代理。
let stateOff = null
export default {
data() {
return {
loading: false,
unread: 0,
// 实际项目从 Pinia、Vuex、globalData 或业务登录接口读取当前账号。
account: {
phone: 'current-user-phone',
username: '当前用户名称',
avatar: 'https://example.com/avatar.png',
},
}
},
onLoad() {
stateOff = subscribeIMState('user', (state) => {
this.unread = state.totalUnread
})
},
onUnload() {
stateOff?.()
stateOff = null
},
methods: {
async runOpen(target = {}) {
if (this.loading) return
this.loading = true
try {
const opened = await openIMPage('user', this.account, target)
if (!opened) uni.showToast({ title: '聊天页面打开失败', icon: 'none' })
} catch (error) {
uni.showToast({
title: error?.errMsg || error?.message || '即时通讯服务暂不可用',
icon: 'none',
})
} finally {
this.loading = false
}
},
openSessionList() {
return this.runOpen()
},
openCustomerService() {
return this.runOpen({
phone: 'service-phone',
friend_type: 4,
customerOnly: true,
})
},
logout() {
logoutIM('user')
this.unread = 0
},
},
}
</script>
<style scoped>
.page {
padding: 32rpx;
}
.unread {
margin-top: 24rpx;
}
</style>
pages/im-chat/index.vue 是通用聊天承载页:
<template>
<view class="page">
<!-- easycom 会自动选择插件中的 Vue 组件。 -->
<jszn-im-chat
:role="route.role"
:session-id="route.sessionId"
:phone="route.phone"
:friend-type="route.friendType"
:customer-only="route.customerOnly"
@error="handleError"
@unread-change="handleUnreadChange"
/>
</view>
</template>
<script>
import { getIMInstance } from '@/services/im'
export default {
data() {
return {
route: {
role: 'user',
sessionId: '',
phone: '',
friendType: 0,
customerOnly: false,
},
}
},
onLoad(query = {}) {
const role = query.role === 'staff' ? 'staff' : 'user'
// 创建或恢复对应角色实例,供 easycom 组件通过 getIMSDK 获取。
getIMInstance(role)
this.route = {
role,
sessionId: String(query.session_id || ''),
phone: String(query.phone || ''),
friendType: Number(query.friend_type || 0),
customerOnly: String(query.customeronly || '') === '1',
}
},
methods: {
handleError(payload) {
console.error('IM 页面错误', payload)
},
handleUnreadChange(count) {
// 可在这里同步 tabBar 角标或应用级未读状态。
console.log('IM 未读数', count)
},
},
}
</script>
<style>
page,
.page {
width: 100%;
height: 100%;
}
</style>
pages.json:
{
"pages": [
{
"path": "pages/index/index"
},
{
"path": "pages/im-chat/index",
"style": {
"navigationStyle": "custom",
"backgroundColor": "#f7f8fa"
}
}
]
}
员工端调用方式相同,只需传入 role='staff',并确保账号资料包含 site:
await openIMPage('staff', {
phone: 'current-staff-phone',
username: '当前员工名称',
avatar: 'https://example.com/staff-avatar.png',
site: 'current-site-id',
})
uni-app x 完整接入
项目结构:
├── config/
│ └── im.uts
├── services/
│ └── im.uts
├── pages/
│ ├── index/index.uvue
│ └── im-chat/index.uvue
├── uni_modules/
│ └── jszn-im-sdk/
└── pages.json
config/im.uts 使用上文完整的 IMSDKConfig。services/im.uts:
import {
createStaffIMSDK,
createUserIMSDK,
openIM
} from '@/uni_modules/jszn-im-sdk'
import type {
IMSDKInstance,
IMSDKLoginInfo,
IMSDKOpenOptions,
IMSDKRole,
IMSDKStateListener
} from '@/uni_modules/jszn-im-sdk'
import { imConfig } from '@/config/im.uts'
export type IMAccount = {
phone : string
username : string
avatar : string
site ?: string | null
}
export type IMTarget = {
session_id ?: string | null
phone ?: string | null
friend_type ?: number | null
customerOnly ?: boolean | null
}
let userSDK : IMSDKInstance | null = null
let staffSDK : IMSDKInstance | null = null
export function getIMInstance(role : IMSDKRole) : IMSDKInstance {
if (role == 'staff') {
if (staffSDK == null) staffSDK = createStaffIMSDK(imConfig)
return staffSDK as IMSDKInstance
}
if (userSDK == null) userSDK = createUserIMSDK(imConfig)
return userSDK as IMSDKInstance
}
export async function authorizeIM(
role : IMSDKRole,
account : IMAccount,
forceRefresh : boolean = false
) : Promise<boolean> {
const loginInfo : IMSDKLoginInfo = {
phone: account.phone.trim(),
username: account.username.trim(),
avatar: account.avatar.trim(),
site: role == 'staff' ? (account.site ?? '') : null
}
return await getIMInstance(role).ensureRoleAuthorized(loginInfo, { forceRefresh })
}
export async function openIMPage(
role : IMSDKRole,
account : IMAccount,
target : IMTarget | null = null
) : Promise<boolean> {
const authorized = await authorizeIM(role, account)
if (!authorized) return false
const value = target ?? ({} as IMTarget)
const options : IMSDKOpenOptions = {
path: '/pages/im-chat/index',
role,
session_id: value.session_id ?? '',
phone: value.phone ?? '',
friend_type: value.friend_type ?? 0,
customerOnly: value.customerOnly == true
}
return await openIM(options)
}
export function subscribeIMState(
role : IMSDKRole,
listener : IMSDKStateListener
) : () => void {
return getIMInstance(role).onStateChange(listener)
}
export function logoutIM(role : IMSDKRole) : void {
getIMInstance(role).logout()
}
pages/index/index.uvue:
<template>
<view class="page">
<button :disabled="loading" @click="openSessionList">会话列表</button>
<button :disabled="loading" @click="openCustomerService">在线客服</button>
<button @click="logout">退出 IM</button>
<text class="unread">未读消息:{{ unread }}</text>
</view>
</template>
<script setup lang="uts">
import type { IMSDKState } from '@/uni_modules/jszn-im-sdk'
import {
logoutIM,
openIMPage,
subscribeIMState
} from '@/services/im.uts'
import type { IMAccount, IMTarget } from '@/services/im.uts'
const loading = ref(false)
const unread = ref(0)
let stateOff : (() => void) | null = null
// 实际项目从应用级状态或业务登录接口读取当前账号。
const account : IMAccount = {
phone: 'current-user-phone',
username: '当前用户名称',
avatar: 'https://example.com/avatar.png',
site: null
}
const runOpen = async (target : IMTarget | null = null) : Promise<void> => {
if (loading.value) return
loading.value = true
try {
const opened = await openIMPage('user', account, target)
if (!opened) uni.showToast({ title: '聊天页面打开失败', icon: 'none' })
} catch (error) {
const message = error instanceof Error ? error.message : '即时通讯服务暂不可用'
uni.showToast({ title: message, icon: 'none' })
} finally {
loading.value = false
}
}
const openSessionList = async () : Promise<void> => {
await runOpen()
}
const openCustomerService = async () : Promise<void> => {
const target : IMTarget = {
phone: 'service-phone',
friend_type: 4,
customerOnly: true
}
await runOpen(target)
}
const logout = () : void => {
logoutIM('user')
unread.value = 0
}
onLoad(() => {
stateOff = subscribeIMState('user', (state : IMSDKState) : void => {
unread.value = state.totalUnread
})
})
onUnload(() => {
stateOff?.()
stateOff = null
})
</script>
<style scoped>
.page {
display: flex;
flex-direction: column;
padding: 32rpx;
}
.unread {
margin-top: 24rpx;
}
</style>
pages/im-chat/index.uvue:
<template>
<view class="page">
<!-- easycom 自动选择插件中的 UVue 组件。 -->
<jszn-im-chat
:role="role"
:session-id="sessionId"
:phone="phone"
:friend-type="friendType"
:customer-only="customerOnly"
@error="handleError"
@unread-change="handleUnreadChange"
/>
</view>
</template>
<script setup lang="uts">
import type { IMSDKRole } from '@/uni_modules/jszn-im-sdk'
import { getIMInstance } from '@/services/im.uts'
const role = ref<IMSDKRole>('user')
const sessionId = ref('')
const phone = ref('')
const friendType = ref(0)
const customerOnly = ref(false)
const stringValue = (value : any | null) : string => {
return value == null ? '' : value.toString().trim()
}
const handleError = (payload : UTSJSONObject) : void => {
console.error('IM 页面错误', payload)
}
const handleUnreadChange = (count : number) : void => {
// 可在这里同步 tabBar 角标或应用级未读状态。
console.log('IM 未读数', count)
}
onLoad((options : OnLoadOptions) => {
role.value = stringValue(options['role']) == 'staff' ? 'staff' : 'user'
getIMInstance(role.value)
sessionId.value = stringValue(options['session_id'])
phone.value = stringValue(options['phone'])
const parsedFriendType = parseFloat(stringValue(options['friend_type']))
friendType.value = Number.isNaN(parsedFriendType) ? 0 : parsedFriendType
customerOnly.value = stringValue(options['customeronly']) == '1'
})
</script>
<style scoped>
.page {
width: 100%;
height: 100%;
flex: 1;
}
</style>
pages.json:
{
"pages": [
{
"path": "pages/index/index"
},
{
"path": "pages/im-chat/index",
"style": {
"navigationStyle": "custom",
"backgroundColor": "#f7f8fa"
}
}
]
}
员工端调用时传入 staff 和包含 site 的 IMAccount,其余流程不变。
ensureRoleAuthorized 会优先恢复当前角色的有效本地会话;资料不完整时返回
false,网络或服务端鉴权失败时抛出 SDK 错误。login 始终主动鉴权。
聊天组件
完成对应角色鉴权后,页面可直接使用 easycom 主组件,无需手写组件导入路径:
<jszn-im-chat role="user" @unread-change="handleUnreadChange" />
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
role |
string |
user |
绑定由工厂创建的 user 或 staff 实例 |
session-id |
string |
空 | 直接打开指定服务端会话 |
phone |
string |
空 | 直接打开目标联系人 |
friend-type |
number |
0 |
联系人类型 |
customer-only |
boolean |
false |
直接进入平台客服模式 |
title |
string |
空 | 自定义页面标题 |
enable-location |
boolean |
false |
显示位置选择入口;宿主需先关联 uniCloud 服务空间并安装 uni-map-common |
upload-image-options |
object \| null |
null |
图片上传选项,结构见“请求模块” |
upload-file-options |
object \| null |
null |
视频上传选项,结构见“请求模块” |
未传 session-id、phone 且 customer-only=false 时显示会话列表;
customer-only=true 时直接进入平台客服聊天视图。
事件
error:发生可展示错误,参数包含已归一化消息和原始错误。session-change:进入会话,参数包含session_id和会话记录。unread-change:总未读数变化,参数为未读数量。
公开方法
refresh():刷新当前会话列表或消息列表。openSession(sessionRecord):打开指定会话记录。backToSessions():返回并刷新会话列表。
插件还包含 jszn-im-session-list、jszn-im-message-list、
jszn-im-chat-input 和 jszn-im-emoji 底层 easycom 组件。推荐业务页面使用
jszn-im-chat 统一处理鉴权实例、会话、消息、已读和未读联动。
页面与导航
推荐由宿主注册 /pages/chat/index 短路由并承载 <jszn-im-chat />。插件同时通过
pages_init.json 注册 /uni_modules/jszn-im-sdk/page/index 兼容页面。
import { buildIMRoute, openIM } from '@/uni_modules/jszn-im-sdk'
const url = buildIMRoute({
role: 'user',
session_id: 'session-id',
})
const opened = await openIM({
path: '/pages/chat/index',
role: 'user',
phone: 'target-phone',
friend_type: 1,
})
导航参数包括 path、role、session_id、phone、friend_type 和
customerOnly。uni-app 及 uni-app x 非 App-iOS 平台的 openIM 使用
uni.navigateTo,成功返回 true,失败返回 false。uni-app x App-iOS 的 UTS
插件原生层不能调用页面路由 API,因此 openIM 返回 false;宿主应调用
buildIMRoute 后在页面层自行导航。宿主未注册默认短路由时必须传入 path。
根导出
| 导出 | 说明 |
|---|---|
createIMSDK(role, config) |
创建指定角色实例 |
createUserIMSDK(config) |
创建用户端实例 |
createStaffIMSDK(config) |
创建员工端实例 |
getIMSDK(role) |
获取最近创建的对应角色实例,未创建时返回 null |
MESSAGE_TYPE |
文本、图片、表情、位置、系统、卡片和视频消息类型 |
WS_EVENT_TYPE |
系统、消息和已读 WebSocket 事件类型 |
buildIMRoute(options) |
构建宿主聊天页地址 |
openIM(options) |
打开聊天页并返回 Promise<boolean> |
IMSDK_ERROR_CODE |
稳定错误码映射 |
IMSDK_ERROR_MESSAGE |
默认错误消息映射 |
uni-app 额外导出 IMSDKError;uni-app x 导出 IMSDKFail、IMSDKErrors、
UniErrorSubject、imError 及全部公开 UTS 类型。
实例 API
实例属性:
role:固定角色,值为user或staff。state:包含totalUnread、authStatus、defaultAvatar、themeColor。request、auth、session、message、websocket:子模块 API。
实例方法:
configure(configPatch):合并更新配置并返回当前实例。setRole(options?):重新应用固定角色;reconnect=true时重连,不改变角色。ensureRoleAuthorized(loginInfo?, options?):恢复会话或重新鉴权。login(loginInfo):主动鉴权并返回公开用户对象。logout():关闭连接并清理当前角色本地会话。connectWebSocket(userId, phone):发起连接。getUserInfo()、getToken()、getConfig()、getCore()。onStateChange(listener):立即回调当前状态,持续通知后续变化,并返回取消订阅函数。subscribeState(listener):与onStateChange等价;uni-app x 应使用onStateChange, 以符合原生端持续回调的保活规则。
uni-app x 应优先使用本节列出的实例根方法;根方法与对应子模块共享同一状态和实现,
是 App-iOS 原生代理的稳定调用形式。传统 uni-app 可以继续使用 sdk.auth、
sdk.session、sdk.message 和 sdk.websocket。
themeColor 会同步进入实例状态,并用于本人消息气泡与气泡尾、发送按钮、附件
抽屉取消文字、重试按钮和加载指示器。已读、未读、角色等业务语义色不跟随主题色
变化。themeColorLight 与 themeColorDark 保留在配置中,当前组件不自动消费。
请求模块
sdk.request 提供 request、get、post、put、delete、upload 和
download。普通请求与下载会自动添加签名、当前角色鉴权和已配置的 site-id。
上传使用 upload({ url, filePath, name?, formData?, headers?, timeout? })。name 是
multipart 文件字段名,formData 是附加表单字段,headers 是上传接口专用请求头,
timeout 单位为毫秒。未传选项时不附加自定义请求头。
鉴权模块
sdk.auth 提供:
login(loginInfo)getUserInfo(phone, friendType)getUserInfoBySessionId(sessionId)
对应根方法为 login、getRemoteUserInfo 和 getRemoteUserInfoBySession。
会话模块
sdk.session 提供:
getChatConfig()getSessionList(page?, limit?, options?)clearUnread(sessionRecordId)closeSession({ session_id })getTotalUnreadCount()syncUnreadCount(options?)getCachedTotalUnread()onUnreadChange(listener, options?):注册监听并返回取消订阅函数。
上述方法在实例根级同名提供。
员工端优先使用服务端总未读接口;用户端按 max(0, count - read_count) 聚合会话
未读。第 1 页替换会话缓存,后续页按会话 ID 合并。
消息模块
sdk.message 提供:
getMessages(query?)sendTextMessage(sessionId, content, options?)sendImageMessage(sessionId, imageURL, options?)sendVideoMessage(sessionId, videoURL, options?)sendLocationMessage(sessionId, location, options?)uploadImage(filePath, options?)uploadVideo(filePath, options?)readMessage(sessionId, options?)onMessage(listener):注册监听并返回取消订阅函数。
上述方法在实例根级同名提供。
uploadImage 和 uploadVideo 的 options 使用“请求模块”定义的上传选项,不需要传
url 和 filePath;还可传 fileSize(字节),用于跨平台上传前大小校验。
const imageURL = await sdk.uploadImage(filePath, {
formData: { scene: 'chat' },
})
图片上限为 5MB,视频上限为 50MB。getMessages 优先按 session_id 查询;平台
客服可使用 customerOnly=true 和 phone。尚未建立会话时,发送选项必须提供有效
receive_id 和 friend_type。消息字段保持后端 snake_case,例如 session_id、
send_id、receive_id、friend_type、msg_type、is_read、create_time。
WebSocket 模块
sdk.websocket 提供:
connect(force?, userId?, phone?)close()waitForConnection(timeout?)isConnected()onMessage(listener):注册监听并返回取消订阅函数。
需要等待连接结果时,实例根级提供 openWebSocket(force?, userId?, phone?)。鉴权成功后
SDK 仍会自动连接,无需重复调用。
SDK 在鉴权成功后自动连接,异常断线按配置退避重连,并发送握手和心跳。连接层只
使用真实、非零服务端消息 ID 去重;缺少有效 ID 时生成本地 ID。type=1 表示可
展示消息,type=2 表示已读回执;msg_type 表示消息内容类型。当前会话收到消息
后会上报已读,自己的消息回显不会增加未读数。
错误契约
uni-app 抛出 IMSDKError,uni-app x 抛出实现 IMSDKFail 的错误。两端都提供:
errSubject:固定为jszn-im-sdk。errCode:稳定错误码。errMsg:适合展示或记录的错误消息。
| 错误码 | 常量 | 含义 |
|---|---|---|
9401001 |
CONFIG_INVALID |
配置或调用参数不完整 |
9401002 |
LOGIN_INVALID |
登录资料或鉴权状态无效 |
9401003 |
NETWORK_ERROR |
网络、上传或下载失败 |
9401004 |
RESPONSE_ERROR |
HTTP 或业务响应不符合契约 |
9401005 |
SOCKET_ERROR |
WebSocket 未连接或发送失败 |
平台支持
插件支持 uni-app 与 uni-app x 官方支持的客户端平台:
- uni-app:Vue 2、Vue 3、Web、Android、iOS、HarmonyOS、小程序和快应用。
- uni-app x:Web、Android、iOS、HarmonyOS 和微信小程序。
小程序支持微信、支付宝、抖音、百度、快手、京东、QQ、飞书、小红书及鸿蒙元 服务;快应用支持华为与联盟快应用。
安全与隐私
- 登录资料、鉴权信息、会话和消息只发送到宿主配置的 API 与 WebSocket 服务。
- 图片、视频和位置仅在用户主动选择对应发送功能时上传到宿主配置的服务。
- 宿主必须为相机、相册、定位、网络域名和 WebSocket 域名配置目标平台权限、隐私 声明与白名单。

收藏人数:
下载插件并导入HBuilderX
下载示例项目ZIP
赞赏(0)
下载 62
赞赏 0
下载 12449454
赞赏 1935
赞赏
京公网安备:11010802035340号