# SpringBoot Vue WebSocket实现多人聊天

Vue WebSocket实现聊天


SpringBoot后端代码

1.引入Websocket依赖

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

2.配置websocket

@ServerEndpoint(value = "/chat/{fromUser}")
@Component
public class MyWebsocket {

    private static Logger log = LoggerFactory.getLogger(MyWebsocket.class);
    private Session session;

    /**
     * 在线用户列表
     */
    public static Map<String, Session> sessionPool = new HashMap();
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "fromUser") String fromUser) {
        this.session = session;
        sessionPool.put(fromUser, session);
        System.out.println(fromUser + "【websocket消息】有新的连接,总数为:" + sessionPool.size());
    }

    @OnClose
    public void onClose() {
        sessionPool.remove(this);
        System.out.println("【websocket消息】连接断开,总数为:" + sessionPool.size());

    }

    @OnMessage
    public void onMessage(String msg) {
        log.info("【websocket消息】收到客户端消息:" + msg);
        String resoult = msg;
        Map<String, Object> map = JsonUtils.toMap(msg);
        String toUser = (String) map.get("toUser");
        String ms = (String) map.get("msg");
        String account = (String) map.get("fromUser");
        sendOneMessage(account, toUser, ms);
    }
    /**
     * 此为单点消息
     */
    public void sendOneMessage(String account, String userName, String message) {
        try {
            log.info("【websocket消息】单点消息:" + message);
            Session session = sessionPool.get(userName);
            sessionPool.get(userName);
            try {
                //将信息保存到message表中
                Record record=new Record();
                record.set("date",TimeUtils.getCurrentTime());
                record.set("touser",userName);
                record.set("fromuser",account);
                record.set("type","test");
                record.set("message",message);
                Db.save("message","id",record);
            } catch (Exception e) {
                log.info(e.getMessage());
            }

            ChatMessage chatMessage = new ChatMessage();
            chatMessage.setToUser(userName);
            chatMessage.setMsg(message);
            chatMessage.setFromUser(account);
            chatMessage.setType("test");
            String time = TimeUtils.getCurrentTime();
            chatMessage.setTime(time);
            String s = JsonUtils.toJSONString(chatMessage);
            if (session != null) {
                session.getAsyncRemote().sendText(s);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Vue前端展示

1.完整代码chat.vue

<template>
  <div>
    <!--弹出层查看用户历史发送的消息 -->
    <el-dialog
      title="历史消息"
      :visible.sync="dialogFormVisible"
      width="900px"
      top="200px"
    >
      <el-table :data="tableData" height="250" border style="width: 100%">
        <el-table-column prop="date" label="日期" width="180">
        </el-table-column>
        <el-table-column prop="fromuser" label="发送者" width="180">
        </el-table-column>
        <el-table-column prop="touser" label="接收者"> </el-table-column>
        <el-table-column prop="message" label="信息"> </el-table-column>
      </el-table>
    </el-dialog>

    <el-row type="flex" justify="center">
      <el-col :md="14">
        <el-card
          :body-style="{ padding: '10px' }"
          style="background-color: #E8E8E8; border: 1px solid #DDDDDD; border-bottom: 0;"
        >
          <div style="height: 20px;">
            <div
              style="width: 20px; height: 20px; background-color: #DF7065; border-radius: 50%; float: left;"
              @click="webSokcetClose()"
            ></div>
            <div
              style="width: 20px; height: 20px; background-color: #E6BB46; border-radius: 50%; float: left; margin-left: 10px;"
            ></div>
            <div
              style="width: 20px; height: 20px; background-color: #5BCC8B; border-radius: 50%; float: left; margin-left: 10px;"
            ></div>
            <div>
              <el-button
                style="margin-left: 10px;"
                size="mini"
                type="success"
                icon="el-icon-check"
                circle
                @click="getMessage()"
              ></el-button>
            </div>
          </div>
        </el-card>
        <el-card
          :body-style="{ padding: '0' }"
          style="border: 1px solid #DDDDDD; border-top: 0;"
        >
          <el-row>
            <el-col
              :md="6"
              style=" height: 600px; border-right: 2px solid #DDDDDD; overflow: auto;"
            >
              <el-card
                :body-style="{
                  'padding-top': '11px',
                  'padding-bottom': '12px',
                  'padding-left': '10px',
                  'padding-right': '12px'
                }"
                shadow="never"
              >
                <div>
                  <el-input
                    size="mini"
                    placeholder="请输入内容"
                    v-model="input1"
                  >
                  </el-input>
                </div>
              </el-card>
              <div v-for="item in listUser" :key="item.id">
                <a href="javascript:void(0);" @click="toggleChat(item.account)">
                  <el-card :body-style="{ padding: '5px 10px' }" shadow="never">
                    <el-row>
                      <el-col :span="7">
                        <el-image
                          style="width: 40px; height: 40px; border-radius: 50%;"
                          src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
                          fit="cover"
                        ></el-image>
                      </el-col>
                      <el-col :span="17">
                        <h4
                          class="m-0"
                          style="color: #666666; font-size: 15px;"
                        >
                          {{ item.account }}
                        </h4>
                        <p
                          style="color: #999999; font-size: 12px; margin: 0; margin-top: 3px;"
                          class="limitTitleDirectory"
                        >
                          这个家伙很懒,什么也没有留下。
                        </p>
                      </el-col>
                    </el-row>
                  </el-card>
                </a>
              </div>
            </el-col>
            <el-col :md="18">
              <div v-show="toUser !== '游客'">
                <el-card :body-style="{ padding: '15px' }" shadow="never">
                  <div class="text-center">
                    <p class="m-0">{{ toUser }}</p>
                  </div>
                </el-card>
                <el-card :body-style="{ padding: '5px' }" shadow="never">
                  <div
                    v-for="item in this.listUser"
                    :key="item.id"
                    v-show="toUser === item.account"
                    :id="item.account"
                    style="height: 327px; overflow: auto;"
                  ></div>
                </el-card>
                <el-input
                  type="textarea"
                  :rows="5"
                  v-model="input"
                  @keyup.enter.native="webSokcetSend()"
                  style="margin-top:60px"
                ></el-input>
              </div>
              <div v-show="toUser === '游客'">
                <div
                  style="text-align: center; line-height: 600px; background-color: #f3f3f3;"
                >
                  <p style="margin: 0; font-size: 22px; color: #909399;">
                    没有会话消息
                  </p>
                </div>
              </div>
            </el-col>
          </el-row>
        </el-card>
      </el-col>
    </el-row>
  </div>
</template>

<script>
import { getOnlineuser } from "@/api/chat";
import { formatTime } from "@/utils/TimeUtils";
import { getMessage } from "@/api/chat";
export default {
  name: "mac",
  data() {
    return {
      input: "",
      input1: "",
      //在线人数
      listUser: [],
      webSocket: "",
      fromUser: "游客",
      toUser: "游客",

      tableData: "",
      dialogFormVisible: false //弹出层默认为关闭
    };
  },
  created() {
     //得到在线人数
    getOnlineuser("/ws/getOnlineuser").then(res => {
      if (res.code === 200) {
        this.listUser = res.data;
      } else if (res.code === 500) {
        this.$message(res.data);
      }
    });
    this.fromUser = localStorage.getItem("username");
  },
  mounted() {
    if (document.cookie === "") {
      this.webSokcetClose();
    } else {
    }
    //配置websocket服务器地址
    this.webSocket = new WebSocket("ws://localhost:8001/chat/" + this.fromUser);
    this.initWebSocket();
  },
  methods: {
    initWebSocket() {
      this.webSocket.onerror = this.onError; // 通讯异常
      this.webSocket.onopen = this.onOpen; // 连接成功
      this.webSocket.onmessage = this.onMessage; // 收到消息时回调
      this.webSocket.onclose = this.onClose; // 连接关闭时回调
    },
    onError() {
      console.log("通讯异常");
    },
    onOpen() {
      console.log("通讯开始");
    },
    onMessage(event) {
      console.log(event.data);
      let data = JSON.parse(event.data);
      // console.log(data);
      if (data.list !== undefined) {
        let list = data.list;
        for (let i = 0; i < list.length; i++) {
          if (list[i] === this.fromUser) {
            list.splice(i, 1);
          }
        }
        this.listUser = list;
      }
      this.messageDiv(event.data);
      console.log(event.data);
    },
    onClose() {
      console.log("通讯关闭");
    },
    webSokcetSend() {
      var now = new Date();
      var strnow = formatTime(now);
      /*
       * 发送消息
       * */
      let message = JSON.stringify({
        fromUser: this.fromUser,
        toUser: this.toUser,
        msg: this.input,
        time: this.strnow
      });
      this.webSocket.send(message);
      this.input = "";
      this.messageDiv(message);
    },
    webSokcetClose() {
    },
    toggleChat(toUser) {
      this.toUser = toUser;
    },
    messageDiv(data1) {
      let data = JSON.parse(data1);
      let div = document.createElement("div");
      let p1 = document.createElement("p");
      let p = document.createElement("p");
      if (data.time == null) {
        data.time = formatTime(new Date());
      }
      p1.innerHTML = data.fromUser + " " + data.time;
      debugger;
      if (data.fromUser !== "系统通知") {
        if (data.type !== undefined) {
          /*
           * data.type !== undefined
           * 说明这是接收到消息
           * 把值插到发送者的div
           * */
          // let fromUser = document.getElementById("test");
          let fromUser = document.getElementById(data.fromUser);
          p.innerHTML = "收到:" + data.msg;
          div.style =
            "width: 500px; float: left; margin-left: 15px; margin-top:10px; ";
          p1.style =
            "font-size: 15px; margin-bottom: 0; margin-top: 0; float: left; font-weight: 500; ";
          p.style =
            "padding: 10px; background-color: #F1F1F1; margin-top: 25px; margin-bottom: 10px; word-wrap : break-word; ";
          div.appendChild(p1);
          div.appendChild(p);

          fromUser.appendChild(div);
          fromUser.scrollTop = fromUser.scrollHeight;
          if (fromUser.style.display === "none") {
            // console.log("有新的消息未查看!");
            this.$message("收到新的消息");
          }
        } else {
          /*
           * 说明这是发送消息
           * 把值插到接收者的div
           * */
          // let toUser = document.getElementById("test");
          let toUser = document.getElementById(data.toUser);
          p.innerHTML = "发出:" + data.msg;
          div.style =
            "width: 500px; float: right; margin-right: 15px; margin-top: 10px;";
          p1.style =
            "font-size: 15px; margin-bottom: 0; margin-top: 0; float: right; font-weight: 500;";
          p.style =
            "padding: 10px; background-color: #9FE86C; margin-top: 25px; margin-bottom: 10px; word-wrap : break-word;";
          div.appendChild(p1);
          div.appendChild(p);

          toUser.appendChild(div);
          toUser.scrollTop = toUser.scrollHeight;
        }
      }
    },
    //获得历史消息
    getMessage() {
      getMessage("/ws/getMessage").then(res => {
        if (res.code === 200) {
          this.dialogFormVisible = true;
          this.tableData = res.data;
        } else if (res.code === 500) {
          this.$message(res.data);
        }
      });
    }
  },
  beforeRouteEnter(to, from, next) {
    document
      .querySelector("body")
      .setAttribute("style", "background-color: #F9F9F9");
    next();
  },
  beforeRouteLeave(to, from, next) {
    // 去除背景色
    document.querySelector("body").setAttribute("style", "");
    next();
  }
};
</script>

<style scoped>
.limitTitleDirectory {
  width: 120px; /* 限制文本宽度 */
  overflow: hidden; /* 超出的文本隐藏 */
  text-overflow: ellipsis; /* 溢出的文本内容用 ... 代替 */
  white-space: nowrap; /* 溢出不换行*/
}
element.style {
  padding-left: 10px;
}
.el-menu-item {
  font-size: 14px;
  color: #303133;
  padding: 0 10px;
  cursor: pointer;
  -webkit-transition: border-color 0.3s, background-color 0.3s, color 0.3s;
  transition: border-color 0.3s, background-color 0.3s, color 0.3s;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}

/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar {
  width: 5px; /*滚动条宽度*/
  height: 5px; /*滚动条高度*/
}

/*定义滚动条轨道 内阴影+圆角*/
::-webkit-scrollbar-track {
  /*-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);*/
  border-radius: 10px; /*滚动条的背景区域的圆角*/
  /*background-color: red;!*滚动条的背景颜色*!*/
}

/*定义滑块 内阴影+圆角*/
::-webkit-scrollbar-thumb {
  border-radius: 10px; /*滚动条的圆角*/
  /*-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);*/
  background-color: #b8b8bc; /*滚动条的背景颜色*/
}
.el-col-md-14 {
  width: 80%;
}
.el-scrollbar {
  height: 100%;
}
</style>

实现效果
在这里插入图片描述

源码地址:

https://gitee.com/Marlon_Brando
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值