Socket implementation chat room

Example: Implementing chat rooms

Based on node+ Express +socket. IO +vue+ Flex layout to achieve simple chat room

Realize the function

Login to detect

System prompts online personnel status (enter/leave)

Receive and send messages

Customize message font colors

Support for sending emojis

Support to send pictures

Support sending window vibration

Project Structure:

index.html

<! DOCTYPEhtml>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="style/index.css">
    <link rel="stylesheet" href="Style/font - awesome - 4.7.0 / CSS font - awesome. Min. CSS">
  </head>

  <body>
    <div id="app">
      <div class="name" v-if='isShow'>
        <! -- <h2> Please enter your nickname </h2> -->
        <input @keyup.enter='handleClick' type="text" id="name" placeholder="Please enter a nickname..." autocomplete="off"
               v-model='username'>
        <button id="nameBtn" @click='handleClick'>determine</button>
      </div>
      <div class="main" :class='{shaking:isShake}'>
        <div class="header">
          <img src="image/logo.jpg">❤ ️ chat room</div>
        <div id="container">
          <div class="conversation">
            <ul id="messages">
              <li v-for='(user,index) in userSystem' :class='user.side'>
                <div v-if='user.isUser'>
                  <img :src="user.img" alt="">
                  <div>
                    <span>{{user.name}}</span>
                    <p :style="{color: user.color}" v-html='user.msg'>
                    </p>
                  </div>
                </div>
                <p class='system' v-else>
                  <span>{{nowDate}}</span><br />
                  <span v-if='user.status'>{{user.name}}{{user.status}} in the chat room</span>
                  <span v-else>{{user.name}} sent a window jitter</span>
                </p>

              </li>
            </ul>
            <form action="">
              <div class="edit">
                <input type="color" id="color" v-model='color'>
                <i title="Customize font colors" id="font" class="fa fa-font">
                </i><i @click='handleSelectEmoji' @dblclick='handleDoubleSelectEmoji' title="Double click to deselect" class="fa fa-smile-o" id="smile">
                </i><i @click='handleShake' title="Click page vibrate" id="shake" class="fa fa-bolt">
                </i>
                <input type="file" id="file">
                <i class="fa fa-picture-o" id="img"></i>
                <div class="selectBox" v-show='isEmojiShow'>
                  <div class="smile" id="smileDiv">
                    <p>A classic expression</p>
                    <ul class="emoji">
                      <li v-for='(emojiSrc,i) in emojis' :id='i' :key='i'>
                        <img :src="emojiSrc" :alt="i+1" @click='handleEmojiImg(i+1)'>
                      </li>
                    </ul>
                  </div>
                </div>
              </div>
              <! -- AutoComplete disables autocomplete -->
              <textarea id="m" v-model='msgVal' autofocus @keyup.enter='handleSendMsg'></textarea>
              <button class="btn rBtn" id="sub" @click='handleSendMsg'>send</button>
              <button class="btn" id="clear" @click='handleLogout'>Shut down</button>
            </form>
          </div>
          <div class="contacts">
            <h1>Online staff (<span id="num">{{userInfo.length}}</span>)</h1>
            <ul id="users">
              <li v-for='(user,index) in userInfo'>
                <img :src="user.img" alt="">
                <span>{{user.username}}</span>
              </li>
            </ul>
            <p v-if='userInfo.length==0'>No one is online at present</p>
          </div>
        </div>
      </div>
    </div>
    <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src='js/client.js'></script>
  </body>

</html>
Copy the code

server.js

const express = require('express');
const app = express();1
const http = require('http').Server(app);
const io = require('socket.io')(http);
const port = process.env.PORT || 5000;

let users = [];// Store the logged-in user
let userInfo = [];// Store the user name and profile picture

app.use('/',express.static(__dirname+'/static'));
app.get('/'.function (req, res) {
  res.sendFile(__dirname + '/index.html');
});

