通过上一篇文章https://blog.csdn.net/beekim/article/details/134176752?spm=1001.2014.3001.5501,
我们已经在vue-advanced-chat中替换掉原有的firebase,用socket.io简单的实现了聊天功能。
现在需要自义定该组件,改造成我们想要的样子:
先将比较重要的几块提取出来
1.重要模块
1.1 socket.io的封装
ws的几个事件是参考firebase的封装方式
socket.ts
import {
reactive } from 'vue';
import {
io, Socket } from 'socket.io-client';
console.log(import.meta.env.VITE_GLOB_CHATROBAT_URL);
//这个地址我配置在.env配置文件里面的,方便维护
const wsUrl:any = import.meta.env.VITE_GLOB_CHATROBAT_URL;
class SocketService {
socket: Socket;
state: any;
constructor() {
this.state = reactive({
id: '',//这里面可以自义定变量
});
// http://192.168.1.50:8567/
// http://140.207.154.220:8567
// ws://localhost:3333
this.socket = io(wsUrl, {
autoConnect: false, // 禁止自动连接
extraHeaders: {
'Access-Control-Allow-Origin': '*', // 设置跨域请求头
},
transports: ['websocket', 'polling', 'flashsocket'],//注意点:加上这个配置ws才可以注册到后台
});
connect() {
this.socket.connect();
console.log('socket connect');
}
disconnect() {
this.socket.disconnect();
console.log('socket disconnect');
}
/**
* @description 房间更新
* @param bn_user_name 当前登录人的邮箱 ,必传
* @param callback 回调函数
* @constructor
*/
// callback?: any
RoomUpdate(bn_user_name: string, callback?: any) {
const param = {
bn_user_name: bn_user_name,
};
console.log('ws事件room参数', param);
this.socket.emit('room_update', param);
//回调函数中传入最新获取到的值
this.socket.on('room_update', (res) => {
console.log('room列表(room_update)', res);
if (typeof res !== 'string') {
callback(res);
}
});
}
/**
* @description 房间内的消息更新
* @param roomId 房间id ,必传
* @param callback 回调函数
* @constructor
*/
MessageUpdate(roomId: string, callback?: any) {
const param = {
room_id: roomId,
};
console.log('ws事件参数', param);
this.socket.emit('room_message_update', param);
//回调函数中传入最新获取到的值
this.socket.off('room_message_update'); //避免重复订阅
this.socket.on('room_message_update', (res) => {
console.log('房间消息(room_message_update)', res);
if (typeof res !== 'string') {
callback(res);
}
});
}
/**
* @description 最后一条消息更新
* @param bn_user_name 当前登录人 邮箱 ,必传
* @param callback 回调函数
* @constructor
*/
LastMessageUpdate(bn_user_name, callback?: any) {
const param = {
bn_user_name: bn_user_name,
};
//回调函数中传入最新获取到的值
console.log('ws事件最后一条消息参数', param);
this.socket.emit('last_message_update', param);
this.socket.off('last_message_update'); //避免重复订阅
this.socket.on('last_message_update', (res) => {
console.log('最后一条消息(last_message_update)', res);
if (typeof res !== 'string') {
callback(res);
}
});
}
}
export const socketService = new SocketService();
export const socket = socketService.socket;
export const state = socketService.state;
在生命周期中使用和注销:(注意:这里一定要注销,我在测试时遇到了游览器偶尔能注册连接ws,多数情况下注册不了就是这个原因)
mounted() {
socketService.connect();
},
unmounted() {
console.log('distory');
socketService.disconnect();
},
1.2 自义定的监听事件(替换firebase方案)
1.2.1 listenMessages
监听房间内的消息
listenMessages(roomId) {
socketService.MessageUpdate(roomId, (messages) => {
if (messages) {
const rawRoom = this.rooms.find((r) => r.roomId === roomId);
if (rawRoom) {
messages.forEach((message) => {
//message是对话窗口里面的所有消息值
const formattedMessage = this.formatMessage(rawRoom, message);
const messageIndex = this.messages.findIndex((m) => m._id === message.id);
if (messageIndex === -1) {
//只有两个窗口登录人为同一个人,给某个人发消息是才会出现-1的情况
// console.log('[listenMessages] new formatted message:' + JSON.stringify(formattedMessage));
this.messages = this.messages.concat([formattedMessage]);
} else {
this.messages[messageIndex] = formattedMessage;
this.messages = [...this.messages];
}
// this.markMessagesSeen(room, message);
});
}
}
});
},
1.2.2 listenRooms
监听左侧房间list
listenRooms() {
socketService.RoomUpdate(this.currentUserId, (rooms) => {
rooms.forEach((room) => {
const foundRoom = this.rooms.find((r) => r.roomId === room.id);
if (JSON.stringify(this.rooms) !== '[]') {
//判空
if (foundRoom) {
//正在编辑的用户,该功能暂时用不上
foundRoom.typingUsers = room.typingUsers;
//最后一次更新时间
foundRoom.index = room.lastUpdated.seconds;
} else {
/**
* 更新房间,这里不是使用实时刷新的方案,因为这里要在原有的房间list上追加新房间不像追加消息那么简单,
* 需要重新获取很多信息(最后一条消息、格式化房间、房间内用户信息等)。所以这里采用的是重新加载本组件的方案。
**/
// createMessage.loading('对话窗口更新中...');
this.$emit('refresh-chat', new Date().getTime());
this.fetchMoreRooms();
}
}
});
});
},
1.2.3 listenLastMessage
监听最后一条消息,如果有消息更新,只返回更新的消息。所以在第一次进入组件时,需要从接口请求获取所有房间的最后一条消息list。
listenLastMessage() {
socketService.LastMessageUpdate(this.currentUserId, (messages) => {
messages &&
messages.forEach((message) => {
const rawRoom = this.rooms.find((r) => r.roomId === message.room_id);
if (rawRoom) {
const lastMessage = this.formatLastMessage(message, rawRoom);
const roomIndex = this.rooms.findIndex((r) => rawRoom.roomId === r.roomId);
this.rooms[roomIndex].lastMessage = lastMessage;
this.rooms = [...this.rooms];
}
});
if (this.loadingLastMessageByRoom < this.rooms.length) {
this.loadingLastMessageByRoom++;
if (this.loadingLastMessageByRoom === this.rooms.length) {
this.loadingRooms = false;
this.roomsLoadedCount = this.rooms.length;
}
}
});
},
1.3 vue-advanced-chat基础事件重构
1.3.1 fetchMoreRooms
这个事件是用于获取左侧房间list的
async fetchMoreRooms() {
// 判断是否在房间列表的最底部
if (this.endRooms && !this.startRooms) {
this.roomsLoaded = true;
return;
}
console.info('[fetchMoreRooms]Current user id:' + this.currentUserId);
console.info('[fetchMoreRooms]Rooms per page:' + this.roomsPerPage);
console.info('[fetchMoreRooms]Start rooms:' + this.startRooms);
// firebase逻辑
// const query = firestoreService.roomsQuery(this.currentUserId, this.roomsPerPage, this.startRooms);
// console.log(query);
//替换逻辑:获取所有房间room的list
let data = [];
if (this.currentUserId) {
let temp = await getChatUsersRooms({
bn_user_name: this.currentUserId });
data = temp.data;
}
// firebase逻辑
// const { data, docs } = await firestoreService.getRooms(query);
// this.incrementDbCounter('Fetch Rooms', data.length)
// console.log(data, docs);
console.log('[fetchMoreRooms]Rooms data:', data);
this.roomsLoaded = data.length === 0 || data.length < this.roomsPerPage;
if (this.startRooms) this.endRooms = this.startRooms;
// this.startRooms = docs[docs.length - 1];
this.startRooms = data[data.length - 1];
const roomUserIds = [];
//更新目前所有房间的用户
data.forEach((room) => {
room.users.forEach((userId) => {
const foundUser = this.allUsers.find((user) => user?._id === userId);
if (!foundUser && roomUserIds.indexOf(userId) === -1) {
roomUserIds.push(userId);
}
});
});
// this.incrementDbCounter('Fetch Room Users', roomUserIds.length)
let rawUsers = [];
//查询用户信息,并将所有用户的信息更新到allUsers
for (const userId of roomUserIds) {
//查询用户信息的接口
let temp = await getChatUserInfo({
id: userId });
let promise = temp.data;
rawUsers.push(promise);
}
this.allUsers = [...this.allUsers, ...rawUsers];
const roomList = {
};
//通过allUsers里面的用户id和接口返回的数据的用户id对比,更新房间list的信息
data.forEach((room) => {
roomList[room.id] = {
...room, users: [] };
room.users.forEach((userId) => {
const foundUser = this.allUsers.find((user) => user?._id === userId);
if (foundUser) roomList[room.id].users.push(foundUser);
});
});
const formattedRooms = [];
Object.keys(roomList).forEach((key) => {
const room = roomList[key];
const roomContacts = room.users.filter((user) => user._id !== this.currentUserId);
room.roomName = roomContacts.map((user) => user.username).join(', ') || '我自己';
const roomAvatar = roomContacts.length === 1 && roomContacts[0].avatar ? roomContacts[0].avatar : logoAvatar;
formattedRooms.push({
...room,
roomId: key,
avatar: roomAvatar,
index: room.lastUpdated.seconds,
lastMessage: {
content: 'Room created',
timestamp: formatTimestamp(new Date(room.lastUpdated.seconds), room.lastUpdated),
},
});
});
this.rooms = this.rooms.concat(formattedRooms);
//第一次进入获取一次最后一条消息的接口数据
if (this.currentUserId) {
getChatLastMessage({
bn_user_name: this.currentUserId }).then((messages) => {
messages.data.forEach((message) => {
const rawRoom = this.rooms.find((r) => r.roomId === message.sender_id);
const lastMessage = this.formatLastMessage(message, rawRoom);
const roomIndex = this.rooms.findIndex((r) => rawRoom.roomId === r.roomId);
this.rooms[roomIndex].lastMessage = lastMessage;
this.rooms = [...this.rooms];
if (this.loadingLastMessageByRoom < this.rooms.length) {
this.loadingLastMessageByRoom++;
if (this.loadingLastMessageByRoom === this.rooms.length) {
this.loadingRooms = false;
this.roomsLoadedCount = this.rooms.length;
}
}
});
});
}
if (!this.rooms.length) {
this.loadingRooms = false;
this.roomsLoadedCount = 0;
}
// console.info('[fetchMoreRooms]Formatted Rooms data:' + JSON.stringify(formattedRooms));
console.info('[fetchMoreRooms]Rooms Loaded count:' + this.roomsLoadedCount);
// console.info('[fetchMoreRooms]Listen users online status:' + JSON.stringify(formattedRooms));
// this.listenUsersOnlineStatus(formattedRooms);
// console.info('[fetchMoreRooms]Listen rooms:' + JSON.stringify(query));
// this.listenRooms(query);
// setTimeout(() => console.log('TOTAL', this.dbRequestCount), 2000)
//注册ws事件
this.listenRooms();
this.listenLastMessage();
},
1.3.2 fetchMessages
这个事件用于获取、格式化房间内的历史消息
fetchMessages({
room, options = {
} }) {
this.$emit('show-demo-options', false);
this.room = room;
// 如果是第一次打开就初始化房间的参数
if (options.reset) {
this.resetMessages()