1.网易云信SDK
1)先申请注册云信账号
2)下载SDK
npm install @nertc/wechat-miniprogram --save
3)配置manifest.json
{
"mp-weixin": {
"permission": {
"scope.userLocation": {
"desc": "您的位置信息将用于小程序位置接口的效果展示"
}
},
"requiredBackgroundModes": ["audio"],
"plugins": {
"live-player-plugin": {
"version": "1.0.0",
"provider": "wx2b03c6e691cd7370"
}
}
}
}
4)初始化SDK
// utils/nim.js
import SDK from '@/utils/nim-weapp-sdk'
class NIMService {
constructor() {
this.nim = null
}
// 初始化SDK
init(options) {
this.nim = SDK.NIM.getInstance({
appKey: 'your_app_key',
account: options.account,
token: options.token,
onconnect: this.onConnect.bind(this),
onwillreconnect: this.onWillReconnect.bind(this),
ondisconnect: this.onDisconnect.bind(this),
onerror: this.onError.bind(this),
onroamingmsgs: this.onRoamingMsgs.bind(this),
onofflinemsgs: this.onOfflineMsgs.bind(this),
onmsg: this.onMsg.bind(this),
onsessions: this.onSessions.bind(this),
onupdatesession: this.onUpdateSession.bind(this)
})
}
// 连接成功
onConnect() {
console.log('连接成功')
uni.$emit('nim-connect-success')
}
// 收到消息
onMsg(msg) {
console.log('收到消息:', msg)
uni.$emit('nim-receive-msg', msg)
}
// 发送消息
sendTextMsg(sessionId, text) {
return this.nim.sendText({
scene: 'p2p',
to: sessionId,
text: text
})
}
// 发送图片
sendImageMsg(sessionId, imagePath) {
return this.nim.sendFile({
scene: 'p2p',
to: sessionId,
type: 'image',
file: imagePath
})
}
}
export default new NIMService()
5)具体代码
<!-- components/chat/index.vue -->
<template>
<view class="chat-container">
<!-- 消息列表 -->
<scroll-view
class="message-list"
scroll-y
:scroll-top="scrollTop"
@scrolltoupper="loadMore"
>
<view
v-for="(msg, index) in messageList"
:key="index"
class="message-item"
:class="{'self': msg.from === currentUser}"
>
<view class="message-content">
<text>{{ msg.text }}</text>
</view>
<text class="message-time">{{ formatTime(msg.time) }}</text>
</view>
</scroll-view>
<!-- 输入框 -->
<view class="input-area">
<input
v-model="inputText"
placeholder="输入消息..."
@confirm="sendMessage"
class="text-input"
/>
<button @click="sendMessage" class="send-btn">发送</button>
</view>
</view>
</template>
<script>
import nimService from '@/utils/nim.js'
export default {
data() {
return {
messageList: [],
inputText: '',
scrollTop: 0,
currentUser: '',
sessionId: ''
}
},
onLoad(options) {
this.sessionId = options.sessionId
this.currentUser = uni.getStorageSync('userInfo').account
this.initChat()
},
methods: {
// 初始化聊天
initChat() {
// 监听消息
uni.$on('nim-receive-msg', this.handleReceiveMsg)
// 获取历史消息
this.loadHistoryMessages()
},
// 发送消息
sendMessage() {
if (!this.inputText.trim()) return
nimService.sendTextMsg(this.sessionId, this.inputText)
.then(() => {
this.inputText = ''
this.scrollToBottom()
})
.catch(err => {
console.error('发送失败:', err)
})
},
// 处理接收消息
handleReceiveMsg(msg) {
if (msg.scene === 'p2p' && msg.from === this.sessionId) {
this.messageList.push({
from: msg.from,
text: msg.text,
time: msg.time
})
this.scrollToBottom()
}
},
// 滚动到底部
scrollToBottom() {
this.$nextTick(() => {
this.scrollTop = this.messageList.length * 100
})
}
}
}
</script>
2.腾讯云IM
1)申请注册腾讯云申请权限
2)下载SDK
npm install tim-wx-sdk --save
npm install tim-upload-plugin --save
3)配置 manifest.json,添加腾讯云域名
{
"mp-weixin": {
"appid": "你的小程序appid",
"setting": {
"urlCheck": false
},
"networkTimeout": {
"request": 10000,
"connectSocket": 10000
},
"domain": {
"requestDomain": ["https://webim.tim.qq.com", "https://yun.tim.qq.com"],
"socketDomain": ["wss://wss.im.qcloud.com"]
}
}
}
4)初始化SDK
import TIM from 'tim-wx-sdk';
import TIMUploadPlugin from 'tim-upload-plugin';
let tim = null;
let isInitialized = false;
// 初始化IM
export function initLiveIM(SDKAppID) {
if (isInitialized) return tim;
// 创建实例
tim = TIM.create({ SDKAppID });
// 注册上传插件
tim.registerPlugin({ 'tim-upload-plugin': TIMUploadPlugin });
// 监听核心事件
tim.on(TIM.EVENT.SDK_READY, handleSDKReady);
tim.on(TIM.EVENT.MESSAGE_RECEIVED, handleMessageReceived);
tim.on(TIM.EVENT.ERROR, handleError);
isInitialized = true;
return tim;
}
// 登录IM
export function loginIM(userID, userSig) {
return tim.login({ userID, userSig });
}
// 加入直播群
export function joinLiveRoom(roomID) {
return tim.joinGroup({
groupID: roomID,
type: TIM.TYPES.GRP_AVCHATROOM // 直播群类型
});
}
// 发送文本消息(弹幕)
export function sendDanmaku(roomID, text) {
const message = tim.createTextMessage({
to: roomID,
conversationType: TIM.TYPES.CONV_AVCHATROOM,
payload: { text }
});
return tim.sendMessage(message);
}
// 发送礼物消息(自定义消息)
export function sendGift(roomID, giftInfo) {
const message = tim.createCustomMessage({
to: roomID,
conversationType: TIM.TYPES.CONV_AVCHATROOM,
payload: {
data: JSON.stringify(giftInfo),
description: 'gift', // 用于区分消息类型
extension: ''
}
});
return tim.sendMessage(message);
}
// 事件处理函数
function handleSDKReady() {
console.log('IM SDK 就绪');
// 可以在这里获取历史消息
}
function handleMessageReceived(event) {
const { messageList } = event;
messageList.forEach(msg => {
// 区分消息类型并触发对应事件
if (msg.type === TIM.TYPES.MSG_TEXT) {
// 普通文本消息(弹幕)
wx.$emit('liveDanmaku', {
user: msg.from,
content: msg.payload.text,
time: msg.time
});
} else if (msg.type === TIM.TYPES.MSG_CUSTOM && msg.payload.description === 'gift') {
// 礼物消息
wx.$emit('liveGift', {
user: msg.from,
gift: JSON.parse(msg.payload.data),
time: msg.time
});
}
});
}
function handleError(error) {
console.error('IM 错误:', error);
if (error.data.type === TIM.TYPES.ERROR_TOKEN_EXPIRED) {
// 处理token过期,需要重新登录
wx.$emit('imTokenExpired');
}
}
// 退出登录
export function logoutIM() {
return tim.logout();
}
export default {
initLiveIM,
loginIM,
joinLiveRoom,
sendDanmaku,
sendGift,
logoutIM
};
5)具体代码
<!-- 直播播放器 -->
<live-player
src="{{ liveUrl }}"
mode="live"
autoplay
class="live-player"
></live-player>
<!-- 弹幕显示区域 -->
<view class="danmaku-container">
<view
class="danmaku"
wx:for="{{ danmakus }}"
wx:key="index"
style="top: {{ item.top }}px; left: {{ item.left }}px;"
>
<text class="danmaku-user">{{ item.user }}:</text>
<text class="danmaku-content">{{ item.content }}</text>
</view>
</view>
<!-- 礼物特效区域 -->
<view class="gift-container" wx:if="{{ currentGift }}">
<image src="{{ currentGift.gift.icon }}" class="gift-icon"></image>
<text class="gift-text">{{ currentGift.user }} 送出了 {{ currentGift.gift.name }}</text>
</view>
<!-- 聊天输入区域 -->
<view class="chat-area">
<input
type="text"
value="{{ inputText }}"
placeholder="发送弹幕..."
bindinput="onInputChange"
bindconfirm="sendDanmaku"
class="chat-input"
/>
<button bindtap="sendDanmaku" class="send-btn">发送</button>
</view>
<!-- 礼物选择区 -->
<view class="gift-selector">
<view
class="gift-item"
wx:for="{{ gifts }}"
wx:key="id"
bindtap="sendGift"
data-gift="{{ item }}"
>
<image src="{{ item.icon }}" class="gift-img"></image>
<text class="gift-name">{{ item.name }}</text>
</view>
</view>