您的当前位置:首页>全部文章>文章详情

【Vue】uniapp(vue3)+node.js+websocket(实现实时通信效果)

CrazyPanda发表于:2023-12-05 19:48:47浏览:303次TAG:



概要

uniapp基于vue3,小程序的聊天功能

项目是基于node.js服务器搭建的简易双向通信网页,实现了实时更新在线人数以及用户间即时通讯的功能。


整体架构流程

后台接口代码

1、首先我们可以通过Express 应用程序生成器快速搭建一个后台框架。(这快可以参考官网

2、服务端
/**
 * WebSocket模块
 */
 
const { WebSocketServer } = require('ws');
// 端口号
const server = new WebSocketServer({ port: 8082 });
// 存储聊天信息
let chatInfo = []
// 存储在线用户
let onlineUser = []
server.on("connection", (socket, req, res) => {
  console.log('链接成功');
  // 用户注册
  // 获取当前连接websocket的用户uid
  let uid = req.url.substr(1)
  socket["id"] = uid
  let index = onlineUser.findIndex(val => val.id === uid)
 
  if (index == -1) {
    onlineUser.push(socket)
  }
 
  // 从客户端接收消息
  socket.on("message", (data) => {
    // 将收到的消息转成对象格式
    let msg = data.toString()
    let message = JSON.parse(msg)
    // console.log(message, '123456789');
    // 通知当前用户消息发送成功
    socket.send(JSON.stringify({
      message: message.message,
      types: message.types,
      toid: message.fid,
      tip: 0
    }))
    // 存储当前用户发送的数据
    chatInfo.push({
      from: uid,
      to: message.fid,
      message: message.message,
      types: message.types
    })
    // console.log(onlineUser);
 
    // 根据fid匹配到接收消息的用户并发送通知
    onlineUser.forEach(item => {
      if (item.id == message.fid) {
        console.log("发送消息成功");
        item.send(JSON.stringify({
          message: message.message,
          types: message.types,
          from: uid,
          tip: 1
        }))
      }
    })
  });
 
 
 
  // 连接关闭后清除在线用户
  socket.on('close', res => {
    let newUserArr = []
    let newUserIds = []
    for (let i = 0; i < onlineUser.length; i++) {
      let val = onlineUser[i]
      if (val.id !== uid) {
        newUserArr.push(val)
        newUserIds.push(val.id)
      }
    }
    onlineUser = newUserArr
    let User = JSON.stringify({
      onlineUser: newUserIds,
      type: 'users'
    })
    onlineUser.forEach((client) => client.websocket.send(User))
    console.log('用户离开了');
  })
 
  socket.send(JSON.stringify({
    msg: '连接已建立',
    type: 'system'
  }))
});
3、用户端(好友列表)

3.1、首先在onLoad生命周期中判断是否链接后端

onLoad(() => {
		uni.connectSocket({
			url: 'ws://localhost:8082/' + store.userid,
			success: data => {
				console.log(data);
			}
		})
		uni.onSocketOpen(function(res) {
			socketOpen = true;
			for (var i = 0; i < socketMsgQueue.length; i++) {
				sendSocketMessage(socketMsgQueue[i]);
			}
			socketMsgQueue = [];
		});
		uni.onSocketClose(function(res) {
			console.log('WebSocket 已关闭!');
		});
		uni.onSocketError(function(res) {
			console.log('WebSocket连接打开失败,请检查!');
		});
		receiveSocketMsg()
	})
3.2、获取好友列表
// 获取好友数据
	const getFrineds = () => {
		uni.request({
			url: 'http://localhost:3461/wsb/getfreind',
			data: {
				uid: store.userid,
			},
			method: 'POST',
			success: (data) => {
				let code = data.data.code
				if (code == 200) {
					let res = data.data.query
					if (res.length > 0) {
						for (let i = 0; i < res.length; i++) {
							List.value.push(res[i])
						}
					}
					List.value = myfun.paixu(List.value, 'lastTime', 0)
					// console.log(List.value);
					// 获取好友内信息
					for (let i = 0; i < List.value.length; i++) {
						getLastMsg(List.value, i)
						getunread(List.value, i)
					}
				} else {
					uni.showToast({
						title: "服务器出错啦!",
						icon: "none",
						duration: 2000
					})
				}
			}
		})
	}
3.3、好友列表实时通信
// socket聊天数据接收
	const receiveSocketMsg = () => {
		uni.onSocketMessage(function(res) {
			console.log('收到服务器内容123:' + res.data);
			const msggs = JSON.parse(res.data)
			let nmsg = ''
			if (msggs.types == 0) {
				nmsg = msggs.message
			} else if (msggs.types == 1) {
				nmsg = '[图片]'
			} else if (msggs.types == 2) {
				nmsg = '[音频]'
			} else if (msggs.types == 3) {
				nmsg = '[位置]'
			}
			for (let i = 0; i < List.value.length; i++) {
				if (List.value[i].id == msggs.from) {
					let e = List.value[i]
					e.lastTime = new Date()
					e.msg = nmsg
					e.tip++
					// 删除原来数据项
					List.value.splice(i, 1)
					// 新消息插入到最顶部
					List.value.unshift(e)
					// 更改最后一条消息时间
					uni.request({
						url: 'http://localhost:3461/wsb/updatetime',
						data: {
							uid: store.userid,
							fid: msggs.from
						},
						method: 'POST',
						success: (data) => {
							let code = data.data.code
							if (code == 200) {} else {
								uni.showToast({
									title: "服务器出错啦!",
									icon: "none",
									duration: 2000
								})
							}
						}
					})
				}
			}
		});
	}
效果:
e58a3946cb8047d69fd4760343a0ca41.gif

如果还没有打开就会是未读消息,消息数加一,就在我们的头像右上角,大家可以看到小红数,当打开聊天框后小红数才会消失

4、用户端(聊天页面)

4.1、用户端uniapp是一个基于vue3的框架,首先在onMounted生命周期中判断是否链接后端

onMounted(() => {
		uni.connectSocket({
			// 接口
			url: 'ws://localhost:8082/' + user.uid,
			success: data => {
				console.log(data);
			}
		});
		uni.onSocketOpen(function(res) {
			socketOpen = true;
			for (var i = 0; i < socketMsgQueue.length; i++) {
				sendSocketMessage(socketMsgQueue[i]);
			}
			socketMsgQueue = [];
		});
		uni.onSocketClose(function(res) {
			console.log('WebSocket 已关闭!');
		});
		uni.onSocketError(function(res) {
			console.log('WebSocket连接打开失败,请检查!');
		});
		receiveSocketMsg()
	})

4.2、获取好友聊天内容,把内容存储到List数组中

const getList = () => {
		uni.request({
			url: 'http://localhost:3461/wsb/msg',
			data: {
				uid: user.uid,
				fid: friend.fid
			},
			method: 'POST',
			success: (data) => {
				let code = data.data.code
				if (code == 200) {
					let msg = data.data.query
					msg.reverse()
					if (msg.length > 0) {
						// console.log(msg);
						let oldtime = msg[0].time
						let imgarr = []
						for (var i = 1; i < msg.length; i++) {
							// 时间间隔
							if (i < msg.length - 1) {
								let t = myfun.spaceTime(oldtime, msg[i].time)
								if (t) {
									oldtime = t
								}
								msg[i].time = t
							}
							// 匹配最大时间
							if (msg[i].time > oldTime.value) {
								oldTime.value = msg[i].time
							}
							// 补充图片地址
							if (msg[i].types == 1) {
								msg[i].message = 'http://localhost:3461/' + msg[i].message
								imgarr.push(msg[i].message)
							}
							// json字符串还原
							if (msg[i].types == 3) {
								msg[i].message = JSON.parse(msg[i].message)
							}
							// List.value.unshift(msg[i])
							nextTick(() => {
								scrollToView.value = 'msg' + List.value[i - 1].id
							})
						}
						// 两个数组拼接
						List.value = msg.concat(List.value)
						getimg.value = imgarr.concat(getimg.value)
					}
				} else {
					uni.showToast({
						title: "服务器出错啦!",
						icon: "none",
						duration: 2000
					})
				}
			}
		})
	}

4.3、把你要发送的内容发给服务器,然后在看服务器是否接收

// 聊天数据发送给服务端(socket)
	const sendSocket = (e) => {
		uni.request({
			url: 'http://localhost:3461/wsb/insertMsg',
			data: JSON.stringify({
				msg: e.message,
				types: e.types,
				uid: user.uid,
				fid: friend.fid
			}),
			method: 'POST',
			success: (data) => {
				let code = data.data.code
				if (code == 200) {
					// console.log("添加成功");
				} else {
					uni.showToast({
						title: "服务器出错啦!",
						icon: "none",
						duration: 2000
					})
				}
			}
		})
		// json字符串还原
		if(e.types == 2){
			e.message = JSON.parse(e.message)
		}
		uni.sendSocketMessage({
			data: JSON.stringify({
				message: e.message,
				types: e.types,
				uid: user.uid,
				fid: friend.fid
			})
		});
	}

4.4、服务器接收到消息后存储到用户消息里面,然后发送给客户端

// socket聊天数据接收
	const receiveSocketMsg = () => {
		uni.onSocketMessage(function(res) {
			console.log('收到服务器内容123:' + res.data);
			const msggs = JSON.parse(res.data)
			if (msggs.from == friend.fid && msggs.tip==1) {
				let len = List.value.length;
				let nowTime = new Date()
				// 时间间隔
				let t = myfun.spaceTime(oldTime.value, nowTime)
				if (t) {
					oldTime.value = t
				}
				// 判断是否加ip
				if (msggs.types == 1) {
					msggs.message = 'http://localhost:3461' + msggs.message
				}
				if ( msggs.types == 2) {
					msggs.message.voice = 'http://localhost:3461' + msggs.message.voice
				}
				nowTime = t
				let data = {
					fromId: msggs.from, //发送者的id
					imgurl: friend.fimgurl,
					time: nowTime,
					message: msggs.message,
					types: msggs.types,
					id: len
				};
				List.value.push(data)
				// 图片
				if (msggs.types == 1) {
					getimg.value.push(msggs.message)
				}
				nextTick(() => {
					scrollToView.value = 'msg' + len
				})
			}
 
		});
	}
表情效果:

e58a3946cb8047d69fd4760343a0ca41.gif

表情可以看作是线上文字交流的重要补充。相较于面对面的沟通,人们在线上文字对话时较难感知对方的状态或情绪,而发送表情刚好可以弥补这一缺憾。

语音效果:

e58a3946cb8047d69fd4760343a0ca41.gif

语音可以帮助我们快速回复对方,在发语音时,我们可以通过声音来表达自己的想法和情感,与对方进行交流和互动。

完整代码

完成效果:

e58a3946cb8047d69fd4760343a0ca41.gif

服务端

/**
 * WebSocket模块
 */
 
const { WebSocketServer } = require('ws');
// 端口号
const server = new WebSocketServer({ port: 8082 });
// 存储聊天信息
let chatInfo = []
// 存储在线用户
let onlineUser = []
server.on("connection", (socket, req, res) => {
  console.log('链接成功');
  // 用户注册
  // 获取当前连接websocket的用户uid
  let uid = req.url.substr(1)
  socket["id"] = uid
  let index = onlineUser.findIndex(val => val.id === uid)
 
  if (index == -1) {
    onlineUser.push(socket)
  }
 
  // 从客户端接收消息
  socket.on("message", (data) => {
    // 将收到的消息转成对象格式
    let msg = data.toString()
    let message = JSON.parse(msg)
    // console.log(message, '123456789');
    // 通知当前用户消息发送成功
    socket.send(JSON.stringify({
      message: message.message,
      types: message.types,
      toid: message.fid,
      tip: 0
    }))
    // 存储当前用户发送的数据
    chatInfo.push({
      from: uid,
      to: message.fid,
      message: message.message,
      types: message.types
    })
    // console.log(onlineUser);
 
    // 根据fid匹配到接收消息的用户并发送通知
    onlineUser.forEach(item => {
      if (item.id == message.fid) {
        console.log("发送消息成功");
        item.send(JSON.stringify({
          message: message.message,
          types: message.types,
          from: uid,
          tip: 1
        }))
      }
    })
  });
 
 
 
  // 连接关闭后清除在线用户
  socket.on('close', res => {
    let newUserArr = []
    let newUserIds = []
    for (let i = 0; i < onlineUser.length; i++) {
      let val = onlineUser[i]
      if (val.id !== uid) {
        newUserArr.push(val)
        newUserIds.push(val.id)
      }
    }
    onlineUser = newUserArr
    let User = JSON.stringify({
      onlineUser: newUserIds,
      type: 'users'
    })
    onlineUser.forEach((client) => client.websocket.send(User))
    console.log('用户离开了');
  })
 
  socket.send(JSON.stringify({
    msg: '连接已建立',
    type: 'system'
  }))
});

客户端:

<!-- 详情页 -->
<template>
<view>
<view>
<view @click="back">
<image src="../../../static/icons/返回.png"></image>
</view>
<view>
<view>
{{friend.fname}}
</view>
</view>
</view>
<scroll-view scroll-y="true" scroll-with-animation="true" :scroll-into-view="scrollToView">
<view :style="{paddingBottom:inputh + 'px'}">
<view v-for="(item,index) in List" :key="index" :id="'msg'+item.id">
<view v-if="item.time != ''">
{{Datas(item.time)}}
</view>
<!-- 左用户 -->
<view class="msg-m msg-left" v-if="item.fromId != user.uid">
<image :src="item.imgurl"></image>
<!-- 文字 -->
<view v-if="item.types == 0">
<view>
{{item.message}}
</view>
</view>
<!-- 图片 -->
<view v-if="item.types == 1">
<image :src="item.message" mode="widthFix" @tap="previewImg(item.message)">
</image>
</view>
<!-- 音频 -->
<view v-if="item.types == 2">
<view class="msg-text voice" :style="{width:item.message.time*4+'px'}"
@tap="playVoice(item.message.voice)">
<image src="../../../static/submit/yp.png" mode=""></image>
{{item.message.time}}″
</view>
</view>
<!-- 定位 -->
<view v-if="item.types == 3">
<view @click="openLocations(item.message)">
<view>
{{item.message.name}}
</view>
<view>
{{item.message.address}}
</view>
<image src="../../../static/submit/dt.jpg" mode="aspectFill"></image>
<!-- <map :longitude="item.message.longitude" :latitude="item.message.latitude"
:markers="covers(item.message)"></map> -->
</view>
</view>
</view>
<!-- 右用户 -->
<view class="msg-m msg-right" v-if="item.fromId == user.uid">
<image :src="item.imgurl"></image>
<view v-if="item.types == 0">
<view>
{{item.message}}
</view>
</view>
<!-- 照片 -->
<view v-if="item.types == 1">
<image :src="item.message" mode="widthFix" @tap="previewImg(item.message)">
</image>
</view>
<!-- 音频 -->
<view v-if="item.types == 2">
<view class="msg-text voice" :style="{width:item.message.time*4+'px'}"
@tap="playVoice(item.message.voice)">
{{item.message.time}}″
<image src="../../../static/submit/yp.png" mode=""></image>
</view>
</view>
<!-- 定位 -->
<view v-if="item.types == 3">
<view @click="openLocations(item.message)">
<view>
{{item.message.name}}
</view>
<view>
{{item.message.address}}
</view>
<!-- <map :longitude="item.message.longitude" :latitude="item.message.latitude"
:markers="covers(item.message)"></map> -->
<image src="../../../static/submit/dt.jpg" mode="aspectFill"></image>
</view>
</view>
</view>
</view>
</view>
<view></view>
</scroll-view>
<!-- 底部导航栏 -->
<submit></submit>
</view>
</template>
 
<script setup>
import submit from '@/pages/buyCar/chat/submit.vue'
import {
onLoad,
} from '@dcloudio/uni-app'
import datas from '@/pages/buyCar/datas.js'
import myfun from '@/pages/buyCar/myfun.js'
// pinia
import {
useStore
} from '@/store/users.js'
// 获取用户id
const store = useStore()
// 用户信息
const user = reactive({
uid: '',
uimgurl: '',
uname: ''
})
// 获取用户信息
const getUser = () => {
user.uid = store.userid
user.uimgurl = store.uimgurl
user.uname = store.uname
}
import {
nextTick,
onMounted,
reactive,
ref
} from "vue";
// 音频
const innerAudioContext = uni.createInnerAudioContext();
// 总数据
const List = ref([]) //获取所以消息
const getimg = ref([]) //获取所以图片
const oldTime = ref(0) //时间差
const scrollToView = ref('') // 让他定位到最后一条数据
const inputh = ref(72)
// websocket信息存储
var socketOpen = false;
var socketMsgQueue = [];
// 对方的信息(前面的页面传递过来的信息)
const friend = reactive({
fid: '',
fimgurl: '',
fname: ''
})
// 进页面渲染(获取聊天数据)
onLoad((options) => {
friend.fid = options.id
friend.fimgurl = options.img
friend.fname = options.name
getUser()
getList()
})
onMounted(() => {
uni.connectSocket({
// 接口
url: 'ws://localhost:8082/' + user.uid,
success: data => {
console.log(data);
}
});
uni.onSocketOpen(function(res) {
socketOpen = true;
for (var i = 0; i < socketMsgQueue.length; i++) {
sendSocketMessage(socketMsgQueue[i]);
}
socketMsgQueue = [];
});
uni.onSocketClose(function(res) {
console.log('WebSocket 已关闭!');
});
uni.onSocketError(function(res) {
console.log('WebSocket连接打开失败,请检查!');
});
receiveSocketMsg()
})
// const getList1 = () => {
//  let msg = datas.message()
//  for (let i = 0; i < msg.length; i++) {
//  msg[i].imgurl = '../../../static/' + msg[i].imgurl;
//  // 时间间隔
//  if (i < msg.length - 1) {
//  let t = myfun.spaceTime(new Date(), msg[i].time)
//  if (t) {
//  oldTime = t
//  }
//  msg[i].time = t
//  }
 
//  // 补充图片地址
//  if (msg[i].types == 1) {
//  msg[i].message = '../../../static/' + msg[i].message;
//  getimg.value.unshift(msg[i].message)
//  }
//  List.value.unshift(msg[i])
//  nextTick(() => {
//  scrollToView.value = 'msg' + List.value[i - 1].tip
//  })
//  }
//  // 让他定位到最后一条数据
 
//  // console.log(List.value);
//  // console.log(scrollToView.value);
// }
 
 
const getList = () => {
uni.request({
url: 'http://localhost:3461/wsb/msg',
data: {
uid: user.uid,
fid: friend.fid
},
method: 'POST',
success: (data) => {
let code = data.data.code
if (code == 200) {
let msg = data.data.query
msg.reverse()
if (msg.length > 0) {
// console.log(msg);
let oldtime = msg[0].time
let imgarr = []
for (var i = 1; i < msg.length; i++) {
// 时间间隔
if (i < msg.length - 1) {
let t = myfun.spaceTime(oldtime, msg[i].time)
if (t) {
oldtime = t
}
msg[i].time = t
}
// 匹配最大时间
if (msg[i].time > oldTime.value) {
oldTime.value = msg[i].time
}
// 补充图片地址
if (msg[i].types == 1) {
msg[i].message = 'http://localhost:3461/' + msg[i].message
imgarr.push(msg[i].message)
}
// json字符串还原
if (msg[i].types == 3) {
msg[i].message = JSON.parse(msg[i].message)
}
// List.value.unshift(msg[i])
nextTick(() => {
scrollToView.value = 'msg' + List.value[i - 1].id
})
}
// 两个数组拼接
List.value = msg.concat(List.value)
getimg.value = imgarr.concat(getimg.value)
}
} else {
uni.showToast({
title: "服务器出错啦!",
icon: "none",
duration: 2000
})
}
}
})
}
 
// 接收输入框的内容
// uni.$on('inputs', (e) => {
//  // console.log(e);
//  let len = List.value.length;
//  let nowTime = new Date()
//  // 时间间隔
//  let t = myfun.spaceTime(new Date(), nowTime)
//  if (t) {
//  oldTime = t
//  }
//  nowTime = t
//  let data = {
//  _id: "b",
//  imgurl: "../../../static/07.jpg",
//  time: nowTime,
//  message: e.message,
//  types: e.types,
//  tip: len
//  };
//  List.value.push(data)
//  nextTick(() => {
//  scrollToView.value = 'msg' + len
//  })
//  if (e.types == 1) {
//  getimg.value.push(e.message)
//  }
// })
uni.$on('inputs', (e) => {
getInput(e, user.uid, user.uimgurl, 0)
console.log(e);
})
// 接收消息
const getInput = (e, id, img, tip) => {
// tip=0表示自己发的。tip=1表示对方发的

// socket提交
// 文字或者地图
if (e.types == 0 || e.types == 3) {
sendSocket(e)
}
// 图片
if (e.types == 1) {
getimg.value.push(e.message)
// 提交图片处理
const uploadTask = uni.uploadFile({
url: 'http://localhost:3461/files/upload', //仅为示例,非真实的接口地址
filePath: e.message,
name: 'file',
formData: {
url: 'picture',
name: new Date().getTime() + user.uid + Math.ceil(Math.random() * 10),
},
success: (uploadFileRes) => {
// console.log(uploadFileRes);
let data = {
message: uploadFileRes.data,
types: e.types
}
sendSocket(data)
// let path =  uploadFileRes.data
// img.value.push('http://localhost:3461/'+path)
// console.log(uploadFileRes.data);
},
});

uploadTask.onProgressUpdate((res) => {
// console.log('上传进度' + res.progress);
// console.log('已经上传的数据长度' + res.totalBytesSent);
// console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);

// 测试条件,取消上传任务。
// if (res.progress > 50) {
//  uploadTask.abort();
// }
});
}
// 音频
if (e.types == 2) {
// 提交音频处理
const uploadTask = uni.uploadFile({
url: 'http://localhost:3461/files/upload', //仅为示例,非真实的接口地址
filePath: e.message.voice,
name: 'file',
formData: {
url: 'voice',
name: new Date().getTime() + user.uid + Math.ceil(Math.random() * 10),
},
success: (uploadFileRes) => {
// console.log(uploadFileRes);
let data = {
// json转json字符串
message: JSON.stringify({
voice:uploadFileRes.data,
time:e.message.time
}),
types: e.types
}
sendSocket(data)
// let path =  uploadFileRes.data
// img.value.push('http://localhost:3461/'+path)
// console.log(uploadFileRes.data);
},
});

uploadTask.onProgressUpdate((res) => {
// console.log('上传进度' + res.progress);
// console.log('已经上传的数据长度' + res.totalBytesSent);
// console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);

// 测试条件,取消上传任务。
// if (res.progress > 50) {
//  uploadTask.abort();
// }
});
}
// 前端处理
let len = List.value.length;
let nowTime = new Date()
// 时间间隔
let t = myfun.spaceTime(oldTime.value, nowTime)
if (t) {
oldTime.value = t
}
nowTime = t
// json字符串还原
if(e.types == 3){
e.message = JSON.parse(e.message)
}
let data = {
fromId: id,
imgurl: img,
time: nowTime,
message: e.message,
types: e.types,
id: len
};
List.value.push(data)
nextTick(() => {
scrollToView.value = 'msg' + len
})

 
}
// 聊天数据发送给服务端(socket)
const sendSocket = (e) => {
uni.request({
url: 'http://localhost:3461/wsb/insertMsg',
data: JSON.stringify({
msg: e.message,
types: e.types,
uid: user.uid,
fid: friend.fid
}),
method: 'POST',
success: (data) => {
let code = data.data.code
if (code == 200) {
// console.log("添加成功");
} else {
uni.showToast({
title: "服务器出错啦!",
icon: "none",
duration: 2000
})
}
}
})
// json字符串还原
if(e.types == 2){
e.message = JSON.parse(e.message)
}
uni.sendSocketMessage({
data: JSON.stringify({
message: e.message,
types: e.types,
uid: user.uid,
fid: friend.fid
})
});


}
// socket聊天数据接收
const receiveSocketMsg = () => {
uni.onSocketMessage(function(res) {
console.log('收到服务器内容123:' + res.data);
const msggs = JSON.parse(res.data)
if (msggs.from == friend.fid && msggs.tip==1) {
let len = List.value.length;
let nowTime = new Date()
// 时间间隔
let t = myfun.spaceTime(oldTime.value, nowTime)
if (t) {
oldTime.value = t
}
// 判断是否加ip
if (msggs.types == 1) {
msggs.message = 'http://localhost:3461' + msggs.message
}
if ( msggs.types == 2) {
msggs.message.voice = 'http://localhost:3461' + msggs.message.voice
}
nowTime = t
let data = {
fromId: msggs.from, //发送者的id
imgurl: friend.fimgurl,
time: nowTime,
message: msggs.message,
types: msggs.types,
id: len
};
List.value.push(data)
// 图片
if (msggs.types == 1) {
getimg.value.push(msggs.message)
}
nextTick(() => {
scrollToView.value = 'msg' + len
})
}
 
});
}
// 输入框的高度
uni.$on('heights', (e) => {
// console.log(e);
inputh.value = e.msg
// console.log(inputh .value);
goBottom()
})
// 滚动到底部
const goBottom = () => {
scrollToView.value = ''
nextTick(() => {
let len = List.value.length - 1
scrollToView.value = 'msg' + List.value[len].id
})
}
// 处理时间
const Datas = (data) => {
return myfun.dateTime(data)
}
// 返回上一页
const back = () => {
uni.navigateBack({
delta: 1
});
}
 
// 图片预览
const previewImg = (e) => {
let index = 0;
// 点击图片后通过for循环查找出第几张是该点击得图片
for (let i = 0; i < getimg.value.length; i++) {
if (getimg.value[i] == e) {
index = i;
}
}
// uni中的api,图片预览
uni.previewImage({
current: index, // 点击图片看第几张
urls: getimg.value,
longPressActions: {
itemList: ['发送给朋友', '保存图片', '收藏'],
success: function(data) {
console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
},
fail: function(err) {
console.log(err.errMsg);
}
}
});
}
// 音频播放
const playVoice = (e) => {
innerAudioContext.src = e;
innerAudioContext.play();
}
// 获取位置
const covers = (e) => {
let map = [{
latitude: e.latitude,
longitude: e.longitude,
iconPath: '../../../static/submit/dw1.png'
}]
return map
}
// 导航定位
const openLocations = (e) => {
uni.openLocation({
latitude: e.latitude,
longitude: e.longitude,
name: e.name,
address: e.address,
success: function() {
console.log('success');
}
});
}
</script>
 
<style>
.chatroom {
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.1);
 
.top-bar {
position: fixed;
z-index: 1001;
top: 0;
left: 0;
width: 100%;
height: 120rpx;
padding-top: var(--status-bar-height);
background: #fff;
border-bottom: 1rpx solid #ccc;
 
.top-bar-left {
float: left;
padding-left: 5rpx;
 
image {
margin-top: 10rpx;
width: 68rpx;
height: 88rpx;
border-radius: 16rpx;
}
}
 
.top-bar-center {
float: auto;
text-align: center;
 
.title {
font-size: 40rpx;
color: #000;
line-height: 120rpx;
}
}
}
}
 
