import { computed, ref } from 'vue' import { useAuth } from './useAuth' import { useLocale } from '~/utils/locale' import type { Notification, NotificationSettings } from '~/types' export const useNotifications = () => { const { getAuthConfig, isAuthenticated } = useAuth() const { composableErrors } = useLocale() const action = (key: keyof typeof composableErrors.value.actions) => composableErrors.value.actions[key] const loginRequired = (key: keyof typeof composableErrors.value.actions) => composableErrors.value.loginRequired(action(key)) const failed = (key: keyof typeof composableErrors.value.actions) => composableErrors.value.failed(action(key)) const notifications = ref([]) const unreadCount = ref(0) const settings = ref(null) const loading = ref(false) const error = ref('') // 分页相关状态 const currentPage = ref(1) const pageSize = ref(10) const totalPages = ref(0) const totalCount = ref(0) const currentFilter = ref<'all' | 'unread'>('all') const currentSearch = ref('') const hasNextPage = computed(() => currentPage.value < totalPages.value) const hasPrevPage = computed(() => currentPage.value > 1) const isPaginationLoading = ref(false) let fetchSequence = 0 const fetchNotifications = async ( page?: number, limit?: number, filter: 'all' | 'unread' = currentFilter.value, search: string = currentSearch.value ) => { const requestId = ++fetchSequence if (!isAuthenticated.value) { notifications.value = [] unreadCount.value = 0 resetPagination() return } // 如果是分页请求(非初始加载),显示分页加载状态 if (page && page !== 1) { isPaginationLoading.value = true } else { loading.value = true } error.value = '' // 使用传入的参数或当前状态 const requestPage = page || currentPage.value const requestLimit = limit || pageSize.value const requestFilter = filter === 'unread' ? 'unread' : 'all' const requestSearch = search.trim().slice(0, 100) currentFilter.value = requestFilter currentSearch.value = requestSearch try { const authConfig = getAuthConfig() const data = await $fetch('/api/notifications', { query: { page: requestPage, limit: requestLimit, filter: requestFilter, search: requestSearch || undefined }, ...authConfig }) if (data && requestId === fetchSequence) { notifications.value = data.notifications || [] unreadCount.value = data.unreadCount || 0 // 更新分页信息 if (data.pagination) { currentPage.value = data.pagination.currentPage totalPages.value = data.pagination.totalPages totalCount.value = data.pagination.totalCount pageSize.value = data.pagination.limit } } } catch (err: any) { if (requestId !== fetchSequence) return const errorHandler = useErrorHandler() if (await errorHandler.checkAndHandleFetchError(err)) { return } console.error('获取通知错误:', err) notifications.value = [] unreadCount.value = 0 resetPagination() } finally { if (requestId === fetchSequence) { loading.value = false isPaginationLoading.value = false } } } // 重置分页状态 const resetPagination = () => { currentPage.value = 1 totalPages.value = 0 totalCount.value = 0 } // 分页导航方法 const goToPage = async (page: number) => { if (page >= 1 && page <= totalPages.value && page !== currentPage.value) { await fetchNotifications(page, pageSize.value) } } const nextPage = async () => { if (hasNextPage.value) { await goToPage(currentPage.value + 1) } } const prevPage = async () => { if (hasPrevPage.value) { await goToPage(currentPage.value - 1) } } const changePageSize = async (newSize: number) => { if (newSize !== pageSize.value) { pageSize.value = newSize // 重新计算当前页,确保不超出范围 const newTotalPages = Math.ceil(totalCount.value / newSize) const newCurrentPage = Math.min(currentPage.value, newTotalPages || 1) await fetchNotifications(newCurrentPage, newSize) } } const changeFilter = async (filter: 'all' | 'unread') => { if (filter === currentFilter.value) return currentPage.value = 1 await fetchNotifications(1, pageSize.value, filter) } const changeSearch = async (search: string) => { const normalizedSearch = search.trim().slice(0, 100) if (normalizedSearch === currentSearch.value) return currentPage.value = 1 await fetchNotifications(1, pageSize.value, currentFilter.value, normalizedSearch) } const fetchNotificationSettings = async () => { if (!isAuthenticated.value) { settings.value = null return } loading.value = true error.value = '' try { const authConfig = getAuthConfig() const data = await $fetch('/api/notifications/settings', { ...authConfig }) if (data) { settings.value = data } } catch (err: any) { const errorHandler = useErrorHandler() if (await errorHandler.checkAndHandleFetchError(err)) { return } console.error('获取通知设置错误:', err) settings.value = null } finally { loading.value = false } } const updateNotificationSettings = async (newSettings: Partial) => { if (!isAuthenticated.value) { error.value = loginRequired('updateNotificationSettings') return null } loading.value = true error.value = '' try { const authConfig = getAuthConfig() const data = await $fetch('/api/notifications/settings', { method: 'POST', body: newSettings, ...authConfig }) if (data) { settings.value = data return data } return null } catch (err: any) { const errorHandler = useErrorHandler() if (await errorHandler.checkAndHandleFetchError(err)) { return null } error.value = err.message || failed('updateNotificationSettings') console.error('更新通知设置错误:', err) return null } finally { loading.value = false } } // 标记通知为已读 const markAsRead = async (notificationId: number) => { if (!isAuthenticated.value) { error.value = loginRequired('markNotification') return null } loading.value = true error.value = '' try { const authConfig = getAuthConfig() const data = await $fetch(`/api/notifications/${notificationId}/read`, { method: 'POST', ...authConfig }) const index = notifications.value.findIndex((n: Notification) => n.id === notificationId) if (index !== -1) { notifications.value[index].read = true unreadCount.value = Math.max(0, unreadCount.value - 1) } if (currentFilter.value === 'unread') { const targetPage = notifications.value.length <= 1 && currentPage.value > 1 ? currentPage.value - 1 : currentPage.value await fetchNotifications(targetPage, pageSize.value) } return data } catch (err: any) { const errorHandler = useErrorHandler() if (await errorHandler.checkAndHandleFetchError(err)) { return null } error.value = err.message || failed('markNotification') console.error('标记通知错误:', err) return null } finally { loading.value = false } } // 标记所有通知为已读 const markAllAsRead = async () => { if (!isAuthenticated.value) { error.value = loginRequired('markAllNotifications') return null } loading.value = true error.value = '' try { const authConfig = getAuthConfig() const data = await $fetch('/api/notifications/read-all', { method: 'POST', ...authConfig }) if (data) { notifications.value.forEach((n) => (n.read = true)) unreadCount.value = 0 if (currentFilter.value === 'unread') { notifications.value = [] resetPagination() } return data } return null } catch (err: any) { const errorHandler = useErrorHandler() if (await errorHandler.checkAndHandleFetchError(err)) { return null } error.value = err.message || failed('markAllNotifications') console.error('标记所有通知错误:', err) return null } finally { loading.value = false } } // 删除通知 const deleteNotification = async (notificationId: number) => { if (!isAuthenticated.value) { error.value = loginRequired('deleteNotification') return null } loading.value = true error.value = '' try { const authConfig = getAuthConfig() await $fetch(`/api/notifications/${notificationId}`, { method: 'DELETE', ...authConfig }) // 更新本地通知列表 const index = notifications.value.findIndex((n: Notification) => n.id === notificationId) if (index !== -1) { if (!notifications.value[index].read) { unreadCount.value = Math.max(0, unreadCount.value - 1) } notifications.value.splice(index, 1) } // 删除后重新获取当前页数据,以补充后续通知 if (notifications.value.length === 0 && currentPage.value > 1) { // 如果当前页数据已被清空且不是第一页,则跳转到上一页 await goToPage(currentPage.value - 1) } else { // 否则重新加载当前页(如果有下一页数据会自动补充上来) await fetchNotifications(currentPage.value, pageSize.value) } return true } catch (err: any) { // 检查是否为401错误 const errorHandler = useErrorHandler() if (await errorHandler.checkAndHandleFetchError(err)) { return null } error.value = err.message || failed('deleteNotification') console.error('删除通知错误:', err) return null } finally { loading.value = false } } // 清空所有通知 const clearAllNotifications = async () => { if (!isAuthenticated.value) { error.value = loginRequired('clearNotifications') return null } loading.value = true error.value = '' try { const authConfig = getAuthConfig() await $fetch('/api/notifications/clear-all', { method: 'DELETE', ...authConfig }) // 清空本地通知列表 notifications.value = [] unreadCount.value = 0 resetPagination() return true } catch (err: any) { // 检查是否为401错误 const errorHandler = useErrorHandler() if (await errorHandler.checkAndHandleFetchError(err)) { return null } error.value = err.message || failed('clearNotifications') console.error('清空通知错误:', err) return null } finally { loading.value = false } } // 计算未读通知数量 const computedUnreadCount = computed(() => unreadCount.value) return { notifications, unreadCount: computedUnreadCount, settings, loading, error, // 分页相关状态 currentPage, pageSize, totalPages, totalCount, currentFilter, currentSearch, hasNextPage, hasPrevPage, isPaginationLoading, // 原有方法 fetchNotifications, fetchNotificationSettings, updateNotificationSettings, markAsRead, markAllAsRead, deleteNotification, clearAllNotifications, // 分页方法 goToPage, nextPage, prevPage, changePageSize, changeFilter, changeSearch, resetPagination } }