1.根据网易云信这个快速集成步骤先集成(这个链接内容貌似已经更新,和我那时候的略有不同)快速集成 uni-app 源码(Vue 3) - IM 即时通讯(增强版)
注意:初始化IM步骤,一定要放在app.vue的onLaunch。
2.当你想在自己小程序上登录账号时同步登录初始化IM,可以参考以下代码:
注意:vue3的项目,最好将app.vue写成vue2的,因为我也不知道vue3写法该如何处理。
在methods里定义初始化方法同时监听'sessions', 'updateSession'这两个事件,用于处理显示未读消息数,sessions事件一般是在IM登录时触发一次,是一个数组,里面包含聊天列表的聊天对象,每个对象属性里有一个unread属性,还有一个id属性,当我们存储时可以以键值对的方式存储(id:unread)。
'updateSession事件:每次收到信息和点击聊天对象时触发,事件对象也有id属性和unread属性,点击聊天对象触发时,unread为0,然后我们可以根据事件对象的id和unread 配合sessions事件进行存储和判断,进而可以显示准确的消息数。
在methods里定义
initNim(opts) {
//保存登录信息
uni.setStorage({
key: 'IMkey',
data: opts,
success() {
console.log('保存登录信息success');
}
});
const nim = uni.$UIKitNIM = new NimKitCore({
initOptions: {
"appkey": opts.app_key, //您在云信控制台注册的appkey
"account": opts.accid, //云信控制台上的account
"token": opts.token, //云信控制台的账号token
"lbsUrls": [
"https://lbs.netease.im/lbs/webconf.jsp"
],
"linkUrl": "weblink.netease.im",
"needReconnect": true,
// "reconnectionAttempts": 5,
debugLevel: 'debug'
},
platform: 'UniApp',
})
uni.$UIKitStore = new RootStore(nim, {
addFriendNeedVerify: false,
teamBeInviteMode: 'noVerify',
teamJoinMode: 'noVerify',
teamUpdateExtMode: 'all',
teamUpdateTeamMode: 'all',
teamInviteMode: 'all',
sendMsgBefore: async (options, type) => {
const pushContent = getMsgContentTipByType({
body: options.body,
type
})
// 如果是 at 消息,需要走离线强推
const {
forcePushIDsList,
needForcePush
} = options.ext?.yxAitMsg
// @ts-ignore
?
store.msgStore._formatExtAitToPushInfo(options.ext?.yxAitMsg, options.body) : {
forcePushIDsList: '[]',
needForcePush: false
}
console.log('forcePushIDsList: ', forcePushIDsList)
// 不同产商的推送消息体
const {
scene,
to
} = options
const pushPayload = JSON.stringify({
// oppo
oppoField: {
"click_action_type": 4, // 参考 oppo 官网
"click_action_activity": '', // 各端不一样 TODO
"action_parameters": {
"sessionId": scene,
"sessionType": to
} // 自定义
},
// vivo
vivoField: {
"pushMode": 0 //推送模式 0:正式推送;1:测试推送,不填默认为0
},
// huawei
hwField: {
click_action: {
'type': 1,
'action': '' // 各端不一样 TODO
},
androidConfig: {
'category': 'IM',
'data': JSON.stringify({
'sessionId': to,
'sessionType': scene
})
}
},
// 通用
sessionId: to,
sessionType: scene
})
const pushInfo = {
needPush: true,
needPushBadge: true,
pushPayload: '{}',
pushContent,
needForcePush,
forcePushIDsList,
forcePushContent: pushContent,
}
return {
...options,
pushInfo
}
},
})
const eventList = [
/* 'syncOfflinemsgs',
'msg',
'syncRoamingmsgs ', */
'sessions',
'updateSession'
]
nim.connect()
//显示未读消息红点
let map = new Map();
let countMsg = 0
eventList.forEach((key) => {
nim.on(key, (res) => {
/* console.log(`收到 ${key} 事件:`, res ? JSON.parse(JSON.stringify(res)) : res); */
if (key == 'sessions') {
console.log(`收到 ${key} 事件:`, res ? JSON.parse(JSON.stringify(res)) : res);
for (let i = 0; i < res.length; i++) {
map.set(res[i].id, res[i].unread);
}
console.log('map', map)
for (let key of map.keys()) {
console.log(key + ' => ' + map.get(key))
countMsg += map.get(key)
console.log('countMsg' + countMsg)
}
if (countMsg > 0) {
uni.setTabBarBadge({
index: 1,
text: String(countMsg)
})
// wx.showToast({
// title: '您有新信息',
// icon: 'success', // 可以是 'success', 'loading' 等,或者是自定义图标的路径
// duration: 2000 // 提示的延迟时间,单位是毫秒,默认是1500
// })
} else {
uni.removeTabBarBadge({
index: 1
})
}
} else if (key == 'updateSession') {
console.log(`收到 ${key} 事件:`, res ? JSON.parse(JSON.stringify(res)) : res);
console.log('countMsgupdateSession', countMsg, res.id)
//原本不在聊天列表的人发信息过来
if (!map.has(res.id)) {
map.set(res.id, res.unread)
countMsg += map.get(res.id)
}
for (let key of map.keys()) {
console.log('key', key)
//在聊天列表里的人发消息过来
if (res.id == key) {
//点击聊天对象,消除一定数字
if (res.unread == 0) {
console.log('updateSessionjinlaile2')
countMsg -= map.get(key)
map.set(key, 0)
} else {
//未点击聊天对象时,发来的消息
console.log('updateSessionjinlaile')
countMsg -= map.get(key)
map.set(key, res.unread)
countMsg += map.get(key)
}
}
}
if (countMsg > 0) {
uni.setTabBarBadge({
index: 1,
text: String(countMsg)
})
// wx.showToast({
// title: '您有新信息',
// icon: 'success', // 可以是 'success', 'loading' 等,或者是自定义图标的路径
// duration: 2000 // 提示的延迟时间,单位是毫秒,默认是1500
// })
} else {
uni.removeTabBarBadge({
index: 1
})
}
}
});
})
}
在App.vue的onLaunch方法里初始化
onLaunch: function() {
console.log('App Launch')
//im
const imOptions = uni.getStorageSync('IMkey');
if (imOptions) {
this.initNim(imOptions)
}
/* else {
//需要登录IM
uni.redirectTo({
url: '/pages/common/login/login'
})
}
*/
},
在登录的方法添加IM初始化方法
//im
const result = await initIm({})//后端接口,返回的初始化必填信息
console.log('yzmdengluim', result)
const app = getApp()
app.initNim({ app_key: result.data.data.app_key, accid: result.data.data.accid, token: result.data.data.token })
在退出的方法添加销毁IM,这里要注意,要用reLaunch,因为在重新登录后,这个聊天页面渲染之前需要重新执行初始化和和链接,所以需要relaunch。不然旧帐户退出登录后,新用户登录,聊天列表界面就会显示网络不可用,需要重新刷新小程序
//im
uni.removeStorage({
key: 'IMkey'
})
await uni.$UIKitNIM?.disconnect()
uni.$UIKitStore?.destroy()
console.log('imtuichu')
uni.reLaunch({
url: "/pages/common/login/login"
})
我们把聊天列表界面放在自己的小程序tabbar上,还需要在网易云信的conversation界面做个判断,不然还没登陆,加载里面的东西会报错。
<template>
<ConversationList v-if="flag" />
</template>
<script lang="ts" setup>
import ConversationList from './conversation-list/index.vue'
import { trackInit } from '../../utils/reporter'
import { onShow, onLoad } from '@dcloudio/uni-app';
import { ref } from 'vue';
/* trackInit('ConversationUIKit') */
//用于IM未登录时点击聊天页面的处理
let key = ref('')
let flag = ref(false)
onLoad(() => {
key.value = uni.getStorageSync('IMkey')
})
onShow(() => {
key.value = uni.getStorageSync('IMkey')
flag.value = (key.value !== '')
if (flag.value) {
trackInit('ConversationUIKit')
} else {
wx.showModal({
title: '提示',
content: '账号尚未登录,请先登录账号',
success: res => {
if (res.confirm) {
uni.navigateTo({
url: '/pages/common/login/login'
})
} else if (res.cancel) {
uni.reLaunch({
url: '/pages/tabbar/index/index'
})
}
}
})
}
})
</script>
<style lang="scss">
@import "../styles/common.scss";
page {
height: 100vh;
overflow: hidden;
}
</style>
注意:如果你想把搜索好友添加好友的不走变成搜索好友直接去聊天,可以直接找到该页面,直接把添加按钮UI换去聊天按钮UI就可以
注意:如果你想直接在别的界面直接点击某按钮就跳转到和别人聊天的界面,可以这样,首先你得拿到你想要聊天的对象的accid(每个你们的小程序用户都会绑着IM用户都会有一个accid,这个你们后端会处理),然后调用下面这段代码
//im talk
const talk = async (data) => {
console.log('data++++++++++', data)
const res = await straightTalk({ "schedule_id": data.id })
console.log(res)
if (res.data.msg !== 'ok') {
uni.showToast({
icon: 'error',
title: res.data.msg,
duration: 1500
})
} else {
const sessionId = res.data.data?.other?.accid //u4867
if (sessionId) {
try {
await uni.$UIKitStore.uiStore.selectSession('p2p-' + sessionId)
uni.navigateTo({
url: '/pages/NEUIKit/src/pages/Chat/index'
})
} catch (error) {
uni.showToast({
title: t('gotoChatFailText'),
icon: 'error'
})
}
}
}
}
注意:在网易云信控制台打开漫游消息,不然聊天列表是没有聊过的对象的,没有后端也可在控制台创建应用,在应用配置里用测试子账号进行测试,也可用安全通进行信息拦截