vue 前端实现模拟微信公众号回复页面

312 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

1、背景

最近公司的项目中需要做跟微信公众号相关的业务,其中有一块是实现命中关键词回复,老板要求需要做一个类似聊天的模拟器在界面上,只要命中了设置的关键词,在聊天页面上就收到回复消息。 实现的伪界面如下图所示:

GIF.gif

在底部的输入框输入内容后,点击发送按钮,服务端收到发送的内容后进行回复。

我这个地方采用启动本地的websocket服务进行模拟的。 当本地的websocket服务接收到发送的内容后,回复消息给前端。

2、实现

2.1 服务端

服务端是通过node.js搭建的,通过使用nodejs-websocket

具体的实现步骤: 第一步:新建一个文件夹,如下图所示:

1661135861800.png

然后打开命令行工具,进入到刚才新建的目录中,执行如下安装指令:

npm i nodejs-websocket -S

执行完成后,在当前文件夹下新建一个文件app.js 并添加如下代码:

var ws = require("nodejs-websocket");

const PORT = 3500;
var server = ws.createServer((conn) => {
	console.log('连接成功!');
	
	conn.on("error",()=>{
		console.log("error");
	});
	
	conn.on("close",()=>{
		console.log("连接关闭!");
	});
	
	conn.on("text",(data)=>{
		console.log(`二狗子你对我说了..${data.toUpperCase()}`)
		conn.send(`二狗子你对我说了..${data.toUpperCase()}`)
	});
});

server.listen(PORT, ()=> {
	console.log("服务启动成功,监听端口" + PORT);
	
})

到这里服务端的代码已经写完了,在命令行中开启服务,执行

node app.js

执行nodejs服务.gif

服务启动成功,并监听了端口 3500

2.2 客户端实现

2.2.1 布局、样式

直接上代码

布局

<template>
<div class="phone-box">
  <div class="phone-box-head">
    <i class="el-icon-arrow-left"></i>
    <span>微信聊天</span>
    <i class="el-icon-chat-line-round"></i>
  </div>
  <div class="phone-box-content">
    <div class="chat-time">{{firstTime}}</div>
    <ul v-for="(sitem,index) in charDatas" :key="index">
      <div v-for="(item, sindex) of sitem.info" :key="sindex">
        <li class="other" v-if="!item.isOwn">
	 <img src="@/assets/delete.png" alt="">
         <div class="span-info">{{item.content}}</div>
        </li>
        <li class="own" v-else>
          <div class="span-info-right">{{item.content}}</div>
	  <img src="@/assets/data.png" alt="">
	</li>
      </div>
    </ul>
  </div>
  <div class="phone-box-footer">
    <el-input v-model="charText" placeholder="请输入内容"></el-input>
    <el-button @click="send">发送</el-button>
  </div>
</div>
</template>

样式

<style lang="less" scoped>
.phone-box{
  display: flex;
  flex-direction: column;
  width: 272px;
  height: 575px;
  border-radius: 10px;
  border: 5px solid black;
  background: #f5f5f5;
  align-content: center;
}

.phone-box-head {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  height: 38px;
  font-size: 24px;
}

.phone-box-content {
  width: 100%;
  height: calc(100% - 38px - 70px);
  border-bottom: 1px solid red;
  overflow-y: auto;
  ul,
  li {
    list-style: none;
    padding-inline-start: 0px; margin:0px; padding:0px;
    img {
      width: 40px;
      height: 40px;
    }
  }

  li{
    display: flex;
    flex-direction: row;
    margin-top: 10px
  }

  .other {
    float: left;
  }

.span-info {
  padding: 5px;
  position: relative;
  border-radius: 5px;
  border: 1px solid #fff;
  margin-left: 7px;
  background-color: #fff;
  margin-right: 40px;
}

.span-info::before{
  content: '';
  width: 0;
  height: 0;
  position: absolute;
  top: 11px;
  left: -7px;
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
  border-right: 7px solid #fff;
}

.span-info-right{
  padding: 5px;
  position: relative;
  border-radius: 5px;
  border: 1px solid #96EE65;
  margin-right: 10px;
  background-color: #96EE65;
  margin-left: 40px;
  align-items: center;
}

.span-info-right::before{
  content: '';
  position: absolute;
  width: 0;
  height: 0;
  /* 箭头靠右边 */
  top: 11px;
  right: -7px;
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
  border-left: 7px solid #96EE65;
}

 .own {
    float: right;
  }
}

