金歌 发表于 2025-4-14 08:26:30

vue3+nodeJs+webSocket实现聊天功能

利用websocket 来实现聊天功能,紧张是通过以下几点来实现的
一、利用nodejs来实现后端接口
     1、起首举行初始化
        
const { WebSocketServer } = require("ws"); //引入WebSocketServer模块
const Websocket = require("ws");
const WebSocket = require("ws"); //引入WebSocket模块

const onLineList = []; //定义一个在线用户列表

// 我们的port是8090
const wss = new WebSocket.Server({ port: 8080 });

// 如果有ws就代表初始化成功
if (wss) {
    console.log("ws初始化成功");2、举行连接 --- 设置进入的欢迎语
    const user = new URL(request.url, `http://${request.headers.host}`).searchParams.get('user');
    console.log(`${user} 已连接`);
    let welCome = JSON.stringify({
      type: "tips",
      content: "欢迎加入聊天室,现在我们开始畅聊吧"
    });

    ws.send(welCome, { binary: false }); 2)举行注册消息的变乱
      
ws.on("message", async function message(data) {
      const message = JSON.parse(data); //将收到的数据解析为JSON格式
      switch (
      message.type //根据消息类型进行不同的处理
      ) {
      case "init":
          if (message.userId) {
            //如果消息中包含用户id
            // 为当前的用户ws链接绑定 用户id,用于用户断开连接时,改变用户状态
            ws.userId = message.userId;
            // 上线
            keepLatestOnlineList("onLine", message);
          }
          break;
      case "message":
          wss.clients.forEach(client => {
            //遍历所有客户端
            if (!message.timestamp) {
            message.timestamp = new Date().toISOString();
            }
            if (client.readyState === Websocket.OPEN) {
            //如果客户端处于打开状态
            client.send(JSON.stringify(message), { binary: false }); //发送消息
            }
          });
          break;
      default:
          break;
      }
    }); 
3)被动断开说明用户已经脱离网站了,维护在线的列表
    ws.on("close", function() {
      keepLatestOnlineList("close", { userId: ws.userId });
    });

function keepLatestOnlineList(type, message) {
    let index = onLineList.findIndex(item => item.userId === message.userId); //在在线列表中查找用户
    switch (type) {
      case "onLine":
      if (index === -1) {
          //如果用户不在在线列表中,则添加用户
          onLineList.push({ userId: message.userId });
      }
      break;
      case "close":
      if (index !== -1) {
          //如果用户在在线列表中,则删除用户
          onLineList.splice(index, 1);
          console.log("有客户被断开");
      }
      break;
      default:
      break;
    }
}


二、 设置前端页面
1)模板部分
   <template>
    <div class="app">
      <div class="chat_container">
            <div class="main_content_header">实时聊天</div>
            <div class="chat-room">
                <div class="message-item" v-for="(item, index) in messageList" :key="index">
                  <div v-if="item.isSystem" class="system-message">
                        {{ formatSendTime(item.timestamp) }}
                  </div>
                  <!-- 左边通新内容 -->
                  <div class="flex-left" v-if="item.userId !== userInfo.userId">
                        <!--其他人的 头像 -->
                        <div class="avater">
                            <el-avatar src="https://picsum.photos/200/300"></el-avatar>
                        </div>
                        <div class="message-content">{{ item.content }}</div>
                  </div>
                  <div class="flex-right" v-else>
                        <!--自己的 头像 -->
                        <div class="message-content">{{ item.content }}</div>
                        <div class="avater">
                            <el-avatar>
                              <img src="../../../assets/images/avater.jpg" alt="">
                            </el-avatar>
                        </div>
                  </div>
                </div>
            </div>
            <div class="send-box">
                <el-input type="text" v-model="yourMessage" />
                <el-button type="primary" @click="sendMesage">发送</el-button>
            </div>
      </div>
    </div>
</template>
2)逻辑部分
   <script setup >
import { onMounted, ref } from 'vue'
const yourMessage = ref('')
const messageList = ref([])
let ws = null
const userInfo = ref({
    userId: ""
})

const user = ref(sessionStorage.getItem('user'))

// 发送消息
function sendMesage() {
    const timestamp = new Date().toISOString()
    if (yourMessage.value) {
      ws.send(JSON.stringify({
            type: "message",
            isSystem: true,
            userId: userInfo.value.userId,
            content: yourMessage.value,
            timestamp: timestamp, // 添加时间戳
      })
      )
      yourMessage.value = ''
    }
}

const initWebSocket = () => {
    ws = new WebSocket(`ws://localhost:4090?user=${String(user.value)}`)
    ws.onopen = () => {
      console.log('链接成功')
      ws.send(JSON.stringify({
            type: "init",
            isSystem: true,
            userId: userInfo.value.userId,
            content: '欢迎来到聊天室',
      })
      )
    }

    ws.onmessage = (e) => {
      const message = JSON.parse(e.data)
      switch (message.type) { // 根据消息类型进行不同的操作
            case "tips":
                console.log(message.content) // 如果消息类型为tips,则打印消息内容

            case 'message':
                messageList.value.push(message) // 如果消息类型为message,则将消息添加到消息列表中

                console.log(messageList.value) // 打印消息列表
                break;
            case 'onlineList':
                onlineList.value = message.onlineList // 如果消息类型为onlineList,则将在线用户列表赋值给onlineList
                break;
      }
    }
    ws.onclose = () => {
      console.log('连接关闭')
    }
    ws.onerror = () => {

    }
}

