目录
前言
1.需求场景
2.页面效果图
3.线上地址
4.项目分析
大三的时候写过一个小demo,socket.io实现在线匿名聊天室
博客地址:https://blog.csdn.net/qq_30604453/article/details/64159036
demo地址:http://www.chunling.online:2800/(此处请不要计较样式,毕竟只是造着玩的demo)
示例图:
闲来无事,改造一下这个demo,做一个一对多的在线咨询客服的系统(不涉及数据库操作)。
很多企业官网都会有在线咨询客服的功能。【很想要找个样例截图一下,可是懒得找啊,看粗糙版的样例吧 = =】
希望达到的效果:(1)每个用户进入网站,立即生成一个匿名身份,可与客服进行在线交流。(2)单一客服,客服可以接收所有用户的信息,并进行回复交流。所以这是一个一对多的关系,多个用户,一个客服。
用户页面:如上的gif图。点击右下角“在线咨询”按钮,弹出对话框,点击“发送”按钮或者“回车”可以发送消息给客服。点击“关闭”按钮关闭对话框
客服页面:
左侧面板显示用户列表,点击可切换当前聊天对象,如有未读消息显示橙色背景,当前聊天对象显示蓝色背景。
中间面板则是聊天视窗。
右侧面板记录用户进入网站,离开网站的时间。
用户页面(潦草地做一下移动端的适配)
代码地址:https://gitee.com/wuchunling/contact-online
demo地址:
客服页面:http://www.chunling.online:2666/serve
用户页面:http://www.chunling.online:2666
在线客服系统主要用socket.io。简单回顾一下socketio的用法》》socket.io的基本用法
由于是基于之前的小demo改造的,此处就不用什么高大上的框架。后端采用:express + socket.io;前端采用:jQuery + socket.io
4.1 用户客户端
(1)一对多的关键在于如何标识一个用户?在这里,我采用最简单的方法,用时间戳来标识一个用户。当用户进入网站浏览时,生成一个时间戳,这个时间戳就标志这个用户。
$(document).ready(function () {
if (!sessionStorage.username) {
sessionStorage.username = new Date().getTime()
}
var username = sessionStorage.username;
})
(2)当用户进入网站浏览,告知服务端“用户进入网站”;当用户离开网站,告知服务端“用户离开了网站”
在这里触发两个事件 loginIn和loginOut(或者说两个消息),loginIn和loginOut需要服务端去监听,服务端再将这两个消息下发给客服端。
$(document).ready(function () {
var iosocket = io.connect();
if (!sessionStorage.username) {
sessionStorage.username = new Date().getTime()
}
var username = sessionStorage.username;
iosocket.emit('loginIn', username); // 进入页面
window.onunload = function () { // 关闭页面
iosocket.emit('loginOut', username)
}
})
(3)用户发送消息,主要是触发msgFromClient事件,将聊天信息和用户标识发送给服务端,服务端监听此事件,再将此消息转发给客服。
$(".btn-blue").click(function () { // 点击“发送”按钮进行发送消息
var text = $(".mesbox").val();
if (text != "") {
var data = {
name: username, // sessionStorage.username:标识用户的时间戳
content: text
}
iosocket.emit('msgFromClient', data);
$('.mesbox').val('');
sendHtml(username, text); // dom操作,更新聊天窗的视图
}
});
document.onkeydown = function (e) {
var ev = document.all ? window.event : e;
if (ev.keyCode == 13) { // 回车发送消息
$(".btn-blue").click();
stopDefaultKey(e)
}
}
(4)接收客服发送来的消息,主要是监听发送给当前用户的消息,此处用username接收,username即一开始进入页面生成的时间戳。
iosocket.on('connect', function () {
iosocket.on(username, function (msg) { // 获取客服消息
reciveHtml('客服', msg.content); // dom操作,更新聊天窗视图
});
});
此时,用户端的socket操作都已完成,剩下的都是dom操作,此处不赘述。
4.2 客服客户端
(1)客服端有一个很重要的数据结构,需要存储所有用户的消息记录。
var currentClient = { flag: -1, name: null } // 当前客户
var clientList = {} // 聊天记录
① currentClient表示当前聊天对象,name为用户标识(时间戳),flag为服务端传过来的客户计数(表示第几个用户),例如:
{
"name": "1573104726051",
"flag": "11"
}
② clientList存储所有用户的所有聊天记录,例如:
{
"1573104726051": {
"list": [
{
"from": "client",
"content": "helo world"
},
{
"from": "client",
"content": "this is Peter!"
},
{
"from": "client",
"content": "在吗?"
},
{
"from": "serve",
"content": "(*´▽`)ノノ"
}
],
"flag": 11,
"noread": false
},
"1573105081440": {
"list": [
{
"from": "client",
"content": "this is Jessica!"
},
{
"from": "client",
"content": "Hello!"
}
],
"flag": 12,
"noread": true
},
"1573105218701": {
"list": [],
"flag": 13,
"noread": false
},
"1573105224254": {
"list": [],
"flag": 14,
"noread": false
},
"1573109712098": {
"list": [
{
"from": "client",
"content": "你好呀"
},
{
"from": "client",
"content": "咨询一下"
}
],
"flag": 15,
"noread": true
}
}
(2)客服发送消息给用户
$('.btn-blue').click(function () { // 点击发送按钮
if (currentClient) {
var text = $(".mesbox").val();
if (text != "") {
var data = {
from: 'serve',
to: currentClient,
content: text
}
iosocketServe.emit('msgFromServe', data); // data发送给服务端
$('.mesbox').val(''); // 清空输入框
updateRoomView(0, data, currentClient.name); // dom操作
}
}
})
document.onkeydown = function (e) { // 回车发送
var ev = document.all ? window.event : e;
if (ev.keyCode == 13) {
$(".btn-blue").click();
}
}
(3)用户进入网站,通知客服端
$(document).ready(function () {
const iosocketServe = io.connect();
iosocketServe.on('connect', function () {
iosocketServe.on('clientInto', function (client) { // 用户进入网站
clientIn(client)
})
iosocketServe.on('clientLeave', function (client) { // 用户离开网站
clientOut(client)
})
iosocketServe.on('reciveClientMsg', function (msg) { // 接收用户消息
updateRoomView(1, msg, msg.name)
})
})
})
自此,客服端的socket操作已经完成,剩下的是dom操作,此处不赘述。
4.3 服务端
服务端主要起到桥梁的作用,连接用户与客服,将用户发送的消息转发给客服,将客服的消息转发给用户。
const path = require('path');
const express = require('express');
const socketio = require("socket.io");
const app = new express();
const port = 2666;
app.use(express.static('public'));
app.get('/*', (req, res) => {
const pathname = req.params['0'];
if (!pathname) {
res.sendFile(path.join(__dirname, 'views', 'index.html'));
return;
}
res.sendFile(path.join(__dirname, 'views', pathname + '.html'))
});
var server = app.listen(port, (error) => {
if (error) {
console.error(error);
} else {
console.info('==> Listening on port %s. Open up http://localhost:%s/ in your browser.', port, port);
}
});
const clientList = []
let onlinePeople = 0
socketio.listen(server).on('connection', function (socket) {
socket.on('msgFromClient', function (msg) { // 接收客户端发送的消息
socket.broadcast.emit('reciveClientMsg', {
from: 'client',
content: msg.content,
name: msg.name
}) // 发送给客服:客户发送消息
});
socket.on('loginIn', function (msg) { // 客户进入页面
onlinePeople++
let obj = {
name: msg, // 用时间戳来标识用户
flag: onlinePeople // 第几个
}
clientList.push(obj)
console.log( obj.name + '进入页面' )
socket.broadcast.emit('clientInto', obj) // 发送给客服:有新客户进入页面
});
socket.on('loginOut', function (msg) { // 客户退出页面
let obj = null
for(let i=0;i
完整代码戳:》》https://gitee.com/wuchunling/contact-online