quasar+node实现一个简单的群聊天

quasar+node实现一个简单的聊天

npm install -g @quasar/cli

安装:Wampserver

新建chat_data数据库
chat_data下面新建login表和message
node 初始化

npm init -y

服务器结构目录
在这里插入图片描述
mySql.js

let mysqlStyle = require('mysql')
let mysql = mysqlStyle.createConnection({
  host: "localhost",
  user: "root",
  password: '',
  database: "chat_data"
})
module.exports = {
  mysql
}

main.js


const app = require('express')()
const httpServer = require('http').Server(app)
const io = require('socket.io')(httpServer,{ cors: true });
const bodyParser = require('body-parser')
app.use(bodyParser.json());  //body-parser 解析json格式数据
app.use(bodyParser.urlencoded({            //此项必须在 bodyParser.json 下面,为参数编码
  extended: true
}));
let mysqlUserinfo = require('./mySql')
httpServer.listen(3000,()=>{
  console.log('服务启动')
})
app.all("*",function(req,res,next){
  //设置允许跨域的域名,*代表允许任意域名跨域
  res.header("Access-Control-Allow-Origin","*");
  //允许的header类型
  res.header("Access-Control-Allow-Headers","content-type");
  //跨域允许的请求方式
  res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
  if (req.method.toLowerCase() == 'options')
   return res.send(200);  //让options尝试请求快速结束
  else
   return  next();
})
app.get('/',(req,res)=>{
  res.sendFile(__dirname + '/index.html')
})
//cunnent 用来控制人数
let cunnent = 0,userId = {}
io.on('connect', socket => {
  let obj = {}
  socket.on('login',data=>{
    let userId = data.userId
    let image = data.image[0].image
    let userName = data.userNmae
    let id = Math.floor(Math.random()*100000000000000)
    let sql = `INSERT INTO login (id,userName,image) VALUES (?,?,?)`;
    mysqlUserinfo.mysql.query(sql,[id,userName,image],(err,data)=>{
      cunnent++
       obj = {
        id,userName,image,cunnent,data,userId,
        xxx: `用户${userName}进入聊天室`,ok:true // 告诉群里面的人XX加入群聊
      }
      socket.emit('loginDate',obj)
    })
  })
  socket.on('request',data=>{
    io.emit('name',obj)
  }),
  socket.on('messageValue',data=>{
    console.log(data.data.message)
    userId = JSON.stringify(data.data)
    mysqlUserinfo.mysql.query(`INSERT INTO message VALUES ('${userId}')`,[userId],(err,result)=>{
      if (result.protocol41){
        let sqlDate = `SELECT * FROM message`
        mysqlUserinfo.mysql.query(sqlDate,(err,data)=>{
          io.emit('redirecTsenOut',data) // 告诉全部人的消息
        })
      }

    })
  })
  // 离开的时候告诉全部的人谁谁离开了
  socket.on('disconnect',_=>{
    cunnent--
    io.emit('delurlName',obj)
  })
});

