| 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;
 |