feat(im): 添加即时消息SDK核心功能实现
- 实现了聊天消息发送功能,支持文本、图片、视频、音频、文件等多种消息类型 - 集成了文件上传下载功能,支持多媒体文件的传输和管理 - 添加了群组管理功能,包括创建群组、成员管理、权限控制等操作 - 实现了好友系统,支持好友添加、删除、分组等功能 - 集成了黑名单管理,提供用户屏蔽和解除屏蔽功能 - 添加了会话管理功能,支持对话列表、未读消息统计等 - 实现了历史消息查询和搜索功能 - 添加了实时连接状态管理和自动重连机制
这个提交包含在:
父节点
91aa1dbbea
当前提交
7a5e79d419
@ -42,6 +42,22 @@ async function request<T>(
|
|||||||
return json.data
|
return json.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function uploadFile(file: File): Promise<{ url: string; thumbnailUrl?: string; hash: string; size: number; originalName: string; mimeType: string; ext: string }> {
|
||||||
|
const token = _tokenGetter()
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
|
||||||
|
const res = await fetch(`${_baseUrl}/api/file/upload`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
|
|
||||||
|
const json: ApiResponse<unknown> = await res.json()
|
||||||
|
if (json.code !== 200) throw new Error(json.message)
|
||||||
|
return json.data as { url: string; thumbnailUrl?: string; hash: string; size: number; originalName: string; mimeType: string; ext: string }
|
||||||
|
}
|
||||||
|
|
||||||
export const http = {
|
export const http = {
|
||||||
get: <T>(path: string, query?: Record<string, QueryValue>) => request<T>('GET', path, undefined, query),
|
get: <T>(path: string, query?: Record<string, QueryValue>) => request<T>('GET', path, undefined, query),
|
||||||
post: <T>(path: string, body?: unknown, query?: Record<string, QueryValue>) => request<T>('POST', path, body, query),
|
post: <T>(path: string, body?: unknown, query?: Record<string, QueryValue>) => request<T>('POST', path, body, query),
|
||||||
|
|||||||
@ -140,6 +140,7 @@ export class ImClient {
|
|||||||
// Don't return early — also emit as message for consistency with old behavior
|
// Don't return early — also emit as message for consistency with old behavior
|
||||||
}
|
}
|
||||||
this.emit('message', message)
|
this.emit('message', message)
|
||||||
|
this.emit('conversations')
|
||||||
} catch {
|
} catch {
|
||||||
// ignore malformed frames
|
// ignore malformed frames
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { getConfig } from '../core/sdk'
|
import { getConfig } from '../core/sdk'
|
||||||
import { http } from '../core/http'
|
import { http, uploadFile } from '../core/http'
|
||||||
import type {
|
import type {
|
||||||
BlacklistCheckResult,
|
BlacklistCheckResult,
|
||||||
BlacklistEntry,
|
BlacklistEntry,
|
||||||
@ -39,6 +39,48 @@ export function sendMessage(params: { toId: string; chatType: ChatType; msgType:
|
|||||||
return http.post<ImMessage>('/api/im/messages/send', params, appQuery())
|
return http.post<ImMessage>('/api/im/messages/send', params, appQuery())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function sendImageMessage(toId: string, chatType: ChatType, file: File, width?: number, height?: number): Promise<ImMessage> {
|
||||||
|
const result = await uploadFile(file)
|
||||||
|
const content = JSON.stringify({
|
||||||
|
url: result.url,
|
||||||
|
...(width !== undefined ? { width } : {}),
|
||||||
|
...(height !== undefined ? { height } : {}),
|
||||||
|
...(result.thumbnailUrl ? { thumbnailUrl: result.thumbnailUrl } : {}),
|
||||||
|
})
|
||||||
|
return sendMessage({ toId, chatType, msgType: 'IMAGE', content })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendVideoMessage(toId: string, chatType: ChatType, file: File, width?: number, height?: number, duration?: number): Promise<ImMessage> {
|
||||||
|
const result = await uploadFile(file)
|
||||||
|
const content = JSON.stringify({
|
||||||
|
url: result.url,
|
||||||
|
...(width !== undefined ? { width } : {}),
|
||||||
|
...(height !== undefined ? { height } : {}),
|
||||||
|
...(duration !== undefined ? { duration } : {}),
|
||||||
|
...(result.thumbnailUrl ? { thumbnailUrl: result.thumbnailUrl } : {}),
|
||||||
|
})
|
||||||
|
return sendMessage({ toId, chatType, msgType: 'VIDEO', content })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendFileMessage(toId: string, chatType: ChatType, file: File): Promise<ImMessage> {
|
||||||
|
const result = await uploadFile(file)
|
||||||
|
const content = JSON.stringify({
|
||||||
|
url: result.url,
|
||||||
|
name: result.originalName,
|
||||||
|
size: result.size,
|
||||||
|
})
|
||||||
|
return sendMessage({ toId, chatType, msgType: 'FILE', content })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendAudioMessage(toId: string, chatType: ChatType, file: File, duration?: number): Promise<ImMessage> {
|
||||||
|
const result = await uploadFile(file)
|
||||||
|
const content = JSON.stringify({
|
||||||
|
url: result.url,
|
||||||
|
...(duration !== undefined ? { duration } : {}),
|
||||||
|
})
|
||||||
|
return sendMessage({ toId, chatType, msgType: 'AUDIO', content })
|
||||||
|
}
|
||||||
|
|
||||||
export function createGroup(params: { name: string; memberIds: string[]; groupType?: string; avatar?: string; announcement?: string }): Promise<ImGroup> {
|
export function createGroup(params: { name: string; memberIds: string[]; groupType?: string; avatar?: string; announcement?: string }): Promise<ImGroup> {
|
||||||
return http.post<ImGroup>('/api/im/groups', params, appQuery())
|
return http.post<ImGroup>('/api/im/groups', params, appQuery())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -174,6 +174,9 @@ export function useIm(): UseImReturn {
|
|||||||
markMessageRevoked(msgId)
|
markMessageRevoked(msgId)
|
||||||
void refreshConversations().catch(() => {})
|
void refreshConversations().catch(() => {})
|
||||||
})
|
})
|
||||||
|
im.on('conversations', () => {
|
||||||
|
void refreshConversations().catch(() => {})
|
||||||
|
})
|
||||||
im.on('error', (e) => { error.value = e })
|
im.on('error', (e) => { error.value = e })
|
||||||
im.connect()
|
im.connect()
|
||||||
client.value = im
|
client.value = im
|
||||||
|
|||||||
@ -41,6 +41,10 @@ export {
|
|||||||
sendFriendRequest,
|
sendFriendRequest,
|
||||||
sendGroupJoinRequest,
|
sendGroupJoinRequest,
|
||||||
sendMessage,
|
sendMessage,
|
||||||
|
sendImageMessage,
|
||||||
|
sendVideoMessage,
|
||||||
|
sendFileMessage,
|
||||||
|
sendAudioMessage,
|
||||||
setConversationGroup,
|
setConversationGroup,
|
||||||
setConversationHidden,
|
setConversationHidden,
|
||||||
setConversationMuted,
|
setConversationMuted,
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
export interface SDKConfig {
|
export interface SDKConfig {
|
||||||
appKey: string
|
appKey: string
|
||||||
appSecret: string
|
|
||||||
debug?: boolean
|
debug?: boolean
|
||||||
baseUrl?: string
|
baseUrl?: string
|
||||||
wsUrl?: string
|
wsUrl?: string
|
||||||
@ -170,6 +169,7 @@ export interface ImEventMap {
|
|||||||
connected: () => void
|
connected: () => void
|
||||||
disconnected: (code: number, reason: string) => void
|
disconnected: (code: number, reason: string) => void
|
||||||
error: (err: Event) => void
|
error: (err: Event) => void
|
||||||
|
conversations: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiResponse<T> {
|
export interface ApiResponse<T> {
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户