uniapp——实现聊天室功能——技能提升

news2024/11/25 9:59:42

这里写目录标题

  • 效果图
  • 聊天室功能
  • 代码——`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(),
				// #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,
				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, //红包ID
					from: 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, //红包ID
							from: "大黑哥",
							face: "https://zhoukaiwen.com/img/qdpz/face/face.jpg",
							blessing: "恭喜发财,大吉大利",
							money: "已领完"
						}
					} else {
						this.redenvelopeData = {
							rid: 1, //红包ID
							from: "售后客服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+红包系列等。由于内容过多,暂不提供,如有需要,可以评论区留言,我会发送到指定邮箱。

完成!!!多多积累,多多收获!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1009563.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《C++ Primer》第3章 字符串、向量和数组(三)

参考资料&#xff1a; 《C Primer》第5版《C Primer 习题集》第5版 3.5 数组&#xff08;P101&#xff09; 数组类似于 vector &#xff0c;不同点在于数组的大小固定不变&#xff0c;在某些情况下性能较好&#xff0c;但灵活性较差。 3.5.1 定义和初始化内置数组&#xff…

一文了解袋鼠云在实时数据湖上的探索与实践

近日&#xff0c;袋鼠云大数据引擎专家郝卫亮&#xff0c;为大家带来了《袋鼠云在实时数据湖上的探索与实践》主题分享&#xff0c;帮助大家能了解到什么是实时数据湖、如何进行数据湖选型及数据平台建设数据湖的经验。 如今&#xff0c;大规模、高时效、智能化数据处理已是“…

阿里云新用户:定义,专享福利及优惠活动

随着云计算技术的快速发展&#xff0c;越来越多的个人与企业开始将业务迁移到云端。阿里云作为国内领先的云计算服务提供商&#xff0c;为新用户提供了一系列专享福利和优惠活动。本文将详细介绍阿里云新用户的定义、专享福利和优惠活动&#xff0c;助力大家轻松上云&#xff0…

OPCAE接口

目录 1 主要接口 1.1 OPCEventServer对象 1.2 OPCEventSubscription对象 1.3 OPCEventAreaBrowser对象&#xff08;可选&#xff09; 1.4 自定义接口开发注意 2 OPCEventServer 2.1 接口介绍 2.2 接口方法 3 IOPCEventServer2 3.1 接口介绍 3.2 接口方法 4 IConnect…

堆排序(Topk问题)

1.堆排序 堆排序是在堆的思想上面进行延伸的一种排序方法,原理就是利用堆的特性. 堆排序首先得建堆,这里建堆有一个原则: 升序建大堆降序建小堆 利用堆删除的思想来进行排序: 建堆和堆删除都可以使用向下调整的方法来实现 堆删除的向下调整和建堆的向上调整在堆的实现这篇博…

快速搭建超轻量级图床——Cpolar+和树洞外链

文章目录 1.前言2. 树洞外链网站搭建2.1. 树洞外链下载和安装2.2 树洞外链网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3 Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测试5.结语…

MT6785(Helio G95)安卓核心板_联发科4G高能低耗安卓主板开发板

MTK6785&#xff08;Helio G95&#xff09;安卓核心板采用八核 CPU 具有两个强大的 Arm Cortex-A76 处理器内核&#xff0c;主频高达 2.05GHz&#xff0c;外加六个 Cortex-A55 高效处理器。其强大的图形性能由 Arm Mali-G76 MC4 提供&#xff0c;速度可提升至 900MHz 。高达 10…

【高效开发工具系列】Fork版本管理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

CUDA小白 - NPP(9) 图像处理 Statistical Operations

cuda小白 原始API链接 NPP GPU架构近些年也有不少的变化&#xff0c;具体的可以参考别的博主的介绍&#xff0c;都比较详细。还有一些cuda中的专有名词的含义&#xff0c;可以参考《详解CUDA的Context、Stream、Warp、SM、SP、Kernel、Block、Grid》 常见的NppStatus&#xf…

游戏平台能否进行定制开发?

游戏平台通常可以进行定制开发&#xff0c;以满足游戏开发商或发行商的特定需求。定制开发可以确保平台更好地适应特定游戏的要求和运营策略。以下是一些常见的定制开发方面&#xff1a; 用户界面和外观&#xff1a;定制平台的用户界面和外观&#xff0c;以符合游戏公司和游戏本…

冒泡排序及其优化

前言 本文将简单介绍冒泡排序及其优化版本&#xff0c;默认从小到大顺序 什么是冒泡排序 冒泡排序是一种简单且经典的排序算法。 基本思想&#xff1a; 是通过反复交换相邻的未按顺序排列的元素&#xff0c;将最小&#xff08;或最大&#xff09;的元素逐渐“浮”到正确位置…

MongoDB差异数据对比的快速指南

MongoDB是一种非关系型数据库&#xff0c;它以灵活的 JSON-like 文档的形式存储数据&#xff0c;这种特性使其在处理大量数据和实现快速开发时更具有优势。而由于其灵活的数据模型和强大的性能&#xff0c;MongoDB 被广泛应用在各种业务场景中。随着业务的发展和数据的增长&…

浅谈STL|STL函数对象篇

一.函数对象概念 概念: 重载函数调用操作符的类&#xff0c;其对象常称为函数对象 函数对象使用重载的()时&#xff0c;行为类似函数调用&#xff0c;也叫仿函数 本质: 函数对象(仿函数)是一个类&#xff0c;不是一个函数 特点 函数对象在使用时&#xff0c;可以像普通函数那…

【RocketMQ】消息的拉取

在上一讲中&#xff0c;介绍了消息的存储&#xff0c;生产者向Broker发送消息之后&#xff0c;数据会写入到CommitLog中&#xff0c;这一讲&#xff0c;就来看一下消费者是如何从Broker拉取消息的。 RocketMQ消息的消费以组为单位&#xff0c;有两种消费模式&#xff1a; 广播…

C语言——三子棋游戏

本文目录 三子棋游戏简介三子棋游戏功能说明游戏界面 C语言代码实现多个文件共同实现game.hgame.ctest.c 三子棋游戏实现逻辑分析编写test.c 文件实现menu()函数实现game()函数打印空棋盘选手落子判断输赢实现game()函数 源代码game.hgame.ctest.c 更多C语言实战项目&#xff0…

微信会员卡开发流程

功能需求&#xff1a; 通过微信第三方平台创建的模板小程序&#xff0c;想要实现用户在小程序支付一定金额后领取会员卡&#xff0c;领取会员卡后可给用户下发一定数量的优惠券&#xff0c;并且实现用户在小程序消费享受商品折扣。 开发流程&#xff1a; 一、了解微信的3个平…

AMS爆炸来袭,上线即巅峰

1.关于首发项目Antmons(AMS)空投结果 Gate.io Startup 首发项目Antmons代币AMS于Aug15th,AM 07:00开始下单&#xff0c;24小时内下单同等对待总共有15,950人下单&#xff0c;下单总价值超过1,000万美金分发系数约为0.001640495298341。根据上线规则AMS项目认购成功&#xff0c;…

67、数据源配置 及 配置多个数据源--C3P0 数据源 和 Hikari 数据源

★ Spring Boot如何选择DataSource数据源 优先级从高到低&#xff1a; HikariCP > Tomcat pooling DataSource > Commons DBCP2 如果要使用Tomcat pooling DataSource这种池化数据源&#xff0c; 那么可以用</exclusions>这个把HikariCP 排除掉&#xff0c;然后sp…

前端html原生页面兼容多端H5和移动端适配方案

目录 图片代码最后 图片 是一个注册页面 代码 自己查看效果 注意: 单位全部用rem这样才能保证兼容性适配多端&#xff0c;px转rem转换公式 1px 1/37.5rem 所以想要20px应该对应20/37.5 0.53rem <!DOCTYPE html> <html lang"en"><head><met…

生成随机数列向量并保存到不同的log文件中

随机数列要求 长度 10元素随机每个数列中有两个相同的元素生成到100个文本中每个文本数列数量为 1000 代码 import torch import pickle import numpy as np import os import shutil import loggingdef creat_logger(log_path,logging_name,suf_name):if not os.path.exists(…