diff --git a/apps/web-antd/src/api/core/im.ts b/apps/web-antd/src/api/core/im.ts index 04cfe5d..fb453fc 100644 --- a/apps/web-antd/src/api/core/im.ts +++ b/apps/web-antd/src/api/core/im.ts @@ -1,6 +1,8 @@ import { RequestClient } from '@vben/request'; import { useAccessStore } from '@vben/stores'; +import { requestClient } from '#/api/request'; + const imRequestClient = new RequestClient({ baseURL: 'http://localhost:5212/api', }); @@ -29,14 +31,14 @@ export namespace ImApi { export interface Conversation { id: string; type: number; - lastMessageContent: string | null; - lastMessageAt: string | null; + lastMessageContent: null | string; + lastMessageAt: null | string; unreadCount: number; - groupName: string | null; - groupAvatar: string | null; - otherUserId: string | null; - otherUserName: string | null; - otherUserAvatar: string | null; + groupName: null | string; + groupAvatar: null | string; + otherUserId: null | string; + otherUserName: null | string; + otherUserAvatar: null | string; } export interface Message { @@ -44,22 +46,22 @@ export namespace ImApi { conversationId: string; senderId: string; senderName: string; - senderAvatar: string | null; + senderAvatar: null | string; contentType: number; content: string; metadata: MessageMetadata | null; - replyToId: string | null; + replyToId: null | string; createdAt: string; } export interface MessageMetadata { - fileName?: string | null; - fileSize?: number | null; - fileUrl?: string | null; - imageUrl?: string | null; - imageWidth?: number | null; - imageHeight?: number | null; - emojiCode?: string | null; + fileName?: null | string; + fileSize?: null | number; + fileUrl?: null | string; + imageUrl?: null | string; + imageWidth?: null | number; + imageHeight?: null | number; + emojiCode?: null | string; } export interface MessageHistory { @@ -76,8 +78,8 @@ export namespace ImApi { id: string; conversationId: string; name: string; - avatar: string | null; - description: string | null; + avatar: null | string; + description: null | string; ownerId: string; memberCount: number; createdAt: string; @@ -86,10 +88,19 @@ export namespace ImApi { export interface GroupMember { userId: string; userName: string; - avatar: string | null; + avatar: null | string; role: number; joinedAt: string; } + + export interface User { + id: string; + username: string; + email: string; + isActive: boolean; + createdAt: string; + roles: string[]; + } } export async function getConversationsApi() { @@ -114,17 +125,17 @@ export async function getUnreadCountApi() { } export async function createGroupApi(data: { - name: string; avatar?: string; description?: string; memberUserIds: string[]; + name: string; }) { return imRequestClient.post('/groups', data); } export async function updateGroupApi( groupId: string, - data: { name?: string; avatar?: string; description?: string }, + data: { avatar?: string; description?: string; name?: string; }, ) { return imRequestClient.put(`/groups/${groupId}`, data); } @@ -144,3 +155,17 @@ export async function removeGroupMemberApi(groupId: string, userId: string) { export async function getGroupMembersApi(groupId: string) { return imRequestClient.get(`/groups/${groupId}/members`); } + +// 用户搜索 — 直连 rag-backend +export async function searchUsersApi(search?: string) { + const params: Record = {}; + if (search) params.search = search; + return requestClient.get('/users', { params }); +} + +// 创建或获取私聊会话 — IM 后端 +export async function createPrivateConversationApi(targetUserId: string) { + return imRequestClient.post('/conversations/private', { + targetUserId, + }); +} diff --git a/apps/web-antd/src/store/im.ts b/apps/web-antd/src/store/im.ts index 8c4fc59..b40317e 100644 --- a/apps/web-antd/src/store/im.ts +++ b/apps/web-antd/src/store/im.ts @@ -1,23 +1,30 @@ -import { ref, computed } from 'vue'; -import { defineStore } from 'pinia'; - import type { ImApi } from '#/api/core/im'; +import { computed, ref } from 'vue'; + +import { useUserStore } from '@vben/stores'; + +import { defineStore } from 'pinia'; + import { + createPrivateConversationApi, getConversationsApi, getMessageHistoryApi, getUnreadCountApi, + searchUsersApi, } from '#/api/core/im'; import { signalRService } from '#/api/core/signalr'; export const useImStore = defineStore('im', () => { const conversations = ref([]); - const activeConversationId = ref(null); + const activeConversationId = ref(null); const messages = ref>(new Map()); const unreadCounts = ref>(new Map()); const onlineUsers = ref>(new Set()); const connected = ref(false); const typingUsers = ref>>(new Map()); + const contacts = ref([]); + const contactsLoading = ref(false); const totalUnread = computed(() => { let total = 0; @@ -50,7 +57,7 @@ export const useImStore = defineStore('im', () => { if (conv) { conv.lastMessageContent = message.contentType === 0 - ? message.content.substring(0, 50) + ? message.content.slice(0, 50) : `[${['文本', '图片', '文件', '系统', '表情'][message.contentType] || '消息'}]`; conv.lastMessageAt = message.createdAt; } @@ -145,10 +152,31 @@ export const useImStore = defineStore('im', () => { await signalRService.typing(conversationId); } - function setActiveConversation(conversationId: string | null) { + function setActiveConversation(conversationId: null | string) { activeConversationId.value = conversationId; } + async function searchContacts(query?: string) { + contactsLoading.value = true; + try { + const userStore = useUserStore(); + const data = await searchUsersApi(query); + contacts.value = data.filter( + (u) => u.id !== userStore.userInfo?.userId, + ); + } finally { + contactsLoading.value = false; + } + } + + async function startPrivateChat(targetUserId: string) { + const conversation = await createPrivateConversationApi(targetUserId); + await loadConversations(); + setActiveConversation(conversation.id); + await loadMessages(conversation.id); + return conversation; + } + return { // State conversations, @@ -158,6 +186,8 @@ export const useImStore = defineStore('im', () => { onlineUsers, connected, typingUsers, + contacts, + contactsLoading, // Computed totalUnread, activeMessages, @@ -172,5 +202,7 @@ export const useImStore = defineStore('im', () => { markAsRead, typing, setActiveConversation, + searchContacts, + startPrivateChat, }; }); diff --git a/apps/web-antd/src/views/im/index.vue b/apps/web-antd/src/views/im/index.vue index a953b73..26627c9 100644 --- a/apps/web-antd/src/views/im/index.vue +++ b/apps/web-antd/src/views/im/index.vue @@ -1,4 +1,6 @@