"use strict";
const common_vendor = require("../../common/vendor.js");
const sheep_index = require("../../sheep/index.js");
function useChatWebSocket(socketConfig) {
let SocketIo = null;
const state = common_vendor.reactive({
chatDotNum: 0,
//总状态红点
chatList: [],
//会话信息
customerUserInfo: {},
//用户信息
customerServerInfo: {
//客服信息
title: "连接中...",
state: "connecting",
avatar: null,
nickname: ""
},
socketState: {
isConnect: true,
//是否连接成功
isConnecting: false,
//重连中,不允许新的socket开启。
tip: ""
},
chatHistoryPagination: {
page: 0,
//当前页
list_rows: 10,
//每页条数
last_id: 0,
//最后条ID
lastPage: 0,
//总共多少页
loadStatus: "loadmore"
//loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
},
templateChatList: [],
//猜你想问
chatConfig: {},
// 配置信息
isSendSucces: -1
// 是否发送成功 -1=发送中|0=发送成功|1发送失败
});
const socketInit = (config, callBack) => {
state.chatConfig = config;
if (SocketIo && SocketIo.connected)
return;
if (state.socketState.isConnecting)
return;
SocketIo = common_vendor.io(config.chat_domain, {
reconnection: true,
// 默认 true 是否断线重连
reconnectionAttempts: 5,
// 默认无限次 断线尝试次数
reconnectionDelay: 1e3,
// 默认 1000,进行下一次重连的间隔。
reconnectionDelayMax: 5e3,
// 默认 5000, 重新连接等待的最长时间 默认 5000
randomizationFactor: 0.5,
// 默认 0.5 [0-1],随机重连延迟时间
timeout: 2e4,
// 默认 20s
transports: ["websocket", "polling"],
// websocket | polling,
...config
});
SocketIo.on("connect", async (res) => {
socketReset(callBack);
console.log("socket:connect");
});
SocketIo.on("message", (res) => {
if (res.error === 0) {
res.data;
state.chatList.push(formatMessage(res.data.message));
callBack && callBack();
}
});
SocketIo.on("customer_service_access", (res) => {
if (res.error === 0) {
editCustomerServerInfo({
title: res.data.customer_service.name,
state: "online",
avatar: res.data.customer_service.avatar
});
state.chatList.push(formatMessage(res.data.message));
}
});
SocketIo.on("waiting_queue", (res) => {
if (res.error === 0) {
editCustomerServerInfo({
title: res.data.title,
state: "waiting",
avatar: ""
});
}
});
SocketIo.on("no_customer_service", (res) => {
if (res.error === 0) {
editCustomerServerInfo({
title: "暂无客服在线...",
state: "waiting",
avatar: ""
});
}
state.chatList.push(formatMessage(res.data.message));
});
SocketIo.on("customer_service_online", (res) => {
if (res.error === 0) {
editCustomerServerInfo({
title: res.data.customer_service.name,
state: "online",
avatar: res.data.customer_service.avatar
});
}
});
SocketIo.on("customer_service_offline", (res) => {
if (res.error === 0) {
editCustomerServerInfo({
title: res.data.customer_service.name,
state: "offline",
avatar: res.data.customer_service.avatar
});
}
});
SocketIo.on("customer_service_busy", (res) => {
if (res.error === 0) {
editCustomerServerInfo({
title: res.data.customer_service.name,
state: "busy",
avatar: res.data.customer_service.avatar
});
}
});
SocketIo.on("customer_service_break", (res) => {
if (res.error === 0) {
editCustomerServerInfo({
title: "客服服务结束",
state: "offline",
avatar: ""
});
state.socketState.isConnect = false;
state.socketState.tip = "当前服务已结束";
}
state.chatList.push(formatMessage(res.data.message));
});
SocketIo.on("custom_error", (error) => {
editCustomerServerInfo({
title: error.msg,
state: "offline",
avatar: ""
});
console.log("custom_error:", error);
});
SocketIo.on("error", (error) => {
console.log("error:", error);
});
SocketIo.on("connect_error", (error) => {
console.log("connect_error");
});
SocketIo.on("connect_timeout", (error) => {
console.log(error, "connect_timeout");
});
SocketIo.on("disconnect", (error) => {
console.log(error, "disconnect");
});
SocketIo.on("reconnect", (error) => {
console.log(error, "reconnect");
});
SocketIo.on("reconnect_attempt", (error) => {
state.socketState.isConnect = false;
state.socketState.isConnecting = true;
editCustomerServerInfo({
title: `重连中,第${error}次尝试...`,
state: "waiting",
avatar: ""
});
console.log(error, "reconnect_attempt");
});
SocketIo.on("reconnecting", (error) => {
console.log(error, "reconnecting");
});
SocketIo.on("reconnect_error", (error) => {
console.log("reconnect_error");
});
SocketIo.on("reconnect_failed", (error) => {
state.socketState.isConnecting = false;
editCustomerServerInfo({
title: `重连失败,请刷新重试~`,
state: "waiting",
avatar: ""
});
console.log(error, "reconnect_failed");
state.isSendSucces = 1;
});
};
const socketReset = (callBack) => {
state.chatList = [];
state.chatHistoryList = [];
state.chatHistoryPagination = {
page: 0,
per_page: 10,
last_id: 0,
totalPage: 0
};
socketConnection(callBack);
};
const socketClose = () => {
SocketIo.emit("customer_logout", {}, (res) => {
console.log("socket:退出", res);
});
};
const socketTest = () => {
SocketIo.emit("test", {}, (res) => {
console.log("test:test", res);
});
};
const socketSendMsg = (data, sendMsgCallBack) => {
state.isSendSucces = -1;
state.chatList.push(data);
sendMsgCallBack && sendMsgCallBack();
SocketIo.emit(
"message",
{
message: formatInput(data),
...data.customData
},
(res) => {
state.isSendSucces = res.error;
}
);
};
const socketConnection = (callBack) => {
SocketIo.emit(
"connection",
{
auth: "user",
token: common_vendor.index.getStorageSync("socketUserToken") || "",
session_id: common_vendor.index.getStorageSync("socketSessionId") || ""
},
(res) => {
if (res.error === 0) {
socketCustomerLogin(callBack);
common_vendor.index.setStorageSync("socketSessionId", res.data.session_id);
state.customerUserInfo = res.data.chat_user;
state.socketState.isConnect = true;
} else {
editCustomerServerInfo({
title: `服务器异常!`,
state: "waiting",
avatar: ""
});
state.socketState.isConnect = false;
}
}
);
};
const getUserToken = async (id) => {
const res = await chat.unifiedToken();
if (res.error === 0) {
common_vendor.index.setStorageSync("socketUserToken", res.data.token);
}
return res;
};
const socketCustomerLogin = (callBack) => {
SocketIo.emit(
"customer_login",
{
room_id: state.chatConfig.room_id
},
(res) => {
state.templateChatList = res.data.questions.length ? res.data.questions : [];
state.chatList.push({
from: "customer_service",
// 用户customer右 | 顾客customer_service左 | 系统system中间
mode: "template",
// goods,order,image,text,system
date: (/* @__PURE__ */ new Date()).getTime(),
//时间
content: {
//内容
list: state.templateChatList
}
});
res.error === 0 && socketHistoryList(callBack);
}
);
};
const socketHistoryList = (historyCallBack) => {
state.chatHistoryPagination.loadStatus = "loading";
state.chatHistoryPagination.page += 1;
SocketIo.emit("messages", state.chatHistoryPagination, (res) => {
if (res.error === 0) {
state.chatHistoryPagination.total = res.data.messages.total;
state.chatHistoryPagination.lastPage = res.data.messages.last_page;
state.chatHistoryPagination.page = res.data.messages.current_page;
res.data.messages.data.forEach((item) => {
item.message_type && state.chatList.unshift(formatMessage(item));
});
state.chatHistoryPagination.loadStatus = state.chatHistoryPagination.page < state.chatHistoryPagination.lastPage ? "loadmore" : "nomore";
if (state.chatHistoryPagination.last_id == 0) {
state.chatHistoryPagination.last_id = res.data.messages.data.length ? res.data.messages.data[0].id : 0;
}
state.chatHistoryPagination.page === 1 && historyCallBack && historyCallBack();
}
});
};
const editCustomerServerInfo = (data) => {
state.customerServerInfo = {
...state.customerServerInfo,
...data
};
};
const showTime = (item, index) => {
if (common_vendor.unref(state.chatList)[index + 1]) {
let dateString = common_vendor.dayjs(common_vendor.unref(state.chatList)[index + 1].date).fromNow();
if (dateString === common_vendor.dayjs(common_vendor.unref(item).date).fromNow()) {
return false;
} else {
dateString = common_vendor.dayjs(common_vendor.unref(item).date).fromNow();
return true;
}
}
return false;
};
const formatTime = (time) => {
let diffTime = (/* @__PURE__ */ new Date()).getTime() - time;
if (diffTime > 28 * 24 * 60 * 1e3) {
return common_vendor.dayjs(time).format("MM/DD HH:mm");
}
if (diffTime > 360 * 28 * 24 * 60 * 1e3) {
return common_vendor.dayjs(time).format("YYYY/MM/DD HH:mm");
}
return common_vendor.dayjs(time).fromNow();
};
const getFocus = (virtualNode) => {
if (window.getSelection) {
let chatInput2 = common_vendor.unref(virtualNode);
chatInput2.focus();
let range = window.getSelection();
range.selectAllChildren(chatInput2);
range.collapseToEnd();
} else if (document.selection) {
let range = document.selection.createRange();
range.moveToElementText(chatInput);
range.collapse(false);
range.select();
}
};
const upload = (name, file) => {
return new Promise((resolve, reject) => {
let data = new FormData();
data.append("file", file, name);
data.append("group", "chat");
ajax({
url: "/upload",
method: "post",
headers: {
"Content-Type": "multipart/form-data"
},
data,
success: function(res) {
resolve(res);
},
error: function(err) {
reject(err);
}
});
});
};
const onPaste = async (e) => {
let paste = e.clipboardData || window.clipboardData;
let filesArr = Array.from(paste.files);
filesArr.forEach(async (child) => {
if (child && child.type.includes("image")) {
e.preventDefault();
let file = child;
const img = await readImg(file);
const blob = await compressImg(img, file.type);
const { data } = await upload(file.name, blob);
let image = ``;
document.execCommand("insertHTML", false, image);
} else {
document.execCommand("insertHTML", false, paste.getData("text"));
}
});
};
const onDrop = async (e) => {
e.preventDefault();
let filesArr = Array.from(e.dataTransfer.files);
filesArr.forEach(async (child) => {
if (child && child.type.includes("image")) {
let file = child;
const img = await readImg(file);
const blob = await compressImg(img, file.type);
const { data } = await upload(file.name, blob);
let image = `
`;
document.execCommand("insertHTML", false, image);
} else {
ElMessage({
message: "禁止拖拽非图片资源",
type: "warning"
});
}
});
};
const formatChatInput = (virtualNode, formatInputCallBack) => {
let res = "";
let elemArr = Array.from(virtualNode.childNodes);
elemArr.forEach((child, index) => {
if (child.nodeName === "#text") {
res += child.nodeValue;
if (
//文本节点的后面是图片,并且不是emoji,分开发送。输入框中的图片和文本表情分开。
elemArr[index + 1] && elemArr[index + 1].nodeName === "IMG" && elemArr[index + 1] && elemArr[index + 1].name !== "emoji"
) {
const data = {
from: "customer",
mode: "text",
date: (/* @__PURE__ */ new Date()).getTime(),
content: {
text: filterXSS(res)
}
};
formatInputCallBack && formatInputCallBack(data);
res = "";
}
} else if (child.nodeName === "BR") {
res += "
";
} else if (child.nodeName === "IMG") {
if (child.name !== "emoji") {
let srcReg = /src=[\'\']?([^\'\']*)[\'\']?/i;
let src = child.outerHTML.match(srcReg);
const data = {
from: "customer",
mode: "image",
date: (/* @__PURE__ */ new Date()).getTime(),
content: {
url: src[1],
path: src[1].replace(/http:\/\/[^\/]*/, "")
}
};
formatInputCallBack && formatInputCallBack(data);
} else {
res += child.outerHTML;
}
} else if (child.nodeName === "DIV") {
res += `