123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197 |
- define(['io', 'moment', 'moment/locale/zh-cn'], function (io, Moment) {
- // 表情数据
- const emojiList = [
- { "name": "[笑掉牙]", "file": "xiaodiaoya.png" },
- { "name": "[可爱]", "file": "keai.png" },
- { "name": "[冷酷]", "file": "lengku.png" },
- { "name": "[闭嘴]", "file": "bizui.png" },
- { "name": "[生气]", "file": "shengqi.png" },
- { "name": "[惊恐]", "file": "jingkong.png" },
- { "name": "[瞌睡]", "file": "keshui.png" },
- { "name": "[大笑]", "file": "daxiao.png" },
- { "name": "[爱心]", "file": "aixin.png" },
- { "name": "[坏笑]", "file": "huaixiao.png" },
- { "name": "[飞吻]", "file": "feiwen.png" },
- { "name": "[疑问]", "file": "yiwen.png" },
- { "name": "[开心]", "file": "kaixin.png" },
- { "name": "[发呆]", "file": "fadai.png" },
- { "name": "[流泪]", "file": "liulei.png" },
- { "name": "[汗颜]", "file": "hanyan.png" },
- { "name": "[惊悚]", "file": "jingshu.png" },
- { "name": "[困~]", "file": "kun.png" },
- { "name": "[心碎]", "file": "xinsui.png" },
- { "name": "[天使]", "file": "tianshi.png" },
- { "name": "[晕]", "file": "yun.png" },
- { "name": "[啊]", "file": "a.png" },
- { "name": "[愤怒]", "file": "fennu.png" },
- { "name": "[睡着]", "file": "shuizhuo.png" },
- { "name": "[面无表情]", "file": "mianwubiaoqing.png" },
- { "name": "[难过]", "file": "nanguo.png" },
- { "name": "[犯困]", "file": "fankun.png" },
- { "name": "[好吃]", "file": "haochi.png" },
- { "name": "[呕吐]", "file": "outu.png" },
- { "name": "[龇牙]", "file": "ziya.png" },
- { "name": "[懵比]", "file": "mengbi.png" },
- { "name": "[白眼]", "file": "baiyan.png" },
- { "name": "[饿死]", "file": "esi.png" },
- { "name": "[凶]", "file": "xiong.png" },
- { "name": "[感冒]", "file": "ganmao.png" },
- { "name": "[流汗]", "file": "liuhan.png" },
- { "name": "[笑哭]", "file": "xiaoku.png" },
- { "name": "[流口水]", "file": "liukoushui.png" },
- { "name": "[尴尬]", "file": "ganga.png" },
- { "name": "[惊讶]", "file": "jingya.png" },
- { "name": "[大惊]", "file": "dajing.png" },
- { "name": "[不好意思]", "file": "buhaoyisi.png" },
- { "name": "[大闹]", "file": "danao.png" },
- { "name": "[不可思议]", "file": "bukesiyi.png" },
- { "name": "[爱你]", "file": "aini.png" },
- { "name": "[红心]", "file": "hongxin.png" },
- { "name": "[点赞]", "file": "dianzan.png" },
- { "name": "[恶魔]", "file": "emo.png" }
- ]
- // 通知消息
- function audioPlay(type) {
- let ex = new Audio(`/assets/addons/shopro/img/chat/${type}.mp3`);
- if (ex) {
- ex.play();
- }
- }
- // 客服错误通知
- function callBackNotice(res, flag = false) {
- flag &&
- res.msg &&
- ElementPlus.ElNotification({
- title: 'socket',
- message: `客服错误:${res.msg}`,
- showClose: true,
- type: res.code == 1 ? 'success' : 'warning',
- duration: 3000,
- });
- }
- const IS_DEBUG = true; // DEBUG开关
- const debug = (...args) => {
- if (IS_DEBUG) {
- console.info('%c%s', 'color: blue; background: yellow; font-size: 11px;', ...args);
- }
- return;
- };
- // 重新连接尝试次数
- const reconnectionAttempts = 5; //次
- // 重新连接间隔时间
- const reconnectionDelay = 10; // 秒
- const SaChat = {
- template: `#saChatTemplate`,
- props: {},
- setup(props) {
- const { ref, reactive, computed, getCurrentInstance, nextTick } = Vue
- const { proxy } = getCurrentInstance();
- const chat = {
- state: reactive({
- config: {}, // 配置信息
- chatService: null, // 示例
- sessionList: [], // 会话列表
- customerServicesList: [], // 客服列表
- sessionType: 'ing', // 列表状态 ing=会话中|waiting=排队中| history=历史
- customerOnLineList: [], // 会话中
- customerWaitingList: [], // 排队中
- customerHistoryList: [], // 历史
- chatList: [], // 会话信息
- commonWords: [], // 常用语列表
- currentCustomerService: {}, // 当前客服信息
- customerServiceIdentityList: [], // 客服身份列表
- currentCustomer: null, // 当前顾客信息
- historyPagination: {
- page: 0,
- list_rows: 10,
- last_id: 0,
- lastPage: 0,
- loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
- },
- connection: {
- status: '',
- attempts: 0,
- reconnectionAttempts,
- delay: 0,
- showTip: true,
- isFlag: false,
- }, // 连接状态
- isSendSucces: 0, // 是否发送成功 -1=发送中|0=发送成功|1发送失败
- notificationType: '', // 站内信类型
- notificationTypeList: [], // 站内信类型列表
- notificationList: [], // 站内信列表
- }),
- // 打开客服弹窗
- open_chat() {
- chat.state.currentCustomer = chat.state.customerOnLineList[0];
- chat.state.customerOnLineList.length && chat.socket_change_customer_list({
- session: chat.state.sessionType,
- })
- },
- // 初始化chat配置请求
- async chatInit() {
- Fast.api.ajax({
- url: 'shopro/chat/index/init',
- type: 'GET',
- }, function (ret, res) {
- chat.state.config = res.data;
- chat.socket_init(chat.state.config.chat_domain);
- return false
- }, function (ret, res) { })
- },
- // 1.初始化socket
- async socket_init(connectionUrl, options) {
- chat.state.connection.status = 'connecting';
- chat.state.connection.isFlag = true
- // 连接
- try {
- chat.state.chatService = io(connectionUrl, {
- reconnection: true, // 默认 true 是否断线重连
- reconnectionAttempts: reconnectionAttempts, // 默认无限次 断线尝试次数
- reconnectionDelay: reconnectionDelay * 1000, // 默认 1000,进行下一次重连的间隔。
- reconnectionDelayMax: reconnectionDelay * 1000, // 默认 5000, 重新连接等待的最长时间 默认 5000
- randomizationFactor: 0.5, // 默认 0.5 [0-1],随机重连延迟时间
- timeout: 20000, // 默认 20s
- transports: ['websocket', 'polling'], // websocket | polling,
- ...options,
- });
- } catch (error) {
- debug('Socket连接错误', error);
- }
- // 连接成功
- chat.state.chatService.on('connect', (res) => {
- debug('connect', res);
- chat.state.connection.status = 'connect';
- chat.socket_connection();
- });
- // 监听会话
- chat.state.chatService.on('message', (res) => {
- debug('message', res);
- if (res.code == 1) {
- audioPlay('chat');
- const { message, sender } = res.data;
- if (chat.state.currentCustomer && chat.state.currentCustomer?.session_id == sender.session_id) {
- // 如果是当前会话,数字角标不增加,chatList,push
- chat.state.chatList.push(message);
- scrollBottom();
- } else {
- // 非当前会话,顾客角标+1
- const index = chat.state.customerOnLineList.findIndex(
- (item) => item.session_id == sender.session_id,
- );
- if (~index) chat.state.customerOnLineList[index].unread_num += 1;
- }
- }
- });
- // 用户上线
- chat.state.chatService.on('customer_online', (res) => {
- res.code == 1 && chat.socket_change_customer_state(res.data);
- });
- // 用户下线
- chat.state.chatService.on('customer_offline', (res) => {
- res.code == 1 && chat.socket_change_customer_state(res.data);
- });
- // 用户被接入
- chat.state.chatService.on('customer_accessed', (res) => {
- if (res.code == 1) {
- const { session_id, chat_user } = res.data;
- const index = chat.state.customerWaitingList.findIndex((item) => item.session_id == session_id);
- index >= 0 && chat.state.customerWaitingList.splice(index, 1);
- if (chat.state.sessionType == 'waiting') {
- chat.state.currentCustomer =
- chat.state.customerWaitingList.length >= 1
- ? chat.state.customerWaitingList[0]
- : chat.socket_reset();
- }
- }
- });
- // 新用户接入
- chat.state.chatService.on('customer_access', (res) => {
- if (res.code == 1) {
- const { session_id, chat_user } = res.data;
- const index = chat.state.customerOnLineList.findIndex((item) => item.session_id == session_id);
- // 如果用户存在,修改会话中用户状态
- if (index >= 0) {
- chat.state.customerOnLineList[index].status = chat_user.status;
- } else {
- // 如果用户不存在
- chat.state.customerOnLineList.unshift(chat_user);
- // 如果当前会话列表有且只有一个新增会话
- if (chat.state.customerOnLineList.length > 0 && chat.state.sessionType == 'ing') {
- chat.state.currentCustomer = chat_user;
- chat.socket_customer_history();
- }
- }
- callBackNotice(res);
- }
- });
- // 更新客服列表customer_service_update
- chat.state.chatService.on('customer_service_update', (res) => {
- if (res.code == 1) {
- chat.state.customerServicesList = res.data.customer_services.map((item) => ({
- label: item.name,
- value: item.id,
- }));
- }
- });
- // 新顾客等待,更新等待列表
- chat.state.chatService.on('customer_waiting', (res) => {
- if (res.code == 1) {
- chat.state.customerWaitingList = res.data.waitings;
- audioPlay('chat');
- if (chat.state.sessionType == 'waiting') {
- chat.state.sessionList = res.data.waitings;
- chat.state.currentCustomer = chat.state.sessionList.length
- ? res.data.waitings[0]
- : chat.socket_reset();
- }
- }
- });
- // 消息通知
- chat.state.chatService.on('notification', (res) => {
- if (res.code == 1) {
- audioPlay('notice');
- if (chat.state.notificationType == res.data.notification_type) {
- chat.state.notificationList.unshift(res.data);
- } else {
- chat.state.notificationTypeList.map((item) => {
- if (item.value == res.data.notification_type) {
- item.unread_num += 1;
- }
- });
- }
- }
- });
- // 自定义错误
- chat.state.chatService.on('custom_error', (error) => {
- callBackNotice(error, true);
- });
- chat.state.chatService.on('error', (error) => {
- chat.state.connection.status = 'error';
- debug('error', error);
- scrollBottom();
- });
- // 连接失败
- chat.state.chatService.on('connect_error', (error) => {
- debug('connect_error:', error);
- scrollBottom();
- });
- // 连接超时
- chat.state.chatService.on('connect_timeout', (error) => {
- debug('connect_timeout:', error);
- });
- // 断开连接
- chat.state.chatService.on('disconnect', (error) => {
- debug('disconnect:', error);
- });
- // 服务重启重连上reconnect
- chat.state.chatService.on('reconnect', (error) => {
- debug('disconnect:', error);
- });
- // 尝试重连
- chat.state.chatService.on('reconnect_attempt', (counter) => {
- debug('reconnect_attempt', counter);
- chat.state.connection.status = 'reconnect_attempt';
- });
- // 重新连接中
- chat.state.chatService.on('reconnecting', (counter) => {
- debug('reconnecting', counter);
- chat.state.connection.status = 'reconnecting';
- chat.state.connection.attempts = counter;
- });
- // 重连失败
- chat.state.chatService.on('reconnect_error', (error) => {
- debug('reconnect_error', error);
- if (chat.state.connection.attempts >= chat.state.connection.reconnectionAttempts) {
- return;
- }
- chat.state.connection.status = 'reconnect_error';
- // 设置倒计时
- chat.state.connection.delay = reconnectionDelay;
- const timer = setInterval(() => {
- chat.state.connection.delay -= 1;
- if (chat.state.connection.delay <= 1) {
- clearInterval(timer);
- }
- }, 1000);
- });
- // 重连失败 达到最大重试次数
- chat.state.chatService.on('reconnect_failed', () => {
- debug('reconnect_failed');
- chat.state.connection.status = 'reconnect_failed';
- chat.state.isSendSucces = 1;
- chat.state.connection.isFlag = false
- });
- // 与服务器连接失败
- chat.state.chatService.on('connect_failed', (error) => {
- debug('connect_failed', error);
- });
- },
- // socket 连接
- socket_connection(token) {
- chat.state.chatService.emit(
- 'connection',
- {
- auth: 'admin',
- token: chat.state.config.token,
- },
- (res) => {
- debug('socket_connection:', res);
- if (res.code == 1) {
- chat.socket_check_identify();
- }
- },
- );
- },
- // 检测是否是客服 获取客服身份
- socket_check_identify() {
- chat.state.chatService.emit('check_identify', {}, (res) => {
- debug('socket_check_identify:', res);
- if (res.code == 1) {
- chat.socket_customer_login(res.data.customer_services[0]);
- chat.state.customerServiceIdentityList = res.data.customer_services.map((item) => ({
- label: item.name,
- value: item.id,
- room_id: item.room_id,
- }));
- }
- });
- },
- // 客服登录
- socket_customer_login(data) {
- chat.state.chatService.emit(
- 'customer_service_login',
- {
- room_id: data?.room_id || 'admin',
- },
- (res) => {
- debug('customer_service_login:', res);
- if (res.code == 1) {
- chat.state.currentCustomerService = res.data.customer_service;
- chat.state.commonWords = res.data.common_words;
- chat.socket_customer_init();
- }
- callBackNotice(res);
- },
- );
- },
- // 初始化客服
- socket_customer_init() {
- chat.state.chatService.emit('customer_service_init', {}, (res) => {
- if (res.code == 1) {
- chat.state.customerOnLineList = res.data.onlines;
- chat.state.customerWaitingList = res.data.waitings;
- if (chat.state.customerWaitingList.length > 0) {
- // audioPlay('chat');
- }
- chat.state.customerHistoryList = res.data.histories;
- chat.state.sessionList = res.data.onlines;
- // 当前顾客选中
- if (chat.state.sessionList.length) {
- if (!currentSessionTypeIndexs.hasOwnProperty(chat.state.sessionType)) {
- currentSessionTypeIndexs[chat.state.sessionType] = 0;
- }
- }
- chat.socket_change_customer_list({
- session: chat.state.sessionType,
- });
- }
- });
- },
- // 切换客服身份
- socket_change_customer_identity(data) {
- // 退出客服
- chat.socket_logout_customer();
- // 登录客服
- let info = chat.state.customerServiceIdentityList.filter((item) => data == item.value);
- chat.socket_customer_login(info[0]);
- // 初始化客服
- },
- // 切换会话列表
- socket_change_customer_list(data) {
- chat.socket_reset();
- chat.state.sessionType = data.session;
- switch (data.session) {
- case 'ing':
- chat.state.sessionList = chat.state.customerOnLineList;
- break;
- case 'waiting':
- chat.state.sessionList = chat.state.customerWaitingList;
- break;
- case 'history':
- chat.state.sessionList = chat.state.customerHistoryList;
- break;
- default:
- break;
- }
- if (!data.type) {
- // 默认显示第一个人的会话信息
- chat.state.sessionList.length && chat.socket_change_customer_info(data.index || 0);
- }
- },
- // 修改顾客信息,获取历史记录
- socket_change_customer_info(index = 0) {
- chat.socket_reset();
- chat.state.currentCustomer = chat.state.sessionList[index];
- if (chat.state.sessionList[index]) {
- chat.state.sessionList[index].unread_num = 0;
- }
- chat.socket_customer_history();
- },
- // 重置
- socket_reset() {
- chat.state.currentCustomer = null;
- chat.state.chatList = [];
- chat.state.historyPagination = {
- page: 0,
- list_rows: 10,
- last_id: 0,
- totalPage: 0,
- loadStatus: 'loadmore',
- };
- },
- // 退出客服
- socket_logout_customer() {
- debug('socket_logout_start');
- chat.state.chatService.emit('customer_service_logout', {}, (res) => {
- debug('socket_logout_res:', res);
- });
- },
- // 获取用户历史消息
- socket_customer_history() {
- if (!chat.state.currentCustomer) return;
- chat.state.historyPagination.loadStatus = 'loading';
- chat.state.historyPagination.page += 1;
- chat.state.chatService.emit(
- 'messages',
- {
- session_id: chat.state.currentCustomer?.session_id || 0,
- ...chat.state.historyPagination,
- },
- (res) => {
- if (res.code == 1) {
- chat.state.historyPagination.total = res.data.messages.total;
- chat.state.historyPagination.lastPage = res.data.messages.last_page;
- chat.state.historyPagination.page = res.data.messages.current_page;
- if (res.data.messages.current_page == 1) {
- chat.state.chatList = [];
- }
- res.data.messages.data.forEach((item) => {
- chat.state.chatList.unshift(item);
- });
- chat.state.historyPagination.loadStatus =
- chat.state.historyPagination.page < chat.state.historyPagination.lastPage
- ? 'loadmore'
- : 'nomore';
- chat.state.historyPagination.page == 1 && scrollBottom();
- if (chat.state.historyPagination.last_id == 0) {
- chat.state.historyPagination.last_id = res.data.messages.data.length
- ? res.data.messages.data[0].id
- : 0;
- }
- }
- },
- );
- },
- // 修改顾客状态
- socket_change_customer_state(data) {
- const { session_id, chat_user } = data;
- let waitingIndex = -1,
- onlineIndex = -1,
- historyIndex = -1;
- if (chat.state.customerWaitingList.length) {
- waitingIndex = chat.state.customerWaitingList.findIndex((item) => item.session_id == session_id);
- if (waitingIndex >= 0) {
- chat.state.customerWaitingList[waitingIndex].status = chat_user.status;
- }
- }
- if (chat.state.customerOnLineList.length) {
- onlineIndex = chat.state.customerOnLineList.findIndex((item) => item.session_id == session_id);
- if (onlineIndex >= 0) {
- chat.state.customerOnLineList[onlineIndex].status = chat_user.status;
- }
- }
- if (chat.state.customerHistoryList.length) {
- historyIndex = chat.state.customerHistoryList.findIndex((item) => item.session_id == session_id);
- if (historyIndex >= 0) {
- chat.state.customerHistoryList[historyIndex].status = chat_user.status;
- }
- }
- },
- // 发送消息
- socket_send(data) {
- // 给会话列表
- chat.state.isSendSucces = -1;
- chat.state.chatList.push(data);
- // 给socket
- chat.state.chatService.emit(
- 'message',
- {
- session_id: chat.state.currentCustomer?.session_id || 0,
- message: {
- message: data.message,
- message_type: data.message_type,
- },
- },
- (res) => {
- chat.state.isSendSucces = res.error;
- },
- );
- },
- // 断开,删除顾客
- socket_change_customer(session_id, index, sessionType, is_del_record) {
- if (sessionType == 'ing') {
- chat.state.chatService.emit('break_customer', { session_id }, (res) => {
- if (res.code == 1) {
- chat.state.customerOnLineList.splice(index, 1);
- chat.socket_reset();
- }
- callBackNotice(res);
- });
- }
- if (sessionType == 'history') {
- chat.state.chatService.emit('del_customer', { session_id, is_del_record }, (res) => {
- if (res.code == 1) {
- chat.state.customerHistoryList.splice(index, 1);
- chat.socket_reset();
- }
- callBackNotice(res);
- });
- }
- },
- // 切换客服状态
- socket_change_customer_service_status(status) {
- console.log(chat.state, 'chat.state')
- // return
- switch (status) {
- case 'online':
- chat.state.chatService.emit('customer_service_online', {}, (res) => {
- changeBack(res);
- });
- break;
- case 'busy':
- chat.state.chatService.emit('customer_service_busy', {}, (res) => {
- changeBack(res);
- });
- chat.state.sessionList = chat.state.customerWaitingList;
- break;
- case 'offline':
- chat.state.chatService.emit('customer_service_offline', {}, (res) => {
- changeBack(res);
- });
- chat.state.sessionList = chat.state.customerHistoryList;
- break;
- default:
- break;
- }
- // 切换回调
- function changeBack(res) {
- if (res.code == 1) chat.state.currentCustomerService = res.data.customer_service;
- callBackNotice(res);
- }
- },
- // access 接入客户,会触发监听接入,被接入
- socket_customer_access(index) {
- chat.state.chatService.emit(
- 'access',
- { session_id: chat.state.currentCustomer?.session_id },
- (res) => { },
- );
- },
- // transfer 转接顾客
- socket_transfer(customer_service_id) {
- chat.state.chatService.emit(
- 'transfer',
- {
- session_id: chat.state.currentCustomer?.session_id,
- customer_service_id,
- },
- (res) => {
- callBackNotice(res);
- if (res.code == 1) {
- const index = chat.state.customerOnLineList.findIndex(
- (item) => item.session_id == chat.state.currentCustomer?.session_id,
- );
- chat.state.customerOnLineList.splice(index, 1);
- chat.socket_reset();
- }
- },
- );
- },
- }
- const state = reactive({})
- // 打开客服
- const showChat = ref(false)
- function onShowChat() {
- showChat.value = !showChat.value;
- // 初始化聊天
- if (chat.state.connection.status != 'connect') {
- if (!chat.state.connection.isFlag) {
- chat.chatInit();
- }
- chat.open_chat();
- }
- }
- // 客服是否有未读消息
- const isChatUnreadNum = computed(() => {
- return chat.state.customerOnLineList.reduce((pre, cur) => {
- return pre + cur?.unread_num;
- }, chat.state.customerWaitingList.length);
- })
- // 修改客服状态
- const customerServiceStatus = {
- online: '在线',
- offline: '离线',
- busy: '忙碌',
- // disconnect: 'sa-duankailianjie',
- }
- function onChangeCustomerServiceStatus(status) {
- if (chat.state.currentCustomerService.status == status) return;
- chat.socket_change_customer_service_status(status);
- };
- // 切换客服身份
- function onChangeCustomerServiceIdentity(e) {
- chat.socket_change_customer_identity(e);
- };
- // 修改会话类型
- const sessionTypeList = {
- ing: {
- label: '会话中',
- left: '2px'
- },
- waiting: {
- label: '排队中',
- left: '52px'
- },
- history: {
- label: '历史',
- left: '102px'
- }
- }
- function onChangeSessionType(session, type = '') {
- if (chat.state.sessionType == session) return;
- if (!currentSessionTypeIndexs.hasOwnProperty(session)) {
- currentSessionTypeIndexs[session] = 0;
- }
- chat.socket_change_customer_list({
- session,
- type,
- index: currentSessionTypeIndexs[session],
- });
- }
- // 操作当前顾客
- const currentSessionTypeIndexs = reactive({});
- function onChangeCurrentSessionTypeIndex(index) {
- if (currentSessionTypeIndexs[chat.state.sessionType] == index) return;
- currentSessionTypeIndexs[chat.state.sessionType] = index;
- chat.socket_change_customer_info(index);
- };
- // 删除会话中顾客
- function onDeleteSession(session_id, index, sessionType) {
- chat.socket_change_customer(session_id, index, sessionType);
- }
- // 操作历史顾客
- const historyDeletePopover = reactive({
- flag: {},
- is_del_record: 0,
- });
- function onCancelHistoryDeletePopover(index) {
- historyDeletePopover.flag[index] = false;
- historyDeletePopover.is_del_record = 0;
- }
- function onConfirmHistoryDeletePopover(session_id, index, sessionType) {
- chat.socket_change_customer(
- session_id,
- index,
- sessionType,
- historyDeletePopover.is_del_record,
- );
- onCancelHistoryDeletePopover(index);
- }
- // 获取除了自己的客服列表
- const avaliableCustomerServicesList = computed(() => {
- return chat.state.customerServicesList.filter((item) => {
- return item.value != chat.state.currentCustomerService.id;
- });
- })
- // 转接客服
- const transferCustomer = ref(null);
- function onTransferCommand(val) {
- transferCustomer.value = val
- }
- function onTransferCustomer() {
- chat.socket_transfer(transferCustomer.value);
- };
- // 立即接入
- function onAccessCustomer() {
- chat.socket_customer_access();
- // 获取历史记录
- onChangeSessionType('ing', 'access');
- }
- // 加载更多
- function onLoadMore() {
- chat.state.historyPagination.page < chat.state.historyPagination.lastPage &&
- chat.socket_customer_history();
- };
- const showTime = (item, index) => {
- if (chat.state.chatList[index + 1]) {
- let dateString = Moment(chat.state.chatList[index + 1].createtime * 1000).fromNow();
- if (dateString == Moment(item.createtime * 1000).fromNow()) {
- return false;
- } else {
- dateString = Moment(item.createtime * 1000).fromNow();
- return true;
- }
- }
- return false;
- };
- // 格式化时间
- const formatTime = (time) => {
- let diffTime = Moment().unix() - time;
- if (diffTime > 28 * 24 * 60) {
- return Moment(time * 1000).format('MM/DD HH:mm');
- }
- if (diffTime > 360 * 28 * 24 * 60) {
- return Moment(time * 1000).format('YYYY/MM/DD HH:mm');
- }
- return Moment(time * 1000).fromNow();
- };
- function replaceEmoji(data) {
- let newData = data;
- if (typeof newData != 'object') {
- let reg = /\[(.+?)\]/g; // [] 中括号
- let zhEmojiName = newData.match(reg);
- if (zhEmojiName) {
- zhEmojiName.forEach((item) => {
- let emojiFile = selEmojiFile(item);
- newData = newData.replace(
- item,
- `<img class="message-emoji" src="${Fast.api.cdnurl(`/assets/addons/shopro/img/chat/emoji/${emojiFile}`)}" />`,
- );
- });
- }
- }
- return newData;
- }
- function selEmojiFile(name) {
- for (let index in emojiList) {
- if (emojiList[index].name == name) {
- return emojiList[index].file;
- }
- }
- return false;
- }
- const messageInput = ref('');
- function getMessageInput(e) {
- if (
- e.target.innerHTML.replace(/ |\s/g, '') &&
- e.target.innerHTML.replace(/ |\s/g, '').indexOf('<br>') != 0
- ) {
- messageInput.value = e.target;
- } else {
- messageInput.value = '';
- }
- };
- // 获取焦点
- function getMessageInputFocus() {
- if (window.getSelection) {
- let chatInput = proxy.$refs.messageInputRef;
- chatInput.focus();
- let range = window.getSelection();
- range.selectAllChildren(chatInput);
- range.collapseToEnd();
- } else if (document.selection) {
- let range = document.selection.createRange();
- range.moveToElementText(chatInput);
- range.collapse(false);
- range.select();
- }
- };
- // ctrl + enter 换行 ,enter发送
- function onKeyDown(e) {
- if (e.ctrlKey && e.keyCode == 13) {
- document.execCommand('insertHTML', false, '<br></br>');
- } else if (e.keyCode == 13) {
- // 阻止默认enter
- e.preventDefault();
- onSendMessage();
- return false;
- }
- }
- function onSendMessage() {
- if (!messageInput.value || !chat.state.currentCustomer) return;
- let res = '';
- let elemArr = Array.from(messageInput.value.childNodes);
- elemArr.forEach((child, index) => {
- if (child.nodeName == '#text') {
- res += child.nodeValue;
- if (elemArr[index + 1]?.nodeName == 'IMG' && elemArr[index + 1]?.name != 'emoji') {
- const data = {
- sender_identify: 'customer_service',
- message_type: 'text',
- message: res,
- createtime: Moment().unix(),
- };
- chat.socket_send(data);
- scrollBottom();
- 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 = {
- sender_identify: 'customer_service',
- message_type: 'image',
- message: {
- url: Fast.api.cdnurl(src[1].replace(/http:\/\/[^\/]*/, ''), true),
- path: src[1].replace(/http:\/\/[^\/]*/, ''),
- },
- createtime: Moment().unix(),
- };
- chat.socket_send(data);
- scrollBottom();
- } else {
- res += child.outerHTML;
- }
- } else if (child.nodeName == 'DIV') {
- res += `<div style="width:200px; white-space: nowrap;">${child.outerHTML}</div>`;
- }
- });
- if (res) {
- const data = {
- sender_identify: 'customer_service',
- message_type: 'text',
- message: res,
- createtime: Moment().unix(),
- };
- chat.socket_send(data);
- }
- messageInput.value = '';
- proxy.$refs.messageInputRef.innerHTML = '';
- scrollBottom();
- }
- function onSelectToolbar(message_type, message) {
- !messageInput.value && getMessageInputFocus();
- let obj
- switch (message_type) {
- case 'emoji':
- let img = `<img src="${Fast.api.cdnurl('/assets/addons/shopro/img/chat/emoji/' + message.file, true)}" name="emoji" style="object-fit: cover;vertical-align:bottom; display: inline-block;width:20px !important;height:20px;margin:2px">`;
- document.execCommand('insertHTML', false, img);
- break;
- case 'text':
- obj = {
- sender_identify: 'customer_service',
- message_type,
- message,
- createtime: Moment().unix(),
- };
- chat.socket_send(obj);
- scrollBottom();
- break;
- case 'image':
- Fast.api.open(`general/attachment/select`, "选择", {
- callback: function (data) {
- obj = {
- sender_identify: 'customer_service',
- message_type,
- message: data.url,
- createtime: Moment().unix(),
- }
- chat.socket_send(obj);
- scrollBottom();
- }
- });
- break;
- case 'goods':
- Fast.api.open(`shopro/goods/goods/select`, "选择商品", {
- callback(data) {
- obj = {
- sender_identify: 'customer_service',
- message_type,
- message: {
- id: data.id,
- title: data.title,
- image: data.image,
- price: data.price,
- stock: data.stock,
- },
- createtime: Moment().unix(),
- };
- chat.socket_send(obj);
- scrollBottom();
- }
- })
- break;
- default:
- break;
- }
- }
- function onOpenGoodsDetail(id) {
- Fast.api.open(`shopro/goods/goods/add?type=edit&id=${id}`, "商品详情")
- }
- function onOpenOrderDetail(id) {
- Fast.api.open(`shopro/order/order/detail?id=${id}`, "订单详情")
- }
- function scrollBottom() {
- chat.state.connection.status == 'connect' &&
- nextTick(() => {
- console.log(proxy.$refs.chatScrollRef, '000')
- console.log(proxy.$refs.chatScrollRef['wrap$'].childNodes[0].offsetHeight, 1)
- console.log(proxy.$refs.chatScrollRef['wrap$'].offsetHeight, 2)
- let scrollTop = proxy.$refs.chatScrollRef['wrap$'].childNodes[0].offsetHeight - proxy.$refs.chatScrollRef['wrap$'].offsetHeight + 20
- console.log(scrollTop, 'scrollTop')
- proxy.$refs.chatScrollRef.setScrollTop(scrollTop);
- });
- }
- // 消息通知
- const showNotification = ref(false)
- function onShowNotification() {
- showNotification.value = true
- chat.state.notificationList = [];
- getNotificationType()
- }
- // 消息通知-是否有未读数据
- const isNotificationUnreadNum = computed(() => {
- return chat.state.notificationTypeList.reduce((pre, cur) => {
- return pre + cur.unread_num;
- }, 0);
- })
- // 消息通知-类型
- function getNotificationType() {
- Fast.api.ajax({
- url: 'shopro/notification/notification/notificationType',
- type: 'GET',
- }, function (ret, res) {
- chat.state.notificationTypeList = res.data.notification_type;
- chat.state.notificationType = res.data.notification_type[0].value
- // 请求列表
- pagination.page = 1;
- getNotificationList()
- return false
- }, function (ret, res) { })
- };
- // 消息通知-切换类型
- function onChangeNotificationType(type) {
- chat.state.notificationType = type
- chat.state.notificationList = [];
- pagination.page = 1;
- getNotificationList();
- };
- // 消息通知-列表
- const pagination = reactive({
- page: 0,
- lastPage: 0,
- loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
- });
- function getNotificationList() {
- pagination.loadStatus = 'loading';
- Fast.api.ajax({
- url: 'shopro/notification/notification',
- type: 'GET',
- data: {
- search: JSON.stringify({ notification_type: chat.state.notificationType }),
- page: pagination.page,
- },
- }, function (ret, res) {
- if (pagination.page == 1) {
- chat.state.notificationList = []
- }
- res.data.data.forEach((item) => {
- chat.state.notificationList.push(item);
- });
- pagination.page = res.data.current_page;
- pagination.lastPage = res.data.last_page;
- pagination.loadStatus = pagination.page < pagination.lastPage ? 'loadmore' : 'nomore';
- // 请求之后 把未读改为0
- chat.state.notificationTypeList.map((item) => {
- if (item.value == chat.state.notificationType) {
- item.unread_num = 0;
- }
- });
- return false
- }, function (ret, res) { })
- };
- function onLoadMoreNotification() {
- if (pagination.page < pagination.lastPage) {
- pagination.page += 1;
- getNotificationList();
- }
- };
- // 消息通知-标记为已读消息
- function onReadNotification(id, index) {
- Fast.api.ajax({
- url: `shopro/notification/notification/read/id/${id}`,
- type: 'POST',
- }, function (ret, res) {
- chat.state.notificationList[index] = res.data;
- return false
- }, function (ret, res) { })
- }
- // 消息通知-清空已读消息
- function onClearNotification() {
- Fast.api.ajax({
- url: 'shopro/notification/notification/delete',
- type: 'DELETE',
- }, function (ret, res) {
- pagination.page = 1;
- chat.state.notificationList = []
- getNotificationList();
- return false
- }, function (ret, res) { })
- };
- return {
- Fast,
- emojiList,
- chat,
- state,
- showChat,
- onShowChat,
- isChatUnreadNum,
- customerServiceStatus,
- onChangeCustomerServiceStatus,
- onChangeCustomerServiceIdentity,
- sessionTypeList,
- onChangeSessionType,
- currentSessionTypeIndexs,
- onChangeCurrentSessionTypeIndex,
- onDeleteSession,
- historyDeletePopover,
- onCancelHistoryDeletePopover,
- onConfirmHistoryDeletePopover,
- avaliableCustomerServicesList,
- transferCustomer,
- onTransferCommand,
- onTransferCustomer,
- onAccessCustomer,
- onLoadMore,
- showTime,
- formatTime,
- replaceEmoji,
- selEmojiFile,
- messageInput,
- getMessageInput,
- getMessageInputFocus,
- onKeyDown,
- onSendMessage,
- onSelectToolbar,
- onOpenGoodsDetail,
- onOpenOrderDetail,
- scrollBottom,
- showNotification,
- onShowNotification,
- isNotificationUnreadNum,
- onChangeNotificationType,
- pagination,
- onLoadMoreNotification,
- onReadNotification,
- onClearNotification,
- loadingMap: {
- loadmore: {
- title: '查看更多',
- icon: 'el-icon-arrow-left',
- },
- nomore: {
- title: '没有更多了',
- icon: '',
- },
- loading: {
- title: '加载中... ',
- icon: 'el-icon-loading',
- },
- },
- }
- }
- }
- return SaChat
- })
|