onMounted(() => {
    userInfo.value.userId = new Date().getTime().toString().slice(8)
    initWebSocket()
})
// 时间格式化
const formatSendTime = (sendTime) => {
    const now = new Date();
    const sendDate = new Date(sendTime);
    const timeDiff = now - sendDate;
    const oneDay = 24 * 60 * 60 * 1000;

    if (timeDiff < 0) {
      return "Invalid time"; // 或者其他错误处理
    }
    if (timeDiff < oneDay) {
      return "今天" + formatTime(sendDate);
    }
    return sendDate.toLocaleDateString("zh-CN") + " " + formatTime(sendDate);
};

const formatTime = (date) => {
    const hours = date.getHours().toString().padStart(2, "0");
    const minutes = date.getMinutes().toString().padStart(2, "0");
    return hours + ":" + minutes;
};
</script>
 
3)样式部分
    
<style lang='less'>
.app {
    // width: 100vw;
    // height: 100vh;
    overflow: hidden;
    // background-color: #fff;
    display: grid;
    place-items: center;

    .chat_container {
      width: 650px;
      height: 650px;
      overflow: hidden;
      background-color: #fff;
      border-radius: 8px;
    }
}

.chat-room {
    height: calc(100% - 110px);
    padding: 10px;
    overflow: auto;
    background: #000;
}

.send-box {
    border-top: 1px solid #eee;
    display: flex;
    align-items: center;
    height: 60px;

}

.message-item {
    width: 100%;
    // margin: 0 auto;
    margin-bottom: 10px;


    .flex-left {
      display: flex;
      justify-content: flex-start;

      .avater {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color: #eee;
            margin-right: 10px;

      }

      .message-content {
            min-height: 30px;
            height: 40px;
            line-height: 40px;
            background: #fff;
            border-radius: 8px;
            padding: 0 10px;
      }
    }

    .flex-right {
      display: flex;
      justify-content: flex-end;

      .avater {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color: #eee;
            margin-left: 10px;

      }

      .message-content {
            min-height: 30px;
            height: 40px;
            line-height: 40px;
            background: #fff;
            border-radius: 8px;
            padding: 0 10px;
      }
    }
}

.main_content_header {
    width: 100%;
    height: 50px;
    border-radius: 5px;
    background-color: #7de0bd;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
}

.system-message {
    font-size: 12px;
    color: #fff;
    display: flex;
    justify-content: center;
    align-items: center;
}
</style>
完备代码
nodejs 部分
   const { WebSocketServer } = require("ws"); //引入WebSocketServer模块
const Websocket = require("ws");
const WebSocket = require("ws"); //引入WebSocket模块

//定义一个在线列表

const onLineList = []

//设置端口号
const wss = new WebSocket.Server({ port: 4090 });

// 如果ws就代表初始化成功

if(wss){
console.log('WebSocket初始化成功')
}

// 进行连接

wss.on("connection", function connection(ws, request) {
//   获取对应的用户名,来进行展示连接
const user = new URL(request.url, `http://${request.headers.host}`).searchParams.get('user');
console.log(`${user} 已连接`)

//   设置欢迎语
let welCome = JSON.stringify({
    type: "tips",
    content:"欢迎回来~"
})

ws.send(welCome, { binary: false });

ws.on("error", (error) => {
    console.log("WebSocket error:", error);
})

   //注册收到消息的事件

ws.on("message", async function message(data) {
    const message = JSON.parse(data);
    switch (message.type) {
      case "init":
      if (message.userId) {
          ws.userId = message.userId;
          keepLatestOnlineList('online',message)
      }
      break;
      case "message":
      wss.clients.forEach(client => {
      //   遍历所有的客户端
          if(!message.timestamp) {
            message.timestamp = new Date().toISOString();
          }
          if (client.readyState === Websocket.OPEN) {
            //如果客户端处于打开状态
            client.send(JSON.stringify(message), { binary: false }); //发送消息
          }
      })
      break;
      default:
          break;
    }
})
ws.on("close", function() {
    keepLatestOnlineList("close", { userId: ws.userId });
});
})

function keepLatestOnlineList(type, message) {
let index = onLineList.findIndex(item => item.userId === message.userId); //在在线列表中查找用户
switch (type) {
    case "onLine":
      if (index === -1) {
      //如果用户不在在线列表中,则添加用户
      onLineList.push({ userId: message.userId });
      }
      break;
    case "close":
      if (index !== -1) {
      //如果用户在在线列表中,则删除用户
      onLineList.splice(index, 1);
      console.log("有客户被断开");
      }
      break;
    default:
      break;
}
}

完成之后的样式如下
https://i-blog.csdnimg.cn/direct/0542c356a8a84a5b92a3f6e1c9acb8aa.png 喜欢点个关注吧~

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: vue3+nodeJs+webSocket实现聊天功能