uniapp——实现聊天室功能——技能提升
这里写目录标题
- 效果图
- 聊天室功能
- 代码——`html`部分
- 代码——`js`部分
- 代码——`其他`部分
首先声明一点:下面的内容是从一个uniapp
的程序中摘录的,并非本人所写,先做记录,以免后续遇到相似需求抓耳挠腮。
效果图
聊天室功能
发送图片
相机
发红包系列
聊天
发送表情
等
代码中还包含发送语音,但是测试发现,该功能不太成熟,而且页面上并未提供发送语音的入口。
代码——html
部分
<template><view><view class="content" @touchstart="hideDrawer"><scroll-view class="msg-list" :class="popupLayerClass" 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="(row,index) in msgList" :key="index" :id="'msg'+row.msg.id"><!-- 系统消息 --><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/chat/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></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 v-if="row.msg.type=='img'" class="bubble img" @tap="showPic(row.msg)"><image :src="row.msg.content.url":style="{'width': row.msg.content.w+'px','height': row.msg.content.h+'px'}"></image></view><!-- 红包 --><view v-if="row.msg.type=='redEnvelope'" class="bubble red-envelope"@tap="openRedEnvelope(row.msg,index)"><image src="/static/chat/red-envelope.png"></image><view class="tis"><!-- 点击开红包 --></view><view class="blessing">{{row.msg.content.blessing}}</view></view></view><!-- 右-头像 --><view class="right"><image :src="row.msg.userinfo.face"></image></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.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></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 v-if="row.msg.type=='img'" class="bubble img" @tap="showPic(row.msg)"><image :src="row.msg.content.url":style="{'width': row.msg.content.w+'px','height': row.msg.content.h+'px'}"></image></view><!-- 红包 --><view v-if="row.msg.type=='redEnvelope'" class="bubble red-envelope"@tap="openRedEnvelope(row.msg,index)"><image src="/static/chat/red-envelope.png"></image><view class="tis"><!-- 点击开红包 --></view><view class="blessing">{{row.msg.content.blessing}}</view></view></view></view></block></view></scroll-view></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)"><!-- https://zhoukaiwen.com/img/icon/emojj1/1.png --><image mode="widthFix" :src="'https://zhoukaiwen.com/img/icon/emojj1/'+em.url"></image></view></swiper-item></swiper><!-- 更多功能 相册-拍照-红包 --><view class="more-layer" :class="{hidden:hideMore}"><view class="list"><view class="box" @tap="chooseImage"><view class="icon tupian2"></view></view><view class="box" @tap="camera"><view class="icon paizhao"></view></view><view class="box" @tap="handRedEnvelopes"><view class="icon hongbao"></view></view></view></view></view><!-- 底部输入栏 --><view class="input-box cu-bar tabbar" :class="popupLayerClass" @touchmove.stop.prevent="discard"><!-- H5下不能录音,输入栏布局改动一下 --><!-- #ifndef H5 --><view class="voice"><view class="icon" :class="isVoice?'jianpan':'yuyin'" @tap="switchVoice"></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="text-mode" :class="isVoice?'hidden':''"><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 class="windows" :class="windowsState"><!-- 遮罩层 --><view class="mask" @touchmove.stop.prevent="discard" @tap="closeRedEnvelope"></view><view class="layer" @touchmove.stop.prevent="discard"><view class="open-redenvelope"><view class="top"><view class="close-btn"><view class="icon close" @tap="closeRedEnvelope"></view></view><image src="https://zhoukaiwen.com/img/qdpz/face/face_1.jpg"></image></view><view class="from">来自{{redenvelopeData.from}}</view><view class="blessing">{{redenvelopeData.blessing}}</view><view class="money">{{redenvelopeData.money}}</view><view class="showDetails" @tap="toDetails(redenvelopeData.rid)">查看领取详情 <view class="icon to"></view></view></view></view></view></view>
</template>
代码——js
部分
<script>export default {data() {return {//文字消息textMsg: '',//消息列表isHistoryLoading: false,scrollAnimation: false,scrollTop: 0,scrollToView: '',msgList: [],msgImgList: [],myuid: 0,//录音相关参数// #ifndef H5//H5不能录音RECORDER: uni.getRecorderManager(),// #endifisVoice: 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,emojiList: [[{"url": "1.png",alt: "[微笑]"}, {"url": "2.png",alt: "[生气]"}, {"url": "3.png",alt: "[坏笑]"}, {"url": "4.png",alt: "[难受]"}, {"url": "5.png",alt: "[困]"}, {"url": "6.png",alt: "[偷看]"}, {"url": "7.png",alt: "[难过]"}, {"url": "8.png",alt: "[斜眼]"}, {"url": "9.png",alt: "[委屈]"}, {"url": "10.png",alt: "[害羞]"}, {"url": "11.png",alt: "[裂开]"}, {"url": "12.png",alt: "[偷笑]"}, {"url": "13.png",alt: "[痛苦]"}, {"url": "14.png",alt: "[白眼]"}, {"url": "15.png",alt: "[丑]"}, {"url": "16.png",alt: "[哇哇哭]"}, {"url": "17.png",alt: "[笑嘻嘻]"}, {"url": "18.png",alt: "[盯着你]"}, {"url": "19.png",alt: "[啊哈]"}, {"url": "20.png",alt: "[吃瓜]"}, {"url": "21.png",alt: "[哦吼]"}, {"url": "22.png",alt: "[哭死]"}, {"url": "23.png",alt: "[打脸]"}, {"url": "24.png",alt: "[斗鸡眼]"}],[{"url": "25.png",alt: "[发呆]"}, {"url": "26.png",alt: "[憨笑]"}, {"url": "27.png",alt: "[无语]"}, {"url": "28.png",alt: "[鸡贼]"}, {"url": "29.png",alt: "[大无语]"}, {"url": "30.png",alt: "[哭吐了]"}, {"url": "31.png",alt: "[呲牙笑]"}, {"url": "32.png",alt: "[奸笑]"}, {"url": "33.png",alt: "[啊啊啊]"}, {"url": "34.png",alt: "[哈嘿]"}, {"url": "35.png",alt: "[惊讶]"}, {"url": "36.png",alt: "[指你]"}, {"url": "37.png",alt: "[可爱型]"}, {"url": "38.png",alt: "[快哭了]"}, {"url": "39.png",alt: "[抠鼻屎]"}, {"url": "40.png",alt: "[酷酷]"}, {"url": "41.png",alt: "[笑汗]"}, {"url": "42.png",alt: "[算命]"}, {"url": "43.png",alt: "[红脸坏笑]"}, {"url": "44.png",alt: "[委屈死了]"}, {"url": "45.png",alt: "[爆炸]"}, {"url": "46.png",alt: "[吐了]"}, {"url": "47.png",alt: "[么么哒]"}, {"url": "48.png",alt: "[吐血]"}],[{"url": "49.png",alt: "[面无表情]"}, {"url": "50.png",alt: "[捂嘴吐]"}, {"url": "51.png",alt: "[斜眼看]"}, {"url": "52.png",alt: "[花痴]"}, {"url": "53.png",alt: "[被打]"}, {"url": "54.png",alt: "[瞌睡]"}, {"url": "55.png",alt: "[冥想]"}, {"url": "56.png",alt: "[俏皮]"}, {"url": "57.png",alt: "[戳手委屈]"}, {"url": "58.png",alt: "[端庄]"}, {"url": "59.png",alt: "[emmm]"}, {"url": "60.png",alt: "[欢呼]"}, {"url": "61.png",alt: "[笑哭了]"}, {"url": "62.png",alt: "[抱抱]"}, {"url": "63.png",alt: "[闭眼笑]"}, {"url": "64.png",alt: "[捂嘴微笑]"}, {"url": "65.png",alt: "[笑哭2]"}, {"url": "66.png",alt: "[笑嘻嘻]"}, {"url": "67.png",alt: "[笑露齿]"}, {"url": "68.png",alt: "[阴脸笑]"}, {"url": "69.png",alt: "[问号脸]"}, {"url": "70.png",alt: "[拜拜]"}, {"url": "71.png",alt: "[难受2]"}, {"url": "72.png",alt: "[傻笑2]"}],[{"url": "73.png",alt: "[爆炸2]"}, {"url": "74.png",alt: "[二哈]"}, {"url": "75.png",alt: "[二哈吐舌]"}, {"url": "76.png",alt: "[狗狗笑哭]"}, {"url": "77.png",alt: "[狗狗绿帽]"}, {"url": "78.png",alt: "[狗狗张嘴]"}, {"url": "79.png",alt: "[狗狗绿扇]"}, {"url": "80.png",alt: "[狗狗]"}, {"url": "81.png",alt: "[猫咪]"}, {"url": "82.png",alt: "[牛啊]"}, {"url": "83.png",alt: "[爱心]"}, {"url": "84.png",alt: "[心裂开]"}, {"url": "85.png",alt: "[玫瑰花]"}, {"url": "86.png",alt: "[枯萎]"}, {"url": "87.png",alt: "[棒]"}, {"url": "88.png",alt: "[差]"}, {"url": "89.png",alt: "[红药]"}, {"url": "90.png",alt: "[绿药]"}, {"url": "91.png",alt: "[抱拳]"}, {"url": "92.png",alt: "[ok]"}, {"url": "93.png",alt: "[pk]"}, {"url": "94.png",alt: "[绿帽子]"}, {"url": "95.png",alt: "[菜刀]"}]],//表情图片图床名称 ,由于我上传的第三方图床名称会有改变,所以有此数据来做对应,您实际应用中应该不需要onlineEmoji: {"1.png": "1.png","2.png": "2.png","3.png": "3.png","4.png": "4.png","5.png": "5.png","6.png": "6.png","7.png": "7.png","8.png": "8.png","9.png": "9.png","10.png": "10.png","11.png": "11.png","12.png": "12.png","13.png": "13.png","14.png": "14.png","15.png": "15.png","16.png": "16.png","17.png": "17.png","18.png": "18.png","19.png": "19.png","20.png": "20.png","21.png": "21.png","22.png": "22.png","23.png": "23.png","24.png": "24.png","25.png": "25.png","26.png": "26.png","27.png": "27.png","28.png": "28.png","29.png": "29.png","30.png": "30.png","31.png": "31.png","32.png": "32.png","33.png": "33.png","34.png": "34.png","35.png": "35.png","36.png": "36.png","37.png": "37.png","38.png": "38.png","39.png": "39.png","40.png": "40.png","41.png": "41.png","42.png": "42.png","43.png": "43.png","44.png": "44.png","45.png": "45.png","46.png": "46.png","47.png": "47.png","48.png": "48.png","49.png": "49.png","50.png": "50.png","51.png": "51.png","52.png": "52.png","53.png": "53.png","54.png": "54.png","55.png": "55.png","56.png": "56.png","57.png": "57.png","58.png": "58.png","59.png": "59.png","60.png": "60.png","61.png": "61.png","62.png": "62.png","63.png": "63.png","64.png": "64.png","65.png": "65.png","66.png": "66.png","67.png": "67.png","68.png": "68.png","69.png": "69.png","70.png": "70.png","71.png": "71.png","72.png": "72.png","73.png": "73.png","74.png": "74.png","75.png": "75.png","76.png": "76.png","77.png": "77.png","78.png": "78.png","79.png": "79.png","80.png": "80.png","81.png": "81.png","82.png": "82.png","83.png": "83.png","84.png": "84.png","85.png": "85.png","86.png": "86.png","87.png": "87.png","88.png": "88.png","89.png": "89.png","90.png": "90.png","91.png": "91.png","92.png": "92.png","93.png": "93","94.png": "94.png","95.png": "95.png"},//红包相关参数windowsState: '',redenvelopeData: {rid: null, //红包IDfrom: null,face: null,blessing: null,money: null}};},onLoad(option) {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;//模板借由本地缓存实现发红包效果,实际应用中请不要使用此方法。//uni.getStorage({key: 'redEnvelopeData',success: (res) => {console.log(res.data);let nowDate = new Date();let lastid = this.msgList[this.msgList.length - 1].msg.id;lastid++;let row = {type: "user",msg: {id: lastid,type: "redEnvelope",time: nowDate.getHours() + ":" + nowDate.getMinutes(),userinfo: {uid: 0,username: "大黑哥",face: "https://zhoukaiwen.com/img/kevinLogo.png"},content: {blessing: res.data.blessing,rid: Math.floor(Math.random() * 1000 + 1),isReceived: false}}};this.screenMsg(row);uni.removeStorage({key: 'redEnvelopeData'});}});},methods: {// 接受消息(筛选处理)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);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.$nextTick(function() {// 滚动到底this.scrollToView = 'msg' + msg.msg.id});},//触发滑动到顶部(加载历史信息记录)loadHistory(e) {if (this.isHistoryLoading) {return;}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: "https://zhoukaiwen.com/img/kevinLogo.png"},content: {text: "web前端开发该怎么学习?"}}},{type: "user",msg: {id: 2,type: "text",time: "12:57",userinfo: {uid: 1,username: "售后客服008",face: "https://zhoukaiwen.com/img/qdpz/face/face_2.jpg"},content: {text: "按照基本路线,从html、css、js三大基础开始,然后ajax、vue进阶学习,最后学习小程序、node、react。"}}},{type: "user",msg: {id: 3,type: "voice",time: "12:59",userinfo: {uid: 1,username: "售后客服008",face: "https://zhoukaiwen.com/img/qdpz/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: "https://zhoukaiwen.com/img/kevinLogo.png"},content: {url: "/static/voice/2.mp3",length: "00:06"}}},]// 获取消息中的图片,并处理显示尺寸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]);}//这段代码很重要,不然每次加载历史数据都会跳到顶部this.$nextTick(function() {this.scrollToView = 'msg' + Viewid; //跳转上次的第一行信息位置this.$nextTick(function() {this.scrollAnimation = true; //恢复滚动动画});});this.isHistoryLoading = false;}, 1000)},// 加载初始页面消息getMsgList() {// 消息列表let list = [{type: "system",msg: {id: 0,type: "text",content: {text: "欢迎进入Kevin聊天室"}}},{type: "user",msg: {id: 1,type: "text",time: "12:56",userinfo: {uid: 0,username: "大黑哥",face: "https://zhoukaiwen.com/img/kevinLogo.png"},content: {text: "web前端开发该怎么学习?"}}},{type: "user",msg: {id: 2,type: "text",time: "12:57",userinfo: {uid: 1,username: "售后客服008",face: "https://zhoukaiwen.com/img/qdpz/face/face_2.jpg"},content: {text: "按照基本路线,从html、css、js三大基础开始,然后ajax、vue进阶学习,最后学习小程序、node、react。"}}},{type: "user",msg: {id: 3,type: "voice",time: "12:59",userinfo: {uid: 1,username: "售后客服008",face: "https://zhoukaiwen.com/img/qdpz/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: "https://zhoukaiwen.com/img/kevinLogo.png"},content: {url: "/static/voice/2.mp3",length: "00:06"}}},{type: "user",msg: {id: 5,type: "img",time: "13:05",userinfo: {uid: 0,username: "大黑哥",face: "https://zhoukaiwen.com/img/kevinLogo.png"},content: {url: "https://zhoukaiwen.com/img/Design/logo/psketch3.png",w: 200,h: 200}}},{type: "user",msg: {id: 6,type: "img",time: "12:59",userinfo: {uid: 1,username: "售后客服008",face: "https://zhoukaiwen.com/img/qdpz/face/face_2.jpg"},content: {url: "https://zhoukaiwen.com/img/Design/pc/ybss_jt.png",w: 1920,h: 1080}}},{type: "system",msg: {id: 7,type: "text",content: {text: "欢迎进入Kevin聊天室"}}},{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: "https://zhoukaiwen.com/img/kevinLogo.png"},content: {blessing: "恭喜发财,大吉大利,万事如意",rid: 0,isReceived: false}}},{type: "user",msg: {id: 11,type: "redEnvelope",time: "12:56",userinfo: {uid: 1,username: "售后客服008",face: "https://zhoukaiwen.com/img/qdpz/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);}}this.msgList = list;// 滚动到底部this.$nextTick(function() {//进入页面滚动到底部this.scrollTop = 9999;this.$nextTick(function() {this.scrollAnimation = true;});});},//处理图片尺寸,如果不处理宽高,新进入页面加载图片时候会闪setPicSize(content) {// 让图片最长边等于设置的最大长度,短边等比例缩小,图片控件真实改变,区别于aspectFit方式。let maxW = uni.upx2px(350); //350是定义消息图片最大宽度let maxH = uni.upx2px(350); //350是定义消息图片最大高度if (content.w > maxW || content.h > maxH) {let scale = content.w / content.h;content.w = scale > 1 ? maxW : maxH * scale;content.h = scale > 1 ? maxW / scale : maxH;}return content;},//更多功能(点击+弹出) 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);},// 选择图片发送chooseImage() {this.getImage('album');},//拍照发送camera() {this.getImage('camera');},//发红包handRedEnvelopes() {uni.navigateTo({url: 'hand/hand'});this.hideDrawer();},//选照片 or 拍照getImage(type) {this.hideDrawer();uni.chooseImage({sourceType: [type],sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有success: (res) => {for (let i = 0; i < res.tempFilePaths.length; i++) {uni.getImageInfo({src: res.tempFilePaths[i],success: (image) => {console.log(image.width);console.log(image.height);let msg = {url: res.tempFilePaths[i],w: image.width,h: image.height};this.sendMsg(msg, 'img');}});}}});},// 选择表情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) => {console.log("str: " + str);console.log("index: " + index);console.log("item: " + item);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://zhoukaiwen.com/img/icon/emojj1/'let imgstr = '<img width="32rpx" src="' + onlinePath + this.onlineEmoji[EM.url] +'">';console.log("imgstr: " + imgstr);return imgstr;}}}});return '<div style="display: flex;align-items: center;word-wrap:break-word;">' + replacedStr + '</div>';},// 发送消息sendMsg(content, type) {//实际应用中,此处应该提交长连接,模板仅做本地处理。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: 0,username: "大黑哥",face: "https://zhoukaiwen.com/img/kevinLogo.png"},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: "https://zhoukaiwen.com/img/qdpz/face/face_2.jpg"},content: content}}// 本地模拟发送消息this.screenMsg(msg);}, 3000)},// 添加文字消息到列表addTextMsg(msg) {this.msgList.push(msg);},// 添加语音消息到列表addVoiceMsg(msg) {this.msgList.push(msg);},// 添加图片消息到列表addImgMsg(msg) {msg.msg.content = this.setPicSize(msg.msg.content);this.msgImgList.push(msg.msg.content.url);this.msgList.push(msg);},addRedEnvelopeMsg(msg) {this.msgList.push(msg);},// 添加系统文字消息到列表addSystemTextMsg(msg) {this.msgList.push(msg);},// 添加系统红包消息到列表addSystemRedEnvelopeMsg(msg) {this.msgList.push(msg);},// 打开红包openRedEnvelope(msg, index) {let rid = msg.content.rid;uni.showLoading({title: '加载中...'});console.log("index: " + index);//模拟请求服务器效果setTimeout(() => {//加载数据if (rid == 0) {this.redenvelopeData = {rid: 0, //红包IDfrom: "大黑哥",face: "https://zhoukaiwen.com/img/qdpz/face/face.jpg",blessing: "恭喜发财,大吉大利",money: "已领完"}} else {this.redenvelopeData = {rid: 1, //红包IDfrom: "售后客服008",face: "https://zhoukaiwen.com/img/qdpz/face/face_2.jpg",blessing: "恭喜发财",money: "0.01"}if (!msg.content.isReceived) {// {type:"system",msg:{id:8,type:"redEnvelope",content:{text:"你领取了售后客服008的红包"}}},this.sendSystemMsg({text: "你领取了" + (msg.userinfo.uid == this.myuid ? "自己" : msg.userinfo.username) + "的红包"}, 'redEnvelope');console.log("this.msgList[index]: " + JSON.stringify(this.msgList[index]));this.msgList[index].msg.content.isReceived = true;}}uni.hideLoading();this.windowsState = 'show';}, 200)},// 关闭红包弹窗closeRedEnvelope() {this.windowsState = 'hide';setTimeout(() => {this.windowsState = '';}, 200)},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)},//领取详情toDetails(rid) {uni.navigateTo({url: 'details/details?rid=' + rid})},// 预览图片showPic(msg) {uni.previewImage({indicator: "none",current: msg.content.url,urls: this.msgImgList});},// 播放语音playVoice(msg) {this.playMsgid = msg.id;this.AUDIO.src = msg.content.url;this.$nextTick(function() {this.AUDIO.play();});},// 录音开始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));let msg = {length: 0,url: 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');} else {console.log('取消发送录音');}this.willStop = false;},// 切换语音/文字输入switchVoice() {this.hideDrawer();this.isVoice = this.isVoice ? false : true;},discard() {return;}}}
</script>
代码——其他
部分
其他部分代码包含:图标
+css
+红包系列
等。由于内容过多,暂不提供,如有需要,可以评论区留言,我会发送到指定邮箱。
完成!!!多多积累,多多收获!!!
相关文章:
uniapp——实现聊天室功能——技能提升
这里写目录标题 效果图聊天室功能代码——html部分代码——js部分代码——其他部分 首先声明一点:下面的内容是从一个uniapp的程序中摘录的,并非本人所写,先做记录,以免后续遇到相似需求抓耳挠腮。 效果图 聊天室功能 发送图片 …...
脚本:用python实现五子棋
文章目录 1. 语言2. 效果3. 脚本4. 解读5. FutureReference 1. 语言 Python 无环境配置、无库安装。 2. 效果 以第一回合为例 玩家X 玩家0 3. 脚本 class GomokuGame:def __init__(self, board_size15):self.board_size board_sizeself.board [[ for _ in range(board_…...
Java-华为真题-预定酒店
需求: 放暑假了,小王决定到某旅游景点游玩,他在网上搜索到了各种价位的酒店(长度为n的数组A),他的心理价位是x元,请帮他筛选出k个最接近x元的酒店(n>k>0)ÿ…...
win10 自带虚拟机软件 虚拟CentOS系统
win10 下使用需要虚拟一个系统,不需要额外安装VMware、Virtual box等软件。使用win10 自带虚拟机软件即可 步骤1 确保启动Hyper-V 功能启用 控制面板 -> 程序 -> 启用或关闭Windows功能 步骤 2 创建虚拟机 2.1 打开Typer-V 2.2 创建虚拟机 2.2.1 操作 -&g…...
【深度学习】 Python 和 NumPy 系列教程(十):NumPy详解:2、数组操作(索引和切片、形状操作、转置操作、拼接操作)
目录 一、前言 二、实验环境 三、NumPy 0、多维数组对象(ndarray) 1. 多维数组的属性 1、创建数组 2、数组操作 1. 索引和切片 a. 索引 b. 切片 2. 形状操作 a. 获取数组形状 b. 改变数组形状 c. 展平数组 3. 转置操作 a. 使用.T属性 b…...
3D视觉测量:复现Gocator的间隙面差
文章目录 0. 测试效果1. Gocator实现基本内容1.1 实现步骤1.2 参数说明 2. 未作 or TODO3. 开发说明 目标:使用C PCL复现Gocator中的间隙面差前置说明:因为没有拿到Gocator中用到的原始数据,仅是拿到与之类似的数据,因此最后测试的…...
文献综述怎么写?(以利用Zotero的文献管理软件为例)
文章目录 文章内容总结前言一. 利用文献管理软件建立文献库1. 创建文献分类2. 在论文库中搜索关键词并导入到文献管理软件中以web of science 为例以 IEEE Xplore为例 二、 导出文献、阅读摘要并记录关键字三、寻找一两篇本方向的文献综述,分析借鉴其文章结构四、写…...
中尺度混凝土二维有限元求解——运行弯曲、运行光盘、运行比较、运行半圆形(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Python文件存读取
Python文件存读取 想整理一下存读取函数,方便以后直接调用。 读取 1、读xls、csv、xlsx到dataframe 这段代码非常好用,这个函数直接把各种格式文件给汇总了 def readDataFile(readPath): # readPath: 数据文件的地址和文件名try:if (readPath[-4:]…...
SpringBoot整合Easy-ES实现对ES操作
请确保已有可用的ES,若没有,请移步:Docker安装部署ElasticSearch(ES) 新建SpringBoot项目 这里是用的springboot版本是2.6.0 引入依赖 <!-- 排除springboot中内置的es依赖,以防和easy-es中的依赖冲突--><…...
让NPU跑起来迅为RK3588开发板设置交叉编译器
让NPU跑起来迅为RK3588开发板设置交叉编译器 编译器下载地址是网盘资料“iTOP-3588 开发板\02_【iTOP-RK3588 开发板】开发资料 \12_NPU 使用配套资料\03_编译所需工具\Linux”。 拷贝 gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.gz 到 Ubuntu 的/opt/tool_ch…...
yyyy-MM-dd‘T‘HH:mm:ssZ的T是什么意思?为什么要用单引号引着
背景 Java里的日期格式,通常我们看到 yyyy-MM-ddTHH:mm:ssZ 的写法是什么意思? 尤其这个T作为分隔符为什么左右有单引号,这个单引号会打印出来吗? 这个Z又是什么意思?是时区吗?如果是时区,输…...
SIT1145AQ收发器芯片CAN FD Passive功能详解(摘自官网)
芯力特在SIT1145AQ/FD版本中加入了CAN FD Passive功能,CAN FD Passive功能简单来说就是SIT1145AQ/FD在休眠或待机模式下屏蔽总线上的CAN FD报文。 为什么需要CAN FD Passive功能呢? SIT1145AQ的特定帧唤醒只支持CAN报文,当SIT1145AQ休眠模式…...
安卓毕业设计各种app项目,Android毕设设计,Android课程设计,毕业论文
作为一位从事软件开发多年的专业人士,您积累了丰富的经验和技能,解决了许多不同类型的问题。除了开发原创项目,您还愿意分享您的知识,指导实习生和在校生。这种乐于助人的行为对于行业的发展和新一代软件开发者的成长都起着积极的…...
c++下的ros通信(cmake的报错问题多)
1.自定义msg 这里的自定义msg和python的其实是一样的: 首先在src目录下 catkin_create_pkg car_interfaces rospy roscpp std_msgs message_runtime message_generation然后新建一个msg文件夹,然后建立相应的msg文件,接着就可以修改编译所需…...
测试必备 | 测试工程师必知的Linux命令有哪些?
在日常的测试工作中,涉及到测试环境搭建及通过查看日志来定位相关问题时经常会用到Linux,在测试工程师的面试中也经常会有笔试或面试的题目来考查测试人员对Linux的熟悉程度,这里分享下测试工程师需知的 Linux 命令有哪些。 Linux 作为一种常…...
成集云 | 药师帮集成英克ERP接口 | 解决方案
源系统成集云目标系统 业务背景 药师帮是一家专注于医药行业的电商平台,提供医药产品在线采购、销售和物流等一站式服务。药师帮致力于用数字化赋能院外医药市场的参与者,包括药企、药品分销商、药店及基层医疗机构,努力以安全高效…...
ICPC 2022 网络赛 d ( 数位dp + 二分
#include<bits/stdc.h> using namespace std; using VI vector<int>; using ll long long; const int mod 998244353;ll n; int d[100]; int dp[60][40][40][2]; set<int> s; //枚举数位,枚举这一位余数是几 //每一位的限制, int d…...
透视俄乌网络战之二:Conti勒索软件集团(下)
透视俄乌网络战之一:数据擦除软件 透视俄乌网络战之二:Conti勒索软件集团(上) Conti勒索软件集团(下) 1. 管理面板源代码2. Pony凭证窃取恶意软件3. TTPs4. Conti Locker v2源代码5. Conti团伙培训材料6. T…...
网络安全深入学习第一课——热门框架漏洞(RCE-命令执行)
文章目录 一、RCE二、命令执行/注入-概述三、命令执行-常见函数四、PHP命令执行-常见函数1、exec:2、system3、passthru4、shell_exec5、反引号 backquote 五、PHP命令执行-常见函数总结六、命令执行漏洞成因七、命令执行漏洞利用条件八、命令执行漏洞分类1、代码层…...
应用在电子体温计中的国产温度传感芯片
电子体温计由温度传感芯片,液晶显示器,纽扣电池,专用集成电路及其他电子元器件组成。能快速准确地测量人体体温,与传统的水银玻璃体温计相比,具有读数方便,测量时间短,测量精度高,能…...
JVM 虚拟机 ----> Java 内存模型(JMM)
文章目录 Java 内存模型(JMM)一、运行时数据区域划分二、程序计数器(Program Counter Register)计数器的作用 三、Java 虚拟机栈(VM Stack)四、本地方法栈(Native Method Stack)五、…...
指针-字符串替换
任务描述 从标准输入读入数据,每行中最多包含一个字符串 “_xy_”,且除了字符串“_xy_”外,输入数据中不包括下划线字符,请将输入行中的 “_xy_” 替换为 “_ab_”, 在标准输出上输出替换后的结果;若没有进行过满足条…...
docker 网络(单机环境)
文章目录 深入理解 Namespace什么是NamespaceNamespace当中的 Network Namespace Libcontainerdocker 网络基础创建两个命名空间创建网络接口 veth pair命名空间添加 veth 接口为 veth 接口分配 IP启动 veth 接口相互 ping bridge 网络搭建网络环境查看docker0 网桥创建网桥 br…...
14、二叉树的morris遍历等
统计热词 有一个包含100亿个URL的大文件,假设每个URL占用64B,请找出其中所有重复的URL 【补充】 某搜索公司一天的用户搜索词汇是海量的(百亿数据量),请设计一种求出每天热门Top100 词汇的可行办法 多个小文件的大根堆,然后把每…...
BeanFactory与ApplicationContext
BeanFactory与ApplicationContext的区别 使用Alt Ctrl U查看java类图 什么是BeanFactory接口 他是ApplicationContext的父接口他才是Spring 的核心容器,主要的ApplicationContext功能的实现都间接通过BeanFactory接口来实现 在ApplicationContext类中方法的实现是…...
【计算机网络】 粘包问题
文章目录 为什么会产生粘包问题?解决办法先发包大小再发包内容代码示例 为什么会产生粘包问题? tcp是数据流传输,是一种没有边界的,可以合并的传输数据方式。合并就要能拆开,拆不开就是粘包。 解决办法 设置标志位&a…...
valgrind massif 详解(内存分配释放分析)
参考 https://valgrind.org/docs/manual/ms-manual.html 使用格式 valgrind --toolmassif [--massif-opts] prog [prog-args]目的 记录每一次的malloc, free; 概念: malloc申请内存, 实际分配内存(字节对齐, 分配器的记录头, 等等原因) 对内存进行分析, 优化, 以达到资源…...
使用命令行创建一个vue项目卡住不动如何解决
问题 在使用命令去创建一个vue项目, 出现下面卡住不动的一个状态。 解决方案一 首先先ctrlc停止进入创建好的项目文件手动输入npm install 、npm run dev如果npm run dev 的时候 出现 ‘vite’ 相关的错误查看node版本是否是最新的稳定版本node -v查看安装源是否…...
七天学会C语言-第一天(C语言基本语句)
一、固定格式 这个是C程序的基本框架,需要记住!!! #include<stdio.h>int main(){return 0; }二、printf 语句 简单输出一句C程序: #include<stdio.h> int main(){printf("大家好,&quo…...
网站开发接单平台/竞价培训
作用域闭包递归 (自己调自己) 简单闭包 function parent() {var x parentfunction son() {var x sonreturn x}return son() } parent() //son闭包写法: /*写法一*/ function parent() {var x parentreturn function son() {var x …...
网站开发一般流程/做网站建设的公司
LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统。本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一。负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡…...
wordpress采集csdn/福州seo推广
现在电脑已经是生活必不可缺少的电子产品之一,无论学习还是办公都不可缺少。笔记本电脑给我们带来方便高效的办公,最大的优点就是携带方便,无论外出都可以自如办公,在笔记本的使用上,很多电脑小白不懂一些笔记本使用技…...
智能网站建设推荐/百度推广手机登录
作者:朱金灿 来源:http://blog.csdn.net/clever101前几天同事叫我帮忙看一个问题,问题是这样的:MFC程序中创建了一个停靠栏窗口之后窗口跟视图窗口贴在一块是透明的,然后程序退出时崩溃。经过查看堆栈,发现…...
云南网站建设优选平台/长沙seo关键词
1 .责任分配矩阵展示项目资源在各个()中的任务分配? A.工作包 B.项目活动 4 .在虚拟团队环境中,()规划变得日益重要? A.资源 B.沟通 参考答案 A B...
公司网站建设公/2021时事政治热点50条
你现在是不是想学 Java,但又很迷茫,不知从何下手,不知道该从哪里学起,不知道需要学些什么?那么看完这篇文章你会有一种豁然开朗的感觉。学习 Java 的第一步,就是制定自己的 Java 学习路线。对 Java 主要的重…...