Vue + Element-Plus + SpringBoot + WebSocket实现简易网络聊天室

这篇具有很好参考价值的文章主要介绍了Vue + Element-Plus + SpringBoot + WebSocket实现简易网络聊天室。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

项目流程图

1. 前端搭建:Vue + Element-Plus + SpringBoot + WebSocket实现简易网络聊天室,java

        前端用Vue+Element-Plus 来搭建,由登录页面和聊天页面组成

1.1 登录页面

        由一个昵称输入框组成,用户输入自己的昵称若昵称和别的用户不重复,则可进入聊天室,否则提示错误并请重新输入。

<template>
  <div class="name">
    <el-form
      class="name-form"
      ref="nameFormRef"
      :model="nameForm"
      :rules="nameRules"
      @submit.prevent
    >
      <el-form-item>
        <h1>EZ-Chat</h1>
      </el-form-item>
      <el-form-item class="name-input" prop="nickname">
        <el-input
          class="nickname"
          v-model="nameForm.nickname"
          @keydown.enter="submitButton(nameFormRef)"
          placeholder="输入一个昵称"
        ></el-input>
        <el-button
          class="submit"
          type="primary"
          @click="submitButton(nameFormRef)"
          >进入</el-button
        >
      </el-form-item>
    </el-form>
  </div>
</template>

        这段代码是一个Vue.js组件的模板部分,用于实现聊天应用的用户昵称输入和登录功能

<script lang="ts" setup>
import { reactive, ref } from "vue";
import type { FormInstance, FormRules } from "element-plus";
import { ElMessage } from "element-plus";
import router from "@/router";
import axios from "axios";

// 昵称表单
const nameFormRef = ref<FormInstance>();

// 昵称表单
const nameForm = reactive({
  nickname: "",
});

// 表单校验
const nameRules = reactive<FormRules>({
  nickname: [{ required: true, message: "请输入一个昵称", trigger: "blur" }],
});

// 登录操作
const submitButton = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.validate((valid) => {
    if (valid) {
      axios
        .get("http://localhost:8888/list/" + nameForm.nickname)
        .then((resp) => {
          const data = resp.data;

          // 判断用户名是否存在
          if (!data.isExist) {
            sessionStorage.setItem("name", nameForm.nickname);
            router.push("/chat");
          } else {
            ElMessage({
              message: "该用户名已存在,请更换",
              grouping: true,
              type: "error",
            });
          }
        });
    } else {
      ElMessage({
        message: "请输入一个昵称",
        grouping: true,
        type: "error",
      });
      return false;
    }
  });
};
</script>
<style lang="scss">
@import "../assets/css/name";
</style>

        这段代码使用了Element Plus库中的el-formel-inputel-button组件来构建表单界面。其中,el-form用于创建表单容器,el-input用于输入昵称,el-button用于提交表单。

        在表单中,用户需要输入一个昵称,并点击"进入"按钮进行登录操作。当用户按下回车键时,会触发submitButton函数,该函数会验证表单是否通过校验。如果校验通过,则发送一个HTTP请求到服务器,获取与该昵称对应的用户信息。

Vue + Element-Plus + SpringBoot + WebSocket实现简易网络聊天室,java

上图是登录界面的展示。

1.2聊天页面

