import API from '@/api' import { getWsUrl } from '@/util/contract.js' import WsManager from '@/util/wsManager.js' import Vue from 'vue' import { addSomeInArray, addTimeMsgInItem, decryptoMsg, addLinkItem, removeItemIfEixt, formatPinMsg, dealErrorMsg, checkAtMe } from '@/util/util.js' import axios from 'axios' import _ from 'lodash' var socket = null // websocket实例 const group = { state: { useCache: false, // 是否使用缓存 adminList: [], // 管理员列表 creator: '', // 群主 blockList: [], // 禁言小分队 userCounts: '', // 当前房间用户数 groupName: '', // 当前房间名称 shareName: '', // 房间分享名称 groupNotice: '', // 群公告 privateName: '', // 私聊人名称 coverPhoto: '', // 群头像 inviteUrl: '', // 邀请链接 sessionInfo: {}, // 群设置 groupId: '', // 当前房间ID members: {}, // 当前房间用户列表 userId: '', // 当前用户ID nickName: '', atList: [], // 被at的msgMap pinList: [], // 当前置顶列表 chatList: [], // 聊天数据列表 startHash: '', // 当前储存消息列表开始的hash endHash: '', // 当前储存消息列表结束的hash userInfo: {}, // 用户群角色信息 unreadNums: 0, // 用户未读消息数 chatInputFocus: false, // 聊天输入框聚焦 pinMsg: {} // 置顶消息 }, mutations: { initGroup (state, data) { state.userId = data.userId state.groupId = data.groupId state.useCache = data.useCache }, initState (state, data) { state.endHash = '' state.startHash = '' state.chatList = [] state.pinList = [] state.atList = [] state.unreadNums = 0 state.userId = data.user_id state.userInfo = data }, setGroupInfo (state, data) { state.groupName = data.groupName state.userCounts = data.userCounts state.members = data.members state.groupNotice = data.groupNotice state.inviteUrl = data.inviteUrl state.sessionInfo = data.sessionInfo state.coverPhoto = data.coverPhoto state.adminList = data.adminList state.blockList = data.blockList state.shareName = data.shareName state.creator = data.creator if (data.pinMsg) { formatPinMsg(data.pinMsg, state.userId) state.pinList.push(data.pinMsg) removeItemIfEixt(state.pinList, state.chatList, item => item.hash) } state.pinMsg = data.pinMsg || {} }, setGroupUserInfo (state, data) { state.userInfo = data state.userId = data.user_id }, /** * @des 添加历史消息到当前chatList队列 * @param {Object} state * @param {Array} data 待添加的消息队列 */ addHistoryList (state, data) { if (data.length) { addSomeInArray(data) removeItemIfEixt(state.pinList, data, item => item.hash) // 根据 hash 求data相对于chatList的差集(去重) let newList = _.differenceBy(data, state.chatList, 'hash') || [] state.chatList = newList.concat(state.chatList) } }, addChatItem (state, data, isFirst) { if (data && data.length > 0) { addSomeInArray(data) removeItemIfEixt(state.pinList, data, item => item.hash) isFirst && data.forEach(item => { // 检测是否被@ if (checkAtMe(item.content, state.userInfo.user_name)) { state.atList.push(item) } }) // 根据 hash 求data相对于chatList的差集(去重) let newList = _.differenceBy(data, state.chatList, 'hash') || [] state.chatList = state.chatList.concat(newList) } else { let item = { name: state.members[data.from] ? state.members[data.from].nick_name : 'unknown', content: data.content, userId: data.from, timestamp: data.timestamp, avatar: state.members[data.from] ? state.members[data.from].cover_photo : '', hash: data.hash, type: data.from === state.userId ? 'me' : 'you', msg_type: data.msg_type, loading: Boolean(data.loading), res: data.res, fail: false } // 针对自己发送的情况 if (data.createTime) { item.createTime = data.createTime } addTimeMsgInItem(item, state.chatList) addLinkItem(item) // 去重 if (!state.chatList.some(n => { return n.hash === item.hash })) { state.chatList.push(item) } } }, addPacketItem (state, data) { let item = { name: state.members[data.from] ? state.members[data.from].nick_name : 'unknown', timestamp: data.timestamp, avatar: state.members[data.from] ? state.members[data.from].cover_photo : '', userId: data.from, msg_type: data.type === 'new_redpack' ? 4 : 5, content: JSON.parse(decryptoMsg(data.content)), type: data.from === state.userId ? 'me' : 'you', ext: { grabbed: 0, redpack_status: 0 }, hash: data.hash } addTimeMsgInItem(item, state.chatList) state.chatList.push(item) state.endHash = data.hash }, addPacketTip (state, data) { let item = { content: { trxId: data.content.redpack_trx_id, title: data.content.title }, ext: { grabbed: 1, redpack_status: 0 }, avatar: state.members[data.from] ? state.members[data.from].cover_photo : '', name: state.members[data.from] ? state.members[data.from].nick_name : 'unknown', from: data.from, to: data.to, redPackTip: true } state.chatList.push(item) }, unpdatePacketItem (state, { type, trxId, data }) { let list = state.chatList let ind = list.findIndex(e => { return e.msg_type == 4 && e.content.trxId == trxId }) let item = list[ind] item.ext[type] = data Vue.set(state.chatList, ind, item) }, reSendChatItem (state, data) { let chatList = state.chatList chatList.forEach(item => { if (item.createTime == data.createTime) { item.loading = true item.fail = false } }) }, clearAtList (state) { state.atList = [] }, /** * 撤回消息,设置消息数据为{ repealMsg: true } * @param {state} state * @param {Object|Number} data */ repealChatItem (state, data) { let { pinList, chatList } = state for (let i = chatList.length - 1; i >= 0; i--) { let item = chatList[i] if (item.hash === data.hash) { Vue.set(item, 'repealMsg', true) Vue.set(item, 'from', data.from) } } if (pinList.length) { removeItemIfEixt(pinList, [data], item => item.hash) } }, deleteChatItem (state, hash) { let index = state.chatList.findIndex(item => item.hash === hash) state.chatList.splice(index, index) }, addUnreadNums (state) { state.unreadNums++ }, resetUnreadNums (state) { state.unreadNums = 0 }, setHash (state, data) { if (data.startHash) { state.startHash = data.startHash } if (data.endHash) { state.endHash = data.endHash } }, /** * @des 更新置顶 */ updatePin (state, data) { state.sessionInfo.is_pin = data }, /** * @des 更新免打扰 */ updateMute (state, data) { state.sessionInfo.is_mute = data }, /** * @des 更新群信息 */ updateGroup (state, { key, data }) { state[key] = data }, updateMembers (state, data) { state.members = Object.assign({}, state.members, data) }, /** * @des 更新members里自己的头像 */ updateMemberAvatar (state, { userId, imageUrl }) { let members = state.members let chatList = state.chatList for (let i = 0; i < chatList.length; i++) { let id = chatList[i]['userId'] if (id === userId) { let item = chatList[i] item['avatar'] = imageUrl Vue.set(state.chatList, i, item) } } for (let k in members) { if (k === userId) { let item = members[k] item['cover_photo'] = imageUrl Vue.set(state.members, k, item) return } } }, /** * @des 更新members里自己的名字 */ updateMemberNickName (state, { userId, nickName }) { let members = state.members let chatList = state.chatList for (let i = 0; i < chatList.length; i++) { let id = chatList[i]['userId'] if (id === userId) { let item = chatList[i] item['name'] = nickName Vue.set(state.chatList, i, item) } } for (let k in members) { if (k === userId) { let item = members[k] item['nick_name'] = nickName Vue.set(state.members, k, item) return } } }, /** * @des 更新聊天输入框聚焦状态 */ updateChatInputFocus (state, focus) { state.chatInputFocus = focus }, /** * @des 更新群组封禁人员列表 * @param {String} param.type [delete,add] * @param {String} param.id */ updateGroupBlockList (state, param) { let { type, id } = param if (type === 'delete') { let index = state.blockList.findIndex(uid => uid == id) index !== -1 && state.blockList.splice(index, 1) } else if (type === 'add') { state.blockList.push(id) } }, /** * * @param {Object|Null} param */ updateGroupPinMsg (state, param) { if (param) { param.visible = true formatPinMsg(param, state.userId) state.pinMsg = param } else { state.pinMsg = {} state.pinList = [] } } }, actions: { /** * @des 获取群基础信息 * @param {Object} store * @param {Object} params */ async getGroupInfo ({ dispatch, commit, state }, params) { let info = await API.group .getGroupInfo({ group_id: state.groupId }) .catch(() => {}) if (info.data.code === 0) { let adminList = info.data.data.adminList let blockList = info.data.data.blockList let pinMsg = info.data.data.pinMsg let group = info.data.data.group let members = info.data.data.members let userInfo = info.data.data.userInfo let sessionInfo = info.data.data.sessionInfo pinMsg && (pinMsg.visible = true) let _members = {} members.forEach(n => { _members[n.user_id] = n }) commit('setGroupInfo', { userCounts: members.length + group.increase_num, groupName: group.group_title, shareName: group.group_name, members: _members, userInfo, groupNotice: group.group_notice, inviteUrl: group.invite_url, sessionInfo, coverPhoto: group.cover_photo, adminList: adminList, blockList: blockList, pinMsg: pinMsg, creator: group.creator }) } }, /** * @des 群聊 获取最新消息【不需要登录】 * @param {Object} store * @param {Object} params */ async getNewMsg ({ dispatch, commit, state }, params = {}) { let isFirst = state.endHash == null let msg = await API.group .getNewMsg({ group_id: state.groupId, client_hash: state.endHash }) .catch(() => {}) if (msg.data.code === 0 && msg.data.data.list.length > 0) { let list = [] msg.data.data.list.forEach(n => { let member = msg.data.data.userMap[n.from] if (!member) return list.push({ name: member ? member.nick_name : 'unknown', content: n.msg, userId: n.from, timestamp: n.create_time_int, avatar: member ? member.cover_photo : '', hash: n.hash, type: n.from === state.userId ? 'me' : 'you', msg_type: n.msg_type, ext: n.ext ? n.ext : null }) }) if (state.chatList.length) { commit('setHash', { endHash: list[list.length - 1].hash }) } else { commit('setHash', { endHash: list[list.length - 1].hash, startHash: list[0].hash }) } commit('addChatItem', list, isFirst) } return 'done' }, /** * @des 私聊 获取最新消息【不需要登录】 * @param {Object} store * @param {Object} params */ async getPrivateNewMsg ({ dispatch, commit, state, rootState }, params = {}) { let msg = await API.person .getNewMsg({ session_id: rootState.curSession, client_hash: state.endHash }) .catch(() => {}) // 有聊天记录 if (msg.data.code === 0 && msg.data.data.list.length > 0) { let list = [] // 更新members信息 let members = msg.data.data.userMap commit('updateGroup', { key: 'members', data: members }) for (let k in members) { if (k !== rootState.userId) { commit('updateGroup', { key: 'privateName', data: members[k]['nick_name'] }) } } msg.data.data.list.forEach(n => { let member = msg.data.data.userMap[n.from] list.push({ name: member ? member.nick_name : 'unknown', content: n.msg, userId: n.from, timestamp: n.create_time_int, avatar: member.cover_photo || '', hash: n.hash, type: n.from === rootState.userId ? 'me' : 'you', msg_type: n.msg_type, ext: n.ext ? n.ext : null }) }) commit('setHash', { endHash: list[list.length - 1].hash, startHash: list[0].hash }) commit('addChatItem', list) } return 'done' }, /** * @des 群聊 获取历史消息【不需要登录】 * @param {Object} store * @param {Object} params */ async getHistoryMsg ({ dispatch, commit, state }, params = {}) { let his = localStorage.getItem(params.hash) // 先从本地缓存中获取记录 let dList = [] let status = '' // 请求状态 if (his && state.useCache) { dList = JSON.parse(his) } else { // 本地缓存中没有记录,从接口获取 let msg = await API.group .getHistoryMsg({ group_id: state.groupId, client_hash: state.startHash }) .catch(() => {}) if (msg.data.code === 0 && msg.data.data.list.length > 0) { // 重新格式数据 msg.data.data.list.forEach(n => { if (n.msg_type == 4) { n.msg = JSON.parse(decryptoMsg(n.msg)) } else { n.msg = decryptoMsg(n.msg) } let member = msg.data.data.userMap[n.from] if (!member) return dList.push({ name: member ? member.nick_name : 'unknown', content: n.msg, userId: n.from, timestamp: n.create_time_int, avatar: member ? member.cover_photo : '', hash: n.hash, type: n.from === state.userId ? 'me' : 'you', msg_type: n.msg_type, ext: n.ext ? n.ext : null }) }) // 保存到本地缓存中 if (state.useCache) { localStorage.setItem(params.hash, JSON.stringify(dList)) } } } if (dList.length > 0) { commit('setHash', { startHash: dList[0].hash }) commit('addHistoryList', dList) status = 'done' } else { commit('setHash', { startHash: null }) status = 'end' } return status }, /** * @des 私聊 获取历史消息【不需要登录】 * @param {Object} store * @param {Object} params */ async getPrivateHistoryMsg ( { dispatch, commit, state, rootState }, params = {} ) { let his = localStorage.getItem(params.hash) // 先从本地缓存中获取记录 let dList = [] let status = '' // 请求状态 if (his && state.useCache) { dList = JSON.parse(his) } else { // 本地缓存中没有记录,从接口获取 let msg = await API.person .getHistoryMsg({ session_id: rootState.curSession, client_hash: params.hash }) .catch(() => {}) if (msg.data.code === 0 && msg.data.data.list.length > 0) { // 重新格式数据 msg.data.data.list.forEach(n => { n.msg = decryptoMsg(n.msg) let member = msg.data.data.userMap[n.from] dList.push({ name: member ? member.nick_name : 'unknow', content: n.msg, userId: n.from, timestamp: n.create_time_int, avatar: member.cover_photo || '', hash: n.hash, type: n.from === rootState.userId ? 'me' : 'you', msg_type: n.msg_type, ext: n.ext ? n.ext : null }) }) // 保存到本地缓存中 if (state.useCache) { localStorage.setItem(params.hash, JSON.stringify(dList)) } } } if (dList.length > 0) { commit('setHash', { startHash: dList[0].hash }) commit('addHistoryList', dList) status = 'done' } else { commit('setHash', { startHash: null }) status = 'end' } return status }, /** * 发送群聊消息 * @param {Object} data * @param {Object} params */ doSendMsg ({ dispatch, commit, state }, params = {}) { return new Promise((resolve, reject) => { API.group .sendMsg({ group_id: state.groupId, msg_type: params.type, msg: params.msg }) .then(({ data }) => { if (data.code === 0) { if (params.createTime) { let createTime = params.createTime data.data.content = decryptoMsg(data.data.content) let list = state.chatList for (let i = list.length - 1; i >= 0; i--) { let listItem = list[i] if (listItem.createTime === createTime) { listItem.hash = data.data.hash listItem.loading = false } } } else { // 针对用户第一次发言 commit('updateMembers', data.data.userMap) data.data.content = decryptoMsg(data.data.content) commit('addChatItem', data.data) } commit('setHash', { endHash: data.data.hash }) resolve(data.data) } else { if (params.createTime) { dealErrorMsg(state, params.createTime) } } }) .catch(err => { if (params.createTime) { dealErrorMsg(state, params.createTime) } reject(err) }) }) }, /** * 发送聊天文件 * @param {Object} data * @param {Object} params */ async doSendFile ({ dispatch, commit, state, rootState }, params = {}) { return new Promise((resolve, reject) => { let userId = rootState.userId let token = rootState.token let formData = new FormData() formData.append('group_id', state.groupId) formData.append('res', params.res) formData.append('user_id', userId) formData.append('token', token) axios({ url: API.host + 'group/sendFile', method: 'post', data: formData, headers: { 'Content-Type': 'multipart/form-data' } }) .then(({ data }) => { if (data.code === 0) { if (params.createTime) { let createTime = params.createTime data.data.content = decryptoMsg(data.data.content) let list = state.chatList list.forEach(item => { if (item.createTime === createTime) { item.res = undefined item.content = data.data.content item.hash = data.data.hash item.loading = false item.fail = false } }) } else { // 针对用户第一次发言 commit('updateMembers', data.data.userMap) data.data.content = decryptoMsg(data.data.content) commit('addChatItem', data.data) } commit('setHash', { endHash: data.data.hash }) resolve(data.data) } else { if (params.createTime) { dealErrorMsg(state, params.createTime) } } }) .catch(err => { if (params.createTime) { dealErrorMsg(state, params.createTime) } reject(err) }) }) }, /** * 发送私聊消息 * @param {Object} data * @param {Object} params */ doSendPrivateMsg ({ dispatch, commit, rootState }, params = {}) { return new Promise((resolve, reject) => { API.person .sendMsg({ session_id: rootState.curSession, msg_type: params.type, msg: params.msg }) .then(({ data }) => { if (data.code === 0) { commit('addChatItem', data.data) resolve(data.data) } }) .catch(err => reject(err)) }) }, doUploadFile ({ dispatch, commit, state }, params = {}) { API.session.fileUpload({ name: 'res' }) }, /** * 链接websocket * @param {Object} data * @param {Object} params */ initSocket ({ commit, rootState, dispatch }, params = {}) { if (!window.WebSocket) { console.error('Error: WebSocket is not supported .') return } let host = `${getWsUrl()}/?user_id=${rootState.userId}&token=${ rootState.token }` if (socket) { socket.destroy() socket = null } socket = new WsManager(host, { autoConnect: true, // 自动连接 reconnection: true, // 断开自动重连 reconnectionDelay: 5000, // 重连间隔时间,单位秒 keepAliveContent: JSON.stringify({ act: 'alive', player: name }) // 心跳包内容 }) socket.on('open', res => {}) socket.on('message', e => { let _data = JSON.parse(e) let channel = _data.channel let data = _data.data // 群聊 if (channel.match('chat:group')) { if (data.type === 'msg' && data.from !== rootState.userId) { // 用户打开当前群 if (rootState.curSession === data.group_id) { dispatch('getNewMsg') commit('addUnreadNums') } } if (data.type === 'leave') { } if (data.type === 'update') { } if (data.type === 'add_admin') { } if (data.type === 'remove_admin') { } if (data.type === 'repeal') { commit('repealChatItem', data) } if (data.type === 'unblock') { commit('updateGroupBlockList', { type: 'delete', id: data.to }) } if (data.type === 'join') { commit('updateMembers', data.user_info) } if (data.type === 'pin_msg') { commit('updateGroupPinMsg', data.pinMsg) } if (data.type === 'unpin_msg') { commit('updateGroupPinMsg', null) } } // 私聊 if (channel.match('chat:person')) { if (data.type === 'msg') { // 用户打开当前聊天 if (rootState.curSession.indexOf(data.to) > -1) { // commit('addChatItem', data) dispatch('getPrivateNewMsg') commit('addUnreadNums') } } if (data.type === 'repeal') { commit('repealChatItem', data.data) } } }) }, /** * 撤回消息 * @param {Object} params * {index:number, session_id:string, hash:string} */ async doRepealGroupMsg ({ dispatch, commit, state }, params = {}) { try { await API.group.repealGroupMsg({ group_id: state.groupId, hash: params.hash }) } catch (error) {} }, /** * 封禁群用户 */ async doBlockUser ({ state }, params = {}) { try { await API.group.blockUser({ group_id: state.groupId, block_id: params.id }) } catch (error) {} }, /** * 解禁用户 */ async doUnBlockUser ({ state }, params = {}) { try { await API.group.unblockUser({ group_id: state.groupId, block_id: params.id }) } catch (error) {} }, /** * 更新置顶 */ async doPinMsg ({ state }, params = {}) { try { await API.group.pinMsg({ group_id: state.groupId, hash: params.hash }) } catch (error) {} }, /** * 取消置顶 */ async doUnpinMsg ({ state }, params = {}) { try { await API.group.unpinMsg({ group_id: state.groupId, hash: params.hash }) } catch (error) {} } }, getters: {} } export default group