|
|
|
@ -4,23 +4,34 @@ |
|
|
|
<div class="navi-bar"> |
|
|
|
<div class="navi-bar-box"> |
|
|
|
<div class="top"> |
|
|
|
<div class="user-head-image"> |
|
|
|
<head-image :name="userStore.userInfo.nickName" :size="38" |
|
|
|
:url="userStore.userInfo.headImageThumb" @click.native="showSettingDialog = true"> |
|
|
|
<!-- 用户头像区域 - 点击打开账号切换菜单 --> |
|
|
|
<div class="user-head-image-wrapper"> |
|
|
|
<div class="user-head-image" @click.stop="toggleAccountMenu"> |
|
|
|
<head-image |
|
|
|
:name="userStore.userInfo.nickName" |
|
|
|
:size="38" |
|
|
|
:url="userStore.userInfo.headImageThumb"> |
|
|
|
</head-image> |
|
|
|
<!-- 账号切换标识 --> |
|
|
|
<!-- <div class="account-indicator"> |
|
|
|
<span class="account-name">{{ userStore.userInfo.userName }}</span> |
|
|
|
<i class="el-icon-arrow-down" :class="{ active: showAccountMenu }"></i> |
|
|
|
</div> --> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="menu"> |
|
|
|
<router-link class="link" v-bind:to="'/home/chat'"> |
|
|
|
<!-- <router-link class="link" v-bind:to="'/home/chat'"> |
|
|
|
<div class="menu-item"> |
|
|
|
<span class="icon iconfont icon-chat"></span> |
|
|
|
<div v-show="unreadCount > 0" class="unread-text">{{ unreadCount }}</div> |
|
|
|
</div> |
|
|
|
</router-link> |
|
|
|
<router-link class="link" v-bind:to="'/home/friend'"> |
|
|
|
</router-link> --> |
|
|
|
<!-- <router-link class="link" v-bind:to="'/home/friend'"> |
|
|
|
<div class="menu-item"> |
|
|
|
<span class="icon iconfont icon-friend"></span> |
|
|
|
</div> |
|
|
|
</router-link> |
|
|
|
</router-link> --> |
|
|
|
<!-- <router-link class="link" v-bind:to="'/home/group'"> |
|
|
|
<div class="menu-item"> |
|
|
|
<span class="icon iconfont icon-group" style="font-size: 28px"></span> |
|
|
|
@ -42,9 +53,29 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="content-box"> |
|
|
|
<router-view></router-view> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 账号切换菜单 --> |
|
|
|
<account-switch-menu |
|
|
|
:visible="showAccountMenu" |
|
|
|
:currentUser="userStore.userInfo" |
|
|
|
:position="menuPosition" |
|
|
|
@switch="onSwitchAccount" |
|
|
|
@add-account="onAddAccount" |
|
|
|
@logout="onExit" |
|
|
|
@close="showAccountMenu = false"> |
|
|
|
</account-switch-menu> |
|
|
|
|
|
|
|
<!-- 遮罩层 --> |
|
|
|
<div |
|
|
|
v-if="showAccountMenu" |
|
|
|
class="menu-mask" |
|
|
|
@click="showAccountMenu = false"> |
|
|
|
</div> |
|
|
|
|
|
|
|
<setting :visible="showSettingDialog" @close="closeSetting()"></setting> |
|
|
|
<user-info ref="userInfo"></user-info> |
|
|
|
<full-image ref="fullImage"></full-image> |
|
|
|
@ -62,6 +93,7 @@ import FullImage from '../components/common/FullImage.vue'; |
|
|
|
import RtcPrivateVideo from '../components/rtc/RtcPrivateVideo.vue'; |
|
|
|
import RtcPrivateAcceptor from '../components/rtc/RtcPrivateAcceptor.vue'; |
|
|
|
import RtcGroupVideo from '../components/rtc/RtcGroupVideo.vue'; |
|
|
|
import AccountSwitchMenu from '../components/account/AccountSwitchMenu.vue'; |
|
|
|
|
|
|
|
export default { |
|
|
|
components: { |
|
|
|
@ -71,11 +103,14 @@ export default { |
|
|
|
FullImage, |
|
|
|
RtcPrivateVideo, |
|
|
|
RtcPrivateAcceptor, |
|
|
|
RtcGroupVideo |
|
|
|
RtcGroupVideo, |
|
|
|
AccountSwitchMenu |
|
|
|
}, |
|
|
|
data() { |
|
|
|
return { |
|
|
|
showSettingDialog: false, |
|
|
|
showAccountMenu: false, |
|
|
|
menuPosition: { x: 0, y: 0 }, |
|
|
|
lastPlayAudioTime: new Date().getTime() - 1000, |
|
|
|
reconnecting: false, |
|
|
|
privateMessagesBuffer: [], |
|
|
|
@ -85,22 +120,20 @@ export default { |
|
|
|
methods: { |
|
|
|
init() { |
|
|
|
this.$eventBus.$on('openPrivateVideo', (rctInfo) => { |
|
|
|
// 进入单人视频通话 |
|
|
|
this.$refs.rtcPrivateVideo.open(rctInfo); |
|
|
|
}); |
|
|
|
this.$eventBus.$on('openGroupVideo', (rctInfo) => { |
|
|
|
// 进入多人视频通话 |
|
|
|
this.$refs.rtcGroupVideo.open(rctInfo); |
|
|
|
}); |
|
|
|
this.$eventBus.$on('openUserInfo', (user, pos) => { |
|
|
|
// 打开用户卡片 |
|
|
|
this.$refs.userInfo.open(user, pos); |
|
|
|
}); |
|
|
|
this.$eventBus.$on('openFullImage', url => { |
|
|
|
// 图片全屏 |
|
|
|
this.$refs.fullImage.open(url); |
|
|
|
}); |
|
|
|
this.configStore.setAppInit(false) |
|
|
|
|
|
|
|
this.configStore.setAppInit(false); |
|
|
|
|
|
|
|
this.loadStore().then(() => { |
|
|
|
// ws初始化 |
|
|
|
this.$wsApi.connect(process.env.VUE_APP_WS_URL, sessionStorage.getItem("accessToken")); |
|
|
|
@ -108,16 +141,14 @@ export default { |
|
|
|
if (this.reconnecting) { |
|
|
|
this.onReconnectWs(); |
|
|
|
} else { |
|
|
|
// 加载离线消息 |
|
|
|
this.pullOfflineMessage(); |
|
|
|
this.configStore.setAppInit(true); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
this.$wsApi.onMessage((cmd, msgInfo) => { |
|
|
|
if (cmd == 2) { |
|
|
|
// 关闭ws |
|
|
|
this.$wsApi.close(3000) |
|
|
|
// 异地登录,强制下线 |
|
|
|
this.$wsApi.close(3000); |
|
|
|
this.$alert("您已在其他地方登录,将被强制下线", "强制下线通知", { |
|
|
|
confirmButtonText: '确定', |
|
|
|
callback: action => { |
|
|
|
@ -126,69 +157,156 @@ export default { |
|
|
|
}); |
|
|
|
} else if (cmd == 3) { |
|
|
|
if (!this.configStore.appInit || this.chatStore.loading) { |
|
|
|
// 如果正在拉取离线消息,先放进缓存区,等待消息拉取完成再处理,防止消息乱序 |
|
|
|
this.privateMessagesBuffer.push(msgInfo); |
|
|
|
} else { |
|
|
|
// 插入私聊消息 |
|
|
|
this.handlePrivateMessage(msgInfo); |
|
|
|
} |
|
|
|
} else if (cmd == 4) { |
|
|
|
if (!this.configStore.appInit || this.chatStore.loading) { |
|
|
|
// 如果正在拉取离线消息,先放进缓存区,等待消息拉取完成再处理,防止消息乱序 |
|
|
|
this.groupMessagesBuffer.push(msgInfo); |
|
|
|
} else { |
|
|
|
// 插入群聊消息 |
|
|
|
this.handleGroupMessage(msgInfo); |
|
|
|
} |
|
|
|
} else if (cmd == 5) { |
|
|
|
// 处理系统消息 |
|
|
|
this.handleSystemMessage(msgInfo); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
this.$wsApi.onClose((e) => { |
|
|
|
if (e.code != 3000) { |
|
|
|
// 断线重连 |
|
|
|
if (!this.reconnecting) { |
|
|
|
this.reconnectWs(); |
|
|
|
this.configStore.setAppInit(false) |
|
|
|
this.configStore.setAppInit(false); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}).catch((e) => { |
|
|
|
console.log("初始化失败", e); |
|
|
|
}) |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 切换账号菜单 |
|
|
|
toggleAccountMenu(event) { |
|
|
|
if (this.showAccountMenu) { |
|
|
|
this.showAccountMenu = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const trigger = event.currentTarget; |
|
|
|
const rect = trigger.getBoundingClientRect(); |
|
|
|
|
|
|
|
// 菜单显示在头像右侧 |
|
|
|
this.menuPosition = { |
|
|
|
x: rect.right + 10, |
|
|
|
y: rect.top |
|
|
|
}; |
|
|
|
|
|
|
|
this.showAccountMenu = true; |
|
|
|
}, |
|
|
|
|
|
|
|
// 切换到其他账号 |
|
|
|
async onSwitchAccount(targetUser) { |
|
|
|
this.showAccountMenu = false; |
|
|
|
|
|
|
|
const loading = this.$loading({ |
|
|
|
lock: true, |
|
|
|
text: '正在切换账号...', |
|
|
|
spinner: 'el-icon-loading' |
|
|
|
}); |
|
|
|
|
|
|
|
try { |
|
|
|
const res = await this.$http({ |
|
|
|
url: '/user/switchAccount', |
|
|
|
method: 'post', |
|
|
|
data: { |
|
|
|
targetUserId: targetUser.id, |
|
|
|
terminal: this.getTerminalType() |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
const loginData = res; |
|
|
|
|
|
|
|
if (loginData.user) { |
|
|
|
this.setCookie('username', loginData.user.userName); |
|
|
|
|
|
|
|
sessionStorage.setItem("accessToken", loginData.accessToken); |
|
|
|
sessionStorage.setItem("refreshToken", loginData.refreshToken); |
|
|
|
|
|
|
|
localStorage.setItem('userInfo', JSON.stringify(loginData.user)); |
|
|
|
this.userStore.setUserInfo(loginData.user); |
|
|
|
} |
|
|
|
|
|
|
|
loading.close(); |
|
|
|
this.$message.success(`已切换到客服账号:${targetUser.nickName}`); |
|
|
|
|
|
|
|
// 关闭 WebSocket 连接 |
|
|
|
this.$wsApi.close(3000); |
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
window.location.reload(); |
|
|
|
}, 300); |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
loading.close(); |
|
|
|
if (error !== 'cancel') { |
|
|
|
console.error('切换账号失败:', error); |
|
|
|
this.$message.error('切换账号失败'); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 获取终端类型 |
|
|
|
getTerminalType() { |
|
|
|
const userAgent = navigator.userAgent; |
|
|
|
if (/mobile/i.test(userAgent)) { |
|
|
|
return 2; |
|
|
|
} |
|
|
|
if (/tablet/i.test(userAgent)) { |
|
|
|
return 3; |
|
|
|
} |
|
|
|
return 1; |
|
|
|
}, |
|
|
|
|
|
|
|
setCookie(name, value) { |
|
|
|
document.cookie = name + "=" + escape(value); |
|
|
|
}, |
|
|
|
|
|
|
|
// 添加新账号 |
|
|
|
onAddAccount() { |
|
|
|
this.showAccountMenu = false; |
|
|
|
// 跳转到登录页 |
|
|
|
this.unloadStore(); |
|
|
|
this.configStore.setAppInit(false); |
|
|
|
this.$wsApi.close(3000); |
|
|
|
sessionStorage.removeItem("accessToken"); |
|
|
|
location.href = "/"; |
|
|
|
}, |
|
|
|
|
|
|
|
reconnectWs() { |
|
|
|
// 记录标志 |
|
|
|
this.reconnecting = true; |
|
|
|
// 重新加载一次个人信息,目的是为了保证网络已经正常且token有效 |
|
|
|
this.userStore.loadUser().then(() => { |
|
|
|
// 断线重连 |
|
|
|
this.$message.error("连接断开,正在尝试重新连接..."); |
|
|
|
this.$wsApi.reconnect(process.env.VUE_APP_WS_URL, sessionStorage.getItem( |
|
|
|
"accessToken")); |
|
|
|
this.$wsApi.reconnect(process.env.VUE_APP_WS_URL, sessionStorage.getItem("accessToken")); |
|
|
|
}).catch(() => { |
|
|
|
// 10s后重试 |
|
|
|
setTimeout(() => this.reconnectWs(), 10000) |
|
|
|
}) |
|
|
|
setTimeout(() => this.reconnectWs(), 10000); |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
onReconnectWs() { |
|
|
|
// 重连成功 |
|
|
|
this.reconnecting = false; |
|
|
|
// 重新加载群和好友 |
|
|
|
const promises = []; |
|
|
|
promises.push(this.friendStore.loadFriend()); |
|
|
|
promises.push(this.groupStore.loadGroup()); |
|
|
|
Promise.all(promises).then(() => { |
|
|
|
// 加载离线消息 |
|
|
|
this.pullOfflineMessage(); |
|
|
|
this.configStore.setAppInit(true) |
|
|
|
this.configStore.setAppInit(true); |
|
|
|
this.$message.success("重新连接成功"); |
|
|
|
}).catch(() => { |
|
|
|
this.$message.error("初始化失败"); |
|
|
|
this.onExit(); |
|
|
|
}) |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
loadStore() { |
|
|
|
return this.userStore.loadUser().then(() => { |
|
|
|
const promises = []; |
|
|
|
@ -197,105 +315,94 @@ export default { |
|
|
|
promises.push(this.chatStore.loadChat()); |
|
|
|
promises.push(this.configStore.loadConfig()); |
|
|
|
return Promise.all(promises); |
|
|
|
}) |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
unloadStore() { |
|
|
|
this.friendStore.clear(); |
|
|
|
this.groupStore.clear(); |
|
|
|
this.chatStore.clear(); |
|
|
|
this.userStore.clear(); |
|
|
|
}, |
|
|
|
|
|
|
|
pullOfflineMessage() { |
|
|
|
this.chatStore.setLoading(true); |
|
|
|
const promises = []; |
|
|
|
promises.push(this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId)); |
|
|
|
promises.push(this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId)); |
|
|
|
Promise.all(promises).then(messages => { |
|
|
|
// 处理离线消息 |
|
|
|
messages[0].forEach(m => this.handlePrivateMessage(m)); |
|
|
|
messages[1].forEach(m => this.handleGroupMessage(m)); |
|
|
|
// 处理缓冲区收到的实时消息 |
|
|
|
this.privateMessagesBuffer.forEach(m => this.handlePrivateMessage(m)); |
|
|
|
this.groupMessagesBuffer.forEach(m => this.handleGroupMessage(m)); |
|
|
|
// 清空缓冲区 |
|
|
|
this.privateMessagesBuffer = []; |
|
|
|
this.groupMessagesBuffer = []; |
|
|
|
// 关闭加载离线标记 |
|
|
|
this.chatStore.setLoading(false); |
|
|
|
// 刷新会话 |
|
|
|
this.chatStore.refreshChats(); |
|
|
|
}).catch((e) => { |
|
|
|
console.log(e) |
|
|
|
console.log(e); |
|
|
|
this.$message.error("拉取离线消息失败"); |
|
|
|
this.onExit(); |
|
|
|
}) |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
pullPrivateOfflineMessage(minId) { |
|
|
|
return this.$http({ |
|
|
|
url: "/message/private/loadOfflineMessage?minId=" + minId, |
|
|
|
method: 'GET' |
|
|
|
}) |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
pullGroupOfflineMessage(minId) { |
|
|
|
return this.$http({ |
|
|
|
url: "/message/group/loadOfflineMessage?minId=" + minId, |
|
|
|
method: 'GET' |
|
|
|
}) |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
handlePrivateMessage(msg) { |
|
|
|
// 标记这条消息是不是自己发的 |
|
|
|
msg.selfSend = msg.sendId == this.userStore.userInfo.id; |
|
|
|
// 好友id |
|
|
|
let friendId = msg.selfSend ? msg.recvId : msg.sendId; |
|
|
|
// 会话信息 |
|
|
|
let chatInfo = { |
|
|
|
type: 'PRIVATE', |
|
|
|
targetId: friendId |
|
|
|
} |
|
|
|
// 消息已读处理,清空已读数量 |
|
|
|
}; |
|
|
|
|
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.READED) { |
|
|
|
this.chatStore.resetUnreadCount(chatInfo) |
|
|
|
this.chatStore.resetUnreadCount(chatInfo); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 消息回执处理,改消息状态为已读 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.RECEIPT) { |
|
|
|
this.chatStore.readedMessage({ |
|
|
|
friendId: msg.sendId |
|
|
|
}) |
|
|
|
this.chatStore.readedMessage({ friendId: msg.sendId }); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 消息撤回 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.RECALL) { |
|
|
|
this.chatStore.recallMessage(msg, chatInfo) |
|
|
|
this.chatStore.recallMessage(msg, chatInfo); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 新增好友 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.FRIEND_NEW) { |
|
|
|
this.friendStore.addFriend(JSON.parse(msg.content)); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 删除好友 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.FRIEND_DEL) { |
|
|
|
this.friendStore.removeFriend(friendId); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 对好友设置免打扰 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.FRIEND_DND) { |
|
|
|
this.friendStore.setDnd(friendId, JSON.parse(msg.content)); |
|
|
|
this.chatStore.setDnd(chatInfo, JSON.parse(msg.content)); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 单人webrtc 信令 |
|
|
|
if (this.$msgType.isRtcPrivate(msg.type)) { |
|
|
|
this.$refs.rtcPrivateVideo.onRTCMessage(msg) |
|
|
|
this.$refs.rtcPrivateVideo.onRTCMessage(msg); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 插入消息 |
|
|
|
if (this.$msgType.isNormal(msg.type) || this.$msgType.isTip(msg.type) || this.$msgType.isAction(msg.type)) { |
|
|
|
let friend = this.loadFriendInfo(friendId); |
|
|
|
this.insertPrivateMessage(friend, msg); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
insertPrivateMessage(friend, msg) { |
|
|
|
let chatInfo = { |
|
|
|
type: 'PRIVATE', |
|
|
|
@ -304,75 +411,64 @@ export default { |
|
|
|
headImage: friend.headImage, |
|
|
|
isDnd: friend.isDnd |
|
|
|
}; |
|
|
|
// 打开会话 |
|
|
|
this.chatStore.openChat(chatInfo); |
|
|
|
// 插入消息 |
|
|
|
this.chatStore.insertMessage(msg, chatInfo); |
|
|
|
// 播放提示音 |
|
|
|
if (!friend.isDnd && !this.chatStore.loading && !msg.selfSend && this.$msgType.isNormal(msg.type) && |
|
|
|
msg.status != this.$enums.MESSAGE_STATUS.READED) { |
|
|
|
if (!friend.isDnd && !this.chatStore.loading && !msg.selfSend && |
|
|
|
this.$msgType.isNormal(msg.type) && msg.status != this.$enums.MESSAGE_STATUS.READED) { |
|
|
|
this.playAudioTip(); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
handleGroupMessage(msg) { |
|
|
|
// 标记这条消息是不是自己发的 |
|
|
|
msg.selfSend = msg.sendId == this.userStore.userInfo.id; |
|
|
|
let chatInfo = { |
|
|
|
type: 'GROUP', |
|
|
|
targetId: msg.groupId |
|
|
|
} |
|
|
|
// 消息已读处理 |
|
|
|
}; |
|
|
|
|
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.READED) { |
|
|
|
// 我已读对方的消息,清空已读数量 |
|
|
|
this.chatStore.resetUnreadCount(chatInfo) |
|
|
|
this.chatStore.resetUnreadCount(chatInfo); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 消息回执处理 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.RECEIPT) { |
|
|
|
// 更新消息已读人数 |
|
|
|
let msgInfo = { |
|
|
|
id: msg.id, |
|
|
|
groupId: msg.groupId, |
|
|
|
readedCount: msg.readedCount, |
|
|
|
receiptOk: msg.receiptOk |
|
|
|
}; |
|
|
|
this.chatStore.updateMessage(msgInfo, chatInfo) |
|
|
|
this.chatStore.updateMessage(msgInfo, chatInfo); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 消息撤回 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.RECALL) { |
|
|
|
this.chatStore.recallMessage(msg, chatInfo) |
|
|
|
this.chatStore.recallMessage(msg, chatInfo); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 新增群 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.GROUP_NEW) { |
|
|
|
this.groupStore.addGroup(JSON.parse(msg.content)); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 删除群 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.GROUP_DEL) { |
|
|
|
this.groupStore.removeGroup(msg.groupId); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 对群设置免打扰 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.GROUP_DND) { |
|
|
|
this.groupStore.setDnd(msg.groupId, JSON.parse(msg.content)); |
|
|
|
this.chatStore.setDnd(chatInfo, JSON.parse(msg.content)); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 群视频信令 |
|
|
|
if (this.$msgType.isRtcGroup(msg.type)) { |
|
|
|
this.$nextTick(() => { |
|
|
|
this.$refs.rtcGroupVideo.onRTCMessage(msg); |
|
|
|
}) |
|
|
|
}); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 插入群聊消息 |
|
|
|
if (this.$msgType.isNormal(msg.type) || this.$msgType.isTip(msg.type) || this.$msgType.isAction(msg.type)) { |
|
|
|
let group = this.loadGroupInfo(msg.groupId); |
|
|
|
this.insertGroupMessage(group, msg); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
insertGroupMessage(group, msg) { |
|
|
|
let chatInfo = { |
|
|
|
type: 'GROUP', |
|
|
|
@ -381,19 +477,15 @@ export default { |
|
|
|
headImage: group.headImageThumb, |
|
|
|
isDnd: group.isDnd |
|
|
|
}; |
|
|
|
// 打开会话 |
|
|
|
this.chatStore.openChat(chatInfo); |
|
|
|
// 插入消息 |
|
|
|
this.chatStore.insertMessage(msg, chatInfo); |
|
|
|
// 播放提示音 |
|
|
|
if (!group.isDnd && !this.chatStore.loading && |
|
|
|
!msg.selfSend && this.$msgType.isNormal(msg.type) && |
|
|
|
msg.status != this.$enums.MESSAGE_STATUS.READED) { |
|
|
|
if (!group.isDnd && !this.chatStore.loading && !msg.selfSend && |
|
|
|
this.$msgType.isNormal(msg.type) && msg.status != this.$enums.MESSAGE_STATUS.READED) { |
|
|
|
this.playAudioTip(); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
handleSystemMessage(msg) { |
|
|
|
// 用户被封禁 |
|
|
|
if (msg.type == this.$enums.MESSAGE_TYPE.USER_BANNED) { |
|
|
|
this.$wsApi.close(3000); |
|
|
|
this.$alert("您的账号已被管理员封禁,原因:" + msg.content, "账号被封禁", { |
|
|
|
@ -405,21 +497,28 @@ export default { |
|
|
|
return; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
closeUserInfo() { |
|
|
|
if (this.$refs.userInfo) { |
|
|
|
this.$refs.userInfo.close(); |
|
|
|
} |
|
|
|
this.showAccountMenu = false; |
|
|
|
}, |
|
|
|
|
|
|
|
onSwtichFullScreen() { |
|
|
|
this.configStore.setFullScreen(!this.configStore.fullScreen); |
|
|
|
}, |
|
|
|
|
|
|
|
onExit() { |
|
|
|
this.showAccountMenu = false; |
|
|
|
this.unloadStore(); |
|
|
|
this.configStore.setAppInit(false); |
|
|
|
this.$wsApi.close(3000); |
|
|
|
sessionStorage.removeItem("accessToken"); |
|
|
|
location.href = "/"; |
|
|
|
}, |
|
|
|
|
|
|
|
playAudioTip() { |
|
|
|
// 防止过于密集播放 |
|
|
|
if (new Date().getTime() - this.lastPlayAudioTime > 1000) { |
|
|
|
this.lastPlayAudioTime = new Date().getTime(); |
|
|
|
let audio = new Audio(); |
|
|
|
@ -427,25 +526,28 @@ export default { |
|
|
|
audio.src = url; |
|
|
|
audio.play(); |
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
showSetting() { |
|
|
|
this.showSettingDialog = true; |
|
|
|
}, |
|
|
|
|
|
|
|
closeSetting() { |
|
|
|
this.showSettingDialog = false; |
|
|
|
}, |
|
|
|
|
|
|
|
loadFriendInfo(id) { |
|
|
|
let friend = this.friendStore.findFriend(id); |
|
|
|
if (!friend) { |
|
|
|
friend = { |
|
|
|
id: id, |
|
|
|
showNickName: "未知用户", |
|
|
|
nickName: "未知用户", |
|
|
|
headImage: "" |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
return friend; |
|
|
|
}, |
|
|
|
|
|
|
|
loadGroupInfo(id) { |
|
|
|
let group = this.groupStore.findGroup(id); |
|
|
|
if (!group) { |
|
|
|
@ -453,7 +555,7 @@ export default { |
|
|
|
id: id, |
|
|
|
showGroupName: "未知群聊", |
|
|
|
headImageThumb: "" |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
return group; |
|
|
|
} |
|
|
|
@ -464,7 +566,7 @@ export default { |
|
|
|
let chats = this.chatStore.chats; |
|
|
|
chats.forEach(chat => { |
|
|
|
if (!chat.delete && !chat.isDnd) { |
|
|
|
unreadCount += chat.unreadCount |
|
|
|
unreadCount += chat.unreadCount; |
|
|
|
} |
|
|
|
}); |
|
|
|
return unreadCount; |
|
|
|
@ -485,7 +587,7 @@ export default { |
|
|
|
unmounted() { |
|
|
|
this.$wsApi.close(); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped lang="scss"> |
|
|
|
@ -536,9 +638,57 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.user-head-image-wrapper { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
align-items: center; |
|
|
|
|
|
|
|
.user-head-image { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
align-items: center; |
|
|
|
cursor: pointer; |
|
|
|
transition: all 0.2s; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
transform: scale(1.05); |
|
|
|
|
|
|
|
.account-indicator { |
|
|
|
background: rgba(255, 255, 255, 0.25); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.account-indicator { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
margin-top: 8px; |
|
|
|
padding: 4px 10px; |
|
|
|
background: rgba(255, 255, 255, 0.15); |
|
|
|
border-radius: 12px; |
|
|
|
transition: all 0.2s; |
|
|
|
max-width: 65px; |
|
|
|
|
|
|
|
.account-name { |
|
|
|
font-size: 11px; |
|
|
|
color: #fff; |
|
|
|
overflow: hidden; |
|
|
|
text-overflow: ellipsis; |
|
|
|
white-space: nowrap; |
|
|
|
margin-right: 3px; |
|
|
|
} |
|
|
|
|
|
|
|
.el-icon-arrow-down { |
|
|
|
font-size: 12px; |
|
|
|
color: rgba(255, 255, 255, 0.8); |
|
|
|
transition: transform 0.2s; |
|
|
|
|
|
|
|
&.active { |
|
|
|
transform: rotate(180deg); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.menu { |
|
|
|
@ -566,9 +716,8 @@ export default { |
|
|
|
.menu-item { |
|
|
|
position: relative; |
|
|
|
color: #eee; |
|
|
|
width: var(--width); |
|
|
|
height: 46px; |
|
|
|
width: 46px; |
|
|
|
height: 46px; |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
@ -576,7 +725,7 @@ export default { |
|
|
|
border-radius: 10px; |
|
|
|
|
|
|
|
.icon { |
|
|
|
font-size: var(--icon-font-size) |
|
|
|
font-size: var(--icon-font-size); |
|
|
|
} |
|
|
|
|
|
|
|
.unread-text { |
|
|
|
@ -606,7 +755,7 @@ export default { |
|
|
|
font-size: var(--icon-font-size); |
|
|
|
|
|
|
|
.icon { |
|
|
|
font-size: var(--icon-font-size) |
|
|
|
font-size: var(--icon-font-size); |
|
|
|
} |
|
|
|
|
|
|
|
&:hover { |
|
|
|
@ -623,4 +772,14 @@ export default { |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.menu-mask { |
|
|
|
position: fixed; |
|
|
|
top: 0; |
|
|
|
left: 0; |
|
|
|
right: 0; |
|
|
|
bottom: 0; |
|
|
|
z-index: 9998; |
|
|
|
background: transparent; |
|
|
|
} |
|
|
|
</style> |