feat(mobile): 接入移动端订单与消息中心

This commit is contained in:
yml2213
2026-10-30 12:46:01 +08:00
parent 17b985a4a4
commit e8a20ebea7
17 changed files with 2153 additions and 30 deletions
+131
View File
@@ -0,0 +1,131 @@
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
import { apiClient, unwrapData } from '@/shared/api/client'
import type { ApiResponse } from '@/shared/api/types'
import type { AppNotification, ChatConversation, ChatMessage, ChatPage } from '@/features/messages/types'
const PAGE_SIZE = 20
export function fetchConversations(page = 1) {
return unwrapData<Partial<ChatPage<ChatConversation>>>(
apiClient.get<ApiResponse<Partial<ChatPage<ChatConversation>>>>('/chats', {
params: { page, page_size: PAGE_SIZE },
})
).then(data => ({
items: data.items ?? [],
total: data.total ?? 0,
page: data.page ?? page,
page_size: data.page_size ?? PAGE_SIZE,
}))
}
export function useConversations(enabled = true) {
return useInfiniteQuery({
queryKey: ['chat-conversations'],
queryFn: ({ pageParam }) => fetchConversations(pageParam),
initialPageParam: 1,
getNextPageParam: last =>
last.page * last.page_size < last.total ? last.page + 1 : undefined,
enabled,
})
}
export function fetchNotifications(page = 1) {
return unwrapData<Partial<ChatPage<AppNotification>>>(
apiClient.get<ApiResponse<Partial<ChatPage<AppNotification>>>>('/notifications', {
params: { page, page_size: PAGE_SIZE },
})
).then(data => ({
items: data.items ?? [],
total: data.total ?? 0,
page: data.page ?? page,
page_size: data.page_size ?? PAGE_SIZE,
}))
}
export function useNotifications(enabled = true) {
return useInfiniteQuery({
queryKey: ['notifications'],
queryFn: ({ pageParam }) => fetchNotifications(pageParam),
initialPageParam: 1,
getNextPageParam: last =>
last.page * last.page_size < last.total ? last.page + 1 : undefined,
enabled,
})
}
export function useConversation(id?: string, enabled = true) {
return useQuery({
queryKey: ['chat-conversation', id],
queryFn: () =>
unwrapData<ChatConversation>(apiClient.get<ApiResponse<ChatConversation>>(`/chats/${id}`)),
enabled: Boolean(id) && enabled,
})
}
export function fetchChatMessages(id: number | string, page = 1) {
return unwrapData<Partial<ChatPage<ChatMessage>>>(
apiClient.get<ApiResponse<Partial<ChatPage<ChatMessage>>>>(`/chats/${id}/messages`, {
params: { page, page_size: PAGE_SIZE },
})
).then(data => ({
items: data.items ?? [],
total: data.total ?? 0,
page: data.page ?? page,
page_size: data.page_size ?? PAGE_SIZE,
}))
}
export function useChatMessages(id?: string, enabled = true) {
return useInfiniteQuery({
queryKey: ['chat-messages', id],
queryFn: ({ pageParam }) => fetchChatMessages(id as string, pageParam),
initialPageParam: 1,
getNextPageParam: last =>
last.page * last.page_size < last.total ? last.page + 1 : undefined,
enabled: Boolean(id) && enabled,
refetchInterval: 6000,
})
}
export async function sendChatMessage(id: number | string, content: string) {
const { data } = await apiClient.post<ApiResponse<ChatMessage>>(`/chats/${id}/messages`, {
content,
})
return data.data
}
export async function markConversationRead(id: number | string) {
await apiClient.post(`/chats/${id}/read`)
}
export async function markNotificationRead(id: number | string) {
await apiClient.post(`/notifications/${id}/read`)
}
export async function markAllNotificationsRead() {
await apiClient.put('/notifications/read-all')
}
export async function ensureSupportConversation() {
return unwrapData<ChatConversation>(
apiClient.post<ApiResponse<ChatConversation>>('/chats/support', { scene: 'general' })
)
}
export function useChatUnreadCount(enabled = true) {
return useQuery({
queryKey: ['chat-unread-count'],
queryFn: () =>
unwrapData<{ unread_count: number }>(
apiClient.get<ApiResponse<{ unread_count: number }>>(
'/chats/unread-count',
{ skipErrorHandler: true } as never
)
)
.then(data => data.unread_count ?? 0)
.catch(() => 0),
enabled,
refetchInterval: 20_000,
})
}
+67
View File
@@ -0,0 +1,67 @@
export interface ChatParticipant {
id: number
participant_type: string
participant_id: number
role: string
display_name: string
avatar_url: string
last_read_at?: string | null
joined_at: string
}
export interface ChatConversation {
id: number
order_id?: number | null
listing_id?: number | null
latest_order_id?: number | null
latest_order_no?: string
latest_order_status?: string
type: string
support_scene?: string
title: string
status: string
role: string
participants?: ChatParticipant[]
last_message_id?: number | null
last_message_preview: string
last_message_at?: string | null
last_sender_type?: string
unread_count: number
needs_reply: boolean
created_at: string
updated_at: string
}
export interface ChatPage<T> {
items: T[]
total: number
page: number
page_size: number
}
export interface ChatMessage {
id: number
conversation_id: number
sender_type: string
sender_id: number
sender_role: string
sender_name: string
sender_avatar: string
is_self: boolean
is_read: boolean
content_type: string
content: string
attachment_urls: string[]
created_at: string
}
export interface AppNotification {
id: number
type: string
title: string
content: string
biz_type: string
biz_id?: number | null
read_at?: string | null
created_at: string
}