room.vue
15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
<template>
<view v-if="showRoom">
<view class="content" @touchstart="hideDrawer">
<scroll-view @scroll="scroll" class="msg-list" scroll-y="true" :scroll-with-animation="scrollAnimation" :scroll-top="scrollTop"
:scroll-into-view="scrollToView" @scrolltoupper="loadHistory" upper-threshold="50">
<!-- 加载历史数据waitingUI -->
<!-- <view class="loading">
<view class="spinner">
<view class="rect1"></view>
<view class="rect2"></view>
<view class="rect3"></view>
<view class="rect4"></view>
<view class="rect5"></view>
</view>
</view> -->
<view class="row" v-for="(item,index) in msgList" :key="index" :id="item.ID">
<!-- 用户消息 -->
<block>
<!-- 礼物消息 -->
<view class="system" v-if="item.flow == 'in' && item.type==TIM.TYPES.MSG_CUSTOM">
<!-- 发送礼物消息 -->
<view class="red-envelope">
{{item.payload.data}}
</view>
</view>
<!-- 自己发出的消息 -->
<view class="my" v-if="item.flow=='out' && item.type==TIM.TYPES.MSG_TEXT">
<!-- 左-消息 -->
<view class="left">
<view class="username">
<view class="time">{{timeFliter(item.time)}}</view>
</view>
<!-- 文字消息 -->
<view class="myMsg">
<view class="peerStatus">
{{item.isPeerRead ? '已读' : '未读'}}
</view>
<view class="bubble">
<rich-text :nodes="nodesFliter(item.payload.text)"></rich-text>
</view>
</view>
</view>
<!-- 右-头像 -->
<view class="right" @click="$href('/pages/my/my',2)">
<image :src="userInfo.img" mode="aspectFill"></image>
</view>
</view>
<!-- 别人发出的消息 -->
<view class="other" v-if="item.flow=='in' && item.type==TIM.TYPES.MSG_TEXT">
<!-- 左-头像 -->
<navigator class="left" hover-class="none" :url="'/pages/index/landHome?id='+toUserInfo.id">
<image :src="toUserInfo.avatar" mode="aspectFill"></image>
</navigator>
<!-- 右-用户名称-时间-消息 -->
<view class="right">
<view class="username">
<view class="name">{{toUserInfo.nickname}}</view>
<view class="time">{{timeFliter(item.time)}}</view>
</view>
<!-- 文字消息 -->
<view v-if="item.type==TIM.TYPES.MSG_TEXT" class="bubble">
<rich-text :nodes="nodesFliter(item.payload.text)" @longtap="longtap(item)"></rich-text>
</view>
</view>
</view>
</block>
</view>
</scroll-view>
</view>
<view v-if="plat == 'android' || (plat == 'ios' && switchTab == '1')" class="giftWrap" :style="{bottom: keyboardHeight + 150 +'rpx'}" @click="showSendGift = true">
<image src="../../static/image/roomGiftIcon.png" mode=""></image>
</view>
<!-- 抽屉栏 -->
<view class="popup-layer" :class="popupLayerClass" @touchmove.stop.prevent="discard">
<!-- 表情 -->
<swiper class="emoji-swiper" :class="{hidden:hideEmoji}" indicator-dots="true" duration="150">
<swiper-item v-for="(page,pid) in emojiList" :key="pid">
<view v-for="(em,eid) in page" :key="eid" @tap="addEmoji(em)">
<image mode="widthFix" :src="'/static/img/douyin/'+em.url"></image>
</view>
</swiper-item>
</swiper>
</view>
<!-- 送礼物弹框 -->
<send-gift v-if="showSendGift" :toUserId="toUserInfo.id" @closeSendGift="closeSendGift"></send-gift>
<!-- 底部输入栏 -->
<view class="input-box" :style="{bottom: keyboardHeight+'rpx'}" :class="popupLayerClass" @touchmove.stop.prevent="discard">
<view class="textbox">
<view class="text-mode">
<view class="box">
<textarea auto-height="true" v-model="textMsg" @focus="textareaFocus" />
</view>
<view class="em" @tap="chooseEmoji">
<view class="icon biaoqing"></view>
</view>
</view>
</view>
<view class="send" @tap="sendText">
<view class="btn">发送</view>
</view>
</view>
</view>
</template>
<script>
import userList from '../../commen/tim/user.js'
import {mapState} from "vuex";
import sendGift from '@/components/sendGift.vue'
export default {
data() {
return {
windowHeight:0,//窗口高度
rectInfo:[],
showSendGift:false,//展示送礼物弹框
showRoom:false,
keyboardHeight:-2,
//TIM变量
conversationActive:null,
toUserId:'',
toUserInfo:null,
userInfo:null,
nextReqMessageID:'',
count:15,
isCompleted:'',
msgList:[],
TIM:null,
//文字消息
textMsg:'',
scrollAnimation:false,
scrollTop:0,
scrollToView:'',
// 抽屉参数
popupLayerClass:'',
// more参数
hideMore:true,
//表情定义
hideEmoji:true,
emojiList:this.$commen.emojiList,
switchTab:'1',
plat:'android'
};
},
components:{
sendGift
},
computed:{
...mapState({
userinfo: state => state.userinfo,
isLogin: state => state.isLogin,
isSDKReady: state => state.isSDKReady,
currentMessageList:state=>state.currentMessageList,
})
},
watch:{
currentMessageList(newVal,oldVal){
this.msgList = newVal
if(this.isSDKReady){
this.screenMsg(newVal,oldVal)
}
},
},
onLoad() {
uni.getSystemInfo({
success: (res) => {
this.windowHeight = res.windowHeight
}
})
this.$request('/common/getConfig',{type:'8'}).then((res)=>{
this.switchTab = res.data.switch
this.plat = plus.os.name.toLowerCase()
})
this.showRoom = false
if(!this.isSDKReady){
this.loginTim()
}else{
this.getTimInfo()
}
//监听键盘高度变化
uni.onKeyboardHeightChange(res => {
if(res.height == 0){
this.keyboardHeight = -2
}else{
this.keyboardHeight = 20
}
})
},
/* onShow(){
console.log('onShow方法执行了')
this.scrollTop = 9999999;
}, */
onUnload(){
//退出页面 将所有的会话内的消息设置为已读
if(this.isSDKReady){
let promise = this.tim.setMessageRead({conversationID: this.conversationActive.conversationID});
promise.then(function(imResponse) {
// 已读上报成功
}).catch(function(imError) {
// 已读上报失败
console.warn('setMessageRead error:', imError);
});
}
},
methods:{
scroll(e){
var scrollTop = e.detail.scrollTop;
var index;
var top=0;
var bottom=0;
var temp=0;
for(var i=0;i<this.msgList.length;i++){
if(!this.msgList[i].isRead && this.msgList[i].to == this.userinfo.id){
let view = uni.createSelectorQuery().in(this).select('#'+this.msgList[i].ID);
view.fields({
size: true,
rect: true
}, data => {
top=temp;
bottom=top+data.height
temp+=data.height;
if(scrollTop>top&&(scrollTop + this.windowHeight)>bottom){
let promise = this.tim.setMessageRead({conversationID: this.conversationActive.conversationID});
promise.then(function(imResponse) {
// 已读上报成功
}).catch(function(imError) {
// 已读上报失败
console.warn('setMessageRead error:', imError);
});
}
}).exec();
}
}
},
closeSendGift(e){
if(e){
let msg = {text: this.userinfo.nickname+"给您送了"+e}
this.sendMsg(msg,'custom');
}
this.showSendGift = false
},
longtap(item){
uni.showModal({
title:'提示',
content:'是否举报此信息',
success: (res) => {
if(res.confirm){
uni.navigateTo({
url:'/pages/index/report?article_id='+item.ID
})
}
}
})
},
getTimInfo(){
if(uni.getStorageSync('toUserItem') && uni.getStorageSync('toRoomType') == 'toUserItem'){
this.$store.commit('updateConversationActive', JSON.parse(uni.getStorageSync('toUserItem')))
}
if(uni.getStorageSync('toUserId') && uni.getStorageSync('toRoomType') == 'toUserId'){
this.$store.commit('createConversationActive',uni.getStorageSync('toUserId'))
}
this.userInfo = JSON.parse(uni.getStorageSync('userInfo'))
this.toUserId = this.$store.state.toUserId
this.conversationActive = this.$store.state.conversationActive
this.TIM = this.$TIM
//获取聊天对象的用户信息
this.$request('/user/userInfo',{user_id:this.toUserId}).then((res)=>{
this.toUserInfo = res.data
uni.setNavigationBarTitle({
title:this.toUserInfo.nickname
})
this.getMsgList();
})
},
loginTim(){
let userID = this.userinfo.id.toString()
let userSig = this.userinfo.user_sign
let userInfo = {
user: this.userinfo.nickname,
userId:this.userinfo.id,
img:this.userinfo.avatar,
userSig:this.userinfo.user_sign
}
//登录腾讯IM及时通讯
let promise = this.tim.login({
userID: userID,
userSig: userSig
});
promise.then((res) => {
//登录成功后 更新登录状态
this.$store.commit("toggleIsLogin", true);
//自己平台的用户基础信息
uni.setStorageSync('userInfo', JSON.stringify(userInfo))
//tim 返回的用户信息
uni.setStorageSync('userTIMInfo', JSON.stringify(res.data))
setTimeout(()=>{
this.getTimInfo()
},1000)
}).catch((err) => {
console.warn('腾讯云即时通讯登录报错', err);
});
},
//聊天的节点加上外层的div
nodesFliter(str){
let nodeStr = '<div style="align-items: center;word-wrap:break-word;">'+str+'</div>'
return nodeStr
},
//时间过滤
timeFliter(timer){
let timeData = new Date(timer*1000)
let str = this.$commen.dateTimeFliter(timeData)
return str
},
// 接受消息(定位消息)
screenMsg(newVal,oldVal){
if(newVal.length > 0 && oldVal.length > 0){
if(newVal[0].ID != oldVal[0].ID && newVal.length>=this.count ){
this.$nextTick(()=> {this.scrollToView =oldVal[0].ID});
}else{
if(newVal.length > oldVal.length){
this.$nextTick(()=> {this.scrollToView =newVal[newVal.length-1].ID});
}
}
}else{
if(newVal.length > 0){
this.$nextTick(()=> {this.scrollToView =newVal[newVal.length-1].ID});
}
}
},
//触发滑动到顶部(加载历史信息记录)
loadHistory(e){
uni.showLoading({
title:'加载中...'
})
this.scrollAnimation = false;//恢复滚动动画
// 更多消息列表
let conversationID = this.conversationActive.conversationID
let promise = this.tim.getMessageList({conversationID: conversationID,nextReqMessageID:this.nextReqMessageID,count: this.count});
promise.then((res)=> {
this.$store.commit('unshiftCurrentMessageList', res.data.messageList)
this.nextReqMessageID = res.data.nextReqMessageID // 用于续拉,分页续拉时需传入该字段。
this.isCompleted = res.data.isCompleted
//这段代码很重要,不然每次加载历史数据都会跳到顶部
this.$nextTick(() => {
this.scrollToView = this.nextReqMessageID;//跳转上次的第一行信息位置
this.$nextTick(() => {
this.scrollAnimation = true;//恢复滚动动画
});
});
uni.hideLoading()
});
},
// 加载初始页面消息
getMsgList(){
uni.showLoading({
title:'加载中...'
})
// 历史消息列表
let conversationID = this.conversationActive.conversationID
let promise = this.tim.getMessageList({conversationID: conversationID, count: this.count});
promise.then((res)=> {
this.$store.commit('pushCurrentMessageList', res.data.messageList)
this.nextReqMessageID = res.data.nextReqMessageID // 用于续拉,分页续拉时需传入该字段。
this.isCompleted = res.data.isCompleted
// 滚动到底部
this.$nextTick(() => {
//进入页面滚动到底部
this.scrollToView = res.data.messageList[res.data.messageList.length-1].ID
this.$nextTick(() => {
this.scrollAnimation = true;
});
});
this.showRoom = true
uni.hideLoading()
})
},
// 打开抽屉
openDrawer(){
this.popupLayerClass = 'showLayer';
},
// 隐藏抽屉
hideDrawer(){
this.popupLayerClass = '';
setTimeout(()=>{
this.hideMore = true;
this.hideEmoji = true;
},150);
},
// 选择表情
chooseEmoji(){
this.hideMore = true;
if(this.hideEmoji){
this.hideEmoji = false;
this.openDrawer();
}else{
this.hideDrawer();
}
},
//添加表情
addEmoji(em){
this.textMsg+=em.alt;
},
//获取焦点,如果不是选表情ing,则关闭抽屉
textareaFocus(){
if(this.popupLayerClass=='showLayer' && this.hideMore == false){
this.hideDrawer();
}
},
// 发送文字消息
sendText(){
this.hideDrawer();//隐藏抽屉
if(!this.textMsg){
return;
}
let content = this.replaceEmoji(this.textMsg);
let msg = {text:content}
this.sendMsg(msg,'text');
this.textMsg = '';//清空输入框
},
//替换表情符号为图片
replaceEmoji(str){
let replacedStr = str.replace(/\[([^(\]|\[)]*)\]/g,(item, index)=>{
for(let i=0;i<this.emojiList.length;i++){
let row = this.emojiList[i];
for(let j=0;j<row.length;j++){
let EM = row[j];
if(EM.alt==item){
//在线表情路径,图文混排必须使用网络路径,请上传一份表情到你的服务器后再替换此路径
//比如你上传服务器后,你的100.gif路径为https://www.xxx.com/emoji/100.gif 则替换onlinePath填写为https://www.xxx.com/emoji/
// let onlinePath = 'https://s2.ax1x.com/2019/04/12/'
// let imgstr = '<img src="'+onlinePath+this.onlineEmoji[EM.url]+'">';
let imgstr = '<img style="width:24px;height:24px;" mode="widthFix" src="static/img/douyin/'+EM.url+'">';
return imgstr;
}
}
}
});
return replacedStr;
},
// 发送消息
sendMsg(content,type){
let message
let toUserId = this.toUserId.toString()
if(type == 'text'){
message = this.tim.createTextMessage({
to: toUserId,
conversationType: 'C2C',
payload: {
text: content.text
}
});
}
if(type == 'custom'){
message = this.tim.createCustomMessage({
to: toUserId,
conversationType: 'C2C',
// 消息优先级,用于群聊(v2.4.2起支持)。如果某个群的消息超过了频率限制,后台会优先下发高优先级的消息,详细请参考:https://cloud.tencent.com/document/product/269/3663#.E6.B6.88.E6.81.AF.E4.BC.98.E5.85.88.E7.BA.A7.E4.B8.8E.E9.A2.91.E7.8E.87.E6.8E.A7.E5.88.B6)
// 支持的枚举值:TIM.TYPES.MSG_PRIORITY_HIGH, TIM.TYPES.MSG_PRIORITY_NORMAL(默认), TIM.TYPES.MSG_PRIORITY_LOW, TIM.TYPES.MSG_PRIORITY_LOWEST
// priority: TIM.TYPES.MSG_PRIORITY_HIGH,
payload: {
data: content.text, // 用于标识该消息是骰子类型消息
description: String(this.random(1,6)), // 获取骰子点数
extension: ''
}
});
}
this.$store.commit('pushCurrentMessageList', message)
let pomise = this.tim.sendMessage(message)
pomise.then(res=>{
this.$nextTick(()=> {
// 滚动到底
this.scrollToView = res.data.message.ID
});
})
},
random(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
},
discard(){
return;
}
}
}
</script>
<style lang="scss">
@import "@/static/HM-chat/css/style.scss";
</style>