来咯来咯webSocket

在项目总目录下 设置socketServe文件夹 里面创建下面两个文件

使用的时候需要开启 node webSocket.cjs

var { Server } = require('ws');
var moment = require('moment');

const wss = new Server({
  port: 8888
});

let id = 0;
let onlineMemberList = [];
const defaultUser = 'user';

wss.on('connection', function (ws, req) {
  id++;
  ws.id = id;

  let reqUser = req.url.split('?')[1];
  let name = reqUser && reqUser.split('=')[1];
  let userName;

  if (name) {
    userName = decodeURIComponent(name);
  } else {
    userName = defaultUser + id;
  }

  const userInfo = {
    userName: userName,
    socketId: id,
    date: moment().format('MMMM Do YYYY, h:mm:ss a')
  };

  for (let i = 0; i < onlineMemberList.length; i++) {
    if (userInfo.userName === onlineMemberList[i].userName) {
      onlineMemberList[i] = userInfo;
      wss.clients.forEach(itemWs => {
        itemWs.send(JSON.stringify(onlineMemberList));
      });
      return;
    }
  }

  onlineMemberList.push(userInfo);
  wss.clients.forEach(itemWs => {
    itemWs.send(JSON.stringify(onlineMemberList));
  });

  ws.on('message', function (data) {
    console.log(data);
    const newData = JSON.parse(data);
    newData.serveDate = moment().format('MMMM Do YYYY, h:mm:ss a');

    wss.clients.forEach(itemWs => {
      itemWs.send(JSON.stringify(newData));
    });
  });

  ws.on('close', function (ev) {
    console.log('客户端断开连接');
    onlineMemberList = onlineMemberList.filter(item => {
      return item.socketId !== ws.id;
    });

    wss.clients.forEach(itemWs => {
      itemWs.send(JSON.stringify(onlineMemberList));
    });
    console.log(onlineMemberList, 'onlineMemberList');
    console.log(ws.id, 'ws.id');
  });

  ws.on('error', function (ve) {
    console.log('客户端异常');
  });
});

console.log('webSocket服务已开启,端口为:8888');
{
  "type": "module"
}

页面展示 监听消息

