当前位置:   article > 正文

基于vue-advanced-chat组件自义定聊天(socket.io+vue2)

vue-advanced-chat

通过上一篇文章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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129

在生命周期中使用和注销:(注意:这里一定要注销,我在测试时遇到了游览器偶尔能注册连接ws,多数情况下注册不了就是这个原因)

    mounted() {
   
      socketService.connect();
    },
   unmounted() {
   
      console.log('distory');
      socketService.disconnect();
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

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
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150

1.3.2 fetchMessages

这个事件用于获取、格式化房间内的历史消息

fetchMessages({
     room, options = {
    } }) {
   
        this.$emit('show-demo-options', false);
        this.room = room;
        // 如果是第一次打开就初始化房间的参数
        if (options.reset) {
   
          this.resetMessages();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/900301
推荐阅读
相关标签
  

闽ICP备14008679号