feat(chat): 添加聊天界面视图模型和联系人管理功能

- 实现 ChatViewModel 处理消息收发、历史记录加载和状态管理
- 添加消息搜索、草稿保存、引用回复等功能
- 实现多媒体附件发送包括图片、视频、音频和文件
- 添加群组提及用户功能和消息撤回机制
- 实现联系人管理功能包括好友搜索、添加、删除和黑名单管理
- 添加好友请求处理和实时消息监听
- 实现会话列表管理包含未读消息统计和实时更新
- 集成 IM SDK 的连接状态管理和事件监听
- 添加消息状态跟踪和超时处理机制
- 实现数据缓存机制优化用户体验
这个提交包含在:
XuqmGroup 2026-04-28 22:32:21 +08:00
父节点 d3eb86ae8c
当前提交 cc139e14e2
共有 5 个文件被更改,包括 85 次插入2 次删除

查看文件

@ -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 })
}

查看文件

@ -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<ImMessage[] | null> {
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<ImMessage[] | null> {
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<ImMessage> {
return http.put<ImMessage>(
`/api/im/messages/${encodeURIComponent(messageId)}`,
{ content },
appQuery(),
)
}
export function revokeMessage(messageId: string): Promise<ImMessage> {
return http.post<ImMessage>(
`/api/im/messages/${encodeURIComponent(messageId)}/revoke`,
undefined,
appQuery(),
)
}
export function markRead(targetId: string, chatType: ChatType = 'SINGLE'): Promise<void> {
return http.put<void>(
`/api/im/conversations/${encodeURIComponent(targetId)}/read`,

查看文件

@ -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,

查看文件

@ -8,6 +8,10 @@ export {
getGroupInfo,
fetchGroupHistory,
fetchHistory,
editMessage,
locateGroupHistoryPage,
locateHistoryPage,
revokeMessage,
listFriendRequests,
listFriends,
listGroupJoinRequests,

查看文件

@ -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