.phone-box-footer {
  display: flex;
  flex-direction: row;
  align-items: center;
  width: 100%;
  height: 56px;
}

.chat-time {
  margin-top: 10px;
  text-align: center;
}
</style>

关注点: 1、span-info-right::before 中箭头的实现

2.2.2 js代码

data() {
  return {
  charText:"",
  socket:null,
  firstTime:"",
  firstTime1:"",
  curTime:"",
  curTime2:"",
  }
},
methods: {
      send(){
        if(this.charText){
          let infoArr = [];
          let obj = {imageUrl:this.deleteImg,content:this.charText,isOwn:true}
          infoArr.push(obj);
          if(!this.firstTime){
            //第一次发送
            this.firstTime1 = (Date.parse(new Date()))/1000;
            this.firstTime = this.getLocalTime(this.firstTime1)
            this.charDatas.push({sendTime:this.firstTime,info:infoArr});
            this.socket.send(this.charText);
            this.charText = "";
            console.log(this.charDatas);
            return;
          }
          if(this.firstTime){
            //非第一次发送
            this.curTime1 = (Date.parse(new Date()))/1000;
            let calc=  this.intervalTime(this.firstTime1,this.curTime1)
            if(calc >= 1){
              console.log("calc > 1")
              this.curTime = this.getLocalTime(this.curTime1);
              this.charDatas.push({sendTime:this.curTime,info:infoArr})
            } else {
              console.log("calc <= 1")
              this.charDatas[this.charDatas.length - 1].info.push(obj)
            }
          }
          console.log(this.charDatas);
          this.socket.send(this.charText);
          this.charText = "";
         
        }
      },
      
      //创建websocket
      createWebSocket(){
          if(!this.socket){
            this.socket = new WebSocket("ws://127.0.0.1:3500");
            this.socket.addEventListener("open",()=>{
            console.log("建立socket连接成功!")
          })

          this.addSocketResultListen();
          }
        },
        
        //添加socket结果监听
        addSocketResultListen(){
          this.socket.addEventListener("message",(e)=>{
            let obj = {imageUrl:this.deleteImg,content:e.data,isOwn:false}
            this.charDatas[this.charDatas.length - 1].info.push(obj);
          })
        },
        
 intervalTime(startTime,endTime) {
    var date1 = startTime; 
    var date2 = endTime; //结束时间
    var date3 =  (date2- date1)*1000; //时间差的毫秒数
    //计算出相差天数
    var days = Math.floor(date3 / (24 * 3600 * 1000));
    //计算出小时数

    var leave1 = date3 % (24 * 3600 * 1000); //计算天数后剩余的毫秒数
    var hours = Math.floor(leave1 / (3600 * 1000));
    //计算相差分钟数
    var leave2 = leave1 % (3600 * 1000); //计算小时数后剩余的毫秒数
    var minutes = Math.floor(leave2 / (60 * 1000));

    //计算相差秒数
    var leave3 = leave2 % (60 * 1000); //计算分钟数后剩余的毫秒数
    var seconds = Math.round(leave3 / 1000);
    return minutes
},

getLocalTime(nS) {     
   return new Date(parseInt(nS) * 1000).toLocaleString().replace(/:\d{1,2}$/,' ');     
}

 created() {
   this.createWebSocket();
 },

关注点: 1、createWebSocket 创建socket连接 2、计算两次发消息之间的时间差值,如果大于1分钟,则将数据划分到另一个时间组中。

数据结构如下:

数据结构.gif

好了,到这里基本实现了本地发消息,收到本地websocket的回复了。