<template>
  <div class="chat">
    <div class="list-pane">
      <div class="user-pane">
        <div class="user-count">
          <h2>当前在线人数:{{ userCount }}</h2>
        </div>
        <div class="user-list">
          <div class="user" v-for="user in userList" :key="user">
            <el-image
              class="user-img"
              :src="require('@/assets/images/user.png')"
            ></el-image>
            <p class="username">{{ user }}</p>
          </div>
        </div>
      </div>
    </div>
    <div class="chat-pane">
      <div class="chat-header">
        <h2>EZ-Chat - {{ nickname }}</h2>
      </div>
      <div class="chat-message" ref="chatHistory">
        <div class="user-message" v-for="message in messages" :key="message">
          <div class="img">
            <el-image
              class="user-img"
              :src="require('@/assets/images/user.png')"
            ></el-image>
          </div>
          <div class="message">
            <div class="username">
              {{ message.name }} <span class="time">{{ message.time }}</span>
            </div>
            <div class="text user-text" v-if="nickname === message.name">
              {{ message.msg }}
            </div>
            <div class="text" v-if="nickname !== message.name">
              {{ message.msg }}
            </div>
          </div>
        </div>
      </div>
      <div class="chat-textarea">
        <el-input
          v-model="text"
          class="user-textarea"
          type="textarea"
          resize="none"
          @keydown.enter="sendButton"
        ></el-input>
        <el-button type="primary" class="send-button" @click="sendButton"
          >发送</el-button
        >
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, onActivated } from "vue";
import router from "@/router";

let nickname = ref();
let socket: WebSocket;

onActivated(() => {
  nickname.value = sessionStorage.getItem("name");
  // 查询是否设置了昵称
  if (nickname.value == null) {
    router.push("/");
    return;
  }

  // 查询浏览器是否支持 WebSocket
  if (typeof WebSocket == "undefined") {
    alert("您的浏览器不支持 WebSocket");
    router.push("/");
    return;
  }

  // 开启 WebSocket 服务
  let socketHost = "localhost";
  let socketPort = "8888";
  let socketUrl =
    "ws://" + socketHost + ":" + socketPort + "/socket/" + nickname.value;
  socket = new WebSocket(socketUrl);

  // 连接服务器
  socket.onopen = () => {
    console.log("已连接至服务器");
  };

  // 浏览器接收服务端发送的消息
  socket.onmessage = (msg) => {
    let data = JSON.parse(msg.data);
    if (data.userlist) {
      // 接收用户列表消息
      userList.value = data.userlist;
      userCount.value = data.userlist.length;
    } else {
      // 接收消息
      messages.value.push(data);

      // 获取节点
      let chatHistory = document.getElementsByClassName("chat-message")[0];
      if (chatHistory.scrollHeight >= chatHistory.clientHeight) {
        setTimeout(function () {
          //设置滚动条到最底部
          chatHistory.scrollTop = chatHistory.scrollHeight;
        }, 0);
      }
    }
  };
  // 关闭服务
  socket.onclose = () => {
    console.log("WebSocket 服务已关闭");
  };
  // 错误事件
  socket.onerror = () => {
    console.log("WebSocket 服务发生错误");
  };
});

// 日期转换
const formatTime = (date: Date) => {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  const hour = date.getHours();
  const minute = date.getMinutes();
  const second = date.getSeconds();

  return (
    [year, month, day].map(formatNumber).join("-") +
    " " +
    [hour, minute, second].map(formatNumber).join(":")
  );
};
const formatNumber = (n: number) => {
  const s = n.toString();
  return s[1] ? s : "0" + s;
};

// 用户数量
let userCount = ref(0);

// 用户列表
let userList = ref([]);

// 信息框
let text = ref("");

// 信息列表
let messages = ref([]);

// 信息
let message = {
  name: "",
  time: "",
  msg: "",
};

// 发送信息
const sendButton = (event: { preventDefault: () => void }) => {
  event.preventDefault();

  if (text.value != null && text.value !== "" && nickname.value != null) {
    message.name = nickname.value;
    message.time = formatTime(new Date());
    message.msg = text.value;
    socket.send(JSON.stringify(message));
    message.msg = "";
    text.value = "";
  }
};
</script>

<style lang="scss">
@import "../assets/css/chat";
</style>

这段代码使用了Vue的响应式数据绑定和生命周期钩子函数来实现实时更新聊天界面的功能。

在模板部分,通过使用Vue的指令和组件来构建聊天界面的各个部分。其中包括用户列表、聊天消息、输入框和发送按钮等元素。

