diff --git a/public/nginx.conf b/public/nginx.conf index a1deac19..886f0681 100644 --- a/public/nginx.conf +++ b/public/nginx.conf @@ -49,7 +49,7 @@ http { root html; } - location ~ ^/api/v1/system/(message|progress/) { + location ~ ^/api/v1/(system/(message|progress/|logging)|search/.*/stream$) { # SSE MIME类型设置 default_type text/event-stream; diff --git a/src/components/cards/DownloaderCard.vue b/src/components/cards/DownloaderCard.vue index bae2ed6d..3e170913 100644 --- a/src/components/cards/DownloaderCard.vue +++ b/src/components/cards/DownloaderCard.vue @@ -9,14 +9,14 @@ import { cloneDeep } from 'lodash-es' import { useI18n } from 'vue-i18n' import { downloaderDict, storageAttributes } from '@/api/constants' import { useDisplay } from 'vuetify' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' // 显示器宽度 const display = useDisplay() // 获取i18n实例 const { t } = useI18n() -const { useConditionalDataRefresh } = useBackgroundOptimization() +const { useConditionalDataRefresh } = useBackground() // 定义输入 const props = defineProps({ diff --git a/src/components/dialog/ReorganizeDialog.vue b/src/components/dialog/ReorganizeDialog.vue index 06d643d0..38e4949e 100644 --- a/src/components/dialog/ReorganizeDialog.vue +++ b/src/components/dialog/ReorganizeDialog.vue @@ -14,7 +14,7 @@ import { TransferDirectoryConf, TransferForm, } from '@/api/types' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' import MediaIdSelector from '../misc/MediaIdSelector.vue' import ProgressDialog from './ProgressDialog.vue' import { useI18n } from 'vue-i18n' @@ -24,7 +24,7 @@ import { useGlobalSettingsStore } from '@/stores' // 国际化 const { t } = useI18n() -const { useProgressSSE } = useBackgroundOptimization() +const { useProgressSSE } = useBackground() // 显示器宽度 const display = useDisplay() diff --git a/src/components/dialog/TransferQueueDialog.vue b/src/components/dialog/TransferQueueDialog.vue index d59e66e3..2ef0d639 100644 --- a/src/components/dialog/TransferQueueDialog.vue +++ b/src/components/dialog/TransferQueueDialog.vue @@ -5,7 +5,7 @@ import api from '@/api' import { FileItem, TransferQueue } from '@/api/types' import { useDisplay } from 'vuetify' import { useI18n } from 'vue-i18n' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' import CryptoJS from 'crypto-js' type TransferTask = TransferQueue['tasks'][number] @@ -20,7 +20,7 @@ interface MediaTaskGroup { // 多语言支持 const { t } = useI18n() -const { useProgressSSE } = useBackgroundOptimization() +const { useProgressSSE } = useBackground() // 显示器宽度 const display = useDisplay() diff --git a/src/components/filebrowser/FileList.vue b/src/components/filebrowser/FileList.vue index 9d7df57e..1313fa31 100644 --- a/src/components/filebrowser/FileList.vue +++ b/src/components/filebrowser/FileList.vue @@ -11,14 +11,14 @@ import ProgressDialog from '../dialog/ProgressDialog.vue' import { useDisplay } from 'vuetify' import MediaInfoDialog from '../dialog/MediaInfoDialog.vue' import { useI18n } from 'vue-i18n' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' import { usePWA } from '@/composables/usePWA' import { useAvailableHeight } from '@/composables/useAvailableHeight' import { useKeepAliveRefresh } from '@/composables/useKeepAliveRefresh' // 国际化 const { t } = useI18n() -const { useProgressSSE } = useBackgroundOptimization() +const { useProgressSSE } = useBackground() // 显示器宽度 const display = useDisplay() @@ -652,7 +652,7 @@ function handleProgressMessage(event: MessageEvent) { } } -// 使用优化的进度SSE连接 +// 使用进度SSE连接 const progressSSE = useProgressSSE( `${import.meta.env.VITE_API_BASE_URL}system/progress/batchrename`, handleProgressMessage, diff --git a/src/composables/useBackgroundOptimization.ts b/src/composables/useBackground.ts similarity index 83% rename from src/composables/useBackgroundOptimization.ts rename to src/composables/useBackground.ts index 90198b63..eb1dcf81 100644 --- a/src/composables/useBackgroundOptimization.ts +++ b/src/composables/useBackground.ts @@ -1,14 +1,18 @@ -import { onMounted, onUnmounted, ref, type Ref } from 'vue' -import { sseManagerSingleton } from '@/utils/sseManager' +import { getCurrentInstance, onMounted, onUnmounted, ref, type Ref } from 'vue' +import { sseManagerSingleton, type SSEManagerOptions } from '@/utils/sseManager' import { addBackgroundTimer, removeBackgroundTimer } from '@/utils/backgroundManager' +type UseSSEOptions = Partial & { + connectDelay?: number +} + /** - * 后台优化组合函数 - * 统一管理SSE连接和定时器,优化iOS后台性能 + * 后台任务组合函数 + * 统一管理SSE连接和定时器,减少后台常驻活动。 */ -export function useBackgroundOptimization() { +export function useBackground() { /** - * 使用优化的SSE连接 + * 使用SSE连接 * @param url SSE连接地址 * @param messageHandler 消息处理函数 * @param listenerId 监听器ID(用于区分不同的监听器) @@ -18,24 +22,30 @@ export function useBackgroundOptimization() { url: string, messageHandler: (event: MessageEvent) => void, listenerId: string, - options?: { - backgroundCloseDelay?: number - reconnectDelay?: number - maxReconnectAttempts?: number - connectDelay?: number // 新增:连接延迟 - }, + options?: UseSSEOptions, ) => { // 使用独立的SSE管理器,确保每个监听器都有独立的连接 const manager = sseManagerSingleton.getIndependentManager(url, listenerId, options) const isConnected = ref(false) let connectTimer: ReturnType | null = null + let isClosed = false + const statusListenerId = `${listenerId}:status` + + manager.addStatusListener(statusListenerId, status => { + isConnected.value = status === 'open' + }) const cleanup = () => { + if (isClosed) return + + isClosed = true + if (connectTimer) { clearTimeout(connectTimer) connectTimer = null } + manager.removeStatusListener(statusListenerId) manager.removeMessageListener(listenerId) sseManagerSingleton.closeIndependentManager(url, listenerId) isConnected.value = false @@ -46,11 +56,10 @@ export function useBackgroundOptimization() { const connectDelay = options?.connectDelay || 100 connectTimer = setTimeout(() => { connectTimer = null + if (isClosed) return + try { - manager.addMessageListener(listenerId, event => { - messageHandler(event) - isConnected.value = true - }) + manager.addMessageListener(listenerId, messageHandler) } catch (error) { console.error('SSE连接建立失败:', error) } @@ -69,7 +78,7 @@ export function useBackgroundOptimization() { } /** - * 使用优化的定时器 + * 使用定时器 * @param id 定时器ID * @param callback 回调函数 * @param interval 间隔时间(毫秒) @@ -110,25 +119,40 @@ export function useBackgroundOptimization() { messageHandler: (event: MessageEvent) => void, listenerId: string, delay: number = 3000, - options?: Parameters[3], + options?: UseSSEOptions, ) => { // 使用独立的SSE管理器,确保每个监听器都有独立的连接 const manager = sseManagerSingleton.getIndependentManager(url, listenerId, options) + const isConnected = ref(false) let connectTimer: ReturnType | null = null + let isClosed = false + const statusListenerId = `${listenerId}:status` + + manager.addStatusListener(statusListenerId, status => { + isConnected.value = status === 'open' + }) const cleanup = () => { + if (isClosed) return + + isClosed = true + if (connectTimer) { clearTimeout(connectTimer) connectTimer = null } + manager.removeStatusListener(statusListenerId) manager.removeMessageListener(listenerId) sseManagerSingleton.closeIndependentManager(url, listenerId) + isConnected.value = false } onMounted(() => { connectTimer = setTimeout(() => { connectTimer = null + if (isClosed) return + manager.addMessageListener(listenerId, messageHandler) }, delay) }) @@ -139,6 +163,7 @@ export function useBackgroundOptimization() { manager, readyState: () => manager.readyState, close: cleanup, + isConnected, } } @@ -189,9 +214,12 @@ export function useBackgroundOptimization() { isListening = false } - onUnmounted(() => { - stopProgress(true) - }) + // 进度监听有些场景会在用户操作后动态创建;只有 setup 阶段创建时才注册自动卸载钩子。 + if (getCurrentInstance()) { + onUnmounted(() => { + stopProgress(true) + }) + } return { start: startProgress, diff --git a/src/layouts/components/UserNotification.vue b/src/layouts/components/UserNotification.vue index b9b0a101..4d9579af 100644 --- a/src/layouts/components/UserNotification.vue +++ b/src/layouts/components/UserNotification.vue @@ -2,10 +2,10 @@ import { formatDateDifference } from '@core/utils/formatters' import { SystemNotification } from '@/api/types' import { useI18n } from 'vue-i18n' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' const { t } = useI18n() -const { useDelayedSSE } = useBackgroundOptimization() +const { useDelayedSSE } = useBackground() // 是否有新消息 const hasNewMessage = ref(false) @@ -39,7 +39,7 @@ function handleMessage(event: MessageEvent) { } } -// 使用优化的SSE连接,延迟3秒启动,避免认证问题 +// 延迟3秒启动SSE连接,避免认证信息尚未准备好。 useDelayedSSE( `${import.meta.env.VITE_API_BASE_URL}system/message`, handleMessage, diff --git a/src/pages/resource.vue b/src/pages/resource.vue index 58621607..3a1f4e9c 100644 --- a/src/pages/resource.vue +++ b/src/pages/resource.vue @@ -240,6 +240,8 @@ const progressEnabled = ref(false) // 进度是否激活 const progressActive = ref(false) +let progressResetTimer: ReturnType | null = null + // 是否显示搜索进度 const isSearchProgressVisible = computed( () => progressActive.value || (!isRefreshed.value && (progressEnabled.value || progressValue.value > 0)), @@ -268,10 +270,12 @@ const errorTitle = ref(t('resource.noData')) const errorDescription = ref(t('resource.noResourceFound')) let searchEventSource: EventSource | null = null +let searchStreamIdleTimer: ReturnType | null = null const streamPreviewLimit = 24 const streamUiFlushDelay = 1000 const streamPreviewBufferLimit = streamPreviewLimit * 4 +const searchStreamIdleTimeout = 90_000 const streamTotalCount = ref(0) const streamPreviewDataList = ref>([]) @@ -343,6 +347,7 @@ const watchProgressValue = watch( // 使用SSE监听加载进度 function startLoadingProgress() { + clearProgressResetTimer() watchProgressValue.resume() progressText.value = t('resource.searching') progressValue.value = 0 @@ -357,18 +362,41 @@ function stopLoadingProgress() { // 确保进度显示100%,然后再渐进清零 progressValue.value = 100 - setTimeout(() => { + clearProgressResetTimer() + progressResetTimer = setTimeout(() => { + progressResetTimer = null progressValue.value = 0 progressEnabled.value = false }, 1500) } +function clearProgressResetTimer() { + if (progressResetTimer) { + clearTimeout(progressResetTimer) + progressResetTimer = null + } +} + // 关闭SSE连接 -function closeSearchEventSource() { +function closeSearchEventSource(source?: EventSource) { + if (source && searchEventSource !== source) { + source.close() + return + } + if (searchEventSource) { searchEventSource.close() searchEventSource = null } + + clearSearchStreamIdleTimer() +} + +function clearSearchStreamIdleTimer() { + if (searchStreamIdleTimer) { + clearTimeout(searchStreamIdleTimer) + searchStreamIdleTimer = null + } } // 渐进式搜索期间只保留有限预览数据,避免每个批次都触发完整筛选和分组计算。 @@ -607,36 +635,48 @@ function searchByStream(params: SearchParams, requestToken?: string) { const source = new EventSource(buildSearchStreamUrl(params, requestToken)) searchEventSource = source + const settleSearchStream = (callback: () => void) => { + if (settled) return + + settled = true + closeSearchEventSource(source) + callback() + } + + const resetIdleTimeout = () => { + clearSearchStreamIdleTimer() + searchStreamIdleTimer = setTimeout(() => { + settleSearchStream(() => reject(new Error(t('resource.noResourceFound')))) + }, searchStreamIdleTimeout) + } + + resetIdleTimeout() + source.onmessage = event => { + if (source !== searchEventSource || settled) return + try { + resetIdleTimeout() const eventData = JSON.parse(event.data) handleSearchStreamMessage(eventData) if (eventData.type === 'error') { - settled = true - closeSearchEventSource() - resolve() + settleSearchStream(resolve) return } if (eventData.type === 'done') { - settled = true - closeSearchEventSource() - resolve() + settleSearchStream(resolve) } } catch (error) { - settled = true - closeSearchEventSource() - reject(error) + settleSearchStream(() => reject(error)) } } source.onerror = () => { - if (settled) return + if (source !== searchEventSource || settled) return - settled = true - closeSearchEventSource() - reject(new Error(t('resource.noResourceFound'))) + settleSearchStream(() => reject(new Error(t('resource.noResourceFound')))) } }) } @@ -1011,6 +1051,7 @@ onMounted(async () => { onUnmounted(() => { closeSearchEventSource() stopLoadingProgress() + clearProgressResetTimer() stopAiRecommendPolling() clearStreamPreviewState() }) diff --git a/src/utils/sseManager.ts b/src/utils/sseManager.ts index 8244c587..7f057716 100644 --- a/src/utils/sseManager.ts +++ b/src/utils/sseManager.ts @@ -1,21 +1,33 @@ +export type SSEConnectionStatus = 'idle' | 'connecting' | 'open' | 'error' | 'closed' + +export interface SSEManagerOptions { + backgroundCloseDelay: number + reconnectDelay: number + maxReconnectAttempts: number + reconnectBackoffMultiplier: number + maxReconnectDelay: number +} + +type SSEMessageListener = (event: MessageEvent) => void +type SSEStatusListener = (status: SSEConnectionStatus) => void + /** * SSE连接管理器 - * 优化后台SSE连接,减少iOS系统杀掉应用的概率 + * 统一收口 EventSource 生命周期,避免后台常驻连接和重复重连。 */ export class SSEManager { private eventSource: EventSource | null = null private url: string - private isBackground = false + private isBackground = document.hidden private reconnectTimer: number | null = null private backgroundCloseTimer: number | null = null - private listeners: Map void> = new Map() - private options: { - backgroundCloseDelay: number - reconnectDelay: number - maxReconnectAttempts: number - } + private listeners: Map = new Map() + private statusListeners: Map = new Map() + private options: SSEManagerOptions private reconnectAttempts = 0 private isConnecting = false + private isDestroyed = false + private connectionStatus: SSEConnectionStatus = 'idle' private readonly handleVisibilityChange = () => { if (document.hidden) { this.handleBackground() @@ -27,12 +39,14 @@ export class SSEManager { this.destroy() } - constructor(url: string, options: Partial = {}) { + constructor(url: string, options: Partial = {}) { this.url = url this.options = { backgroundCloseDelay: 5000, // 5秒后关闭后台连接 reconnectDelay: 3000, // 3秒后重连 maxReconnectAttempts: 3, + reconnectBackoffMultiplier: 1.5, + maxReconnectDelay: 30_000, ...options, } @@ -50,38 +64,44 @@ export class SSEManager { } private handleBackground() { + if (this.isDestroyed) return + this.isBackground = true + this.clearReconnectTimer() // 延迟关闭SSE连接,避免频繁切换 - if (this.backgroundCloseTimer) { - clearTimeout(this.backgroundCloseTimer) - } + this.clearBackgroundCloseTimer() this.backgroundCloseTimer = window.setTimeout(() => { if (this.isBackground && this.eventSource) { - this.eventSource.close() - this.eventSource = null + this.closeCurrentEventSource() + this.setConnectionStatus('closed') } }, this.options.backgroundCloseDelay) } private handleForeground() { + if (this.isDestroyed) return + this.isBackground = false // 清除后台关闭定时器 - if (this.backgroundCloseTimer) { - clearTimeout(this.backgroundCloseTimer) - this.backgroundCloseTimer = null - } + this.clearBackgroundCloseTimer() // 只有在有活跃监听器时才重新建立连接 if (this.listeners.size > 0 && (!this.eventSource || this.eventSource.readyState === EventSource.CLOSED)) { - this.reconnectSSE() + this.reconnectSSE(0) } } private reconnectSSE(attemptCount = 0) { - if (attemptCount >= this.options.maxReconnectAttempts) { + if (this.isDestroyed || this.isBackground || this.listeners.size === 0) { + return + } + + if (attemptCount > this.options.maxReconnectAttempts) { + this.reconnectAttempts = this.options.maxReconnectAttempts + this.setConnectionStatus('closed') return } @@ -89,40 +109,38 @@ export class SSEManager { return } - // 如果没有活跃的监听器,不进行重连 - if (this.listeners.size === 0) { - return - } - + this.clearReconnectTimer() + this.closeCurrentEventSource() this.isConnecting = true this.reconnectAttempts = attemptCount + this.setConnectionStatus('connecting') try { - this.eventSource = new EventSource(this.url) + const source = new EventSource(this.url) + this.eventSource = source - this.eventSource.onopen = () => { + source.onopen = () => { + if (source !== this.eventSource) return this.isConnecting = false this.reconnectAttempts = 0 + this.setConnectionStatus('open') } - this.eventSource.onerror = error => { + source.onerror = () => { + if (source !== this.eventSource) return + this.isConnecting = false + this.setConnectionStatus('error') - if (this.eventSource?.readyState === EventSource.CLOSED) { - // 连接已关闭,尝试重连 - if (this.reconnectTimer) { - clearTimeout(this.reconnectTimer) - } - - this.reconnectTimer = window.setTimeout(() => { - if (!this.isBackground && this.listeners.size > 0) { - this.reconnectSSE(this.reconnectAttempts + 1) - } - }, this.options.reconnectDelay) + if (source.readyState === EventSource.CLOSED) { + this.closeCurrentEventSource() + this.scheduleReconnect(this.reconnectAttempts + 1) } } - this.eventSource.onmessage = event => { + source.onmessage = event => { + if (source !== this.eventSource || this.isDestroyed) return + // 分发消息给所有监听器 this.listeners.forEach((listener, listenerId) => { try { @@ -135,29 +153,95 @@ export class SSEManager { } } catch (error) { this.isConnecting = false + this.setConnectionStatus('error') // 连接创建失败,尝试重连 - if (this.reconnectTimer) { - clearTimeout(this.reconnectTimer) - } - - this.reconnectTimer = window.setTimeout(() => { - if (!this.isBackground && this.listeners.size > 0) { - this.reconnectSSE(this.reconnectAttempts + 1) - } - }, this.options.reconnectDelay) + this.scheduleReconnect(this.reconnectAttempts + 1) + console.error('SSE: 连接创建失败', error) } } + private scheduleReconnect(attemptCount: number) { + if (this.isDestroyed || this.isBackground || this.listeners.size === 0) { + return + } + + if (attemptCount > this.options.maxReconnectAttempts) { + this.reconnectAttempts = this.options.maxReconnectAttempts + this.setConnectionStatus('closed') + return + } + + this.clearReconnectTimer() + this.reconnectAttempts = attemptCount + + // 失败越多等待越久,避免网络波动时短时间内打满连接。 + const reconnectDelay = Math.min( + this.options.reconnectDelay * this.options.reconnectBackoffMultiplier ** Math.max(0, attemptCount - 1), + this.options.maxReconnectDelay, + ) + + this.reconnectTimer = window.setTimeout(() => { + this.reconnectTimer = null + this.reconnectSSE(attemptCount) + }, reconnectDelay) + } + + private closeCurrentEventSource() { + if (!this.eventSource) { + return + } + + this.eventSource.onopen = null + this.eventSource.onerror = null + this.eventSource.onmessage = null + this.eventSource.close() + this.eventSource = null + this.isConnecting = false + } + + private clearReconnectTimer() { + if (!this.reconnectTimer) return + + clearTimeout(this.reconnectTimer) + this.reconnectTimer = null + } + + private clearBackgroundCloseTimer() { + if (!this.backgroundCloseTimer) return + + clearTimeout(this.backgroundCloseTimer) + this.backgroundCloseTimer = null + } + + private setConnectionStatus(status: SSEConnectionStatus) { + if (this.connectionStatus === status) return + + this.connectionStatus = status + this.statusListeners.forEach((listener, listenerId) => { + try { + listener(status) + } catch (error) { + console.error(`SSE: 状态监听器错误 [${listenerId}]`, error) + } + }) + } + /** * 添加消息监听器 */ - addMessageListener(id: string, listener: (event: MessageEvent) => void) { + addMessageListener(id: string, listener: SSEMessageListener) { + if (this.isDestroyed) return + this.listeners.set(id, listener) // 如果还没有连接且不在后台,现在建立连接 - if (!this.eventSource && !this.isBackground && !this.isConnecting) { - this.reconnectSSE() + if ( + !this.isBackground && + !this.isConnecting && + (!this.eventSource || this.eventSource.readyState === EventSource.CLOSED) + ) { + this.reconnectSSE(0) } } @@ -165,6 +249,8 @@ export class SSEManager { * 移除消息监听器 */ removeMessageListener(id: string) { + if (this.isDestroyed) return + this.listeners.delete(id) // 如果没有监听器了,关闭连接 @@ -184,25 +270,17 @@ export class SSEManager { * 销毁管理器并清理所有引用 */ destroy() { + if (this.isDestroyed) return + + this.isDestroyed = true this.resetConnectionState(true) this.removeVisibilityListener() } private resetConnectionState(clearListeners = false) { - if (this.eventSource) { - this.eventSource.close() - this.eventSource = null - } - - if (this.reconnectTimer) { - clearTimeout(this.reconnectTimer) - this.reconnectTimer = null - } - - if (this.backgroundCloseTimer) { - clearTimeout(this.backgroundCloseTimer) - this.backgroundCloseTimer = null - } + this.closeCurrentEventSource() + this.clearReconnectTimer() + this.clearBackgroundCloseTimer() if (clearListeners) { this.listeners.clear() @@ -210,6 +288,31 @@ export class SSEManager { this.isConnecting = false this.reconnectAttempts = 0 + this.setConnectionStatus(this.listeners.size > 0 ? 'closed' : 'idle') + + if (clearListeners) { + this.statusListeners.clear() + } + } + + /** + * 添加连接状态监听器 + */ + addStatusListener(id: string, listener: SSEStatusListener, emitCurrent = true) { + if (this.isDestroyed) return + + this.statusListeners.set(id, listener) + + if (emitCurrent) { + listener(this.connectionStatus) + } + } + + /** + * 移除连接状态监听器 + */ + removeStatusListener(id: string) { + this.statusListeners.delete(id) } /** @@ -219,6 +322,13 @@ export class SSEManager { return this.eventSource?.readyState ?? EventSource.CLOSED } + /** + * 获取内部连接状态 + */ + get status(): SSEConnectionStatus { + return this.connectionStatus + } + /** * 获取连接URL */ @@ -230,10 +340,12 @@ export class SSEManager { * 强制重新连接 */ forceReconnect() { + if (this.isDestroyed) return + const hasActiveListeners = this.listeners.size > 0 this.close() if (!this.isBackground && hasActiveListeners) { - this.reconnectSSE() + this.reconnectSSE(0) } } diff --git a/src/views/dashboard/AnalyticsCpu.vue b/src/views/dashboard/AnalyticsCpu.vue index 08dea62f..58d816d9 100644 --- a/src/views/dashboard/AnalyticsCpu.vue +++ b/src/views/dashboard/AnalyticsCpu.vue @@ -3,11 +3,11 @@ import { useTheme } from 'vuetify' import { hexToRgb } from '@layouts/utils' import api from '@/api' import { useI18n } from 'vue-i18n' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' // 国际化 const { t } = useI18n() -const { useDataRefresh } = useBackgroundOptimization() +const { useDataRefresh } = useBackground() // 输入参数 const props = defineProps({ @@ -122,7 +122,7 @@ async function loadCpuData() { } } -// 使用优化的数据刷新定时器 +// 使用数据刷新定时器 const { loading } = useDataRefresh( 'analytics-cpu', loadCpuData, diff --git a/src/views/dashboard/AnalyticsMemory.vue b/src/views/dashboard/AnalyticsMemory.vue index a15b603c..41edc2d6 100644 --- a/src/views/dashboard/AnalyticsMemory.vue +++ b/src/views/dashboard/AnalyticsMemory.vue @@ -4,11 +4,11 @@ import { hexToRgb } from '@layouts/utils' import api from '@/api' import { formatBytes } from '@/@core/utils/formatters' import { useI18n } from 'vue-i18n' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' // 国际化 const { t } = useI18n() -const { useDataRefresh } = useBackgroundOptimization() +const { useDataRefresh } = useBackground() // 输入参数 const props = defineProps({ @@ -127,7 +127,7 @@ async function loadMemoryData() { } } -// 使用优化的数据刷新定时器 +// 使用数据刷新定时器 const { loading } = useDataRefresh( 'analytics-memory', loadMemoryData, diff --git a/src/views/dashboard/AnalyticsNetwork.vue b/src/views/dashboard/AnalyticsNetwork.vue index 27fb4a6f..ee599885 100644 --- a/src/views/dashboard/AnalyticsNetwork.vue +++ b/src/views/dashboard/AnalyticsNetwork.vue @@ -3,11 +3,11 @@ import { useTheme } from 'vuetify' import { hexToRgb } from '@layouts/utils' import api from '@/api' import { useI18n } from 'vue-i18n' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' // 国际化 const { t } = useI18n() -const { useDataRefresh } = useBackgroundOptimization() +const { useDataRefresh } = useBackground() // 输入参数 const props = defineProps({ @@ -160,7 +160,7 @@ async function getNetworkUsage() { } } -// 使用优化的数据刷新定时器 +// 使用数据刷新定时器 useDataRefresh( 'dashboard-network', getNetworkUsage, diff --git a/src/views/dashboard/AnalyticsProcesses.vue b/src/views/dashboard/AnalyticsProcesses.vue index e66320e6..7308eca2 100644 --- a/src/views/dashboard/AnalyticsProcesses.vue +++ b/src/views/dashboard/AnalyticsProcesses.vue @@ -3,11 +3,11 @@ import { formatSeconds } from '@/@core/utils/formatters' import api from '@/api' import type { Process } from '@/api/types' import { useI18n } from 'vue-i18n' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' // 国际化 const { t } = useI18n() -const { useDataRefresh } = useBackgroundOptimization() +const { useDataRefresh } = useBackground() // 表头 const headers = [ @@ -31,7 +31,7 @@ async function loadProcessList() { } } -// 使用优化的数据刷新定时器 +// 使用数据刷新定时器 useDataRefresh( 'dashboard-processes', loadProcessList, diff --git a/src/views/dashboard/AnalyticsScheduler.vue b/src/views/dashboard/AnalyticsScheduler.vue index 4d496df8..18248286 100644 --- a/src/views/dashboard/AnalyticsScheduler.vue +++ b/src/views/dashboard/AnalyticsScheduler.vue @@ -2,11 +2,11 @@ import api from '@/api' import type { ScheduleInfo } from '@/api/types' import { useI18n } from 'vue-i18n' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' // 国际化 const { t } = useI18n() -const { useDataRefresh } = useBackgroundOptimization() +const { useDataRefresh } = useBackground() // 输入参数 const props = defineProps({ @@ -34,7 +34,7 @@ async function loadSchedulerList() { } } -// 使用优化的数据刷新定时器 +// 使用数据刷新定时器 useDataRefresh( 'dashboard-scheduler', loadSchedulerList, diff --git a/src/views/dashboard/AnalyticsSpeed.vue b/src/views/dashboard/AnalyticsSpeed.vue index 1ebb83ea..eb208708 100644 --- a/src/views/dashboard/AnalyticsSpeed.vue +++ b/src/views/dashboard/AnalyticsSpeed.vue @@ -3,11 +3,11 @@ import { formatFileSize } from '@/@core/utils/formatters' import api from '@/api' import type { DownloaderInfo } from '@/api/types' import { useI18n } from 'vue-i18n' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' // 国际化 const { t } = useI18n() -const { useDataRefresh } = useBackgroundOptimization() +const { useDataRefresh } = useBackground() // 输入参数 const props = defineProps({ @@ -77,7 +77,7 @@ async function loadDownloaderInfo() { } } -// 使用优化的数据刷新定时器 +// 使用数据刷新定时器 const { loading } = useDataRefresh( 'analytics-speed', loadDownloaderInfo, diff --git a/src/views/reorganize/DownloadingListView.vue b/src/views/reorganize/DownloadingListView.vue index 3c229e24..4e12f350 100644 --- a/src/views/reorganize/DownloadingListView.vue +++ b/src/views/reorganize/DownloadingListView.vue @@ -7,11 +7,11 @@ import DownloadingCard from '@/components/cards/DownloadingCard.vue' import ProgressiveCardGrid from '@/components/misc/ProgressiveCardGrid.vue' import { useUserStore } from '@/stores' import { useI18n } from 'vue-i18n' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' // 国际化 const { t } = useI18n() -const { useDataRefresh } = useBackgroundOptimization() +const { useDataRefresh } = useBackground() // 定义输入参数 const props = defineProps<{ @@ -56,7 +56,7 @@ const filteredDataList = computed(() => { else return dataList.value.filter(data => data.userid === userName || data.username === userName) }) -// 使用优化的数据刷新定时器 +// 使用数据刷新定时器 const { loading: dataLoading } = useDataRefresh( 'downloading-list', fetchData, diff --git a/src/views/reorganize/TransferHistoryView.vue b/src/views/reorganize/TransferHistoryView.vue index 624cb140..a37e3c13 100644 --- a/src/views/reorganize/TransferHistoryView.vue +++ b/src/views/reorganize/TransferHistoryView.vue @@ -14,7 +14,7 @@ import { useI18n } from 'vue-i18n' import { usePWA } from '@/composables/usePWA' import { useDynamicButton } from '@/composables/useDynamicButton' import { useAvailableHeight } from '@/composables/useAvailableHeight' -import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization' +import { useBackground } from '@/composables/useBackground' import { useGlobalSettingsStore } from '@/stores' // i18n @@ -27,7 +27,7 @@ const globalSettingsStore = useGlobalSettingsStore() const display = useDisplay() // PWA模式检测 const { appMode } = usePWA() -const { useProgressSSE } = useBackgroundOptimization() +const { useProgressSSE } = useBackground() // 计算列表可用高度 // componentOffset = VCardItem搜索栏(68) + VDivider(1) + 分页栏(40) + VCard边距(2) = 111 diff --git a/src/views/system/LoggingView.vue b/src/views/system/LoggingView.vue index d1b3b859..f4690726 100644 --- a/src/views/system/LoggingView.vue +++ b/src/views/system/LoggingView.vue @@ -1,7 +1,7 @@