文章概述
整个 IM 我的项目的关键点来了, 本文将讨论一下聊天音讯的实现,如何收发音讯并且实现聊天音讯的 UI 显示。
聊天音讯项的实现
1. 收发聊天音讯
1.1 接管聊天音讯
接管聊天音讯显得很简略,在之前的会话列表实现中曾经做过一次,这次咱们代码其实差不多,惟一不一样的是承受后咱们须要做筛选确定须要回显到聊天音讯界面的我的项目。
绑定音讯事件局部的代码如下:
// 这里抉择在 onLoad 绑定,确定不会漏接数据
async onLoad (params) {
// 做好一个变量确定我当初在和谁聊天
this.receiver = params.receiver
// 监听新的音讯
this.$txim.$on('onRecvC2CTextMessage', this.onRecvMessageHanlder)
this.$txim.$on('onRecvC2CCustomMessage', this.onRecvMessageHanlder)
this.$txim.$on('onRecvGroupTextMessage', this.onRecvMessageHanlder)
this.$txim.$on('onRecvGroupCustomMessage', this.onRecvMessageHanlder)
this.$txim.$on('onRecvNewMessage', this.onRecvMessageHanlder)
}
接管到音讯的回调事件处理如下
// 获取音讯
async onRecvMessageHanlder ({data}) {let V2TIMMessageManager = this.$txim.getMessageManager()
let senderId = data?.sender?.userID || data?.sender
if (senderId == this.receiver) {this.HistoryMessageToChatLog([data])
this.$txim.markC2CMessageAsRead(senderId)
await this.$nextTick()
this.$refs.chatLayout.scrollToBottom()}
},
1.2 发送聊天音讯
发送聊天音讯实际上 demo 曾经有所封装了,对于文本音讯而言发送的代码如下:
// 发送文字 这里须要防抖解决,反正屡次点击发送按钮从而呈现问题
sendText: _.debounce(async function (text) {let V2TIMMessageManager = this.$txim.getMessageManager()
// 创立音讯构造体
let v2TIMMessage = V2TIMMessageManager.createTextMessage(text)
try {
// 发送音讯
let ret = await V2TIMMessageManager.sendMessage(v2TIMMessage, this.receiver)
// 更新聊天音讯
this.HistoryMessageToChatLog([ret.data])
} catch (e) {this.$utils.toast('发送失败')
}
await this.$nextTick()
// 放弃滚动到底部
this.$refs.chatLayout.scrollToBottom()}, 500, {leading: true, trailing: false}),
下面演示的是文本音讯,而对于其余音讯而言,只是创立的音讯构造体不一样,各种音讯的构造体如下
// 文本
let v2TIMMessage = V2TIMMessageManager.createTextMessage(text)
// 大表情
let v2TIMMessage = V2TIMMessageManager.createFaceMessage(0, { faceUrl: url})
// 图片
let v2TIMMessage = V2TIMMessageManager.createImageMessage(filePath)
// 视频
let v2TIMMessage = V2TIMMessageManager.createVideoMessage(filePath, 'mp4', timeLen, snapshotPath)
// 语音
let v2TIMMessage = V2TIMMessageManager.createSoundMessage(filePath, timeLen)
// 定位
let v2TIMMessage = V2TIMMessageManager.createLocationMessage(address, longitude, latitude)
2. 聊天音讯格式化
为了做好组件之间的解耦合,咱们须要将腾讯音讯的构造体进行格式化,映射为咱们音讯组件中能辨认的构造体,映射局部的代码实际上 utils/imUtils 文件中曾经有所提供了,具体的操作如下(这里须要留神,外面依据了音讯记录的 elemType 做了判断,elemType 腾讯 TXIM 的官网文档对应这里 https://im.sdk.qcloud.com/doc/zh-cn/classcom_1_1tencent_1_1imsdk_1_1v2_1_1V2TIMMessageManager.html)
async HistoryMessageToChatLog (msgLogs, unshift) {for (let item of msgLogs) {
let msgType = [
null,
this.$imUtils.MSG_TEXT,
null,
this.$imUtils.MSG_IMAGE,
this.$imUtils.MSG_AUDIO,
this.$imUtils.MSG_VIDEO,
null,
this.$imUtils.MSG_LOCATION,
this.$imUtils.MSG_TIP_FACE,
][item.elemType]
let dataJson = {
id: item.msgID,
head: '../static/icon_u_head.jpg',
self: item.isSelf,
type: msgType,
data: null
}
switch (msgType) {
case this.$imUtils.MSG_TEXT:
dataJson.data = this.$imUtils.buildMessageItem(msgType, item.elem.text, item)
break
case this.$imUtils.MSG_LOCATION:
dataJson.data = this.$imUtils.buildMessageItem(msgType, item, item.elem.latitude, item.elem.longitude, item.elem.desc, item)
break
case this.$imUtils.MSG_IMAGE:
dataJson.data = this.$imUtils.buildMessageItem(msgType, item.elem.imageList[0].url, item)
break
case this.$imUtils.MSG_AUDIO:
dataJson.data = this.$imUtils.buildMessageItem(msgType, '', item.elem.duration * 1000, item)
break
case this.$imUtils.MSG_VIDEO:
dataJson.data = this.$imUtils.buildMessageItem(msgType, item, item)
break
case this.$imUtils.MSG_TIP_FACE:
dataJson.data = this.$imUtils.buildMessageItem(msgType, item, item)
break
default:
// console.log(item)
}
if (msgType) {this.chatLogs[unshift ? 'unshift' : 'push'](dataJson)
}
}
}
3. 聊天音讯回显
聊天音讯局部 demo 曾经内置了一个聊天组件,咱们能够实现开箱即用,只须要引入组件定义好 list 即可
首先咱们须要引入组件,并且在 template 对应的地位写好即可
<chat-message-item
v-for="item in chatLogs"
:ref="item.id"
:self="item.self"
:head="item.head"
:type="item.type"
:data="item.data"
:prevent="currentPopItem && currentPopItem.item.id == item.id"
@longpressContent="currentPopItem = {ref: $refs[item.id][0], item }"
>
</chat-message-item>
<script>
import ChatMessageItem from '../components/ChatMessageItem'
export default {components: { ChatMessageItem},
}
</script>
4. 聊天音讯弹出菜单
一般而言咱们聊天音讯还须要有一些操作,比方删除,转发之类的,那么咱们也须要为聊天音讯减少弹出菜单,这须要咱们去做浮动地位的计算,然而 demo 中曾经提供了一个弹出菜单,使得咱们能够很简略的实现这个性能。
聊天音讯的弹出菜单组件在 componetns/ChatMsgItemProp 下,开发者须要本人批改成为本人须要的性能,这里咱们先引入组件而后退出到 template 中对应地位
<chat-layout
class="page"
ref="chatLayout"
:end="end"
@upperLoading="loadMoreLog"
@scroll="onChatLayoutScroll"
@clickRoot="onRootClick"
>
<chat-message-item
v-for="item in chatLogs"
:ref="item.id"
:self="item.self"
:head="item.head"
:type="item.type"
:data="item.data"
:prevent="currentPopItem && currentPopItem.item.id == item.id"
@longpressContent="currentPopItem = {ref: $refs[item.id][0], item }"
>
</chat-message-item>
<!-- 这里要显著留神,弹出不是在 ChatMessageItem 外面的!-->
<chat-msg-item-pop
:show="currentPopItem"
@clickFn="onPopClickFn($event)"
></chat-msg-item-pop>
</chat-layout>
这里咱们次要是从 chatMessageItem 中获取到了长按事件的坐标信息,而后交给 chatMsgItemPop,弹出组件会依据坐标计算确定显示地位和方向,计算局部的算法如下:
calcPopPosition () {let { ref} = this.show
let el = ref.$el
let pop = this.$refs.pop
let content = el.children[2]
dom.getComponentRect(pop, ({ size}) => {
let popSize = size
dom.getComponentRect(content, ({ size}) => {
let contentSize = size
this.popPoint.reverse = (contentSize.top - popSize.height) < 0
this.popPoint.popHeight = popSize.height
this.popPoint.itemHeight = contentSize.height
this.popPoint.touchX = contentSize.left + contentSize.width / 2
this.popPoint.touchY = contentSize.top
})
})
}
通过计算确定了显示地位之后就会显示性能菜单列表,在组件中内置了以下的按钮图标,性能暂未实现,开发者能够本人依据理论须要定制化解决哦。
{type: 'copy', label: '复制', icon: '/static/icon_copy.png'},
{type: 'share', label: '转发', icon: '/static/icon_share.png'},
{type: 'collect', label: '珍藏', icon: '/static/icon_collect.png'},
{type: 'delete', label: '删除', icon: '/static/icon_delete.png'},
{type: 'ref', label: '援用', icon: '/static/icon_ref_msg.png'},
{type: 'translate', label: '翻译', icon: '/static/icon_fanyi.png'},
{type: 'search', label: '搜一搜', icon: '/static/icon_search_fun.png'},
我的项目开源地址及交换群
我的项目成品成果查看:请点击我的项目引言
我的项目开源地址:https://gitee.com/ckong/Zhimi…
Uniapp 开发交换群:755910061