客户端(

npm i vue-socket.io
npm i socket.io-client

app.js

import VueSocketIO from 'vue-socket.io';
import socketio from 'socket.io-client';
Vue.use(new VueSocketIO({
  debug: true,
  connection: socketio('ws://localhost:3000'),
}))
import App from 'app/src/App.vue' // 先引在渲染页面

结构在这里插入图片描述
index.vue文件

<template>
  <div>
    <div>
      请输入用户名:<input type="text" placeholder="请输入用户名" v-model="userName">
    </div>
    <div>
      <div>
        请选择头像
      </div>
      <div class="image" v-for="(item,index) in imageList" :key="item.id" >
        <img :src="item.image" alt="" :class="index === indexActive ? 'active' : ''" @click="activeBtn(item,index)">
      </div>
    </div>
    <div>
      <button class="login" @click="loginBtn">登录</button>
    </div>
  </div>
</template>

<script lang="ts">
import {nmaeImage} from 'components/models';
import axios from 'axios'
// import ExampleComponent from 'components/CompositionComponent.vue';
import { defineComponent, ref } from '@vue/composition-api';
interface dataObj{
  indexActive:number | Boolean,
  itemArray: Array,
  userName:string
}
export default defineComponent({
  data():dataObj{
    return {
      indexActive: 100,
      itemArray: [],
      userName:""
    }
  },
  setup(){
    const imageList = ref<nmaeImage[]>([
      {id:1,image:require('../assets/images/1.png')},
      {id:2,image:require('../assets/images/2.png')},
      {id:3,image:require('../assets/images/3.png')},
      {id:4, image:require('../assets/images/4.png')},
      {id:5,image:require('../assets/images/5.png')},
    ])
    return {imageList}
  },
  methods:{
    activeBtn(item:object,index:number){
      this.itemArray = []
     if(this.indexActive === index){
        this.itemArray = []
        this.indexActive = !this.indexActive
     } else {
        this.itemArray.push(item)
        this.indexActive = index
     }
    },
   async loginBtn(){
      if(this.userName === "" || this.itemArray.length === 0 ) return
      const data = {
        image:this.itemArray,
        userNmae: this.userName,
        userId:1
      }
      this.$socket.emit('login',data)
    }
  },
  sockets:{
    loginDate(data:any){
      if(data){
        this.$router.push({path:'/mainLayout',query:{params:JSON.stringify(data)}})
      }
    }
  }
})
</script>
<style lang="less" scope>
  .image{
    display: inline-block;
    img{
      width: 100px;
      height: 100px;
      margin: 10px;
      cursor: pointer;
    }
    .active{
      border: 3px solid red;
    }
  }
  .uwernam{
    color:red;
  }
  .welcomeUsername{
    color:rgb(214, 8, 8);
  }
</style>

models.ts

export interface nmaeImage {
  id: Number | string
  image: string;
}

MainLayout.vue

<template>
  <div>
    <div>
      当前人数 -------------------{{
        cunnent < 2 ? cunnent + 1 : cunnent + 1 || 1
      }}
    </div>
    <div>
      当前用户:---------- <span class="uwernam">{{ userName.userName }}</span>
    </div>
    <div class="huanyingdsadas">
      <div v-for="(item, index) in huanyingh" :key="index">
        欢迎-----<span class="welcomeUsername">{{ item.xxx }}</span>
      </div>
    </div>
    <div class="q-pa-md row justify-center">
      <div
        style="width: 100%; max-width: 600px;height:350px;overflow-y:auto"
        id="scollView"
      >
        <div v-for="(item, index) in redirecTsenOutDateMessage" :key="item.id">
          <div class="messageValue" v-if="item.id === userName.id">
            <!-- 当前用户 -->
            <q-chat-message
              :name="item.userName"
              :avatar="item.image"
              :text="[item.message]"
              sent
            />
          </div>
          <div class="redirecTsenOutDateMessage" v-else>
            <q-chat-message
              :name="item.userName"
              :avatar="item.image"
              :text="[item.message]"
            />
          </div>
        </div>
      </div>
    </div>
    <div class="border-item">
      <textarea v-model="message" name="" id="" cols="30" rows="10"></textarea>
      <q-btn color="primary" label="发送" @click="btnClickMessageValue" />
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from '@vue/composition-api';
export default defineComponent({
  name: 'MainLayout',
  data() {
    return {
      userName: [],
      message: '',
      huanyingh: [],
      cunnent: 0,
      redirecTsenOutDate: {},
      messageValue: [],
      redirecTsenOutDateMessage: [],
      redirecTsenOutDateMessageValue: false
    };
  },
  methods: {
    btnClickMessageValue() {
      if (this.message === '') return;
      this.messageValue.push(this.message);
      this.redirecTsenOutDateMessage = [];
      let result = {
        data: this.userName
      };
      this.$socket.emit('messageValue', result);
      this.message = '';
    }
  },
  mounted() {
    let params = this.$route.query.params || {};
    this.userName = JSON.parse(params); // 获取当前用户
    this.$socket.emit('request', 1111);
  },
  sockets: {
    connect() {
      console.log('链接成功');
    },
    name(data: any) {
      console.log(data);
      this.cunnent = data.cunnent;
      this.huanyingh.push(data);
    },
    delurlName(data: any) {
      let index = this.huanyingh.findIndex(element => element.id === data.id);
      let xxx = {
        xxx: `用户${data.userName}离开了群聊`
      };
      this.huanyingh.push(xxx);
      this.huanyingh.splice(index, 1);
    },
    redirecTsenOut(data?: any) { // 消息
      if (data) {
        data.forEach((element: any) => {this.redirecTsenOutDateMessage.push(JSON.parse(element.userId));
          console.log(this.redirecTsenOutDateMessage);
        });
      }
    }
  }
});
</script>
<style lang="less">
.border-item {
  width: 600px;
  border: 1px solid #000;
  margin: 40px;
  margin: 0 auto;
  textarea {
    width: 100%;
    border: none;
    resize: none;
    border-bottom: 1px solid #000;
  }
  textarea:focus-visible {
    outline: none;
  }
}
.huanyingdsadas {
  width: 400px;
  height: 200px;
  float: left;
}
</style>

注意:在测试的时候请多跑几个页面

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值