123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648 |
- "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 = `<img class="full-url" src='${data.fullurl}'>`;
- 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 = `<img class="full-url" src='${data.fullurl}' >`;
- 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 += "<br/>";
- } 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 += `<div style='width:200px; white-space: nowrap;'>${child.outerHTML}</div>`;
- }
- });
- if (res) {
- const data = {
- from: "customer",
- mode: "text",
- date: (/* @__PURE__ */ new Date()).getTime(),
- content: {
- text: filterXSS(res)
- }
- };
- formatInputCallBack && formatInputCallBack(data);
- }
- common_vendor.unref(virtualNode).innerHTML = "";
- };
- const callBackNotice = (res) => {
- ElNotification({
- title: "socket",
- message: res.msg,
- showClose: true,
- type: res.error === 0 ? "success" : "warning",
- duration: 1200
- });
- };
- const formatInput = (message) => {
- let obj = {};
- switch (message.mode) {
- case "text":
- obj = {
- message_type: "text",
- message: message.content.text
- };
- break;
- case "image":
- obj = {
- message_type: "image",
- message: message.content.path
- };
- break;
- case "goods":
- obj = {
- message_type: "goods",
- message: message.content.item
- };
- break;
- case "order":
- obj = {
- message_type: "order",
- message: message.content.item
- };
- break;
- }
- return obj;
- };
- const formatMessage = (message) => {
- let obj = {};
- switch (message.message_type) {
- case "system":
- obj = {
- from: "system",
- // 用户customer左 | 顾客customer_service右 | 系统system中间
- mode: "system",
- // goods,order,image,text,system
- date: message.create_time * 1e3,
- //时间
- content: {
- //内容
- text: message.message
- }
- };
- break;
- case "text":
- obj = {
- from: message.sender_identify,
- mode: message.message_type,
- date: message.create_time * 1e3,
- //时间
- sender: message.sender,
- content: {
- text: message.message,
- messageId: message.id
- }
- };
- break;
- case "image":
- obj = {
- from: message.sender_identify,
- mode: message.message_type,
- date: message.create_time * 1e3,
- //时间
- sender: message.sender,
- content: {
- url: sheep_index.sheep.$url.cdn(message.message),
- messageId: message.id
- }
- };
- break;
- case "goods":
- obj = {
- from: message.sender_identify,
- mode: message.message_type,
- date: message.create_time * 1e3,
- //时间
- sender: message.sender,
- content: {
- item: message.message,
- messageId: message.id
- }
- };
- break;
- case "order":
- obj = {
- from: message.sender_identify,
- mode: message.message_type,
- date: message.create_time * 1e3,
- //时间
- sender: message.sender,
- content: {
- item: message.message,
- messageId: message.id
- }
- };
- break;
- }
- return obj;
- };
- const readImg = (file) => {
- return new Promise((resolve, reject) => {
- const img = new Image();
- const reader = new FileReader();
- reader.onload = function(e) {
- img.src = e.target.result;
- };
- reader.onerror = function(e) {
- reject(e);
- };
- reader.readAsDataURL(file);
- img.onload = function() {
- resolve(img);
- };
- img.onerror = function(e) {
- reject(e);
- };
- });
- };
- const compressImg = (img, type = "image/jpeg", mx = 1e3, mh = 1e3, quality = 1) => {
- return new Promise((resolve, reject) => {
- const canvas = document.createElement("canvas");
- const context = canvas.getContext("2d");
- const { width: originWidth, height: originHeight } = img;
- const maxWidth = mx;
- const maxHeight = mh;
- let targetWidth = originWidth;
- let targetHeight = originHeight;
- if (originWidth > maxWidth || originHeight > maxHeight) {
- if (originWidth / originHeight > 1) {
- targetWidth = maxWidth;
- targetHeight = Math.round(maxWidth * (originHeight / originWidth));
- } else {
- targetHeight = maxHeight;
- targetWidth = Math.round(maxHeight * (originWidth / originHeight));
- }
- }
- canvas.width = targetWidth;
- canvas.height = targetHeight;
- context.clearRect(0, 0, targetWidth, targetHeight);
- context.drawImage(img, 0, 0, targetWidth, targetHeight);
- canvas.toBlob(
- function(blob) {
- resolve(blob);
- },
- type,
- quality
- );
- });
- };
- return {
- compressImg,
- readImg,
- formatMessage,
- formatInput,
- callBackNotice,
- socketInit,
- socketSendMsg,
- socketClose,
- socketHistoryList,
- getFocus,
- formatChatInput,
- onDrop,
- onPaste,
- upload,
- getUserToken,
- state,
- socketTest,
- showTime,
- formatTime
- };
- }
- exports.useChatWebSocket = useChatWebSocket;
|