io.on('connection'.function (socket) {
  console.log('Connection successful');
  socket.on('login'.function (user) {
    const {username} = user;
    console.log(users.indexOf(username))

    if (users.indexOf(username)>-1){            
      socket.emit('loginError');

    }else{
      // Storage user name
      users.push(username);
      userInfo.push(user);
      io.emit('loginSuc');
      socket.nickName = username;
      // System notification
      io.emit('system', {
        name: username,
        status: '进入'
      })

      // Display people online
      io.emit('disUser',userInfo);
      console.log('One user login'); }});// Send window events
  socket.on('shake'.() = >{
    socket.emit('shake', {name:'you'
    })
    // Broadcast a message
    socket.broadcast.emit('shake', {name:socket.nickName
    })
  });
  // Send a message event
  socket.on('sendMsg'.(data) = >{

    let img = ' ';
    for(let i = 0; i < userInfo.length; i++){if(userInfo[i].username === socket.nickName){ img = userInfo[i].img; }}/ / radio
    socket.broadcast.emit('receiveMsg', {
      name: socket.nickName,
      img: img,
      msg: data.msgVal,
      color: data.color,
      type: data.type,
      side: 'left'.isUser:true
    });
    socket.emit('receiveMsg', {
      name: socket.nickName,
      img: img,
      msg: data.msgVal,
      color: data.color,
      type: data.type,
      side: 'right'.isUser: true
    });

  })

  // When disconnecting
  socket.on('disconnect'.() = >{
    console.log('Disconnect');

    let index = users.indexOf(socket.nickName);        
    if(index > -1){
      users.splice(index,1);// Delete user information
      userInfo.splice(index,1);// Delete user information

      io.emit('system', {name:socket.nickName,
        status:'leave'
      })
      io.emit('disUser,userInfo'); // re-render
      console.log('A user leaves'); }})}); http.listen(3000.() = > {
  console.log('Listen on port 3000');
})
Copy the code

client.js


const vm = new Vue({
  el: '#app'.data() {
    return {
      username: ' '.msgVal: ' '.isShow: true.nowDate: new Date().toTimeString().substr(0.8),
      userHtml: ' '.userInfo: [].isShake: false.timer: null.userSystem: [].color: '# 000000'.emojis: [].isEmojiShow: false}},methods: {
    handleClick() {
      var imgN = Math.floor(Math.random() * 4) + 1; // Randomly assign heads
      if (this.username) {
        this.socket.emit('login', {
          username: this.username,
          img: 'image/user' + imgN + '.jpg'}); }},shake() {
      this.isShake = true;
      clearTimeout(this.timer);
      this.timer = setTimeout(() = > {
        this.isShake = false;
      }, 500);
    },
    handleShake(e) {
      this.socket.emit('shake');
    },
    // Send a message
    handleSendMsg(e) {
      e.preventDefault();
      if (this.msgVal) {
        this.socket.emit('sendMsg', {
          msgVal: this.msgVal,
          color: this.color,
          type: 'text'
        })
        this.msgVal = ' '; }},scrollBottom() {
      this.$nextTick(() = > {
        const div = document.getElementById('messages'); div.scrollTop = div.scrollHeight; })},initEmoji() {
      for (let i = 0; i < 141; i++) {
        this.emojis.push(`image/emoji/emoji (${i + 1}).png`); }},// Click smile to bring up the emoticon
    handleSelectEmoji() {
      this.isEmojiShow = true;
    },
    handleDoubleSelectEmoji() {
      this.isEmojiShow = false;
    },
    // The user clicks send emoji
    handleEmojiImg(index) {
      this.isEmojiShow = false;
      this.msgVal = this.msgVal + `[emoji${index}] `;
    },
    handleLogout(e) {
      e.preventDefault();
      this.socket.emit('disconnect')}},created() {
    const socket = io();
    this.socket = socket;
    this.socket.on('loginSuc'.() = > {
      this.isShow = false;
    });
    this.socket.on('loginError'.() = > {
      alert('Username already exists, please re-enter');
      this.username = ' ';
    })
    // A system message is displayed
    this.socket.on('system'.(user) = > {
      console.log(user);
      this.userSystem.push(user);
      this.scrollBottom()
    })
    // Display people online
    this.socket.on('disUser'.(userInfo) = > {
      this.userInfo = userInfo;
    })
    // Listen for jitter events
    this.socket.on('shake'.(user) = > {
      this.userSystem.push(user);
      this.shake();
      this.scrollBottom();
    })
    this.socket.on('receiveMsg'.(obj) = > {
      let msg = obj.msg;
      let content = ' '
      if (obj.type === 'img') {}// Extract emoticons from text to render
      while (msg.indexOf('[') > -1) {  // It is better to extract the contents of [] with the re
        var start = msg.indexOf('[');
        var end = msg.indexOf('] ');
        content += '<span>' + msg.substr(0, start) + '</span>';
        content += '<img src="image/emoji/emoji%20(' + msg.substr(start + 6, end - start - 6) + ').png">';
        msg = msg.substr(end + 1, msg.length);
      }
      content += '<span>' + msg + '</span>';
      obj.msg = content;
      this.userSystem.push(obj);
      // The scrollbar is always at the bottom
      this.scrollBottom();
    })
    // Render emoticons
    this.initEmoji(); }})Copy the code