在脚本部分,首先导入了Vue的ref和onActivated函数,以及WebSocket库。然后定义了一些变量和函数,用于处理用户的昵称、连接WebSocket服务、接收服务器发送的消息、格式化时间等操作。

当页面激活时,会执行onActivated函数。在这个函数中,首先获取用户的昵称,并检查浏览器是否支持WebSocket。如果不支持或发生错误,则跳转到首页。

接下来,根据昵称构造WebSocket服务的URL,并创建一个新的WebSocket实例。然后设置一些事件监听器,包括连接成功、接收消息、关闭连接和发生错误等。

在接收消息的事件监听器中,解析服务器发送的数据,并根据数据类型进行相应的处理。如果是用户列表消息,则更新用户列表和用户数量;如果是普通消息,则将消息添加到信息列表中,并滚动到聊天历史记录的底部。

最后,定义了一个sendButton函数,用于发送用户输入的信息。这个函数会在按下回车键时触发,并检查输入框和昵称是否为空。如果不为空,则创建一个新的消息对象,并通过WebSocket服务发送给服务器。

整个聊天应用的样式部分使用了SCSS语言编写,并引入了一个外部的CSS文件。可以根据需要进一步自定义样式。

Vue + Element-Plus + SpringBoot + WebSocket实现简易网络聊天室,java

上图是聊天室页面的展示。

2. 后端部分

2.1环境配置

首先在idea中创建maven工程,接着导入依赖,pom.xml文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.chat</groupId>
    <artifactId>chat-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>chat-server</name>
    <description>chat-server</description>
    <packaging>war</packaging>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--    Test    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--    Spring Web    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- 移除嵌入式tomcat插件 -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--        添加tomcat支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <!--<scope>provided</scope>-->
        </dependency>

        <!--    WebSocket   -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <!--    Lombok    -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--    FastJson    -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.28</version>
        </dependency>

        <!--    FastJson依赖    -->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>

            <!-- maven 打包时跳过测试 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>

        </plugins>
    </build>

</project>

2.2 后端组件

 创建WebSocket的配置类

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET","HEAD","POST","PUT","DELETE","OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

WebSocket服务器端实现

import com.alibaba.fastjson.JSON;
import com.chat.entity.Message;
import com.chat.entity.UserList;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket 服务
 */
@Component
@ServerEndpoint("/socket/{username}")
public class WebSocketServer {
    /**
     * 存储对象 map
     */
    public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    /***
     * WebSocket 建立连接事件
     * 1.把登录的用户存到 sessionMap 中
     * 2.发送给所有人当前登录人员信息
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("username") String username) {

        // 搜索名称是否存在
        boolean isExist = sessionMap.containsKey(username);
        if (!isExist) {
            System.out.println(username + "加入了聊天室");
            sessionMap.put(username, session);
            sendAllMessage(setUserList());
            showUserList();
        }
    }

    /**
     * WebSocket 关闭连接事件
     * 1.把登出的用户从 sessionMap 中剃除
     * 2.发送给所有人当前登录人员信息
     */
    @OnClose
    public void onClose(@PathParam("username") String username) {
        if (username != null) {
            System.out.println(username + "退出了聊天室");
            sessionMap.remove(username);
            sendAllMessage(setUserList());
            showUserList();
        }
    }

    /**
     * WebSocket 接受信息事件
     * 接收处理客户端发来的数据
     * @param message 信息
     */
    @OnMessage
    public void onMessage(String message) {
        Message msg = JSON.parseObject(message, Message.class);
        sendAllMessage(JSON.toJSONString(msg));
    }

    /**
     * WebSocket 错误事件
     * @param session 用户 Session
     * @param error 错误信息
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 显示在线用户
     */
    private void showUserList() {
        System.out.println("------------------------------------------");
        System.out.println("当前在线用户");
        System.out.println("------------------------------------------");
        for (String username : sessionMap.keySet()) {
            System.out.println(username);
        }
        System.out.println("------------------------------------------");
        System.out.println();
    }