<template>
  <div class="chating">
    <div class="chating-wrap">
      <div class="title">聊天页面</div>
      <div class="chating-content">
        <div class="chating-body">
          <div class="chating-list">
            <ul class="chating-records" ref="chatingList">
              <div
                :key="index"
                v-for="(item, index) in chatingRecords"
              >
                <li
                  class="other"
                  v-show="item.nickName != myNickName"
                >
                  <img
                    alt="用户头像"
                    src="@/assets/logo.svg"
                  />
                  <div class="record-text-wrap">
                    <div class="nick-name">{{item.nickName}}</div>
                    <div class="record-text">{{item.message}}</div>
                  </div>
                </li>
                <li
                  class="my"
                  v-show="item.nickName == myNickName"
                >
                  <div class="record-text-wrap">
                    <!-- <div class="nick-name">迷离</div> -->
                    <div class="record-text">{{item.message}}</div>
                  </div>
                  <img
                    alt="用户头像"
                    src="@/assets/logo.svg"
                  />
                </li>
              </div>
            </ul>
          </div>
          <div class="chating-btns">
            <input
              class="input-text"
              placeholder="请输入聊天内容"
              type="text"
              v-model="text"
              @keydown.native.enter="sendData"
            />
            <button
              @click="sendData"
              class="send"
            >发送</button>
          </div>
        </div>
        <div class="chating-online-number">
          <div class="online-num">在线用户{{userList.length}}</div>
          <ul v-if="userList.length > 0">
            <li
              :key="index"
              class="user"
              v-for="(item, index) in userList"
            >
              <img
                alt="用户头像"
                src="@/assets/logo.svg"
              />
              <span>{{ item.userName }}</span>
              <span v-if="userList.includes(item)">(在线)</span>
              <span v-else>(离线)</span>
            </li>
          </ul>
          <button @click="loginOutHandler">退出群聊</button>
        </div>
      </div>
    </div>

    <div
      class="login"
      v-if="showLogin"
    >
      <div class="opacity-wrap">
        <div>
          用户名:
          <input
            class="user-name"
            v-model="userName"
          />
        </div>
        <button
          @click="loginHandler"
          class="login-btn"
        >登录</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      text: '',
      socketUrl: 'ws://localhost:8888?userName=',
      client: null,
      chatingRecords: [],
      myNickName: '',
      userName: '',
      showLogin: false,
      userList: [],
      onlineStatus: false // 添加在线状态变量,默认为 false 表示离线
    }
  },

  created() {
    console.log('created')
    // this.initChaing()
  },
  mounted() {
    console.log('mounted')
  },
  methods: {
    /* 初始化聊天,连接socket */
    initChaing() {
      let that = this
      if (window.WebSocket) {
        /* webSocket 连接服务器 */
        this.client = new WebSocket(this.socketUrl + this.myNickName)

        /* 监听客户端连接 */
        this.client.onopen = function (ev) {
          if (ev.type === 'open') {
            console.log('客户端连接socket服务')
            that.onlineStatus = true; // 更新在线状态为 true
          }
        }

        /* 监听服务端发送的消息 */
        this.client.onmessage = function (ev) {
          let data = JSON.parse(ev.data)
          /* 用户在线信息接收的是一个jsony数组 */
          if (data instanceof Array === true) {
            that.userList = data // 在线用户数量变化
          } else {
            /* 聊天信息接收的是一个json对象 */
            that.chatingRecords.push(data) // 在线用户聊天
          }
        }

        /* 监听服务端关闭 */
        this.client.onclose = function (ev) {
          console.log('socket服务已关闭')
          that.client = null // 客户端或者是服务端断开后,将webSocket实例清除
          that.onlineStatus = false; // 更新在线状态为 false
        }

        /* 监听服务端异常 */
        this.client.onerror = function () {
          if (!that.client) {
            console.log('socket服务连接失败')
          }
          that.loginOutHandler()
        }
      } else {
        alert('该浏览器不支持webSocket,请使用主流浏览器,如chrome')
      }
    },
    loginHandler() {
    this.myNickName = this.userName;
    this.showLogin = false;
    this.onlineStatus = true; // 用户登录成功后将在线状态更新为 true
    this.initChaing();
    },

    loginOutHandler() {
      this.client.close()
      this.client = null // 客户端或者是服务端断开后,将webSocket实例清除
      this.$router.push('/')
    },
    
    sendData() {
      if (!this.myNickName) {
        alert('请登录')
        this.showLogin = true
        return
      }
      if(this.text === ''){
        return
      }

      let data = {
        nickName: this.myNickName, // 将 myNickName 改为 nickName
        uid: new Date().getTime(),
        message: this.text,
        date: new Date()
      }
      if (this.client) {
        this.client.send(JSON.stringify(data))
        this.text = ''

        // 在添加新消息后,将列表容器滚动到底部
        this.$nextTick(() => {
          let chatList = this.$refs.chatingList;
          if (chatList) {
            // 计算滚动位置,考虑到边距和边框
            let scrollHeight = chatList.scrollHeight;
            let clientHeight = chatList.clientHeight;
            let offset = 54; // 考虑到可能的额外偏移量
            chatList.scrollTop = scrollHeight - clientHeight + offset;
          }
        });
      } else {
        console.log('socket服务连接失败,正在重新连接服务..')
        this.initChaing()
      }
    }
  },
  beforeDestroy() {
    this.client.close()
  }
}
</script>

<style>
.login {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  justify-content: center;
  align-items: center;
}
.opacity-wrap {
  width: 500px;
  height: 300px;
  background: #fff;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}
.user-name {
  font-size: 16px;
  padding: 5px;
  text-indent: 10px;
}
.login-btn {
  font-size: 20px;
  background: cornflowerblue;
  color: 20px;
  margin-top: 30px;
  color: #fff;
  border: none;
  outline: none;
  padding: 10px 20px;
  border-radius: 10px;
}
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.chating {
  width: calc(100vw - 200px);
  max-width: 800px;
  max-height: 600px;
  overflow-y: auto;
  border: 20px solid lightcyan;
  border-radius: 20px;
  margin: 0 auto 0;
}
.title {
  background: cornflowerblue;
  color: #fff;
  padding: 5px 0 5px;
}
.chating-content {
  width: 100%;
  display: flex;
  justify-content: space-between;
}
.chating-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  background: #f3f3f3;
}
.chating-list {
  flex: 1;
  border: 1px solid cornflowerblue;
}
.chating-records {
  padding: 10px;
  min-height: 300px;
  max-height: 600px;
  overflow-y: auto;
}
.chating-records li {
  margin-bottom: 20px;
}
.chating-records .other {
  display: flex;
  justify-content: start;
  align-items: flex-start;
}
.chating-records .my {
  display: flex;
  justify-content: flex-end;
  align-items: center;
}

