diff --git a/.env b/.env index 7c7751a..714c44b 100644 --- a/.env +++ b/.env @@ -2,3 +2,4 @@ VITE_BASE_URL=/api VITE_SOCKET_URL=wss://k8s-horse-gateway.mashibing.cn/ws #VITE_SOCKET_URL=ws://192.168.10.93:8090/ws VITE_REQUEST_TIMEOUT=5000 +VITE_BROWSER_URL = https://k8s-shop-pc.mashibing.cn \ No newline at end of file diff --git a/.env.prod b/.env.prod index a51d947..4df8ab0 100644 --- a/.env.prod +++ b/.env.prod @@ -1,3 +1,4 @@ VITE_BASE_URL=https://you-gateway.mashibing.com VITE_SOCKET_URL=wss://you-gateway.mashibing.com/ws VITE_REQUEST_TIMEOUT=20000 +VITE_BROWSER_URL = https://k8s-shop-pc.mashibing.cn diff --git a/.env.test b/.env.test index 4415b91..21b266b 100644 --- a/.env.test +++ b/.env.test @@ -1,3 +1,4 @@ VITE_BASE_URL=https://k8s-horse-gateway.mashibing.cn/ VITE_SOCKET_URL=wss://k8s-horse-gateway.mashibing.cn/ws VITE_REQUEST_TIMEOUT=20000 +VITE_BROWSER_URL = https://k8s-shop-pc.mashibing.cn diff --git a/.eslintignore b/.eslintignore index 03c5c1d..5f0eb95 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,5 +4,5 @@ public dist node_modules src/utils/msb-im.js -src/utils/proto-rsq.js +src/utils/poto-req.js src/utils/proto-rsp.js \ No newline at end of file diff --git a/src/api/chat/index.js b/src/api/chat/index.js index ff501e6..e98fb7d 100644 --- a/src/api/chat/index.js +++ b/src/api/chat/index.js @@ -2,7 +2,7 @@ * @Author: xwk * @Date: 2022-05-24 17:00:26 * @LastEditors: ch - * @LastEditTime: 2022-06-09 10:10:47 + * @LastEditTime: 2022-06-13 14:26:53 * @Description: file content */ import request from '@/utils/request.js'; @@ -52,6 +52,16 @@ export const getCustomeServiceTicket = () => { }, }); }; + +/** + * 获取当前客服 + */ +export const getCustomerService = () => { + return request({ + url: '/mall/im/admin/waiterUser/getWaiterByUserId', + method: 'get', + }); +}; /** * 获取可转移客服列表 * @param {*} params diff --git a/src/store/modules/chat/chat copy.js b/src/store/modules/chat/chat copy.js new file mode 100644 index 0000000..5ded1e6 --- /dev/null +++ b/src/store/modules/chat/chat copy.js @@ -0,0 +1,363 @@ +import { login } from '@/api/chat'; +import config from '@/configs'; +import { ElMessage } from '@/plugins/element-plus'; +import { UUID } from '@/utils/chat'; +import dayjs from 'dayjs'; +const state = () => ({ + socket: null, + heart: null, + queue: [], + task: [], + + currentSession: null, + messageList: [], + messageType: { 1: 'text', 2: 'audio', 3: 'image', 4: 'video', 5: 'revoke', 6: 'custom', 7: 'notify' }, + + curCustomerService: {}, + sessionData: [], + customerServiceList: [], + opts: { + customerServiceType: [ + { + label: '售前', + value: 1, + }, + { + label: '售后', + value: 2, + }, + { + label: '发货', + value: 3, + }, + ], + }, +}); +const getters = { + parseTime: () => { + return (timestamp) => { + dayjs(new Date(timestamp)).format('MM-DD HH:mm:ss'); + }; + }, + parseText: () => { + return ({ payload, type }) => { + if (type === 2) { + payload = '[语音]'; + } else if (type === 3) { + payload = '[图片]'; + } else if (type === 4) { + payload = '[视频]'; + } else if (type === 5) { + payload = '[撤回消息]'; + } else if (type === 6 || type === 1) { + try { + // payload = JSON.parse(payload); + if ('text' in payload) { + payload = payload.text; + } else if ('linkJump' in payload) { + payload = '[超链接]'; + } else if ('orderNo' in payload) { + payload = '[订单信息]'; + } else if ('productImageUrl' in payload) { + payload = '[商品信息]'; + } else { + payload = '[未知数据]'; + } + } catch (e) { + payload = '[解析异常]'; + } + } else if (type === 7) { + payload = '[撤回消息]'; + } else { + payload = '[未知类型]'; + } + return payload; + }; + }, + parseImage: () => { + return (payload) => { + try { + // payload = JSON.parse(payload); + if ('url' in payload) { + payload = payload.url; + } else { + payload = '[未知图片]'; + } + } catch (e) { + payload = '[解析异常]'; + } + return payload; + }; + }, + parseVideo: () => { + return (payload) => { + try { + // payload = JSON.parse(payload); + if ('url' in payload) { + payload = payload.url; + } else { + payload = '[未知视频]'; + } + } catch (e) { + payload = '[解析异常]'; + } + return payload; + }; + }, + parseContent: () => { + return (payload) => { + try { + // payload = JSON.parse(payload); + if ('linkJump' in payload) { + payload.type = 'link'; + } else if ('orderNo' in payload) { + payload.type = 'order'; + } else if ('productImageUrl' in payload) { + payload.type = 'product'; + } else { + payload = '[未知消息]'; + } + } catch (e) { + payload = '[解析异常]'; + } + return payload; + }; + }, +}; +const mutations = { + setSocket: (state, data) => (state.socket = data), + setHeart: (state, data) => (state.heart = data), + setTask: (state, data) => (state.task = data), + addTask: (state, data) => state.task.push(data), + delTask: (state, data) => state.task.splice(data, 1), + setCurrentSession: (state, data) => (state.currentSession = data), + setSessionData: (state, data) => (state.sessionData = data), + setMessageList: (state, data) => (state.messageList = data), + setCustomerServiceList: (state, data) => (state.customerServiceList = data), + + SET_CUR_SERVICE(state, data) { + state.curCustomerService = data || {}; + }, + SET_SESSION_DATA(state, data) { + state.sessionData = data; + }, + SET_SERVICE_LIST(state, data) { + state.customerServiceList = data; + }, +}; +const actions = { + /** + * 创建连接 + */ + connect: async ({ state, commit, dispatch }) => { + let { ticket } = await login({ storeId: 1 }); + return new Promise((resolve, reject) => { + if (window.WebSocket) { + const socket = new WebSocket(`${config.socketURL}?client=${ticket}&type=2`); + socket.onmessage = ({ data }) => { + dispatch('receive', data); + }; + socket.onopen = () => { + commit( + 'setHeart', + setInterval(() => { + dispatch('heart'); + }, 3000) + ); + console.info('[chat] open'); + resolve(socket); + }; + socket.onclose = () => { + clearInterval(state.heart); + console.info('[chat] close'); + }; + socket.onerror = (e) => { + clearInterval(state.heart); + console.info('[chat] error', e); + reject(e); + }; + commit('setSocket', socket); + } else { + ElMessage.error('当前浏览器不支持 WebSocket'); + reject('not support websocket'); + } + }); + }, + /** + * 发送心跳,任务监测 + */ + heart: ({ state, dispatch }) => { + dispatch('send', { + traceType: 26, + content: { storeId: 1 }, + }); + // console.info('[chat] heart'); + state.task.forEach((item) => { + dispatch('send', item); + }); + }, + /** + * 执行任务 + */ + invoke: ({ commit, dispatch }, data) => { + data.traceId = UUID(); + commit('addTask', data); + dispatch('send', data); + }, + /** + * 撤销任务 + */ + revoke: ({ state, commit }, data) => { + console.info(state.task.length); + commit( + 'setTask', + state.task.filter((item) => item.traceType !== data) + ); + console.info(state.task.length); + }, + /** + * 发送数据 + */ + send: ({ state }, data) => { + if (window.WebSocket) { + if (state.socket?.readyState === WebSocket.OPEN) { + data.traceId = data.traceId || UUID(); + state.socket.send(JSON.stringify(data)); + if (data.traceType !== 26) { + console.info('[chat] send', data); + } + } + } + }, + /** + * 接收数据 + */ + receive: ({ state, commit, dispatch }, data) => { + data = JSON.parse(data); + if (data.traceType !== 0) { + let index = state.task.findIndex((item) => item.traceId === data.traceId); + if (index !== -1) { + console.info('[chat] data', data); + commit('delTask', index); + dispatch('handle', data); + } else if (data.traceType === 25) { + console.info('[chat] msg', data); + dispatch('handle', data); + } else { + console.info('[chat] deprecated', data); + } + } + }, + /** + * 处理数据 + */ + handle: ({ state, commit, dispatch }, { code, traceType, content }) => { + if (code === 200) { + switch (traceType) { + case 25: // 收到消息 + if (content.sessionId === state.currentSession) { + commit('setMessageList', [...state.messageList, content]); + dispatch('submitRead'); + } else { + dispatch('querySession'); + } + break; + case 32: // 发送消息 + commit('setMessageList', [...state.messageList, content]); + break; + case 27: // 会话列表 + commit('setSessionData', content); + break; + case 28: // 消息列表 + commit('setMessageList', [...content, ...state.messageList]); + break; + case 29: // 客服列表 + commit('setCustomerServiceList', content); + break; + case 31: // 已读消息 + dispatch('querySession'); + break; + default: + break; + } + } + }, + /** + * 查询会话列表 + */ + querySession: ({ dispatch }) => { + dispatch('invoke', { + traceType: 27, + content: { storeId: 1 }, + }); + }, + /** + * 查询会话消息列表 + */ + querySessionMessage: ({ state, dispatch }, data) => { + dispatch('invoke', { + traceType: 28, + content: { sessionId: state.currentSession, size: 10, topMessageId: null, ...data }, + }); + }, + /** + * 查询可转移客服列表 + */ + queryCustomerService: ({ dispatch }) => { + dispatch('invoke', { + traceType: 29, + content: { storeId: 1 }, + }); + }, + /** + * 提交转移会话 + */ + submitTransferSession: ({ dispatch }, data) => { + dispatch('invoke', { + traceType: 30, + content: { storeId: 1, ...data }, + }); + }, + /** + * 提交已读消息 + */ + submitRead: ({ state, dispatch }) => { + dispatch('invoke', { + traceType: 31, + content: { sessionId: state.currentSession }, + }); + }, + /** + * 提交发送消息 + */ + submitMessage: ({ state, dispatch }, payload) => { + dispatch('invoke', { + traceType: 32, + content: { payload, toSessionId: state.currentSession, type: 1 }, + }); + }, + /** + * 提交发送图片 + */ + submitImage: ({ state, dispatch }, payload) => { + dispatch('invoke', { + traceType: 32, + content: { payload, toSessionId: state.currentSession, type: 3 }, + }); + }, + /** + * 提交发送视频 + */ + submitVideo: ({ state, dispatch }, payload) => { + dispatch('invoke', { + traceType: 32, + content: { payload, toSessionId: state.currentSession, type: 4 }, + }); + }, +}; +export default { + state, + getters, + mutations, + actions, +}; diff --git a/src/store/modules/chat/chat.js b/src/store/modules/chat/chat.js index 0785fed..c32a560 100644 --- a/src/store/modules/chat/chat.js +++ b/src/store/modules/chat/chat.js @@ -1,18 +1,18 @@ -import { login } from '@/api/chat'; -import config from '@/configs'; -import { ElMessage } from '@/plugins/element-plus'; -import { UUID } from '@/utils/chat'; +/* + * @Author: ch + * @Date: 2022-06-07 15:41:05 + * @LastEditors: ch + * @LastEditTime: 2022-06-13 17:16:27 + * @Description: file content + */ +import * as api from '@/api/chat'; import dayjs from 'dayjs'; -const state = () => ({ - socket: null, - heart: null, - queue: [], - task: [], - sessionData: {}, - currentSession: null, - messageList: [], - messageType: { 1: 'text', 2: 'audio', 3: 'image', 4: 'video', 5: 'revoke', 6: 'custom', 7: 'notify' }, +const state = { + curCustomerService: {}, + sessionData: [], customerServiceList: [], + messageType: { 1: 'text', 2: 'audio', 3: 'image', 4: 'video', 5: 'revoke', 6: 'custom', 7: 'notify' }, + opts: { customerServiceType: [ { @@ -29,7 +29,8 @@ const state = () => ({ }, ], }, -}); +}; + const getters = { parseTime: () => { return (timestamp) => { @@ -48,7 +49,7 @@ const getters = { payload = '[撤回消息]'; } else if (type === 6 || type === 1) { try { - payload = JSON.parse(payload); + // payload = JSON.parse(payload.value); if ('text' in payload) { payload = payload.text; } else if ('linkJump' in payload) { @@ -64,7 +65,7 @@ const getters = { payload = '[解析异常]'; } } else if (type === 7) { - payload = '[撤回消息]'; + payload = payload.text; } else { payload = '[未知类型]'; } @@ -74,7 +75,7 @@ const getters = { parseImage: () => { return (payload) => { try { - payload = JSON.parse(payload); + // payload = JSON.parse(payload); if ('url' in payload) { payload = payload.url; } else { @@ -89,7 +90,7 @@ const getters = { parseVideo: () => { return (payload) => { try { - payload = JSON.parse(payload); + // payload = JSON.parse(payload); if ('url' in payload) { payload = payload.url; } else { @@ -104,7 +105,7 @@ const getters = { parseContent: () => { return (payload) => { try { - payload = JSON.parse(payload); + // payload = JSON.parse(payload); if ('linkJump' in payload) { payload.type = 'link'; } else if ('orderNo' in payload) { @@ -122,224 +123,46 @@ const getters = { }, }; const mutations = { - setSocket: (state, data) => (state.socket = data), - setHeart: (state, data) => (state.heart = data), - setTask: (state, data) => (state.task = data), - addTask: (state, data) => state.task.push(data), - delTask: (state, data) => state.task.splice(data, 1), - setCurrentSession: (state, data) => (state.currentSession = data), - setSessionData: (state, data) => (state.sessionData = data), - setMessageList: (state, data) => (state.messageList = data), - setCustomerServiceList: (state, data) => (state.customerServiceList = data), -}; -const actions = { - /** - * 创建连接 - */ - connect: async ({ state, commit, dispatch }) => { - let { ticket } = await login({ storeId: 1 }); - return new Promise((resolve, reject) => { - if (window.WebSocket) { - const socket = new WebSocket(`${config.socketURL}?client=${ticket}&type=2`); - socket.onmessage = ({ data }) => { - dispatch('receive', data); - }; - socket.onopen = () => { - commit( - 'setHeart', - setInterval(() => { - dispatch('heart'); - }, 3000) - ); - console.info('[chat] open'); - resolve(socket); - }; - socket.onclose = () => { - clearInterval(state.heart); - console.info('[chat] close'); - }; - socket.onerror = (e) => { - clearInterval(state.heart); - console.info('[chat] error', e); - reject(e); - }; - commit('setSocket', socket); - } else { - ElMessage.error('当前浏览器不支持 WebSocket'); - reject('not support websocket'); - } - }); + SET_CUR_SERVICE(state, data) { + state.curCustomerService = data || {}; }, - /** - * 发送心跳,任务监测 - */ - heart: ({ state, dispatch }) => { - dispatch('send', { - traceType: 26, - content: { storeId: 1 }, - }); - // console.info('[chat] heart'); - state.task.forEach((item) => { - dispatch('send', item); - }); + SET_SESSION_DATA(state, data) { + state.sessionData = data; }, - /** - * 执行任务 - */ - invoke: ({ commit, dispatch }, data) => { - data.traceId = UUID(); - commit('addTask', data); - dispatch('send', data); - }, - /** - * 撤销任务 - */ - revoke: ({ state, commit }, data) => { - console.info(state.task.length); - commit( - 'setTask', - state.task.filter((item) => item.traceType !== data) - ); - console.info(state.task.length); + SET_SERVICE_LIST(state, data) { + state.customerServiceList = data; }, - /** - * 发送数据 - */ - send: ({ state }, data) => { - if (window.WebSocket) { - if (state.socket?.readyState === WebSocket.OPEN) { - data.traceId = data.traceId || UUID(); - state.socket.send(JSON.stringify(data)); - if (data.traceType !== 26) { - console.info('[chat] send', data); - } - } - } - }, - /** - * 接收数据 - */ - receive: ({ state, commit, dispatch }, data) => { - data = JSON.parse(data); - if (data.traceType !== 0) { - let index = state.task.findIndex((item) => item.traceId === data.traceId); - if (index !== -1) { - console.info('[chat] data', data); - commit('delTask', index); - dispatch('handle', data); - } else if (data.traceType === 25) { - console.info('[chat] msg', data); - dispatch('handle', data); - } else { - console.info('[chat] deprecated', data); - } - } - }, - /** - * 处理数据 - */ - handle: ({ state, commit, dispatch }, { code, traceType, content }) => { - if (code === 200) { - switch (traceType) { - case 25: // 收到消息 - if (content.sessionId === state.currentSession) { - commit('setMessageList', [...state.messageList, content]); - dispatch('submitRead'); - } else { - dispatch('querySession'); - } - break; - case 32: // 发送消息 - commit('setMessageList', [...state.messageList, content]); - break; - case 27: // 会话列表 - commit('setSessionData', content); - break; - case 28: // 消息列表 - commit('setMessageList', [...content, ...state.messageList]); - break; - case 29: // 客服列表 - commit('setCustomerServiceList', content); - break; - case 31: // 已读消息 - dispatch('querySession'); - break; - default: - break; - } - } - }, - /** - * 查询会话列表 - */ - querySession: ({ dispatch }) => { - dispatch('invoke', { - traceType: 27, - content: { storeId: 1 }, - }); - }, - /** - * 查询会话消息列表 - */ - querySessionMessage: ({ state, dispatch }, data) => { - dispatch('invoke', { - traceType: 28, - content: { sessionId: state.currentSession, size: 10, topMessageId: null, ...data }, +}; +const actions = { + queryCurCustomerService: ({ commit }) => { + api.getCustomerService().then((res) => { + commit('SET_CUR_SERVICE', res); }); }, + /** * 查询可转移客服列表 */ - queryCustomerService: ({ dispatch }) => { - dispatch('invoke', { - traceType: 29, - content: { storeId: 1 }, + queryCustomerService: ({ commit }) => { + api.customerServiceList({ + length: 100, + pageIndex: 1, + }).then((res) => { + commit('SET_SERVICE_LIST', res.records); }); }, /** * 提交转移会话 */ - submitTransferSession: ({ dispatch }, data) => { - dispatch('invoke', { - traceType: 30, - content: { storeId: 1, ...data }, - }); - }, - /** - * 提交已读消息 - */ - submitRead: ({ state, dispatch }) => { - dispatch('invoke', { - traceType: 31, - content: { sessionId: state.currentSession }, - }); - }, - /** - * 提交发送消息 - */ - submitMessage: ({ state, dispatch }, payload) => { - dispatch('invoke', { - traceType: 32, - content: { payload, toSessionId: state.currentSession, type: 1 }, - }); - }, - /** - * 提交发送图片 - */ - submitImage: ({ state, dispatch }, payload) => { - dispatch('invoke', { - traceType: 32, - content: { payload, toSessionId: state.currentSession, type: 3 }, - }); - }, - /** - * 提交发送视频 - */ - submitVideo: ({ state, dispatch }, payload) => { - dispatch('invoke', { - traceType: 32, - content: { payload, toSessionId: state.currentSession, type: 4 }, - }); + submitTransferSession: ({}, data) => { + return api + .transferCustomerService({ + storeId: 1, + ...data, + }) + .then((res) => { + console.log(res, 'resresres'); + }); }, }; export default { diff --git a/src/utils/im.js b/src/utils/im.js index 2ad55fa..d369ebf 100644 --- a/src/utils/im.js +++ b/src/utils/im.js @@ -2,7 +2,7 @@ * @Author: ch * @Date: 2022-06-07 15:52:37 * @LastEditors: ch - * @LastEditTime: 2022-06-09 10:59:15 + * @LastEditTime: 2022-06-13 17:32:19 * @Description: file content */ import * as api from '@/api/chat'; @@ -13,27 +13,15 @@ import { FormatJsonSearch, ToAsyncAwait } from '@/utils/utils'; const Im = new MsbIm({ reconnect: true, }); -const ImInit = () => { +const ImInit = (waiterId) => { return new Promise((reslove, reject) => { - const storeUc = $store.state.auth.userInfo; - if (!storeUc) { - ImInit(); - return false; - } api.getCustomeServiceTicket().then(async (res) => { + console.log(res); const par = FormatJsonSearch({ client: res.client, ticket: res.ticket, - user: storeUc.userId, - // nickname: storeUc.employeeName, - // avatar: storeUc.avatar, - // 1普通用户 2客服链接 + user: waiterId, connect: 2, - // user: 2, - // client: 'yan_xuan', - // ticket: '9kpEgiLzVG14znSTvElLOJE5MEMa/EGdexhab4CbDmLzDGnE+UXmVOvUs4ixyPeQ', - // nickname: '周渺', - // avatar: 'https://msb-edu-dev.oss-cn-beijing.aliyuncs.com/uc/account-avatar/桌面水果.jpg', }); const { error, result } = await ToAsyncAwait( Im.init({ @@ -50,7 +38,7 @@ const ImInit = () => { }; Im.interceptors.dataChangeAfter = () => { - $store.commit('im/SET_SESSION_DATA', JSON.parse(JSON.stringify(Im.sessionData))); + $store.commit('chat/SET_SESSION_DATA', JSON.parse(JSON.stringify(Im.sessionData))); // let msgCount = 0; // Im.sessionData.forEach((i) => { // msgCount += i.unreadCount; @@ -61,7 +49,7 @@ Im.interceptors.dataChangeAfter = () => { Im.interceptors.onLogout = () => { Im.setSessionData([]); // Im.setCurSessionId(null); - $store.commit('im/SET_SESSION_DATA', []); + $store.commit('chat/SET_SESSION_DATA', []); // $store.commit('SET_IM_MSG_COUNT', 0); }; export { Im, ImInit }; diff --git a/src/utils/msb-im.js b/src/utils/msb-im.js index 2bec60b..5866b2c 100644 --- a/src/utils/msb-im.js +++ b/src/utils/msb-im.js @@ -2,7 +2,7 @@ * @Author: ch * @Date: 2022-05-18 14:54:47 * @LastEditors: ch - * @LastEditTime: 2022-06-10 19:15:46 + * @LastEditTime: 2022-06-13 22:14:45 * @Description: file content */ import '@/utils/poto-req'; @@ -34,6 +34,7 @@ const fromatPotoReq = (traceId, traceType, content) => { message: res.getMessage(), }; }; + class MsbIm { defaultOption = { ioKey: 'traceId', @@ -186,6 +187,12 @@ class MsbIm { // 不在当前会话窗口则向会话消息加1条未读 if (ctx.sessionId !== this.curSessionId) { curHisData.unreadCount++; + } else { + this.setRead({ + content: { + sessionId: this.curSessionId, + }, + }); } newData = historyData; } else { @@ -193,9 +200,9 @@ class MsbIm { newData = [ ...historyData, { - fromAvatar: ctx.fromAvatar, + fromAvatar: ctx.session.fromAvatar, fromId: ctx.fromId, - fromNickname: ctx.fromNickname, + fromNickname: ctx.session.fromNickname, id: ctx.sessionId, lastMessage: ctx, messageList: [ctx], @@ -270,23 +277,24 @@ class MsbIm { return Promise.reject(error); } const { content } = result; + // let newData = []; content.sessionVOS.forEach((item) => { if (item.lastMessage) { item.lastMessage.payload = JSON.parse(item.lastMessage.payload || {}); } - let historyData = this.sessionData; - let hisIndex = historyData.findIndex((i) => i.id === item.id); - if (hisIndex >= 0) { - historyData[hisIndex].lastMessage = item.lastMessage; - historyData[hisIndex].unreadCount++; - this.setSessionData(historyData); - } else { - item.messageList = []; - const newData = [...historyData, item]; - this.setSessionData(newData); - } + // let historyData = this.sessionData; + // let hisIndex = historyData.findIndex((i) => i.id === item.id); + // if (hisIndex >= 0) { + // historyData[hisIndex].lastMessage = item.lastMessage; + // historyData[hisIndex].unreadCount++; + // newData.push(historyData[hisIndex]); + // } else { + // item.messageList = []; + // newData = [...newData, item]; + // } }); + this.setSessionData(content.sessionVOS); return Promise.resolve(result); } /** @@ -369,6 +377,8 @@ class MsbIm { } // 点发送,立即把消息加入消息列表,标记为发送中状态 curSession.messageList.push(msgCtx); + this.setSessionData(this.sessionData); + // 超过时间未返回视为发送失败 this.timerStatus(msgCtx); @@ -455,5 +465,11 @@ class MsbIm { } return Promise.resolve(result); } + close() { + this.socket.close(); + this.socket = null; + this.isOpen = false; + this.setSessionData([]); + } } export default MsbIm; diff --git a/src/views/chat/index.vue b/src/views/chat/index.vue index d52d724..e7f7e4d 100644 --- a/src/views/chat/index.vue +++ b/src/views/chat/index.vue @@ -37,19 +37,19 @@
{{ item.fromNickname }}
- {{ store.getters['im/parseTime'](item.lastMessage.createTimeStamp) }} + {{ store.getters['chat/parseTime'](item.lastMessage.createTimeStamp) }}
- {{ store.getters['im/parseText'](item.lastMessage) }} + {{ store.getters['chat/parseText'](item.lastMessage) }}
-
+
@@ -68,16 +68,11 @@
- + 加载更多 { - Im.getSessionList(); + store.dispatch('chat/queryCurCustomerService'); + const socketInit = () => { + if (!store.state.chat.curCustomerService.waiterId) { + setTimeout(() => { + socketInit(); + }, 1000); + return false; + } + ImInit(store.state.chat.curCustomerService.waiterId).then(() => { + Im.getSessionList(); + }); + }; + onMounted(() => { + socketInit(); }); + const opts = computed(() => store.state.chat.opts); // 统计 @@ -170,26 +177,30 @@ const state = reactive({ message: '', }); - const sessionList = computed(() => store.state.im.sessionData); - const currentSession = computed(() => sessionList.value.find((item) => item.id === currentSessionId.value)); + const sessionList = computed(() => store.state.chat.sessionData); + const currentSession = computed(() => sessionList.value.find((item) => item.id === currentSessionId.value) || {}); const handleChangeSession = (id) => { currentSessionId.value = id; // 设置实例对象当前会话ID Im.setCurSessionId(id); + // 已读 Im.setRead({ content: { sessionId: id, }, }); - if (!sessionMessageList.length) { - Im.getHistoryMsg(); - } + setTimeout(() => { + if (!sessionMessageList.value.length) { + Im.getHistoryMsg(); + } + }, 100); }; // 聊天 - const sessionMessageList = computed(() => { - return currentSession.messageList || []; + const sessionMessageList = ref([]); + watch(currentSession, () => { + sessionMessageList.value = currentSession.value.messageList || []; }); const refsMessageList = ref(null); watch(sessionMessageList, (value, old) => { @@ -197,23 +208,26 @@ ? refsMessageList.value.resize$.scrollHeight - refsMessageList.value.resize$.scrollTop : 0; nextTick(() => { - if (!old?.length || value.indexOf(old[0]) === 0) { - refsMessageList.value.setScrollTop(refsMessageList.value.resize$.scrollHeight); - } else { - refsMessageList.value.setScrollTop(refsMessageList.value.resize$.scrollHeight - offset); - } + // if (!old?.length || value.indexOf(old[0]) === 0) { + refsMessageList.value && refsMessageList.value.setScrollTop(refsMessageList.value.resize$.scrollHeight); + // } else { + // refsMessageList.value.setScrollTop(refsMessageList.value.resize$.scrollHeight - offset); + // } }); }); const handleLoadMore = () => { Im.getHistoryMsg(); }; const handleSendMessage = (e) => { + const curService = store.state.chat.curCustomerService; if (e && e.shiftKey) { state.message += '\n'; } else { if (state.message) { Im.sendMsg({ - fromId: userInfo.value.userId, + fromId: curService.waiterId, + fromAvatar: curService.waiterAvatar, + fromNickname: curService.waiterNickname, content: { toSessionId: currentSessionId.value, payload: { text: state.message }, @@ -247,14 +261,14 @@ prop: 'waiterNickname', minWidth: 160, }, - { - label: '类型', - prop: 'type', - width: 160, - slots: { - default: ({ row }) => proxy.$dict(unref(opts).customerServiceType, row.type), - }, - }, + // { + // label: '类型', + // prop: 'type', + // width: 160, + // slots: { + // default: ({ row }) => proxy.$dict(unref(opts).customerServiceType, row.type), + // }, + // }, { label: '操作', width: 100, @@ -268,9 +282,9 @@ }, ], }); - const customerServiceList = computed(() => store.state.im.customerServiceList); + const customerServiceList = computed(() => store.state.chat.customerServiceList); const handleTransferSession = () => { - store.dispatch('im/queryCustomerService'); + store.dispatch('chat/queryCustomerService'); transferVisible.value = true; }; const handleConfirmTransfer = async (row) => { @@ -279,11 +293,17 @@ confirmButtonText: '确定', }); if (res.action === 'confirm') { - store.dispatch('im/submitTransferSession', { - toWaiterId: row.waiterId, - sessionId: unref(currentSessionId), - reason: res.value, - }); + store + .dispatch('chat/submitTransferSession', { + toWaiterId: row.waiterId, + sessionId: unref(currentSessionId), + reason: res.value, + }) + .then((res) => { + currentSessionId.value = null; + Im.setCurSessionId(null); + Im.getSessionList(); + }); transferVisible.value = false; } } catch (e) { @@ -306,12 +326,17 @@ }; const handleSendImage = async (e) => { const file = e.target.files[0]; + if (!file.type.includes('image')) { + proxy.$message.warning('只能发送图片哦~'); + return false; + } e.target.value = null; let url = await upload('mall-product', 'im/', file); - // store.dispatch('chat/submitImage', { url }); - + const curService = store.state.chat.curCustomerService; Im.sendMsg({ - fromId: 2, //userInfo.value.id, + fromId: curService.waiterId, + fromAvatar: curService.waiterAvatar, + fromNickname: curService.waiterNickname, content: { toSessionId: currentSessionId.value, payload: { url }, @@ -327,12 +352,19 @@ }; const handleSendVideo = async (e) => { const file = e.target.files[0]; + if (!file.type.includes('video')) { + proxy.$message.warning('只能发送视频哦~'); + return false; + } e.target.value = null; let url = await upload('mall-product', 'im/', file); // store.dispatch('chat/submitVideo', { url }); + const curService = store.state.chat.curCustomerService; Im.sendMsg({ - fromId: 2, //userInfo.value.id, + fromId: curService.waiterId, + fromAvatar: curService.waiterAvatar, + fromNickname: curService.waiterNickname, content: { toSessionId: currentSessionId.value, payload: { url }, @@ -362,7 +394,6 @@ display: flex; border: 1px solid #ebeef5; .aside { - width: 240px; border-right: 1px solid #ebeef5; .aside-header { height: 60px; diff --git a/src/views/chat/message.vue b/src/views/chat/message.vue index b648613..d9a03eb 100644 --- a/src/views/chat/message.vue +++ b/src/views/chat/message.vue @@ -25,7 +25,7 @@ 复制
-
+
{{ content.name }}
@@ -73,26 +73,26 @@
- {{ store.getters['im/parseText'](props.message) }} + {{ store.getters['chat/parseText'](props.message) }}
- {{ store.getters['im/parseTime'](props.message.createTimeStamp) }} + {{ store.getters['chat/parseTime'](props.message.createTimeStamp) }}
@@ -111,7 +111,7 @@ }, }); const messageType = computed(() => store.state.chat.messageType); - const content = computed(() => store.getters['im/parseContent']({ ...props.message.payload })); + const content = computed(() => store.getters['chat/parseContent']({ ...props.message.payload })); const handleProduct = (id) => { router.push({ name: 'UpdateProduct', @@ -121,6 +121,9 @@ }, }); }; + const handleProductDetail = (id) => { + window.open(`${import.meta.env.VITE_BROWSER_URL}/goods/detail/${id}`, '_blank'); + }; const handleOrder = (id) => { router.push({ name: 'OrderDetail', @@ -148,6 +151,9 @@ justify-content: space-between; align-items: center; } + .product { + cursor: pointer; + } .avatar { margin: 0 @layout-space; }