websocket 和vue 实现简易局域网多人聊天

共分为两个界面,一个websocket服务器js页,一个vue.config.js页,一个路由页,用了vant UI 版本2.12.54,vue版本2.6.14 记得npm install ws模块 版本8.13.0,使用的node版本16.14.0。

gitee地址liaotian: websocket和vue实现的简易局域网多人聊天

1.home页也是登录页

<template>
  <div class="home">
    <div class="lit">全民聊天室</div>
    <van-form @submit="onSubmit">
      <van-field
        v-model="username"
        name="用户名"
        label="用户名"
        placeholder="用户名"
        :rules="[{ required: true, message: '请填写用户名' }]"
      />
      <van-field
        v-model="password"
        type="password"
        name="密码"
        label="密码"
        placeholder="密码"
        :rules="[{ required: true, message: '请填写密码' }]"
      />
      <div style="margin: 16px">
        <van-button round block type="info" native-type="submit"
          >进入聊天室</van-button
        >
      </div>
    </van-form>
  </div>                      
</template>

<script>
export default {
  data() {
    return {
      username: '',
      password: '',
    };
  },
  methods: {
    onSubmit() {
      this.$router.push({
        name:'about',
        params:{
          name:this.username
        }
      })
    },
  },
}
</script>
<style scoped>
.lit{
  margin:40px 0;
  font-size: 20px;
  text-align: center;
}
</style>

2.about页也是聊天页

<template>
  <div class="about">
    <div class="lit">全民聊天室</div>
    <div class="about-box" ref="about">
      <div
        class="liao"
        :class="{ liao1: item.name === $route.params.name }"
        v-for="(item, index) in list"
        :key="index"
      >
        <div class="tou" :class="{ tou1: item.name === $route.params.name }">
          {{ item.userId }}
        </div>
        <div class="info" :class="{ info1: item.name === $route.params.name }">
          <div
            class="info-name"
            :class="{ infoname1: item.name === $route.params.name }"
          >
            {{ item.name }}
          </div>
          <div
            class="info-content"
            :class="{ infocontent1: item.name === $route.params.name }"
          >
            {{ item.content }}
          </div>
        </div>
      </div>
      <!-- <div :class="{liao1: item.name=$route.params.name}">
        <div class="info1">
          <div class="info-name1"></div>
          <div class="info-content1"></div>
        </div>
        <div class="tou1"></div>
      </div> -->
    </div>
    <van-field class="ipt" v-model="contentText" center clearable ref="sendMsg">
      <template #button>
        <van-button size="small" type="primary" @click="btn">发送</van-button>
      </template>
    </van-field>
  </div>
</template>

<script>
import { Dialog } from 'vant';
export default {
  
  data () {
    return {
      userInfo: {
        type: "name",
        data: '',
        head: ''
      },
      ws: null,
      count: 0,
      userId: null, //当前用户ID
      list: [], //聊天记录的数组
      contentText: "" //input输入的值
    }
  },
  created () {
    
  },
  mounted () {
    this.initWebSocket();

  },
  updated () {
    this.scrollBottm()
  },
  methods: {
    btn () {
      if (!this.$route.params.name) {
        Dialog({
          title: '警告',
          message: '登录信息已失效,请重新登录',
        }).then(() => {
          this.$router.back()
        });
        return
      }
     
      let me = {
        tou: this.$route.params.name.charAt(0),
        name: this.$route.params.name,
        content: this.contentText
      }
      if (!this.contentText) {
        return;
      }
      this.sendText(me)
      // let bie=`<div class="liao" >
      //   <div class="tou">{{item.tou}}</div>
      //   <div class="info">
      //     <div class="info-name">{{item.name}}</div>
      //     <div class="info-content">{{item.content}}</div>
      //   </div>
      // </div>`
      // let m=`<div class="liao1">
      //   <div class="info1">
      //     <div class="info-name1">${me.name}</div>
      //     <div class="info-content1">${me.content}</div>
      //   </div>
      //   <div class="tou1">${me.tou}</div>
      // </div>`
      // this.$refs.about.innerHTML+=m
      // this.list.push(me)
      // this.$refs.about.innerHTML+=bie
      this.initWebSocket();
    },
    scrollBottm () {
      let el = this.$refs["about"];
      el.scrollTop = el.scrollHeight;
      console.log(el.scrollHeight);
    },
    //发送聊天信息
    sendText (me) {
      let _this = this;
      _this.$refs["sendMsg"].focus();
      if (!_this.contentText) {
        return;
      }
      _this.ws.send(JSON.stringify(me)); //调用WebSocket send()发送信息的方法
      _this.contentText = "";
      setTimeout(() => {
        _this.scrollBottm();
      }, 500);
    },
    //进入页面创建websocket连接
    initWebSocket () {
      let _this = this;
      //判断页面有没有存在websocket连接
      if (window.WebSocket) {
        // 10.12.16.27:8181 是我本地IP地址 此处的 :8181 端口号 要与后端配置的一致
        let ws = new WebSocket("ws:192.168.1.97:8181"); 
        _this.ws = ws;
        ws.onopen = function (e) {
          console.log("服务器连接成功" + e);
        };
        ws.onclose = function (e) {
          console.log("服务器连接关闭" + e);
        };
        ws.onerror = function () {
          console.log("服务器连接出错");
        };
        ws.onmessage = function (e) {
          //接收服务器返回的数据
          let resData = JSON.parse(e.data);
          console.log(resData);
          if (resData.funName == "userCount") {
            _this.count = resData.users;
            _this.list = resData.chat
            console.log(resData.chat);
          } else {
            _this.list = [
              ..._this.list,
            ];
          }
        };
      }
    }
  },
}
</script>

