From cc139e14e2c54669ffee4e6de1646723a0d4db64 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Tue, 28 Apr 2026 22:32:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(chat):=20=E6=B7=BB=E5=8A=A0=E8=81=8A?= =?UTF-8?q?=E5=A4=A9=E7=95=8C=E9=9D=A2=E8=A7=86=E5=9B=BE=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=92=8C=E8=81=94=E7=B3=BB=E4=BA=BA=E7=AE=A1=E7=90=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现 ChatViewModel 处理消息收发、历史记录加载和状态管理 - 添加消息搜索、草稿保存、引用回复等功能 - 实现多媒体附件发送包括图片、视频、音频和文件 - 添加群组提及用户功能和消息撤回机制 - 实现联系人管理功能包括好友搜索、添加、删除和黑名单管理 - 添加好友请求处理和实时消息监听 - 实现会话列表管理包含未读消息统计和实时更新 - 集成 IM SDK 的连接状态管理和事件监听 - 添加消息状态跟踪和超时处理机制 - 实现数据缓存机制优化用户体验 --- src/im/ImClient.ts | 9 ++++++++- src/im/api.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++++ src/im/useIm.ts | 25 +++++++++++++++++++++++- src/index.ts | 4 ++++ src/types/index.ts | 1 + 5 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/im/ImClient.ts b/src/im/ImClient.ts index b88e2e4..796276f 100644 --- a/src/im/ImClient.ts +++ b/src/im/ImClient.ts @@ -49,7 +49,14 @@ export class ImClient { try { const frame = JSON.parse(event.data as string) if (frame.type === 'MESSAGE') { - this.emit('message', this.normalizeMessage(frame.payload as ImMessage)) + const message = this.normalizeMessage(frame.payload as ImMessage) + if (message.status === 'READ') { + this.emit('read', message) + } + if (message.revoked || message.status === 'REVOKED' || message.msgType === 'REVOKED') { + this.emit('revoke', { msgId: message.id, operatorId: message.fromId ?? message.fromUserId }) + } + this.emit('message', message) } else if (frame.type === 'REVOKE') { this.emit('revoke', frame.payload as { msgId: string; operatorId: string }) } diff --git a/src/im/api.ts b/src/im/api.ts index ca9776c..de938e8 100644 --- a/src/im/api.ts +++ b/src/im/api.ts @@ -48,6 +48,54 @@ export function fetchGroupHistory(groupId: string, query: HistoryQuery = {}): Pr ) } +export async function locateHistoryPage( + toId: string, + messageId: string, + pageSize = 20, + maxPages = 20, +): Promise { + for (let page = 0; page < Math.max(maxPages, 1); page += 1) { + const result = await fetchHistory(toId, { page, size: pageSize }) + if (result.content.some((item) => item.id === messageId)) { + return result.content + } + if (result.content.length < pageSize) return null + } + return null +} + +export async function locateGroupHistoryPage( + groupId: string, + messageId: string, + pageSize = 20, + maxPages = 20, +): Promise { + for (let page = 0; page < Math.max(maxPages, 1); page += 1) { + const result = await fetchGroupHistory(groupId, { page, size: pageSize }) + if (result.content.some((item) => item.id === messageId)) { + return result.content + } + if (result.content.length < pageSize) return null + } + return null +} + +export function editMessage(messageId: string, content: string): Promise { + return http.put( + `/api/im/messages/${encodeURIComponent(messageId)}`, + { content }, + appQuery(), + ) +} + +export function revokeMessage(messageId: string): Promise { + return http.post( + `/api/im/messages/${encodeURIComponent(messageId)}/revoke`, + undefined, + appQuery(), + ) +} + export function markRead(targetId: string, chatType: ChatType = 'SINGLE'): Promise { return http.put( `/api/im/conversations/${encodeURIComponent(targetId)}/read`, diff --git a/src/im/useIm.ts b/src/im/useIm.ts index 60b6f64..2862d53 100644 --- a/src/im/useIm.ts +++ b/src/im/useIm.ts @@ -7,6 +7,10 @@ import { getGroupInfo, fetchGroupHistory, fetchHistory, + editMessage, + locateGroupHistoryPage, + locateHistoryPage, + revokeMessage, listFriendRequests, listFriends, listGroupJoinRequests, @@ -64,6 +68,14 @@ export function useIm() { return fetchGroupHistory(groupId, query) } + function jumpToMessagePage(toId: string, messageId: string, pageSize = 20, maxPages = 20) { + return locateHistoryPage(toId, messageId, pageSize, maxPages) + } + + function jumpToGroupMessagePage(groupId: string, messageId: string, pageSize = 20, maxPages = 20) { + return locateGroupHistoryPage(groupId, messageId, pageSize, maxPages) + } + function connect() { const im = new ImClient() im.on('connected', () => { @@ -75,6 +87,10 @@ export function useIm() { upsertMessage(msg) void refreshConversations().catch(() => {}) }) + im.on('read', (msg) => { + upsertMessage(msg) + void refreshConversations().catch(() => {}) + }) im.on('error', (e) => { error.value = e }) im.connect() client.value = im @@ -89,7 +105,11 @@ export function useIm() { } function revoke(msgId: string) { - client.value?.revoke(msgId) + return revokeMessage(msgId) + } + + function edit(msgId: string, content: string) { + return editMessage(msgId, content) } function disconnect() { @@ -168,6 +188,7 @@ export function useIm() { connect, send, revoke, + edit, disconnect, messages, conversations, @@ -176,6 +197,8 @@ export function useIm() { refreshConversations, loadHistory, loadGroupHistory, + jumpToMessagePage, + jumpToGroupMessagePage, setConversationRead, setConversationPinnedState, setConversationMutedState, diff --git a/src/index.ts b/src/index.ts index 78ab265..97072c2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,10 @@ export { getGroupInfo, fetchGroupHistory, fetchHistory, + editMessage, + locateGroupHistoryPage, + locateHistoryPage, + revokeMessage, listFriendRequests, listFriends, listGroupJoinRequests, diff --git a/src/types/index.ts b/src/types/index.ts index f517088..09440d4 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -130,6 +130,7 @@ export interface SendMessageParams { export interface ImEventMap { message: (msg: ImMessage) => void + read: (msg: ImMessage) => void revoke: (data: { msgId: string; operatorId: string }) => void connected: () => void disconnected: (code: number, reason: string) => void