    /**
     * 设置接收消息的用户列表
     * @return 用户列表
     */
    private String setUserList(){
        ArrayList<String> list = new ArrayList<>(sessionMap.keySet());
        UserList userList = new UserList();
        userList.setUserlist(list);
        return JSON.toJSONString(userList);
    }

    /**
     * 发送消息到所有用户种
     * @param message 消息
     */
    private void sendAllMessage(String message) {
        try {
            for (Session session : sessionMap.values()) {
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这段代码实现了一个基于WebSocket的聊天室服务器,具有以下功能:

  1. 当有新的用户加入聊天室时,将其添加到sessionMap中,并向所有已连接的用户发送当前在线用户列表。
  2. 当用户离开聊天室时,将其从sessionMap中移除,并向所有已连接的用户发送当前在线用户列表。
  3. 当收到客户端发送的消息时,将消息转换为JSON格式并发送给所有已连接的用户。
  4. 在发生错误时,打印错误堆栈信息。
  5. 提供了一些辅助方法,如sendAllMessage用于向所有用户发送消息,setUserList用于设置用户列表并将其转换为JSON格式字符串,showUserList用于显示当前在线用户列表。

 用户列表控制器

import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static com.chat.component.WebSocketServer.sessionMap;

/**
 * 用户列表控制器
 */
@RestController
@RequestMapping("/list")
public class UserListController {

    /**
     * 判断用户名是否存在与聊天室
     * @param username 用户名
     * @return json
     */
    @GetMapping("/{username}")
    public JSONObject getUsername(@PathVariable("username") String username) {
        JSONObject jsonObject = new JSONObject();
        boolean isEmpty = sessionMap.isEmpty();
        jsonObject.put("isEmpty", isEmpty);
        jsonObject.put("isExist", false);
        if (!isEmpty) {
            boolean isExist = sessionMap.containsKey(username);
            jsonObject.replace("isExist", isExist);
        }
        return jsonObject;
    }

}

这段代码是一个用户列表控制器,用于判断用户名是否存在于聊天室中。它使用了Spring框架的注解来实现HTTP请求映射和处理。

在控制器类`UserListController`中,定义了一个方法`getUsername`,该方法使用`@GetMapping`注解来映射到路径`/list/{username}`的GET请求。通过`@PathVariable("username")`注解,可以将URL中的`username`参数传递给方法作为输入。

方法内部首先创建一个`JSONObject`对象`jsonObject`,然后检查`sessionMap`是否为空。如果为空,将`isEmpty`属性设置为`true`,表示聊天室中没有用户。否则,将`isEmpty`属性设置为`false`。

接下来,方法检查`sessionMap`中是否包含指定的用户名。如果包含,将`isExist`属性设置为`true`,表示用户名存在于聊天室中;否则,将`isExist`属性设置为`false`。

最后,方法返回`jsonObject`对象,其中包含了判断结果的信息。

这段代码中的`sessionMap`是一个静态变量,用于存储当前聊天室中的所有用户会话。

项目代码

ShuoC/ez-chat文章来源地址https://www.toymoban.com/news/detail-777350.html

到了这里,关于Vue + Element-Plus + SpringBoot + WebSocket实现简易网络聊天室的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • (二) Vue3 + Element-Plus 实现动态菜单栏

    系列介绍:Vue3 + Vite + TS 从零开始学习 项目搭建:(一) Vue3 + Vite + TS 项目搭建 实现动态菜单栏:(二) Vue3 + Element-Plus 实现动态菜单栏 实现动态面包屑:(三) Vue3 + Element-Plus 实现动态面包屑 实现动态标签页:(四) Vue3 + Element-Plus 实现动态标签页 实现动态主题色切换(demo):(五)

    2023年04月23日
    浏览(39)
  • Vue3 element-plus表单嵌套表格实现动态表单验证

    部分效果图如下: 另表格有添加和删除按钮,点击提交进行表单验证。 首先data格式必须是对象包裹数组 给表单绑定form数据 表格绑定tableData数据 给表单项增加验证规则 rules对应data rules对象,prop对应表单字段(注意是表格里每一行对应的字段 forms.tableData[下标].key) prop的关

    2024年02月14日
    浏览(27)
  • Vue3使用element-plus实现弹窗效果-demo

    2024年02月13日
    浏览(28)
  • vue3+element-plus+el-image实现点击按钮预览大图

    需求:点击某个按钮实现el-image中预览大图的效果 官方文档:以下是官方的写法,并不能达到我们的要求,官方实现的功能是点击图片达到预览大图的效果。如果你的按钮就是图片,也可以达到目前的功能 el-image-viewer组件是element官方的组件,只是文档中没有写出来,这个组

    2024年02月12日
    浏览(39)
  • vue3+element-plus 通过v-infinite实现下拉滚动无限加载

    v-infinite官网 v-infinite-scroll无限滚动组件使用详解  官网给到的基础案例: 自己写了一个简单的demo: 当使用v-infinite时,控制台会报错:  原因: 官方上的Issues解释是需要nextTick()之后再去显示 解决方法是组件挂载完成再去显示el-select组件 所以在上面demo中select组件加了v-if,

    2024年02月09日
    浏览(36)
  • vue-router + element-plus实现面包屑导航栏和路由标签栏

    首先,先解释一下什么是面包屑导航栏和路由标签栏。 如下图所示,面包屑导航栏就是展示当前所处路由信息和父辈路由信息的导航栏,它的作用是提示用户当前页面所在位置;路由标签栏就类似于浏览器的标签栏,每个标签对应一个路由页面,点击该标签可以进入该路由页

    2023年04月23日
    浏览(34)
  • Vue3+Element-Plus 实现用户列表页面的UI结构及动态加载表单功能 三一

    1.1 头部是一个面包屑 (Breadcrumb)导航区域 1.2 白色区域是一个卡片(Card)视图 1.3 卡片 (Card)视图中嵌套了   输入框(Input )、 按钮(Button)、 表单(Form)、分页(Pagination ) Breadcrumb 面包屑 | Element Plus (gitee.io) https://element-plus.gitee.io/zh-CN/component/breadcrumb.html  2.1.1 复制

    2023年04月09日
    浏览(43)
  • vue3 vue.config.js配置Element-plus组件和Icon图标实现按需自动引入

    打包时,报警告,提示包太大会影响性能 在页面直接使用,直接使用 SVG 图标,当做一般的 svg 使用 icon使用时需要用以下两种方式方式: 如果用在el-button里面的icon属性上使用,用SVG方式无效,还是需要引入再使用(不知道有没有其他方式) 注意: 使用 :icon=\\\"Edit\\\" 则icon的大

    2024年02月06日
    浏览(39)
  • Vue3+Vue-Router+Element-Plus根据后端数据实现前端动态路由——权限管理模块

    提示:文章内容仔细看一些,或者直接粘贴复制,效果满满 提示:文章大概 1、项目:前后端分离 2、前端:基于Vite创建的Vue3项目 3、后端:没有,模拟的后端数据 4、关于路径“@”符号——vite.config.js 文件里修改 提示:以下是本篇文章正文内容,下面案例可供复制粘贴使用

    2024年02月02日
    浏览(35)
  • vue3+element-plus实现表格多选功能(可以清除选项或分页保留选项)

    如图所示,在实际开发中,数据量大的表格基本都添加上了分页功能,每个页面请求的数据回交换更新,第一页的选中效果在跳转至第二页后,如果没有做相关处理,选中项会被清空,具体解决方法如下 在需要处理的表格标签中加上 :row-key=\\\"getRowKeys\\\" 以及 @selection-change=“ha

    2024年02月12日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包