<style>
.lit {
  text-align: center;
}
.about-box {
  height: 155vw;
  background-color: gainsboro;
  padding-bottom: 53px;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}
.liao {
  padding: 10px;
  display: flex;
  /* width: 100%; */
  padding-right: 20px;
  margin-bottom: 10px;
}
.tou {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background-color: white;
  text-align: center;
  line-height: 30px;
  margin-right: 10px;
}
.info-content {
  box-sizing: content-box;
  background-color: white;
  padding: 5px;
  border-radius: 5px;
  margin-top: 5px;
  max-width: 81vw;
}
.liao1 {
  /* width: 100%; */
  padding: 1px;
  display: flex;
  justify-content: flex-end;
  padding-left: 20px;
  margin-bottom: 10px;
}
.tou1 {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background-color: white;
  text-align: center;
  line-height: 30px;
  margin-right: 10px;
  margin-left: 10px;
  order: 1;
}
.info1 {
  /* flex: 1; */
  order: 0;
}
.infoname1 {
  text-align: right;
}
.infocontent1 {
  box-sizing: border-box;
  background-color: rgb(25, 184, 7);
  padding: 5px;
  border-radius: 5px;
  margin-top: 5px;
  max-width: 81vw;
}
.ipt {
  position: fixed;
  bottom: 0;
  left: 0;
}
</style>

3.serves.js页也是服务器页

var userNum = 0; //统计在线人数
var chatList = [];//记录聊天记录
var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({ host:'192.168.1.97',port: 8181 }); //8181 与前端相对应
//调用 broadcast 广播,实现数据互通和实时更新
wss.broadcast = function (msg) {
    wss.clients.forEach(function each (client) {
        client.send(msg);
    });
};
wss.on('connection', function (ws) {
    userNum++;//建立连接成功在线人数 +1
    wss.broadcast(JSON.stringify({ funName: 'userCount', users: userNum, chat: chatList })); //建立连接成功广播一次当前在线人数
    console.log('Connected clients:', userNum);
    //接收前端发送过来的数据
    ws.on('message', function (e) {
        var resData = JSON.parse(e)
        console.log('接收到来自clent的消息:' + resData.name)
        chatList.push({ userId: resData.tou, name: resData.name, content: resData.content });//每次发送信息,都会把信息存起来,然后通过广播传递出去,这样此每次进来的用户就能看到之前的数据
        wss.broadcast(JSON.stringify({ userId: resData.tou, name: resData.name, msg: resData.content })); //每次发送都相当于广播一次消息

    });
    ws.on('close', function () {
        userNum--;//建立连接关闭在线人数 -1
        wss.broadcast(JSON.stringify({ funName: 'userCount', users: userNum, chat: chatList }));//建立连接关闭广播一次当前在线人数
        console.log('Connected clients:', userNum);
        console.log('长连接已关闭')
    })
})
console.log(wss)

4.vue.config.js页

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {

    // https:true,
    // lintOnSave: false,
    // port: 8181,
    client: {
      webSocketURL: 'ws://192.168.1.97:8181', //电脑ip地址
    },
    // headers: {
    //   'Access-Control-Allow-Origin': '*',
    // },
    // disableHostCheck: true,
    historyApiFallback: true,  //跨不跨的没有啥影响
    allowedHosts: 'all',
    proxy: {
      'ws': {
        target: `ws://localhost:8181`, //自定义的请求后台接口
        ws: true,
        changeOrigin: true, //允许跨域
      }
    },
  },
})

5.路由配置 

import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
Vue.use(VueRouter)
const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/AboutView.vue')
  }
]
const router = new VueRouter({
  routes
})

export default router

6.目录

 先启动服务器再运行项目!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值