You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
11 KiB
398 lines
11 KiB
<template> |
|
<div class="message-main"> |
|
<MessageList ref="messageList" @select-contact="handleSelectContact" /> |
|
<div class="message-right"> |
|
<MessageDisplay |
|
ref="messageDisplay" |
|
@clear-unread="handleClearUnread" |
|
@send-files="handleSendFiles" |
|
@group-setting="handleGroupSetting" |
|
@refresh-list="handleRefreshList" |
|
/> |
|
|
|
<MessageEditor |
|
ref="messageEditor" |
|
:group-info="groupInfo" |
|
@send-message="handleSendMessage" |
|
@update-message-status="handleUpdateMessageStatus" |
|
@font-size-change="handleFontSizeChange" |
|
@refresh-list="handleRefreshList" |
|
/> |
|
</div> |
|
|
|
<!-- 群设置弹窗 --> |
|
<GroupSetting |
|
v-if="groupInfo" |
|
ref="groupSettingRef" |
|
:chat="currentChat" |
|
:group-info="groupInfo" |
|
@quit-group="handleDismissGroup" |
|
@dismiss-group="handleDismissGroup" |
|
@refresh-list="handleRefreshList" |
|
/> |
|
</div> |
|
</template> |
|
|
|
<script> |
|
import { mapGetters, mapActions } from "vuex"; |
|
import MessageList from "./components/MessageList.vue"; |
|
import MessageDisplay from "./components/MessageDisplay.vue"; |
|
import GroupSetting from "./components/GroupSetting.vue"; |
|
import MessageEditor from "./components/MessageEditor.vue"; |
|
import { getMultiChatInfo, editMultiChatName } from "@/api/multichat"; |
|
import { MessageType, ContactsScene, MqttTopics } from "@/utils/constants"; |
|
|
|
export default { |
|
name: "MessageMain", |
|
|
|
components: { |
|
MessageList, |
|
MessageDisplay, |
|
GroupSetting, |
|
MessageEditor, |
|
}, |
|
|
|
data() { |
|
return { |
|
groupInfo: null, |
|
}; |
|
}, |
|
|
|
computed: { |
|
...mapGetters(["mqttMessages", "currentChat", "userInfo", "mqttConnected"]), |
|
}, |
|
|
|
watch: { |
|
mqttMessages: { |
|
deep: true, |
|
handler(newMessages, oldMessages) { |
|
const oldLen = oldMessages ? oldMessages.length : 0; |
|
const newLen = newMessages ? newMessages.length : 0; |
|
if (newLen > oldLen) { |
|
for (let i = oldLen; i < newLen; i++) { |
|
this.handleMqttMessage(newMessages[i]); |
|
} |
|
} |
|
}, |
|
}, |
|
}, |
|
|
|
mounted() { |
|
this.initMqttSubscriptions(); |
|
this.handleUrlParams(); |
|
}, |
|
|
|
beforeDestroy() { |
|
// 清理 |
|
}, |
|
|
|
methods: { |
|
...mapActions("mqtt", ["subscribe", "unsubscribe"]), |
|
...mapActions("message", ["setCurrentChat"]), |
|
|
|
// 处理URL参数,实现从通讯录发消息联动 |
|
handleUrlParams() { |
|
const { contactId, contactName, scene } = this.$route.query; |
|
if (contactId && scene) { |
|
const chat = { |
|
id: contactId, |
|
name: contactName || "未知联系人", |
|
scene: parseInt(scene), |
|
avatar: "", |
|
unread_count: 0, |
|
last_message: "", |
|
last_message_time: new Date().toISOString(), |
|
}; |
|
// 设置当前聊天 |
|
this.setCurrentChat(chat); |
|
// 如果是群聊,订阅MQTT主题 |
|
if (parseInt(scene) === ContactsScene.GROUP) { |
|
this.subscribe(MqttTopics.MULTI_CHAT + contactId); |
|
} |
|
// 清除URL参数 |
|
this.$router.replace({ query: {} }); |
|
} |
|
}, |
|
|
|
async initMqttSubscriptions() { |
|
// 订阅个人消息主题 |
|
if (this.userInfo?.id) { |
|
await this.subscribe(MqttTopics.PRIVATE_CHAT + this.userInfo.id); |
|
} |
|
}, |
|
|
|
handleMqttMessage(msg) { |
|
const type = msg.type || msg.message_type; |
|
switch (type) { |
|
case MessageType.READ: |
|
this.handleReadMessage(msg); |
|
break; |
|
case MessageType.TEXT: |
|
case MessageType.IMAGE: |
|
case MessageType.VIDEO: |
|
case MessageType.AUDIO: |
|
case MessageType.FILE: |
|
case MessageType.REPORT_SHARE: |
|
case MessageType.KNOWLEDGE_SHARE: |
|
case MessageType.CONSULTATION_MESSAGE_INVITE: |
|
this.handleBaseMessage(msg); |
|
break; |
|
case MessageType.MULTI_CHAT_INVITE: |
|
this.handleMultiChatInvite(msg); |
|
break; |
|
case MessageType.MULTI_CHAT_JOIN: |
|
this.handleMultiChatJoin(msg); |
|
break; |
|
case MessageType.MULTI_CHAT_EDIT: |
|
this.handleMultiChatEdit(msg); |
|
break; |
|
case MessageType.MULTI_CHAT_QUIT: |
|
this.handleMultiChatQuit(msg); |
|
break; |
|
case MessageType.MULTI_CHAT_DISMISS: |
|
this.handleMultiChatDismiss(msg); |
|
break; |
|
case MessageType.NOTIFY_CONTENT: |
|
this.handleNotifyContent(msg); |
|
break; |
|
default: |
|
console.log("未处理的MQTT消息类型:", type, msg); |
|
} |
|
}, |
|
|
|
handleReadMessage(msg) { |
|
// 更新消息已读状态 |
|
if (this.$refs.messageDisplay) { |
|
this.$refs.messageDisplay.updateMessageStatus(msg.message_id, { |
|
read: true, |
|
read_status: 1, |
|
}); |
|
} |
|
}, |
|
|
|
handleBaseMessage(msg) { |
|
const isSelf = msg.source_id === this.userInfo?.id; |
|
const scene = |
|
msg.scene || |
|
(msg.multi_chat_id ? ContactsScene.GROUP : ContactsScene.PRIVATE); |
|
const targetId = msg.target_id || msg.source_id; |
|
const contactId = isSelf ? targetId : msg.source_id; |
|
|
|
// 如果当前正在查看该会话,展示消息 |
|
if ( |
|
this.currentChat && |
|
this.currentChat.id === |
|
(scene === ContactsScene.GROUP ? msg.multi_chat_id : contactId) && |
|
this.currentChat.scene === scene |
|
) { |
|
if (this.$refs.messageDisplay) { |
|
this.$refs.messageDisplay.addChatMessage(msg); |
|
} |
|
// 标记已读 |
|
if (!isSelf) { |
|
this.handleClearUnread(this.currentChat.id, this.currentChat.scene); |
|
} |
|
} |
|
|
|
// 更新联系人列表最后消息 |
|
if (this.$refs.messageList) { |
|
this.$refs.messageList.updateContactLastMessage( |
|
scene === ContactsScene.GROUP ? msg.multi_chat_id : contactId, |
|
scene, |
|
msg, |
|
msg.timestamp || new Date().toISOString() |
|
); |
|
} |
|
}, |
|
|
|
async handleMultiChatInvite(msg) { |
|
const payload = msg.payload || {}; |
|
const multiChatId = payload.multi_chat_id || msg.multi_chat_id; |
|
if (!multiChatId) return; |
|
// 订阅群主题 |
|
await this.subscribe(MqttTopics.MULTI_CHAT + multiChatId); |
|
// 获取群详情 |
|
try { |
|
const res = await getMultiChatInfo({ multi_chat_id: multiChatId }); |
|
const group = res.data; |
|
if (group && this.$refs.messageList) { |
|
this.$refs.messageList.loadContacts(); |
|
} |
|
} catch (e) { |
|
console.error("获取群详情失败", e); |
|
} |
|
}, |
|
|
|
async handleMultiChatJoin(msg) { |
|
const multiChatId = msg.multi_chat_id || msg.payload?.multi_chat_id; |
|
if (!multiChatId) return; |
|
try { |
|
await getMultiChatInfo({ multi_chat_id: multiChatId }); |
|
if (this.$refs.messageList) { |
|
this.$refs.messageList.loadContacts(); |
|
} |
|
} catch (e) { |
|
console.error("获取群详情失败", e); |
|
} |
|
}, |
|
|
|
async handleMultiChatEdit(msg) { |
|
const multiChatId = msg.multi_chat_id || msg.payload?.multi_chat_id; |
|
if (!multiChatId) return; |
|
try { |
|
await getMultiChatInfo({ multi_chat_id: multiChatId }); |
|
if (this.$refs.messageList) { |
|
this.$refs.messageList.loadContacts(); |
|
} |
|
// 如果当前正在该群聊,刷新标题 |
|
if ( |
|
this.currentChat && |
|
this.currentChat.id === multiChatId && |
|
this.currentChat.scene === ContactsScene.GROUP |
|
) { |
|
this.$refs.messageList.loadContacts(); |
|
} |
|
} catch (e) { |
|
console.error("获取群详情失败", e); |
|
} |
|
}, |
|
|
|
handleMultiChatQuit(msg) { |
|
const payload = msg.payload || {}; |
|
const multiChatId = payload.multi_chat_id || msg.multi_chat_id; |
|
const quitUserId = payload.user_id || msg.source_id; |
|
if (!multiChatId) return; |
|
if (quitUserId === this.userInfo?.id) { |
|
// 自己退出,取消订阅并删除列表项 |
|
this.unsubscribe(MqttTopics.MULTI_CHAT + multiChatId); |
|
if (this.$refs.messageList) { |
|
this.$refs.messageList.removeContact( |
|
multiChatId, |
|
ContactsScene.GROUP |
|
); |
|
} |
|
} else { |
|
// 他人退出,更新列表 |
|
if (this.$refs.messageList) { |
|
this.$refs.messageList.loadContacts(); |
|
} |
|
} |
|
}, |
|
|
|
handleMultiChatDismiss(msg) { |
|
const multiChatId = msg.multi_chat_id || msg.payload?.multi_chat_id; |
|
if (!multiChatId) return; |
|
this.unsubscribe(MqttTopics.MULTI_CHAT + multiChatId); |
|
if (this.$refs.messageList) { |
|
this.$refs.messageList.removeContact(multiChatId, ContactsScene.GROUP); |
|
} |
|
}, |
|
// 群设置修改后刷新列表 |
|
handleRefreshList() { |
|
if (this.$refs.messageList) { |
|
this.$refs.messageList.refreshList({ keepCurrent: true }); |
|
} |
|
}, |
|
|
|
handleNotifyContent(msg) { |
|
// 系统通知处理 |
|
this.$notify({ |
|
title: "系统通知", |
|
message: msg.payload?.content || msg.content || "新通知", |
|
type: "info", |
|
duration: 5000, |
|
}); |
|
}, |
|
|
|
handleSelectContact(contact) { |
|
// 如果是群聊,订阅MQTT主题 |
|
if (contact.scene == ContactsScene.GROUP) { |
|
this.subscribe(MqttTopics.MULTI_CHAT + contact.id); |
|
// 保存群详情 |
|
getMultiChatInfo({ multi_chat_id: contact.id }).then((res) => { |
|
this.groupInfo = res.data || null; |
|
}); |
|
// 将群成员传递给编辑器 |
|
} else { |
|
this.groupInfo = null; |
|
} |
|
}, |
|
|
|
handleClearUnread(contactId, scene) { |
|
if (this.$refs.messageList) { |
|
this.$refs.messageList.clearUnread(contactId, scene); |
|
} |
|
}, |
|
|
|
handleSendMessage(message) { |
|
// 更新联系人列表 |
|
if (this.$refs.messageList) { |
|
this.$refs.messageList.updateContactLastMessage( |
|
message.target_id, |
|
message.scene, |
|
message, |
|
new Date().toISOString() |
|
); |
|
} |
|
if (this.$refs.messageDisplay) { |
|
this.$refs.messageDisplay.addChatMessage(message); |
|
} |
|
// 刷新列表 |
|
this.$refs.messageList.refreshList({ selectFirst: true }); |
|
}, |
|
|
|
handleUpdateMessageStatus(messageId, updates) { |
|
if (this.$refs.messageDisplay) { |
|
this.$refs.messageDisplay.updateMessageStatus(messageId, updates); |
|
} |
|
}, |
|
|
|
handleFontSizeChange(fontSize) { |
|
if (this.$refs.messageDisplay) { |
|
this.$refs.messageDisplay.setFontSize(fontSize); |
|
} |
|
}, |
|
|
|
handleSendFiles(files) { |
|
if (this.$refs.messageEditor) { |
|
files.forEach((file) => { |
|
const type = file.type.startsWith("image/") |
|
? "image" |
|
: file.type.startsWith("video/") |
|
? "video" |
|
: file.type.startsWith("audio/") |
|
? "audio" |
|
: "file"; |
|
this.$refs.messageEditor.sendFileMessage(file, type); |
|
}); |
|
} |
|
}, |
|
|
|
handleGroupSetting() { |
|
this.$refs.groupSettingRef.loadGroupInfo(); |
|
}, |
|
handleDismissGroup(chat) { |
|
// 解散群组逻辑 |
|
if (this.$refs.messageList) { |
|
// 刷新列表并打开第一个 |
|
this.$refs.messageList.refreshList({ selectFirst: true }); |
|
} |
|
}, |
|
}, |
|
}; |
|
</script> |
|
|
|
<style lang="scss" scoped> |
|
.message-main { |
|
display: flex; |
|
height: 100%; |
|
background: #fff; |
|
|
|
.message-right { |
|
flex: 1; |
|
display: flex; |
|
flex-direction: column; |
|
min-width: 0; |
|
} |
|
} |
|
</style>
|
|
|