.chat {
height: 100%;
 
.padbt {
width: 100%;
height: 40rpx;
}
 
.chat-main {
padding-left: 32rpx;
padding-right: 32rpx;
padding-top: 160rpx;
display: flex;
flex-direction: column;
}
 
.chat-ls {
.chat-time {
font-size: 24rpx;
color: rgba(39, 40, 50, 0.3);
line-height: 34rpx;
padding: 25rpx 0;
text-align: center;
}
 
.msg-m {
display: flex;
padding: 20rpx 0;
 
.user-img {
flex: none;
width: 80rpx;
height: 80rpx;
border-radius: 20rpx;
}
 
.massage {
flex: none;
max-width: 480rpx;
}
 
.msg-text {
font-size: 32rpx;
color: rgba(39, 40, 50, 1);
line-height: 44rpx;
padding: 18rpx 24rpx;
}
 
.msg-img {
max-width: 400rpx;
border-radius: 20rpx;
}
 
.msg-map {
background-color: #fff;
width: 460rpx;
height: 284rpx;
overflow: hidden;
 
.map-name {
font-size: 32rpx;
color: rgba(39, 40, 50, 1);
line-height: 44rpx;
padding: 18rpx 24rpx 0 24rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
 
.map-address {
font-size: 26rpx;
color: rgba(39, 40, 50, 0.4);
padding: 0rpx 24rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
 
.map {
padding-top: 8rpx;
width: 460rpx;
height: 190rpx;
}
}
 
.voice {
min-width: 80rpx;
max-width: 400rpx;
}
 
.voice-img {
width: 28rpx;
height: 36rpx;
}
 
}
 
.msg-left {
flex-direction: row;
 
.msg-text {
margin-left: 16rpx;
background: #fff;
border-radius: 0rpx 20rpx 20rpx 20rpx;
}
 
.msg-img {
margin-left: 16rpx;
}
 
.msg-map {
margin-left: 16rpx;
border-radius: 0rpx 20rpx 20rpx 20rpx;
}
 
.voice {
width: 200rpx;
text-align: left;
}
 
.voice-img {
width: 28rpx;
height: 36rpx;
padding-top: 4rpx;
}
 
}
 
.msg-right {
flex-direction: row-reverse;
 
.msg-text {
margin-right: 16rpx;
background: #fff260;
border-radius: 20rpx 0rpx 20rpx 20rpx;
}
 
.msg-img {
margin-right: 16rpx;
}
 
.msg-map {
margin-right: 16rpx;
border-radius: 20rpx 0rpx 20rpx 20rpx;
}
 
.voice {
width: 200rpx;
text-align: right;
}
 
.voice-img {
float: right;
transform: rotate(180deg);
width: 28rpx;
height: 36rpx;
padding-top: 4rpx;
}
}
}
}
</style>

猜你喜欢

【Vue】Antd Pro Vue的使用(一)—— 安装及运行
前言Ant Design Pro 是一个企业级中后台前端/设计解决方案,致力于在设计规范和基础组件的基础上,继续向上构建,提炼出典型模板/业务组件/配套设计资源,进一步提升企业级中后台产品设计研发过程中的『用户』和『设计者』的体验。AntDesignVue与react版本有几乎相同的布局AntDesignPro React版本:开箱即用的中台前端/设计解决方案 - Ant Design ProAntDesign组件:Ant Design - 一套企业级 UI 设计语言和 React 组件库Ant
发表于:2024-04-20 浏览:305 TAG:
【Vue】Vue中对axios进行封装的最佳实践
vue是当前前端开发中最常用的框架之一,而ajax请求又是前端开发中非常关键的一环。为了方便开发者使用,vue社区中出现了许多对ajax请求库axios进行封装的实践。本文将介绍vue中对axios进行封装的最佳实践,帮助您更好地理解如何在vue项目中使用axios。封装axios在Vue项目中,我们需要把axios进行封装以方便使用。这里介绍一个标准的axios封装:import&nbsp;axios&nbsp;from&nbsp;&#39;axios&#39; import&nbsp;sto
发表于:2024-04-29 浏览:318 TAG:
【Vue】Antd Pro Vue的使用(五)—— 多文件上传回显问题
需求: 多文件上传 ,上传的时候绑定fileList回显问题: 上传成功了,也拿到了后台返回的数据,但是onchang监听的时候,file的状态一直是uploading原因:onchange 只触发了一次解决: 使用单文件上传时@change事件会至少触发两次,一次file.status=uploading,最后一次要么是done或者error,handleUpload1(info)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(info
发表于:2024-05-06 浏览:349 TAG:
【Vue】Antd Pro Vue的使用(三)—— table列表的使用
用了几天ant design pro vue,发现vue2真的不是很好用,各种写法好麻烦。还有研究组件时,一定要看低版本的组件,高版本都是vue3的,并不适用。vue2版本组件位置:https://1x.antdv.com/components/alert-cn/ 作为后台管理端,用到最多的就是table列表,官网给的有预览但是自己上手的时候有事另外一回事了,首先就是接口请求的数据结果,官网并没有介绍接口应该返回什么样的数据结构,导致接口成功请求到数据,但table就是无法正常显示,最终参考de
发表于:2024-04-26 浏览:385 TAG:
【Vue】vue3比vue2好在哪里
vue 3 优于 vue 2 的关键优势包括:性能提升:响应式系统重写,优化更新速度虚拟 dom 优化,提高渲染效率代码组织和可维护性:组合式 api,提升可维护性teleport 和 suspense,提高代码灵活性和可读性开发者体验:更好的调试工具,简化调试过程typescript 2.7 支持,增强代码提示和类型检查Vue 3 与 Vue 2 的优势对比核心性能提升响应式系统重写:Vue 3 引入 Reactivity API,优化了响应式系统的性能,提升了更新速度。虚拟 DOM 优化:采
发表于:2024-04-28 浏览:321 TAG:
【Vue】Vue定义全局变量的方法
在Vue项目中我们需要使用许多的变量来维护数据的流向和状态,这些变量可以是本地变量、组件变量、父子组件变量等,但这些变量都是有局限性的。在一些场景中,可能需要在多个组件中共享某个变量,此时全局变量就派上了用场。定义全局变量的方法1. 使用Vue.prototype定义全局变量通过在 vue 的原型上定义属性,可以在所有组件中访问该属性。在main.js定义全局变量// main.jsVue.prototype.baseUrl = &quot;https://www.example.com/api
发表于:2024-04-22 浏览:359 TAG:
【VUE】如何查看前端的vue项目是vue2还是vue3项目
1. 检查package.json文件在项目的根目录下,打开package.json文件,查找dependencies或devDependencies部分中的vue条目。版本号将告诉你是Vue 2还是Vue 3。例如:Vue 2.x: &quot;vue&quot;: &quot;^2.x.x&quot;Vue 3.x: &quot;vue&quot;: &quot;^3.x.x&quot;2. 使用Vue Devtools如果项目正在运行,并且你已经安装了Vue Devtools(Vue开发者
发表于:2024-03-11 浏览:419 TAG:
【Vue】Vue3 开发实战分享——打印插件 Print.js 的使用(Vue3 + Nodejs + Print.js 实战)以及 el-table 与 el-pagination 的深入使用(下)
文章目录📋前情回顾&amp;前言🎯关于 el-table1️⃣获取每行对应的内容数据2️⃣行内数据判断处理(过滤)3️⃣对表格内容的索引🧩项目中延申使用🎯关于 el-pagination1️⃣显示总条数与分页展示2️⃣跳转页和页码样式3️⃣设置为中文🧩项目中延申使用📝最后📋前情回顾&amp;前言上一篇文章(Vue3 开发实战分享——打印插件 Print.js 的使用(Vue3 + Nodejs + Print.js 实战)以及 el-table 与 el-pagination 的深
发表于:2023-12-10 浏览:242 TAG:
【Vue】Antd Pro Vue的使用(二)—— 全局配置及登录
1. 默认语言设置&nbsp;Antd Pro Vue安装好之后,默认使用的是英文,我们需要把它设置为中文简体。找到/src/core/bootstrap.js文件,把最后一行 en-US 修改为 zh-CN,然后一定要清除浏览器缓存,修改才能生效修改后修改后的页面2. 请求服务端接口Antd Pro Vue封装好的有请求方法,在/src/api/文件夹,我们把自己的接口写到这里面就可以任意调用。Antd Pro Vue安装好之后,默认使用的是mock数据,我们要使用自己的接口,要把mock去掉
发表于:2024-04-25 浏览:469 TAG:
【Vue】Vue刷新页面的7中方法总结
vue使用中经常会用到刷新当前页面,下面总结一下几种常用的刷新方法,供大家参考。1、 使用 location.reload() 方法进行页面刷新使用&nbsp;location.reload()&nbsp;方法可以简单地实现当前页面的刷新,这个方法会重新加载当前页面,类似于用户点击浏览器的刷新按钮。在 Vue 中,可以将该方法绑定到 Vue 实例上,比如在 Vue 的 methods 中添加如下的方法:methods:&nbsp;{ &nbsp;&nbsp;refresh()&nbsp;{ &amp;n
发表于:2024-05-07 浏览:418 TAG: