修改部分加粗了 ,复制时注意删除加粗的MD语法.代码自行验证.
<template>
<view>
<view class="content" @touchstart="hideDrawer" :style="{height: style.contentViewHeight + 'px'}">
<scroll-view id="scrollview" class="msg-list" scroll-y="true" :scroll-with-animation="scrollAnimation"
:scroll-top="scrollTop" :scroll-into-view="scrollToView" @scrolltoupper="loadHistory"
upper-threshold="50" :style="{height: style.contentViewHeight + 'px'}">
###<!-- 这里是顶部 -->
###<view class="" id="top"></view>
<!-- 加载历史数据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>
###<!-- 这里使用了index 不依赖后端数据 -->
###<view class="row" v-for="(row,index) in msgList" :key="index" :id="'msg'+index">
<!-- 系统消息 -->
<block v-if="row.type=='system'">
<view class="system">
<!-- 文字消息 -->
<view v-if="row.msg.type=='text'" class="text">
{{row.msg.content.text}}
</view>
<!-- 领取红包消息 -->
<view v-if="row.msg.type=='redEnvelope'" class="red-envelope">
<image src="/static/img/red-envelope-chat.png"></image>
{{row.msg.content.text}}
</view>
</view>
</block>
<!-- 用户消息 -->
<block v-if="row.type=='user'">
<!-- 自己发出的消息 -->
<view class="my" v-if="row.msg.userinfo.uid==myuid">
<!-- 左-消息 -->
<view class="left">
<!-- 文字消息 -->
<view v-if="row.msg.type=='text'" class="bubble">
<!-- <rich-text :nodes="row.msg.content.text"></rich-text> -->
<rich-text :nodes="row.msg.content"></rich-text> <!-- 20210515 -->
</view>
<!-- 语言消息 -->
<view v-if="row.msg.type=='voice'" class="bubble voice" @tap="playVoice(row.msg)"
:class="playMsgid == row.msg.id?'play':''">
<!-- <view class="length">{{row.msg.content.length}}</view> -->
<view class="icon my-voice"></view>
</view>
</view>
<!-- 右-头像 -->
<view class="right">
<!-- <image :src="row.msg.userinfo.face"></image> -->
<text>我</text>
</view>
</view>
<!-- 别人发出的消息 -->
<view class="other" v-if="row.msg.userinfo.uid!=myuid">
<!-- 左-头像 -->
<!-- <view class="left">
<image :src="row.msg.userinfo.face"></image>
</view> -->
<!-- 右-用户名称-时间-消息 -->
<view class="right">
<view class="username">
<!-- <view class="name">{{row.msg.userinfo.uid}}</view> <view class="time">{{row.msg.time}}</view> -->
<view class="name">{{row.msg.userinfo.username}}</view>
<view class="time">{{row.msg.time}}</view>
</view>
<!-- 文字消息 -->
<view v-if="row.msg.type=='text'" class="bubble">
<!-- <rich-text :nodes="row.msg.content.text"></rich-text> -->
<!-- 20210515 -->
<rich-text :nodes="row.msg.content"></rich-text>
<!-- <rich-text :nodes="row.msg.userinfo.uid"></rich-text>
<rich-text :nodes="myuid"></rich-text> -->
</view>
<!-- 语音消息 -->
<view v-if="row.msg.type=='voice'" class="bubble voice" @tap="playVoice(row.msg)"
:class="playMsgid == row.msg.id?'play':''">
<view class="icon other-voice"></view>
<!-- <view class="length">{{row.msg.content.length}}</view> -->
</view>
</view>
</view>
</block>
</view>
###<!-- 这里是底部 -->
###<view class="" id="bottom"></view>
</scroll-view>
</view>
<!-- 底部输入栏 -->
<view class="input-box" :class="popupLayerClass" @touchmove.stop.prevent="discard">
<!-- H5下不能录音,输入栏布局改动一下 -->
<!-- #ifndef H5 -->
<view class="voice">
<!-- <view class="icon" :class="isVoice?'jianpan':'yuyin'" @tap="switchVoice"></view> -->
<view class="icon" :class="'yuyin'"></view>
</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="more" @tap="showMore">
<view class="icon add"></view>
</view>
<!-- #endif -->
<view class="textbox">
<!-- <view class="voice-mode" :class="[isVoice?'':'hidden',recording?'recording':'']" @touchstart="voiceBegin"
@touchmove.stop.prevent="voiceIng" @touchend="voiceEnd" @touchcancel="voiceCancel">{{voiceTis}}</view> -->
<view class="voice-mode" :class="['','recording']" @touchstart="voiceBegin"
@touchmove.stop.prevent="voiceIng" @touchend="voiceEnd" @touchcancel="voiceCancel">{{voiceTis}}
</view>
<!-- <view class="text-mode" :class="isVoice?'hidden':''"> -->
<!-- <view class="text-mode" :class="''">
<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>
<!-- #ifndef H5 -->
<!-- <view class="more" @tap="showMore">
<view class="icon add"></view>
</view> -->
<!-- #endif -->
<!-- <view class="send" :class="isVoice?'hidden':''" @tap="sendText">
<view class="btn">发送</view>
</view> -->
</view>
<!-- 录音UI效果 -->
<view class="record" :class="recording?'':'hidden'">
<view class="ing" :class="willStop?'hidden':''">
<view class="icon luyin2"></view>
</view>
<view class="cancel" :class="willStop?'':'hidden'">
<view class="icon chehui"></view>
</view>
<view class="tis" :class="willStop?'change':''">{{recordTis}}</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 聊天页面时时滚动样式
style: {
pageHeight: 0,
contentViewHeight: 0,
footViewHeight: 90,
mitemHeight: 0
},
secondTouch: true,
// 确保websocket是打开状态
is_open_socket: false,
timer: '',
groupId: -1,
page: 1,
//文字消息
textMsg: '',
//消息列表
isHistoryLoading: false,
scrollAnimation: false,
scrollTop: 0,
scrollToView: '',
msgList: [],
msgImgList: [],
//myuid:0,
myuid: uni.getStorageSync("userId"),
//录音相关参数
// #ifndef H5
//H5不能录音
RECORDER: uni.getRecorderManager(),
// #endif
isVoice: false,
voiceTis: '按住 说话',
recordTis: "手指上滑 取消发送",
recording: false,
willStop: false,
initPoint: {
identifier: 0,
Y: 0
},
recordTimer: null,
recordLength: 0,
//播放语音相关参数
AUDIO: uni.createInnerAudioContext(),
playMsgid: null,
VoiceTimer: null,
// 抽屉参数
popupLayerClass: '',
// more参数
hideMore: true,
//表情定义
hideEmoji: true,
//红包相关参数
windowsState: '',
redenvelopeData: {
rid: null, //红包ID
from: null,
face: null,
blessing: null,
money: null,
}
};
},
onReady() {
this.$nextTick(function() {
console.log('进入页面滚动到底部')
//进入页面滚动到底部
//this.scrollTop = 9999;
setTimeout(() => {
this.scrollToView = 'bottom';
})
this.$nextTick(function() {
this.scrollAnimation = true;
});
});
},
created() {
const res = uni.getSystemInfoSync(); //获取手机可使用窗口高度 api为获取系统信息同步接口
this.style.pageHeight = res.windowHeight;
this.style.contentViewHeight = res.windowHeight - uni.getSystemInfoSync().screenWidth / 750 * (100) -
70; //像素 因为给出的是像素高度 然后我们用的是upx 所以换算一下
this.scrollToBottom(); //创建后调用回到底部方法
},
onLoad(option) {
uni.setNavigationBarTitle({ // 这里加载标题
title: option.groupName + '分组'
});
this.groupId = option.groupId;
// 进入这个页面的时候创建websocket连接【整个页面随时使用】
this.connectSocketInit();
this.timer = setInterval(this.xinTiao, 30000);
this.getMsgList();
//语音自然播放结束
this.AUDIO.onEnded((res) => {
this.playMsgid = null;
});
// #ifndef H5
//录音开始事件
this.RECORDER.onStart((e) => {
this.recordBegin(e);
})
//录音结束事件
this.RECORDER.onStop((e) => {
this.recordEnd(e);
})
// #endif
},
onShow() {
// this.scrollTop = 9999999;
//模板借由本地缓存实现发红包效果,实际应用中请不要使用此方法。
//
},
beforeDestroy() {
//this.closeSocket();
},
methods: {
/**
* 滚动到底部
* @param Number flag 方式
* @param String position 位置 (top,bottom)
*/
###newToBottom(flag = 0,position = 'bottom') {
### setTimeout(() => {
### if (flag) { // 方式一
### this.scrollToView = 'msg' + (position == 'top' ? 0 : this.msgList.length - 1);
### return
### }
### // 方式二
### this.scrollToView = '';
### this.scrollToView = position;
### })
###},
/**
* @author gongliying
* @date 2019-07-26 https://www.cnblogs.com/gongliying/p/11258189.html
* @information 跳转页面底部
*/
scrollToBottom: function() {
console.log('in scrollToBottom')
let that = this;
let query = uni.createSelectorQuery();
console.log('in scrollToBottom 2')
query.selectAll('.row').boundingClientRect();
query.select('#scrollview').boundingClientRect();
console.log('in scrollToBottom 3')
query.exec((res) => {
console.log("得到布局位置信息:" + JSON.stringify(res));
console.log("节点离页面顶部的距离为:" + res[0].top);
that.style.mitemHeight = 0;
res[0].forEach((rect) => {
console.log('rect.height= ' + rect.height);
that.style.mitemHeight = that.style.mitemHeight + rect.height + 40;
})
//获取所有内部子元素的高度
// 因为vue的虚拟DOM 每次生成的新消息都是之前的,所以采用异步setTimeout 主要就是添加了这红字
setTimeout(() => {
if (that.style.mitemHeight > (that.style.contentViewHeight -
100)) { //判断子元素高度是否大于显示高度
that.scrollTop = that.style.mitemHeight - that.style
.contentViewHeight //用子元素的高度减去显示的高度就获益获得序言滚动的高度
}
}, 100);
})
},
xinTiao: function() {
//setInterval(this.run,5000);
let _this = this;
let tempData = '{"type":"1","userid": "' + uni.getStorageSync("userId") + '"}';
console.log(tempData)
_this.socketTask.send({
data: tempData, /// '{"type":"1","userid": '+uni.getStorageSync("userId")+'}',
async success() {
//console.log("消息发送成功");
},
});
},
// 进入这个页面的时候创建websocket连接【整个页面随时使用】
connectSocketInit() {
// 创建一个this.socketTask对象【发送、接收、关闭socket都由这个对象操作】
this.socketTask = uni.connectSocket({
// 【非常重要】必须确保你的服务器是成功的,如果是手机测试千万别使用ws://127.0.0.1:9099【特别容易犯的错误】
url: 'ws://www.bjcn-tech.com:30190/vom/api/websocket/61', // 31',//'后台的地址', **例如:ws: '192.168.10.101:8080'**
success(data) {
console.log("websocket连接成功");
},
});
// 消息的发送和接收必须在正常连接打开中,才能发送或接收【否则会失败】
this.socketTask.onOpen((res) => {
console.log("WebSocket连接正常打开中...!");
this.is_open_socket = true;
this.xinTiao();
// 注:只有连接正常打开中 ,才能正常成功发送消息
/* this.socketTask.send({
data: '{"type":"2","userid":"118","groupid":"31","mp3":"uni-app发送一条消息222"}',
async success() {
console.log("消息发送成功");
},
}); */
// 注:只有连接正常打开中 ,才能正常收到消息
this.socketTask.onMessage((res) => {
console.log("收到服务器内容:" + res.data);
if (JSON.parse(res.data).type == "2") {
console.log("收到服务器内容:" + res.data);
console.log("收到服务器内容:" + typeof(JSON.parse(res.data).type));
console.log("收到服务器内容:" + JSON.parse(res.data).userid + '****' + JSON.parse(
res
.data).username);
console.log('in this.socketTask.onMessage((res)')
var nowDate = new Date();
let lastid = this.msgList[this.msgList.length - 1].msg.id;
lastid++;
if (JSON.parse(res.data).userid != uni.getStorageSync('userId')) {
/* let msg = {type:'user',msg:{id:lastid,time:nowDate.getHours()+":"+nowDate.getMinutes(),type:'text',userinfo:{uid:JSON.parse(res.data).userid,username:"大黑哥",face:"/static/img/face.jpg"},content:res.data}} */
let msg = {
type: 'user',
msg: {
id: lastid,
time: nowDate.getHours() + ":" + nowDate.getMinutes(),
type: 'voice',
userinfo: {
uid: JSON.parse(res.data).userid,
username: JSON.parse(res.data).username,
face: "/static/img/face.jpg"
},
content: JSON.parse(res.data).mp3
}
} //username:"大黑哥" content:res.data
// uid 得改 username:"大黑哥"
// 发送消息
this.screenMsg(msg);
}
}
});
})
// 这里仅是事件监听【如果socket关闭了会执行】
this.socketTask.onClose(() => {
this.is_open_socket = false;
console.log("已经被关闭了")
})
},
// 关闭websocket【离开这个页面的时候执行关闭】
/* closeSocket() {
this.socketTask.close({
success(res) {
this.is_open_socket = false;
console.log("关闭成功", res)
},
fail(err) {
console.log("关闭失败", err)
}
})
}, */
// 离开也不关闭 20210516
// 接受消息(筛选处理)
screenMsg(msg) {
//从长连接处转发给这个方法,进行筛选处理
if (msg.type == 'system') {
// 系统消息
switch (msg.msg.type) {
case 'text':
this.addSystemTextMsg(msg);
break;
case 'redEnvelope':
this.addSystemRedEnvelopeMsg(msg);
break;
}
} else if (msg.type == 'user') {
// 用户消息
switch (msg.msg.type) {
case 'text':
this.addTextMsg(msg);
break;
case 'voice':
this.addVoiceMsg(msg);
//this.addVoiceMsg(msg.msg.mp3);
break;
case 'img':
this.addImgMsg(msg);
break;
case 'redEnvelope':
this.addRedEnvelopeMsg(msg);
break;
}
console.log('用户消息');
//非自己的消息震动
if (msg.msg.userinfo.uid != this.myuid) {
console.log('振动');
uni.vibrateLong();
this.playVoice(msg.msg);
/* const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = true;
innerAudioContext.src = '/static/audio/6000.mp3';
innerAudioContext.onPlay(() => {
console.log('开始播放');
});
innerAudioContext.onError((res) => {
console.log(res.errMsg);
console.log(res.errCode);
}); */
}
}
this.$nextTick(function() {
// 滚动到底
###this.newToBottom();
console.log('this.scrollToView= in screenMsg' + this.scrollToView)
});
},
//触发滑动到顶部(加载历史信息记录)
loadHistory(e) {
//let that = this;
console.log('in loadHistory')
if (this.isHistoryLoading) {
return;
}
console.log('in loadHistory 2');
console.log(typeof(this.page))
this.page = this.page + 1;
console.log('前面的 ' + this.page);
this.isHistoryLoading = true; //参数作为进入请求标识,防止重复请求
this.scrollAnimation = false; //关闭滑动动画
let Viewid = this.msgList[0].msg.id; //记住第一个信息ID
//本地模拟请求历史记录效果
setTimeout(() => {
// 消息列表
/* let list = [
{type:"user",msg:{id:1,type:"text",time:"12:56",userinfo:{uid:0,username:"大黑哥",face:"/static/img/face.jpg"},content:{text:"为什么温度会相差那么大?"}}},
{type:"user",msg:{id:2,type:"text",time:"12:57",userinfo:{uid:1,username:"售后客服008",face:"/static/img/im/face/face_2.jpg"},content:{text:"这个是有偏差的,两个温度相差十几二十度是很正常的,如果相差五十度,那即是质量问题了。"}}},
{type:"user",msg:{id:3,type:"voice",time:"12:59",userinfo:{uid:1,username:"售后客服008",face:"/static/img/im/face/face_2.jpg"},content:{url:"/static/voice/1.mp3",length:"00:06"}}},
{type:"user",msg:{id:4,type:"voice",time:"13:05",userinfo:{uid:0,username:"大黑哥",face:"/static/img/face.jpg"},content:{url:"/static/voice/2.mp3",length:"00:06"}}},
] */
console.log(uni.getStorageSync('userId'));
console.log(this.groupId);
console.log(this.page);
uni.request({
url: 'myUrl',
data: {
'userId': uni.getStorageSync('userId'), // 118, 432
'chatGroupId': this.groupId, // 31 , 62
'page': this.page
},
/* this.$http.get('cgr/listChatMsgByChatGroupId', {
'userId': uni.getStorageSync('userId'), // 118, 432
'chatGroupId': this.groupId, // 31 , 62
'page': this.page
}).then((res) => { */
success: (res) => {
var list = [];
console.log(res);
res = res.data;
/* if(res.success == true && res.totalSize >0){ */
if (res.success == true && res.data.length > 0) {
var i = 0;
/* for(;i<res.totalSize;i++){ */
for (; i < res.data.length; i++) {
/* let jiLu = {type:"user",msg:{id:res.data[i].id,type:"voice",time:res.data[i].msgTime,userinfo:{uid:res.data[i].userId,username:res.data[i].userId,face:"/static/img/face.jpg"},content:{text:res.data[i].msgMacro}}} */ //username:"大黑哥"
let jiLu = {
type: "user",
msg: {
id: res.data[i].id,
type: "voice",
time: res.data[i].msgTime,
userinfo: {
uid: res.data[i].userId,
username: res.data[i].nickname,
face: "/static/img/face.jpg"
},
content: res.data[i].msgMacro
}
}
console.log(jiLu);
list.push(jiLu);
}
} else {
this.scrollTop = 0;
setTimeout(function() {
//_this.isRotate = false;
uni.showToast({
icon: 'none',
position: 'bottom',
title: '已经到底最顶端,上面没有了'
});
}, 3000);
this.$nextTick(function() {
this.scrollAnimation = false;
});
}
//this.msgList = list;
// 获取消息中的图片,并处理显示尺寸
for (let i = 0; i < list.length; i++) {
/* if(list[i].type=='user'&&list[i].msg.type=="img"){
list[i].msg.content = this.setPicSize(list[i].msg.content);
this.msgImgList.unshift(list[i].msg.content.url);
}
list[i].msg.id = Math.floor(Math.random()*1000+1); */
this.msgList.unshift(list[i]);
}
}, //).catch((e) => {
fail: (e) => {
console.log(e);
console.log(JSON.stringify(e));
setTimeout(function() {
//_this.isRotate = false;
uni.showToast({
icon: 'none',
position: 'bottom',
title: '不行啊,请求超时,请稍后再试'
});
}, 3000);
}
});
//这段代码很重要,不然每次加载历史数据都会跳到顶部
this.$nextTick(function() {
###this.newToBottom(0,'top'); //跳转上次的第一行信息位置
// this.scrollToView = 'msg' + Viewid; //跳转上次的第一行信息位置
this.$nextTick(function() {
this.scrollAnimation = true; //恢复滚动动画
});
console.log('this.scrollToView in loadHistory ' + this.scrollToView)
});
this.isHistoryLoading = false;
}, 1000);
},
// 加载初始页面消息
getMsgList() {
//let list = [{type:"system",msg:{id:0,type:"text",content:{text:"欢迎进入 聊天室"}}},];
let list = [];
//for(var pageLinShi =1;pageLinShi<3;pageLinShi++){
/* this.$http.get('cgr/listChatMsgByChatGroupId', {
'userId': uni.getStorageSync('userId'), // 118, 432
'chatGroupId': this.groupId, // 31 , 62
'page': 1, //pageLinShi
}).then((res) => { */
uni.request({
url: 'myUrl',
data: {
'userId': uni.getStorageSync('userId'), // 118, 432
'chatGroupId': this.groupId, // 31 , 62
'page': 1
},
success: (res) => {
console.log(res);
/* if(res.success == true && res.totalSize >0){ */
res = res.data;
if (res.success == true && res.data.length > 0) {
var i = 0;
/* for(;i<res.totalSize;i++){ */
for (; i < res.data.length; i++) {
/* let jiLu = {type:"user",msg:{id:res.data[i].id,type:"text",time:res.data[i].msgTime,userinfo:{uid:res.data[i].userId,username:res.data[i].userId,face:"/static/img/face.jpg"},content:res.data[i].msgMacro}} */ // username:"大黑哥"
let jiLu = {
type: "user",
msg: {
id: res.data[i].id,
type: "voice",
time: res.data[i].msgTime,
userinfo: {
uid: res.data[i].userId,
username: res.data[i].nickname,
face: "/static/img/face.jpg"
},
content: res.data[i].msgMacro
}
}
//console.log(jiLu);
list.unshift(jiLu);
//this.msgList.unshift(jiLu);
}
}
}, //).catch((e) => {
fail: (e) => {
console.log(e)
console.log(JSON.stringify(e));
setTimeout(function() {
//_this.isRotate = false;
uni.showToast({
icon: 'none',
position: 'bottom',
title: '不行啊,请求超时,请稍后再试'
});
}, 3000);
}
});
///}
this.msgList = list;
// this.scrollToBottom();
// 滚动到底部
this.$nextTick(function() {
console.log('进入页面滚动到底部')
//进入页面滚动到底部
//this.scrollTop = 9999;
###this.newToBottom();
this.$nextTick(function() {
this.scrollAnimation = true;
});
});
//}
// 消息列表
//let list = [{type:"system",msg:{id:0,type:"text",content:{text:"欢迎进入HM-chat聊天室"}}},];
/* let list = [
{type:"system",msg:{id:0,type:"text",content:{text:"欢迎进入HM-chat聊天室"}}},
{type:"user",msg:{id:1,type:"text",time:"12:56",userinfo:{uid:0,username:"大黑哥",face:"/static/img/face.jpg"},content:{text:"为什么温度会相差那么大?"}}},
{type:"user",msg:{id:2,type:"text",time:"12:57",userinfo:{uid:1,username:"售后客服008",face:"/static/img/im/face/face_2.jpg"},content:{text:"这个是有偏差的,两个温度相差十几二十度是很正常的,如果相差五十度,那即是质量问题了。"}}},
{type:"user",msg:{id:3,type:"voice",time:"12:59",userinfo:{uid:1,username:"售后客服008",face:"/static/img/im/face/face_2.jpg"},content:{url:"/static/voice/1.mp3",length:"00:06"}}},
{type:"user",msg:{id:4,type:"voice",time:"13:05",userinfo:{uid:0,username:"大黑哥",face:"/static/img/face.jpg"},content:{url:"/static/voice/2.mp3",length:"00:06"}}},
{type:"user",msg:{id:5,type:"img",time:"13:05",userinfo:{uid:0,username:"大黑哥",face:"/static/img/face.jpg"},content:{url:"/static/img/p10.jpg",w:200,h:200}}},
{type:"user",msg:{id:6,type:"img",time:"12:59",userinfo:{uid:1,username:"售后客服008",face:"/static/img/im/face/face_2.jpg"},content:{url:"/static/img/q.jpg",w:1920,h:1080}}},
{type:"system",msg:{id:7,type:"text",content:{text:"欢迎进入HM-chat聊天室"}}},
{type:"system",msg:{id:9,type:"redEnvelope",content:{text:"售后客服008领取了你的红包"}}},
{type:"user",msg:{id:10,type:"redEnvelope",time:"12:56",userinfo:{uid:0,username:"大黑哥",face:"/static/img/face.jpg"},content:{blessing:"恭喜发财,大吉大利,万事如意",rid:0,isReceived:false}}},
{type:"user",msg:{id:11,type:"redEnvelope",time:"12:56",userinfo:{uid:1,username:"售后客服008",face:"/static/img/im/face/face_2.jpg"},content:{blessing:"恭喜发财",rid:1,isReceived:false}}},
] */
// 获取消息中的图片,并处理显示尺寸
/* for(let i=0;i<list.length;i++){
if(list[i].type=='user'&&list[i].msg.type=="img"){
list[i].msg.content = this.setPicSize(list[i].msg.content);
this.msgImgList.push(list[i].msg.content.url);
}
} */
},
//更多功能(点击+弹出)
showMore() {
this.isVoice = false;
this.hideEmoji = true;
if (this.hideMore) {
this.hideMore = false;
this.openDrawer();
} else {
this.hideDrawer();
}
},
// 打开抽屉
openDrawer() {
this.popupLayerClass = 'showLayer';
},
// 隐藏抽屉
hideDrawer() {
this.popupLayerClass = '';
setTimeout(() => {
this.hideMore = true;
this.hideEmoji = true;
}, 150);
},
//获取焦点,如果不是选表情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 content =this.textMsg;
let msg = {text:content}
this.sendMsg(msg,'text'); */
console.log('in sendText' + this.textMsg);
this.sendMsg(this.textMsg, 'text'); //只发文字
this.textMsg = ''; //清空输入框
},
// 发送消息
sendMsg(content, type) {
if (this.is_open_socket) {
//var json = '{"type":"2","userid":"118","groupid":"31","mp3":"'+content+'"}';
var json = '{"type":"2","userid":"' + 434 + '","username":"' + uni
.getStorageSync("username") + '","groupid":"' + this.groupId + '","mp3":"' + content + '"}';
console.log(json);
// websocket的服务器的原理是:发送一次消息,同时返回一组数据【否则服务器会进去死循环崩溃】
this.socketTask.send({
data: json, //data要字符串!!!!
//data:"关闭吗",
async success() {
console.log("消息发送成功");
},
});
//message.isSend = true;
//this.msgListMy.push(JSON.parse(json));
//this.scrollBottom();
//this.msg = ""
}
//实际应用中,此处应该提交长连接,模板仅做本地处理。
var nowDate = new Date();
let lastid = this.msgList[this.msgList.length - 1].msg.id;
lastid++;
let msg = {
type: 'user',
msg: {
id: lastid,
time: nowDate.getHours() + ":" + nowDate.getMinutes(),
type: type,
userinfo: {
uid: 434,
username: "大黑哥",
face: "/static/img/face.jpg"
},
content: content
}
}
// 发送消息
this.screenMsg(msg);
// 定时器模拟对方回复,三秒
/* setTimeout(()=>{
lastid = this.msgList[this.msgList.length-1].msg.id;
lastid++;
msg = {type:'user',msg:{id:lastid,time:nowDate.getHours()+":"+nowDate.getMinutes(),type:type,userinfo:{uid:1,username:"售后客服008",face:"/static/img/im/face/face_2.jpg"},content:content}}
// 本地模拟发送消息
this.screenMsg(msg);
},3000) */
},
// 添加文字消息到列表
addTextMsg(msg) {
console.log('in addTextMsg,and msg = ' + msg)
this.msgList.push(msg);
this.$set(this.msgList, this.msgList.length - 1, this.msgList[this.msgList.length - 1]);
//debugger;
console.log(this.msgList[this.msgList.length - 1].msg.content)
},
// 添加语音消息到列表
addVoiceMsg(msg) {
this.msgList.push(msg);
this.$set(this.msgList, this.msgList.length - 1, this.msgList[this.msgList.length - 1]);
this.scrollToBottom();
//this.playVoice(msg.msg);
},
sendSystemMsg(content, type) {
let lastid = this.msgList[this.msgList.length - 1].msg.id;
lastid++;
let row = {
type: "system",
msg: {
id: lastid,
type: type,
content: content
}
};
this.screenMsg(row)
},
// 播放语音
playVoice(msg) {
console.log('in playVoice(msg)')
console.log('msg.id= ' + msg.id)
console.log('msg.content.url=' + msg.content)
console.log(msg)
this.playMsgid = msg.id;
//this.AUDIO.src = msg.content.url;
this.AUDIO.src = 'http://www.bjcn-tech.com:30180/uploadFiles/socketMp3/' + msg.content;
//if(this.secondTouch){
this.$nextTick(function() {
this.AUDIO.play();
this.secondTouch = false;
});
/* }else{
this.AUDIO.stop();
this.secondTouch= true;
} */
},
// 录音开始
voiceBegin(e) {
if (e.touches.length > 1) {
return;
}
this.initPoint.Y = e.touches[0].clientY;
this.initPoint.identifier = e.touches[0].identifier;
this.RECORDER.start({
format: "mp3"
}); //录音开始,
},
//录音开始UI效果
recordBegin(e) {
this.recording = true;
this.voiceTis = '松开 结束';
this.recordLength = 0;
this.recordTimer = setInterval(() => {
this.recordLength++;
}, 1000)
},
// 录音被打断
voiceCancel() {
this.recording = false;
this.voiceTis = '按住 说话';
this.recordTis = '手指上滑 取消发送'
this.willStop = true; //不发送录音
this.RECORDER.stop(); //录音结束
},
// 录音中(判断是否触发上滑取消发送)
voiceIng(e) {
if (!this.recording) {
return;
}
let touche = e.touches[0];
//上滑一个导航栏的高度触发上滑取消发送
if (this.initPoint.Y - touche.clientY >= uni.upx2px(100)) {
this.willStop = true;
this.recordTis = '松开手指 取消发送'
} else {
this.willStop = false;
this.recordTis = '手指上滑 取消发送'
}
},
// 结束录音
voiceEnd(e) {
if (!this.recording) {
return;
}
this.recording = false;
this.voiceTis = '按住 说话';
this.recordTis = '手指上滑 取消发送'
this.RECORDER.stop(); //录音结束
},
//录音结束(回调文件)
recordEnd(e) {
clearInterval(this.recordTimer);
if (!this.willStop) {
console.log("e: " + JSON.stringify(e));
//console.log(this.uploadMp3Action(e));
//var myFirstPromise = this.uploadMp3Action(e);
uni.showLoading({
title: '录音上传中...'
});
const uploadTask = uni.uploadFile({
url: 'myUrl',
filePath: e.tempFilePath, //filePath,//录音结束后返回的临时路径
//name: 'audio',
name: 'file',
success: (uploadFileRes) => {
console.log(JSON.stringify(uploadFileRes.data));
console.log(JSON.parse(uploadFileRes.data));
console.log(JSON.parse(uploadFileRes.data).success)
console.log(JSON.parse(uploadFileRes.data).data[0].appPath);
if (JSON.stringify(uploadFileRes.data) != "" && JSON.parse(uploadFileRes.data)
.success) {
/* let msg = {
length:0,
url:JSON.parse(uploadFileRes.data).data[0].appPath,//e.tempFilePath
}
let min = parseInt(this.recordLength/60);
let sec = this.recordLength%60;
min = min<10?'0'+min:min;
sec = sec<10?'0'+sec:sec;
msg.length = min+':'+sec;
this.sendMsg(msg,'voice');*/
this.sendMsg(JSON.parse(uploadFileRes.data).data[0].appPath, 'voice');
}
},
fail: (res) => {
console.log(JSON.stringify(res));
}
});
uni.hideLoading();
} else {
console.log('取消发送录音');
}
this.willStop = false;
},
// 切换语音/文字输入
switchVoice() {
this.hideDrawer();
this.isVoice = this.isVoice ? false : true;
},
discard() {
return;
}
}
}
</script>
<style lang="scss">
@import "./HM-chat/css/style.scss";
</style>
8 个回复
jasonDev (作者) - 努力
_this.msgList.length 这里的长度,总是和 页面初始化时候的长度不一样
回梦無痕 - 暂停服务
这里长连接代码应该是你自己写的,既然用了箭头函数,应该就不用 定义_this 了,直接用this,_this这个指向可能有问题。
另外我使用lastid是因为模板里面是本地创建消息,实际中应该直接取服务器返回的消息ID的。
jasonDev (作者)
服务器返回的消息ID?服务器一张表里存了好几个群的聊天记录,返回的消息id有用吗?我一开始是用this,效果达不到(不能滚动到最新的记录),就改用了_this
2021-05-27 11:24
回梦無痕
回复 jasonDev: 无论表里面存多少群信息也一样的,因为每个消息都有一个唯一的消息ID(如果你的没有,是数据库设计有问题)。ID作用就是vue渲染和给滚动跳转到最新消息用的,步骤是,服务器返回数据,渲染到页面,然后再根据消息的ID跳转到列表最底。
2021-05-27 14:29
jasonDev (作者)
回复 回梦無痕: 谢谢回复,可是做后台的很强硬,说“显示最新消息到相应的位置 非得用服务端的id 这个说法很奇怪”。不要这个id,安卓端能显示最新消息到底端吗?
2021-05-27 15:20
回梦無痕
回复 jasonDev: 你们的后台是坑货,如果以后增加需求,要撤回/删除消息,你没消息的ID传给后台,后台怎么知道要撤回哪条消息,这个ID又不是什么敏感信息,估计就单纯他不想改而已。
如果后台的确不给ID,那么需要自己创建ID。原理也一样的,后台返回最新消息时候,获取上次的ID,自增1,再赋值给最新消息,然后渲染到页面,然后修改scroll-into-view为最新ID。
2021-05-28 14:43
jasonDev (作者)
回复 回梦無痕: 目前就是这么做的啊。可是收到最新的消息的时候,去msgList里拿上次的id,总是不正确。似乎onMessage里 的 msgList和 一开始加载时候的msgList,不一样啊
2021-05-28 15:40
回梦無痕
回复 jasonDev: 我猜还是_this的指向问题,直接用this吧。这个滚动到底的方案我是在商用项目上实现过的。测试过程和上线至今,客户没有反馈有问题的
2021-05-28 16:01
jasonDev (作者)
回复 回梦無痕: 方便留给qq吗?帮我改下代码,有偿也行
2021-05-30 07:01
jasonDev (作者) - 努力
这段代码有什么问题吗?为何进入聊天页面的时候,滑动不到底部?而你的模板我运行了,可以滑动到底部
DCMarvel
看了半天,你们俩讨论啥呢?怎么还扯到后台id了.滚动和id有什么关系???
光有js,html代码呢?
以下思路自己测试
页面滚动用
scroll-view组件滚动用 (大概代码,具体自己看https://uniapp.dcloud.io/component/scroll-view文档,自己验证)
jasonDev (作者)
不知道放在哪,头疼。。。。。。。。scrollToId也用了,不起作用,有偿求教
2021-05-30 10:10
DCMarvel
回复 jasonDev: 你把源码发出来
2021-05-30 10:32
jasonDev (作者)
回复 DCMarvel: 源码很长的。加qq好吗?我的qq:1789270840
2021-05-30 10:42
jasonDev (作者) - 努力
DCMarvel
修改部分加粗了 ,复制时注意删除加粗的MD语法.代码自行验证.
jasonDev (作者)
谢谢您的热心回复。可是我按照你的改了,还是不能满足需求啊。有时候能刷新出来,有时候又刷新不出来,不知道怎么回事。比如我发了10个语音消息,第4、7、8条语音就是没有刷新出来,但是可以播放出来。方便加qq吗?谢谢,不知道是不是跟我的心跳包有关系
2021-05-30 13:11
DCMarvel
回复 jasonDev: 我只说了新消息滚动到底/顶问题,并没有测试列表信息问题.列表不刷新自己打印list列表自己看就行了.有信息不显示 自己加this.$forceUpdate();
2021-05-30 13:26
jasonDev (作者)
回复 DCMarvel: 有时候收到消息有震动,但是不能自动播放,没有刷新到列表。郁闷。。。。。这种几率问题,最麻烦了。。。
2021-05-30 14:12
jasonDev (作者) - 努力
我是这样添加语音消息到列表的,在demo中测试20次,没问题。可是整合到项目中,有时候出现接收方收不到消息的情况(记录里没显示,也没有播放),不知道什么原因?从后台看,我的语音已经上传到后台了(可以在浏览器播放)
DCMarvel
接收方没消息
1).WebSocket连接失败.
2).WebSocket下发失败
建议在WebSocket接收端输出日志查看
2021-05-30 14:27
jasonDev (作者)
回复 DCMarvel: 有震动,说明收到了。只是自动播放、自动刷新到底端不成功。说明还是前台代码有问题。真不知道到底哪里有问题。有时候又能自动播放、自动刷新到底部
2021-05-30 14:44
DCMarvel
jasonDev (作者)
改了,华为手机向小米手机发了10条,一条不正常;小米向华为发,只有3条正常。不行啊
2021-05-30 15:18