代码逻辑介绍:
isUserTyping记录用户是否输入消息,用setInterval定时50秒检测并改变用户状态为false,发送消息、或者自动打印一轮完毕后,置为true。
sendMessage是处理发送消息的函数;
appendMessage是将消息追加到聊天框的函数;
sendUserDataToServer是将用户信息发送大服务器的函数;
getCurrentTime为时间戳字符串拼接函数,为了格式的统一,建议客户端、服务器(windows开发环境/linux上线)均使用此格式。
preventDefault阻止默认事件,回车时不添加换行。
javascript
messageInput.addEventListener('keydown', function (event) {
if (event.keyCode === 13) { // 回车键
event.preventDefault();
sendMessage();
}
});
以下为全部代码:
javascript
document.addEventListener('DOMContentLoaded', function () {
// 关键词-回复映射表
const replies = {
"你好": "你好,请问有什么可以帮助您的?",
"订单查询": "请提供订单号,我将为您查询订单信息。",
"产品问题": "请详细描述您遇到的问题,我会尽快帮您解决。",
"投诉": "抱歉让您不满意,请您提供详细信息,我们将尽快处理。",
"钱": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
"价格": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
"成考": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
"国家开放大学": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
"国开": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
"电大": "您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
"成人大学": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
"在的": "请您放心,我们不会透露您的个人隐私,请问您的【手机号】是?方便我们进一步向您介绍",
"再见": "再见,祝您生活愉快!"
};
const over20s = [
'您好,还在吗?',
'那我将院校专业、报考条件、报名时间、报考流程和费用明细发您',
'您先了解看看,报不报名再决定哈,您接收资料的【手机号码或者微信】是?',
'免费短信,您说下您的【手机号】,建议您看看,是系统发不会打扰您的',
'是这样的,内容是保存在短信系统的,我复制不了,只是发给您先了解,您了解后有需要再联系我们。',
'我们是正规机构,不会泄露您的信息,可以放心的,您的【手机号码】是?',
];
// 获取聊天容器、消息输入框和发送按钮元素
const chatContainer = document.querySelector('.chat_history');
const messageInput = document.querySelector('.inputWindow');
const sendButton = document.querySelector('.btn');
let isUserTyping = false;
// 监听发送按钮点击事件
sendButton.addEventListener('click', sendMessage);
// 监听消息输入框的键盘事件
messageInput.addEventListener('keydown', function (event) {
if (event.keyCode === 13) { // 回车键
event.preventDefault();
sendMessage();
}
});
// 发送消息的函数
function sendMessage() {
isUserTyping = true;
let message = messageInput.innerHTML.trim();
const regex = /\s+/g; // \s 匹配任何空白字符,包括空格、制表符、换页符等,+ 表示匹配一个或多个
message = message.replace(regex, '');
if (message === '') {
return; // 如果消息为空则不处理
}
appendMessage('user', message); // 添加用户发送的消息到聊天窗口
// 检测用户输入的是否是手机号
if (message.match(/\d{11}/)) {
let reply = "感谢您留下手机号,我们的专业顾问会尽快与您联系,为您提供详细的咨询服务。";
appendMessage('agent', reply);
sendUserDataToServer(message); //将手机号发送到服务器
} else {
const reply = getReply(message); // 获取客服回复的消息
setTimeout(() => { appendMessage('agent', reply); }, 1500); // 延迟一秒后添加客服回复的消息
}
messageInput.innerHTML = ''; // 清空输入框
}
// 根据用户消息获取客服回复的函数
function getReply(message) {
for (const keyword in replies) { // 遍历关键词-回复映射表
if (message.includes(keyword)) { // 如果用户消息包含关键词
return replies[keyword]; // 返回对应的回复消息
}
}
return "抱歉,是这样的,内容是保存在短信系统的,我复制不了,只是发给您先了解,您了解后有需要再联系我们。所以您的【电话号码】是?"; // 默认回复消息
}
/**
* 添加消息到聊天窗口的函数
* chatContainer(.chat_history)
* messageDiv(消息框)
* avatarImg(头像)
* messageContentDiv(消息容器)
* timestampSpan(时间戳)
* textDiv(文本信息)
*
* @param sender 发送消息的类型,user或者agent
* @param text 需要添加到聊天窗口的字符串
*
*/
function appendMessage(sender, text) {
const messageDiv = document.createElement('div'); // 创建消息容器元素
messageDiv.classList.add('box-message', sender + '-message');
const avatarImg = document.createElement('img'); // 创建头像元素
avatarImg.src = sender === 'user' ? './image/beauty.png' : './image/beauty.png';
messageDiv.appendChild(avatarImg);
const messageContentDiv = document.createElement('div'); // 创建消息内容容器元素
messageContentDiv.classList.add('message-content');
messageDiv.appendChild(messageContentDiv);
const timestampSpan = document.createElement('span');
timestampSpan.classList.add('timestamp');
timestampSpan.textContent = getCurrentTime();
messageContentDiv.appendChild(timestampSpan);
const textDiv = document.createElement('div');
textDiv.textContent = text;
messageContentDiv.appendChild(textDiv);
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
/**
* 如果用户超过20秒没有发送消息,自动打印消息
*/
let printOver20 = setInterval(fn, 20 * 1000);
function fn() {
if (!isUserTyping) {
clearInterval(printOver20); // 先暂停检测定时器,避免在打印时间内也计入检测时间
let index = 0;
// 间歇性定时器是非阻塞的
const replyPrintIntervalId = setInterval(() => {
if (isUserTyping) { // 如果用户开始输入,那么停止打印
console.log("isUserTyping === true");
clearInterval(replyPrintIntervalId);
}
appendMessage('agent', over20s[index++]);
if (index === over20s.length) { // 停止循环打印
clearInterval(replyPrintIntervalId);
console.log('定时器结束...');
}
}, 5000);
setTimeout(() => { // 打印结束后重新开启定时器
isUserTyping = true; // 循环走完用户输入状态为true
printOver20 = setInterval(fn, 30 * 1000);
}, 25 * 1000);
}
}
/**
* 定时检查用户输入
*/
setInterval(() => {
if (messageInput.innerHTML === "" && isUserTyping !== false) {
isUserTyping = false;
}
}, 50 * 1000)
function getCurrentTime() {
const date = new Date(); // 获取当前时间
const year = date.getFullYear();
const month = `${date.getMonth() + 1}`.padStart(2, '0'); // 月份是从0开始的,所以要加1
const day = `${date.getDate()}`.padStart(2, '0');
const hours = date.getHours();
const minutes = `${date.getMinutes()}`.padStart(2, '0');
const seconds = `${date.getSeconds()}`.padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 发送用户信息到服务器端
function sendUserDataToServer(userData) {
const nowTime = getCurrentTime();
// 将用户对象转换为JSON字符串
let userDataString = {
phone: `${userData}`
};
userDataString = JSON.stringify(userDataString);
console.log(userDataString);
console.log("This is in typeof ---", typeof userDataString);
// 发送POST请求到服务器
fetch('http://xxx.xx.xx.xxx:3333', {
mode: 'no-cors',
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: userDataString,
})
.then(response => {
if (!response.ok) {
// throw new Error('Network response was not ok');
console.log('then')
}
return response.json();
})
.then(data => {
console.log('Success:', data);
// 这里可以添加其他成功处理逻辑,比如更新UI等
})
.catch(error => {
// console.error('Error:', error);
console.log('catch')
// 这里可以添加错误处理逻辑,比如显示错误消息等
});
}
});
代码中有三个定时器:
一个定时器负责间隔5s打印一次;
一个定时器负责每隔25检测一下用户是否输入;
一个定时器负责50s检查一下将用户输入状态变为false。