携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
1、背景
最近公司的项目中需要做跟微信公众号相关的业务,其中有一块是实现命中关键词回复,老板要求需要做一个类似聊天的模拟器在界面上,只要命中了设置的关键词,在聊天页面上就收到回复消息。 实现的伪界面如下图所示:
在底部的输入框输入内容后,点击发送按钮,服务端收到发送的内容后进行回复。
我这个地方采用启动本地的websocket服务进行模拟的。 当本地的websocket服务接收到发送的内容后,回复消息给前端。
2、实现
2.1 服务端
服务端是通过node.js搭建的,通过使用nodejs-websocket
具体的实现步骤: 第一步:新建一个文件夹,如下图所示:
然后打开命令行工具,进入到刚才新建的目录中,执行如下安装指令:
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
服务启动成功,并监听了端口 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分钟,则将数据划分到另一个时间组中。
数据结构如下:
好了,到这里基本实现了本地发消息,收到本地websocket的回复了。