.chating-records img {
  width: 36px;
  height: 36px;
  /* border-radius: 50%; */
  margin-right: 15px;
  background: rgba(228, 157, 228,0.5);
}
.chating-records .my img {
  margin-right: 0;
  margin-left: 15px;
}
.chating-records .other .record-text-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.chating-records .my .record-text-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}
.nick-name {
  font-size: 14px;
  margin-bottom: 5px;
  color: #666;
}
.record-text {
  max-width: 260px;
  text-align: left;
  font-size: 14px;
  padding: 5px;
  background: #fff;
  border-radius: 5px;
}

.chating-btns {
  background: burlywood;
  padding: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.input-text {
  font-size: 16px;
  border: none;
  outline: none;
  padding: 5px 0 5px 5px;
}
.send {
  font-size: 16px;
  border: none;
  outline: none;
  padding: 4px 15px;
  margin-left: 20px;
}

.online-num {
  font-size: 12px;
  padding-bottom: 15px;
}
.chating-online-number {
  padding: 15px;
  height: 100%;
}
.chating-online-number ul {
  list-style: none;
  margin: 0;
  padding: 0;
  min-width: 120px;
  max-height: 580px;
  overflow-y: auto;
}
.user {
  display: flex;
  justify-content: space-between;
  align-content: center;
  line-height: 20px;
  font-size: 12px;
  border-bottom: 1px solid aqua;
  padding: 10px;
  margin-bottom: 5px;
}
.user img {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  margin-right: 5px;
}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习】PromptFix:多功能AI修图

PromptFix:你来提示,我们修图 NeurIPS 2024 最近,在计算机视觉和图像处理领域,一个名为PromptFix的新项目引起了广泛关注。PromptFix是一个基于PyTorch实现的开源项目,旨在根据用户的自然语言指令,对受损或需要处理的图像进行智能修复和优化。 本文将详细介绍PromptFix…

淘宝商品详情API大揭秘:用Python开启探险之旅

淘宝&#xff0c;一个充满奇迹的丛林 在这个名为淘宝的丛林里&#xff0c;每一件商品都是一座神秘的宝藏。而我们&#xff0c;作为勇敢的探险家&#xff0c;将用Python这把瑞士军刀&#xff0c;去揭开这些宝藏的面纱。准备好了吗&#xff1f;让我们一起踏上这段奇妙的探险之旅…

【Android】名不符实的Window类

1.“名不符实”的Window类 Window 是一个窗口的概念&#xff0c;是所有视图的载体&#xff0c;不管是 Activity&#xff0c;Dialog&#xff0c;还是 Toast&#xff0c;他们的视图都是附加在 Window 上面的。例如在桌面显示一个悬浮窗&#xff0c;就需要用到 Window 来实现。Wi…

sql练习专场(一) (16-20)

第十六题&#xff1a;同时在线问题 create table sql1_16 (id int,stt string,edt string ) row format delimited fields terminated by ,; load data local inpath /home/homedata/sql_1/sql1_16.txt into table sql1_16;id stt edt 1001,2021-…

在vscode中开发运行uni-app项目

确保电脑已经安装配置好了node、vue等相关环境依赖 进行项目的创建 vue create -p dcloudio/uni-preset-vue 项目名 vue create -p dcloudio/uni-preset-vue uni-app 选择模版 这里选择【默认模版】 项目创建成功后在vscode中打开 第一次打开项目 pages.json 文件会报错&a…

多线程案例---阻塞队列

1. 阻塞队列 阻塞队列是一种特殊的队列&#xff0c;也遵守 " 先进先出 " 的原则。 阻塞队列是一种线程安全的数据结构&#xff0c;并且具有以下特性&#xff1a; 1. 当队列为满时&#xff0c;继续进行入队列操作就会阻塞&#xff0c;直到有其他线程从队列中取走元素…

【CANOE】【学习】【诊断功能】功能寻址和物理寻址

文章目录 前言一、功能寻址和物理寻址是什么&#xff1f;二、说明三、在脚本Capl里面进行使用 前言 这边文章我们将要学习和理解功能寻址和物理寻址。 一、功能寻址和物理寻址是什么&#xff1f; 可以很简单的一句话去理解&#xff1a; 物理寻址&#xff1a;是每个ECU的物理…

VisionPro —— CogIPOneImgeTool工具详解

CogIPOneImageTool工具主要用来对单张图像进行算法处理操作 CogIPOneImgeTool简介 CogIPOneImageTool 工具可完成高斯平滑、高通滤波和图像量化等基本图像处理操作。Image Processing One Image 工具编辑控件为此工具提供图形用户界面。 Image Processing Operations (图像处…

从分析Vue实例生命周期开始,剖析Vue页面跳转背后执行过程

文章目录 1.概要2.Vue实例生命周期3.生命周期函数解释4.存在父子组件情况页面执行过程5. 分析路由跳转页面执行过程6.扩展补充7.小结 1.概要 本文旨在分析Vue页面进行路由切换时&#xff0c;Vue背后的运行过程&#xff0c;旨在让大家更加清晰地明白Vue页面运行过程中钩子方法的…

43.第二阶段x86游戏实战2-提取游戏里面的lua

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

是时候用开源降低AI落地门槛了

过去三十多年&#xff0c;从Linux到KVM&#xff0c;从OpenStack到Kubernetes&#xff0c;IT领域众多关键技术都来自开源。开源技术不仅大幅降低了IT成本&#xff0c;也降低了企业技术创新的门槛。 那么&#xff0c;在生成式AI时代&#xff0c;开源能够为AI带来什么&#xff1f;…

xlwings,让excel飞起来!

excel已经成为必不可少的数据处理软件&#xff0c;几乎天天在用。python有很多支持操作excel的第三方库&#xff0c;xlwings是其中一个。 关于xlwings xlwings开源免费&#xff0c;能够非常方便的读写Excel文件中的数据&#xff0c;并且能够进行单元格格式的修改。 xlwings还…

【分布式事务】二、NET8分布式事务实践: DotNetCore.CAP 框架 、 消息队列(RabbitMQ)、 数据库(MySql、MongoDB)

介绍 [CAP]是一个用来解决微服务或者分布式系统中分布式事务问题的一个开源项目解决方案, 同样可以用来作为 EventBus 使用 github地址:https://github.com/dotnetcore/CAP官网地址: https://cap.dotnetcore.xyz/官网文档:https://cap.dotnetcore.xyz/userguide/zh/cap/id…

【论文阅读】Learning dynamic alignment via meta-filter for few-shot learning

通过元滤波器学习动态对齐以实现小样本学习 引用&#xff1a;Xu C, Fu Y, Liu C, et al. Learning dynamic alignment via meta-filter for few-shot learning[C]//Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2021: 5182-5191. 论文…

IDEA 2024使用mybatisplus插件生成代码在项目中

在IDEA 插件市场搜索“mybatisplus”插件并安装&#xff0c;安装好后重启IDEA&#xff0c;安装过程网上很多教程&#xff0c;这里略过&#xff1b;IDEA 2024配置数据库和生成代码迁移到了Tools菜单下&#xff0c;原先版本在Other; 先完成数据库配置&#xff0c;点击Config Data…

Android CCodec Codec2 (十九)C2LinearBlock

在上一篇文章的结尾&#xff0c;我们看到fetchLinearBlock方法最终创建了一个C2LinearBlock对象。这一节&#xff0c;我们将深入了解C2LinearBlock是什么&#xff0c;它的作用是什么&#xff0c;以及它是如何被创建的。 1、_C2BlockFactory 先对上一篇文章的结尾内容做简单回顾…

LabVIEW离心泵性能优化测试系统

开发了一套基于LabVIEW平台开发的离心泵性能优化测试系统。系统集成了数据采集、流量控制、数据存储、报表生成等功能&#xff0c;提供了低成本、便捷操作的解决方案&#xff0c;适用于工业场景中对离心泵性能的精确测评。 项目背景 随着工业化进程的加速&#xff0c;离心泵在…

【NLP自然语言处理】深入探索Self-Attention:自注意力机制详解

目录 &#x1f354; Self-attention的特点 &#x1f354; Self-attention中的归一化概述 &#x1f354; softmax的梯度变化 3.1 softmax函数的输入分布是如何影响输出的 3.2 softmax函数在反向传播的过程中是如何梯度求导的 3.3 softmax函数出现梯度消失现象的原因 &…

MML 中使用 libevent +std::async unix socket domain 进程间通信

可以执行大量超时的接口,直到任务执行完成 还可以在一个事件做检测&#xff0c;funtcure 中的值为ready 状态 uds 的用法和tcp 类似&#xff0c;会维护一个链接状态和分配一个链接套接字,这就为异步执行提供了很方便的条件 客户端就安静的做一个计时,看是否在固定事件内返回执行…

基础算法练习--滑动窗口(已完结)

算法介绍 滑动窗口算法来自tcp协议的一种特性,它的高效使得其也变成了算法题的一种重要考点.滑动窗口的实现实际上也是通过两个指针前后遍历集合实现,但是因为它有固定的解题格式,我将其单独做成一个篇章. 滑动窗口的解题格式: 首先,定义两个指针left和right,与双指针不同的…